From 2cf8c3e27ed711d7e6d06927b41c1c1fe8ac11f3 Mon Sep 17 00:00:00 2001 From: Erik Ernst <github@black-milk.de> Date: Sun, 7 Jul 2013 19:05:39 +0200 Subject: [PATCH 001/138] * updated tablesorter to latest version (2.10.8) * compatibility with rails 4 --- CHANGELOG.markdown | 5 + README.markdown | 4 +- jquery-tablesorter.gemspec | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 292 +++--- .../jquery-tablesorter/jquery.metadata.js | 2 +- .../jquery-tablesorter/jquery.tablesorter.js | 837 +++++++++------- ...ry.tablesorter.widgets-filter-formatter.js | 924 ++++++++++++++++++ .../jquery.tablesorter.widgets.js | 769 ++++++++++----- .../jquery-tablesorter/filter.formatter.css | 183 ++++ .../jquery-tablesorter/theme.black-ice.css | 13 +- .../jquery-tablesorter/theme.blue.css | 13 +- .../jquery-tablesorter/theme.bootstrap.css | 19 +- .../jquery-tablesorter/theme.dark.css | 13 +- .../jquery-tablesorter/theme.default.css | 16 +- .../jquery-tablesorter/theme.dropbox.css | 28 +- .../jquery-tablesorter/theme.green.css | 13 +- .../jquery-tablesorter/theme.grey.css | 13 +- .../jquery-tablesorter/theme.ice.css | 13 +- .../jquery-tablesorter/theme.jui.css | 13 +- 20 files changed, 2388 insertions(+), 786 deletions(-) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js create mode 100644 vendor/assets/stylesheets/jquery-tablesorter/filter.formatter.css diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index 9b05922..7d55cd2 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -1,5 +1,10 @@ Changelog === +#### v1.4.1 - themilkman fork + +* Upgrade tablesorter to v2.10.8 +* Rails 4 compatibility + #### v1.4.1 * Upgrade tablesorter to v2.7.5 diff --git a/README.markdown b/README.markdown index 791e183..1ee8c2c 100644 --- a/README.markdown +++ b/README.markdown @@ -6,7 +6,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.7.5 (2/5/2013), [documentation] +Current tablesorter version: 2.10.8 (6/3/2013), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. @@ -94,7 +94,7 @@ pager theme: 2. Run `rake jquery_tablesorter:update` 3. Run `rake jquery_tablesorter:sanitize_image_paths` 4. Update `README.md` and `CHANGELOG.md` - + [Mottie's fork]: https://github.com/Mottie/tablesorter [documentation]: http://mottie.github.com/tablesorter/docs/index.html diff --git a/jquery-tablesorter.gemspec b/jquery-tablesorter.gemspec index b41d2d4..34bb556 100644 --- a/jquery-tablesorter.gemspec +++ b/jquery-tablesorter.gemspec @@ -15,5 +15,5 @@ Gem::Specification.new do |s| s.files = Dir["{vendor,lib}/**/*"] + ["MIT-LICENSE", "Rakefile", "README.markdown"] - s.add_dependency "railties", ">= 3.1, < 5" + s.add_dependency "railties", ">= 3.1", "< 5" end diff --git a/tablesorter b/tablesorter index 197960a..96a8efb 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 197960a791fc8463f1c55038d5bb152ba3af87c0 +Subproject commit 96a8efb4ddf5198f1f9636826aa64a1e9ca5eeb0 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 8d86772..105e3cf 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 1/29/2013 + * updated 5/27/2013 */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -20,6 +20,14 @@ // and a filterList = [[2,Blue],[3,13]] becomes "&fcol[2]=Blue&fcol[3]=13" in the url ajaxUrl: null, + // modify the url after all processing has been applied + customAjaxUrl: function(table, url) { return url; }, + + // modify the $.ajax object to allow complete control over your ajax requests + ajaxObject: { + dataType: 'json' + }, + // process ajax so that the following information is returned: // [ total_rows (number), rows (array of arrays), headers (array; optional) ] // example: @@ -87,21 +95,24 @@ dis = !!disable, tp = Math.min( c.totalPages, c.filteredPages ); if ( c.updateArrows ) { - $(c.cssFirst + ',' + c.cssPrev, c.container)[ ( dis || c.page === 0 ) ? a : r ](d); - $(c.cssNext + ',' + c.cssLast, c.container)[ ( dis || c.page === tp - 1 ) ? a : r ](d); + c.$container.find(c.cssFirst + ',' + c.cssPrev)[ ( dis || c.page === 0 ) ? a : r ](d); + c.$container.find(c.cssNext + ',' + c.cssLast)[ ( dis || c.page === tp - 1 ) ? a : r ](d); } }, - updatePageDisplay = function(table, c) { - var i, p, s, t, out, f = $(table).hasClass('hasFilters') && !c.ajaxUrl; - c.filteredRows = (f) ? table.config.$tbodies.children('tr:not(.filtered,.remove-me)').length : c.totalRows; - c.filteredPages = (f) ? Math.ceil( c.filteredRows / c.size ) : c.totalPages; - if ( Math.min( c.totalPages, c.filteredPages ) > 0 ) { + updatePageDisplay = function(table, c, flag) { + var i, p, s, t, out, + tc = table.config, + f = $(table).hasClass('hasFilters') && !c.ajaxUrl; + c.totalPages = Math.ceil( c.totalRows / c.size ); // needed for "pageSize" method + c.filteredRows = (f) ? tc.$tbodies.eq(0).children('tr:not(.' + (tc.widgetOptions && tc.widgetOptions.filter_filteredRow || 'filtered') + ',' + tc.selectorRemove + ')').length : c.totalRows; + c.filteredPages = (f) ? Math.ceil( c.filteredRows / c.size ) || 1 : c.totalPages; + if ( Math.min( c.totalPages, c.filteredPages ) >= 0 ) { t = (c.size * c.page > c.filteredRows); - c.startRow = (t) ? 1 : ( c.size * c.page ) + 1; + c.startRow = (t) ? 1 : (c.filteredRows === 0 ? 0 : c.size * c.page + 1); c.page = (t) ? 0 : c.page; c.endRow = Math.min( c.filteredRows, c.totalRows, c.size * ( c.page + 1 ) ); - out = $(c.cssPageDisplay, c.container); + out = c.$container.find(c.cssPageDisplay); // form the output string s = c.output.replace(/\{(page|filteredRows|filteredPages|totalPages|startRow|endRow|totalRows)\}/gi, function(m){ return { @@ -114,24 +125,24 @@ '{totalRows}' : c.totalRows }[m]; }); - if (out[0]) { + if (out.length) { out[ (out[0].tagName === 'INPUT') ? 'val' : 'html' ](s); - if ( $(c.cssGoto, c.container).length ) { + if ( c.$goto.length ) { t = ''; p = Math.min( c.totalPages, c.filteredPages ); for ( i = 1; i <= p; i++ ) { t += '<option>' + i + '</option>'; } - $(c.cssGoto, c.container).html(t).val(c.page + 1); + c.$goto.html(t).val( c.page + 1 ); } } } pagerArrows(c); - if (c.initialized) { $(table).trigger('pagerComplete', c); } + if (c.initialized && flag !== false) { $(table).trigger('pagerComplete', c); } }, fixHeight = function(table, c) { - var d, h, $b = $(table.tBodies[0]); + var d, h, $b = table.config.$tbodies.eq(0); if (c.fixedHeight) { $b.find('tr.pagerSavedHeightSpacer').remove(); h = $.data(table, 'pagerSavedHeight'); @@ -145,7 +156,7 @@ }, changeHeight = function(table, c) { - var $b = $(table.tBodies[0]); + var $b = table.config.$tbodies.eq(0); $b.find('tr.pagerSavedHeightSpacer').remove(); $.data(table, 'pagerSavedHeight', $b.height()); fixHeight(table, c); @@ -155,13 +166,15 @@ hideRows = function(table, c){ if (!c.ajaxUrl) { var i, - rows = $(table.tBodies).children('tr:not(.' + table.config.cssChildRow + ')'), + tc = table.config, + rows = tc.$tbodies.eq(0).children('tr:not(.' + tc.cssChildRow + ')'), l = rows.length, s = ( c.page * c.size ), e = s + c.size, + f = tc.widgetOptions && tc.widgetOptions.filter_filteredRow || 'filtered', j = 0; // size counter for ( i = 0; i < l; i++ ){ - if (!/filtered/.test(rows[i].className)) { + if ( !rows[i].className.match(f) ) { rows[i].style.display = ( j >= s && j < e ) ? '' : 'none'; j++; } @@ -170,7 +183,7 @@ }, hideRowsSetup = function(table, c){ - c.size = parseInt( $(c.cssPageSize, c.container).find('option[selected]').val(), 10 ) || c.size; + c.size = parseInt( c.$size.val(), 10 ) || c.size; $.data(table, 'pagerLastSize', c.size); pagerArrows(c); if ( !c.removeRows ) { @@ -181,65 +194,87 @@ } }, - renderAjax = function(data, table, c, exception){ + renderAjax = function(data, table, c, xhr, exception){ // process data if ( typeof(c.ajaxProcessing) === "function" ) { // ajaxProcessing result: [ total, rows, headers ] - var i, j, hsh, $f, $sh, + var i, j, hsh, $f, $sh, th, d, l, $err, $t = $(table), tc = table.config, hl = $t.find('thead th').length, tds = '', - err = '<tr class="' + c.cssErrorRow + ' ' + tc.selectorRemove.replace(/(tr)?\./g,'') + '"><td style="text-align: center;" colspan="' + hl + '">' + - (exception ? exception.message + ' (' + exception.name + ')' : 'No rows found') + '</td></tr>', - result = c.ajaxProcessing(data) || [ 0, [] ], - d = result[1] || [], - l = d.length, - th = result[2]; - if ( l > 0 ) { - for ( i = 0; i < l; i++ ) { - tds += '<tr>'; - for ( j = 0; j < d[i].length; j++ ) { - // build tbody cells - tds += '<td>' + d[i][j] + '</td>'; - } - tds += '</tr>'; - } - } - // only add new header text if the length matches - if ( th && th.length === hl ) { - hsh = $t.hasClass('hasStickyHeaders'); - $sh = $t.find('.' + ((tc.widgetOptions && tc.widgetOptions.stickyHeaders) || 'tablesorter-stickyheader')); - $f = $t.find('tfoot tr:first').children(); - $t.find('th.' + tc.cssHeader).each(function(j){ - var $t = $(this), icn; - // add new test within the first span it finds, or just in the header - if ( $t.find('.' + tc.cssIcon).length ) { - icn = $t.find('.' + tc.cssIcon).clone(true); - $t.find('.tablesorter-header-inner').html( th[j] ).append(icn); - if ( hsh && $sh.length ) { - icn = $sh.find('th').eq(j).find('.' + tc.cssIcon).clone(true); - $sh.find('th').eq(j).find('.tablesorter-header-inner').html( th[j] ).append(icn); - } - } else { - $t.find('.tablesorter-header-inner').html( th[j] ); - $sh.find('th').eq(j).find('.tablesorter-header-inner').html( th[j] ); - } - $f.eq(j).html( th[j] ); - }); - } - + result = c.ajaxProcessing(data, table) || [ 0, [] ], + // allow [ total, rows, headers ] or [ rows, total, headers ] + t = isNaN(result[0]) && !isNaN(result[1]); + $t.find('thead tr.' + c.cssErrorRow).remove(); // Clean up any previous error. + if ( exception ) { + $err = $('<tr class="' + c.cssErrorRow + '"><td style="text-align:center;" colspan="' + hl + '">' + ( + xhr.status === 0 ? 'Not connected, verify Network' : + xhr.status === 404 ? 'Requested page not found [404]' : + xhr.status === 500 ? 'Internal Server Error [500]' : + exception === 'parsererror' ? 'Requested JSON parse failed' : + exception === 'timeout' ? 'Time out error' : + exception === 'abort' ? 'Ajax Request aborted' : + 'Uncaught error: ' + xhr.statusText + ' [' + xhr.status + ']' ) + '</td></tr>') + .click(function(){ + $(this).remove(); + }) // add error row to thead instead of tbody, or clicking on the header will result in a parser error - $t.find('thead').append(err); + .appendTo( $t.find('thead:first') ); + tc.$tbodies.eq(0).empty(); } else { - $(table.tBodies[0]).html( tds ); // add rows to first tbody + c.totalRows = result[t ? 1 : 0] || c.totalRows || 0; + d = result[t ? 0 : 1] || []; // row data + l = d.length; + th = result[2]; // headers + if (d instanceof jQuery) { + // append jQuery object + tc.$tbodies.eq(0).empty().append(d); + } else if (d.length) { + // build table from array + if ( l > 0 ) { + for ( i = 0; i < l; i++ ) { + tds += '<tr>'; + for ( j = 0; j < d[i].length; j++ ) { + // build tbody cells + tds += '<td>' + d[i][j] + '</td>'; + } + tds += '</tr>'; + } + } + // add rows to first tbody + tc.$tbodies.eq(0).html( tds ); + } + // only add new header text if the length matches + if ( th && th.length === hl ) { + hsh = $t.hasClass('hasStickyHeaders'); + $sh = hsh ? tc.$sticky.children('thead:first').children().children() : ''; + $f = $t.find('tfoot tr:first').children(); + $t.find('th.' + tc.cssHeader).each(function(j){ + var $t = $(this), icn; + // add new test within the first span it finds, or just in the header + if ( $t.find('.' + tc.cssIcon).length ) { + icn = $t.find('.' + tc.cssIcon).clone(true); + $t.find('.tablesorter-header-inner').html( th[j] ).append(icn); + if ( hsh && $sh.length ) { + icn = $sh.eq(j).find('.' + tc.cssIcon).clone(true); + $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ).append(icn); + } + } else { + $t.find('.tablesorter-header-inner').html( th[j] ); + if (hsh && $sh.length) { + $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ); + } + } + $f.eq(j).html( th[j] ); + }); + } } if (tc.showProcessing) { $.tablesorter.isProcessing(table); // remove loading icon } $t.trigger('update'); - c.totalRows = result[0] || 0; c.totalPages = Math.ceil( c.totalRows / c.size ); updatePageDisplay(table, c); fixHeight(table, c); @@ -253,30 +288,39 @@ getAjax = function(table, c){ var url = getAjaxUrl(table, c), + $doc = $(document), tc = table.config; if ( url !== '' ) { if (tc.showProcessing) { $.tablesorter.isProcessing(table, true); // show loading icon } - $(document).bind('ajaxError.pager', function(e, xhr, settings, exception) { - if (settings.url === url) { - renderAjax(null, table, c, exception); - $(document).unbind('ajaxError.pager'); + $doc.bind('ajaxError.pager', function(e, xhr, settings, exception) { + if (url.match(settings.url)) { + renderAjax(null, table, c, xhr, exception); + $doc.unbind('ajaxError.pager'); } }); - $.getJSON(url, function(data) { + c.ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl + c.ajaxObject.success = function(data) { renderAjax(data, table, c); - $(document).unbind('ajaxError.pager'); - }); + $doc.unbind('ajaxError.pager'); + if (typeof c.oldAjaxSuccess === 'function') { + c.oldAjaxSuccess(data); + } + }; + $.ajax(c.ajaxObject); } }, - + getAjaxUrl = function(table, c) { - var url = (c.ajaxUrl) ? c.ajaxUrl.replace(/\{page\}/g, c.page).replace(/\{size\}/g, c.size) : '', + var url = (c.ajaxUrl) ? c.ajaxUrl + // allow using "{page+1}" in the url string to switch to a non-zero based index + .replace(/\{page([\-+]\d+)?\}/, function(s,n){ return c.page + (n ? parseInt(n, 10) : 0); }) + .replace(/\{size\}/g, c.size) : '', sl = table.config.sortList, fl = c.currentFilters || [], - sortCol = url.match(/\{sortList[\s+]?:[\s+]?([^}]*)\}/), - filterCol = url.match(/\{filterList[\s+]?:[\s+]?([^}]*)\}/), + sortCol = url.match(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/), + filterCol = url.match(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/), arry = []; if (sortCol) { sortCol = sortCol[1]; @@ -284,7 +328,8 @@ arry.push(sortCol + '[' + v[0] + ']=' + v[1]); }); // if the arry is empty, just add the col parameter... "&{sortList:col}" becomes "&col" - url = url.replace(/\{sortList[\s+]?:[\s+]?([^\}]*)\}/g, arry.length ? arry.join('&') : sortCol ); + url = url.replace(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : sortCol ); + arry = []; } if (filterCol) { filterCol = filterCol[1]; @@ -294,16 +339,17 @@ } }); // if the arry is empty, just add the fcol parameter... "&{filterList:fcol}" becomes "&fcol" - url = url.replace(/\{filterList[\s+]?:[\s+]?([^\}]*)\}/g, arry.length ? arry.join('&') : filterCol ); + url = url.replace(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : filterCol ); + } + if ( typeof(c.customAjaxUrl) === "function" ) { + url = c.customAjaxUrl(table, url); } - return url; }, renderTable = function(table, rows, c) { c.isDisabled = false; // needed because sorting will change the page and re-enable the pager - var i, j, o, - f = document.createDocumentFragment(), + var i, j, o, $tb, l = rows.length, s = ( c.page * c.size ), e = ( s + c.size ); @@ -315,17 +361,16 @@ if ( e > rows.length ) { e = rows.length; } - $(table.tBodies[0]).addClass('tablesorter-hidden'); $.tablesorter.clearTableBody(table); + $tb = $.tablesorter.processTbody(table, table.config.$tbodies.eq(0), true); for ( i = s; i < e; i++ ) { o = rows[i]; l = o.length; for ( j = 0; j < l; j++ ) { - f.appendChild(o[j]); + $tb.appendChild(o[j]); } } - table.tBodies[0].appendChild(f); - $(table.tBodies[0]).removeClass('tablesorter-hidden'); + $.tablesorter.processTbody(table, $tb, false); } if ( c.page >= c.totalPages ) { moveToLastPage(table, c); @@ -345,21 +390,20 @@ c.page = 0; c.size = c.totalRows; c.totalPages = 1; - $('tr.pagerSavedHeightSpacer', table.tBodies[0]).remove(); + $(table).find('tr.pagerSavedHeightSpacer').remove(); renderTable(table, table.config.rowsCopy, c); } // disable size selector - $(c.container).find(c.cssPageSize + ',' + c.cssGoto).each(function(){ + c.$size.add(c.$goto).each(function(){ $(this).addClass(c.cssDisabled)[0].disabled = true; }); }, - moveToPage = function(table, c) { + moveToPage = function(table, c, flag) { if ( c.isDisabled ) { return; } var p = Math.min( c.totalPages, c.filteredPages ); - if ( c.page < 0 || c.page > ( p - 1 ) ) { - c.page = 0; - } + if ( c.page < 0 ) { c.page = 0; } + if ( c.page > ( p - 1 ) && p !== 0 ) { c.page = p - 1; } if (c.ajax) { getAjax(table, c); } else if (!c.ajax) { @@ -367,11 +411,14 @@ } $.data(table, 'pagerLastPage', c.page); $.data(table, 'pagerUpdateTriggered', true); - if (c.initialized) { $(table).trigger('pageMoved', c); } + if (c.initialized && flag !== false) { + $(table).trigger('pageMoved', c); + } }, setPageSize = function(table, size, c) { c.size = size; + c.$size.val(size); $.data(table, 'pagerLastPage', c.page); $.data(table, 'pagerLastSize', c.size); c.totalPages = Math.ceil( c.totalRows / c.size ); @@ -406,14 +453,14 @@ destroyPager = function(table, c){ showAllRows(table, c); - $(c.container).hide(); // hide pager + c.$container.hide(); // hide pager table.config.appender = null; // remove pager appender function $(table).unbind('destroy.pager sortEnd.pager filterEnd.pager enable.pager disable.pager'); }, enablePager = function(table, c, triggered){ - var p = $(c.cssPageSize, c.container).removeClass(c.cssDisabled).removeAttr('disabled'); - $(c.container).find(c.cssGoto).removeClass(c.cssDisabled).removeAttr('disabled'); + var p = c.$size.removeClass(c.cssDisabled).removeAttr('disabled'); + c.$goto.removeClass(c.cssDisabled).removeAttr('disabled'); c.isDisabled = false; c.page = $.data(table, 'pagerLastPage') || c.page || 0; c.size = $.data(table, 'pagerLastSize') || parseInt(p.find('option[selected]').val(), 10) || c.size; @@ -448,7 +495,9 @@ table = this, tc = table.config, $t = $(table), - pager = $(c.container).addClass('tablesorter-pager').show(); // added in case the pager is reinitialized after being destroyed. + // added in case the pager is reinitialized after being destroyed. + pager = c.$container = $(c.container).addClass('tablesorter-pager').show(); + c.oldAjaxSuccess = c.oldAjaxSuccess || c.ajaxObject.success; config.appender = $this.appender; $t @@ -464,39 +513,51 @@ $.data(table, 'pagerUpdateTriggered', false); return; } - if (e.type === 'filterEnd') { c.page = 0; } - updatePageDisplay(table, c); - moveToPage(table, c); + moveToPage(table, c, false); + updatePageDisplay(table, c, false); fixHeight(table, c); }) - .bind('disable.pager', function(){ + .bind('disable.pager', function(e){ + e.stopPropagation(); showAllRows(table, c); }) - .bind('enable.pager', function(){ + .bind('enable.pager', function(e){ + e.stopPropagation(); enablePager(table, c, true); }) - .bind('destroy.pager', function(){ + .bind('destroy.pager', function(e){ + e.stopPropagation(); destroyPager(table, c); }) - .bind('update.pager', function(){ + .bind('update.pager', function(e){ + e.stopPropagation(); hideRows(table, c); }) .bind('pageSize.pager', function(e,v){ - c.size = parseInt(v, 10) || 10; + e.stopPropagation(); + setPageSize(table, parseInt(v, 10) || 10, c); hideRows(table, c); - updatePageDisplay(table, c); + updatePageDisplay(table, c, false); + if (c.$size.length) { c.$size.val(c.size); } // twice? + }) + .bind('pageSet.pager', function(e,v){ + e.stopPropagation(); + c.page = (parseInt(v, 10) || 1) - 1; + if (c.$goto.length) { c.$goto.val(c.size); } // twice? + moveToPage(table, c); + updatePageDisplay(table, c, false); }); // clicked controls - ctrls = [c.cssFirst, c.cssPrev, c.cssNext, c.cssLast]; + ctrls = [ c.cssFirst, c.cssPrev, c.cssNext, c.cssLast ]; fxn = [ moveToFirstPage, moveToPrevPage, moveToNextPage, moveToLastPage ]; pager.find(ctrls.join(',')) .unbind('click.pager') .bind('click.pager', function(e){ - var i, $this = $(this), l = ctrls.length; - if ( !$this.hasClass(c.cssDisabled) ) { + var i, $t = $(this), l = ctrls.length; + if ( !$t.hasClass(c.cssDisabled) ) { for (i = 0; i < l; i++) { - if ($this.is(ctrls[i])) { + if ($t.is(ctrls[i])) { fxn[i](table, c); break; } @@ -506,21 +567,22 @@ }); // goto selector - if ( pager.find(c.cssGoto).length ) { - pager.find(c.cssGoto) + c.$goto = pager.find(c.cssGoto); + if ( c.$goto.length ) { + c.$goto .unbind('change') .bind('change', function(){ c.page = $(this).val() - 1; moveToPage(table, c); }); - updatePageDisplay(table, c); + updatePageDisplay(table, c, false); } // page size selector - t = pager.find(c.cssPageSize); - if ( t.length ) { - t.unbind('change.pager').bind('change.pager', function() { - t.val( $(this).val() ); // in case there are more than one pagers + c.$size = pager.find(c.cssPageSize); + if ( c.$size.length ) { + c.$size.unbind('change.pager').bind('change.pager', function() { + c.$size.val( $(this).val() ); // in case there are more than one pagers if ( !$(this).hasClass(c.cssDisabled) ) { setPageSize(table, parseInt( $(this).val(), 10 ), c); changeHeight(table, c); @@ -551,7 +613,7 @@ } changeHeight(table, c); - + // pager initialized if (!c.ajax) { c.initialized = true; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.metadata.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.metadata.js index dc4538e..07b10ba 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.metadata.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.metadata.js @@ -1,4 +1,4 @@ -/* +/* * Metadata - jQuery plugin for parsing metadata from elements * * Copyright (c) 2006 John Resig, Yehuda Katz, Jörn Zaefferer, Paul McLanahan diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index e399f15..a955fb2 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /*! -* TableSorter 2.7.5 - Client-side table sorting with ease! +* TableSorter 2.10.8 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.7.5"; + ts.version = "2.10.8"; ts.parsers = []; ts.widgets = []; @@ -118,6 +118,7 @@ log(s + " (" + (new Date().getTime() - d.getTime()) + "ms)"); } + ts.log = log; ts.benchmark = benchmark; function getElementText(table, node, cellIndex) { @@ -131,9 +132,9 @@ text = $(node).text(); } } else { - if (typeof(t) === "function") { + if (typeof t === "function") { text = t(node, table, cellIndex); - } else if (typeof(t) === "object" && t.hasOwnProperty(cellIndex)) { + } else if (typeof t === "object" && t.hasOwnProperty(cellIndex)) { text = t[cellIndex](node, table, cellIndex); } else { text = c.supportsTextContent ? node.textContent : $(node).text(); @@ -143,7 +144,8 @@ } function detectParserForColumn(table, rows, rowIndex, cellIndex) { - var i, l = ts.parsers.length, + var cur, + i = ts.parsers.length, node = false, nodeValue = '', keepLooking = true; @@ -153,19 +155,21 @@ node = rows[rowIndex].cells[cellIndex]; nodeValue = getElementText(table, node, cellIndex); if (table.config.debug) { - log('Checking if value was empty on row ' + rowIndex + ', column: ' + cellIndex + ': ' + nodeValue); + log('Checking if value was empty on row ' + rowIndex + ', column: ' + cellIndex + ': "' + nodeValue + '"'); } } else { keepLooking = false; } } - for (i = 1; i < l; i++) { - if (ts.parsers[i].is && ts.parsers[i].is(nodeValue, table, node)) { - return ts.parsers[i]; + while (--i >= 0) { + cur = ts.parsers[i]; + // ignore the default text parser because it will always be true + if (cur && cur.id !== 'text' && cur.is && cur.is(nodeValue, table, node)) { + return cur; } } - // 0 is always the generic parser (text) - return ts.parsers[0]; + // nothing found, return the generic parser (text) + return ts.getParserById('text'); } function buildParserCache(table) { @@ -205,7 +209,7 @@ if (c.debug) { log(parsersDebug); } - return list; + c.parsers = list; } /* utils */ @@ -252,7 +256,7 @@ v = parsers[j].format(t, table, c[0].cells[j], j); cols.push(v); if ((parsers[j].type || '').toLowerCase() === "numeric") { - colMax[j] = Math.max(Math.abs(v), colMax[j] || 0); // determine column max value (ignore sign) + colMax[j] = Math.max(Math.abs(v) || 0, colMax[j] || 0); // determine column max value (ignore sign) } } cols.push(tc.cache[k].normalized.length); // add position for rowCache @@ -283,7 +287,7 @@ } for (k = 0; k < b.length; k++) { $bk = $(b[k]); - if (!$bk.hasClass(c.cssInfoBlock)) { + if ($bk.length && !$bk.hasClass(c.cssInfoBlock)) { // get tbody $tb = ts.processTbody(table, $bk, true); r = c2[k].row; @@ -370,13 +374,14 @@ function buildHeaders(table) { var header_index = computeThIndexes(table), ch, $t, - h, i, t, lock, time, $tableHeaders, c = table.config; - c.headerList = [], c.headerContent = []; + h, i, t, lock, time, c = table.config; + c.headerList = []; + c.headerContent = []; if (c.debug) { time = new Date(); } i = c.cssIcon ? '<i class="' + c.cssIcon + '"></i>' : ''; // add icon if cssIcon option exists - $tableHeaders = $(table).find(c.selectorHeaders).each(function(index) { + c.$headers = $(table).find(c.selectorHeaders).each(function(index) { $t = $(this); ch = c.headers[index]; c.headerContent[index] = this.innerHTML; // save original header content @@ -393,28 +398,45 @@ this.column = header_index[this.parentNode.rowIndex + "-" + this.cellIndex]; this.order = formatSortingOrder( ts.getData($t, ch, 'sortInitialOrder') || c.sortInitialOrder ) ? [1,0,2] : [0,1,2]; this.count = -1; // set to -1 because clicking on the header automatically adds one - if (ts.getData($t, ch, 'sorter') === 'false') { - this.sortDisabled = true; - $t.addClass('sorter-false'); - } else { - $t.removeClass('sorter-false'); - } this.lockedOrder = false; lock = ts.getData($t, ch, 'lockedOrder') || false; - if (typeof(lock) !== 'undefined' && lock !== false) { + if (typeof lock !== 'undefined' && lock !== false) { this.order = this.lockedOrder = formatSortingOrder(lock) ? [1,1,1] : [0,0,0]; } - $t.addClass( (this.sortDisabled ? 'sorter-false ' : ' ') + c.cssHeader ); + $t.addClass(c.cssHeader); // add cell to headerList c.headerList[index] = this; // add to parent in case there are multiple rows $t.parent().addClass(c.cssHeaderRow); + // allow keyboard cursor to focus on element + $t.attr("tabindex", 0); }); - if (table.config.debug) { + // enable/disable sorting + updateHeader(table); + if (c.debug) { benchmark("Built headers:", time); - log($tableHeaders); + log(c.$headers); } - return $tableHeaders; + } + + function commonUpdate(table, resort, callback) { + var c = table.config; + // remove rows/elements before update + c.$table.find(c.selectorRemove).remove(); + // rebuild parsers + buildParserCache(table); + // rebuild the cache map + buildCache(table); + checkResort(c.$table, resort, callback); + } + + function updateHeader(table) { + var s, c = table.config; + c.$headers.each(function(index, th){ + s = ts.getData( th, c.headers[index], 'sorter' ) === 'false'; + th.sortDisabled = s; + $(th)[ s ? 'addClass' : 'removeClass' ]('sorter-false'); + }); } function setHeadersCss(table) { @@ -449,23 +471,13 @@ // automatically add col group, and column sizes if set function fixColumnWidth(table) { - var $c, c = table.config, - $cg = $('<colgroup>'), - $cgo = c.$table.find('colgroup'), - n = c.columns.length, - overallWidth = c.$table.width(); - $("tr:first td", table.tBodies[0]).each(function(i) { - $c = $('<col>'); - if (c.widthFixed) { - $c.css('width', parseInt(($(this).width()/overallWidth)*1000, 10)/10 + '%'); - } - $cg.append($c); - }); - // replace colgroup contents - if ($cgo.length) { - $cgo.html( $cg.html() ); - } else { - c.$table.prepend( $cg ); + if (table.config.widthFixed && $(table).find('colgroup').length === 0) { + var colgroup = $('<colgroup>'), + overallWidth = $(table).width(); + $(table.tBodies[0]).find("tr:first").children("td").each(function() { + colgroup.append($('<col>').css('width', parseInt(($(this).width()/overallWidth)*1000, 10)/10 + '%')); + }); + $(table).prepend(colgroup); } } @@ -490,11 +502,110 @@ return (parsers && parsers[i]) ? parsers[i].type || '' : ''; } + function initSort(table, cell, e){ + var a, i, j, o, s, + c = table.config, + k = !e[c.sortMultiSortKey], + $this = $(table); + // Only call sortStart if sorting is enabled + $this.trigger("sortStart", table); + // get current column sort order + cell.count = e[c.sortResetKey] ? 2 : (cell.count + 1) % (c.sortReset ? 3 : 2); + // reset all sorts on non-current column - issue #30 + if (c.sortRestart) { + i = cell; + c.$headers.each(function() { + // only reset counts on columns that weren't just clicked on and if not included in a multisort + if (this !== i && (k || !$(this).is('.' + c.cssDesc + ',.' + c.cssAsc))) { + this.count = -1; + } + }); + } + // get current column index + i = cell.column; + // user only wants to sort on one column + if (k) { + // flush the sort list + c.sortList = []; + if (c.sortForce !== null) { + a = c.sortForce; + for (j = 0; j < a.length; j++) { + if (a[j][0] !== i) { + c.sortList.push(a[j]); + } + } + } + // add column to sort list + o = cell.order[cell.count]; + if (o < 2) { + c.sortList.push([i, o]); + // add other columns if header spans across multiple + if (cell.colSpan > 1) { + for (j = 1; j < cell.colSpan; j++) { + c.sortList.push([i + j, o]); + } + } + } + // multi column sorting + } else { + // get rid of the sortAppend before adding more - fixes issue #115 + if (c.sortAppend && c.sortList.length > 1) { + if (ts.isValueInArray(c.sortAppend[0][0], c.sortList)) { + c.sortList.pop(); + } + } + // the user has clicked on an already sorted column + if (ts.isValueInArray(i, c.sortList)) { + // reverse the sorting direction for all tables + for (j = 0; j < c.sortList.length; j++) { + s = c.sortList[j]; + o = c.headerList[s[0]]; + if (s[0] === i) { + s[1] = o.order[o.count]; + if (s[1] === 2) { + c.sortList.splice(j,1); + o.count = -1; + } + } + } + } else { + // add column to sort list array + o = cell.order[cell.count]; + if (o < 2) { + c.sortList.push([i, o]); + // add other columns if header spans across multiple + if (cell.colSpan > 1) { + for (j = 1; j < cell.colSpan; j++) { + c.sortList.push([i + j, o]); + } + } + } + } + } + if (c.sortAppend !== null) { + a = c.sortAppend; + for (j = 0; j < a.length; j++) { + if (a[j][0] !== i) { + c.sortList.push(a[j]); + } + } + } + // sortBegin event triggered immediately before the sort + $this.trigger("sortBegin", table); + // setTimeout needed so the processing icon shows up + setTimeout(function(){ + // set css for headers + setHeadersCss(table); + multisort(table); + appendToTable(table); + }, 1); + } + // sort multiple columns function multisort(table) { /*jshint loopfunc:true */ - var dynamicExp, sortWrapper, col, mx = 0, dir = 0, tc = table.config, + var dir = 0, tc = table.config, sortList = tc.sortList, l = sortList.length, bl = table.tBodies.length, - sortTime, i, j, k, c, colMax, cache, lc, s, e, order, orgOrderCol; + sortTime, i, k, c, colMax, cache, lc, s, order, orgOrderCol; if (tc.serverSideSorting || !tc.cache[0]) { // empty table - fixes #206 return; } @@ -537,7 +648,9 @@ } function checkResort($table, flag, callback) { - if (flag !== false) { + // don't try to resort if the table is still processing + // this will catch spamming of the updateCell method + if (flag !== false && !$table[0].isProcessing) { $table.trigger("sorton", [$table[0].config.sortList, function(){ resortComplete($table, callback); }]); @@ -546,26 +659,177 @@ } } + function bindEvents(table){ + var c = table.config, + $this = c.$table, + j, downTime; + // apply event handling to headers + c.$headers + // http://stackoverflow.com/questions/5312849/jquery-find-self; + .find(c.selectorSort).add( c.$headers.filter(c.selectorSort) ) + .unbind('mousedown.tablesorter mouseup.tablesorter sort.tablesorter keypress.tablesorter') + .bind('mousedown.tablesorter mouseup.tablesorter sort.tablesorter keypress.tablesorter', function(e, external) { + // only recognize left clicks or enter + if ( ((e.which || e.button) !== 1 && !/sort|keypress/.test(e.type)) || (e.type === 'keypress' && e.which !== 13) ) { + return false; + } + // ignore long clicks (prevents resizable widget from initializing a sort) + if (e.type === 'mouseup' && external !== true && (new Date().getTime() - downTime > 250)) { return false; } + // set timer on mousedown + if (e.type === 'mousedown') { + downTime = new Date().getTime(); + return e.target.tagName === "INPUT" ? '' : !c.cancelSelection; + } + if (c.delayInit && !c.cache) { buildCache(table); } + // jQuery v1.2.6 doesn't have closest() + var $cell = /TH|TD/.test(this.tagName) ? $(this) : $(this).parents('th, td').filter(':first'), cell = $cell[0]; + if (!cell.sortDisabled) { + initSort(table, cell, e); + } + }); + if (c.cancelSelection) { + // cancel selection + c.$headers + .attr('unselectable', 'on') + .bind('selectstart', false) + .css({ + 'user-select': 'none', + 'MozUserSelect': 'none' // not needed for jQuery 1.8+ + }); + } + // apply easy methods that trigger bound events + $this + .unbind('sortReset update updateRows updateCell updateAll addRows sorton appendCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave '.split(' ').join('.tablesorter ')) + .bind("sortReset.tablesorter", function(e){ + e.stopPropagation(); + c.sortList = []; + setHeadersCss(table); + multisort(table); + appendToTable(table); + }) + .bind("updateAll.tablesorter", function(e, resort, callback){ + e.stopPropagation(); + ts.refreshWidgets(table, true, true); + ts.restoreHeaders(table); + buildHeaders(table); + bindEvents(table); + commonUpdate(table, resort, callback); + }) + .bind("update.tablesorter updateRows.tablesorter", function(e, resort, callback) { + e.stopPropagation(); + // update sorting (if enabled/disabled) + updateHeader(table); + commonUpdate(table, resort, callback); + }) + .bind("updateCell.tablesorter", function(e, cell, resort, callback) { + e.stopPropagation(); + $this.find(c.selectorRemove).remove(); + // get position from the dom + var l, row, icell, + $tb = $this.find('tbody'), + // update cache - format: function(s, table, cell, cellIndex) + // no closest in jQuery v1.2.6 - tbdy = $tb.index( $(cell).closest('tbody') ),$row = $(cell).closest('tr'); + tbdy = $tb.index( $(cell).parents('tbody').filter(':first') ), + $row = $(cell).parents('tr').filter(':first'); + cell = $(cell)[0]; // in case cell is a jQuery object + // tbody may not exist if update is initialized while tbody is removed for processing + if ($tb.length && tbdy >= 0) { + row = $tb.eq(tbdy).find('tr').index( $row ); + icell = cell.cellIndex; + l = c.cache[tbdy].normalized[row].length - 1; + c.cache[tbdy].row[table.config.cache[tbdy].normalized[row][l]] = $row; + c.cache[tbdy].normalized[row][icell] = c.parsers[icell].format( getElementText(table, cell, icell), table, cell, icell ); + checkResort($this, resort, callback); + } + }) + .bind("addRows.tablesorter", function(e, $row, resort, callback) { + e.stopPropagation(); + var i, rows = $row.filter('tr').length, + dat = [], l = $row[0].cells.length, + tbdy = $this.find('tbody').index( $row.parents('tbody').filter(':first') ); + // fixes adding rows to an empty table - see issue #179 + if (!c.parsers) { + buildParserCache(table); + } + // add each row + for (i = 0; i < rows; i++) { + // add each cell + for (j = 0; j < l; j++) { + dat[j] = c.parsers[j].format( getElementText(table, $row[i].cells[j], j), table, $row[i].cells[j], j ); + } + // add the row index to the end + dat.push(c.cache[tbdy].row.length); + // update cache + c.cache[tbdy].row.push([$row[i]]); + c.cache[tbdy].normalized.push(dat); + dat = []; + } + // resort using current settings + checkResort($this, resort, callback); + }) + .bind("sorton.tablesorter", function(e, list, callback, init) { + e.stopPropagation(); + $this.trigger("sortStart", this); + // update header count index + updateHeaderSortCount(table, list); + // set css for headers + setHeadersCss(table); + $this.trigger("sortBegin", this); + // sort the table and append it to the dom + multisort(table); + appendToTable(table, init); + if (typeof callback === "function") { + callback(table); + } + }) + .bind("appendCache.tablesorter", function(e, callback, init) { + e.stopPropagation(); + appendToTable(table, init); + if (typeof callback === "function") { + callback(table); + } + }) + .bind("applyWidgetId.tablesorter", function(e, id) { + e.stopPropagation(); + ts.getWidgetById(id).format(table, c, c.widgetOptions); + }) + .bind("applyWidgets.tablesorter", function(e, init) { + e.stopPropagation(); + // apply widgets + ts.applyWidget(table, init); + }) + .bind("refreshWidgets.tablesorter", function(e, all, dontapply){ + e.stopPropagation(); + ts.refreshWidgets(table, all, dontapply); + }) + .bind("destroy.tablesorter", function(e, c, cb){ + e.stopPropagation(); + ts.destroy(table, c, cb); + }); + } + /* public methods */ ts.construct = function(settings) { return this.each(function() { // if no thead or tbody, or tablesorter is already present, quit if (!this.tHead || this.tBodies.length === 0 || this.hasInitialized === true) { - return (this.config.debug) ? log('stopping initialization! No thead, tbody or tablesorter has already been initialized') : ''; + return (this.config && this.config.debug) ? log('stopping initialization! No thead, tbody or tablesorter has already been initialized') : ''; } // declare - var $cell, $this = $(this), $t0 = this, - c, i, j, k = '', a, s, o, downTime, + var $this = $(this), table = this, + c, k = '', m = $.metadata; // initialization flag - $t0.hasInitialized = false; + table.hasInitialized = false; + // table is being processed flag + table.isProcessing = true; // new blank config object - $t0.config = {}; + table.config = {}; // merge and extend - c = $.extend(true, $t0.config, ts.defaults, settings); + c = $.extend(true, table.config, ts.defaults, settings); // save the settings where they read - $.data($t0, "tablesorter", c); - if (c.debug) { $.data( $t0, 'startoveralltimer', new Date()); } + $.data(table, "tablesorter", c); + if (c.debug) { $.data( table, 'startoveralltimer', new Date()); } // constants c.supportsTextContent = $('<span>x</span>')[0].textContent === 'x'; c.supportsDataObject = parseFloat($.fn.jquery) >= 1.4; @@ -578,234 +842,17 @@ c.$table = $this.addClass(c.tableClass + k); c.$tbodies = $this.children('tbody:not(.' + c.cssInfoBlock + ')'); // build headers - c.$headers = buildHeaders($t0); + buildHeaders(table); // fixate columns if the users supplies the fixedWidth option // do this after theme has been applied - fixColumnWidth($t0); + fixColumnWidth(table); // try to auto detect column type, and store in tables config - c.parsers = buildParserCache($t0); + buildParserCache(table); // build the cache for the tbody cells // delayInit will delay building the cache until the user starts a sort - if (!c.delayInit) { buildCache($t0); } - // apply event handling to headers - // this is to big, perhaps break it out? - c.$headers - // http://stackoverflow.com/questions/5312849/jquery-find-self - .find('*').andSelf().filter(c.selectorSort) - .unbind('mousedown.tablesorter mouseup.tablesorter') - .bind('mousedown.tablesorter mouseup.tablesorter', function(e, external) { - // jQuery v1.2.6 doesn't have closest() - var $cell = this.tagName.match('TH|TD') ? $(this) : $(this).parents('th, td').filter(':last'), cell = $cell[0]; - // only recognize left clicks - if ((e.which || e.button) !== 1) { return false; } - // set timer on mousedown - if (e.type === 'mousedown') { - downTime = new Date().getTime(); - return e.target.tagName === "INPUT" ? '' : !c.cancelSelection; - } - // ignore long clicks (prevents resizable widget from initializing a sort) - if (external !== true && (new Date().getTime() - downTime > 250)) { return false; } - if (c.delayInit && !c.cache) { buildCache($t0); } - if (!cell.sortDisabled) { - // Only call sortStart if sorting is enabled - $this.trigger("sortStart", $t0); - // store exp, for speed - // $cell = $(this); - k = !e[c.sortMultiSortKey]; - // get current column sort order - cell.count = e[c.sortResetKey] ? 2 : (cell.count + 1) % (c.sortReset ? 3 : 2); - // reset all sorts on non-current column - issue #30 - if (c.sortRestart) { - i = cell; - c.$headers.each(function() { - // only reset counts on columns that weren't just clicked on and if not included in a multisort - if (this !== i && (k || !$(this).is('.' + c.cssDesc + ',.' + c.cssAsc))) { - this.count = -1; - } - }); - } - // get current column index - i = cell.column; - // user only wants to sort on one column - if (k) { - // flush the sort list - c.sortList = []; - if (c.sortForce !== null) { - a = c.sortForce; - for (j = 0; j < a.length; j++) { - if (a[j][0] !== i) { - c.sortList.push(a[j]); - } - } - } - // add column to sort list - o = cell.order[cell.count]; - if (o < 2) { - c.sortList.push([i, o]); - // add other columns if header spans across multiple - if (cell.colSpan > 1) { - for (j = 1; j < cell.colSpan; j++) { - c.sortList.push([i + j, o]); - } - } - } - // multi column sorting - } else { - // get rid of the sortAppend before adding more - fixes issue #115 - if (c.sortAppend && c.sortList.length > 1) { - if (ts.isValueInArray(c.sortAppend[0][0], c.sortList)) { - c.sortList.pop(); - } - } - // the user has clicked on an already sorted column - if (ts.isValueInArray(i, c.sortList)) { - // reverse the sorting direction for all tables - for (j = 0; j < c.sortList.length; j++) { - s = c.sortList[j]; - o = c.headerList[s[0]]; - if (s[0] === i) { - s[1] = o.order[o.count]; - if (s[1] === 2) { - c.sortList.splice(j,1); - o.count = -1; - } - } - } - } else { - // add column to sort list array - o = cell.order[cell.count]; - if (o < 2) { - c.sortList.push([i, o]); - // add other columns if header spans across multiple - if (cell.colSpan > 1) { - for (j = 1; j < cell.colSpan; j++) { - c.sortList.push([i + j, o]); - } - } - } - } - } - if (c.sortAppend !== null) { - a = c.sortAppend; - for (j = 0; j < a.length; j++) { - if (a[j][0] !== i) { - c.sortList.push(a[j]); - } - } - } - // sortBegin event triggered immediately before the sort - $this.trigger("sortBegin", $t0); - // setTimeout needed so the processing icon shows up - setTimeout(function(){ - // set css for headers - setHeadersCss($t0); - multisort($t0); - appendToTable($t0); - }, 1); - } - }); - if (c.cancelSelection) { - // cancel selection - c.$headers.each(function() { - this.onselectstart = function() { - return false; - }; - }); - } - // apply easy methods that trigger binded events - $this - .unbind('sortReset update updateCell addRows sorton appendCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave') - .bind("sortReset", function(){ - c.sortList = []; - setHeadersCss($t0); - multisort($t0); - appendToTable($t0); - }) - .bind("update updateRows", function(e, resort, callback) { - // remove rows/elements before update - $(c.selectorRemove, $t0).remove(); - // rebuild parsers - c.parsers = buildParserCache($t0); - // rebuild the cache map - buildCache($t0); - checkResort($this, resort, callback); - }) - .bind("updateCell", function(e, cell, resort, callback) { - // get position from the dom - var l, row, icell, - $tb = $this.find('tbody'), - // update cache - format: function(s, table, cell, cellIndex) - // no closest in jQuery v1.2.6 - tbdy = $tb.index( $(cell).closest('tbody') ),$row = $(cell).closest('tr'); - tbdy = $tb.index( $(cell).parents('tbody').filter(':last') ), - $row = $(cell).parents('tr').filter(':last'); - cell = $(cell)[0]; // in case cell is a jQuery object - // tbody may not exist if update is initialized while tbody is removed for processing - if ($tb.length && tbdy >= 0) { - row = $tb.eq(tbdy).find('tr').index( $row ); - icell = cell.cellIndex; - l = $t0.config.cache[tbdy].normalized[row].length - 1; - $t0.config.cache[tbdy].row[$t0.config.cache[tbdy].normalized[row][l]] = $row; - $t0.config.cache[tbdy].normalized[row][icell] = c.parsers[icell].format( getElementText($t0, cell, icell), $t0, cell, icell ); - checkResort($this, resort, callback); - } - }) - .bind("addRows", function(e, $row, resort, callback) { - var i, rows = $row.filter('tr').length, - dat = [], l = $row[0].cells.length, - tbdy = $this.find('tbody').index( $row.closest('tbody') ); - // fixes adding rows to an empty table - see issue #179 - if (!c.parsers) { - c.parsers = buildParserCache($t0); - } - // add each row - for (i = 0; i < rows; i++) { - // add each cell - for (j = 0; j < l; j++) { - dat[j] = c.parsers[j].format( getElementText($t0, $row[i].cells[j], j), $t0, $row[i].cells[j], j ); - } - // add the row index to the end - dat.push(c.cache[tbdy].row.length); - // update cache - c.cache[tbdy].row.push([$row[i]]); - c.cache[tbdy].normalized.push(dat); - dat = []; - } - // resort using current settings - checkResort($this, resort, callback); - }) - .bind("sorton", function(e, list, callback, init) { - $this.trigger("sortStart", this); - // update header count index - updateHeaderSortCount($t0, list); - // set css for headers - setHeadersCss($t0); - // sort the table and append it to the dom - multisort($t0); - appendToTable($t0, init); - if (typeof callback === "function") { - callback($t0); - } - }) - .bind("appendCache", function(e, callback, init) { - appendToTable($t0, init); - if (typeof callback === "function") { - callback($t0); - } - }) - .bind("applyWidgetId", function(e, id) { - ts.getWidgetById(id).format($t0, c, c.widgetOptions); - }) - .bind("applyWidgets", function(e, init) { - // apply widgets - ts.applyWidget($t0, init); - }) - .bind("refreshWidgets", function(e, all, dontapply){ - ts.refreshWidgets($t0, all, dontapply); - }) - .bind("destroy", function(e, c, cb){ - ts.destroy($t0, c, cb); - }); - + if (!c.delayInit) { buildCache(table); } + // bind all header events and methods + bindEvents(table); // get sort list from jQuery data or metadata // in jQuery < 1.4, an error occurs when calling $this.data() if (c.supportsDataObject && typeof $this.data().sortlist !== 'undefined') { @@ -814,40 +861,42 @@ c.sortList = $this.metadata().sortlist; } // apply widget init code - ts.applyWidget($t0, true); + ts.applyWidget(table, true); // if user has supplied a sort list to constructor if (c.sortList.length > 0) { $this.trigger("sorton", [c.sortList, {}, !c.initWidgets]); } else if (c.initWidgets) { // apply widget format - ts.applyWidget($t0); + ts.applyWidget(table); } // show processesing icon if (c.showProcessing) { $this - .unbind('sortBegin sortEnd') - .bind('sortBegin sortEnd', function(e) { - ts.isProcessing($t0, e.type === 'sortBegin'); + .unbind('sortBegin.tablesorter sortEnd.tablesorter') + .bind('sortBegin.tablesorter sortEnd.tablesorter', function(e) { + ts.isProcessing(table, e.type === 'sortBegin'); }); } // initialized - $t0.hasInitialized = true; + table.hasInitialized = true; + table.isProcessing = false; if (c.debug) { - ts.benchmark("Overall initialization time", $.data( $t0, 'startoveralltimer')); + ts.benchmark("Overall initialization time", $.data( table, 'startoveralltimer')); } - $this.trigger('tablesorter-initialized', $t0); - if (typeof c.initialized === 'function') { c.initialized($t0); } + $this.trigger('tablesorter-initialized', table); + if (typeof c.initialized === 'function') { c.initialized(table); } }); }; // *** Process table *** // add processing indicator ts.isProcessing = function(table, toggle, $ths) { - var c = table.config, + table = $(table); + var c = table[0].config, // default to all headers - $h = $ths || $(table).find('.' + c.cssHeader); + $h = $ths || table.find('.' + c.cssHeader); if (toggle) { if (c.sortList.length > 0) { // get headers from the sortList @@ -865,8 +914,9 @@ // detach tbody but save the position // don't use tbody because there are portions that look for a tbody index (updateCell) ts.processTbody = function(table, $tb, getIt){ - var t, holdr; + var holdr; if (getIt) { + table.isProcessing = true; $tb.before('<span class="tablesorter-savemyplace"/>'); holdr = ($.fn.detach) ? $tb.detach() : $tb.remove(); return holdr; @@ -874,13 +924,28 @@ holdr = $(table).find('span.tablesorter-savemyplace'); $tb.insertAfter( holdr ); holdr.remove(); + table.isProcessing = false; }; ts.clearTableBody = function(table) { - table.config.$tbodies.empty(); + $(table)[0].config.$tbodies.empty(); + }; + + // restore headers + ts.restoreHeaders = function(table){ + var c = table.config; + // don't use c.$headers here in case header cells were swapped + c.$table.find(c.selectorHeaders).each(function(i){ + // only restore header cells if it is wrapped + // because this is also used by the updateAll method + if ($(this).find('.tablesorter-header-inner').length){ + $(this).html( c.headerContent[i] ); + } + }); }; ts.destroy = function(table, removeClasses, callback){ + table = $(table)[0]; if (!table.hasInitialized) { return; } // remove all widgets ts.refreshWidgets(table, true, true); @@ -893,15 +958,12 @@ // disable tablesorter $t .removeData('tablesorter') - .unbind('sortReset update updateCell addRows sorton appendCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave'); + .unbind('sortReset update updateAll updateRows updateCell addRows sorton appendCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress sortBegin sortEnd '.split(' ').join('.tablesorter ')); c.$headers.add($f) .removeClass(c.cssHeader + ' ' + c.cssAsc + ' ' + c.cssDesc) .removeAttr('data-column'); - $r.find(c.selectorSort).unbind('mousedown.tablesorter mouseup.tablesorter'); - // restore headers - $r.children().each(function(i){ - $(this).html( c.headerContent[i] ); - }); + $r.find(c.selectorSort).unbind('mousedown.tablesorter mouseup.tablesorter keypress.tablesorter'); + ts.restoreHeaders(table); if (removeClasses !== false) { $t.removeClass(c.tableClass + ' tablesorter-' + c.theme); } @@ -915,7 +977,7 @@ // *** sort functions *** // regex used in natural sort ts.regex = [ - /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi, // chunk/tokenize numbers & letters + /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi, // chunk/tokenize numbers & letters /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/, //date /^0x[0-9a-f]+$/i // hex ]; @@ -925,8 +987,8 @@ if (a === b) { return 0; } var c = table.config, e = c.string[ (c.empties[col] || c.emptyTo ) ], r = ts.regex, xN, xD, yN, yD, xF, yF, i, mx; - if (a === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? -1 : 1) : -e || -1; } - if (b === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? 1 : -1) : e || 1; } + if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : -e || -1; } + if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : e || 1; } if (typeof c.textSorter === 'function') { return c.textSorter(a, b, table, col); } // chunk/tokenize xN = a.replace(r[0], '\\0$1\\0').replace(/\\0$/, '').replace(/^\\0/, '').split('\\0'); @@ -961,8 +1023,8 @@ ts.sortTextDesc = function(table, a, b, col) { if (a === b) { return 0; } var c = table.config, e = c.string[ (c.empties[col] || c.emptyTo ) ]; - if (a === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? -1 : 1) : e || 1; } - if (b === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? 1 : -1) : -e || -1; } + if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : e || 1; } + if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : -e || -1; } if (typeof c.textSorter === 'function') { return c.textSorter(b, a, table, col); } return ts.sortText(table, b, a); }; @@ -973,7 +1035,7 @@ ts.getTextValue = function(a, mx, d) { if (mx) { // make sure the text value is greater than the max numerical value (mx) - var i, l = a.length, n = mx + d; + var i, l = a ? a.length : 0, n = mx + d; for (i = 0; i < l; i++) { n += a.charCodeAt(i); } @@ -985,8 +1047,8 @@ ts.sortNumeric = function(table, a, b, col, mx, d) { if (a === b) { return 0; } var c = table.config, e = c.string[ (c.empties[col] || c.emptyTo ) ]; - if (a === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? -1 : 1) : -e || -1; } - if (b === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? 1 : -1) : e || 1; } + if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : -e || -1; } + if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : e || 1; } if (isNaN(a)) { a = ts.getTextValue(a, mx, d); } if (isNaN(b)) { b = ts.getTextValue(b, mx, d); } return a - b; @@ -995,8 +1057,8 @@ ts.sortNumericDesc = function(table, a, b, col, mx, d) { if (a === b) { return 0; } var c = table.config, e = c.string[ (c.empties[col] || c.emptyTo ) ]; - if (a === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? -1 : 1) : e || 1; } - if (b === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? 1 : -1) : -e || -1; } + if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : e || 1; } + if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : -e || -1; } if (isNaN(a)) { a = ts.getTextValue(a, mx, d); } if (isNaN(b)) { b = ts.getTextValue(b, mx, d); } return b - a; @@ -1089,36 +1151,55 @@ }; ts.applyWidget = function(table, init) { + table = $(table)[0]; // in case this is called externally var c = table.config, wo = c.widgetOptions, - ws = c.widgets.sort().reverse(), // ensure that widgets are always applied in a certain order - time, i, w, l = ws.length; - // make zebra last - i = $.inArray('zebra', c.widgets); - if (i >= 0) { - c.widgets.splice(i,1); - c.widgets.push('zebra'); - } - if (c.debug) { - time = new Date(); - } - // add selected widgets - for (i = 0; i < l; i++) { - w = ts.getWidgetById(ws[i]); - if ( w ) { - if (init === true && w.hasOwnProperty('init')) { - w.init(table, w, c, wo); - } else if (!init && w.hasOwnProperty('format')) { - w.format(table, c, wo); + widgets = [], + time, i, w, wd; + if (c.debug) { time = new Date(); } + if (c.widgets.length) { + // ensure unique widget ids + c.widgets = $.grep(c.widgets, function(v, k){ + return $.inArray(v, c.widgets) === k; + }); + // build widget array & add priority as needed + $.each(c.widgets || [], function(i,n){ + wd = ts.getWidgetById(n); + if (wd && wd.id) { + // set priority to 10 if not defined + if (!wd.priority) { wd.priority = 10; } + widgets[i] = wd; } - } + }); + // sort widgets by priority + widgets.sort(function(a, b){ + return a.priority < b.priority ? -1 : a.priority === b.priority ? 0 : 1; + }); + + // add/update selected widgets + $.each(widgets, function(i,w){ + if (w) { + if (init) { + if (w.hasOwnProperty('options')) { + wo = table.config.widgetOptions = $.extend( true, {}, w.options, wo ); + } + if (w.hasOwnProperty('init')) { + w.init(table, w, c, wo); + } + } else if (!init && w.hasOwnProperty('format')) { + w.format(table, c, wo, false); + } + } + }); } if (c.debug) { - benchmark("Completed " + (init === true ? "initializing" : "applying") + " widgets", time); + w = c.widgets.length; + benchmark("Completed " + (init === true ? "initializing " : "applying ") + w + " widget" + (w !== 1 ? "s" : ""), time); } }; ts.refreshWidgets = function(table, doAll, dontapply) { + table = $(table)[0]; // see issue #243 var i, c = table.config, cw = c.widgets, w = ts.widgets, l = w.length; @@ -1151,14 +1232,14 @@ } else if (ch && typeof ch[key] !== 'undefined') { val += ch[key]; } else if (cl !== ' ' && cl.match(' ' + key + '-')) { - // include sorter class name "sorter-text", etc - val = cl.match( new RegExp(' ' + key + '-(\\w+)') )[1] || ''; + // include sorter class name "sorter-text", etc; now works with "sorter-my-custom-parser" + val = cl.match( new RegExp('\\s' + key + '-([\\w-]+)') )[1] || ''; } return $.trim(val); }; ts.formatFloat = function(s, table) { - if (typeof(s) !== 'string' || s === '') { return s; } + if (typeof s !== 'string' || s === '') { return s; } // allow using formatFloat without a table; defaults to US number format var i, t = table && table.config ? table.config.usNumberFormat !== false : @@ -1199,24 +1280,40 @@ // add default parsers ts.addParser({ id: "text", - is: function(s, table, node) { + is: function() { return true; }, - format: function(s, table, cell, cellIndex) { + format: function(s, table) { var c = table.config; - s = $.trim( c.ignoreCase ? s.toLocaleLowerCase() : s ); - return c.sortLocaleCompare ? ts.replaceAccents(s) : s; + if (s) { + s = $.trim( c.ignoreCase ? s.toLocaleLowerCase() : s ); + s = c.sortLocaleCompare ? ts.replaceAccents(s) : s; + } + return s; }, type: "text" }); + ts.addParser({ + id: "digit", + is: function(s) { + return ts.isDigit(s); + }, + format: function(s, table) { + var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ""), table); + return s && typeof n === 'number' ? n : s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; + }, + type: "numeric" + }); + ts.addParser({ id: "currency", is: function(s) { - return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test(s); // £$€¤¥¢ + return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test((s || '').replace(/[,. ]/g,'')); // £$€¤¥¢ }, format: function(s, table) { - return ts.formatFloat(s.replace(/[^\w,. \-()]/g, ""), table); + var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ""), table); + return s && typeof n === 'number' ? n : s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; }, type: "numeric" }); @@ -1227,13 +1324,13 @@ return (/^\d{1,3}[\.]\d{1,3}[\.]\d{1,3}[\.]\d{1,3}$/).test(s); }, format: function(s, table) { - var i, a = s.split("."), + var i, a = s ? s.split(".") : '', r = "", l = a.length; for (i = 0; i < l; i++) { r += ("00" + a[i]).slice(-3); } - return ts.formatFloat(r, table); + return s ? ts.formatFloat(r, table) : s; }, type: "numeric" }); @@ -1244,7 +1341,7 @@ return (/^(https?|ftp|file):\/\//).test(s); }, format: function(s) { - return $.trim(s.replace(/(https?|ftp|file):\/\//, '')); + return s ? $.trim(s.replace(/(https?|ftp|file):\/\//, '')) : s; }, type: "text" }); @@ -1255,7 +1352,7 @@ return (/^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}/).test(s); }, format: function(s, table) { - return ts.formatFloat((s !== "") ? (new Date(s.replace(/-/g, "/")).getTime() || "") : "", table); + return s ? ts.formatFloat((s !== "") ? (new Date(s.replace(/-/g, "/")).getTime() || "") : "", table) : s; }, type: "numeric" }); @@ -1263,10 +1360,10 @@ ts.addParser({ id: "percent", is: function(s) { - return (/(\d\s?%|%\s?\d)/).test(s); + return (/(\d\s*?%|%\s*?\d)/).test(s) && s.length < 15; }, format: function(s, table) { - return ts.formatFloat(s.replace(/%/g, ""), table); + return s ? ts.formatFloat(s.replace(/%/g, ""), table) : s; }, type: "numeric" }); @@ -1279,7 +1376,7 @@ return (/^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i).test(s) || (/^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i).test(s); }, format: function(s, table) { - return ts.formatFloat( (new Date(s.replace(/(\S)([AP]M)$/i, "$1 $2")).getTime() || ''), table); + return s ? ts.formatFloat( (new Date(s.replace(/(\S)([AP]M)$/i, "$1 $2")).getTime() || ''), table) : s; }, type: "numeric" }); @@ -1287,25 +1384,23 @@ ts.addParser({ id: "shortDate", // "mmddyyyy", "ddmmyyyy" or "yyyymmdd" is: function(s) { - // testing for ####-##-####, so it's not perfect - return (/^(\d{1,2}|\d{4})[\/\-\,\.\s+]\d{1,2}[\/\-\.\,\s+](\d{1,2}|\d{4})$/).test(s); + // testing for ##-##-#### or ####-##-##, so it's not perfect; time can be included + return (/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/).test((s || '').replace(/\s+/g," ").replace(/[\-.,]/g, "/")); }, format: function(s, table, cell, cellIndex) { - var c = table.config, ci = c.headerList[cellIndex], - format = ci.shortDateFormat; - if (typeof format === 'undefined') { - // cache header formatting so it doesn't getData for every cell in the column - format = ci.shortDateFormat = ts.getData( ci, c.headers[cellIndex], 'dateFormat') || c.dateFormat; - } - s = s.replace(/\s+/g," ").replace(/[\-|\.|\,]/g, "/"); - if (format === "mmddyyyy") { - s = s.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$3/$1/$2"); - } else if (format === "ddmmyyyy") { - s = s.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$3/$2/$1"); - } else if (format === "yyyymmdd") { - s = s.replace(/(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/, "$1/$2/$3"); + if (s) { + var c = table.config, ci = c.headerList[cellIndex], + format = ci.dateFormat || ts.getData( ci, c.headers[cellIndex], 'dateFormat') || c.dateFormat; + s = s.replace(/\s+/g," ").replace(/[\-.,]/g, "/"); // escaped - because JSHint in Firefox was showing it as an error + if (format === "mmddyyyy") { + s = s.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$3/$1/$2"); + } else if (format === "ddmmyyyy") { + s = s.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$3/$2/$1"); + } else if (format === "yyyymmdd") { + s = s.replace(/(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/, "$1/$2/$3"); + } } - return ts.formatFloat( (new Date(s).getTime() || ''), table); + return s ? ts.formatFloat( (new Date(s).getTime() || ''), table) : s; }, type: "numeric" }); @@ -1316,25 +1411,14 @@ return (/^(([0-2]?\d:[0-5]\d)|([0-1]?\d:[0-5]\d\s?([AP]M)))$/i).test(s); }, format: function(s, table) { - return ts.formatFloat( (new Date("2000/01/01 " + s.replace(/(\S)([AP]M)$/i, "$1 $2")).getTime() || ""), table); - }, - type: "numeric" - }); - - ts.addParser({ - id: "digit", - is: function(s) { - return ts.isDigit(s); - }, - format: function(s, table) { - return ts.formatFloat(s.replace(/[^\w,. \-()]/g, ""), table); + return s ? ts.formatFloat( (new Date("2000/01/01 " + s.replace(/(\S)([AP]M)$/i, "$1 $2")).getTime() || ""), table) : s; }, type: "numeric" }); ts.addParser({ id: "metadata", - is: function(s) { + is: function() { return false; }, format: function(s, table, cell) { @@ -1348,6 +1432,7 @@ // add default widgets ts.addWidget({ id: "zebra", + priority: 90, format: function(table, c, wo) { var $tb, $tv, $tr, row, even, time, k, l, child = new RegExp(c.cssChildRow, 'i'), @@ -1380,7 +1465,7 @@ remove: function(table, c, wo){ var k, $tb, b = c.$tbodies, - rmv = (c.widgetOptions.zebra || [ "even", "odd" ]).join(' '); + rmv = (wo.zebra || [ "even", "odd" ]).join(' '); for (k = 0; k < b.length; k++ ){ $tb = $.tablesorter.processTbody(table, b.eq(k), true); // remove tbody $tb.children().removeClass(rmv); @@ -1389,4 +1474,4 @@ } }); -})(jQuery); \ No newline at end of file +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js new file mode 100644 index 0000000..29d96d7 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js @@ -0,0 +1,924 @@ +/*! Filter widget formatter functions - updated 6/4/2013 + * requires: tableSorter 2.7.7+ and jQuery 1.4.3+ + * + * uiSpinner (jQuery UI spinner) + * uiSlider (jQuery UI slider) + * uiRange (jQuery UI range slider) + * uiDateCompare (jQuery UI datepicker+compare selector; 1 input) + * uiDatepicker (jQuery UI datepicker; 2 inputs, filter range) + * html5Number (spinner+compare selector) + * html5Range (slider) + * html5Color (color) + */ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;(function($){ +"use strict"; +$.tablesorter = $.tablesorter || {}; + +$.tablesorter.filterFormatter = { + + /**********************\ + jQuery UI Spinner + \**********************/ + uiSpinner: function($cell, indx, spinnerDef) { + var o = $.extend({ + min : 0, + max : 100, + step : 1, + value : 1, + delayed : true, + addToggle : true, + disabled : false, + exactMatch : true, + compare : '' + }, spinnerDef ), + // Add a hidden input to hold the range values + $input = $('<input class="filter" type="hidden">') + .appendTo($cell) + // hidden filter update (.tsfilter) namespace trigger by filter widget + .bind('change.tsfilter', function(){ + updateSpinner({ value: this.value, delayed: false }); + }), + $shcell = [], + c = $cell.closest('table')[0].config, + + // this function updates the hidden input and adds the current values to the header cell text + updateSpinner = function(ui) { + var chkd = true, state, + // ui is not undefined on create + v = ui && ui.value && $.tablesorter.formatFloat((ui.value + '').replace(/[><=]/g,'')) || $cell.find('.spinner').val() || o.value; + if (o.addToggle) { + chkd = $cell.find('.toggle').is(':checked'); + } + state = o.disabled || !chkd ? 'disable' : 'enable'; + $cell.find('.filter') + // add equal to the beginning, so we filter exact numbers + .val( chkd ? (o.compare ? o.compare : o.exactMatch ? '=' : '') + v : '' ) + .trigger('search', ui && typeof ui.delayed === 'boolean' ? ui.delayed : o.delayed).end() + .find('.spinner').spinner(state).val(v); + // update sticky header cell + if ($shcell.length) { + $shcell.find('.spinner').spinner(state).val(v); + if (o.addToggle) { + $shcell.find('.toggle')[0].checked = chkd; + } + } + }; + + // add callbacks; preserve added callbacks + o.oldcreate = o.create; + o.oldspin = o.spin; + o.create = function(event, ui) { + updateSpinner(); // ui is an empty object on create + if (typeof o.oldcreate === 'function') { o.oldcreate(event, ui); } + }; + o.spin = function(event, ui) { + updateSpinner(ui); + if (typeof o.oldspin === 'function') { o.oldspin(event, ui); } + }; + if (o.addToggle) { + $('<div class="button"><input id="uispinnerbutton' + indx + '" type="checkbox" class="toggle" /><label for="uispinnerbutton' + indx + '"></label></div>') + .appendTo($cell) + .find('.toggle') + .bind('change', function(){ + updateSpinner(); + }); + } + // make sure we use parsed data + $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed'); + // add a jQuery UI spinner! + $('<input class="spinner spinner' + indx + '" />') + .val(o.value) + .appendTo($cell) + .spinner(o) + .bind('change keyup', function(e){ + updateSpinner(); + }); + + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + if (o.addToggle) { + $('<div class="button"><input id="stickyuispinnerbutton' + indx + '" type="checkbox" class="toggle" /><label for="stickyuispinnerbutton' + indx + '"></label></div>') + .appendTo($shcell) + .find('.toggle') + .bind('change', function(){ + $cell.find('.toggle')[0].checked = this.checked; + updateSpinner(); + }); + } + // add a jQuery UI spinner! + $('<input class="spinner spinner' + indx + '" />') + .val(o.value) + .appendTo($shcell) + .spinner(o) + .bind('change keyup', function(e){ + $cell.find('.spinner').val( this.value ); + updateSpinner(); + }); + }); + + // on reset + c.$table.bind('filterReset', function(){ + // turn off the toggle checkbox + if (o.addToggle) { + $cell.find('.toggle')[0].checked = false; + } + updateSpinner(); + }); + + updateSpinner(); + return $input; + }, + + /**********************\ + jQuery UI Slider + \**********************/ + uiSlider: function($cell, indx, sliderDef) { + var o = $.extend({ + value : 0, + min : 0, + max : 100, + step : 1, + range : "min", + delayed : true, + valueToHeader : false, + exactMatch : true, + compare : '', + allText : 'all' + }, sliderDef ), + // Add a hidden input to hold the range values + $input = $('<input class="filter" type="hidden">') + .appendTo($cell) + // hidden filter update (.tsfilter) namespace trigger by filter widget + .bind('change.tsfilter', function(){ + updateSlider({ value: this.value }); + }), + $shcell = [], + c = $cell.closest('table')[0].config, + + // this function updates the hidden input and adds the current values to the header cell text + updateSlider = function(ui) { + // ui is not undefined on create + var v = typeof ui !== "undefined" ? $.tablesorter.formatFloat((ui.value + '').replace(/[><=]/g,'')) || o.min : o.value, + val = o.compare ? v : v === o.min ? o.allText : v, + result = o.compare + val; + if (o.valueToHeader) { + // add range indication to the header cell above! + $cell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(' (' + result + ')'); + } else { + // add values to the handle data-value attribute so the css tooltip will work properly + $cell.find('.ui-slider-handle').addClass('value-popup').attr('data-value', result); + } + // update the hidden input; + // ****** ADD AN EQUAL SIGN TO THE BEGINNING! <- this makes the slide exactly match the number ****** + // when the value is at the minimum, clear the hidden input so all rows will be seen + $cell.find('.filter') + .val( ( o.compare ? o.compare + v : v === o.min ? '' : (o.exactMatch ? '=' : '') + v ) ) + .trigger('search', ui && typeof ui.delayed === 'boolean' ? ui.delayed : o.delayed).end() + .find('.slider').slider('value', v); + + // update sticky header cell + if ($shcell.length) { + $shcell.find('.slider').slider('value', v); + if (o.valueToHeader) { + $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(' (' + result + ')'); + } else { + $shcell.find('.ui-slider-handle').addClass('value-popup').attr('data-value', result); + } + } + + }; + $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed'); + + // add span to header for value - only works if the line in the updateSlider() function is also un-commented out + if (o.valueToHeader) { + $cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="curvalue" />'); + } + + // add callbacks; preserve added callbacks + o.oldcreate = o.create; + o.oldslide = o.slide; + o.create = function(event, ui) { + updateSlider(); // ui is an empty object on create + if (typeof o.oldcreate === 'function') { o.oldcreate(event, ui); } + }; + o.slide = function(event, ui) { + updateSlider(ui); + if (typeof o.oldslide === 'function') { o.oldslide(event, ui); } + }; + // add a jQuery UI slider! + $('<div class="slider slider' + indx + '"/>') + .appendTo($cell) + .slider(o); + + // on reset + c.$table.bind('filterReset', function(){ + $cell.find('.slider').slider('value', o.value); + updateSlider(); + }); + + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + + // add a jQuery UI slider! + $('<div class="slider slider' + indx + '"/>') + .val(o.value) + .appendTo($shcell) + .slider(o) + .bind('change keyup', function(e){ + $cell.find('.slider').val( this.value ); + updateSlider(); + }); + + }); + + return $input; + }, + + /*************************\ + jQuery UI Range Slider (2 handles) + \*************************/ + uiRange: function($cell, indx, rangeDef) { + var o = $.extend({ + values : [0, 100], + min : 0, + max : 100, + range : true, + delayed : true, + valueToHeader : false + }, rangeDef ), + // Add a hidden input to hold the range values + $input = $('<input class="filter" type="hidden">') + .appendTo($cell) + // hidden filter update (.tsfilter) namespace trigger by filter widget + .bind('change.tsfilter', function(){ + var v = this.value.split(' - '); + if (this.value === '') { v = [ o.min, o.max ]; } + if (v && v[1]) { + updateUiRange({ values: v, delay: false }); + } + }), + $shcell = [], + c = $cell.closest('table')[0].config, + + // this function updates the hidden input and adds the current values to the header cell text + updateUiRange = function(ui) { + // ui.values are undefined for some reason on create + var val = ui && ui.values || o.values, + result = val[0] + ' - ' + val[1], + // make range an empty string if entire range is covered so the filter row will hide (if set) + range = val[0] === o.min && val[1] === o.max ? '' : result; + if (o.valueToHeader) { + // add range indication to the header cell above (if not using the css method)! + $cell.closest('thead').find('th[data-column=' + indx + ']').find('.currange').html(' (' + result + ')'); + } else { + // add values to the handle data-value attribute so the css tooltip will work properly + $cell.find('.ui-slider-handle') + .addClass('value-popup') + .eq(0).attr('data-value', val[0]).end() // adding value to data attribute + .eq(1).attr('data-value', val[1]); // value popup shown via css + } + // update the hidden input + $cell.find('.filter').val(range) + .trigger('search', ui && typeof ui.delayed === 'boolean' ? ui.delayed : o.delayed).end() + .find('.range').slider('values', val); + // update sticky header cell + if ($shcell.length) { + $shcell.find('.range').slider('values', val); + if (o.valueToHeader) { + $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.currange').html(' (' + result + ')'); + } else { + $shcell.find('.ui-slider-handle') + .addClass('value-popup') + .eq(0).attr('data-value', val[0]).end() // adding value to data attribute + .eq(1).attr('data-value', val[1]); // value popup shown via css + } + } + + }; + $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed'); + + // add span to header for value - only works if the line in the updateUiRange() function is also un-commented out + if (o.valueToHeader) { + $cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="currange"/>'); + } + + // add callbacks; preserve added callbacks + o.oldcreate = o.create; + o.oldslide = o.slide; + // add a jQuery UI range slider! + o.create = function(event, ui) { + updateUiRange(); // ui is an empty object on create + if (typeof o.oldcreate === 'function') { o.oldcreate(event, ui); } + }; + o.slide = function(event, ui) { + updateUiRange(ui); + if (typeof o.oldslide === 'function') { o.oldslide(event, ui); } + }; + $('<div class="range range' + indx +'"/>') + .appendTo($cell) + .slider(o); + + // on reset + c.$table.bind('filterReset', function(){ + $cell.find('.range').slider('values', o.values); + updateUiRange(); + }); + + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + + // add a jQuery UI slider! + $('<div class="range range' + indx + '"/>') + .val(o.value) + .appendTo($shcell) + .slider(o) + .bind('change keyup', function(e){ + $cell.find('.range').val( this.value ); + updateUiRange(); + }); + + }); + + // return the hidden input so the filter widget has a reference to it + return $input; + }, + + /*************************\ + jQuery UI Datepicker compare (1 input) + \*************************/ + uiDateCompare: function($cell, indx, defDate) { + var o = $.extend({ + defaultDate : '', + cellText : '', + changeMonth : true, + changeYear : true, + numberOfMonths : 1, + compare : '', + compareOptions : false + }, defDate), + $hdr = $cell.closest('thead').find('th[data-column=' + indx + ']'), + // Add a hidden input to hold the range values + $input = $('<input class="dateCompare" type="hidden">') + .appendTo($cell) + // hidden filter update (.tsfilter) namespace trigger by filter widget + .bind('change.tsfilter', function(){ + var v = this.value; + if (v) { + o.onClose(v); + } + }), + t, l, $shcell = [], + c = $cell.closest('table')[0].config, + + // this function updates the hidden input + updateCompare = function(v) { + var date = new Date($cell.find('.date').datepicker('getDate')).getTime(); + + $cell.find('.compare').val(v); + $cell.find('.dateCompare') + // add equal to the beginning, so we filter exact numbers + .val(v + date) + .trigger('search', o.delayed).end(); + // update sticky header cell + if ($shcell.length) { + $shcell.find('.compare').val(v); + } + }; + + // make sure we're using parsed dates in the search + $hdr.addClass('filter-parsed'); + + // Add date range picker + if (o.compareOptions) { + l = '<select class="compare">'; + for(var myOption in o.compareOptions) { + l += '<option value="' + myOption + '"'; + if (myOption === o.compare) + l += ' selected="selected"'; + l += '>' + myOption + '</option>'; + } + l += '</select>'; + $cell.append(l) + .find('.compare') + .bind('change', function(){ + updateCompare($(this).val()); + }); + } else if (o.cellText) { + l = '<label>' + o.cellText + '</label>'; + $cell.append(l); + } + + t = '<input type="text" class="date date' + indx + + '" placeholder="' + ($hdr.data('placeholder') || $hdr.attr('data-placeholder') || '') + '" />'; + $(t).appendTo($cell); + + // add callbacks; preserve added callbacks + o.oldonClose = o.onClose; + + o.onClose = function( selectedDate, ui ) { + var date = new Date($cell.find('.date').datepicker('getDate')).getTime() || ''; + var compare = ( $cell.find('.compare').val() || o.compare); + $cell + // update hidden input + .find('.dateCompare').val( compare + date ) + .trigger('search').end() + .find('.date') + .datepicker('setDate', selectedDate); + + // update sticky header cell + if ($shcell.length) { + $shcell.find('.date').datepicker('setDate', selectedDate); + } + + if (typeof o.oldonClose === 'function') { o.oldonClose(selectedDate, ui); } + }; + $cell.find('.date').datepicker(o); + + if (o.filterDate) { + $cell.find('.date').datepicker('setDate', o.filterDate); + } + + // on reset + c.$table.bind('filterReset', function(){ + $cell.find('.date').val('').datepicker('option', 'currentText', '' ); + if ($shcell.length) { + $shcell.find('.date').val('').datepicker('option', 'currentText', '' ); + } + }); + + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + if (o.compareOptions) { + $shcell.append(l) + .find('.compare') + .bind('change', function(){ + updateCompare($(this).val()); + }); + } else if (o.cellText) { + $shcell.append(l); + } + + // add a jQuery datepicker! + $shcell + .append(t) + .find('.date') + .datepicker(o); + }); + + // return the hidden input so the filter widget has a reference to it + return $input.val( o.defaultDate ? o.defaultDate : '' ); + }, + + /*************************\ + jQuery UI Datepicker (2 inputs) + \*************************/ + uiDatepicker: function($cell, indx, defDate) { + var o = $.extend({ + from : '', + to : '', + textFrom : 'from', + textTo : 'to', + changeMonth : true, + changeYear : true, + numberOfMonths : 1 + }, defDate), + t, closeFrom, $shcell = [], + // Add a hidden input to hold the range values + $input = $('<input class="dateRange" type="hidden">') + .appendTo($cell) + // hidden filter update (.tsfilter) namespace trigger by filter widget + .bind('change.tsfilter', function(){ + var v = this.value; + if (v.match(' - ')) { + v = v.split(' - '); + $cell.find('.dateTo').val(v[1]); + closeFrom(v[0]); + } else if (v.match('>=')) { + closeFrom( v.replace('>=', '') ); + } else if (v.match('<=')) { + o.onClose( v.replace('<=', '') ); + } + }), + c = $cell.closest('table')[0].config; + + // make sure we're using parsed dates in the search + $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed'); + // Add date range picker + t = '<label>' + o.textFrom + '</label><input type="text" class="dateFrom" /><label>' + o.textTo + '</label><input type="text" class="dateTo" />'; + $(t).appendTo($cell); + + // add callbacks; preserve added callbacks + o.oldonClose = o.onClose; + + o.defaultDate = o.from || o.defaultDate; + + closeFrom = o.onClose = function( selectedDate, ui ) { + var from = new Date($cell.find('.dateFrom').datepicker('getDate')).getTime() || '', + to = new Date($cell.find('.dateTo').datepicker('getDate')).getTime() || '', + range = from ? ( to ? from + ' - ' + to : '>=' + from ) : (to ? '<=' + to : ''); + $cell + .find('.dateTo').datepicker('option', 'minDate', selectedDate ).end() + .find('.dateFrom').val(selectedDate).end() + // update hidden input + .find('.dateRange').val(range) + .trigger('search'); + // update sticky header cell + if ($shcell.length) { + $shcell + .find('.dateTo').datepicker('option', 'minDate', selectedDate ).end() + .find('.dateFrom').val(selectedDate); + } + if (typeof o.oldonClose === 'function') { o.oldonClose(selectedDate, ui); } + }; + + $cell.find('.dateFrom').datepicker(o); + o.defaultDate = o.to || '+7d'; // set to date +7 days from today (if not defined) + o.onClose = function( selectedDate, ui ) { + var from = new Date( $cell.find('.dateFrom').datepicker('getDate') ).getTime() || '', + to = new Date( $cell.find('.dateTo').datepicker('getDate') ).getTime() || '', + range = from ? ( to ? from + ' - ' + to : '>=' + from ) : (to ? '<=' + to : ''); + $cell + .find('.dateFrom').datepicker('option', 'maxDate', selectedDate ).end() + .find('.dateTo').val(selectedDate).end() + .find('.dateRange').val(range) + .trigger('search'); + // update sticky header cell + if ($shcell.length) { + $shcell + .find('.dateFrom').datepicker('option', 'maxDate', selectedDate ).end() + .find('.dateTo').val(selectedDate); + } + if (typeof o.oldonClose === 'function') { o.oldonClose(selectedDate, ui); } + }; + $cell.find('.dateTo').datepicker(o); + + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + // add a jQuery datepicker! + $shcell.append(t).find('.dateTo').datepicker(o); + o.defaultDate = o.from || o.defaultDate || new Date(); + o.onClose = closeFrom; + $shcell.find('.dateFrom').datepicker(o); + }); + + // on reset + $cell.closest('table').bind('filterReset', function(){ + $cell.find('.dateFrom, .dateTo').val(''); + if ($shcell.length) { + $shcell.find('.dateFrom, .dateTo').val(''); + } + }); + + // return the hidden input so the filter widget has a reference to it + t = o.from ? ( o.to ? o.from + ' - ' + o.to : '>=' + o.from ) : (o.to ? '<=' + o.to : ''); + return $input.val( t ); + }, + + /**********************\ + HTML5 Number (spinner) + \**********************/ + html5Number : function($cell, indx, def5Num) { + var t, o = $.extend({ + value : 0, + min : 0, + max : 100, + step : 1, + delayed : true, + disabled : false, + addToggle : true, + exactMatch : true, + compare : '', + compareOptions : false, + skipTest: false + }, def5Num), + + // test browser for HTML5 range support + $number = $('<input type="number" style="visibility:hidden;" value="test">').appendTo($cell), + // test if HTML5 number is supported - from Modernizr + numberSupported = o.skipTest || $number.attr('type') === 'number' && $number.val() !== 'test', + t, l, $shcell = [], + c = $cell.closest('table')[0].config, + + updateCompare = function(v) { + var number = $cell.find('.number').val(); + + $cell.find('.compare').val(v); + $cell.find('input[type=hidden]') + // add equal to the beginning, so we filter exact numbers + .val(v + number) + .trigger('search', o.delayed).end(); + // update sticky header cell + if ($shcell.length) { + $shcell.find('.compare').val(v); + } + }, + + updateNumber = function(v, delayed){ + var chkd = o.addToggle ? $cell.find('.toggle').is(':checked') : true; + var compare = ( $cell.find('.compare').val() || o.compare); + $cell.find('input[type=hidden]') + // add equal to the beginning, so we filter exact numbers + .val( !o.addToggle || chkd ? (o.compare ? o.compare : o.exactMatch ? '=' : '') + v : '' ) + .val( !o.addToggle || chkd ? compare + v : '' ) + .trigger('search', delayed ? delayed : o.delayed).end() + .find('.number').val(v); + if ($cell.find('.number').length) { + $cell.find('.number')[0].disabled = (o.disabled || !chkd); + } + // update sticky header cell + if ($shcell.length) { + $shcell.find('.number').val(v)[0].disabled = (o.disabled || !chkd); + if (o.addToggle) { + $shcell.find('.toggle')[0].checked = chkd; + } + } + }; + $number.remove(); + + if (numberSupported) { + l = o.addToggle ? '<div class="button"><input id="html5button' + indx + '" type="checkbox" class="toggle" /><label for="html5button' + indx + '"></label></div>' : ''; + } + + if (o.compareOptions) { + l = '<select class="compare">'; + for(var myOption in o.compareOptions) { + l += '<option value="' + myOption + '"'; + if (myOption === o.compare) + l += ' selected="selected"'; + l += '>' + myOption + '</option>'; + } + l += '</select>'; + $cell.append(l) + .find('.compare') + .bind('change', function(){ + updateCompare($(this).val()); + }); + } else { + if (l) + $cell.append(l); + } + + if (numberSupported) { + t = '<input class="number" type="number" min="' + o.min + '" max="' + o.max + '" value="' + + o.value + '" step="' + o.step + '" />'; + // add HTML5 number (spinner) + $cell + .append(t + '<input type="hidden" />') + .find('.toggle, .number').bind('change', function(){ + updateNumber( $cell.find('.number').val() ); + }) + .closest('thead').find('th[data-column=' + indx + ']') + .addClass('filter-parsed') // get exact numbers from column + // on reset + .closest('table').bind('filterReset', function(){ + // turn off the toggle checkbox + if (o.addToggle) { + $cell.find('.toggle')[0].checked = false; + if ($shcell.length) { + $shcell.find('.toggle')[0].checked = false; + } + } + updateNumber( $cell.find('.number').val() ); + }); + + // hidden filter update (.tsfilter) namespace trigger by filter widget + // FIXME TheSin, Not sure why but this breaks updates + // Commenting out till it's fixed. + //$cell.find('input[type=hidden]').bind('change.tsfilter', function(){ + // updateNumber( this.value ); + //}); + + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + if (o.compareOptions) { + $shcell.append(l) + .find('.compare') + .bind('change', function(){ + updateCompare($(this).val()); + }); + } else { + $shcell.append(l); + } + + $shcell + .append(t) + .find('.toggle, .number').bind('change', function(){ + updateNumber( $shcell.find('.number').val() ); + }); + updateNumber( $cell.find('.number').val() ); + }); + + updateNumber( $cell.find('.number').val() ); + + } + + return numberSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">'); + }, + + /**********************\ + HTML5 range slider + \**********************/ + html5Range : function($cell, indx, def5Range) { + var t, o = $.extend({ + value : 0, + min : 0, + max : 100, + step : 1, + delayed : true, + valueToHeader : true, + exactMatch : true, + compare : '', + allText : 'all', + skipTest : false + }, def5Range), + + // test browser for HTML5 range support + $range = $('<input type="range" style="visibility:hidden;" value="test">').appendTo($cell), + // test if HTML5 range is supported - from Modernizr (but I left out the method to detect in Safari 2-4) + // see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/inputtypes.js + rangeSupported = o.skipTest || $range.attr('type') === 'range' && $range.val() !== 'test', + $shcell = [], + c = $cell.closest('table')[0].config, + + updateRange = function(v, delayed){ + /*jshint eqeqeq:false */ + v = (v + '').replace(/[<>=]/g,'') || o.min; // hidden input changes may include compare symbols + var t = ' (' + (o.compare ? o.compare + v : v == o.min ? o.allText : v) + ')'; + $cell.find('input[type=hidden]') + // add equal to the beginning, so we filter exact numbers + .val( ( o.compare ? o.compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ) ) + //( val == o.min ? '' : val + (o.exactMatch ? '=' : '')) + .trigger('search', delayed ? delayed : o.delayed).end() + .find('.range').val(v); + // or add current value to the header cell, if desired + $cell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(t); + // update sticky header cell + if ($shcell.length) { + $shcell.find('.range').val(v); + $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(t); + } + }; + $range.remove(); + + if (rangeSupported) { + // add HTML5 range + $cell + .html('<input type="hidden"><input class="range" type="range" min="' + o.min + '" max="' + o.max + '" value="' + o.value + '" />') + .closest('thead').find('th[data-column=' + indx + ']') + .addClass('filter-parsed') // get exact numbers from column + // add span to header for the current slider value + .find('.tablesorter-header-inner').append('<span class="curvalue" />'); + + $cell.find('.range').bind('change', function(){ + updateRange( this.value ); + }); + + // hidden filter update (.tsfilter) namespace trigger by filter widget + $cell.find('input[type=hidden]').bind('change.tsfilter', function(){ + /*jshint eqeqeq:false */ + var v = this.value; + if (v !== this.lastValue) { + this.lastValue = ( o.compare ? o.compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ); + this.value = this.lastValue; + updateRange( v ); + } + }); + + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + $shcell + .html('<input class="range" type="range" min="' + o.min + '" max="' + o.max + '" value="' + o.value + '" />') + .find('.range').bind('change', function(){ + updateRange( $shcell.find('.range').val() ); + }); + updateRange( $cell.find('.range').val() ); + }); + + // on reset + $cell.closest('table').bind('filterReset', function(){ + // just turn off the colorpicker + updateRange(o.value); + }); + + updateRange( $cell.find('.range').val() ); + + } + + return rangeSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">'); + }, + + /**********************\ + HTML5 Color picker + \**********************/ + html5Color: function($cell, indx, defColor) { + var t, o = $.extend({ + value : '#000000', + disabled : false, + addToggle : true, + exactMatch : true, + valueToHeader : false, + skipTest : false + }, defColor), + // Add a hidden input to hold the range values + $color = $('<input type="color" style="visibility:hidden;" value="test">').appendTo($cell), + // test if HTML5 color is supported - from Modernizr + colorSupported = o.skipTest || $color.attr('type') === 'color' && $color.val() !== 'test', + $shcell = [], + c = $cell.closest('table')[0].config, + + updateColor = function(v){ + v = v || o.value; + var chkd = true, + t = ' (' + v + ')'; + if (o.addToggle) { + chkd = $cell.find('.toggle').is(':checked'); + } + if ($cell.find('.colorpicker').length) { + $cell.find('.colorpicker').val(v)[0].disabled = (o.disabled || !chkd); + } + + $cell.find('input[type=hidden]') + .val( chkd ? v + (o.exactMatch ? '=' : '') : '' ) + .trigger('search'); + if (o.valueToHeader) { + // add current color to the header cell + $cell.closest('thead').find('th[data-column=' + indx + ']').find('.curcolor').html(t); + } else { + // current color to span in cell + $cell.find('.currentColor').html(t); + } + + // update sticky header cell + if ($shcell.length) { + $shcell.find('.colorpicker').val(v)[0].disabled = (o.disabled || !chkd); + if (o.addToggle) { + $shcell.find('.toggle')[0].checked = chkd; + } + if (o.valueToHeader) { + // add current color to the header cell + $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curcolor').html(t); + } else { + // current color to span in cell + $shcell.find('.currentColor').html(t); + } + } + }; + $color.remove(); + + if (colorSupported) { + // add HTML5 color picker + t = '<div class="color-controls-wrapper">'; + t += o.addToggle ? '<div class="button"><input id="colorbutton' + indx + '" type="checkbox" class="toggle" /><label for="colorbutton' + indx + '"></label></div>' : ''; + t += '<input type="hidden"><input class="colorpicker" type="color" />'; + t += (o.valueToHeader ? '' : '<span class="currentColor">(#000000)</span>') + '</div>'; + $cell.html(t); + + // add span to header for the current color value - only works if the line in the updateColor() function is also un-commented out + if (o.valueToHeader) { + $cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="curcolor" />'); + } + + $cell.find('.toggle, .colorpicker').bind('change', function(){ + updateColor( $cell.find('.colorpicker').val() ); + }); + + // hidden filter update (.tsfilter) namespace trigger by filter widget + $cell.find('input[type=hidden]').bind('change.tsfilter', function(){ + updateColor( this.value ); + }); + + // on reset + $cell.closest('table').bind('filterReset', function(){ + // just turn off the colorpicker + $cell.find('.toggle')[0].checked = false; + updateColor( $cell.find('.colorpicker').val() ); + }); + + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx); + $shcell + .html(t) + .find('.toggle, .colorpicker').bind('change', function(){ + updateColor( $shcell.find('.colorpicker').val() ); + }); + updateColor( $shcell.find('.colorpicker').val() ); + }); + + updateColor( o.value ); + } + return colorSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">'); + } + +}; + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 49749eb..6c6d127 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.4+ widgets - updated 1/29/2013 +/*! tableSorter 2.8+ widgets - updated 6/4/2013 * * Column Styles * Column Filters @@ -6,15 +6,15 @@ * Sticky Header * UI Theme (generalized) * Save Sort - * ["zebra", "uitheme", "stickyHeaders", "filter", "columns"] + * [ "columns", "filter", "resizable", "stickyHeaders", "uitheme", "saveSort" ] */ /*jshint browser:true, jquery:true, unused:false, loopfunc:true */ /*global jQuery: false, localStorage: false, navigator: false */ ;(function($){ "use strict"; -$.tablesorter = $.tablesorter || {}; +var ts = $.tablesorter = $.tablesorter || {}; -$.tablesorter.themes = { +ts.themes = { "bootstrap" : { table : 'table table-bordered table-striped', header : 'bootstrap-header', // give the header a gradient background @@ -66,19 +66,26 @@ $.tablesorter.themes = { val = (v && v.hasOwnProperty('mywidget')) ? v.mywidget : ''; alert(val); // "data1" if saved, or "" if not */ -$.tablesorter.storage = function(table, key, val){ +ts.storage = function(table, key, val){ var d, k, ls = false, v = {}, id = table.id || $('.tablesorter').index( $(table) ), url = window.location.pathname; - try { ls = !!(localStorage.getItem); } catch(e) {} + // https://gist.github.com/paulirish/5558557 + if ("localStorage" in window) { + try { + window.localStorage.setItem('_tmptest', 'temp'); + ls = true; + window.localStorage.removeItem('_tmptest'); + } catch(e) {} + } // *** get val *** if ($.parseJSON){ if (ls){ - v = $.parseJSON(localStorage[key]) || {}; + v = $.parseJSON(localStorage[key] || '{}'); } else { k = document.cookie.split(/[;\s|=]/); // cookie d = $.inArray(key, k) + 1; // add one to get from the key to the value - v = (d !== 0) ? $.parseJSON(k[d]) || {} : {}; + v = (d !== 0) ? $.parseJSON(k[d] || '{}') : {}; } } // allow val to be an empty string to @@ -101,20 +108,61 @@ $.tablesorter.storage = function(table, key, val){ } }; +// Add a resize event to table headers +// ************************** +ts.addHeaderResizeEvent = function(table, disable, options){ + var defaults = { + timer : 250 + }, + o = $.extend({}, defaults, options), + c = table.config, + wo = c.widgetOptions, + headers, + checkSizes = function(){ + wo.resize_flag = true; + headers = []; + c.$headers.each(function(){ + var d = $.data(this, 'savedSizes'), + w = this.offsetWidth, + h = this.offsetHeight; + if (w !== d[0] || h !== d[1]) { + $.data(this, 'savedSizes', [ w, h ]); + headers.push(this); + } + }); + if (headers.length) { c.$table.trigger('resize', [ headers ]); } + wo.resize_flag = false; + }; + clearInterval(wo.resize_timer); + if (disable) { + wo.resize_flag = false; + return false; + } + c.$headers.each(function(){ + $.data(this, 'savedSizes', [ this.offsetWidth, this.offsetHeight ]); + }); + wo.resize_timer = setInterval(function(){ + if (wo.resize_flag) { return; } + checkSizes(); + }, o.timer); +}; + // Widget: General UI theme // "uitheme" option in "widgetOptions" // ************************** -$.tablesorter.addWidget({ +ts.addWidget({ id: "uitheme", - format: function(table){ + priority: 10, + options: { + uitheme : 'jui' + }, + format: function(table, c, wo){ var time, klass, $el, $tar, - t = $.tablesorter.themes, - $t = $(table), - c = table.config, - wo = c.widgetOptions, - theme = c.theme !== 'default' ? c.theme : wo.uitheme || 'jui', // default uitheme is 'jui' + t = ts.themes, + $t = c.$table, + theme = c.theme !== 'default' ? c.theme : wo.uitheme || 'jui', o = t[ t[theme] ? theme : t[wo.uitheme] ? wo.uitheme : 'jui'], - $h = $(c.headerList), + $h = c.$headers, sh = 'tr.' + (wo.stickyHeaders || 'tablesorter-stickyHeader'), rmv = o.sortNone + ' ' + o.sortDesc + ' ' + o.sortAsc; if (c.debug) { time = new Date(); } @@ -137,10 +185,9 @@ $.tablesorter.addWidget({ $h .addClass(o.header) .filter(':not(.sorter-false)') - .hover(function(){ - $(this).addClass(o.hover); - }, function(){ - $(this).removeClass(o.hover); + .bind('mouseenter.tsuitheme mouseleave.tsuitheme', function(e){ + // toggleClass with switch added in jQuery 1.3 + $(this)[ e.type === 'mouseenter' ? 'addClass' : 'removeClass' ](o.hover); }); if (!$h.find('.tablesorter-wrapper').length) { // Firefox needs this inner div to position the resizer correctly @@ -169,20 +216,20 @@ $.tablesorter.addWidget({ } }); if (c.debug){ - $.tablesorter.benchmark("Applying " + theme + " theme", time); + ts.benchmark("Applying " + theme + " theme", time); } }, remove: function(table, c, wo){ - var $t = $(table), + var $t = c.$table, theme = typeof wo.uitheme === 'object' ? 'jui' : wo.uitheme || 'jui', - o = typeof wo.uitheme === 'object' ? wo.uitheme : $.tablesorter.themes[ $.tablesorter.themes.hasOwnProperty(theme) ? theme : 'jui'], + o = typeof wo.uitheme === 'object' ? wo.uitheme : ts.themes[ ts.themes.hasOwnProperty(theme) ? theme : 'jui'], $h = $t.children('thead').children(), rmv = o.sortNone + ' ' + o.sortDesc + ' ' + o.sortAsc; $t .removeClass('tablesorter-' + theme + ' ' + o.table) .find(c.cssHeader).removeClass(o.header); $h - .unbind('mouseenter mouseleave') // remove hover + .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') // remove hover .removeClass(o.hover + ' ' + rmv + ' ' + o.active) .find('.tablesorter-filter-row').removeClass(o.filterRow); $h.find('.tablesorter-icon').removeClass(o.icons); @@ -193,17 +240,18 @@ $.tablesorter.addWidget({ // "columns", "columns_thead" (true) and // "columns_tfoot" (true) options in "widgetOptions" // ************************** -$.tablesorter.addWidget({ +ts.addWidget({ id: "columns", - format: function(table){ + priority: 30, + options : { + columns : [ "primary", "secondary", "tertiary" ] + }, + format: function(table, c, wo){ var $tb, $tr, $td, $t, time, last, rmv, i, k, l, - $tbl = $(table), - c = table.config, - wo = c.widgetOptions, + $tbl = c.$table, b = c.$tbodies, list = c.sortList, len = list.length, - css = [ "primary", "secondary", "tertiary" ]; // default options // keep backwards compatibility, for now css = (c.widgetColumns && c.widgetColumns.hasOwnProperty('css')) ? c.widgetColumns.css || css : (wo && wo.hasOwnProperty('columns')) ? wo.columns || css : css; @@ -214,7 +262,7 @@ $.tablesorter.addWidget({ } // check if there is a sort (on initialization there may not be one) for (k = 0; k < b.length; k++ ){ - $tb = $.tablesorter.processTbody(table, b.eq(k), true); // detach tbody + $tb = ts.processTbody(table, b.eq(k), true); // detach tbody $tr = $tb.children('tr'); l = $tr.length; // loop through the visible rows @@ -236,7 +284,7 @@ $.tablesorter.addWidget({ } } }); - $.tablesorter.processTbody(table, $tb, false); + ts.processTbody(table, $tb, false); } // add classes to thead and tfoot $tr = wo.columns_thead !== false ? 'thead tr' : ''; @@ -257,83 +305,82 @@ $.tablesorter.addWidget({ } } if (c.debug){ - $.tablesorter.benchmark("Applying Columns widget", time); + ts.benchmark("Applying Columns widget", time); } }, remove: function(table, c, wo){ var k, $tb, b = c.$tbodies, - rmv = (c.widgetOptions.columns || [ "primary", "secondary", "tertiary" ]).join(' '); + rmv = (wo.columns || [ "primary", "secondary", "tertiary" ]).join(' '); c.$headers.removeClass(rmv); - $(table).children('tfoot').children('tr').children('th, td').removeClass(rmv); + c.$table.children('tfoot').children('tr').children('th, td').removeClass(rmv); for (k = 0; k < b.length; k++ ){ - $tb = $.tablesorter.processTbody(table, b.eq(k), true); // remove tbody + $tb = ts.processTbody(table, b.eq(k), true); // remove tbody $tb.children('tr').each(function(){ $(this).children().removeClass(rmv); }); - $.tablesorter.processTbody(table, $tb, false); // restore tbody + ts.processTbody(table, $tb, false); // restore tbody } } }); -/* Widget: filter - widgetOptions: - filter_childRows : false // if true, filter includes child row content in the search - filter_columnFilters : true // if true, a filter will be added to the top of each table column - filter_cssFilter : 'tablesorter-filter' // css class name added to the filter row & each input in the row - filter_functions : null // add custom filter functions using this option - filter_hideFilters : false // collapse filter row when mouse leaves the area - filter_ignoreCase : true // if true, make all searches case-insensitive - filter_reset : null // jQuery selector string of an element used to reset the filters - filter_searchDelay : 300 // typing delay in milliseconds before starting a search - filter_startsWith : false // if true, filter start from the beginning of the cell contents - filter_useParsedData : false // filter all data using parsed content - filter_serversideFiltering : false // if true, server-side filtering should be performed because client-side filtering will be disabled, but the ui and events will still be used. - **************************/ -$.tablesorter.addWidget({ +// Widget: filter +// ************************** +ts.addWidget({ id: "filter", - format: function(table){ - if (table.config.parsers && !$(table).hasClass('hasFilters')){ + priority: 50, + options : { + filter_childRows : false, // if true, filter includes child row content in the search + filter_columnFilters : true, // if true, a filter will be added to the top of each table column + filter_cssFilter : 'tablesorter-filter', // css class name added to the filter row & each input in the row + filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin + filter_formatter : null, // add custom filter elements to the filter row + filter_functions : null, // add custom filter functions using this option + filter_hideFilters : false, // collapse filter row when mouse leaves the area + filter_ignoreCase : true, // if true, make all searches case-insensitive + filter_liveSearch : true, // if true, search column content while the user types (with a delay) + filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available (visible) options within the drop down + filter_reset : null, // jQuery selector string of an element used to reset the filters + filter_searchDelay : 300, // typing delay in milliseconds before starting a search + filter_startsWith : false, // if true, filter start from the beginning of the cell contents + filter_useParsedData : false, // filter all data using parsed content + filter_serversideFiltering : false, // if true, server-side filtering should be performed because client-side filtering will be disabled, but the ui and events will still be used. + filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value + + // regex used in filter "check" functions - not for general use and not documented + filter_regex : { + "regex" : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // regex to test for regex + "child" : /tablesorter-childRow/, // child row class name; this gets updated in the script + "filtered" : /filtered/, // filtered (hidden) row class name; updated in the script + "type" : /undefined|number/, // check type + "exact" : /(^[\"|\'|=])|([\"|\'|=]$)/g, // exact match + "nondigit" : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser) + "operators" : /[<>=]/g // replace operators + } + }, + format: function(table, c, wo){ + if (c.parsers && !c.$table.hasClass('hasFilters')){ var i, j, k, l, val, ff, x, xi, st, sel, str, ft, ft2, $th, rg, s, t, dis, col, + fmt = ts.formatFloat, last = '', // save last filter search - ts = $.tablesorter, - c = table.config, - $ths = $(c.headerList), - wo = c.widgetOptions, - css = wo.filter_cssFilter || 'tablesorter-filter', - $t = $(table).addClass('hasFilters'), - b = c.$tbodies, + $ths = c.$headers, + css = wo.filter_cssFilter, + $t = c.$table.addClass('hasFilters'), + b = $t.find('tbody'), cols = c.parsers.length, - reg = [ // regex used in filter "check" functions - /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // 0 = regex to test for regex - new RegExp(c.cssChildRow), // 1 = child row - /undefined|number/, // 2 = check type - /(^[\"|\'|=])|([\"|\'|=]$)/, // 3 = exact match - /[\"\'=]/g, // 4 = replace exact match flags - /[^\w,. \-()]/g, // 5 = replace non-digits (from digit & currency parser) - /[<>=]/g // 6 = replace operators - ], - parsed = $ths.map(function(i){ - return (ts.getData) ? ts.getData($ths.filter('[data-column="' + i + '"]:last'), c.headers[i], 'filter') === 'parsed' : $(this).hasClass('filter-parsed'); - }).get(), - time, timer, + parsed, time, timer, // dig fer gold checkFilters = function(filter){ var arry = $.isArray(filter), - $inpts = $t.find('thead').eq(0).children('tr').find('select.' + css + ', input.' + css), - v = (arry) ? filter : $inpts.map(function(){ - return $(this).val() || ''; - }).get(), + v = (arry) ? filter : ts.getFilters(table), cv = (v || []).join(''); // combined filter values // add filter array back into inputs if (arry) { - $inpts.each(function(i,el){ - $(el).val(filter[i] || ''); - }); + ts.setFilters( $t, v ); } - if (wo.filter_hideFilters === true){ + if (wo.filter_hideFilters){ // show/hide filter row as needed $t.find('.tablesorter-filter-row').trigger( cv === '' ? 'mouseleave' : 'mouseenter' ); } @@ -353,27 +400,36 @@ $.tablesorter.addWidget({ } }, findRows = function(filter, v, cv){ - var $tb, $tr, $td, cr, r, l, ff, time, arry; + var $tb, $tr, $td, cr, r, l, ff, time, r1, r2, searchFiltered; if (c.debug) { time = new Date(); } - for (k = 0; k < b.length; k++ ){ - $tb = $.tablesorter.processTbody(table, b.eq(k), true); - $tr = $tb.children('tr'); + if (b.eq(k).hasClass(c.cssInfoBlock)) { continue; } // ignore info blocks, issue #264 + $tb = ts.processTbody(table, b.eq(k), true); + $tr = $tb.children('tr:not(.' + c.cssChildRow + ')'); l = $tr.length; if (cv === '' || wo.filter_serversideFiltering){ - $tr.show().removeClass('filtered'); + $tb.children().show().removeClass(wo.filter_filteredRow); } else { + // optimize searching only through already filtered rows - see #313 + searchFiltered = true; + r = $t.data('lastSearch') || []; + $.each(v, function(i,val){ + // check for changes from beginning of filter; but ignore if there is a logical "or" in the string + searchFiltered = (val || '').indexOf(r[i] || '') === 0 && searchFiltered && !/(\s+or\s+|\|)/g.test(val || ''); + }); + // can't search when all rows are hidden - this happens when looking for exact matches + if (searchFiltered && $tr.filter(':visible').length === 0) { searchFiltered = false; } // loop through the rows for (j = 0; j < l; j++){ - // skip child rows - if (reg[1].test($tr[j].className)) { continue; } + r = $tr[j].className; + // skip child rows & already filtered rows + if ( wo.filter_regex.child.test(r) || (searchFiltered && wo.filter_regex.filtered.test(r)) ) { continue; } r = true; cr = $tr.eq(j).nextUntil('tr:not(.' + c.cssChildRow + ')'); // so, if "table.config.widgetOptions.filter_childRows" is true and there is // a match anywhere in the child row, then it will make the row visible // checked here so the option can be changed dynamically - t = (cr.length && (wo && wo.hasOwnProperty('filter_childRows') && - typeof wo.filter_childRows !== 'undefined' ? wo.filter_childRows : true)) ? cr.text() : ''; + t = (cr.length && wo.filter_childRows) ? cr.text() : ''; t = wo.filter_ignoreCase ? t.toLocaleLowerCase() : t; $td = $tr.eq(j).children('td'); for (i = 0; i < cols; i++){ @@ -386,7 +442,7 @@ $.tablesorter.addWidget({ // using older or original tablesorter x = $.trim($td.eq(i).text()); } - xi = !reg[2].test(typeof x) && wo.filter_ignoreCase ? x.toLocaleLowerCase() : x; + xi = !wo.filter_regex.type.test(typeof x) && wo.filter_ignoreCase ? x.toLocaleLowerCase() : x; ff = r; // if r is true, show that row // val = case insensitive, v[i] = case sensitive val = wo.filter_ignoreCase ? v[i].toLocaleLowerCase() : v[i]; @@ -402,15 +458,16 @@ $.tablesorter.addWidget({ ff = wo.filter_functions[i][v[i]](x, c.cache[k].normalized[j][i], v[i], i); } // Look for regex - } else if (reg[0].test(val)){ - rg = reg[0].exec(val); + } else if (wo.filter_regex.regex.test(val)){ + rg = wo.filter_regex.regex.exec(val); try { ff = new RegExp(rg[1], rg[2]).test(xi); } catch (err){ ff = false; } - // Look for quotes or equals to get an exact match - } else if (reg[3].test(val) && xi === val.replace(reg[4], '')){ + // Look for quotes or equals to get an exact match; ignore type since xi could be numeric + /*jshint eqeqeq:false */ + } else if (val.replace(wo.filter_regex.exact, '') == xi){ ff = true; // Look for a not match } else if (/^\!/.test(val)){ @@ -419,14 +476,52 @@ $.tablesorter.addWidget({ ff = val === '' ? true : !(wo.filter_startsWith ? s === 0 : s >= 0); // Look for operators >, >=, < or <= } else if (/^[<>]=?/.test(val)){ - // xi may be numeric - see issue #149 - rg = isNaN(xi) ? $.tablesorter.formatFloat(xi.replace(reg[5], ''), table) : $.tablesorter.formatFloat(xi, table); - s = $.tablesorter.formatFloat(val.replace(reg[5], '').replace(reg[6],''), table); + s = fmt(val.replace(wo.filter_regex.nondigit, '').replace(wo.filter_regex.operators,''), table); + // parse filter value in case we're comparing numbers (dates) + if (parsed[i] || c.parsers[i].type === 'numeric') { + rg = c.parsers[i].format('' + val.replace(wo.filter_regex.operators,''), table, $ths.eq(i), i); + s = (rg !== '' && !isNaN(rg)) ? rg : s; + } + // xi may be numeric - see issue #149; + // check if c.cache[k].normalized[j] is defined, because sometimes j goes out of range? (numeric columns) + rg = ( parsed[i] || c.parsers[i].type === 'numeric' ) && !isNaN(s) && c.cache[k].normalized[j] ? c.cache[k].normalized[j][i] : + isNaN(xi) ? fmt(xi.replace(wo.filter_regex.nondigit, ''), table) : fmt(xi, table); if (/>/.test(val)) { ff = />=/.test(val) ? rg >= s : rg > s; } if (/</.test(val)) { ff = /<=/.test(val) ? rg <= s : rg < s; } - // Look for wild card: ? = single, or * = multiple - } else if (/[\?|\*]/.test(val)){ - ff = new RegExp( val.replace(/\?/g, '\\S{1}').replace(/\*/g, '\\S*') ).test(xi); + if (s === '') { ff = true; } // keep showing all rows if nothing follows the operator + // Look for an AND or && operator (logical and) + } else if (/\s+(AND|&&)\s+/g.test(v[i])) { + s = val.split(/(?:\s+(?:and|&&)\s+)/g); + ff = xi.search($.trim(s[0])) >= 0; + r1 = s.length - 1; + while (ff && r1) { + ff = ff && xi.search($.trim(s[r1])) >= 0; + r1--; + } + // Look for a range (using " to " or " - ") - see issue #166; thanks matzhu! + } else if (/\s+(-|to)\s+/.test(val)){ + s = val.split(/(?: - | to )/); // make sure the dash is for a range and not indicating a negative number + r1 = fmt(s[0].replace(wo.filter_regex.nondigit, ''), table); + r2 = fmt(s[1].replace(wo.filter_regex.nondigit, ''), table); + // parse filter value in case we're comparing numbers (dates) + if (parsed[i] || c.parsers[i].type === 'numeric') { + rg = c.parsers[i].format('' + s[0], table, $ths.eq(i), i); + r1 = (rg !== '' && !isNaN(rg)) ? rg : r1; + rg = c.parsers[i].format('' + s[1], table, $ths.eq(i), i); + r2 = (rg !== '' && !isNaN(rg)) ? rg : r2; + } + rg = ( parsed[i] || c.parsers[i].type === 'numeric' ) && !isNaN(r1) && !isNaN(r2) ? c.cache[k].normalized[j][i] : + isNaN(xi) ? fmt(xi.replace(wo.filter_regex.nondigit, ''), table) : fmt(xi, table); + if (r1 > r2) { ff = r1; r1 = r2; r2 = ff; } // swap + ff = (rg >= r1 && rg <= r2) || (r1 === '' || r2 === '') ? true : false; + // Look for wild card: ? = single, * = multiple, or | = logical OR + } else if ( /[\?|\*]/.test(val) || /\s+OR\s+/.test(v[i]) ){ + s = val.replace(/\s+OR\s+/gi,"|"); + // look for an exact match with the "or" unless the "filter-match" class is found + if (!$ths.filter('[data-column="' + i + '"]:last').hasClass('filter-match') && /\|/.test(s)) { + s = '^(' + s + ')$'; + } + ff = new RegExp( s.replace(/\?/g, '\\S{1}').replace(/\*/g, '\\S*') ).test(xi); // Look for match, and add child row data for matching } else { x = (xi + t).indexOf(val); @@ -436,28 +531,32 @@ $.tablesorter.addWidget({ } } $tr[j].style.display = (r ? '' : 'none'); - $tr.eq(j)[r ? 'removeClass' : 'addClass']('filtered'); + $tr.eq(j)[r ? 'removeClass' : 'addClass'](wo.filter_filteredRow); if (cr.length) { cr[r ? 'show' : 'hide'](); } } } - $.tablesorter.processTbody(table, $tb, false); + ts.processTbody(table, $tb, false); } - last = cv; // save last search + $t.data('lastSearch', v); if (c.debug){ ts.benchmark("Completed filter widget search", time); } $t.trigger('applyWidgets'); // make sure zebra widget is applied $t.trigger('filterEnd'); }, - buildSelect = function(i, updating){ - var o, arry = []; + buildSelect = function(i, updating, onlyavail){ + var o, t, arry = [], currentVal; i = parseInt(i, 10); - o = '<option value="">' + ($ths.filter('[data-column="' + i + '"]:last').attr('data-placeholder') || '') + '</option>'; + t = $ths.filter('[data-column="' + i + '"]:last'); + // t.data('placeholder') won't work in jQuery older than 1.4.3 + o = '<option value="">' + (t.data('placeholder') || t.attr('data-placeholder') || '') + '</option>'; for (k = 0; k < b.length; k++ ){ l = c.cache[k].row.length; // loop through the rows for (j = 0; j < l; j++){ + // check if has class filtered + if (onlyavail && c.cache[k].row[j][0].className.match(wo.filter_filteredRow)) { continue; } // get non-normalized cell content if (wo.filter_useParsedData){ arry.push( '' + c.cache[k].normalized[j][i] ); @@ -474,13 +573,18 @@ $.tablesorter.addWidget({ // if $.tablesorter.sortText exists (not in the original tablesorter), // then natural sort the list otherwise use a basic sort arry = $.grep(arry, function(v, k){ - return $.inArray(v ,arry) === k; + return $.inArray(v, arry) === k; }); - arry = (ts.sortText) ? arry.sort(function(a,b){ return ts.sortText(table, a, b, i); }) : arry.sort(true); + arry = (ts.sortText) ? arry.sort(function(a, b){ return ts.sortText(table, a, b, i); }) : arry.sort(true); + + // Get curent filter value + currentVal = $t.find('thead').find('select.' + css + '[data-column="' + i + '"]').val(); // build option list for (k = 0; k < arry.length; k++){ - o += '<option value="' + arry[k] + '">' + arry[k] + '</option>'; + t = arry[k].replace(/\"/g, """); + // replace quotes - fixes #242 & ignore empty strings - see http://stackoverflow.com/q/14990971/145346 + o += arry[k] !== '' ? '<option value="' + t + '"' + (currentVal === t ? ' selected="selected"' : '') +'>' + arry[k] + '</option>' : ''; } $t.find('thead').find('select.' + css + '[data-column="' + i + '"]')[ updating ? 'html' : 'append' ](o); }, @@ -492,73 +596,114 @@ $.tablesorter.addWidget({ if ((t.hasClass('filter-select') || wo.filter_functions && wo.filter_functions[i] === true) && !t.hasClass('filter-false')){ if (!wo.filter_functions) { wo.filter_functions = {}; } wo.filter_functions[i] = true; // make sure this select gets processed by filter_functions - buildSelect(i, updating); + buildSelect(i, updating, t.hasClass(wo.filter_onlyAvail)); } } + }, + searching = function(filter){ + if (typeof filter === 'undefined' || filter === true){ + // delay filtering + clearTimeout(timer); + timer = setTimeout(function(){ + checkFilters(filter); + }, wo.filter_liveSearch ? wo.filter_searchDelay : 10); + } else { + // skip delay + checkFilters(filter); + } }; - if (c.debug){ time = new Date(); } - wo.filter_ignoreCase = wo.filter_ignoreCase !== false; // set default filter_ignoreCase to true - wo.filter_useParsedData = wo.filter_useParsedData === true; // default is false + wo.filter_regex.child = new RegExp(c.cssChildRow); + wo.filter_regex.filtered = new RegExp(wo.filter_filteredRow); // don't build filter row if columnFilters is false or all columns are set to "filter-false" - issue #156 if (wo.filter_columnFilters !== false && $ths.filter('.filter-false').length !== $ths.length){ - t = '<tr class="tablesorter-filter-row">'; // build filter row + // build filter row + t = '<tr class="tablesorter-filter-row">'; + for (i = 0; i < cols; i++){ + t += '<td></td>'; + } + c.$filters = $(t += '</tr>').appendTo( $t.find('thead').eq(0) ).find('td'); + // build each filter input for (i = 0; i < cols; i++){ dis = false; $th = $ths.filter('[data-column="' + i + '"]:last'); // assuming last cell of a column is the main column sel = (wo.filter_functions && wo.filter_functions[i] && typeof wo.filter_functions[i] !== 'function') || $th.hasClass('filter-select'); - t += '<td>'; - if (sel){ - t += '<select data-column="' + i + '" class="' + css; - } else { - t += '<input type="search" placeholder="' + ($th.attr('data-placeholder') || "") + '" data-column="' + i + '" class="' + css; - } // use header option - headers: { 1: { filter: false } } OR add class="filter-false" if (ts.getData){ - dis = ts.getData($th[0], c.headers[i], 'filter') === 'false'; // get data from jQuery data, metadata, headers option or header class name - t += dis ? ' disabled" disabled' : '"'; + dis = ts.getData($th[0], c.headers[i], 'filter') === 'false'; } else { - dis = (c.headers[i] && c.headers[i].hasOwnProperty('filter') && c.headers[i].filter === false) || $th.hasClass('filter-false'); // only class names and header options - keep this for compatibility with tablesorter v2.0.5 - t += (dis) ? ' disabled" disabled' : '"'; + dis = (c.headers[i] && c.headers[i].hasOwnProperty('filter') && c.headers[i].filter === false) || $th.hasClass('filter-false'); + } + + if (sel){ + t = $('<select>').appendTo( c.$filters.eq(i) ); + } else { + if (wo.filter_formatter && $.isFunction(wo.filter_formatter[i])) { + t = wo.filter_formatter[i]( c.$filters.eq(i), i ); + // no element returned, so lets go find it + if (t && t.length === 0) { t = c.$filters.eq(i).children('input'); } + // element not in DOM, so lets attach it + if (t && (t.parent().length === 0 || (t.parent().length && t.parent()[0] !== c.$filters[i]))) { + c.$filters.eq(i).append(t); + } + } else { + t = $('<input type="search">').appendTo( c.$filters.eq(i) ); + } + if (t) { + t.attr('placeholder', $th.data('placeholder') || $th.attr('data-placeholder') || ''); + } + } + if (t) { + t.addClass(css).attr('data-column', i); + if (dis) { + t.addClass('disabled')[0].disabled = true; // disabled! + } } - t += (sel ? '></select>' : '>') + '</td>'; } - $t.find('thead').eq(0).append(t += '</tr>'); } $t - // add .tsfilter namespace to all BUT search - .bind('addRows updateCell update appendCache search'.split(' ').join('.tsfilter '), function(e, filter){ - if (e.type !== 'search'){ + .bind('addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join('.tsfilter '), function(e, filter){ + if (!/(search|filterReset|filterEnd)/.test(e.type)){ + e.stopPropagation(); + buildDefault(true); + } + if (e.type === 'filterReset') { + $t.find('.' + css).val(''); + } + if (e.type === 'filterEnd') { buildDefault(true); + } else { + // send false argument to force a new search; otherwise if the filter hasn't changed, it will return + filter = e.type === 'search' ? filter : e.type === 'updateComplete' ? $t.data('lastSearch') : ''; + searching(filter); } - checkFilters(e.type === 'search' ? filter : ''); return false; }) .find('input.' + css).bind('keyup search', function(e, filter){ - // ignore arrow and meta keys; allow backspace - if ((e.which < 32 && e.which !== 8) || (e.which >= 37 && e.which <=40)) { return; } - // skip delay - if (typeof filter !== 'undefined'){ - checkFilters(filter); - return false; + // emulate what webkit does.... escape clears the filter + if (e.which === 27) { + this.value = ''; + // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace + } else if ( (typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch && this.value !== '') || ( e.type === 'keyup' && + ( (e.which < 32 && e.which !== 8 && wo.filter_liveSearch === true && e.which !== 13) || (e.which >= 37 && e.which <=40) || (e.which !== 13 && wo.filter_liveSearch === false) ) ) ) { + return; } - // delay filtering - clearTimeout(timer); - timer = setTimeout(function(){ - checkFilters(); - }, wo.filter_searchDelay || 300); + searching(filter); }); + // parse columns after formatter, in case the class is added at that point + parsed = $ths.map(function(i){ + return (ts.getData) ? ts.getData($ths.filter('[data-column="' + i + '"]:last'), c.headers[i], 'filter') === 'parsed' : $(this).hasClass('filter-parsed'); + }).get(); + // reset button/link if (wo.filter_reset && $(wo.filter_reset).length){ - $(wo.filter_reset).bind('click', function(){ - $t.find('.' + css).val(''); - checkFilters(); - return false; + $(wo.filter_reset).bind('click.tsfilter', function(){ + $t.trigger('filterReset'); }); } if (wo.filter_functions){ @@ -573,7 +718,7 @@ $.tablesorter.addWidget({ // add custom drop down list for (str in wo.filter_functions[col]){ if (typeof str === 'string'){ - ff += ff === '' ? '<option value="">' + (t.attr('data-placeholder') || '') + '</option>' : ''; + ff += ff === '' ? '<option value="">' + (t.data('placeholder') || t.attr('data-placeholder') || '') + '</option>' : ''; ff += '<option value="' + str + '">' + str + '</option>'; } } @@ -586,11 +731,11 @@ $.tablesorter.addWidget({ // it would append the same options twice. buildDefault(true); - $t.find('select.' + css).bind('change search', function(){ - checkFilters(); + $t.find('select.' + css).bind('change search', function(e, filter){ + checkFilters(filter); }); - if (wo.filter_hideFilters === true){ + if (wo.filter_hideFilters){ $t .find('.tablesorter-filter-row') .addClass('hideme') @@ -607,7 +752,7 @@ $.tablesorter.addWidget({ // $(':focus') needs jQuery 1.6+ if ($(document.activeElement).closest('tr')[0] !== ft[0]){ // get all filter values - all = $t.find('.' + (wo.filter_cssFilter || 'tablesorter-filter')).map(function(){ + all = $t.find('.' + wo.filter_cssFilter).map(function(){ return $(this).val() || ''; }).get().join(''); // don't hide row if any filter has a value @@ -623,7 +768,7 @@ $.tablesorter.addWidget({ clearTimeout(st); st = setTimeout(function(){ // don't hide row if any filter has a value - if ($t.find('.' + (wo.filter_cssFilter || 'tablesorter-filter')).map(function(){ return $(this).val() || ''; }).get().join('') === ''){ + if ($t.find('.' + wo.filter_cssFilter).map(function(){ return $(this).val() || ''; }).get().join('') === ''){ ft2[ e.type === 'focus' ? 'removeClass' : 'addClass']('hideme'); } }, 200); @@ -632,7 +777,7 @@ $.tablesorter.addWidget({ // show processing icon if (c.showProcessing) { - $t.bind('filterStart filterEnd', function(e, v) { + $t.bind('filterStart.tsfilter filterEnd.tsfilter', function(e, v) { var fc = (v) ? $t.find('.' + c.cssHeader).filter('[data-column]').filter(function(){ return v[$(this).data('column')] !== ''; }) : ''; @@ -643,103 +788,143 @@ $.tablesorter.addWidget({ if (c.debug){ ts.benchmark("Applying Filter widget", time); } + // add default values + $t.bind('tablesorter-initialized', function(){ + ff = ts.getFilters(table); + for (i = 0; i < ff.length; i++) { + ff[i] = $ths.filter('[data-column="' + i + '"]:last').attr(wo.filter_defaultAttrib) || ff[i]; + } + ts.setFilters(table, ff, true); + }); // filter widget initialized $t.trigger('filterInit'); + checkFilters(); } }, remove: function(table, c, wo){ var k, $tb, - $t = $(table), + $t = c.$table, b = c.$tbodies; $t .removeClass('hasFilters') // add .tsfilter namespace to all BUT search - .unbind('addRows updateCell update appendCache search'.split(' ').join('.tsfilter')) + .unbind('addRows updateCell update updateComplete appendCache search filterStart filterEnd '.split(' ').join('.tsfilter ')) .find('.tablesorter-filter-row').remove(); for (k = 0; k < b.length; k++ ){ - $tb = $.tablesorter.processTbody(table, b.eq(k), true); // remove tbody - $tb.children().removeClass('filtered').show(); - $.tablesorter.processTbody(table, $tb, false); // restore tbody + $tb = ts.processTbody(table, b.eq(k), true); // remove tbody + $tb.children().removeClass(wo.filter_filteredRow).show(); + ts.processTbody(table, $tb, false); // restore tbody } - if (wo.filterreset) { $(wo.filter_reset).unbind('click'); } + if (wo.filterreset) { $(wo.filter_reset).unbind('click.tsfilter'); } } }); +ts.getFilters = function(table) { + var c = table ? $(table)[0].config : {}; + if (c && c.widgetOptions && !c.widgetOptions.filter_columnFilters) { return $(table).data('lastSearch'); } + return c && c.$filters ? c.$filters.find('.' + c.widgetOptions.filter_cssFilter).map(function(i, el) { + return $(el).val(); + }).get() || [] : false; +}; +ts.setFilters = function(table, filter, apply) { + var $t = $(table), + c = $t.length ? $t[0].config : {}, + valid = c && c.$filters ? c.$filters.find('.' + c.widgetOptions.filter_cssFilter).each(function(i, el) { + $(el).val(filter[i] || ''); + }).trigger('change.tsfilter') || false : false; + if (apply) { $t.trigger('search', [filter, false]); } + return !!valid; +}; // Widget: Sticky headers // based on this awesome article: // http://css-tricks.com/13465-persistent-headers/ // and https://github.com/jmosbech/StickyTableHeaders by Jonas Mosbech // ************************** -$.tablesorter.addWidget({ +ts.addWidget({ id: "stickyHeaders", - format: function(table){ - if ($(table).hasClass('hasStickyHeaders')) { return; } - var $table = $(table).addClass('hasStickyHeaders'), - c = table.config, - wo = c.widgetOptions, - win = $(window), - header = $(table).children('thead:first'), //.add( $(table).find('caption') ), + priority: 60, + options: { + stickyHeaders : 'tablesorter-stickyHeader', + stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element + stickyHeaders_cloneId : '-sticky', // added to table ID, if it exists + stickyHeaders_addResizeEvent : true, // trigger "resize" event on headers + stickyHeaders_includeCaption : true, // if false and a caption exist, it won't be included in the sticky header + stickyHeaders_zIndex : 2 // The zIndex of the stickyHeaders, allows the user to adjust this to their needs + }, + format: function(table, c, wo){ + if (c.$table.hasClass('hasStickyHeaders')) { return; } + var $t = c.$table, + $win = $(window), + header = $t.children('thead:first'), hdrCells = header.children('tr:not(.sticky-false)').children(), - css = wo.stickyHeaders || 'tablesorter-stickyHeader', innr = '.tablesorter-header-inner', - firstRow = hdrCells.eq(0).parent(), - tfoot = $table.find('tfoot'), - t2 = wo.$sticky = $table.clone(), // clone table, but don't remove id... the table might be styled by css - // clone the entire thead - seems to work in IE8+ - stkyHdr = t2.children('thead:first') - .addClass(css) + tfoot = $t.find('tfoot'), + filterInputs = '.' + (wo.filter_cssFilter || 'tablesorter-filter'), + $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '', + stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, + stickyzIndex = wo.stickyHeaders_zIndex ? wo.stickyHeaders_zIndex : 2, + $stickyTable = wo.$sticky = $t.clone() + .addClass('containsStickyHeaders') .css({ - width : header.outerWidth(true), position : 'fixed', margin : 0, - top : 0, + top : stickyOffset, visibility : 'hidden', - zIndex : 1 + zIndex : stickyzIndex }), - stkyCells = stkyHdr.children('tr:not(.sticky-false)').children(), // issue #172 + stkyHdr = $stickyTable.children('thead:first').addClass(wo.stickyHeaders), + stkyCells, laststate = '', spacing = 0, + flag = false, resizeHdr = function(){ + stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0; var bwsr = navigator.userAgent; spacing = 0; // yes, I dislike browser sniffing, but it really is needed here :( // webkit automatically compensates for border spacing - if ($table.css('border-collapse') !== 'collapse' && !/(webkit|msie)/i.test(bwsr)) { + if ($t.css('border-collapse') !== 'collapse' && !/(webkit|msie)/i.test(bwsr)) { // Firefox & Opera use the border-spacing // update border-spacing here because of demos that switch themes spacing = parseInt(hdrCells.eq(0).css('border-left-width'), 10) * 2; } - stkyHdr.css({ - left : header.offset().left - win.scrollLeft() - spacing, - width: header.outerWidth() + $stickyTable.css({ + left : header.offset().left - $win.scrollLeft() - spacing, + width: $t.width() }); - stkyCells - .each(function(i){ - var $h = hdrCells.eq(i); - $(this).css({ - width: $h.width() - spacing, - height: $h.height() - }); - }) - .find(innr).each(function(i){ - var hi = hdrCells.eq(i).find(innr), - w = hi.width(); // - ( parseInt(hi.css('padding-left'), 10) + parseInt(hi.css('padding-right'), 10) ); - $(this).width(w); + stkyCells.filter(':visible').each(function(i){ + var $h = hdrCells.filter(':visible').eq(i); + $(this) + .css({ + width: $h.width() - spacing, + height: $h.height() + }) + .find(innr).width( $h.find(innr).width() ); }); }; + // fix clone ID, if it exists - fixes #271 + if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; } // clear out cloned table, except for sticky header - t2.find('thead:gt(0),tr.sticky-false,tbody,tfoot,caption').remove(); - t2.css({ height:0, width:0, padding:0, margin:0, border:0 }); - // remove rows you don't want to be sticky - stkyHdr.find('tr.sticky-false').remove(); + // include caption & filter row (fixes #126 & #249) + $stickyTable.find('thead:gt(0), tr.sticky-false, tbody, tfoot').remove(); + if (!wo.stickyHeaders_includeCaption) { + $stickyTable.find('caption').remove(); + } + // issue #172 - find td/th in sticky header + stkyCells = stkyHdr.children().children(); + $stickyTable.css({ height:0, width:0, padding:0, margin:0, border:0 }); // remove resizable block stkyCells.find('.tablesorter-resizer').remove(); // update sticky header class names to match real header after sorting - $table + $t + .addClass('hasStickyHeaders') .bind('sortEnd.tsSticky', function(){ - hdrCells.each(function(i){ - var t = stkyCells.eq(i); - t.attr('class', $(this).attr('class')); + hdrCells.filter(':visible').each(function(i){ + var t = stkyCells.filter(':visible').eq(i); + t + .attr('class', $(this).attr('class')) + // remove processing icon + .removeClass(c.cssProcessing); if (c.cssIcon){ t .find('.' + c.cssIcon) @@ -750,54 +935,86 @@ $.tablesorter.addWidget({ .bind('pagerComplete.tsSticky', function(){ resizeHdr(); }); - // set sticky header cell width and link clicks to real header - hdrCells.find('*').andSelf().filter(c.selectorSort).each(function(i){ - var t = $(this); - stkyCells.eq(i) + // http://stackoverflow.com/questions/5312849/jquery-find-self; + hdrCells.find(c.selectorSort).add( c.$headers.filter(c.selectorSort) ).each(function(i){ + var t = $(this), // clicking on sticky will trigger sort - .bind('mouseup', function(e){ + $cell = stkyHdr.children('tr.tablesorter-headerRow').children().eq(i).bind('mouseup', function(e){ t.trigger(e, true); // external mouseup flag (click timer is ignored) - }) - // prevent sticky header text selection - .bind('mousedown', function(){ - this.onselectstart = function(){ return false; }; - return false; }); + // prevent sticky header text selection + if (c.cancelSelection) { + $cell + .attr('unselectable', 'on') + .bind('selectstart', false) + .css({ + 'user-select': 'none', + 'MozUserSelect': 'none' + }); + } }); // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. - $table.after( t2 ); + $t.after( $stickyTable ); // make it sticky! - win - .bind('scroll.tsSticky', function(){ - var offset = firstRow.offset(), - sTop = win.scrollTop(), - tableHt = $table.height() - (stkyHdr.height() + (tfoot.height() || 0)), + $win.bind('scroll.tsSticky resize.tsSticky', function(e){ + if (!$t.is(':visible')) { return; } // fixes #278 + var pre = 'tablesorter-sticky-', + offset = $t.offset(), + cap = (wo.stickyHeaders_includeCaption ? 0 : $t.find('caption').outerHeight(true)), + sTop = $win.scrollTop() + stickyOffset - cap, + tableHt = $t.height() - ($stickyTable.height() + (tfoot.height() || 0)), vis = (sTop > offset.top) && (sTop < offset.top + tableHt) ? 'visible' : 'hidden'; - stkyHdr + $stickyTable + .removeClass(pre + 'visible ' + pre + 'hidden') + .addClass(pre + vis) .css({ // adjust when scrolling horizontally - fixes issue #143 - left : header.offset().left - win.scrollLeft() - spacing, + left : header.offset().left - $win.scrollLeft() - spacing, visibility : vis }); - if (vis !== laststate){ + if (vis !== laststate || e.type === 'resize'){ // make sure the column widths match resizeHdr(); laststate = vis; } - }) - .bind('resize.tsSticky', function(){ - resizeHdr(); }); + if (wo.stickyHeaders_addResizeEvent) { + ts.addHeaderResizeEvent(table); + } + + // look for filter widget + $t.bind('filterEnd', function(){ + if (flag) { return; } + stkyHdr.find('.tablesorter-filter-row').children().each(function(i){ + $(this).find(filterInputs).val( c.$filters.find(filterInputs).eq(i).val() ); + }); + }); + stkyCells.find(filterInputs).bind('keyup search change', function(e){ + // ignore arrow and meta keys; allow backspace + if ((e.which < 32 && e.which !== 8) || (e.which >= 37 && e.which <=40)) { return; } + flag = true; + var $f = $(this), col = $f.attr('data-column'); + c.$filters.find(filterInputs).eq(col) + .val( $f.val() ) + .trigger('search'); + setTimeout(function(){ + flag = false; + }, wo.filter_searchDelay); + }); + $t.trigger('stickyHeadersInit'); + }, remove: function(table, c, wo){ - var $t = $(table), - css = wo.stickyHeaders || 'tablesorter-stickyHeader'; - $t + c.$table .removeClass('hasStickyHeaders') .unbind('sortEnd.tsSticky pagerComplete.tsSticky') - .find('.' + css).remove(); - if (wo.$sticky) { wo.$sticky.remove(); } // remove cloned thead - $(window).unbind('scroll.tsSticky resize.tsSticky'); + .find('.' + wo.stickyHeaders).remove(); + if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table + // don't unbind if any table on the page still has stickyheaders applied + if (!$('.hasStickyHeaders').length) { + $(window).unbind('scroll.tsSticky resize.tsSticky'); + } + ts.addHeaderResizeEvent(table, false); } }); @@ -805,39 +1022,42 @@ $.tablesorter.addWidget({ // this widget saves the column widths if // $.tablesorter.storage function is included // ************************** -$.tablesorter.addWidget({ +ts.addWidget({ id: "resizable", - format: function(table){ - if ($(table).hasClass('hasResizable')) { return; } - $(table).addClass('hasResizable'); - var $t, t, i, j, s, $c, $cols, w, tw, - $tbl = $(table), - c = table.config, - wo = c.widgetOptions, + priority: 40, + options: { + resizable : true, + resizable_addLastColumn : false + }, + format: function(table, c, wo){ + if (c.$table.hasClass('hasResizable')) { return; } + c.$table.addClass('hasResizable'); + var $t, t, i, j, s = {}, $c, $cols, w, tw, + $tbl = c.$table, position = 0, $target = null, $next = null, fullWidth = Math.abs($tbl.parent().width() - $tbl.width()) < 20, stopResize = function(){ - if ($.tablesorter.storage && $target){ + if (ts.storage && $target){ s[$target.index()] = $target.width(); s[$next.index()] = $next.width(); $target.width( s[$target.index()] ); $next.width( s[$next.index()] ); if (wo.resizable !== false){ - $.tablesorter.storage(table, 'tablesorter-resizable', s); + ts.storage(table, 'tablesorter-resizable', s); } } position = 0; $target = $next = null; $(window).trigger('resize'); // will update stickyHeaders, just in case }; - s = ($.tablesorter.storage && wo.resizable !== false) ? $.tablesorter.storage(table, 'tablesorter-resizable') : {}; + s = (ts.storage && wo.resizable !== false) ? ts.storage(table, 'tablesorter-resizable') : {}; // process only if table ID or url match if (s){ for (j in s){ - if (!isNaN(j) && j < c.headerList.length){ - $(c.headerList[j]).width(s[j]); // set saved resizable widths + if (!isNaN(j) && j < c.$headers.length){ + c.$headers.eq(j).width(s[j]); // set saved resizable widths } } } @@ -846,7 +1066,7 @@ $.tablesorter.addWidget({ $t.children().each(function(){ t = $(this); i = t.attr('data-column'); - j = $.tablesorter.getData( t, c.headers[i], 'resizable') === "false"; + j = ts.getData( t, c.headers[i], 'resizable') === "false"; $t.children().filter('[data-column="' + i + '"]').toggleClass('resizable-false', j); }); // add wrapper inside each cell to allow for positioning of the resizable target block @@ -856,7 +1076,8 @@ $.tablesorter.addWidget({ // Firefox needs this inner div to position the resizer correctly $c.wrapInner('<div class="tablesorter-wrapper" style="position:relative;height:100%;width:100%"></div>'); } - $c = $c.slice(0,-1); // don't include the last column of the row + // don't include the last column of the row + if (!wo.resizable_addLastColumn) { $c = $c.slice(0,-1); } $cols = $cols ? $cols.add($c) : $c; }); $cols @@ -895,12 +1116,12 @@ $.tablesorter.addWidget({ position = e.pageX; }); $tbl.find('thead:first') - .bind('mouseup.tsresize mouseleave.tsresize', function(e){ + .bind('mouseup.tsresize mouseleave.tsresize', function(){ stopResize(); }) // right click to reset columns to default widths .bind('contextmenu.tsresize', function(){ - $.tablesorter.resizableReset(table); + ts.resizableReset(table); // $.isEmptyObject() needs jQuery 1.4+ var rtn = $.isEmptyObject ? $.isEmptyObject(s) : s === {}; // allow right click if already reset s = {}; @@ -908,7 +1129,7 @@ $.tablesorter.addWidget({ }); }, remove: function(table, c, wo){ - $(table) + c.$table .removeClass('hasResizable') .find('thead') .unbind('mouseup.tsresize mouseleave.tsresize contextmenu.tsresize') @@ -916,12 +1137,12 @@ $.tablesorter.addWidget({ .unbind('mousemove.tsresize mouseup.tsresize') // don't remove "tablesorter-wrapper" as uitheme uses it too .find('.tablesorter-resizer,.tablesorter-resizer-grip').remove(); - $.tablesorter.resizableReset(table); + ts.resizableReset(table); } }); -$.tablesorter.resizableReset = function(table){ - $(table.config.headerList).filter(':not(.resizable-false)').css('width',''); - if ($.tablesorter.storage) { $.tablesorter.storage(table, 'tablesorter-resizable', {}); } +ts.resizableReset = function(table){ + table.config.$headers.filter(':not(.resizable-false)').css('width',''); + if (ts.storage) { ts.storage(table, 'tablesorter-resizable', {}); } }; // Save table sort widget @@ -929,38 +1150,46 @@ $.tablesorter.resizableReset = function(table){ // saveSort widget option is true AND the // $.tablesorter.storage function is included // ************************** -$.tablesorter.addWidget({ +ts.addWidget({ id: 'saveSort', - init: function(table, thisWidget){ + priority: 20, + options: { + saveSort : true + }, + init: function(table, thisWidget, c, wo){ // run widget format before all other widgets are applied to the table - thisWidget.format(table, true); + thisWidget.format(table, c, wo, true); }, - format: function(table, init){ - var sl, time, c = table.config, - wo = c.widgetOptions, + format: function(table, c, wo, init){ + var sl, time, + $t = c.$table, ss = wo.saveSort !== false, // make saveSort active/inactive; default to true sortList = { "sortList" : c.sortList }; if (c.debug){ time = new Date(); } - if ($(table).hasClass('hasSaveSort')){ - if (ss && table.hasInitialized && $.tablesorter.storage){ - $.tablesorter.storage( table, 'tablesorter-savesort', sortList ); + if ($t.hasClass('hasSaveSort')){ + if (ss && table.hasInitialized && ts.storage){ + ts.storage( table, 'tablesorter-savesort', sortList ); if (c.debug){ - $.tablesorter.benchmark('saveSort widget: Saving last sort: ' + c.sortList, time); + ts.benchmark('saveSort widget: Saving last sort: ' + c.sortList, time); } } } else { // set table sort on initial run of the widget - $(table).addClass('hasSaveSort'); + $t.addClass('hasSaveSort'); sortList = ''; // get data - if ($.tablesorter.storage){ - sl = $.tablesorter.storage( table, 'tablesorter-savesort' ); + if (ts.storage){ + sl = ts.storage( table, 'tablesorter-savesort' ); sortList = (sl && sl.hasOwnProperty('sortList') && $.isArray(sl.sortList)) ? sl.sortList : ''; if (c.debug){ - $.tablesorter.benchmark('saveSort: Last sort loaded: "' + sortList + '"', time); + ts.benchmark('saveSort: Last sort loaded: "' + sortList + '"', time); } + $t.bind('saveSortReset', function(e){ + e.stopPropagation(); + ts.storage( table, 'tablesorter-savesort', '' ); + }); } // init is true when widget init is run, this will run this widget before all other widgets have initialized // this method allows using this widget in the original tablesorter plugin; but then it will run all widgets twice. @@ -968,13 +1197,13 @@ $.tablesorter.addWidget({ c.sortList = sortList; } else if (table.hasInitialized && sortList && sortList.length > 0){ // update sort change - $(table).trigger('sorton', [sortList]); + $t.trigger('sorton', [sortList]); } } }, - remove: function(table, c, wo){ + remove: function(table){ // clear storage - if ($.tablesorter.storage) { $.tablesorter.storage( table, 'tablesorter-savesort', '' ); } + if (ts.storage) { ts.storage( table, 'tablesorter-savesort', '' ); } } }); diff --git a/vendor/assets/stylesheets/jquery-tablesorter/filter.formatter.css b/vendor/assets/stylesheets/jquery-tablesorter/filter.formatter.css new file mode 100644 index 0000000..cb5dde1 --- /dev/null +++ b/vendor/assets/stylesheets/jquery-tablesorter/filter.formatter.css @@ -0,0 +1,183 @@ +/**** Filter Formatter Elements ****/ +.tablesorter .tablesorter-filter-row td { + text-align: center; + font-size: 0.9em; + font-weight: normal; +} + +/**** Sliders ****/ +/* shrink the sliders to look nicer inside of a table cell */ +.tablesorter .ui-slider, .tablesorter input.range { + width: 90%; + margin: 2px auto 2px auto; /* add enough top margin so the tooltips will fit */ + font-size: 0.8em; +} +.tablesorter .ui-slider { + top: 12px; +} +.tablesorter .ui-slider .ui-slider-handle { + width: 0.9em; + height: 0.9em; +} +.tablesorter .ui-datepicker { + font-size: 0.8em; +} +.tablesorter .ui-slider-horizontal { + height: 0.5em; +} +/* Add tooltips to slider handles */ +.tablesorter .value-popup:after { + content : attr(data-value); + position: absolute; + bottom: 14px; + left: -7px; + min-width: 18px; + height: 12px; + background-color: #444; + background-image: -webkit-gradient(linear, left top, left bottom, from(#444444), to(#999999)); + background-image: -webkit-linear-gradient(top, #444, #999); + background-image: -moz-linear-gradient(top, #444, #999); + background-image: -o-linear-gradient(top, #444, #999); + background-image: linear-gradient(to bottom, #444, #999); + -webkit-border-radius: 3px; + border-radius: 3px; + -webkit-background-clip: padding-box; background-clip: padding-box; + -webkit-box-shadow: 0px 0px 4px 0px #777; + box-shadow: 0px 0px 4px 0px #777; + border: #444 1px solid; + color: #fff; + font: 1em/1.1em Arial, Sans-Serif; + padding: 1px; + text-align: center; +} +.tablesorter .value-popup:before { + content: ""; + position: absolute; + width: 0; + height: 0; + border-top: 8px solid #777; + border-left: 8px solid transparent; + border-right: 8px solid transparent; + top: -8px; + left: 50%; + margin-left: -8px; + margin-top: -1px; +} + +/**** Date Picker ****/ +.tablesorter .dateFrom, .tablesorter .dateTo { + width: 80px; + margin: 2px 5px; +} + +/**** Color Picker/HTML5Number Toggle button ****/ +.tablesorter .button { + width: 14px; + height: 14px; + background: #fcfff4; + background: -webkit-linear-gradient(top, #fcfff4 0%, #dfe5d7 40%, #b3bead 100%); + background: -moz-linear-gradient(top, #fcfff4 0%, #dfe5d7 40%, #b3bead 100%); + background: -o-linear-gradient(top, #fcfff4 0%, #dfe5d7 40%, #b3bead 100%); + background: -ms-linear-gradient(top, #fcfff4 0%, #dfe5d7 40%, #b3bead 100%); + background: linear-gradient(top, #fcfff4 0%, #dfe5d7 40%, #b3bead 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fcfff4', endColorstr='#b3bead', GradientType=0 ); + margin: 1px 5px 1px 1px; + -webkit-border-radius: 25px; + -moz-border-radius: 25px; + border-radius: 25px; + -webkit-box-shadow: inset 0px 1px 1px white, 0px 1px 3px rgba(0,0,0,0.5); + -moz-box-shadow: inset 0px 1px 1px white, 0px 1px 3px rgba(0,0,0,0.5); + box-shadow: inset 0px 1px 1px white, 0px 1px 3px rgba(0,0,0,0.5); + position: relative; + top: 3px; + display: inline-block; +} + +.tablesorter .button label { + cursor: pointer; + position: absolute; + width: 10px; + height: 10px; + -webkit-border-radius: 25px; + -moz-border-radius: 25px; + border-radius: 25px; + left: 2px; + top: 2px; + -webkit-box-shadow: inset 0px 1px 1px rgba(0,0,0,0.5), 0px 1px 0px rgba(255,255,255,1); + -moz-box-shadow: inset 0px 1px 1px rgba(0,0,0,0.5), 0px 1px 0px rgba(255,255,255,1); + box-shadow: inset 0px 1px 1px rgba(0,0,0,0.5), 0px 1px 0px rgba(255,255,255,1); + background: #45484d; + background: -webkit-linear-gradient(top, #222 0%, #45484d 100%); + background: -moz-linear-gradient(top, #222 0%, #45484d 100%); + background: -o-linear-gradient(top, #222 0%, #45484d 100%); + background: -ms-linear-gradient(top, #222 0%, #45484d 100%); + background: linear-gradient(top, #222 0%, #45484d 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#222', endColorstr='#45484d', GradientType=0 ); +} + +.tablesorter .button label:after { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + opacity: 0; + content: ''; + position: absolute; + width: 8px; + height: 8px; + background: #55f; + background: -webkit-linear-gradient(top, #aaf 0%, #55f 100%); + background: -moz-linear-gradient(top, #aaf 0%, #55f 100%); + background: -o-linear-gradient(top, #aaf 0%, #55f 100%); + background: -ms-linear-gradient(top, #aaf 0%, #55f 100%); + background: linear-gradient(top, #aaf 0%, #55f 100%); + -webkit-border-radius: 25px; + -moz-border-radius: 25px; + border-radius: 25px; + top: 1px; + left: 1px; + -webkit-box-shadow: inset 0px 1px 1px #fff, 0px 1px 3px rgba(0,0,0,0.5); + -moz-box-shadow: inset 0px 1px 1px #fff, 0px 1px 3px rgba(0,0,0,0.5); + box-shadow: inset 0px 1px 1px #fff, 0px 1px 3px rgba(0,0,0,0.5); +} + +.tablesorter .button label:hover::after { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; + filter: alpha(opacity=30); + opacity: 0.3; +} + +.tablesorter .button input[type=checkbox] { + visibility: hidden; +} + +.tablesorter .button input[type=checkbox]:checked + label:after { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; + filter: alpha(opacity=100); + opacity: 1; +} + +.tablesorter .colorpicker { + width: 30px; + height: 18px; +} +.tablesorter .ui-spinner-input { + width: 100px; + height: 18px; +} +.tablesorter .currentColor, .tablesorter .ui-spinner { + position: relative; +} +.tablesorter input.number { + position: relative; +} + +/* hide filter row */ +.tablesorter .tablesorter-filter-row.hideme td * { + height: 1px; + min-height: 0; + border: 0; + padding: 0; + margin: 0; + /* don't use visibility: hidden because it disables tabbing */ + opacity: 0; + filter: alpha(opacity=0); +} diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css index d1b8809..5b46fc1 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css @@ -113,6 +113,11 @@ background-color: #5a646b; } +/* caption */ +caption { + background: #fff; +} + /* filter widget */ .tablesorter-blackice .tablesorter-filter-row td { background: #222; @@ -153,7 +158,7 @@ /* filters */ .tablesorter-blackice .tablesorter-filter { width: 98%; - height: inherit; + height: auto; margin: 0; padding: 4px; background-color: #fff; @@ -167,3 +172,9 @@ -o-transition: height 0.1s ease; transition: height 0.1s ease; } + +/* ajax error row */ +.tablesorter .tablesorter-errorRow td { + cursor: pointer; + background-color: #e6bf99; +} diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css index d90360a..8e11a35 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css @@ -148,6 +148,11 @@ background-color: #ebf0fa; } +/* caption */ +caption { + background: #fff; +} + /* filter widget */ .tablesorter-blue .tablesorter-filter-row td { background: #eee; @@ -188,7 +193,7 @@ /* filters */ .tablesorter-blue .tablesorter-filter { width: 98%; - height: inherit; + height: auto; margin: 0; padding: 4px; background-color: #fff; @@ -202,3 +207,9 @@ -o-transition: height 0.1s ease; transition: height 0.1s ease; } + +/* ajax error row */ +.tablesorter .tablesorter-errorRow td { + cursor: pointer; + background-color: #e6bf99; +} diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index eaf9b30..4c67f59 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -71,10 +71,15 @@ z-index: 1000; } +/* caption */ +caption { + background: #fff; +} + /* filter widget */ .tablesorter-bootstrap .tablesorter-filter-row .tablesorter-filter { width: 98%; - height: inherit; + height: auto; margin: 0 auto; padding: 4px 6px; background-color: #fff; @@ -87,6 +92,10 @@ -o-transition: height 0.1s ease; transition: height 0.1s ease; } +.tablesorter-bootstrap .tablesorter-filter-row .tablesorter-filter.disabled { + background: #eee; + cursor: not-allowed; +} .tablesorter-bootstrap .tablesorter-filter-row td { background: #eee; line-height: normal; @@ -121,4 +130,10 @@ } .tablesorter-bootstrap .tablesorter-pager .pagedisplay { border: 0; -} \ No newline at end of file +} + +/* ajax error row */ +.tablesorter .tablesorter-errorRow td { + cursor: pointer; + background-color: #e6bf99; +} diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css index 93292bc..a763fa2 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css @@ -112,6 +112,11 @@ background-color: #0f0f0f; } +/* caption */ +caption { + background: #fff; +} + /* filter widget */ .tablesorter-dark .tablesorter-filter-row td { background: #202020; @@ -154,7 +159,7 @@ /* filters */ .tablesorter-dark .tablesorter-filter { width: 98%; - height: inherit; + height: auto; margin: 4px; padding: 4px; background-color: #111; @@ -168,3 +173,9 @@ -o-transition: height 0.1s ease; transition: height 0.1s ease; } + +/* ajax error row */ +.tablesorter .tablesorter-errorRow td { + cursor: pointer; + background-color: #e6bf99; +} diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css index 9efd03e..2f158b7 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css @@ -22,7 +22,8 @@ Default Theme border-bottom: #ccc 2px solid; padding: 0; } -.tablesorter-default tfoot th { +.tablesorter-default tfoot th, +.tablesorter-default tfoot td { border: 0; } .tablesorter-default .header, @@ -114,6 +115,11 @@ Default Theme background-color: #f2f2f2; } +/* caption */ +caption { + background: #fff; +} + /* filter widget */ .tablesorter-default .tablesorter-filter-row td { background: #eee; @@ -155,7 +161,7 @@ Default Theme /* filters */ .tablesorter-default .tablesorter-filter { width: 95%; - height: inherit; + height: auto; margin: 4px; padding: 4px; background-color: #fff; @@ -169,3 +175,9 @@ Default Theme -o-transition: height 0.1s ease; transition: height 0.1s ease; } + +/* ajax error row */ +.tablesorter .tablesorter-errorRow td { + cursor: pointer; + background-color: #e6bf99; +} diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css index 72ccf99..0315b41 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css @@ -22,7 +22,6 @@ border-color: #82cffa #e7f2fb #96c4ea; border-style: solid; border-width: 1px; - height: 29px; padding: 3px 6px; font-size: 13px; font-weight: normal; @@ -133,9 +132,14 @@ .tablesorter-dropbox tr.even td.tertiary { } +/* caption */ +caption { + background: #fff; +} + /* Filter Widget */ -.tablesorter-dropbox.tablesorter-filter-row td { - background: #eee; +.tablesorter-dropbox .tablesorter-filter-row td { + background: #fff; line-height: normal; text-align: center; /* center the input */ -webkit-transition: line-height 0.1s ease; @@ -144,14 +148,14 @@ transition: line-height 0.1s ease; } /* optional disabled input styling */ -.tablesorter-dropbox.tablesorter-filter-row .disabled { +.tablesorter-dropbox .tablesorter-filter-row .disabled { opacity: 0.5; filter: alpha(opacity=50); cursor: not-allowed; } /* hidden filter row */ -.tablesorter-dropbox.tablesorter-filter-row.hideme td { +.tablesorter-dropbox .tablesorter-filter-row.hideme td { /*** *********************************************** ***/ /*** change this padding to modify the thickness ***/ /*** of the closed filter row (height = padding x 2) ***/ @@ -161,7 +165,7 @@ line-height: 0; cursor: pointer; } -.tablesorter-dropbox.tablesorter-filter-row.hideme .tablesorter-filter { +.tablesorter-dropbox .tablesorter-filter-row.hideme .tablesorter-filter { height: 1px; min-height: 0; border: 0; @@ -173,11 +177,11 @@ } /* filters */ -.tablesorter-dropbox.tablesorter-filter { +.tablesorter-dropbox .tablesorter-filter { width: 98%; - height: inherit; + height: auto; margin: 4px; - padding: 4px; + background-color: #fff; border: 1px solid #bbb; color: #333; @@ -189,3 +193,9 @@ -o-transition: height 0.1s ease; transition: height 0.1s ease; } + +/* ajax error row */ +.tablesorter .tablesorter-errorRow td { + cursor: pointer; + background-color: #e6bf99; +} diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css index 57f6bdd..00b2c10 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css @@ -131,6 +131,11 @@ background-color: #ebfaed; } +/* caption */ +caption { + background: #fff; +} + /* filter widget */ .tablesorter-green .tablesorter-filter-row td { background: #eee; @@ -171,7 +176,7 @@ /* filters */ .tablesorter-green .tablesorter-filter { width: 98%; - height: inherit; + height: auto; margin: 4px; padding: 4px; background-color: #fff; @@ -185,3 +190,9 @@ -o-transition: height 0.1s ease; transition: height 0.1s ease; } + +/* ajax error row */ +.tablesorter .tablesorter-errorRow td { + cursor: pointer; + background-color: #e6bf99; +} diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css index 3041c2c..2ec3669 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css @@ -167,6 +167,11 @@ background-color: #2073B7; } +/* caption */ +caption { + background: #fff; +} + /* filter widget */ .tablesorter-grey .tablesorter-filter-row td { background: #3c3c3c; @@ -207,7 +212,7 @@ /* filters */ .tablesorter-grey .tablesorter-filter { width: 98%; - height: inherit; + height: auto; margin: 0; padding: 4px; background-color: #6d6d6d; @@ -221,3 +226,9 @@ -o-transition: height 0.1s ease; transition: height 0.1s ease; } + +/* ajax error row */ +.tablesorter .tablesorter-errorRow td { + cursor: pointer; + background-color: #e6bf99; +} diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css index 72dd19b..7c1a9cb 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css @@ -122,6 +122,11 @@ background-color: #ebfafa; } +/* caption */ +caption { + background: #fff; +} + /* filter widget */ .tablesorter-ice .tablesorter-filter-row td { background: #eee; @@ -162,7 +167,7 @@ /* filters */ .tablesorter-ice .tablesorter-filter { width: 98%; - height: inherit; + height: auto; margin: 4px; padding: 4px; background-color: #fff; @@ -176,3 +181,9 @@ -o-transition: height 0.1s ease; transition: height 0.1s ease; } + +/* ajax error row */ +.tablesorter .tablesorter-errorRow td { + cursor: pointer; + background-color: #e6bf99; +} diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css index 740f1f5..da00e00 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css @@ -78,6 +78,11 @@ background-color: rgba(255,255,255,0.8); } +/* caption */ +caption { + background: #fff; +} + /* filter widget */ .tablesorter-jui .tablesorter-filter-row td { background: transparent; @@ -118,7 +123,7 @@ /* filters */ .tablesorter-jui .tablesorter-filter { width: 98%; - height: inherit; + height: auto; margin: 0; padding: 4px; background-color: #fff; @@ -132,3 +137,9 @@ -o-transition: height 0.1s ease; transition: height 0.1s ease; } + +/* ajax error row */ +.tablesorter .tablesorter-errorRow td { + cursor: pointer; + background-color: #e6bf99; +} From ae9519063cc2d40db37edadcf80446bd49ed9acf Mon Sep 17 00:00:00 2001 From: Erik Ernst <github@black-milk.de> Date: Tue, 9 Jul 2013 14:29:18 +0200 Subject: [PATCH 002/138] * Release v1.5.0 * Gem is now maintained by Erik-B. Ernst (@themilkman). Special thanks to Jun Lin (@linjunpop) for his work! --- CHANGELOG.markdown | 3 ++- MIT-LICENSE | 2 +- README.markdown | 2 -- jquery-tablesorter.gemspec | 6 +++--- lib/jquery-tablesorter/version.rb | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index 7d55cd2..920a58e 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -1,9 +1,10 @@ Changelog === -#### v1.4.1 - themilkman fork +#### v1.5.0 * Upgrade tablesorter to v2.10.8 * Rails 4 compatibility +* Gem is now maintained by Erik-B. Ernst (@themilkman). Special thanks to Jun Lin (@linjunpop) for his work! #### v1.4.1 diff --git a/MIT-LICENSE b/MIT-LICENSE index ae244e8..59b76f9 100644 --- a/MIT-LICENSE +++ b/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright 2013 Jun Lin +Copyright 2013 Jun Lin, Erik-B. Ernst Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.markdown b/README.markdown index 1ee8c2c..c7dc456 100644 --- a/README.markdown +++ b/README.markdown @@ -1,8 +1,6 @@ # jQuery Table Sorter plugin for Rails -[](http://stillmaintained.com/linjunpop/jquery-tablesorter-rails) [](http://badge.fury.io/rb/jquery-tablesorter) -[](http://coderwall.com/linjunpop) Simple integration of jquery-tablesorter into the asset pipeline. diff --git a/jquery-tablesorter.gemspec b/jquery-tablesorter.gemspec index 34bb556..a520685 100644 --- a/jquery-tablesorter.gemspec +++ b/jquery-tablesorter.gemspec @@ -7,9 +7,9 @@ require "jquery-tablesorter/version" Gem::Specification.new do |s| s.name = "jquery-tablesorter" s.version = JqueryTablesorter::VERSION - s.authors = ["Jun Lin"] - s.email = ["linjunpop@gmail.com"] - s.homepage = "https://github.com/linjunpop/jquery-tablesorter-rails" + s.authors = ["Jun Lin", "Erik-B. Ernst"] + s.email = ["github@black-milk.de"] + s.homepage = "https://github.com/themilkman/jquery-tablesorter-rails" s.summary = "Simple integration of jquery-tablesorter into the asset pipeline." s.description = "Simple integration of jquery-tablesorter into the asset pipeline." diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 6e5f231..8329edd 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = "1.4.1" + VERSION = "1.5.0" end From e4c6760d0d779d7f5c8bfff8ebf11669ba3bc8c2 Mon Sep 17 00:00:00 2001 From: Erik Ernst <github@black-milk.de> Date: Thu, 17 Oct 2013 16:58:29 +0200 Subject: [PATCH 003/138] * updated tablesorter to latest version (2.11.1) --- CHANGELOG.markdown | 4 + README.markdown | 2 +- jquery-tablesorter.gemspec | 1 + lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 551 ++++++++++-------- .../jquery-tablesorter/jquery.tablesorter.js | 299 ++++++---- ...ry.tablesorter.widgets-filter-formatter.js | 47 +- .../jquery.tablesorter.widgets.js | 106 ++-- .../jquery-tablesorter/theme.bootstrap.css | 24 +- 10 files changed, 564 insertions(+), 474 deletions(-) diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index 920a58e..50f45a6 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -1,5 +1,9 @@ Changelog === +#### v1.6.0 + +* Upgrade tablesorter to v2.11.1 + #### v1.5.0 * Upgrade tablesorter to v2.10.8 diff --git a/README.markdown b/README.markdown index c7dc456..21e7021 100644 --- a/README.markdown +++ b/README.markdown @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.10.8 (6/3/2013), [documentation] +Current tablesorter version: 2.11.1 (10/11/2013), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. diff --git a/jquery-tablesorter.gemspec b/jquery-tablesorter.gemspec index a520685..4fd33c7 100644 --- a/jquery-tablesorter.gemspec +++ b/jquery-tablesorter.gemspec @@ -12,6 +12,7 @@ Gem::Specification.new do |s| s.homepage = "https://github.com/themilkman/jquery-tablesorter-rails" s.summary = "Simple integration of jquery-tablesorter into the asset pipeline." s.description = "Simple integration of jquery-tablesorter into the asset pipeline." + s.license = "MIT" s.files = Dir["{vendor,lib}/**/*"] + ["MIT-LICENSE", "Rakefile", "README.markdown"] diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 8329edd..41b11ba 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = "1.5.0" + VERSION = "1.6.0" end diff --git a/tablesorter b/tablesorter index 96a8efb..458669a 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 96a8efb4ddf5198f1f9636826aa64a1e9ca5eeb0 +Subproject commit 458669a75dee8d50a33dfdf3eb9ca99c704fdc62 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 105e3cf..c3b7ca4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,11 +1,14 @@ /*! * tablesorter pager plugin - * updated 5/27/2013 + * updated 10/11/2013 */ /*jshint browser:true, jquery:true, unused:false */ +/*global toString:true */ ;(function($) { "use strict"; /*jshint supernew:true */ + var ts = $.tablesorter; + $.extend({ tablesorterPager: new function() { this.defaults = { @@ -56,6 +59,9 @@ // Number of visible rows size: 10, + // Save pager page & size if the storage script is loaded (requires $.tablesorter.storage in jquery.tablesorter.widgets.js) + savePages: true, + // if true, the table will remain the same height no matter how many records are displayed. The space is made up by an empty // table row set to a height to compensate; default is false fixedHeight: false, @@ -88,128 +94,139 @@ var $this = this, // hide arrows at extremes - pagerArrows = function(c, disable) { + pagerArrows = function(p, disable) { var a = 'addClass', r = 'removeClass', - d = c.cssDisabled, + d = p.cssDisabled, dis = !!disable, - tp = Math.min( c.totalPages, c.filteredPages ); - if ( c.updateArrows ) { - c.$container.find(c.cssFirst + ',' + c.cssPrev)[ ( dis || c.page === 0 ) ? a : r ](d); - c.$container.find(c.cssNext + ',' + c.cssLast)[ ( dis || c.page === tp - 1 ) ? a : r ](d); + tp = Math.min( p.totalPages, p.filteredPages ); + if ( p.updateArrows ) { + p.$container.find(p.cssFirst + ',' + p.cssPrev)[ ( dis || p.page === 0 ) ? a : r ](d); + p.$container.find(p.cssNext + ',' + p.cssLast)[ ( dis || p.page === tp - 1 ) ? a : r ](d); } }, - updatePageDisplay = function(table, c, flag) { - var i, p, s, t, out, - tc = table.config, - f = $(table).hasClass('hasFilters') && !c.ajaxUrl; - c.totalPages = Math.ceil( c.totalRows / c.size ); // needed for "pageSize" method - c.filteredRows = (f) ? tc.$tbodies.eq(0).children('tr:not(.' + (tc.widgetOptions && tc.widgetOptions.filter_filteredRow || 'filtered') + ',' + tc.selectorRemove + ')').length : c.totalRows; - c.filteredPages = (f) ? Math.ceil( c.filteredRows / c.size ) || 1 : c.totalPages; - if ( Math.min( c.totalPages, c.filteredPages ) >= 0 ) { - t = (c.size * c.page > c.filteredRows); - c.startRow = (t) ? 1 : (c.filteredRows === 0 ? 0 : c.size * c.page + 1); - c.page = (t) ? 0 : c.page; - c.endRow = Math.min( c.filteredRows, c.totalRows, c.size * ( c.page + 1 ) ); - out = c.$container.find(c.cssPageDisplay); - // form the output string - s = c.output.replace(/\{(page|filteredRows|filteredPages|totalPages|startRow|endRow|totalRows)\}/gi, function(m){ - return { - '{page}' : c.page + 1, - '{filteredRows}' : c.filteredRows, - '{filteredPages}' : c.filteredPages, - '{totalPages}' : c.totalPages, - '{startRow}' : c.startRow, - '{endRow}' : c.endRow, - '{totalRows}' : c.totalRows - }[m]; - }); + updatePageDisplay = function(table, p, flag) { + var i, pg, s, out, + c = table.config, + f = c.$table.hasClass('hasFilters') && !p.ajaxUrl, + t = (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered') + ',' + c.selectorRemove; + p.totalPages = Math.ceil( p.totalRows / p.size ); // needed for "pageSize" method + p.filteredRows = (f) ? c.$tbodies.eq(0).children('tr:not(.' + t + ')').length : p.totalRows; + p.filteredPages = (f) ? Math.ceil( p.filteredRows / p.size ) || 1 : p.totalPages; + if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) { + t = (p.size * p.page > p.filteredRows); + p.startRow = (t) ? 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1); + p.page = (t) ? 0 : p.page; + p.endRow = Math.min( p.filteredRows, p.totalRows, p.size * ( p.page + 1 ) ); + out = p.$container.find(p.cssPageDisplay); + // form the output string (can now get a new output string from the server) + s = ( p.ajaxData && p.ajaxData.hasOwnProperty('output') ? p.ajaxData.output || p.output : p.output ) + // {page} = one-based index; {page+#} = zero based index +/- value + .replace(/\{page([\-+]\d+)?\}/gi, function(m,n){ + return p.page + (n ? parseInt(n, 10) : 1); + }) + // {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object) + .replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m){ + var t = m.replace(/[{}\s]/g,''), a = t.split(':'), d = p.ajaxData; + return a.length > 1 && d && d[a[0]] ? d[a[0]][a[1]] : p[t] || (d ? d[t] : '') || ''; + }); if (out.length) { out[ (out[0].tagName === 'INPUT') ? 'val' : 'html' ](s); - if ( c.$goto.length ) { + if ( p.$goto.length ) { t = ''; - p = Math.min( c.totalPages, c.filteredPages ); - for ( i = 1; i <= p; i++ ) { + pg = Math.min( p.totalPages, p.filteredPages ); + for ( i = 1; i <= pg; i++ ) { t += '<option>' + i + '</option>'; } - c.$goto.html(t).val( c.page + 1 ); + p.$goto.html(t).val( p.page + 1 ); } } } - pagerArrows(c); - if (c.initialized && flag !== false) { $(table).trigger('pagerComplete', c); } + pagerArrows(p); + if (p.initialized && flag !== false) { + c.$table.trigger('pagerComplete', p); + // save pager info to storage + if (p.savePages && ts.storage) { + ts.storage(table, 'tablesorter-pager', { + page : p.page, + size : p.size + }); + } + } }, - fixHeight = function(table, c) { - var d, h, $b = table.config.$tbodies.eq(0); - if (c.fixedHeight) { + fixHeight = function(table, p) { + var d, h, + c = table.config, + $b = c.$tbodies.eq(0); + if (p.fixedHeight) { $b.find('tr.pagerSavedHeightSpacer').remove(); h = $.data(table, 'pagerSavedHeight'); if (h) { d = h - $b.height(); - if ( d > 5 && $.data(table, 'pagerLastSize') === c.size && $b.children('tr:visible').length < c.size ) { - $b.append('<tr class="pagerSavedHeightSpacer ' + table.config.selectorRemove.replace(/(tr)?\./g,'') + '" style="height:' + d + 'px;"></tr>'); + if ( d > 5 && $.data(table, 'pagerLastSize') === p.size && $b.children('tr:visible').length < p.size ) { + $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.replace(/(tr)?\./g,'') + '" style="height:' + d + 'px;"></tr>'); } } } }, - changeHeight = function(table, c) { + changeHeight = function(table, p) { var $b = table.config.$tbodies.eq(0); $b.find('tr.pagerSavedHeightSpacer').remove(); $.data(table, 'pagerSavedHeight', $b.height()); - fixHeight(table, c); - $.data(table, 'pagerLastSize', c.size); + fixHeight(table, p); + $.data(table, 'pagerLastSize', p.size); }, - hideRows = function(table, c){ - if (!c.ajaxUrl) { + hideRows = function(table, p){ + if (!p.ajaxUrl) { var i, - tc = table.config, - rows = tc.$tbodies.eq(0).children('tr:not(.' + tc.cssChildRow + ')'), + c = table.config, + rows = c.$tbodies.eq(0).children(), l = rows.length, - s = ( c.page * c.size ), - e = s + c.size, - f = tc.widgetOptions && tc.widgetOptions.filter_filteredRow || 'filtered', + s = ( p.page * p.size ), + e = s + p.size, + f = c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered', j = 0; // size counter for ( i = 0; i < l; i++ ){ if ( !rows[i].className.match(f) ) { rows[i].style.display = ( j >= s && j < e ) ? '' : 'none'; - j++; + // don't count child rows + j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) ? 0 : 1; } } } }, - hideRowsSetup = function(table, c){ - c.size = parseInt( c.$size.val(), 10 ) || c.size; - $.data(table, 'pagerLastSize', c.size); - pagerArrows(c); - if ( !c.removeRows ) { - hideRows(table, c); + hideRowsSetup = function(table, p){ + p.size = parseInt( p.$size.val(), 10 ) || p.size; + $.data(table, 'pagerLastSize', p.size); + pagerArrows(p); + if ( !p.removeRows ) { + hideRows(table, p); $(table).bind('sortEnd.pager filterEnd.pager', function(){ - hideRows(table, c); + hideRows(table, p); }); } }, - renderAjax = function(data, table, c, xhr, exception){ + renderAjax = function(data, table, p, xhr, exception){ // process data - if ( typeof(c.ajaxProcessing) === "function" ) { + if ( typeof(p.ajaxProcessing) === "function" ) { // ajaxProcessing result: [ total, rows, headers ] - var i, j, hsh, $f, $sh, th, d, l, $err, - $t = $(table), - tc = table.config, - hl = $t.find('thead th').length, tds = '', - result = c.ajaxProcessing(data, table) || [ 0, [] ], - // allow [ total, rows, headers ] or [ rows, total, headers ] - t = isNaN(result[0]) && !isNaN(result[1]); + var i, j, hsh, $f, $sh, t, th, d, l, $err, rr_count, + c = table.config, + $t = c.$table, + tds = '', + result = p.ajaxProcessing(data, table) || [ 0, [] ], + hl = $t.find('thead th').length; - $t.find('thead tr.' + c.cssErrorRow).remove(); // Clean up any previous error. + $t.find('thead tr.' + p.cssErrorRow).remove(); // Clean up any previous error. if ( exception ) { - $err = $('<tr class="' + c.cssErrorRow + '"><td style="text-align:center;" colspan="' + hl + '">' + ( + $err = $('<tr class="' + p.cssErrorRow + '"><td style="text-align:center;" colspan="' + hl + '">' + ( xhr.status === 0 ? 'Not connected, verify Network' : xhr.status === 404 ? 'Requested page not found [404]' : xhr.status === 500 ? 'Internal Server Error [500]' : @@ -222,16 +239,28 @@ }) // add error row to thead instead of tbody, or clicking on the header will result in a parser error .appendTo( $t.find('thead:first') ); - tc.$tbodies.eq(0).empty(); + c.$tbodies.eq(0).empty(); } else { - c.totalRows = result[t ? 1 : 0] || c.totalRows || 0; - d = result[t ? 0 : 1] || []; // row data + // process ajax object + if (toString.call(result) !== "[object Array]") { + p.ajaxData = result; + p.totalRows = result.total; + th = result.headers; + d = result.rows; + } else { + // allow [ total, rows, headers ] or [ rows, total, headers ] + t = isNaN(result[0]) && !isNaN(result[1]); + //ensure a zero returned row count doesn't fail the logical || + rr_count = result[t ? 1 : 0]; + p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count; + d = result[t ? 0 : 1] || []; // row data + th = result[2]; // headers + } l = d.length; - th = result[2]; // headers if (d instanceof jQuery) { // append jQuery object - tc.$tbodies.eq(0).empty().append(d); - } else if (d.length) { + c.$tbodies.eq(0).empty().append(d); + } else if (l) { // build table from array if ( l > 0 ) { for ( i = 0; i < l; i++ ) { @@ -244,21 +273,22 @@ } } // add rows to first tbody - tc.$tbodies.eq(0).html( tds ); + c.$tbodies.eq(0).html( tds ); } // only add new header text if the length matches if ( th && th.length === hl ) { hsh = $t.hasClass('hasStickyHeaders'); - $sh = hsh ? tc.$sticky.children('thead:first').children().children() : ''; + $sh = hsh ? c.$sticky.children('thead:first').children().children() : ''; $f = $t.find('tfoot tr:first').children(); - $t.find('th.' + tc.cssHeader).each(function(j){ + // don't change td headers (may contain pager) + c.$headers.filter('th').each(function(j){ var $t = $(this), icn; // add new test within the first span it finds, or just in the header - if ( $t.find('.' + tc.cssIcon).length ) { - icn = $t.find('.' + tc.cssIcon).clone(true); + if ( $t.find('.' + ts.css.icon).length ) { + icn = $t.find('.' + ts.css.icon).clone(true); $t.find('.tablesorter-header-inner').html( th[j] ).append(icn); if ( hsh && $sh.length ) { - icn = $sh.eq(j).find('.' + tc.cssIcon).clone(true); + icn = $sh.eq(j).find('.' + ts.css.icon).clone(true); $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ).append(icn); } } else { @@ -271,54 +301,56 @@ }); } } - if (tc.showProcessing) { - $.tablesorter.isProcessing(table); // remove loading icon + if (c.showProcessing) { + ts.isProcessing(table); // remove loading icon + } + p.totalPages = Math.ceil( p.totalRows / p.size ); + updatePageDisplay(table, p); + fixHeight(table, p); + if (p.initialized) { + $t.trigger('pagerChange', p); + $t.trigger('updateComplete'); + } else { + $t.trigger('update'); } - $t.trigger('update'); - c.totalPages = Math.ceil( c.totalRows / c.size ); - updatePageDisplay(table, c); - fixHeight(table, c); - if (c.initialized) { $t.trigger('pagerChange', c); } } - if (!c.initialized) { - c.initialized = true; - $(table).trigger('pagerInitialized', c); + if (!p.initialized) { + p.initialized = true; + $(table).trigger('pagerInitialized', p); } }, - getAjax = function(table, c){ - var url = getAjaxUrl(table, c), + getAjax = function(table, p){ + var url = getAjaxUrl(table, p), $doc = $(document), - tc = table.config; + c = table.config; if ( url !== '' ) { - if (tc.showProcessing) { - $.tablesorter.isProcessing(table, true); // show loading icon + if (c.showProcessing) { + ts.isProcessing(table, true); // show loading icon } $doc.bind('ajaxError.pager', function(e, xhr, settings, exception) { - if (url.match(settings.url)) { - renderAjax(null, table, c, xhr, exception); - $doc.unbind('ajaxError.pager'); - } + renderAjax(null, table, p, xhr, exception); + $doc.unbind('ajaxError.pager'); }); - c.ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl - c.ajaxObject.success = function(data) { - renderAjax(data, table, c); + p.ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl + p.ajaxObject.success = function(data) { + renderAjax(data, table, p); $doc.unbind('ajaxError.pager'); - if (typeof c.oldAjaxSuccess === 'function') { - c.oldAjaxSuccess(data); + if (typeof p.oldAjaxSuccess === 'function') { + p.oldAjaxSuccess(data); } }; - $.ajax(c.ajaxObject); + $.ajax(p.ajaxObject); } }, - getAjaxUrl = function(table, c) { - var url = (c.ajaxUrl) ? c.ajaxUrl + getAjaxUrl = function(table, p) { + var url = (p.ajaxUrl) ? p.ajaxUrl // allow using "{page+1}" in the url string to switch to a non-zero based index - .replace(/\{page([\-+]\d+)?\}/, function(s,n){ return c.page + (n ? parseInt(n, 10) : 0); }) - .replace(/\{size\}/g, c.size) : '', + .replace(/\{page([\-+]\d+)?\}/, function(s,n){ return p.page + (n ? parseInt(n, 10) : 0); }) + .replace(/\{size\}/g, p.size) : '', sl = table.config.sortList, - fl = c.currentFilters || [], + fl = p.currentFilters || [], sortCol = url.match(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/), filterCol = url.match(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/), arry = []; @@ -341,147 +373,143 @@ // if the arry is empty, just add the fcol parameter... "&{filterList:fcol}" becomes "&fcol" url = url.replace(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : filterCol ); } - if ( typeof(c.customAjaxUrl) === "function" ) { - url = c.customAjaxUrl(table, url); + if ( typeof(p.customAjaxUrl) === "function" ) { + url = p.customAjaxUrl(table, url); } return url; }, - renderTable = function(table, rows, c) { - c.isDisabled = false; // needed because sorting will change the page and re-enable the pager + renderTable = function(table, rows, p) { + p.isDisabled = false; // needed because sorting will change the page and re-enable the pager var i, j, o, $tb, l = rows.length, - s = ( c.page * c.size ), - e = ( s + c.size ); + s = ( p.page * p.size ), + e = ( s + p.size ); if ( l < 1 ) { return; } // empty table, abort! - if (c.initialized) { $(table).trigger('pagerChange', c); } - if ( !c.removeRows ) { - hideRows(table, c); + if (p.initialized) { $(table).trigger('pagerChange', p); } + if ( !p.removeRows ) { + hideRows(table, p); } else { if ( e > rows.length ) { e = rows.length; } - $.tablesorter.clearTableBody(table); - $tb = $.tablesorter.processTbody(table, table.config.$tbodies.eq(0), true); + ts.clearTableBody(table); + $tb = ts.processTbody(table, table.config.$tbodies.eq(0), true); for ( i = s; i < e; i++ ) { - o = rows[i]; - l = o.length; - for ( j = 0; j < l; j++ ) { - $tb.appendChild(o[j]); - } + $tb.append(rows[i]); } - $.tablesorter.processTbody(table, $tb, false); + ts.processTbody(table, $tb, false); } - if ( c.page >= c.totalPages ) { - moveToLastPage(table, c); + if ( p.page >= p.totalPages ) { + moveToLastPage(table, p); } - updatePageDisplay(table, c); - if ( !c.isDisabled ) { fixHeight(table, c); } + updatePageDisplay(table, p); + if ( !p.isDisabled ) { fixHeight(table, p); } $(table).trigger('applyWidgets'); }, - showAllRows = function(table, c){ - if ( c.ajax ) { - pagerArrows(c, true); + showAllRows = function(table, p){ + if ( p.ajax ) { + pagerArrows(p, true); } else { - c.isDisabled = true; - $.data(table, 'pagerLastPage', c.page); - $.data(table, 'pagerLastSize', c.size); - c.page = 0; - c.size = c.totalRows; - c.totalPages = 1; + p.isDisabled = true; + $.data(table, 'pagerLastPage', p.page); + $.data(table, 'pagerLastSize', p.size); + p.page = 0; + p.size = p.totalRows; + p.totalPages = 1; $(table).find('tr.pagerSavedHeightSpacer').remove(); - renderTable(table, table.config.rowsCopy, c); + renderTable(table, table.config.rowsCopy, p); } // disable size selector - c.$size.add(c.$goto).each(function(){ - $(this).addClass(c.cssDisabled)[0].disabled = true; + p.$size.add(p.$goto).each(function(){ + $(this).addClass(p.cssDisabled)[0].disabled = true; }); }, - moveToPage = function(table, c, flag) { - if ( c.isDisabled ) { return; } - var p = Math.min( c.totalPages, c.filteredPages ); - if ( c.page < 0 ) { c.page = 0; } - if ( c.page > ( p - 1 ) && p !== 0 ) { c.page = p - 1; } - if (c.ajax) { - getAjax(table, c); - } else if (!c.ajax) { - renderTable(table, table.config.rowsCopy, c); + moveToPage = function(table, p, flag) { + if ( p.isDisabled ) { return; } + var pg = Math.min( p.totalPages, p.filteredPages ); + if ( p.page < 0 ) { p.page = 0; } + if ( p.page > ( pg - 1 ) && pg !== 0 ) { p.page = pg - 1; } + if (p.ajax) { + getAjax(table, p); + } else if (!p.ajax) { + renderTable(table, table.config.rowsCopy, p); } - $.data(table, 'pagerLastPage', c.page); + $.data(table, 'pagerLastPage', p.page); $.data(table, 'pagerUpdateTriggered', true); - if (c.initialized && flag !== false) { - $(table).trigger('pageMoved', c); + if (p.initialized && flag !== false) { + $(table).trigger('pageMoved', p); } }, - setPageSize = function(table, size, c) { - c.size = size; - c.$size.val(size); - $.data(table, 'pagerLastPage', c.page); - $.data(table, 'pagerLastSize', c.size); - c.totalPages = Math.ceil( c.totalRows / c.size ); - moveToPage(table, c); + setPageSize = function(table, size, p) { + p.size = size; + p.$size.val(size); + $.data(table, 'pagerLastPage', p.page); + $.data(table, 'pagerLastSize', p.size); + p.totalPages = Math.ceil( p.totalRows / p.size ); + moveToPage(table, p); }, - moveToFirstPage = function(table, c) { - c.page = 0; - moveToPage(table, c); + moveToFirstPage = function(table, p) { + p.page = 0; + moveToPage(table, p); }, - moveToLastPage = function(table, c) { - c.page = ( Math.min( c.totalPages, c.filteredPages ) - 1 ); - moveToPage(table, c); + moveToLastPage = function(table, p) { + p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 ); + moveToPage(table, p); }, - moveToNextPage = function(table, c) { - c.page++; - if ( c.page >= ( Math.min( c.totalPages, c.filteredPages ) - 1 ) ) { - c.page = ( Math.min( c.totalPages, c.filteredPages ) - 1 ); + moveToNextPage = function(table, p) { + p.page++; + if ( p.page >= ( Math.min( p.totalPages, p.filteredPages ) - 1 ) ) { + p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 ); } - moveToPage(table, c); + moveToPage(table, p); }, - moveToPrevPage = function(table, c) { - c.page--; - if ( c.page <= 0 ) { - c.page = 0; + moveToPrevPage = function(table, p) { + p.page--; + if ( p.page <= 0 ) { + p.page = 0; } - moveToPage(table, c); + moveToPage(table, p); }, - destroyPager = function(table, c){ - showAllRows(table, c); - c.$container.hide(); // hide pager + destroyPager = function(table, p){ + showAllRows(table, p); + p.$container.hide(); // hide pager table.config.appender = null; // remove pager appender function $(table).unbind('destroy.pager sortEnd.pager filterEnd.pager enable.pager disable.pager'); }, - enablePager = function(table, c, triggered){ - var p = c.$size.removeClass(c.cssDisabled).removeAttr('disabled'); - c.$goto.removeClass(c.cssDisabled).removeAttr('disabled'); - c.isDisabled = false; - c.page = $.data(table, 'pagerLastPage') || c.page || 0; - c.size = $.data(table, 'pagerLastSize') || parseInt(p.find('option[selected]').val(), 10) || c.size; - p.val(c.size); // set page size - c.totalPages = Math.ceil( Math.min( c.totalPages, c.filteredPages ) / c.size); + enablePager = function(table, p, triggered){ + var pg = p.$size.removeClass(p.cssDisabled).removeAttr('disabled'); + p.$goto.removeClass(p.cssDisabled).removeAttr('disabled'); + p.isDisabled = false; + p.page = $.data(table, 'pagerLastPage') || p.page || 0; + p.size = $.data(table, 'pagerLastSize') || parseInt(pg.find('option[selected]').val(), 10) || p.size; + pg.val(p.size); // set page size + p.totalPages = Math.ceil( Math.min( p.totalPages, p.filteredPages ) / p.size); if ( triggered ) { $(table).trigger('update'); - setPageSize(table, c.size, c); - hideRowsSetup(table, c); - fixHeight(table, c); + setPageSize(table, p.size, p); + hideRowsSetup(table, p); + fixHeight(table, p); } }; $this.appender = function(table, rows) { - var c = table.config.pager; - if ( !c.ajax ) { + var p = table.config.pager; + if ( !p.ajax ) { table.config.rowsCopy = rows; - c.totalRows = rows.length; - c.size = $.data(table, 'pagerLastSize') || c.size; - c.totalPages = Math.ceil(c.totalRows / c.size); - renderTable(table, rows, c); + p.totalRows = rows.length; + p.size = $.data(table, 'pagerLastSize') || p.size; + p.totalPages = Math.ceil(p.totalRows / p.size); + renderTable(table, rows, p); } }; @@ -490,21 +518,26 @@ // check if tablesorter has initialized if (!(this.config && this.hasInitialized)) { return; } var t, ctrls, fxn, - config = this.config, - c = config.pager = $.extend( {}, $.tablesorterPager.defaults, settings ), - table = this, - tc = table.config, - $t = $(table), - // added in case the pager is reinitialized after being destroyed. - pager = c.$container = $(c.container).addClass('tablesorter-pager').show(); - c.oldAjaxSuccess = c.oldAjaxSuccess || c.ajaxObject.success; - config.appender = $this.appender; + table = this, + c = table.config, + p = c.pager = $.extend( {}, $.tablesorterPager.defaults, settings ), + $t = c.$table, + // added in case the pager is reinitialized after being destroyed. + pager = p.$container = $(p.container).addClass('tablesorter-pager').show(); + p.oldAjaxSuccess = p.oldAjaxSuccess || p.ajaxObject.success; + c.appender = $this.appender; + + if (p.savePages && ts.storage) { + t = ts.storage(table, 'tablesorter-pager'); + p.page = isNaN(t.page) ? p.page : t.page; + p.size = isNaN(t.size) ? p.size : t.size; + } $t .unbind('filterStart.pager filterEnd.pager sortEnd.pager disable.pager enable.pager destroy.pager update.pager pageSize.pager') .bind('filterStart.pager', function(e, filters) { $.data(table, 'pagerUpdateTriggered', false); - c.currentFilters = filters; + p.currentFilters = filters; }) // update pager after filter widget completes .bind('filterEnd.pager sortEnd.pager', function(e) { @@ -513,52 +546,55 @@ $.data(table, 'pagerUpdateTriggered', false); return; } - moveToPage(table, c, false); - updatePageDisplay(table, c, false); - fixHeight(table, c); + //only run the server side sorting if it has been enabled + if (e.type === "filterEnd" || (e.type === "sortEnd" && c.serverSideSorting)) { + moveToPage(table, p, false); + } + updatePageDisplay(table, p, false); + fixHeight(table, p); }) .bind('disable.pager', function(e){ e.stopPropagation(); - showAllRows(table, c); + showAllRows(table, p); }) .bind('enable.pager', function(e){ e.stopPropagation(); - enablePager(table, c, true); + enablePager(table, p, true); }) .bind('destroy.pager', function(e){ e.stopPropagation(); - destroyPager(table, c); + destroyPager(table, p); }) .bind('update.pager', function(e){ e.stopPropagation(); - hideRows(table, c); + hideRows(table, p); }) .bind('pageSize.pager', function(e,v){ e.stopPropagation(); - setPageSize(table, parseInt(v, 10) || 10, c); - hideRows(table, c); - updatePageDisplay(table, c, false); - if (c.$size.length) { c.$size.val(c.size); } // twice? + setPageSize(table, parseInt(v, 10) || 10, p); + hideRows(table, p); + updatePageDisplay(table, p, false); + if (p.$size.length) { p.$size.val(p.size); } // twice? }) .bind('pageSet.pager', function(e,v){ e.stopPropagation(); - c.page = (parseInt(v, 10) || 1) - 1; - if (c.$goto.length) { c.$goto.val(c.size); } // twice? - moveToPage(table, c); - updatePageDisplay(table, c, false); + p.page = (parseInt(v, 10) || 1) - 1; + if (p.$goto.length) { p.$goto.val(p.size); } // twice? + moveToPage(table, p); + updatePageDisplay(table, p, false); }); // clicked controls - ctrls = [ c.cssFirst, c.cssPrev, c.cssNext, c.cssLast ]; + ctrls = [ p.cssFirst, p.cssPrev, p.cssNext, p.cssLast ]; fxn = [ moveToFirstPage, moveToPrevPage, moveToNextPage, moveToLastPage ]; pager.find(ctrls.join(',')) .unbind('click.pager') .bind('click.pager', function(e){ var i, $t = $(this), l = ctrls.length; - if ( !$t.hasClass(c.cssDisabled) ) { + if ( !$t.hasClass(p.cssDisabled) ) { for (i = 0; i < l; i++) { if ($t.is(ctrls[i])) { - fxn[i](table, c); + fxn[i](table, p); break; } } @@ -567,57 +603,56 @@ }); // goto selector - c.$goto = pager.find(c.cssGoto); - if ( c.$goto.length ) { - c.$goto + p.$goto = pager.find(p.cssGoto); + if ( p.$goto.length ) { + p.$goto .unbind('change') .bind('change', function(){ - c.page = $(this).val() - 1; - moveToPage(table, c); + p.page = $(this).val() - 1; + moveToPage(table, p); }); - updatePageDisplay(table, c, false); } // page size selector - c.$size = pager.find(c.cssPageSize); - if ( c.$size.length ) { - c.$size.unbind('change.pager').bind('change.pager', function() { - c.$size.val( $(this).val() ); // in case there are more than one pagers - if ( !$(this).hasClass(c.cssDisabled) ) { - setPageSize(table, parseInt( $(this).val(), 10 ), c); - changeHeight(table, c); + p.$size = pager.find(p.cssPageSize); + if ( p.$size.length ) { + p.$size.unbind('change.pager').bind('change.pager', function() { + p.$size.val( $(this).val() ); // in case there are more than one pagers + if ( !$(this).hasClass(p.cssDisabled) ) { + setPageSize(table, parseInt( $(this).val(), 10 ), p); + changeHeight(table, p); } return false; }); } // clear initialized flag - c.initialized = false; + p.initialized = false; // before initialization event - $t.trigger('pagerBeforeInitialized', c); + $t.trigger('pagerBeforeInitialized', p); - enablePager(table, c, false); + enablePager(table, p, false); - if ( typeof(c.ajaxUrl) === 'string' ) { + if ( typeof(p.ajaxUrl) === 'string' ) { // ajax pager; interact with database - c.ajax = true; + p.ajax = true; //When filtering with ajax, allow only custom filtering function, disable default filtering since it will be done server side. - tc.widgetOptions.filter_serversideFiltering = true; - tc.serverSideSorting = true; - moveToPage(table, c); + c.widgetOptions.filter_serversideFiltering = true; + c.serverSideSorting = true; + moveToPage(table, p); } else { - c.ajax = false; + p.ajax = false; // Regular pager; all rows stored in memory $(this).trigger("appendCache", true); - hideRowsSetup(table, c); + hideRowsSetup(table, p); } - changeHeight(table, c); + changeHeight(table, p); // pager initialized - if (!c.ajax) { - c.initialized = true; - $(table).trigger('pagerInitialized', c); + if (!p.ajax) { + p.initialized = true; + $(table).trigger('pagerInitialized', p); } }); }; @@ -629,4 +664,4 @@ $.fn.extend({ tablesorterPager: $.tablesorterPager.construct }); -})(jQuery); \ No newline at end of file +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index a955fb2..3acb332 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ -/*! -* TableSorter 2.10.8 - Client-side table sorting with ease! +/**! +* TableSorter 2.11.1 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.10.8"; + ts.version = "2.11.1"; ts.parsers = []; ts.widgets = []; @@ -75,16 +75,17 @@ // *** callbacks initialized : null, // function(table){}, - // *** css class names - tableClass : 'tablesorter', - cssAsc : 'tablesorter-headerAsc', - cssChildRow : 'tablesorter-childRow', // previously "expand-child" - cssDesc : 'tablesorter-headerDesc', - cssHeader : 'tablesorter-header', - cssHeaderRow : 'tablesorter-headerRow', - cssIcon : 'tablesorter-icon', // if this class exists, a <i> will be added to the header automatically - cssInfoBlock : 'tablesorter-infoOnly', // don't sort tbody with this class name - cssProcessing : 'tablesorter-processing', // processing icon applied to header during sort/filter + // *** extra css class names + tableClass : '', + cssAsc : '', + cssDesc : '', + cssHeader : '', + cssHeaderRow : '', + cssProcessing : '', // processing icon applied to header during sort/filter + + cssChildRow : 'tablesorter-childRow', // class name indiciating that a row is to be attached to the its parent + cssIcon : 'tablesorter-icon', // if this class exists, a <i> will be added to the header automatically + cssInfoBlock : 'tablesorter-infoOnly', // don't sort tbody with this class name (only one class name allowed here!) // *** selectors selectorHeaders : '> thead th, > thead td', @@ -105,6 +106,20 @@ }; + // internal css classes - these will ALWAYS be added to + // the table and MUST only contain one class name - fixes #381 + ts.css = { + table : 'tablesorter', + childRow : 'tablesorter-childRow', + header : 'tablesorter-header', + headerRow : 'tablesorter-headerRow', + icon : 'tablesorter-icon', + info : 'tablesorter-infoOnly', + processing : 'tablesorter-processing', + sortAsc : 'tablesorter-headerAsc', + sortDesc : 'tablesorter-headerDesc' + }; + /* debuging utils */ function log(s) { if (typeof console !== "undefined" && typeof console.log !== "undefined") { @@ -121,6 +136,15 @@ ts.log = log; ts.benchmark = benchmark; + // $.isEmptyObject from jQuery v1.4 + function isEmptyObject(obj) { + /*jshint forin: false */ + for (var name in obj) { + return false; + } + return true; + } + function getElementText(table, node, cellIndex) { if (!node) { return ""; } var c = table.config, @@ -234,7 +258,7 @@ } for (k = 0; k < b.length; k++) { tc.cache[k] = { row: [], normalized: [] }; - // ignore tbodies with class name from css.cssInfoBlock + // ignore tbodies with class name from c.cssInfoBlock if (!$(b[k]).hasClass(tc.cssInfoBlock)) { totalRows = (b[k] && b[k].rows.length) || 0; totalCells = (b[k].rows[0] && b[k].rows[0].cells.length) || 0; @@ -281,7 +305,7 @@ c2 = c.cache, r, n, totalRows, checkCell, $bk, $tb, i, j, k, l, pos, appendTime; - if (!c2[0]) { return; } // empty table - fixes #206 + if (isEmptyObject(c2)) { return; } // empty table - fixes #206/#346 if (c.debug) { appendTime = new Date(); } @@ -319,6 +343,7 @@ if (!init) { ts.applyWidget(table); } // trigger sortend $(table).trigger("sortEnd", table); + $(table).trigger("updateComplete", table); } // computeTableHeaderCellIndexes from: @@ -380,7 +405,7 @@ if (c.debug) { time = new Date(); } - i = c.cssIcon ? '<i class="' + c.cssIcon + '"></i>' : ''; // add icon if cssIcon option exists + i = c.cssIcon ? '<i class="' + c.cssIcon + ' ' + ts.css.icon + '"></i>' : ''; // add icon if cssIcon option exists c.$headers = $(table).find(c.selectorHeaders).each(function(index) { $t = $(this); ch = c.headers[index]; @@ -403,11 +428,11 @@ if (typeof lock !== 'undefined' && lock !== false) { this.order = this.lockedOrder = formatSortingOrder(lock) ? [1,1,1] : [0,0,0]; } - $t.addClass(c.cssHeader); + $t.addClass(ts.css.header + ' ' + c.cssHeader); // add cell to headerList c.headerList[index] = this; // add to parent in case there are multiple rows - $t.parent().addClass(c.cssHeaderRow); + $t.parent().addClass(ts.css.headerRow + ' ' + c.cssHeaderRow); // allow keyboard cursor to focus on element $t.attr("tabindex", 0); }); @@ -443,7 +468,7 @@ var f, i, j, l, c = table.config, list = c.sortList, - css = [c.cssAsc, c.cssDesc], + css = [ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc], // find the footer $t = $(table).find('tfoot tr').children().removeClass(css.join(' ')); // remove all header information @@ -474,7 +499,8 @@ if (table.config.widthFixed && $(table).find('colgroup').length === 0) { var colgroup = $('<colgroup>'), overallWidth = $(table).width(); - $(table.tBodies[0]).find("tr:first").children("td").each(function() { + // only add col for visible columns - fixes #371 + $(table.tBodies[0]).find("tr:first").children("td:visible").each(function() { colgroup.append($('<col>').css('width', parseInt(($(this).width()/overallWidth)*1000, 10)/10 + '%')); }); $(table).prepend(colgroup); @@ -516,7 +542,7 @@ i = cell; c.$headers.each(function() { // only reset counts on columns that weren't just clicked on and if not included in a multisort - if (this !== i && (k || !$(this).is('.' + c.cssDesc + ',.' + c.cssAsc))) { + if (this !== i && (k || !$(this).is('.' + ts.css.sortDesc + ',.' + ts.css.sortAsc))) { this.count = -1; } }); @@ -606,7 +632,7 @@ var dir = 0, tc = table.config, sortList = tc.sortList, l = sortList.length, bl = table.tBodies.length, sortTime, i, k, c, colMax, cache, lc, s, order, orgOrderCol; - if (tc.serverSideSorting || !tc.cache[0]) { // empty table - fixes #206 + if (tc.serverSideSorting || isEmptyObject(tc.cache)) { // empty table - fixes #206/#346 return; } if (tc.debug) { sortTime = new Date(); } @@ -641,7 +667,10 @@ } function resortComplete($table, callback){ - $table.trigger('updateComplete'); + var c = $table[0].config; + if (c.pager && !c.pager.ajax) { + $table.trigger('updateComplete'); + } if (typeof callback === "function") { callback($table[0]); } @@ -671,16 +700,16 @@ .bind('mousedown.tablesorter mouseup.tablesorter sort.tablesorter keypress.tablesorter', function(e, external) { // only recognize left clicks or enter if ( ((e.which || e.button) !== 1 && !/sort|keypress/.test(e.type)) || (e.type === 'keypress' && e.which !== 13) ) { - return false; + return; } // ignore long clicks (prevents resizable widget from initializing a sort) - if (e.type === 'mouseup' && external !== true && (new Date().getTime() - downTime > 250)) { return false; } + if (e.type === 'mouseup' && external !== true && (new Date().getTime() - downTime > 250)) { return; } // set timer on mousedown if (e.type === 'mousedown') { downTime = new Date().getTime(); return e.target.tagName === "INPUT" ? '' : !c.cancelSelection; } - if (c.delayInit && !c.cache) { buildCache(table); } + if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); } // jQuery v1.2.6 doesn't have closest() var $cell = /TH|TD/.test(this.tagName) ? $(this) : $(this).parents('th, td').filter(':first'), cell = $cell[0]; if (!cell.sortDisabled) { @@ -768,12 +797,15 @@ checkResort($this, resort, callback); }) .bind("sorton.tablesorter", function(e, list, callback, init) { + var c = table.config; e.stopPropagation(); $this.trigger("sortStart", this); // update header count index updateHeaderSortCount(table, list); // set css for headers setHeadersCss(table); + // fixes #346 + if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); } $this.trigger("sortBegin", this); // sort the table and append it to the dom multisort(table); @@ -811,83 +843,99 @@ /* public methods */ ts.construct = function(settings) { return this.each(function() { - // if no thead or tbody, or tablesorter is already present, quit - if (!this.tHead || this.tBodies.length === 0 || this.hasInitialized === true) { - return (this.config && this.config.debug) ? log('stopping initialization! No thead, tbody or tablesorter has already been initialized') : ''; - } - // declare - var $this = $(this), table = this, - c, k = '', - m = $.metadata; - // initialization flag - table.hasInitialized = false; - // table is being processed flag - table.isProcessing = true; - // new blank config object - table.config = {}; - // merge and extend - c = $.extend(true, table.config, ts.defaults, settings); - // save the settings where they read - $.data(table, "tablesorter", c); - if (c.debug) { $.data( table, 'startoveralltimer', new Date()); } - // constants - c.supportsTextContent = $('<span>x</span>')[0].textContent === 'x'; - c.supportsDataObject = parseFloat($.fn.jquery) >= 1.4; - // digit sort text location; keeping max+/- for backwards compatibility - c.string = { 'max': 1, 'min': -1, 'max+': 1, 'max-': -1, 'zero': 0, 'none': 0, 'null': 0, 'top': true, 'bottom': false }; - // add table theme class only if there isn't already one there - if (!/tablesorter\-/.test($this.attr('class'))) { - k = (c.theme !== '' ? ' tablesorter-' + c.theme : ''); - } - c.$table = $this.addClass(c.tableClass + k); - c.$tbodies = $this.children('tbody:not(.' + c.cssInfoBlock + ')'); - // build headers - buildHeaders(table); - // fixate columns if the users supplies the fixedWidth option - // do this after theme has been applied - fixColumnWidth(table); - // try to auto detect column type, and store in tables config - buildParserCache(table); - // build the cache for the tbody cells - // delayInit will delay building the cache until the user starts a sort - if (!c.delayInit) { buildCache(table); } - // bind all header events and methods - bindEvents(table); - // get sort list from jQuery data or metadata - // in jQuery < 1.4, an error occurs when calling $this.data() - if (c.supportsDataObject && typeof $this.data().sortlist !== 'undefined') { - c.sortList = $this.data().sortlist; - } else if (m && ($this.metadata() && $this.metadata().sortlist)) { - c.sortList = $this.metadata().sortlist; - } - // apply widget init code - ts.applyWidget(table, true); - // if user has supplied a sort list to constructor - if (c.sortList.length > 0) { - $this.trigger("sorton", [c.sortList, {}, !c.initWidgets]); - } else if (c.initWidgets) { - // apply widget format - ts.applyWidget(table); + var table = this, + // merge & extend config options + c = $.extend(true, {}, ts.defaults, settings); + // create a table from data (build table widget) + if (!table.hasInitialized && ts.buildTable && this.tagName !== 'TABLE') { + // return the table (in case the original target is the table's container) + ts.buildTable(table, c); } + ts.setup(table, c); + }); + }; - // show processesing icon - if (c.showProcessing) { - $this - .unbind('sortBegin.tablesorter sortEnd.tablesorter') - .bind('sortBegin.tablesorter sortEnd.tablesorter', function(e) { - ts.isProcessing(table, e.type === 'sortBegin'); - }); - } + ts.setup = function(table, c) { + // if no thead or tbody, or tablesorter is already present, quit + if (!table || !table.tHead || table.tBodies.length === 0 || table.hasInitialized === true) { + return c.debug ? log('stopping initialization! No table, thead, tbody or tablesorter has already been initialized') : ''; + } - // initialized - table.hasInitialized = true; - table.isProcessing = false; - if (c.debug) { - ts.benchmark("Overall initialization time", $.data( table, 'startoveralltimer')); - } - $this.trigger('tablesorter-initialized', table); - if (typeof c.initialized === 'function') { c.initialized(table); } - }); + var k = '', + $this = $(table), + m = $.metadata; + // initialization flag + table.hasInitialized = false; + // table is being processed flag + table.isProcessing = true; + // make sure to store the config object + table.config = c; + // save the settings where they read + $.data(table, "tablesorter", c); + if (c.debug) { $.data( table, 'startoveralltimer', new Date()); } + + // constants + c.supportsTextContent = $('<span>x</span>')[0].textContent === 'x'; + // removing this in version 3 (only supports jQuery 1.7+) + c.supportsDataObject = (function(version) { + version[0] = parseInt(version[0], 10); + return (version[0] > 1) || (version[0] === 1 && parseInt(version[1], 10) >= 4); + })($.fn.jquery.split(".")); + // digit sort text location; keeping max+/- for backwards compatibility + c.string = { 'max': 1, 'min': -1, 'max+': 1, 'max-': -1, 'zero': 0, 'none': 0, 'null': 0, 'top': true, 'bottom': false }; + // add table theme class only if there isn't already one there + if (!/tablesorter\-/.test($this.attr('class'))) { + k = (c.theme !== '' ? ' tablesorter-' + c.theme : ''); + } + c.$table = $this.addClass(ts.css.table + ' ' + c.tableClass + k); + c.$tbodies = $this.children('tbody:not(.' + c.cssInfoBlock + ')'); + c.widgetInit = {}; // keep a list of initialized widgets + // build headers + buildHeaders(table); + // fixate columns if the users supplies the fixedWidth option + // do this after theme has been applied + fixColumnWidth(table); + // try to auto detect column type, and store in tables config + buildParserCache(table); + // build the cache for the tbody cells + // delayInit will delay building the cache until the user starts a sort + if (!c.delayInit) { buildCache(table); } + // bind all header events and methods + bindEvents(table); + // get sort list from jQuery data or metadata + // in jQuery < 1.4, an error occurs when calling $this.data() + if (c.supportsDataObject && typeof $this.data().sortlist !== 'undefined') { + c.sortList = $this.data().sortlist; + } else if (m && ($this.metadata() && $this.metadata().sortlist)) { + c.sortList = $this.metadata().sortlist; + } + // apply widget init code + ts.applyWidget(table, true); + // if user has supplied a sort list to constructor + if (c.sortList.length > 0) { + $this.trigger("sorton", [c.sortList, {}, !c.initWidgets]); + } else if (c.initWidgets) { + // apply widget format + ts.applyWidget(table); + } + + // show processesing icon + if (c.showProcessing) { + $this + .unbind('sortBegin.tablesorter sortEnd.tablesorter') + .bind('sortBegin.tablesorter sortEnd.tablesorter', function(e) { + ts.isProcessing(table, e.type === 'sortBegin'); + }); + } + + // initialized + table.hasInitialized = true; + table.isProcessing = false; + if (c.debug) { + ts.benchmark("Overall initialization time", $.data( table, 'startoveralltimer')); + } + $this.trigger('tablesorter-initialized', table); + if (typeof c.initialized === 'function') { c.initialized(table); } }; // *** Process table *** @@ -896,7 +944,7 @@ table = $(table); var c = table[0].config, // default to all headers - $h = $ths || table.find('.' + c.cssHeader); + $h = $ths || table.find('.' + ts.css.header); if (toggle) { if (c.sortList.length > 0) { // get headers from the sortList @@ -905,9 +953,9 @@ return this.sortDisabled ? false : ts.isValueInArray( parseFloat($(this).attr('data-column')), c.sortList); }); } - $h.addClass(c.cssProcessing); + $h.addClass(ts.css.processing + ' ' + c.cssProcessing); } else { - $h.removeClass(c.cssProcessing); + $h.removeClass(ts.css.processing + ' ' + c.cssProcessing); } }; @@ -951,7 +999,7 @@ ts.refreshWidgets(table, true, true); var $t = $(table), c = table.config, $h = $t.find('thead:first'), - $r = $h.find('tr.' + c.cssHeaderRow).removeClass(c.cssHeaderRow), + $r = $h.find('tr.' + ts.css.headerRow).removeClass(ts.css.headerRow + ' ' + c.cssHeaderRow), $f = $t.find('tfoot:first > tr').children('th, td'); // remove widget added rows, just in case $h.find('tr').not($r).remove(); @@ -960,12 +1008,12 @@ .removeData('tablesorter') .unbind('sortReset update updateAll updateRows updateCell addRows sorton appendCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress sortBegin sortEnd '.split(' ').join('.tablesorter ')); c.$headers.add($f) - .removeClass(c.cssHeader + ' ' + c.cssAsc + ' ' + c.cssDesc) + .removeClass( [ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc].join(' ') ) .removeAttr('data-column'); $r.find(c.selectorSort).unbind('mousedown.tablesorter mouseup.tablesorter keypress.tablesorter'); ts.restoreHeaders(table); if (removeClasses !== false) { - $t.removeClass(c.tableClass + ' tablesorter-' + c.theme); + $t.removeClass(ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme); } // clear flag in case the plugin is initialized again table.hasInitialized = false; @@ -976,31 +1024,32 @@ // *** sort functions *** // regex used in natural sort - ts.regex = [ - /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi, // chunk/tokenize numbers & letters - /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/, //date - /^0x[0-9a-f]+$/i // hex - ]; + ts.regex = { + chunk : /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi, // chunk/tokenize numbers & letters + hex: /^0x[0-9a-f]+$/i // hex + }; - // Natural sort - https://github.com/overset/javascript-natural-sort + // Natural sort - https://github.com/overset/javascript-natural-sort (date sorting removed) ts.sortText = function(table, a, b, col) { if (a === b) { return 0; } var c = table.config, e = c.string[ (c.empties[col] || c.emptyTo ) ], r = ts.regex, xN, xD, yN, yD, xF, yF, i, mx; + // sorting empty cells if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : -e || -1; } if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : e || 1; } + // custom sorter if (typeof c.textSorter === 'function') { return c.textSorter(a, b, table, col); } - // chunk/tokenize - xN = a.replace(r[0], '\\0$1\\0').replace(/\\0$/, '').replace(/^\\0/, '').split('\\0'); - yN = b.replace(r[0], '\\0$1\\0').replace(/\\0$/, '').replace(/^\\0/, '').split('\\0'); - // numeric, hex or date detection - xD = parseInt(a.match(r[2]),16) || (xN.length !== 1 && a.match(r[1]) && Date.parse(a)); - yD = parseInt(b.match(r[2]),16) || (xD && b.match(r[1]) && Date.parse(b)) || null; - // first try and sort Hex codes or Dates + // numeric or hex detection + yD = parseInt(b.match(r.hex), 16); + // first try and sort Hex codes if (yD) { + xD = parseInt(a.match(r.hex), 16); if ( xD < yD ) { return -1; } if ( xD > yD ) { return 1; } } + // chunk/tokenize + xN = a.replace(r.chunk, '\\0$1\\0').replace(/\\0$/, '').replace(/^\\0/, '').split('\\0'); + yN = b.replace(r.chunk, '\\0$1\\0').replace(/\\0$/, '').replace(/^\\0/, '').split('\\0'); mx = Math.max(xN.length, yN.length); // natural sorting through split numeric strings and default strings for (i = 0; i < mx; i++) { @@ -1175,18 +1224,19 @@ widgets.sort(function(a, b){ return a.priority < b.priority ? -1 : a.priority === b.priority ? 0 : 1; }); - // add/update selected widgets $.each(widgets, function(i,w){ if (w) { - if (init) { + if (init || !(c.widgetInit[w.id])) { if (w.hasOwnProperty('options')) { wo = table.config.widgetOptions = $.extend( true, {}, w.options, wo ); + c.widgetInit[w.id] = true; } if (w.hasOwnProperty('init')) { w.init(table, w, c, wo); } - } else if (!init && w.hasOwnProperty('format')) { + } + if (!init && w.hasOwnProperty('format')) { w.format(table, c, wo, false); } } @@ -1207,7 +1257,10 @@ for (i = 0; i < l; i++){ if ( w[i] && w[i].id && (doAll || $.inArray( w[i].id, cw ) < 0) ) { if (c.debug) { log( 'Refeshing widgets: Removing ' + w[i].id ); } - if (w[i].hasOwnProperty('remove')) { w[i].remove(table, c, c.widgetOptions); } + if (w[i].hasOwnProperty('remove')) { + w[i].remove(table, c, c.widgetOptions); + c.widgetInit[w[i].id] = false; + } } } if (dontapply !== true) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js index 29d96d7..29c3fee 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js @@ -1,4 +1,4 @@ -/*! Filter widget formatter functions - updated 6/4/2013 +/*! Filter widget formatter functions - updated 10/10/2013 * requires: tableSorter 2.7.7+ and jQuery 1.4.3+ * * uiSpinner (jQuery UI spinner) @@ -488,7 +488,7 @@ $.tablesorter.filterFormatter = { changeYear : true, numberOfMonths : 1 }, defDate), - t, closeFrom, $shcell = [], + t, closeTo, closeFrom, $shcell = [], // Add a hidden input to hold the range values $input = $('<input class="dateRange" type="hidden">') .appendTo($cell) @@ -502,7 +502,7 @@ $.tablesorter.filterFormatter = { } else if (v.match('>=')) { closeFrom( v.replace('>=', '') ); } else if (v.match('<=')) { - o.onClose( v.replace('<=', '') ); + closeTo( v.replace('<=', '') ); } }), c = $cell.closest('table')[0].config; @@ -516,18 +516,18 @@ $.tablesorter.filterFormatter = { // add callbacks; preserve added callbacks o.oldonClose = o.onClose; - o.defaultDate = o.from || o.defaultDate; + var localfrom = o.defaultDate = o.from || o.defaultDate; closeFrom = o.onClose = function( selectedDate, ui ) { - var from = new Date($cell.find('.dateFrom').datepicker('getDate')).getTime() || '', - to = new Date($cell.find('.dateTo').datepicker('getDate')).getTime() || '', + var from = new Date( selectedDate ).getTime() || '', + to = new Date( $cell.find('.dateTo').datepicker('getDate') ).getTime() || '', range = from ? ( to ? from + ' - ' + to : '>=' + from ) : (to ? '<=' + to : ''); $cell - .find('.dateTo').datepicker('option', 'minDate', selectedDate ).end() - .find('.dateFrom').val(selectedDate).end() - // update hidden input .find('.dateRange').val(range) - .trigger('search'); + .trigger('search').end() + .find('.dateTo').datepicker('option', 'minDate', selectedDate ).end() + .find('.dateFrom').val(selectedDate); + // update sticky header cell if ($shcell.length) { $shcell @@ -538,16 +538,18 @@ $.tablesorter.filterFormatter = { }; $cell.find('.dateFrom').datepicker(o); + o.defaultDate = o.to || '+7d'; // set to date +7 days from today (if not defined) - o.onClose = function( selectedDate, ui ) { + closeTo = o.onClose = function( selectedDate, ui ) { var from = new Date( $cell.find('.dateFrom').datepicker('getDate') ).getTime() || '', - to = new Date( $cell.find('.dateTo').datepicker('getDate') ).getTime() || '', + to = new Date( selectedDate ).getTime() || '', range = from ? ( to ? from + ' - ' + to : '>=' + from ) : (to ? '<=' + to : ''); $cell - .find('.dateFrom').datepicker('option', 'maxDate', selectedDate ).end() - .find('.dateTo').val(selectedDate).end() .find('.dateRange').val(range) - .trigger('search'); + .trigger('search').end() + .find('.dateFrom').datepicker('option', 'maxDate', selectedDate ).end() + .find('.dateTo').val(selectedDate); + // update sticky header cell if ($shcell.length) { $shcell @@ -561,24 +563,27 @@ $.tablesorter.filterFormatter = { // has sticky headers? c.$table.bind('stickyHeadersInit', function(){ $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + $shcell.append(t); + // add a jQuery datepicker! - $shcell.append(t).find('.dateTo').datepicker(o); - o.defaultDate = o.from || o.defaultDate || new Date(); + o.onClose = closeTo; + $shcell.find('.dateTo').datepicker(o); + + o.defaultDate = localfrom; o.onClose = closeFrom; $shcell.find('.dateFrom').datepicker(o); }); // on reset $cell.closest('table').bind('filterReset', function(){ - $cell.find('.dateFrom, .dateTo').val(''); + $cell.find('.dateFrom, .dateTo').val('').datepicker('option', 'currentText', '' ); if ($shcell.length) { - $shcell.find('.dateFrom, .dateTo').val(''); + $shcell.find('.dateFrom, .dateTo').val('').datepicker('option', 'currentText', '' ); } }); // return the hidden input so the filter widget has a reference to it - t = o.from ? ( o.to ? o.from + ' - ' + o.to : '>=' + o.from ) : (o.to ? '<=' + o.to : ''); - return $input.val( t ); + return $input.val( o.from ? ( o.to ? o.from + ' - ' + o.to : '>=' + o.from ) : (o.to ? '<=' + o.to : '') ); }, /**********************\ diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 6c6d127..281b35d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -22,8 +22,8 @@ ts.themes = { footerCells: '', icons : '', // add "icon-white" to make them white; this icon class is added to the <i> in the header sortNone : 'bootstrap-icon-unsorted', - sortAsc : 'icon-chevron-up', - sortDesc : 'icon-chevron-down', + sortAsc : 'icon-chevron-up glyphicon glyphicon-chevron-up', + sortDesc : 'icon-chevron-down glyphicon glyphicon-chevron-down', active : '', // applied when column is sorted hover : '', // use custom css here - bootstrap class may not override it filterRow : '', // filter row class @@ -88,7 +88,7 @@ ts.storage = function(table, key, val){ v = (d !== 0) ? $.parseJSON(k[d] || '{}') : {}; } } - // allow val to be an empty string to + // allow val to be an empty string to if ((val || val === '') && window.JSON && JSON.hasOwnProperty('stringify')){ // add unique identifiers = url pathname > table ID/index on page > data if (!v[url]) { @@ -195,7 +195,7 @@ ts.addWidget({ } if (c.cssIcon){ // if c.cssIcon is '', then no <i> is added to the header - $h.find('.' + c.cssIcon).addClass(o.icons); + $h.find('.' + ts.css.icon).addClass(o.icons); } if ($t.hasClass('hasFilters')){ $h.find('.tablesorter-filter-row').addClass(o.filterRow); @@ -203,14 +203,14 @@ ts.addWidget({ } $.each($h, function(i){ $el = $(this); - $tar = (c.cssIcon) ? $el.find('.' + c.cssIcon) : $el; + $tar = (ts.css.icon) ? $el.find('.' + ts.css.icon) : $el; if (this.sortDisabled){ // no sort arrows for disabled columns! $el.removeClass(rmv); $tar.removeClass(rmv + ' tablesorter-icon ' + o.icons); } else { t = ($t.hasClass('hasStickyHeaders')) ? $t.find(sh).find('th').eq(i).add($el) : $el; - klass = ($el.hasClass(c.cssAsc)) ? o.sortAsc : ($el.hasClass(c.cssDesc)) ? o.sortDesc : $el.hasClass(c.cssHeader) ? o.sortNone : ''; + klass = ($el.hasClass(ts.css.sortAsc)) ? o.sortAsc : ($el.hasClass(ts.css.sortDesc)) ? o.sortDesc : $el.hasClass(ts.css.header) ? o.sortNone : ''; $el[klass === o.sortNone ? 'removeClass' : 'addClass'](o.active); $tar.removeClass(rmv).addClass(klass); } @@ -227,7 +227,7 @@ ts.addWidget({ rmv = o.sortNone + ' ' + o.sortDesc + ' ' + o.sortAsc; $t .removeClass('tablesorter-' + theme + ' ' + o.table) - .find(c.cssHeader).removeClass(o.header); + .find(ts.css.header).removeClass(o.header); $h .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') // remove hover .removeClass(o.hover + ' ' + rmv + ' ' + o.active) @@ -287,20 +287,16 @@ ts.addWidget({ ts.processTbody(table, $tb, false); } // add classes to thead and tfoot - $tr = wo.columns_thead !== false ? 'thead tr' : ''; + $tr = wo.columns_thead !== false ? ['thead tr'] : []; if (wo.columns_tfoot !== false) { - $tr += ($tr === '' ? '' : ',') + 'tfoot tr'; + $tr.push('tfoot tr'); } if ($tr.length) { - $t = $tbl.find($tr).children().removeClass(rmv); - if (list && list[0]){ - // primary sort column class - $t.filter('[data-column="' + list[0][0] + '"]').addClass(css[0]); - if (len > 1){ - for (i = 1; i < len; i++){ - // secondary, tertiary, etc sort column classes - $t.filter('[data-column="' + list[i][0] + '"]').addClass(css[i] || css[last]); - } + $t = $tbl.find($tr.join(',')).children().removeClass(rmv); + if (len){ + for (i = 0; i < len; i++){ + // add primary. secondary, tertiary, etc sort column classes + $t.filter('[data-column="' + list[i][0] + '"]').addClass(css[i] || css[last]); } } } @@ -332,7 +328,7 @@ ts.addWidget({ options : { filter_childRows : false, // if true, filter includes child row content in the search filter_columnFilters : true, // if true, a filter will be added to the top of each table column - filter_cssFilter : 'tablesorter-filter', // css class name added to the filter row & each input in the row + filter_cssFilter : '', // css class name added to the filter row & each input in the row (tablesorter-filter is ALWAYS added) filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin filter_formatter : null, // add custom filter elements to the filter row filter_functions : null, // add custom filter functions using this option @@ -353,7 +349,7 @@ ts.addWidget({ "child" : /tablesorter-childRow/, // child row class name; this gets updated in the script "filtered" : /filtered/, // filtered (hidden) row class name; updated in the script "type" : /undefined|number/, // check type - "exact" : /(^[\"|\'|=])|([\"|\'|=]$)/g, // exact match + "exact" : /(^[\"|\'|=]+)|([\"|\'|=]+$)/g, // exact match (allow '==') "nondigit" : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser) "operators" : /[<>=]/g // replace operators } @@ -365,9 +361,8 @@ ts.addWidget({ fmt = ts.formatFloat, last = '', // save last filter search $ths = c.$headers, - css = wo.filter_cssFilter, $t = c.$table.addClass('hasFilters'), - b = $t.find('tbody'), + b = c.$tbodies, cols = c.parsers.length, parsed, time, timer, @@ -403,7 +398,7 @@ ts.addWidget({ var $tb, $tr, $td, cr, r, l, ff, time, r1, r2, searchFiltered; if (c.debug) { time = new Date(); } for (k = 0; k < b.length; k++ ){ - if (b.eq(k).hasClass(c.cssInfoBlock)) { continue; } // ignore info blocks, issue #264 + if (b.eq(k).hasClass(ts.css.info)) { continue; } // ignore info blocks, issue #264 $tb = ts.processTbody(table, b.eq(k), true); $tr = $tb.children('tr:not(.' + c.cssChildRow + ')'); l = $tr.length; @@ -444,6 +439,8 @@ ts.addWidget({ } xi = !wo.filter_regex.type.test(typeof x) && wo.filter_ignoreCase ? x.toLocaleLowerCase() : x; ff = r; // if r is true, show that row + // replace accents - see #357 + v[i] = c.sortLocaleCompare ? ts.replaceAccents(v[i]) : v[i]; // val = case insensitive, v[i] = case sensitive val = wo.filter_ignoreCase ? v[i].toLocaleLowerCase() : v[i]; if (wo.filter_functions && wo.filter_functions[i]){ @@ -452,10 +449,10 @@ ts.addWidget({ ff = ($ths.filter('[data-column="' + i + '"]:last').hasClass('filter-match')) ? xi.search(val) >= 0 : v[i] === x; } else if (typeof wo.filter_functions[i] === 'function'){ // filter callback( exact cell content, parser normalized content, filter input value, column index ) - ff = wo.filter_functions[i](x, c.cache[k].normalized[j][i], v[i], i); + ff = wo.filter_functions[i](x, c.cache[k].normalized[j][i], v[i], i, $tr.eq(j)); } else if (typeof wo.filter_functions[i][v[i]] === 'function'){ // selector option function - ff = wo.filter_functions[i][v[i]](x, c.cache[k].normalized[j][i], v[i], i); + ff = wo.filter_functions[i][v[i]](x, c.cache[k].normalized[j][i], v[i], i, $tr.eq(j)); } // Look for regex } else if (wo.filter_regex.regex.test(val)){ @@ -578,7 +575,7 @@ ts.addWidget({ arry = (ts.sortText) ? arry.sort(function(a, b){ return ts.sortText(table, a, b, i); }) : arry.sort(true); // Get curent filter value - currentVal = $t.find('thead').find('select.' + css + '[data-column="' + i + '"]').val(); + currentVal = $t.find('thead').find('select.tablesorter-filter[data-column="' + i + '"]').val(); // build option list for (k = 0; k < arry.length; k++){ @@ -586,7 +583,7 @@ ts.addWidget({ // replace quotes - fixes #242 & ignore empty strings - see http://stackoverflow.com/q/14990971/145346 o += arry[k] !== '' ? '<option value="' + t + '"' + (currentVal === t ? ' selected="selected"' : '') +'>' + arry[k] + '</option>' : ''; } - $t.find('thead').find('select.' + css + '[data-column="' + i + '"]')[ updating ? 'html' : 'append' ](o); + $t.find('thead').find('select.tablesorter-filter[data-column="' + i + '"]')[ updating ? 'html' : 'append' ](o); }, buildDefault = function(updating){ // build default select dropdown @@ -605,7 +602,7 @@ ts.addWidget({ // delay filtering clearTimeout(timer); timer = setTimeout(function(){ - checkFilters(filter); + checkFilters(filter); }, wo.filter_liveSearch ? wo.filter_searchDelay : 10); } else { // skip delay @@ -658,7 +655,7 @@ ts.addWidget({ } } if (t) { - t.addClass(css).attr('data-column', i); + t.addClass('tablesorter-filter ' + wo.filter_cssFilter).attr('data-column', i); if (dis) { t.addClass('disabled')[0].disabled = true; // disabled! } @@ -672,7 +669,7 @@ ts.addWidget({ buildDefault(true); } if (e.type === 'filterReset') { - $t.find('.' + css).val(''); + searching([]); } if (e.type === 'filterEnd') { buildDefault(true); @@ -683,7 +680,7 @@ ts.addWidget({ } return false; }) - .find('input.' + css).bind('keyup search', function(e, filter){ + .find('input.tablesorter-filter').bind('keyup search', function(e, filter){ // emulate what webkit does.... escape clears the filter if (e.which === 27) { this.value = ''; @@ -701,8 +698,8 @@ ts.addWidget({ }).get(); // reset button/link - if (wo.filter_reset && $(wo.filter_reset).length){ - $(wo.filter_reset).bind('click.tsfilter', function(){ + if (wo.filter_reset){ + $(document).delegate(wo.filter_reset, 'click.tsfilter', function(){ $t.trigger('filterReset'); }); } @@ -722,7 +719,7 @@ ts.addWidget({ ff += '<option value="' + str + '">' + str + '</option>'; } } - $t.find('thead').find('select.' + css + '[data-column="' + col + '"]').append(ff); + $t.find('thead').find('select.tablesorter-filter[data-column="' + col + '"]').append(ff); } } } @@ -731,7 +728,7 @@ ts.addWidget({ // it would append the same options twice. buildDefault(true); - $t.find('select.' + css).bind('change search', function(e, filter){ + $t.find('select.tablesorter-filter').bind('change search', function(e, filter){ checkFilters(filter); }); @@ -752,8 +749,8 @@ ts.addWidget({ // $(':focus') needs jQuery 1.6+ if ($(document.activeElement).closest('tr')[0] !== ft[0]){ // get all filter values - all = $t.find('.' + wo.filter_cssFilter).map(function(){ - return $(this).val() || ''; + all = $t.find('.tablesorter-filter').map(function(){ + return $(this).val() || ''; }).get().join(''); // don't hide row if any filter has a value if (all === ''){ @@ -768,7 +765,7 @@ ts.addWidget({ clearTimeout(st); st = setTimeout(function(){ // don't hide row if any filter has a value - if ($t.find('.' + wo.filter_cssFilter).map(function(){ return $(this).val() || ''; }).get().join('') === ''){ + if ($t.find('.tablesorter-filter').map(function(){ return $(this).val() || ''; }).get().join('') === ''){ ft2[ e.type === 'focus' ? 'removeClass' : 'addClass']('hideme'); } }, 200); @@ -778,7 +775,7 @@ ts.addWidget({ // show processing icon if (c.showProcessing) { $t.bind('filterStart.tsfilter filterEnd.tsfilter', function(e, v) { - var fc = (v) ? $t.find('.' + c.cssHeader).filter('[data-column]').filter(function(){ + var fc = (v) ? $t.find('.' + ts.css.header).filter('[data-column]').filter(function(){ return v[$(this).data('column')] !== ''; }) : ''; ts.isProcessing($t[0], e.type === 'filterStart', v ? fc : ''); @@ -791,10 +788,13 @@ ts.addWidget({ // add default values $t.bind('tablesorter-initialized', function(){ ff = ts.getFilters(table); - for (i = 0; i < ff.length; i++) { - ff[i] = $ths.filter('[data-column="' + i + '"]:last').attr(wo.filter_defaultAttrib) || ff[i]; + // ff is undefined when filter_columnFilters = false + if (ff) { + for (i = 0; i < ff.length; i++) { + ff[i] = $ths.filter('[data-column="' + i + '"]:last').attr(wo.filter_defaultAttrib) || ff[i]; + } + ts.setFilters(table, ff, true); } - ts.setFilters(table, ff, true); }); // filter widget initialized $t.trigger('filterInit'); @@ -815,20 +815,20 @@ ts.addWidget({ $tb.children().removeClass(wo.filter_filteredRow).show(); ts.processTbody(table, $tb, false); // restore tbody } - if (wo.filterreset) { $(wo.filter_reset).unbind('click.tsfilter'); } + if (wo.filterreset) { $(document).undelegate(wo.filter_reset, 'click.tsfilter'); } } }); ts.getFilters = function(table) { var c = table ? $(table)[0].config : {}; if (c && c.widgetOptions && !c.widgetOptions.filter_columnFilters) { return $(table).data('lastSearch'); } - return c && c.$filters ? c.$filters.find('.' + c.widgetOptions.filter_cssFilter).map(function(i, el) { + return c && c.$filters ? c.$filters.find('.tablesorter-filter').map(function(i, el) { return $(el).val(); }).get() || [] : false; }; ts.setFilters = function(table, filter, apply) { var $t = $(table), c = $t.length ? $t[0].config : {}, - valid = c && c.$filters ? c.$filters.find('.' + c.widgetOptions.filter_cssFilter).each(function(i, el) { + valid = c && c.$filters ? c.$filters.find('.tablesorter-filter').each(function(i, el) { $(el).val(filter[i] || ''); }).trigger('change.tsfilter') || false : false; if (apply) { $t.trigger('search', [filter, false]); } @@ -837,14 +837,14 @@ ts.setFilters = function(table, filter, apply) { // Widget: Sticky headers // based on this awesome article: -// http://css-tricks.com/13465-persistent-headers/ +// http://css-tricks.com/13465-persistent-headers/ // and https://github.com/jmosbech/StickyTableHeaders by Jonas Mosbech // ************************** ts.addWidget({ id: "stickyHeaders", priority: 60, options: { - stickyHeaders : 'tablesorter-stickyHeader', + stickyHeaders : '', // extra class name added to the sticky header row stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element stickyHeaders_cloneId : '-sticky', // added to table ID, if it exists stickyHeaders_addResizeEvent : true, // trigger "resize" event on headers @@ -859,7 +859,7 @@ ts.addWidget({ hdrCells = header.children('tr:not(.sticky-false)').children(), innr = '.tablesorter-header-inner', tfoot = $t.find('tfoot'), - filterInputs = '.' + (wo.filter_cssFilter || 'tablesorter-filter'), + filterInputs = '.tablesorter-filter', $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '', stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, stickyzIndex = wo.stickyHeaders_zIndex ? wo.stickyHeaders_zIndex : 2, @@ -872,7 +872,7 @@ ts.addWidget({ visibility : 'hidden', zIndex : stickyzIndex }), - stkyHdr = $stickyTable.children('thead:first').addClass(wo.stickyHeaders), + stkyHdr = $stickyTable.children('thead:first').addClass('tablesorter-stickyHeader ' + wo.stickyHeaders), stkyCells, laststate = '', spacing = 0, @@ -924,11 +924,11 @@ ts.addWidget({ t .attr('class', $(this).attr('class')) // remove processing icon - .removeClass(c.cssProcessing); + .removeClass(ts.css.processing + ' ' + c.cssProcessing); if (c.cssIcon){ t - .find('.' + c.cssIcon) - .attr('class', $(this).find('.' + c.cssIcon).attr('class')); + .find('.' + ts.css.icon) + .attr('class', $(this).find('.' + ts.css.icon).attr('class')); } }); }) @@ -1008,7 +1008,7 @@ ts.addWidget({ c.$table .removeClass('hasStickyHeaders') .unbind('sortEnd.tsSticky pagerComplete.tsSticky') - .find('.' + wo.stickyHeaders).remove(); + .find('.tablesorter-stickyHeader').remove(); if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table // don't unbind if any table on the page still has stickyheaders applied if (!$('.hasStickyHeaders').length) { diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index 4c67f59..4ac9518 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -9,22 +9,9 @@ .tablesorter-bootstrap tfoot th, .tablesorter-bootstrap tfoot td { font: bold 14px/20px Arial, Sans-serif; - position: relative; - padding: 8px; + padding: 4px; margin: 0 0 18px; - list-style: none; - background-color: #FBFBFB; - background-image: -moz-linear-gradient(top, white, #efefef); - background-image: -ms-linear-gradient(top, white, #efefef); - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(white), to(#efefef)); - background-image: -webkit-linear-gradient(top, white, #efefef); - background-image: -o-linear-gradient(top, white, #efefef); - background-image: linear-gradient(to bottom, white, #efefef); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#efefef', GradientType=0); - background-repeat: repeat-x; - -webkit-box-shadow: inset 0 1px 0 white; - -moz-box-shadow: inset 0 1px 0 #ffffff; - box-shadow: inset 0 1px 0 white; + background-color: #eee; } .tablesorter-bootstrap .tablesorter-header { @@ -38,6 +25,7 @@ /* bootstrap uses <i> for icons */ .tablesorter-bootstrap .tablesorter-header i { + font-size: 11px; position: absolute; right: 2px; top: 50%; @@ -97,7 +85,7 @@ caption { cursor: not-allowed; } .tablesorter-bootstrap .tablesorter-filter-row td { - background: #eee; + background: #efefef; line-height: normal; text-align: center; padding: 4px 6px; @@ -131,6 +119,10 @@ caption { .tablesorter-bootstrap .tablesorter-pager .pagedisplay { border: 0; } +/* tfoot i for pager controls */ +.tablesorter-bootstrap tfoot i { + font-size: 11px; +} /* ajax error row */ .tablesorter .tablesorter-errorRow td { From 20b9aadeac49c325ff5ba3a6b2d9907275e031fc Mon Sep 17 00:00:00 2001 From: Erik Ernst <github@black-milk.de> Date: Sun, 20 Oct 2013 01:32:54 +0200 Subject: [PATCH 004/138] * updated tablesorter to latest version (2.12) --- CHANGELOG.markdown | 4 + README.markdown | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 70 ++++++---- .../jquery-tablesorter/jquery.tablesorter.js | 122 ++++++++++-------- .../jquery.tablesorter.widgets.js | 19 ++- 7 files changed, 131 insertions(+), 90 deletions(-) diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index 50f45a6..749be61 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -1,5 +1,9 @@ Changelog === +#### v1.7.0 + +* Upgrade tablesorter to v2.12 + #### v1.6.0 * Upgrade tablesorter to v2.11.1 diff --git a/README.markdown b/README.markdown index 21e7021..fd56c46 100644 --- a/README.markdown +++ b/README.markdown @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.11.1 (10/11/2013), [documentation] +Current tablesorter version: 2.12 (10/18/2013), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 41b11ba..309cf08 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = "1.6.0" + VERSION = "1.7.0" end diff --git a/tablesorter b/tablesorter index 458669a..8310e01 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 458669a75dee8d50a33dfdf3eb9ca99c704fdc62 +Subproject commit 8310e011973a7c57d2e725d864e5e30a8c040a44 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index c3b7ca4..4884f1e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,9 +1,8 @@ /*! * tablesorter pager plugin - * updated 10/11/2013 + * updated 10/18/2013 */ /*jshint browser:true, jquery:true, unused:false */ -/*global toString:true */ ;(function($) { "use strict"; /*jshint supernew:true */ @@ -87,7 +86,12 @@ totalRows: 0, totalPages: 0, filteredRows: 0, - filteredPages: 0 + filteredPages: 0, + currentFilters: [], + startRow: 0, + endRow: 0, + $size: null, + last: {} }; @@ -110,10 +114,11 @@ var i, pg, s, out, c = table.config, f = c.$table.hasClass('hasFilters') && !p.ajaxUrl, - t = (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered') + ',' + c.selectorRemove; - p.totalPages = Math.ceil( p.totalRows / p.size ); // needed for "pageSize" method + t = (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered') + ',' + c.selectorRemove, + sz = p.size || 10; // don't allow dividing by zero + p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method p.filteredRows = (f) ? c.$tbodies.eq(0).children('tr:not(.' + t + ')').length : p.totalRows; - p.filteredPages = (f) ? Math.ceil( p.filteredRows / p.size ) || 1 : p.totalPages; + p.filteredPages = (f) ? Math.ceil( p.filteredRows / sz ) || 1 : p.totalPages; if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) { t = (p.size * p.page > p.filteredRows); p.startRow = (t) ? 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1); @@ -121,7 +126,7 @@ p.endRow = Math.min( p.filteredRows, p.totalRows, p.size * ( p.page + 1 ) ); out = p.$container.find(p.cssPageDisplay); // form the output string (can now get a new output string from the server) - s = ( p.ajaxData && p.ajaxData.hasOwnProperty('output') ? p.ajaxData.output || p.output : p.output ) + s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || p.output : p.output ) // {page} = one-based index; {page+#} = zero based index +/- value .replace(/\{page([\-+]\d+)?\}/gi, function(m,n){ return p.page + (n ? parseInt(n, 10) : 1); @@ -242,7 +247,7 @@ c.$tbodies.eq(0).empty(); } else { // process ajax object - if (toString.call(result) !== "[object Array]") { + if (!$.isArray(result)) { p.ajaxData = result; p.totalRows = result.total; th = result.headers; @@ -304,7 +309,7 @@ if (c.showProcessing) { ts.isProcessing(table); // remove loading icon } - p.totalPages = Math.ceil( p.totalRows / p.size ); + p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) ); updatePageDisplay(table, p); fixHeight(table, p); if (p.initialized) { @@ -380,13 +385,18 @@ }, renderTable = function(table, rows, p) { - p.isDisabled = false; // needed because sorting will change the page and re-enable the pager - var i, j, o, $tb, - l = rows.length, - s = ( p.page * p.size ), - e = ( s + p.size ); + var i, $tb, + l = rows && rows.length || 0, // rows may be undefined + s = ( p.page * p.size ), + e = ( s + p.size ); if ( l < 1 ) { return; } // empty table, abort! + if ( p.page >= p.totalPages ) { + // lets not render the table more than once + moveToLastPage(table, p); + } + p.isDisabled = false; // needed because sorting will change the page and re-enable the pager if (p.initialized) { $(table).trigger('pagerChange', p); } + if ( !p.removeRows ) { hideRows(table, p); } else { @@ -400,9 +410,7 @@ } ts.processTbody(table, $tb, false); } - if ( p.page >= p.totalPages ) { - moveToLastPage(table, p); - } + updatePageDisplay(table, p); if ( !p.isDisabled ) { fixHeight(table, p); } $(table).trigger('applyWidgets'); @@ -418,7 +426,7 @@ p.page = 0; p.size = p.totalRows; p.totalPages = 1; - $(table).find('tr.pagerSavedHeightSpacer').remove(); + $(table).addClass('pagerDisabled').find('tr.pagerSavedHeightSpacer').remove(); renderTable(table, table.config.rowsCopy, p); } // disable size selector @@ -429,9 +437,18 @@ moveToPage = function(table, p, flag) { if ( p.isDisabled ) { return; } - var pg = Math.min( p.totalPages, p.filteredPages ); + var l = p.last, + pg = Math.min( p.totalPages, p.filteredPages ); if ( p.page < 0 ) { p.page = 0; } if ( p.page > ( pg - 1 ) && pg !== 0 ) { p.page = pg - 1; } + // don't allow rendering multiple times on the same page/size/totalpages/filters + if (l.page === p.page && l.size === p.size && l.total === p.totalPages && l.filters === p.currentFilters ) { return; } + p.last = { + page : p.page, + size : p.size, + totalPages : p.totalPages, + currentFilters : p.currentFilters + }; if (p.ajax) { getAjax(table, p); } else if (!p.ajax) { @@ -449,7 +466,7 @@ p.$size.val(size); $.data(table, 'pagerLastPage', p.page); $.data(table, 'pagerLastSize', p.size); - p.totalPages = Math.ceil( p.totalRows / p.size ); + p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) ); moveToPage(table, p); }, @@ -483,7 +500,11 @@ showAllRows(table, p); p.$container.hide(); // hide pager table.config.appender = null; // remove pager appender function + p.initialized = false; $(table).unbind('destroy.pager sortEnd.pager filterEnd.pager enable.pager disable.pager'); + if (ts.storage) { + ts.storage(table, 'tablesorter-pager', ''); + } }, enablePager = function(table, p, triggered){ @@ -493,7 +514,7 @@ p.page = $.data(table, 'pagerLastPage') || p.page || 0; p.size = $.data(table, 'pagerLastSize') || parseInt(pg.find('option[selected]').val(), 10) || p.size; pg.val(p.size); // set page size - p.totalPages = Math.ceil( Math.min( p.totalPages, p.filteredPages ) / p.size); + p.totalPages = Math.ceil( Math.min( p.totalPages, p.filteredPages ) / ( p.size || 10 ) ); if ( triggered ) { $(table).trigger('update'); setPageSize(table, p.size, p); @@ -508,7 +529,7 @@ table.config.rowsCopy = rows; p.totalRows = rows.length; p.size = $.data(table, 'pagerLastSize') || p.size; - p.totalPages = Math.ceil(p.totalRows / p.size); + p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) ); renderTable(table, rows, p); } }; @@ -528,9 +549,9 @@ c.appender = $this.appender; if (p.savePages && ts.storage) { - t = ts.storage(table, 'tablesorter-pager'); + t = ts.storage(table, 'tablesorter-pager') || {}; // fixes #387 p.page = isNaN(t.page) ? p.page : t.page; - p.size = isNaN(t.size) ? p.size : t.size; + p.size = ( isNaN(t.size) ? p.size : t.size ) || 10; } $t @@ -610,6 +631,7 @@ .bind('change', function(){ p.page = $(this).val() - 1; moveToPage(table, p); + updatePageDisplay(table, p, false); }); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 3acb332..fe65dd0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.11.1 - Client-side table sorting with ease! +* TableSorter 2.12.0 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.11.1"; + ts.version = "2.12.0"; ts.parsers = []; ts.widgets = []; @@ -63,7 +63,8 @@ emptyTo : 'bottom', // sort empty cell to bottom, top, none, zero stringTo : 'max', // sort strings in numerical column as max, min, top, bottom, zero textExtraction : 'simple', // text extraction method/function - function(node, table, cellIndex){} - textSorter : null, // use custom text sorter - function(a,b){ return a.sort(b); } // basic sort + textSorter : null, // choose overall or specific column sorter function(a, b, direction, table, columnIndex) [alt: ts.sortText] + numberSorter : null, // choose overall numeric sorter function(a, b, direction, maxColumnValue) // *** widget options widgets: [], // method to add widgets, e.g. widgets: ['zebra'] @@ -388,7 +389,8 @@ } } } - t.config.columns = cols; // may not be accurate if # header columns !== # tbody columns + // may not be accurate if # header columns !== # tbody columns + t.config.columns = cols + 1; // add one because it's a zero-based index return lookup; } @@ -409,14 +411,14 @@ c.$headers = $(table).find(c.selectorHeaders).each(function(index) { $t = $(this); ch = c.headers[index]; - c.headerContent[index] = this.innerHTML; // save original header content + c.headerContent[index] = $(this).html(); // save original header content // set up header template - t = c.headerTemplate.replace(/\{content\}/g, this.innerHTML).replace(/\{icon\}/g, i); + t = c.headerTemplate.replace(/\{content\}/g, $(this).html()).replace(/\{icon\}/g, i); if (c.onRenderTemplate) { h = c.onRenderTemplate.apply($t, [index, t]); if (h && typeof h === 'string') { t = h; } // only change t if something is returned } - this.innerHTML = '<div class="tablesorter-header-inner">' + t + '</div>'; // faster than wrapInner + $(this).html('<div class="tablesorter-header-inner">' + t + '</div>'); // faster than wrapInner if (c.onRenderHeader) { c.onRenderHeader.apply($t, [index]); } @@ -629,41 +631,71 @@ // sort multiple columns function multisort(table) { /*jshint loopfunc:true */ - var dir = 0, tc = table.config, - sortList = tc.sortList, l = sortList.length, bl = table.tBodies.length, - sortTime, i, k, c, colMax, cache, lc, s, order, orgOrderCol; - if (tc.serverSideSorting || isEmptyObject(tc.cache)) { // empty table - fixes #206/#346 + var i, k, e, num, col, colMax, cache, lc, + order, orgOrderCol, sortTime, sort, x, y, + dir = 0, + c = table.config, + cts = c.textSorter || '', + sortList = c.sortList, + l = sortList.length, + bl = table.tBodies.length; + if (c.serverSideSorting || isEmptyObject(c.cache)) { // empty table - fixes #206/#346 return; } - if (tc.debug) { sortTime = new Date(); } + if (c.debug) { sortTime = new Date(); } for (k = 0; k < bl; k++) { - colMax = tc.cache[k].colMax; - cache = tc.cache[k].normalized; + colMax = c.cache[k].colMax; + cache = c.cache[k].normalized; lc = cache.length; orgOrderCol = (cache && cache[0]) ? cache[0].length - 1 : 0; cache.sort(function(a, b) { // cache is undefined here in IE, so don't use it! for (i = 0; i < l; i++) { - c = sortList[i][0]; + col = sortList[i][0]; order = sortList[i][1]; + // sort direction, true = asc, false = desc + dir = order === 0; + + // set a & b depending on sort direction + x = dir ? a : b; + y = dir ? b : a; + + // determine how to sort empty cells + e = c.string[ (c.empties[col] || c.emptyTo ) ]; + if (x[col] === '' && e !== 0) { return ((typeof(e) === 'boolean') ? (e ? -1 : 1) : (e || 1)) * (dir ? 1 : -1); } + if (y[col] === '' && e !== 0) { return ((typeof(e) === 'boolean') ? (e ? 1 : -1) : (-e || -1)) * (dir ? 1 : -1); } + // fallback to natural sort since it is more robust - s = /n/i.test(getCachedSortType(tc.parsers, c)) ? "Numeric" : "Text"; - s += order === 0 ? "" : "Desc"; - if (/Numeric/.test(s) && tc.strings[c]) { + num = /n/i.test(getCachedSortType(c.parsers, col)); + if (num && c.strings[col]) { // sort strings in numerical columns - if (typeof (tc.string[tc.strings[c]]) === 'boolean') { - dir = (order === 0 ? 1 : -1) * (tc.string[tc.strings[c]] ? -1 : 1); + if (typeof (c.string[c.strings[col]]) === 'boolean') { + num = (dir ? 1 : -1) * (c.string[c.strings[col]] ? -1 : 1); + } else { + num = (c.strings[col]) ? c.string[c.strings[col]] || 0 : 0; + } + // fall back to built-in numeric sort + // var sort = $.tablesorter["sort" + s](table, a[c], b[c], c, colMax[c], dir); + sort = c.numberSorter ? c.numberSorter(x[col], y[col], dir, colMax[col], table) : ts.sortNumeric(x[col], y[col], num, colMax[col]); + } else { + // text sort function + if (typeof(cts) === 'function') { + // custom OVERALL text sorter + sort = cts(x[col], y[col], dir, col, table); + } else if (typeof(cts) === 'object' && cts.hasOwnProperty(col)) { + // custom text sorter for a SPECIFIC COLUMN + sort = cts[col](x[col], y[col], dir, col, table); } else { - dir = (tc.strings[c]) ? tc.string[tc.strings[c]] || 0 : 0; + // fall back to natural sort + sort = ts.sortNatural(x[col], y[col]); } } - var sort = $.tablesorter["sort" + s](table, a[c], b[c], c, colMax[c], dir); if (sort) { return sort; } } return a[orgOrderCol] - b[orgOrderCol]; }); } - if (tc.debug) { benchmark("Sorting on " + sortList.toString() + " and dir " + order + " time", sortTime); } + if (c.debug) { benchmark("Sorting on " + sortList.toString() + " and dir " + order + " time", sortTime); } } function resortComplete($table, callback){ @@ -1030,15 +1062,10 @@ }; // Natural sort - https://github.com/overset/javascript-natural-sort (date sorting removed) - ts.sortText = function(table, a, b, col) { + ts.sortNatural = function(a, b) { if (a === b) { return 0; } - var c = table.config, e = c.string[ (c.empties[col] || c.emptyTo ) ], - r = ts.regex, xN, xD, yN, yD, xF, yF, i, mx; - // sorting empty cells - if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : -e || -1; } - if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : e || 1; } - // custom sorter - if (typeof c.textSorter === 'function') { return c.textSorter(a, b, table, col); } + var xN, xD, yN, yD, xF, yF, i, mx, + r = ts.regex; // numeric or hex detection yD = parseInt(b.match(r.hex), 16); // first try and sort Hex codes @@ -1069,19 +1096,15 @@ return 0; }; - ts.sortTextDesc = function(table, a, b, col) { - if (a === b) { return 0; } - var c = table.config, e = c.string[ (c.empties[col] || c.emptyTo ) ]; - if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : e || 1; } - if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : -e || -1; } - if (typeof c.textSorter === 'function') { return c.textSorter(b, a, table, col); } - return ts.sortText(table, b, a); + // basic alphabetical sort + ts.sortText = function(a, b) { + return a > b ? 1 : (a < b ? -1 : 0); }; // return text string value by adding up ascii value // so the text is somewhat sorted when using a digital sort // this is NOT an alphanumeric sort - ts.getTextValue = function(a, mx, d) { + ts.getTextValue = function(a, d, mx) { if (mx) { // make sure the text value is greater than the max numerical value (mx) var i, l = a ? a.length : 0, n = mx + d; @@ -1093,26 +1116,13 @@ return 0; }; - ts.sortNumeric = function(table, a, b, col, mx, d) { + ts.sortNumeric = function(a, b, dir, mx) { if (a === b) { return 0; } - var c = table.config, e = c.string[ (c.empties[col] || c.emptyTo ) ]; - if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : -e || -1; } - if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : e || 1; } - if (isNaN(a)) { a = ts.getTextValue(a, mx, d); } - if (isNaN(b)) { b = ts.getTextValue(b, mx, d); } + if (isNaN(a)) { a = ts.getTextValue(a, dir, mx); } + if (isNaN(b)) { b = ts.getTextValue(b, dir, mx); } return a - b; }; - ts.sortNumericDesc = function(table, a, b, col, mx, d) { - if (a === b) { return 0; } - var c = table.config, e = c.string[ (c.empties[col] || c.emptyTo ) ]; - if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : e || 1; } - if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : -e || -1; } - if (isNaN(a)) { a = ts.getTextValue(a, mx, d); } - if (isNaN(b)) { b = ts.getTextValue(b, mx, d); } - return b - a; - }; - // used when replacing accented characters during sorting ts.characterEquivalents = { "a" : "\u00e1\u00e0\u00e2\u00e3\u00e4\u0105\u00e5", // áàâãäąå @@ -1204,7 +1214,7 @@ var c = table.config, wo = c.widgetOptions, widgets = [], - time, i, w, wd; + time, w, wd; if (c.debug) { time = new Date(); } if (c.widgets.length) { // ensure unique widget ids diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 281b35d..ec4ae47 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.8+ widgets - updated 6/4/2013 +/*! tableSorter 2.8+ widgets - updated 10/18/2013 * * Column Styles * Column Filters @@ -66,10 +66,11 @@ ts.themes = { val = (v && v.hasOwnProperty('mywidget')) ? v.mywidget : ''; alert(val); // "data1" if saved, or "" if not */ -ts.storage = function(table, key, val){ +ts.storage = function(table, key, val, options){ var d, k, ls = false, v = {}, - id = table.id || $('.tablesorter').index( $(table) ), - url = window.location.pathname; + c = table.config, + id = options && options.id || $(table).attr(options && options.group || 'data-table-group') || table.id || $('.tablesorter').index( $(table) ), + url = options && options.url || $(table).attr(options && options.page || 'data-table-page') || c && c.fixedUrl || window.location.pathname; // https://gist.github.com/paulirish/5558557 if ("localStorage" in window) { try { @@ -355,7 +356,9 @@ ts.addWidget({ } }, format: function(table, c, wo){ - if (c.parsers && !c.$table.hasClass('hasFilters')){ + if (c.$table.hasClass('hasFilters')) { return; } + // allow filter widget to work if it is being used + if (c.parsers || !c.parsers && wo.filter_serversideFiltering){ var i, j, k, l, val, ff, x, xi, st, sel, str, ft, ft2, $th, rg, s, t, dis, col, fmt = ts.formatFloat, @@ -363,7 +366,8 @@ ts.addWidget({ $ths = c.$headers, $t = c.$table.addClass('hasFilters'), b = c.$tbodies, - cols = c.parsers.length, + // c.columns defined in computeThIndexes() + cols = c.columns || c.$headers.filter('th').length, parsed, time, timer, // dig fer gold @@ -572,7 +576,7 @@ ts.addWidget({ arry = $.grep(arry, function(v, k){ return $.inArray(v, arry) === k; }); - arry = (ts.sortText) ? arry.sort(function(a, b){ return ts.sortText(table, a, b, i); }) : arry.sort(true); + arry = (ts.sortNatural) ? arry.sort(function(a, b){ return ts.sortNatural(a, b); }) : arry.sort(true); // Get curent filter value currentVal = $t.find('thead').find('select.tablesorter-filter[data-column="' + i + '"]').val(); @@ -615,6 +619,7 @@ ts.addWidget({ wo.filter_regex.child = new RegExp(c.cssChildRow); wo.filter_regex.filtered = new RegExp(wo.filter_filteredRow); // don't build filter row if columnFilters is false or all columns are set to "filter-false" - issue #156 + if (wo.filter_columnFilters !== false && $ths.filter('.filter-false').length !== $ths.length){ // build filter row t = '<tr class="tablesorter-filter-row">'; From be84de2cef921da7194467b04d44d1b1c949033c Mon Sep 17 00:00:00 2001 From: Erik Ernst <github@black-milk.de> Date: Sat, 2 Nov 2013 20:11:45 +0100 Subject: [PATCH 005/138] * updated tablesorter to latest version (2.13.2) --- CHANGELOG.markdown | 4 ++ README.markdown | 2 +- jquery-tablesorter.gemspec | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 16 ++++--- .../jquery-tablesorter/jquery.tablesorter.js | 11 +++-- ...ry.tablesorter.widgets-filter-formatter.js | 8 ++-- .../jquery.tablesorter.widgets.js | 43 ++++++++++--------- 9 files changed, 51 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index 749be61..6a0dee4 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -1,5 +1,9 @@ Changelog === +#### v1.8.0 + +* Upgrade tablesorter to v2.13.2 + #### v1.7.0 * Upgrade tablesorter to v2.12 diff --git a/README.markdown b/README.markdown index fd56c46..393e262 100644 --- a/README.markdown +++ b/README.markdown @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.12 (10/18/2013), [documentation] +Current tablesorter version: 2.13.2 (11/2/2013), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. diff --git a/jquery-tablesorter.gemspec b/jquery-tablesorter.gemspec index 4fd33c7..de158b1 100644 --- a/jquery-tablesorter.gemspec +++ b/jquery-tablesorter.gemspec @@ -12,7 +12,7 @@ Gem::Specification.new do |s| s.homepage = "https://github.com/themilkman/jquery-tablesorter-rails" s.summary = "Simple integration of jquery-tablesorter into the asset pipeline." s.description = "Simple integration of jquery-tablesorter into the asset pipeline." - s.license = "MIT" + s.license = "MIT" s.files = Dir["{vendor,lib}/**/*"] + ["MIT-LICENSE", "Rakefile", "README.markdown"] diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 309cf08..8465b8a 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = "1.7.0" + VERSION = "1.8.0" end diff --git a/tablesorter b/tablesorter index 8310e01..774192b 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 8310e011973a7c57d2e725d864e5e30a8c040a44 +Subproject commit 774192bf35c11c4a6efc304e7ed4f4f1c23ea2c3 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 4884f1e..36db47c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 10/18/2013 + * updated 10/30/2013 */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -65,6 +65,11 @@ // table row set to a height to compensate; default is false fixedHeight: false, + // count child rows towards the set page size? (set true if it is a visible table row within the pager) + // if true, child row(s) may not appear to be attached to its parent row, may be split across pages or + // may distort the table if rowspan or cellspans are included. + countChildRows: false, + // remove rows from the table to speed up the sort of large tables. // setting this to false, only hides the non-visible rows; needed if you plan to add/remove rows with the pager enabled. removeRows: false, // removing rows in larger tables speeds up the sort @@ -199,7 +204,7 @@ if ( !rows[i].className.match(f) ) { rows[i].style.display = ( j >= s && j < e ) ? '' : 'none'; // don't count child rows - j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) ? 0 : 1; + j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) && !p.countChildRows ? 0 : 1; } } } @@ -524,10 +529,11 @@ }; $this.appender = function(table, rows) { - var p = table.config.pager; + var c = table.config, + p = c.pager; if ( !p.ajax ) { - table.config.rowsCopy = rows; - p.totalRows = rows.length; + c.rowsCopy = rows; + p.totalRows = p.countChildRows ? c.$tbodies.eq(0).children().length : rows.length; p.size = $.data(table, 'pagerLastSize') || p.size; p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) ); renderTable(table, rows, p); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index fe65dd0..fd8b734 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.12.0 - Client-side table sorting with ease! +* TableSorter 2.13.2 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.12.0"; + ts.version = "2.13.2"; ts.parsers = []; ts.widgets = []; @@ -1066,11 +1066,10 @@ if (a === b) { return 0; } var xN, xD, yN, yD, xF, yF, i, mx, r = ts.regex; - // numeric or hex detection - yD = parseInt(b.match(r.hex), 16); // first try and sort Hex codes - if (yD) { + if (r.hex.test(b)) { xD = parseInt(a.match(r.hex), 16); + yD = parseInt(b.match(r.hex), 16); if ( xD < yD ) { return -1; } if ( xD > yD ) { return 1; } } @@ -1317,7 +1316,7 @@ } if(/^\s*\([.\d]+\)/.test(s)) { // make (#) into a negative number -> (10) = -10 - s = s.replace(/^\s*\(/,'-').replace(/\)/,''); + s = s.replace(/^\s*\(([.\d]+)\)/, '-$1'); } i = parseFloat(s); // return the text instead of zero diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js index 29c3fee..616ee7b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js @@ -1,4 +1,4 @@ -/*! Filter widget formatter functions - updated 10/10/2013 +/*! Filter widget formatter functions - updated 10/30/2013 * requires: tableSorter 2.7.7+ and jQuery 1.4.3+ * * uiSpinner (jQuery UI spinner) @@ -519,7 +519,7 @@ $.tablesorter.filterFormatter = { var localfrom = o.defaultDate = o.from || o.defaultDate; closeFrom = o.onClose = function( selectedDate, ui ) { - var from = new Date( selectedDate ).getTime() || '', + var from = new Date( $cell.find('.dateFrom').datepicker('getDate') ).getTime() || '', to = new Date( $cell.find('.dateTo').datepicker('getDate') ).getTime() || '', range = from ? ( to ? from + ' - ' + to : '>=' + from ) : (to ? '<=' + to : ''); $cell @@ -542,7 +542,7 @@ $.tablesorter.filterFormatter = { o.defaultDate = o.to || '+7d'; // set to date +7 days from today (if not defined) closeTo = o.onClose = function( selectedDate, ui ) { var from = new Date( $cell.find('.dateFrom').datepicker('getDate') ).getTime() || '', - to = new Date( selectedDate ).getTime() || '', + to = new Date( $cell.find('.dateTo').datepicker('getDate') ).getTime() || '', range = from ? ( to ? from + ' - ' + to : '>=' + from ) : (to ? '<=' + to : ''); $cell .find('.dateRange').val(range) @@ -608,7 +608,7 @@ $.tablesorter.filterFormatter = { $number = $('<input type="number" style="visibility:hidden;" value="test">').appendTo($cell), // test if HTML5 number is supported - from Modernizr numberSupported = o.skipTest || $number.attr('type') === 'number' && $number.val() !== 'test', - t, l, $shcell = [], + l, $shcell = [], c = $cell.closest('table')[0].config, updateCompare = function(v) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index ec4ae47..57fc70f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.8+ widgets - updated 10/18/2013 +/*! tableSorter 2.8+ widgets - updated 11/2/2013 * * Column Styles * Column Filters @@ -123,7 +123,7 @@ ts.addHeaderResizeEvent = function(table, disable, options){ wo.resize_flag = true; headers = []; c.$headers.each(function(){ - var d = $.data(this, 'savedSizes'), + var d = $.data(this, 'savedSizes') || [0,0], // fixes #394 w = this.offsetWidth, h = this.offsetHeight; if (w !== d[0] || h !== d[1]) { @@ -134,14 +134,14 @@ ts.addHeaderResizeEvent = function(table, disable, options){ if (headers.length) { c.$table.trigger('resize', [ headers ]); } wo.resize_flag = false; }; + c.$headers.each(function(){ + $.data(this, 'savedSizes', [ this.offsetWidth, this.offsetHeight ]); + }); clearInterval(wo.resize_timer); if (disable) { wo.resize_flag = false; return false; } - c.$headers.each(function(){ - $.data(this, 'savedSizes', [ this.offsetWidth, this.offsetHeight ]); - }); wo.resize_timer = setInterval(function(){ if (wo.resize_flag) { return; } checkSizes(); @@ -399,7 +399,7 @@ ts.addWidget({ } }, findRows = function(filter, v, cv){ - var $tb, $tr, $td, cr, r, l, ff, time, r1, r2, searchFiltered; + var $tb, $tr, $td, cr, r, l, ff, fr, time, r1, r2, searchFiltered; if (c.debug) { time = new Date(); } for (k = 0; k < b.length; k++ ){ if (b.eq(k).hasClass(ts.css.info)) { continue; } // ignore info blocks, issue #264 @@ -477,11 +477,11 @@ ts.addWidget({ ff = val === '' ? true : !(wo.filter_startsWith ? s === 0 : s >= 0); // Look for operators >, >=, < or <= } else if (/^[<>]=?/.test(val)){ - s = fmt(val.replace(wo.filter_regex.nondigit, '').replace(wo.filter_regex.operators,''), table); + s = fr = fmt(val.replace(wo.filter_regex.nondigit, '').replace(wo.filter_regex.operators,''), table); // parse filter value in case we're comparing numbers (dates) if (parsed[i] || c.parsers[i].type === 'numeric') { rg = c.parsers[i].format('' + val.replace(wo.filter_regex.operators,''), table, $ths.eq(i), i); - s = (rg !== '' && !isNaN(rg)) ? rg : s; + s = (isNaN(s) && rg !== '' && !isNaN(rg)) ? rg : s; } // xi may be numeric - see issue #149; // check if c.cache[k].normalized[j] is defined, because sometimes j goes out of range? (numeric columns) @@ -489,7 +489,7 @@ ts.addWidget({ isNaN(xi) ? fmt(xi.replace(wo.filter_regex.nondigit, ''), table) : fmt(xi, table); if (/>/.test(val)) { ff = />=/.test(val) ? rg >= s : rg > s; } if (/</.test(val)) { ff = /<=/.test(val) ? rg <= s : rg < s; } - if (s === '') { ff = true; } // keep showing all rows if nothing follows the operator + if (!ff && fr === '') { ff = true; } // keep showing all rows if nothing follows the operator // Look for an AND or && operator (logical and) } else if (/\s+(AND|&&)\s+/g.test(v[i])) { s = val.split(/(?:\s+(?:and|&&)\s+)/g); @@ -533,7 +533,12 @@ ts.addWidget({ } $tr[j].style.display = (r ? '' : 'none'); $tr.eq(j)[r ? 'removeClass' : 'addClass'](wo.filter_filteredRow); - if (cr.length) { cr[r ? 'show' : 'hide'](); } + if (cr.length) { + if (c.pager && c.pager.countChildRows || wo.pager_countChildRows) { + cr[r ? 'removeClass' : 'addClass'](wo.filter_filteredRow); // see issue #396 + } + cr[r ? 'show' : 'hide'](); + } } } ts.processTbody(table, $tb, false); @@ -754,9 +759,7 @@ ts.addWidget({ // $(':focus') needs jQuery 1.6+ if ($(document.activeElement).closest('tr')[0] !== ft[0]){ // get all filter values - all = $t.find('.tablesorter-filter').map(function(){ - return $(this).val() || ''; - }).get().join(''); + all = ts.getFilters(table).join(''); // don't hide row if any filter has a value if (all === ''){ ft.addClass('hideme'); @@ -770,7 +773,7 @@ ts.addWidget({ clearTimeout(st); st = setTimeout(function(){ // don't hide row if any filter has a value - if ($t.find('.tablesorter-filter').map(function(){ return $(this).val() || ''; }).get().join('') === ''){ + if (ts.getFilters(table).join('') === ''){ ft2[ e.type === 'focus' ? 'removeClass' : 'addClass']('hideme'); } }, 200); @@ -820,22 +823,22 @@ ts.addWidget({ $tb.children().removeClass(wo.filter_filteredRow).show(); ts.processTbody(table, $tb, false); // restore tbody } - if (wo.filterreset) { $(document).undelegate(wo.filter_reset, 'click.tsfilter'); } + if (wo.filter_reset) { $(document).undelegate(wo.filter_reset, 'click.tsfilter'); } } }); ts.getFilters = function(table) { var c = table ? $(table)[0].config : {}; if (c && c.widgetOptions && !c.widgetOptions.filter_columnFilters) { return $(table).data('lastSearch'); } - return c && c.$filters ? c.$filters.find('.tablesorter-filter').map(function(i, el) { - return $(el).val(); + return c && c.$filters ? c.$filters.map(function(i, el) { + return $(el).find('.tablesorter-filter').val() || ''; }).get() || [] : false; }; ts.setFilters = function(table, filter, apply) { var $t = $(table), c = $t.length ? $t[0].config : {}, - valid = c && c.$filters ? c.$filters.find('.tablesorter-filter').each(function(i, el) { - $(el).val(filter[i] || ''); - }).trigger('change.tsfilter') || false : false; + valid = c && c.$filters ? c.$filters.each(function(i, el) { + $(el).find('.tablesorter-filter').val(filter[i] || ''); + }) || false : false; if (apply) { $t.trigger('search', [filter, false]); } return !!valid; }; From 4c1321cc6fa0ceea863925e9a9f03ef2b9eed87d Mon Sep 17 00:00:00 2001 From: Erik Ernst <github@black-milk.de> Date: Sun, 10 Nov 2013 20:18:48 +0100 Subject: [PATCH 006/138] * updated tablesorter to latest version (2.13.3) --- CHANGELOG.markdown | 4 + README.markdown | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 111 +- .../jquery-tablesorter/jquery.tablesorter.js | 41 +- ...ry.tablesorter.widgets-filter-formatter.js | 15 +- .../jquery.tablesorter.widgets.js | 1061 ++++++++++------- .../jquery-tablesorter/theme.bootstrap.css | 4 +- .../jquery-tablesorter/theme.dark.css | 2 +- .../jquery-tablesorter/theme.jui.css | 4 +- 11 files changed, 728 insertions(+), 520 deletions(-) diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index 6a0dee4..67e371a 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -1,5 +1,9 @@ Changelog === +#### v1.8.1 (2013-11-10) + +* Upgrade tablesorter to v2.13.3 + #### v1.8.0 * Upgrade tablesorter to v2.13.2 diff --git a/README.markdown b/README.markdown index 393e262..fe89111 100644 --- a/README.markdown +++ b/README.markdown @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.13.2 (11/2/2013), [documentation] +Current tablesorter version: 2.13.3 (11/9/2013), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 8465b8a..3f043c7 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = "1.8.0" + VERSION = "1.8.1" end diff --git a/tablesorter b/tablesorter index 774192b..ce7e083 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 774192bf35c11c4a6efc304e7ed4f4f1c23ea2c3 +Subproject commit ce7e0836c0d2c739aa157250acabef5e286d9a22 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 36db47c..014339d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 10/30/2013 + * updated 11/9/2013 */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -236,6 +236,9 @@ $t.find('thead tr.' + p.cssErrorRow).remove(); // Clean up any previous error. if ( exception ) { + if (c.debug) { + ts.log('Ajax Error', xhr, exception); + } $err = $('<tr class="' + p.cssErrorRow + '"><td style="text-align:center;" colspan="' + hl + '">' + ( xhr.status === 0 ? 'Not connected, verify Network' : xhr.status === 404 ? 'Requested page not found [404]' : @@ -272,15 +275,13 @@ c.$tbodies.eq(0).empty().append(d); } else if (l) { // build table from array - if ( l > 0 ) { - for ( i = 0; i < l; i++ ) { - tds += '<tr>'; - for ( j = 0; j < d[i].length; j++ ) { - // build tbody cells - tds += '<td>' + d[i][j] + '</td>'; - } - tds += '</tr>'; + for ( i = 0; i < l; i++ ) { + tds += '<tr>'; + for ( j = 0; j < d[i].length; j++ ) { + // build tbody cells + tds += '<td>' + d[i][j] + '</td>'; } + tds += '</tr>'; } // add rows to first tbody c.$tbodies.eq(0).html( tds ); @@ -314,9 +315,15 @@ if (c.showProcessing) { ts.isProcessing(table); // remove loading icon } - p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) ); + // make sure last pager settings are saved, prevents multiple server side calls with + // the same parameters + p.last.totalPages = p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) ); + p.last.currentFilters = p.currentFilters; + p.last.sortList = (c.sortList || []).join(','); updatePageDisplay(table, p); fixHeight(table, p); + // apply widgets after table has rendered + $t.trigger('applyWidgets'); if (p.initialized) { $t.trigger('pagerChange', p); $t.trigger('updateComplete'); @@ -350,17 +357,21 @@ p.oldAjaxSuccess(data); } }; + if (c.debug) { + ts.log('ajax initialized', p.ajaxObject); + } $.ajax(p.ajaxObject); } }, getAjaxUrl = function(table, p) { - var url = (p.ajaxUrl) ? p.ajaxUrl + var c = table.config, + url = (p.ajaxUrl) ? p.ajaxUrl // allow using "{page+1}" in the url string to switch to a non-zero based index .replace(/\{page([\-+]\d+)?\}/, function(s,n){ return p.page + (n ? parseInt(n, 10) : 0); }) .replace(/\{size\}/g, p.size) : '', - sl = table.config.sortList, - fl = p.currentFilters || [], + sl = c.sortList, + fl = p.currentFilters || $(table).data('lastSearch') || [], sortCol = url.match(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/), filterCol = url.match(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/), arry = []; @@ -382,10 +393,14 @@ }); // if the arry is empty, just add the fcol parameter... "&{filterList:fcol}" becomes "&fcol" url = url.replace(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : filterCol ); + p.currentFilters = fl; } if ( typeof(p.customAjaxUrl) === "function" ) { url = p.customAjaxUrl(table, url); } + if (c.debug) { + ts.log('Pager ajax url: ' + url); + } return url; }, @@ -433,6 +448,9 @@ p.totalPages = 1; $(table).addClass('pagerDisabled').find('tr.pagerSavedHeightSpacer').remove(); renderTable(table, table.config.rowsCopy, p); + if (table.config.debug) { + ts.log('pager disabled'); + } } // disable size selector p.$size.add(p.$goto).each(function(){ @@ -442,17 +460,25 @@ moveToPage = function(table, p, flag) { if ( p.isDisabled ) { return; } - var l = p.last, + var c = table.config, + l = p.last, pg = Math.min( p.totalPages, p.filteredPages ); if ( p.page < 0 ) { p.page = 0; } if ( p.page > ( pg - 1 ) && pg !== 0 ) { p.page = pg - 1; } - // don't allow rendering multiple times on the same page/size/totalpages/filters - if (l.page === p.page && l.size === p.size && l.total === p.totalPages && l.filters === p.currentFilters ) { return; } + // don't allow rendering multiple times on the same page/size/totalpages/filters/sorts + if ( l.page === p.page && l.size === p.size && l.totalPages === p.totalPages && + (l.currentFilters || []).join(',') === (p.currentFilters || []).join(',') && + l.sortList === (c.sortList || []).join(',') ) { return; } + if (c.debug) { + ts.log('Pager changing to page ' + p.page); + } p.last = { page : p.page, size : p.size, + // fixes #408; modify sortList otherwise it auto-updates + sortList : (c.sortList || []).join(','), totalPages : p.totalPages, - currentFilters : p.currentFilters + currentFilters : p.currentFilters || [] }; if (p.ajax) { getAjax(table, p); @@ -460,18 +486,18 @@ renderTable(table, table.config.rowsCopy, p); } $.data(table, 'pagerLastPage', p.page); - $.data(table, 'pagerUpdateTriggered', true); if (p.initialized && flag !== false) { - $(table).trigger('pageMoved', p); + c.$table.trigger('pageMoved', p); + c.$table.trigger('applyWidgets'); } }, setPageSize = function(table, size, p) { - p.size = size; - p.$size.val(size); + p.size = size || p.size || 10; + p.$size.val(p.size); $.data(table, 'pagerLastPage', p.page); $.data(table, 'pagerLastSize', p.size); - p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) ); + p.totalPages = Math.ceil( p.totalRows / p.size ); moveToPage(table, p); }, @@ -517,14 +543,17 @@ p.$goto.removeClass(p.cssDisabled).removeAttr('disabled'); p.isDisabled = false; p.page = $.data(table, 'pagerLastPage') || p.page || 0; - p.size = $.data(table, 'pagerLastSize') || parseInt(pg.find('option[selected]').val(), 10) || p.size; + p.size = $.data(table, 'pagerLastSize') || parseInt(pg.find('option[selected]').val(), 10) || p.size || 10; pg.val(p.size); // set page size - p.totalPages = Math.ceil( Math.min( p.totalPages, p.filteredPages ) / ( p.size || 10 ) ); + p.totalPages = Math.ceil( Math.min( p.totalPages, p.filteredPages ) / p.size ); if ( triggered ) { $(table).trigger('update'); setPageSize(table, p.size, p); hideRowsSetup(table, p); fixHeight(table, p); + if (table.config.debug) { + ts.log('pager enabled'); + } } }; @@ -534,8 +563,8 @@ if ( !p.ajax ) { c.rowsCopy = rows; p.totalRows = p.countChildRows ? c.$tbodies.eq(0).children().length : rows.length; - p.size = $.data(table, 'pagerLastSize') || p.size; - p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) ); + p.size = $.data(table, 'pagerLastSize') || p.size || 10; + p.totalPages = Math.ceil( p.totalRows / p.size ); renderTable(table, rows, p); } }; @@ -551,34 +580,36 @@ $t = c.$table, // added in case the pager is reinitialized after being destroyed. pager = p.$container = $(p.container).addClass('tablesorter-pager').show(); + if (c.debug) { + ts.log('Pager initializing'); + } p.oldAjaxSuccess = p.oldAjaxSuccess || p.ajaxObject.success; c.appender = $this.appender; - + if (ts.filter && c.widgets.indexOf('filter') >= 0) { + // get any default filter settings (data-value attribute) fixes #388 + p.currentFilters = c.$table.data('lastSearch') || ts.filter.setDefaults(table, c, c.widgetOptions) || []; + // set, but don't apply current filters + ts.setFilters(table, p.currentFilters, false); + } if (p.savePages && ts.storage) { t = ts.storage(table, 'tablesorter-pager') || {}; // fixes #387 p.page = isNaN(t.page) ? p.page : t.page; p.size = ( isNaN(t.size) ? p.size : t.size ) || 10; + $.data(table, 'pagerLastSize', p.size); } $t - .unbind('filterStart.pager filterEnd.pager sortEnd.pager disable.pager enable.pager destroy.pager update.pager pageSize.pager') + .unbind('filterStart filterEnd sortEnd disable enable destroy update pageSize '.split(' ').join('.pager ')) .bind('filterStart.pager', function(e, filters) { - $.data(table, 'pagerUpdateTriggered', false); p.currentFilters = filters; }) // update pager after filter widget completes - .bind('filterEnd.pager sortEnd.pager', function(e) { - //Prevent infinite event loops from occuring by setting this in all moveToPage calls and catching it here. - if ($.data(table, 'pagerUpdateTriggered')) { - $.data(table, 'pagerUpdateTriggered', false); - return; - } - //only run the server side sorting if it has been enabled - if (e.type === "filterEnd" || (e.type === "sortEnd" && c.serverSideSorting)) { + .bind('filterEnd.pager sortEnd.pager', function() { + if (p.initialized) { moveToPage(table, p, false); + updatePageDisplay(table, p, false); + fixHeight(table, p); } - updatePageDisplay(table, p, false); - fixHeight(table, p); }) .bind('disable.pager', function(e){ e.stopPropagation(); @@ -617,6 +648,7 @@ pager.find(ctrls.join(',')) .unbind('click.pager') .bind('click.pager', function(e){ + e.stopPropagation(); var i, $t = $(this), l = ctrls.length; if ( !$t.hasClass(p.cssDisabled) ) { for (i = 0; i < l; i++) { @@ -626,7 +658,6 @@ } } } - return false; }); // goto selector diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index fd8b734..ea915fe 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.13.2 - Client-side table sorting with ease! +* TableSorter 2.13.3 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.13.2"; + ts.version = "2.13.3"; ts.parsers = []; ts.widgets = []; @@ -122,7 +122,8 @@ }; /* debuging utils */ - function log(s) { + function log() { + var s = arguments.length > 1 ? Array.prototype.slice.call(arguments) : arguments[0]; if (typeof console !== "undefined" && typeof console.log !== "undefined") { console.log(s); } else { @@ -201,9 +202,12 @@ var c = table.config, // update table bodies in case we start with an empty table tb = c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'), - rows, list, l, i, h, ch, p, parsersDebug = ""; + rows, list, l, i, h, ch, p, time, parsersDebug = ""; if ( tb.length === 0) { return c.debug ? log('*Empty table!* Not building a parser cache') : ''; + } else if (c.debug) { + time = new Date(); + log('Detecting parsers for each column'); } rows = tb[0].rows; if (rows[0]) { @@ -233,6 +237,7 @@ } if (c.debug) { log(parsersDebug); + benchmark("Completed detecting parsers", time); } c.parsers = list; } @@ -301,11 +306,12 @@ // init flag (true) used by pager plugin to prevent widget application function appendToTable(table, init) { var c = table.config, - b = table.tBodies, - rows = [], - c2 = c.cache, - r, n, totalRows, checkCell, $bk, $tb, - i, j, k, l, pos, appendTime; + wo = c.widgetOptions, + b = table.tBodies, + rows = [], + c2 = c.cache, + r, n, totalRows, checkCell, $bk, $tb, + i, j, k, l, pos, appendTime; if (isEmptyObject(c2)) { return; } // empty table - fixes #206/#346 if (c.debug) { appendTime = new Date(); @@ -322,8 +328,8 @@ for (i = 0; i < totalRows; i++) { pos = n[i][checkCell]; rows.push(r[pos]); - // removeRows used by the pager plugin - if (!c.appender || !c.removeRows) { + // removeRows used by the pager plugin; don't render if using ajax - fixes #411 + if (!c.appender || (c.pager && (!c.pager.removeRows || !wo.pager_removeRows) && !c.pager.ajax)) { l = r[pos].length; for (j = 0; j < l; j++) { $tb.append(r[pos][j]); @@ -340,8 +346,8 @@ if (c.debug) { benchmark("Rebuilt table", appendTime); } - // apply table widgets - if (!init) { ts.applyWidget(table); } + // apply table widgets; but not before ajax completes + if (!init && !c.appender) { ts.applyWidget(table); } // trigger sortend $(table).trigger("sortEnd", table); $(table).trigger("updateComplete", table); @@ -517,7 +523,7 @@ // ensure all sortList values are numeric - fixes #127 s = [ parseInt(v[0], 10), parseInt(v[1], 10) ]; // make sure header exists - o = c.headerList[s[0]]; + o = c.$headers[s[0]]; if (o) { // prevents error if sorton array is wrong c.sortList.push(s); t = $.inArray(s[1], o.order); // fixes issue #167 @@ -584,12 +590,13 @@ } // the user has clicked on an already sorted column if (ts.isValueInArray(i, c.sortList)) { - // reverse the sorting direction for all tables + // reverse the sorting direction for (j = 0; j < c.sortList.length; j++) { s = c.sortList[j]; - o = c.headerList[s[0]]; + o = c.$headers[s[0]]; if (s[0] === i) { - s[1] = o.order[o.count]; + // o.count seems to be incorrect when compared to cell.count + s[1] = o.order[cell.count]; if (s[1] === 2) { c.sortList.splice(j,1); o.count = -1; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js index 616ee7b..601a17e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js @@ -1,4 +1,4 @@ -/*! Filter widget formatter functions - updated 10/30/2013 +/*! Filter widget formatter functions - updated 11/9/2013 * requires: tableSorter 2.7.7+ and jQuery 1.4.3+ * * uiSpinner (jQuery UI spinner) @@ -92,7 +92,7 @@ $.tablesorter.filterFormatter = { .val(o.value) .appendTo($cell) .spinner(o) - .bind('change keyup', function(e){ + .bind('change keyup', function(){ updateSpinner(); }); @@ -113,7 +113,7 @@ $.tablesorter.filterFormatter = { .val(o.value) .appendTo($shcell) .spinner(o) - .bind('change keyup', function(e){ + .bind('change keyup', function(){ $cell.find('.spinner').val( this.value ); updateSpinner(); }); @@ -228,7 +228,7 @@ $.tablesorter.filterFormatter = { .val(o.value) .appendTo($shcell) .slider(o) - .bind('change keyup', function(e){ + .bind('change keyup', function(){ $cell.find('.slider').val( this.value ); updateSlider(); }); @@ -337,7 +337,7 @@ $.tablesorter.filterFormatter = { .val(o.value) .appendTo($shcell) .slider(o) - .bind('change keyup', function(e){ + .bind('change keyup', function(){ $cell.find('.range').val( this.value ); updateUiRange(); }); @@ -630,8 +630,7 @@ $.tablesorter.filterFormatter = { var compare = ( $cell.find('.compare').val() || o.compare); $cell.find('input[type=hidden]') // add equal to the beginning, so we filter exact numbers - .val( !o.addToggle || chkd ? (o.compare ? o.compare : o.exactMatch ? '=' : '') + v : '' ) - .val( !o.addToggle || chkd ? compare + v : '' ) + .val( !o.addToggle || chkd ? (compare ? compare : o.exactMatch ? '=' : '') + v : '' ) .trigger('search', delayed ? delayed : o.delayed).end() .find('.number').val(v); if ($cell.find('.number').length) { @@ -732,7 +731,7 @@ $.tablesorter.filterFormatter = { HTML5 range slider \**********************/ html5Range : function($cell, indx, def5Range) { - var t, o = $.extend({ + var o = $.extend({ value : 0, min : 0, max : 100, diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 57fc70f..fe2d31d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.8+ widgets - updated 11/2/2013 +/*! tableSorter 2.8+ widgets - updated 11/9/2013 * * Column Styles * Column Filters @@ -17,6 +17,7 @@ var ts = $.tablesorter = $.tablesorter || {}; ts.themes = { "bootstrap" : { table : 'table table-bordered table-striped', + caption : 'caption', header : 'bootstrap-header', // give the header a gradient background footerRow : '', footerCells: '', @@ -32,6 +33,7 @@ ts.themes = { }, "jui" : { table : 'ui-widget ui-widget-content ui-corner-all', // table classes + caption : 'ui-widget-content ui-corner-all', header : 'ui-widget-header ui-corner-all ui-state-default', // header classes footerRow : '', footerCells: '', @@ -167,10 +169,13 @@ ts.addWidget({ sh = 'tr.' + (wo.stickyHeaders || 'tablesorter-stickyHeader'), rmv = o.sortNone + ' ' + o.sortDesc + ' ' + o.sortAsc; if (c.debug) { time = new Date(); } + // initialization code - run once if (!$t.hasClass('tablesorter-' + theme) || c.theme === theme || !table.hasInitialized){ // update zebra stripes if (o.even !== '') { wo.zebra[0] += ' ' + o.even; } if (o.odd !== '') { wo.zebra[1] += ' ' + o.odd; } + // add caption style + $t.find('caption').addClass(o.caption); // add table/footer class names t = $t // remove other selected themes; use widgetOptions.theme_remove @@ -191,7 +196,7 @@ ts.addWidget({ $(this)[ e.type === 'mouseenter' ? 'addClass' : 'removeClass' ](o.hover); }); if (!$h.find('.tablesorter-wrapper').length) { - // Firefox needs this inner div to position the resizer correctly + // Firefox needs this inner div to position the icon/resizer correctly $h.wrapInner('<div class="tablesorter-wrapper" style="position:relative;height:100%;width:100%"></div>'); } if (c.cssIcon){ @@ -327,6 +332,7 @@ ts.addWidget({ id: "filter", priority: 50, options : { + filter_anyMatch : false, // if true overrides default find rows behaviours and if any column matches query it returns that row filter_childRows : false, // if true, filter includes child row content in the search filter_columnFilters : true, // if true, a filter will be added to the top of each table column filter_cssFilter : '', // css class name added to the filter row & each input in the row (tablesorter-filter is ALWAYS added) @@ -342,504 +348,663 @@ ts.addWidget({ filter_startsWith : false, // if true, filter start from the beginning of the cell contents filter_useParsedData : false, // filter all data using parsed content filter_serversideFiltering : false, // if true, server-side filtering should be performed because client-side filtering will be disabled, but the ui and events will still be used. - filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value - - // regex used in filter "check" functions - not for general use and not documented - filter_regex : { - "regex" : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // regex to test for regex - "child" : /tablesorter-childRow/, // child row class name; this gets updated in the script - "filtered" : /filtered/, // filtered (hidden) row class name; updated in the script - "type" : /undefined|number/, // check type - "exact" : /(^[\"|\'|=]+)|([\"|\'|=]+$)/g, // exact match (allow '==') - "nondigit" : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser) - "operators" : /[<>=]/g // replace operators + filter_defaultAttrib : 'data-value' // data attribute in the header cell that contains the default filter value + }, + format: function(table, c, wo) { + if (!c.$table.hasClass('hasFilters')) { + if (c.parsers || !c.parsers && wo.filter_serversideFiltering) { + ts.filter.init(table, c, wo); + } } }, - format: function(table, c, wo){ - if (c.$table.hasClass('hasFilters')) { return; } - // allow filter widget to work if it is being used - if (c.parsers || !c.parsers && wo.filter_serversideFiltering){ - var i, j, k, l, val, ff, x, xi, st, sel, str, - ft, ft2, $th, rg, s, t, dis, col, - fmt = ts.formatFloat, - last = '', // save last filter search - $ths = c.$headers, - $t = c.$table.addClass('hasFilters'), - b = c.$tbodies, - // c.columns defined in computeThIndexes() - cols = c.columns || c.$headers.filter('th').length, - parsed, time, timer, + remove: function(table, c, wo) { + var tbodyIndex, $tbody, + $table = c.$table, + $tbodies = c.$tbodies; + $table + .removeClass('hasFilters') + // add .tsfilter namespace to all BUT search + .unbind('addRows updateCell update updateComplete appendCache search filterStart filterEnd '.split(' ').join('.tsfilter ')) + .find('.tablesorter-filter-row').remove(); + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody + $tbody.children().removeClass(wo.filter_filteredRow).show(); + ts.processTbody(table, $tbody, false); // restore tbody + } + if (wo.filter_reset) { + $(document).undelegate(wo.filter_reset, 'click.tsfilter'); + } + } +}); + +ts.filter = { - // dig fer gold - checkFilters = function(filter){ - var arry = $.isArray(filter), - v = (arry) ? filter : ts.getFilters(table), - cv = (v || []).join(''); // combined filter values - // add filter array back into inputs - if (arry) { - ts.setFilters( $t, v ); + // regex used in filter "check" functions - not for general use and not documented + regex: { + regex : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // regex to test for regex + child : /tablesorter-childRow/, // child row class name; this gets updated in the script + filtered : /filtered/, // filtered (hidden) row class name; updated in the script + type : /undefined|number/, // check type + exact : /(^[\"|\'|=]+)|([\"|\'|=]+$)/g, // exact match (allow '==') + nondigit : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser) + operators : /[<>=]/g // replace operators + }, + // function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed ) + // filter = array of filter input values; iFilter = same array, except lowercase + // exact = table cell text (or parsed data if column parser enabled) + // iExact = same as exact, except lowercase + // cached = table cell text from cache, so it has been parsed + // index = column index; table = table element (DOM) + // wo = widget options (table.config.widgetOptions) + // parsed = array (by column) of boolean values (from filter_useParsedData or "filter-parsed" class) + types: { + // Look for regex + regex: function( filter, iFilter, exact, iExact ) { + if ( ts.filter.regex.regex.test(iFilter) ) { + var matches, + regex = ts.filter.regex.regex.exec(iFilter); + try { + matches = new RegExp(regex[1], regex[2]).test( iExact ); + } catch (error) { + matches = false; } - if (wo.filter_hideFilters){ - // show/hide filter row as needed - $t.find('.tablesorter-filter-row').trigger( cv === '' ? 'mouseleave' : 'mouseenter' ); + return matches; + } + return null; + }, + // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric + exact: function( filter, iFilter, exact, iExact ) { + /*jshint eqeqeq:false */ + if ( iFilter.replace(ts.filter.regex.exact, '') == iExact ) { + return true; + } + return null; + }, + // Look for a not match + notMatch: function( filter, iFilter, exact, iExact, cached, index, table, wo ) { + if ( /^\!/.test(iFilter) ) { + iFilter = iFilter.replace('!', ''); + var indx = iExact.search( $.trim(iFilter) ); + return iFilter === '' ? true : !(wo.filter_startsWith ? indx === 0 : indx >= 0); + } + return null; + }, + // Look for operators >, >=, < or <= + operators: function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed ) { + if ( /^[<>]=?/.test(iFilter) ) { + var cachedValue, result, + c = table.config, + query = ts.formatFloat( iFilter.replace(ts.filter.regex.operators, ''), table ), + parser = c.parsers[index], + savedSearch = query; + // parse filter value in case we're comparing numbers (dates) + if (parsed[index] || parser.type === 'numeric') { + cachedValue = parser.format( '' + iFilter.replace(ts.filter.regex.operators, ''), table, c.$headers.eq(index), index ); + query = ( typeof query === "number" && cachedValue !== '' && !isNaN(cachedValue) ) ? cachedValue : query; } - // return if the last search is the same; but filter === false when updating the search - // see example-widget-filter.html filter toggle buttons - if (last === cv && filter !== false) { return; } - $t.trigger('filterStart', [v]); - if (c.showProcessing) { - // give it time for the processing icon to kick in - setTimeout(function(){ - findRows(filter, v, cv); - return false; - }, 30); - } else { - findRows(filter, v, cv); - return false; + // iExact may be numeric - see issue #149; + // check if cached is defined, because sometimes j goes out of range? (numeric columns) + cachedValue = ( parsed[index] || parser.type === 'numeric' ) && !isNaN(query) && cached ? cached : + isNaN(iExact) ? ts.formatFloat( iExact.replace(ts.filter.regex.nondigit, ''), table) : + ts.formatFloat( iExact, table ); + if ( />/.test(iFilter) ) { result = />=/.test(iFilter) ? cachedValue >= query : cachedValue > query; } + if ( /</.test(iFilter) ) { result = /<=/.test(iFilter) ? cachedValue <= query : cachedValue < query; } + // keep showing all rows if nothing follows the operator + if ( !result && savedSearch === '' ) { result = true; } + return result; + } + return null; + }, + // Look for an AND or && operator (logical and) + and : function( filter, iFilter, exact, iExact ) { + if ( /\s+(AND|&&)\s+/g.test(filter) ) { + var query = iFilter.split( /(?:\s+(?:and|&&)\s+)/g ), + result = iExact.search( $.trim(query[0]) ) >= 0, + indx = query.length - 1; + while (result && indx) { + result = result && iExact.search( $.trim(query[indx]) ) >= 0; + indx--; } - }, - findRows = function(filter, v, cv){ - var $tb, $tr, $td, cr, r, l, ff, fr, time, r1, r2, searchFiltered; - if (c.debug) { time = new Date(); } - for (k = 0; k < b.length; k++ ){ - if (b.eq(k).hasClass(ts.css.info)) { continue; } // ignore info blocks, issue #264 - $tb = ts.processTbody(table, b.eq(k), true); - $tr = $tb.children('tr:not(.' + c.cssChildRow + ')'); - l = $tr.length; - if (cv === '' || wo.filter_serversideFiltering){ - $tb.children().show().removeClass(wo.filter_filteredRow); - } else { - // optimize searching only through already filtered rows - see #313 - searchFiltered = true; - r = $t.data('lastSearch') || []; - $.each(v, function(i,val){ - // check for changes from beginning of filter; but ignore if there is a logical "or" in the string - searchFiltered = (val || '').indexOf(r[i] || '') === 0 && searchFiltered && !/(\s+or\s+|\|)/g.test(val || ''); - }); - // can't search when all rows are hidden - this happens when looking for exact matches - if (searchFiltered && $tr.filter(':visible').length === 0) { searchFiltered = false; } - // loop through the rows - for (j = 0; j < l; j++){ - r = $tr[j].className; - // skip child rows & already filtered rows - if ( wo.filter_regex.child.test(r) || (searchFiltered && wo.filter_regex.filtered.test(r)) ) { continue; } - r = true; - cr = $tr.eq(j).nextUntil('tr:not(.' + c.cssChildRow + ')'); - // so, if "table.config.widgetOptions.filter_childRows" is true and there is - // a match anywhere in the child row, then it will make the row visible - // checked here so the option can be changed dynamically - t = (cr.length && wo.filter_childRows) ? cr.text() : ''; - t = wo.filter_ignoreCase ? t.toLocaleLowerCase() : t; - $td = $tr.eq(j).children('td'); - for (i = 0; i < cols; i++){ - // ignore if filter is empty or disabled - if (v[i]){ - // check if column data should be from the cell or from parsed data - if (wo.filter_useParsedData || parsed[i]){ - x = c.cache[k].normalized[j][i]; - } else { - // using older or original tablesorter - x = $.trim($td.eq(i).text()); - } - xi = !wo.filter_regex.type.test(typeof x) && wo.filter_ignoreCase ? x.toLocaleLowerCase() : x; - ff = r; // if r is true, show that row - // replace accents - see #357 - v[i] = c.sortLocaleCompare ? ts.replaceAccents(v[i]) : v[i]; - // val = case insensitive, v[i] = case sensitive - val = wo.filter_ignoreCase ? v[i].toLocaleLowerCase() : v[i]; - if (wo.filter_functions && wo.filter_functions[i]){ - if (wo.filter_functions[i] === true){ - // default selector; no "filter-select" class - ff = ($ths.filter('[data-column="' + i + '"]:last').hasClass('filter-match')) ? xi.search(val) >= 0 : v[i] === x; - } else if (typeof wo.filter_functions[i] === 'function'){ - // filter callback( exact cell content, parser normalized content, filter input value, column index ) - ff = wo.filter_functions[i](x, c.cache[k].normalized[j][i], v[i], i, $tr.eq(j)); - } else if (typeof wo.filter_functions[i][v[i]] === 'function'){ - // selector option function - ff = wo.filter_functions[i][v[i]](x, c.cache[k].normalized[j][i], v[i], i, $tr.eq(j)); - } - // Look for regex - } else if (wo.filter_regex.regex.test(val)){ - rg = wo.filter_regex.regex.exec(val); - try { - ff = new RegExp(rg[1], rg[2]).test(xi); - } catch (err){ - ff = false; - } - // Look for quotes or equals to get an exact match; ignore type since xi could be numeric - /*jshint eqeqeq:false */ - } else if (val.replace(wo.filter_regex.exact, '') == xi){ - ff = true; - // Look for a not match - } else if (/^\!/.test(val)){ - val = val.replace('!',''); - s = xi.search($.trim(val)); - ff = val === '' ? true : !(wo.filter_startsWith ? s === 0 : s >= 0); - // Look for operators >, >=, < or <= - } else if (/^[<>]=?/.test(val)){ - s = fr = fmt(val.replace(wo.filter_regex.nondigit, '').replace(wo.filter_regex.operators,''), table); - // parse filter value in case we're comparing numbers (dates) - if (parsed[i] || c.parsers[i].type === 'numeric') { - rg = c.parsers[i].format('' + val.replace(wo.filter_regex.operators,''), table, $ths.eq(i), i); - s = (isNaN(s) && rg !== '' && !isNaN(rg)) ? rg : s; - } - // xi may be numeric - see issue #149; - // check if c.cache[k].normalized[j] is defined, because sometimes j goes out of range? (numeric columns) - rg = ( parsed[i] || c.parsers[i].type === 'numeric' ) && !isNaN(s) && c.cache[k].normalized[j] ? c.cache[k].normalized[j][i] : - isNaN(xi) ? fmt(xi.replace(wo.filter_regex.nondigit, ''), table) : fmt(xi, table); - if (/>/.test(val)) { ff = />=/.test(val) ? rg >= s : rg > s; } - if (/</.test(val)) { ff = /<=/.test(val) ? rg <= s : rg < s; } - if (!ff && fr === '') { ff = true; } // keep showing all rows if nothing follows the operator - // Look for an AND or && operator (logical and) - } else if (/\s+(AND|&&)\s+/g.test(v[i])) { - s = val.split(/(?:\s+(?:and|&&)\s+)/g); - ff = xi.search($.trim(s[0])) >= 0; - r1 = s.length - 1; - while (ff && r1) { - ff = ff && xi.search($.trim(s[r1])) >= 0; - r1--; - } - // Look for a range (using " to " or " - ") - see issue #166; thanks matzhu! - } else if (/\s+(-|to)\s+/.test(val)){ - s = val.split(/(?: - | to )/); // make sure the dash is for a range and not indicating a negative number - r1 = fmt(s[0].replace(wo.filter_regex.nondigit, ''), table); - r2 = fmt(s[1].replace(wo.filter_regex.nondigit, ''), table); - // parse filter value in case we're comparing numbers (dates) - if (parsed[i] || c.parsers[i].type === 'numeric') { - rg = c.parsers[i].format('' + s[0], table, $ths.eq(i), i); - r1 = (rg !== '' && !isNaN(rg)) ? rg : r1; - rg = c.parsers[i].format('' + s[1], table, $ths.eq(i), i); - r2 = (rg !== '' && !isNaN(rg)) ? rg : r2; - } - rg = ( parsed[i] || c.parsers[i].type === 'numeric' ) && !isNaN(r1) && !isNaN(r2) ? c.cache[k].normalized[j][i] : - isNaN(xi) ? fmt(xi.replace(wo.filter_regex.nondigit, ''), table) : fmt(xi, table); - if (r1 > r2) { ff = r1; r1 = r2; r2 = ff; } // swap - ff = (rg >= r1 && rg <= r2) || (r1 === '' || r2 === '') ? true : false; - // Look for wild card: ? = single, * = multiple, or | = logical OR - } else if ( /[\?|\*]/.test(val) || /\s+OR\s+/.test(v[i]) ){ - s = val.replace(/\s+OR\s+/gi,"|"); - // look for an exact match with the "or" unless the "filter-match" class is found - if (!$ths.filter('[data-column="' + i + '"]:last').hasClass('filter-match') && /\|/.test(s)) { - s = '^(' + s + ')$'; - } - ff = new RegExp( s.replace(/\?/g, '\\S{1}').replace(/\*/g, '\\S*') ).test(xi); - // Look for match, and add child row data for matching - } else { - x = (xi + t).indexOf(val); - ff = ( (!wo.filter_startsWith && x >= 0) || (wo.filter_startsWith && x === 0) ); - } - r = (ff) ? (r ? true : false) : false; - } - } - $tr[j].style.display = (r ? '' : 'none'); - $tr.eq(j)[r ? 'removeClass' : 'addClass'](wo.filter_filteredRow); - if (cr.length) { - if (c.pager && c.pager.countChildRows || wo.pager_countChildRows) { - cr[r ? 'removeClass' : 'addClass'](wo.filter_filteredRow); // see issue #396 - } - cr[r ? 'show' : 'hide'](); - } - } + return result; + } + return null; + }, + // Look for a range (using " to " or " - ") - see issue #166; thanks matzhu! + range : function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed ) { + if ( /\s+(-|to)\s+/.test(iFilter) ) { + var result, + c = table.config, + query = iFilter.split(/(?: - | to )/), // make sure the dash is for a range and not indicating a negative number + range1 = ts.formatFloat(query[0].replace(ts.filter.regex.nondigit, ''), table), + range2 = ts.formatFloat(query[1].replace(ts.filter.regex.nondigit, ''), table); + // parse filter value in case we're comparing numbers (dates) + if (parsed[index] || c.parsers[index].type === 'numeric') { + result = c.parsers[index].format('' + query[0], table, c.$headers.eq(index), index); + range1 = (result !== '' && !isNaN(result)) ? result : range1; + result = c.parsers[index].format('' + query[1], table, c.$headers.eq(index), index); + range2 = (result !== '' && !isNaN(result)) ? result : range2; + } + result = ( parsed[index] || c.parsers[index].type === 'numeric' ) && !isNaN(range1) && !isNaN(range2) ? cached : + isNaN(iExact) ? ts.formatFloat( iExact.replace(ts.filter.regex.nondigit, ''), table) : + ts.formatFloat( iExact, table ); + if (range1 > range2) { result = range1; range1 = range2; range2 = result; } // swap + return (result >= range1 && result <= range2) || (range1 === '' || range2 === ''); + } + return null; + }, + // Look for wild card: ? = single, * = multiple, or | = logical OR + wild : function( filter, iFilter, exact, iExact, cached, index, table ) { + if ( /[\?|\*]/.test(iFilter) || /\s+OR\s+/.test(filter) ) { + var c = table.config, + query = iFilter.replace(/\s+OR\s+/gi,"|"); + // look for an exact match with the "or" unless the "filter-match" class is found + if (!c.$headers.filter('[data-column="' + index + '"]:last').hasClass('filter-match') && /\|/.test(query)) { + query = '^(' + query + ')$'; + } + return new RegExp( query.replace(/\?/g, '\\S{1}').replace(/\*/g, '\\S*') ).test(iExact); + } + return null; + }, + // fuzzy text search; modified from https://github.com/mattyork/fuzzy (MIT license) + fuzzy: function( filter, iFilter, exact, iExact ) { + if ( /^~/.test(iFilter) ) { + var indx, + patternIndx = 0, + len = iExact.length, + pattern = iFilter.slice(1); + for (indx = 0; indx < len; indx++) { + if (iExact[indx] === pattern[patternIndx]) { + patternIndx += 1; } - ts.processTbody(table, $tb, false); } - last = cv; // save last search - $t.data('lastSearch', v); - if (c.debug){ - ts.benchmark("Completed filter widget search", time); + if (patternIndx === pattern.length) { + return true; } - $t.trigger('applyWidgets'); // make sure zebra widget is applied - $t.trigger('filterEnd'); - }, - buildSelect = function(i, updating, onlyavail){ - var o, t, arry = [], currentVal; - i = parseInt(i, 10); - t = $ths.filter('[data-column="' + i + '"]:last'); - // t.data('placeholder') won't work in jQuery older than 1.4.3 - o = '<option value="">' + (t.data('placeholder') || t.attr('data-placeholder') || '') + '</option>'; - for (k = 0; k < b.length; k++ ){ - l = c.cache[k].row.length; - // loop through the rows - for (j = 0; j < l; j++){ - // check if has class filtered - if (onlyavail && c.cache[k].row[j][0].className.match(wo.filter_filteredRow)) { continue; } - // get non-normalized cell content - if (wo.filter_useParsedData){ - arry.push( '' + c.cache[k].normalized[j][i] ); - } else { - t = c.cache[k].row[j][0].cells[i]; - if (t){ - arry.push( $.trim(c.supportsTextContent ? t.textContent : $(t).text()) ); + return false; + } + return null; + } + }, + init: function(table, c, wo) { + var options, string, $header, column, filters, time; + if (c.debug) { + time = new Date(); + } + c.$table.addClass('hasFilters'); + + ts.filter.regex.child = new RegExp(c.cssChildRow); + ts.filter.regex.filtered = new RegExp(wo.filter_filteredRow); + + // don't build filter row if columnFilters is false or all columns are set to "filter-false" - issue #156 + if (wo.filter_columnFilters !== false && c.$headers.filter('.filter-false').length !== c.$headers.length) { + // build filter row + ts.filter.buildRow(table, c, wo); + } + + c.$table.bind('addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join('.tsfilter '), function(event, filter) { + if ( !/(search|filterReset|filterEnd)/.test(event.type) ) { + event.stopPropagation(); + ts.filter.buildDefault(table, true); + } + if (event.type === 'filterReset') { + ts.filter.searching(table, []); + } + if (event.type === 'filterEnd') { + ts.filter.buildDefault(table, true); + } else { + // send false argument to force a new search; otherwise if the filter hasn't changed, it will return + filter = event.type === 'search' ? filter : event.type === 'updateComplete' ? c.$table.data('lastSearch') : ''; + ts.filter.searching(table, filter); + } + return false; + }); + ts.filter.bindSearch( table, c.$table.find('input.tablesorter-filter') ); + + // reset button/link + if (wo.filter_reset) { + $(document).delegate(wo.filter_reset, 'click.tsfilter', function() { + ts.filter.searching(table, []); + }); + } + if (wo.filter_functions) { + // column = column # (string) + for (column in wo.filter_functions) { + if (wo.filter_functions.hasOwnProperty(column) && typeof column === 'string') { + $header = c.$headers.filter('[data-column="' + column + '"]:last'); + options = ''; + if (wo.filter_functions[column] === true && !$header.hasClass('filter-false')) { + ts.filter.buildSelect(column); + } else if (typeof column === 'string' && !$header.hasClass('filter-false')) { + // add custom drop down list + for (string in wo.filter_functions[column]) { + if (typeof string === 'string') { + options += options === '' ? + '<option value="">' + ($header.data('placeholder') || $header.attr('data-placeholder') || '') + '</option>' : ''; + options += '<option value="' + string + '">' + string + '</option>'; } } + c.$table.find('thead').find('select.tablesorter-filter[data-column="' + column + '"]').append(options); } } + } + } + // not really updating, but if the column has both the "filter-select" class & filter_functions set to true, + // it would append the same options twice. + ts.filter.buildDefault(table, true); - // get unique elements and sort the list - // if $.tablesorter.sortText exists (not in the original tablesorter), - // then natural sort the list otherwise use a basic sort - arry = $.grep(arry, function(v, k){ - return $.inArray(v, arry) === k; - }); - arry = (ts.sortNatural) ? arry.sort(function(a, b){ return ts.sortNatural(a, b); }) : arry.sort(true); + c.$table.find('select.tablesorter-filter').bind('change search', function(event, filter) { + ts.filter.checkFilters(table, filter); + }); - // Get curent filter value - currentVal = $t.find('thead').find('select.tablesorter-filter[data-column="' + i + '"]').val(); + if (wo.filter_hideFilters) { + ts.filter.hideFilters(table, c, wo); + } - // build option list - for (k = 0; k < arry.length; k++){ - t = arry[k].replace(/\"/g, """); - // replace quotes - fixes #242 & ignore empty strings - see http://stackoverflow.com/q/14990971/145346 - o += arry[k] !== '' ? '<option value="' + t + '"' + (currentVal === t ? ' selected="selected"' : '') +'>' + arry[k] + '</option>' : ''; - } - $t.find('thead').find('select.tablesorter-filter[data-column="' + i + '"]')[ updating ? 'html' : 'append' ](o); - }, - buildDefault = function(updating){ - // build default select dropdown - for (i = 0; i < cols; i++){ - t = $ths.filter('[data-column="' + i + '"]:last'); - // look for the filter-select class; build/update it if found - if ((t.hasClass('filter-select') || wo.filter_functions && wo.filter_functions[i] === true) && !t.hasClass('filter-false')){ - if (!wo.filter_functions) { wo.filter_functions = {}; } - wo.filter_functions[i] = true; // make sure this select gets processed by filter_functions - buildSelect(i, updating, t.hasClass(wo.filter_onlyAvail)); + // show processing icon + if (c.showProcessing) { + c.$table.bind('filterStart.tsfilter filterEnd.tsfilter', function(event, columns) { + // only add processing to certain columns to all columns + $header = (columns) ? c.$table.find('.' + ts.css.header).filter('[data-column]').filter(function() { + return columns[$(this).data('column')] !== ''; + }) : ''; + ts.isProcessing(table, event.type === 'filterStart', columns ? $header : ''); + }); + } + if (c.debug) { + ts.benchmark("Applying Filter widget", time); + } + // add default values + c.$table.bind('tablesorter-initialized pagerInitialized', function() { + filters = ts.filter.setDefaults(table, c, wo) || []; + if (filters.length) { + ts.setFilters(table, filters, true); + } + }); + // filter widget initialized + wo.filter_Initialized = true; + c.$table.trigger('filterInit'); + ts.filter.checkFilters(table); + }, + setDefaults: function(table, c, wo){ + var indx, + filters = [], + columns = c.columns; + for (indx = 0; indx < columns; indx++) { + filters[indx] = c.$headers.filter('[data-column="' + indx + '"]:last').attr(wo.filter_defaultAttrib) || filters[indx]; + } + $(table).data('lastSearch', filters); + return filters; + }, + buildRow: function(table, c, wo) { + var column, $header, buildSelect, disabled, + // c.columns defined in computeThIndexes() + columns = c.columns, + buildFilter = '<tr class="tablesorter-filter-row">'; + for (column = 0; column < columns; column++) { + buildFilter += '<td></td>'; + } + c.$filters = $(buildFilter += '</tr>').appendTo( c.$table.find('thead').eq(0) ).find('td'); + // build each filter input + for (column = 0; column < columns; column++) { + disabled = false; + // assuming last cell of a column is the main column + $header = c.$headers.filter('[data-column="' + column + '"]:last'); + buildSelect = (wo.filter_functions && wo.filter_functions[column] && typeof wo.filter_functions[column] !== 'function') || + $header.hasClass('filter-select'); + // get data from jQuery data, metadata, headers option or header class name + if (ts.getData) { + // get data from jQuery data, metadata, headers option or header class name + disabled = ts.getData($header[0], c.headers[column], 'filter') === 'false'; + } else { + // only class names and header options - keep this for compatibility with tablesorter v2.0.5 + disabled = (c.headers[column] && c.headers[column].hasOwnProperty('filter') && c.headers[column].filter === false) || + $header.hasClass('filter-false'); + } + if (buildSelect) { + buildFilter = $('<select>').appendTo( c.$filters.eq(column) ); + } else { + if (wo.filter_formatter && $.isFunction(wo.filter_formatter[column])) { + buildFilter = wo.filter_formatter[column]( c.$filters.eq(column), column ); + // no element returned, so lets go find it + if (buildFilter && buildFilter.length === 0) { + buildFilter = c.$filters.eq(column).children('input'); + } + // element not in DOM, so lets attach it + if ( buildFilter && (buildFilter.parent().length === 0 || + (buildFilter.parent().length && buildFilter.parent()[0] !== c.$filters[column])) ) { + c.$filters.eq(column).append(buildFilter); } - } - }, - searching = function(filter){ - if (typeof filter === 'undefined' || filter === true){ - // delay filtering - clearTimeout(timer); - timer = setTimeout(function(){ - checkFilters(filter); - }, wo.filter_liveSearch ? wo.filter_searchDelay : 10); } else { - // skip delay - checkFilters(filter); + buildFilter = $('<input type="search">').appendTo( c.$filters.eq(column) ); + } + if (buildFilter) { + buildFilter.attr('placeholder', $header.data('placeholder') || $header.attr('data-placeholder') || ''); } - }; - if (c.debug){ - time = new Date(); } - wo.filter_regex.child = new RegExp(c.cssChildRow); - wo.filter_regex.filtered = new RegExp(wo.filter_filteredRow); - // don't build filter row if columnFilters is false or all columns are set to "filter-false" - issue #156 - - if (wo.filter_columnFilters !== false && $ths.filter('.filter-false').length !== $ths.length){ - // build filter row - t = '<tr class="tablesorter-filter-row">'; - for (i = 0; i < cols; i++){ - t += '<td></td>'; + if (buildFilter) { + buildFilter.addClass('tablesorter-filter ' + wo.filter_cssFilter).attr('data-column', column); + if (disabled) { + buildFilter.addClass('disabled')[0].disabled = true; // disabled! } - c.$filters = $(t += '</tr>').appendTo( $t.find('thead').eq(0) ).find('td'); - // build each filter input - for (i = 0; i < cols; i++){ - dis = false; - $th = $ths.filter('[data-column="' + i + '"]:last'); // assuming last cell of a column is the main column - sel = (wo.filter_functions && wo.filter_functions[i] && typeof wo.filter_functions[i] !== 'function') || $th.hasClass('filter-select'); - // use header option - headers: { 1: { filter: false } } OR add class="filter-false" - if (ts.getData){ - // get data from jQuery data, metadata, headers option or header class name - dis = ts.getData($th[0], c.headers[i], 'filter') === 'false'; - } else { - // only class names and header options - keep this for compatibility with tablesorter v2.0.5 - dis = (c.headers[i] && c.headers[i].hasOwnProperty('filter') && c.headers[i].filter === false) || $th.hasClass('filter-false'); - } - - if (sel){ - t = $('<select>').appendTo( c.$filters.eq(i) ); + } + } + }, + bindSearch: function(table, $el) { + table = $(table)[0]; + var external, wo = table.config.widgetOptions; + $el.bind('keyup search', function(event, filter) { + // emulate what webkit does.... escape clears the filter + if (event.which === 27) { + this.value = ''; + // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace + } else if ( (typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch && this.value !== '') || + ( event.type === 'keyup' && ( (event.which < 32 && event.which !== 8 && wo.filter_liveSearch === true && event.which !== 13) || + ( event.which >= 37 && event.which <= 40 ) || (event.which !== 13 && wo.filter_liveSearch === false) ) ) ) { + return; + } + // external searches won't have a filter parameter, so grab the value + external = $(this).hasClass('tablesorter-filter') ? filter : [ $(this).val() ]; + ts.filter.searching(table, filter, external); + }); + }, + checkFilters: function(table, filter) { + var c = table.config, + wo = c.widgetOptions, + filterArray = $.isArray(filter), + filters = (filterArray) ? filter : ts.getFilters(table), + combinedFilters = (filters || []).join(''); // combined filter values + // add filter array back into inputs + if (filterArray) { + ts.setFilters( table, filters ); + } + if (wo.filter_hideFilters) { + // show/hide filter row as needed + c.$table.find('.tablesorter-filter-row').trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); + } + // return if the last search is the same; but filter === false when updating the search + // see example-widget-filter.html filter toggle buttons + if (c.lastCombinedFilter === combinedFilters && filter !== false) { return; } + c.$table.trigger('filterStart', [filters]); + if (c.showProcessing) { + // give it time for the processing icon to kick in + setTimeout(function() { + ts.filter.findRows(table, filters, combinedFilters); + return false; + }, 30); + } else { + ts.filter.findRows(table, filters, combinedFilters); + return false; + } + }, + hideFilters: function(table, c, wo) { + var $filterRow, $filterRow2, timer; + c.$table + .find('.tablesorter-filter-row') + .addClass('hideme') + .bind('mouseenter mouseleave', function(e) { + // save event object - http://bugs.jquery.com/ticket/12140 + var event = e; + $filterRow = $(this); + clearTimeout(timer); + timer = setTimeout(function() { + if ( /enter|over/.test(event.type) ) { + $filterRow.removeClass('hideme'); } else { - if (wo.filter_formatter && $.isFunction(wo.filter_formatter[i])) { - t = wo.filter_formatter[i]( c.$filters.eq(i), i ); - // no element returned, so lets go find it - if (t && t.length === 0) { t = c.$filters.eq(i).children('input'); } - // element not in DOM, so lets attach it - if (t && (t.parent().length === 0 || (t.parent().length && t.parent()[0] !== c.$filters[i]))) { - c.$filters.eq(i).append(t); + // don't hide if input has focus + // $(':focus') needs jQuery 1.6+ + if ( $(document.activeElement).closest('tr')[0] !== $filterRow[0] ) { + // don't hide row if any filter has a value + if (ts.getFilters(table).join('') === '') { + $filterRow.addClass('hideme'); } - } else { - t = $('<input type="search">').appendTo( c.$filters.eq(i) ); - } - if (t) { - t.attr('placeholder', $th.data('placeholder') || $th.attr('data-placeholder') || ''); - } - } - if (t) { - t.addClass('tablesorter-filter ' + wo.filter_cssFilter).attr('data-column', i); - if (dis) { - t.addClass('disabled')[0].disabled = true; // disabled! } } - } - } - $t - .bind('addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join('.tsfilter '), function(e, filter){ - if (!/(search|filterReset|filterEnd)/.test(e.type)){ - e.stopPropagation(); - buildDefault(true); - } - if (e.type === 'filterReset') { - searching([]); - } - if (e.type === 'filterEnd') { - buildDefault(true); - } else { - // send false argument to force a new search; otherwise if the filter hasn't changed, it will return - filter = e.type === 'search' ? filter : e.type === 'updateComplete' ? $t.data('lastSearch') : ''; - searching(filter); - } - return false; + }, 200); }) - .find('input.tablesorter-filter').bind('keyup search', function(e, filter){ - // emulate what webkit does.... escape clears the filter - if (e.which === 27) { - this.value = ''; - // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace - } else if ( (typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch && this.value !== '') || ( e.type === 'keyup' && - ( (e.which < 32 && e.which !== 8 && wo.filter_liveSearch === true && e.which !== 13) || (e.which >= 37 && e.which <=40) || (e.which !== 13 && wo.filter_liveSearch === false) ) ) ) { - return; - } - searching(filter); + .find('input, select').bind('focus blur', function(e) { + $filterRow2 = $(this).closest('tr'); + clearTimeout(timer); + var event = e; + timer = setTimeout(function() { + // don't hide row if any filter has a value + if (ts.getFilters(table).join('') === '') { + $filterRow2[ event.type === 'focus' ? 'removeClass' : 'addClass']('hideme'); + } + }, 200); }); - + }, + findRows: function(table, filters, combinedFilters) { + var cached, len, $rows, rowIndex, tbodyIndex, $tbody, $cells, columnIndex, + childRow, childRowText, exact, iExact, iFilter, lastSearch, matches, result, + searchFiltered, filterMatched, showRow, time, + c = table.config, + wo = c.widgetOptions, + columns = c.columns, + $tbodies = c.$tbodies, + // anyMatch really screws up with these types of filters + anyMatchNotAllowedTypes = [ 'range', 'operators' ], // parse columns after formatter, in case the class is added at that point - parsed = $ths.map(function(i){ - return (ts.getData) ? ts.getData($ths.filter('[data-column="' + i + '"]:last'), c.headers[i], 'filter') === 'parsed' : $(this).hasClass('filter-parsed'); + parsed = c.$headers.map(function(columnIndex) { + return (ts.getData) ? + ts.getData(c.$headers.filter('[data-column="' + columnIndex + '"]:last'), c.headers[columnIndex], 'filter') === 'parsed' : + $(this).hasClass('filter-parsed'); }).get(); - - // reset button/link - if (wo.filter_reset){ - $(document).delegate(wo.filter_reset, 'click.tsfilter', function(){ - $t.trigger('filterReset'); + if (c.debug) { time = new Date(); } + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + if ($tbodies.eq(tbodyIndex).hasClass(ts.css.info)) { continue; } // ignore info blocks, issue #264 + $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); + $rows = $tbody.children('tr').not('.' + c.cssChildRow); + len = $rows.length; + if (combinedFilters === '' || wo.filter_serversideFiltering) { + $tbody.children().show().removeClass(wo.filter_filteredRow); + } else { + // optimize searching only through already filtered rows - see #313 + searchFiltered = true; + lastSearch = c.lastSearch || c.$table.data('lastSearch') || []; + $.each(filters, function(indx, val) { + // check for changes from beginning of filter; but ignore if there is a logical "or" in the string + searchFiltered = (val || '').indexOf(lastSearch[indx] || '') === 0 && searchFiltered && !/(\s+or\s+|\|)/g.test(val || ''); }); - } - if (wo.filter_functions){ - // i = column # (string) - for (col in wo.filter_functions){ - if (wo.filter_functions.hasOwnProperty(col) && typeof col === 'string'){ - t = $ths.filter('[data-column="' + col + '"]:last'); - ff = ''; - if (wo.filter_functions[col] === true && !t.hasClass('filter-false')){ - buildSelect(col); - } else if (typeof col === 'string' && !t.hasClass('filter-false')){ - // add custom drop down list - for (str in wo.filter_functions[col]){ - if (typeof str === 'string'){ - ff += ff === '' ? '<option value="">' + (t.data('placeholder') || t.attr('data-placeholder') || '') + '</option>' : ''; - ff += '<option value="' + str + '">' + str + '</option>'; - } + // can't search when all rows are hidden - this happens when looking for exact matches + if (searchFiltered && $rows.filter(':visible').length === 0) { searchFiltered = false; } + // loop through the rows + for (rowIndex = 0; rowIndex < len; rowIndex++) { + childRow = $rows[rowIndex].className; + // skip child rows & already filtered rows + if ( ts.filter.regex.child.test(childRow) || (searchFiltered && ts.filter.regex.filtered.test(childRow)) ) { continue; } + showRow = true; + // *** nextAll/nextUntil not supported by Zepto! *** + childRow = $rows.eq(rowIndex).nextUntil('tr:not(.' + c.cssChildRow + ')'); + // so, if "table.config.widgetOptions.filter_childRows" is true and there is + // a match anywhere in the child row, then it will make the row visible + // checked here so the option can be changed dynamically + childRowText = (childRow.length && wo.filter_childRows) ? childRow.text() : ''; + childRowText = wo.filter_ignoreCase ? childRowText.toLocaleLowerCase() : childRowText; + $cells = $rows.eq(rowIndex).children('td'); + for (columnIndex = 0; columnIndex < columns; columnIndex++) { + // ignore if filter is empty or disabled + if (filters[columnIndex] || wo.filter_anyMatch) { + cached = c.cache[tbodyIndex].normalized[rowIndex][columnIndex]; + // check if column data should be from the cell or from parsed data + if (wo.filter_useParsedData || parsed[columnIndex]) { + exact = cached; + } else { + // using older or original tablesorter + exact = $.trim($cells.eq(columnIndex).text()); + exact = c.sortLocaleCompare ? ts.replaceAccents(exact) : exact; // issue #405 } - $t.find('thead').find('select.tablesorter-filter[data-column="' + col + '"]').append(ff); - } - } - } - } - // not really updating, but if the column has both the "filter-select" class & filter_functions set to true, - // it would append the same options twice. - buildDefault(true); + iExact = !ts.filter.regex.type.test(typeof exact) && wo.filter_ignoreCase ? exact.toLocaleLowerCase() : exact; + result = showRow; // if showRow is true, show that row - $t.find('select.tablesorter-filter').bind('change search', function(e, filter){ - checkFilters(filter); - }); + if (typeof filters[columnIndex] === "undefined" || filters[columnIndex] === null) { + filters[columnIndex] = wo.filter_anyMatch ? combinedFilters : filters[columnIndex]; + } - if (wo.filter_hideFilters){ - $t - .find('.tablesorter-filter-row') - .addClass('hideme') - .bind('mouseenter mouseleave', function(e){ - // save event object - http://bugs.jquery.com/ticket/12140 - var all, evt = e; - ft = $(this); - clearTimeout(st); - st = setTimeout(function(){ - if (/enter|over/.test(evt.type)){ - ft.removeClass('hideme'); + // replace accents - see #357 + filters[columnIndex] = c.sortLocaleCompare ? ts.replaceAccents(filters[columnIndex]) : filters[columnIndex]; + // val = case insensitive, filters[columnIndex] = case sensitive + iFilter = wo.filter_ignoreCase ? filters[columnIndex].toLocaleLowerCase() : filters[columnIndex]; + if (wo.filter_functions && wo.filter_functions[columnIndex]) { + if (wo.filter_functions[columnIndex] === true) { + // default selector; no "filter-select" class + result = (c.$headers.filter('[data-column="' + columnIndex + '"]:last').hasClass('filter-match')) ? + iExact.search(iFilter) >= 0 : filters[columnIndex] === exact; + } else if (typeof wo.filter_functions[columnIndex] === 'function') { + // filter callback( exact cell content, parser normalized content, filter input value, column index, jQuery row object ) + result = wo.filter_functions[columnIndex](exact, cached, filters[columnIndex], columnIndex, $rows.eq(rowIndex)); + } else if (typeof wo.filter_functions[columnIndex][filters[columnIndex]] === 'function') { + // selector option function + result = wo.filter_functions[columnIndex][filters[columnIndex]](exact, cached, filters[columnIndex], columnIndex, $rows.eq(rowIndex)); + } } else { - // don't hide if input has focus - // $(':focus') needs jQuery 1.6+ - if ($(document.activeElement).closest('tr')[0] !== ft[0]){ - // get all filter values - all = ts.getFilters(table).join(''); - // don't hide row if any filter has a value - if (all === ''){ - ft.addClass('hideme'); + filterMatched = null; + // cycle through the different filters + // filters return a boolean or null if nothing matches + $.each(ts.filter.types, function(type, typeFunction) { + if (!wo.filter_anyMatch || (wo.filter_anyMatch && anyMatchNotAllowedTypes.indexOf(type) < 0)) { + matches = typeFunction( filters[columnIndex], iFilter, exact, iExact, cached, columnIndex, table, wo, parsed ); + if (matches !== null) { + filterMatched = matches; + return false; + } } + }); + if (filterMatched !== null) { + result = filterMatched; + // Look for match, and add child row data for matching + } else { + exact = (iExact + childRowText).indexOf(iFilter); + result = ( (!wo.filter_startsWith && exact >= 0) || (wo.filter_startsWith && exact === 0) ); } } - }, 200); - }) - .find('input, select').bind('focus blur', function(e){ - ft2 = $(this).closest('tr'); - clearTimeout(st); - st = setTimeout(function(){ - // don't hide row if any filter has a value - if (ts.getFilters(table).join('') === ''){ - ft2[ e.type === 'focus' ? 'removeClass' : 'addClass']('hideme'); + if (wo.filter_anyMatch) { + showRow = result; + if (showRow){ + break; + } + } else { + showRow = (result) ? showRow : false; } - }, 200); - }); + } + } + $rows[rowIndex].style.display = (showRow ? '' : 'none'); + $rows.eq(rowIndex)[showRow ? 'removeClass' : 'addClass'](wo.filter_filteredRow); + if (childRow.length) { + if (c.pager && c.pager.countChildRows || wo.pager_countChildRows) { + childRow[showRow ? 'removeClass' : 'addClass'](wo.filter_filteredRow); // see issue #396 + } + childRow.toggle(showRow); + } + } } - - // show processing icon - if (c.showProcessing) { - $t.bind('filterStart.tsfilter filterEnd.tsfilter', function(e, v) { - var fc = (v) ? $t.find('.' + ts.css.header).filter('[data-column]').filter(function(){ - return v[$(this).data('column')] !== ''; - }) : ''; - ts.isProcessing($t[0], e.type === 'filterStart', v ? fc : ''); - }); + ts.processTbody(table, $tbody, false); + } + c.lastCombinedFilter = combinedFilters; // save last search + c.lastSearch = filters; + c.$table.data('lastSearch', filters); + if (c.debug) { + ts.benchmark("Completed filter widget search", time); + } + c.$table.trigger('applyWidgets'); // make sure zebra widget is applied + c.$table.trigger('filterEnd'); + }, + buildSelect: function(table, column, updating, onlyavail) { + column = parseInt(column, 10); + var indx, rowIndex, tbodyIndex, len, currentValue, txt, + c = table.config, + wo = c.widgetOptions, + $tbodies = c.$tbodies, + arry = [], + node = c.$headers.filter('[data-column="' + column + '"]:last'), + // t.data('placeholder') won't work in jQuery older than 1.4.3 + options = '<option value="">' + ( node.data('placeholder') || node.attr('data-placeholder') || '' ) + '</option>'; + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + len = c.cache[tbodyIndex].row.length; + // loop through the rows + for (rowIndex = 0; rowIndex < len; rowIndex++) { + // check if has class filtered + if (onlyavail && c.cache[tbodyIndex].row[rowIndex][0].className.match(wo.filter_filteredRow)) { continue; } + // get non-normalized cell content + if (wo.filter_useParsedData) { + arry.push( '' + c.cache[tbodyIndex].normalized[rowIndex][column] ); + } else { + node = c.cache[tbodyIndex].row[rowIndex][0].cells[column]; + if (node) { + arry.push( $.trim( node.textContent || node.innerText || $(node).text() ) ); + } + } } + } + // get unique elements and sort the list + // if $.tablesorter.sortText exists (not in the original tablesorter), + // then natural sort the list otherwise use a basic sort + arry = $.grep(arry, function(value, indx) { + return $.inArray(value, arry) === indx; + }); + arry = (ts.sortNatural) ? arry.sort(function(a, b) { return ts.sortNatural(a, b); }) : arry.sort(true); - if (c.debug){ - ts.benchmark("Applying Filter widget", time); + // Get curent filter value + currentValue = c.$table.find('thead').find('select.tablesorter-filter[data-column="' + column + '"]').val(); + + // build option list + for (indx = 0; indx < arry.length; indx++) { + txt = arry[indx].replace(/\"/g, """); + // replace quotes - fixes #242 & ignore empty strings - see http://stackoverflow.com/q/14990971/145346 + options += arry[indx] !== '' ? '<option value="' + txt + '"' + (currentValue === txt ? ' selected="selected"' : '') + + '>' + arry[indx] + '</option>' : ''; + } + c.$table.find('thead').find('select.tablesorter-filter[data-column="' + column + '"]')[ updating ? 'html' : 'append' ](options); + }, + buildDefault: function(table, updating) { + var columnIndex, $header, + c = table.config, + wo = c.widgetOptions, + columns = c.columns; + // build default select dropdown + for (columnIndex = 0; columnIndex < columns; columnIndex++) { + $header = c.$headers.filter('[data-column="' + columnIndex + '"]:last'); + // look for the filter-select class; build/update it if found + if (($header.hasClass('filter-select') || wo.filter_functions && wo.filter_functions[columnIndex] === true) && + !$header.hasClass('filter-false')) { + if (!wo.filter_functions) { wo.filter_functions = {}; } + wo.filter_functions[columnIndex] = true; // make sure this select gets processed by filter_functions + ts.filter.buildSelect(table, columnIndex, updating, $header.hasClass(wo.filter_onlyAvail)); } - // add default values - $t.bind('tablesorter-initialized', function(){ - ff = ts.getFilters(table); - // ff is undefined when filter_columnFilters = false - if (ff) { - for (i = 0; i < ff.length; i++) { - ff[i] = $ths.filter('[data-column="' + i + '"]:last').attr(wo.filter_defaultAttrib) || ff[i]; - } - ts.setFilters(table, ff, true); - } - }); - // filter widget initialized - $t.trigger('filterInit'); - checkFilters(); } }, - remove: function(table, c, wo){ - var k, $tb, - $t = c.$table, - b = c.$tbodies; - $t - .removeClass('hasFilters') - // add .tsfilter namespace to all BUT search - .unbind('addRows updateCell update updateComplete appendCache search filterStart filterEnd '.split(' ').join('.tsfilter ')) - .find('.tablesorter-filter-row').remove(); - for (k = 0; k < b.length; k++ ){ - $tb = ts.processTbody(table, b.eq(k), true); // remove tbody - $tb.children().removeClass(wo.filter_filteredRow).show(); - ts.processTbody(table, $tb, false); // restore tbody + searching: function(table, filter, external) { + if (typeof filter === 'undefined' || filter === true || external) { + var wo = table.config.widgetOptions; + // delay filtering + clearTimeout(wo.searchTimer); + wo.searchTimer = setTimeout(function() { + ts.filter.checkFilters(table, external || filter); + }, wo.filter_liveSearch ? wo.filter_searchDelay : 10); + } else { + // skip delay + ts.filter.checkFilters(table, filter); } - if (wo.filter_reset) { $(document).undelegate(wo.filter_reset, 'click.tsfilter'); } } -}); +}; + ts.getFilters = function(table) { var c = table ? $(table)[0].config : {}; - if (c && c.widgetOptions && !c.widgetOptions.filter_columnFilters) { return $(table).data('lastSearch'); } - return c && c.$filters ? c.$filters.map(function(i, el) { + if (c && c.widgetOptions && !c.widgetOptions.filter_columnFilters) { + // no filter row + return $(table).data('lastSearch'); + } + return c && c.$filters ? c.$filters.map(function(indx, el) { return $(el).find('.tablesorter-filter').val() || ''; }).get() || [] : false; }; + ts.setFilters = function(table, filter, apply) { - var $t = $(table), - c = $t.length ? $t[0].config : {}, - valid = c && c.$filters ? c.$filters.each(function(i, el) { - $(el).find('.tablesorter-filter').val(filter[i] || ''); - }) || false : false; - if (apply) { $t.trigger('search', [filter, false]); } + var $table = $(table), + c = $table.length ? $table[0].config : {}, + valid = c && c.$filters ? c.$filters.each(function(indx, el) { + $(el).find('.tablesorter-filter').val(filter[indx] || ''); + }).trigger('change.tsfilter') || false : false; + if (apply) { $table.trigger('search', [filter, false]); } return !!valid; }; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index 4ac9518..2ce7c9f 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -55,12 +55,14 @@ /* processing icon */ .tablesorter-bootstrap .tablesorter-processing { background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs='); + background-position: center center !important; + background-repeat: no-repeat !important; position: absolute; z-index: 1000; } /* caption */ -caption { +.caption { background: #fff; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css index a763fa2..f1cf1ae 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css @@ -114,7 +114,7 @@ /* caption */ caption { - background: #fff; + background: #202020; } /* filter widget */ diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css index da00e00..2108f71 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css @@ -79,8 +79,8 @@ } /* caption */ -caption { - background: #fff; +.tablesorter-jui caption { + border: 0; } /* filter widget */ From b4de9eb8614906f2f3b8603bbf07a58119a2836b Mon Sep 17 00:00:00 2001 From: Erik Ernst <github@black-milk.de> Date: Wed, 20 Nov 2013 18:40:39 +0100 Subject: [PATCH 007/138] * updated tablesorter to latest version (2.14.0) --- CHANGELOG.markdown | 4 + README.markdown | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery-tablesorter/jquery.tablesorter.js | 82 +- .../jquery.tablesorter.widgets.js | 721 +++++++++--------- 6 files changed, 441 insertions(+), 372 deletions(-) diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index 67e371a..0a6ee80 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -1,5 +1,9 @@ Changelog === +#### v1.9.0 (2013-11-20) + +* Upgrade tablesorter to v2.14.0 + #### v1.8.1 (2013-11-10) * Upgrade tablesorter to v2.13.3 diff --git a/README.markdown b/README.markdown index fe89111..f207edb 100644 --- a/README.markdown +++ b/README.markdown @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.13.3 (11/9/2013), [documentation] +Current tablesorter version: 2.14.0 (11/19/2013), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 3f043c7..68d9694 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = "1.8.1" + VERSION = "1.9.0" end diff --git a/tablesorter b/tablesorter index ce7e083..f68b389 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit ce7e0836c0d2c739aa157250acabef5e286d9a22 +Subproject commit f68b3898c01bc668bb9c577ea64ec0f3c14aedbe diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index ea915fe..8147aef 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.13.3 - Client-side table sorting with ease! +* TableSorter 2.14.0 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.13.3"; + ts.version = "2.14.0"; ts.parsers = []; ts.widgets = []; @@ -41,6 +41,7 @@ // *** functionality cancelSelection : true, // prevent text selection in the header + tabIndex : true, // add tabindex to header for keyboard accessibility dateFormat : 'mmddyyyy', // other options: "ddmmyyy" or "yyyymmdd" sortMultiSortKey : 'shiftKey', // key used to select additional columns sortResetKey : 'ctrlKey', // key used to remove sorting on a column @@ -54,6 +55,7 @@ sortForce : null, // column(s) first sorted; always applied sortList : [], // Initial sort order; applied initially; updated when manually sorted sortAppend : null, // column(s) sorted last; always applied + sortStable : false, // when sorting two rows with exactly the same content, the original sort order is maintained sortInitialOrder : 'asc', // sort direction on first click sortLocaleCompare: false, // replace equivalent character (accented characters) @@ -413,7 +415,8 @@ if (c.debug) { time = new Date(); } - i = c.cssIcon ? '<i class="' + c.cssIcon + ' ' + ts.css.icon + '"></i>' : ''; // add icon if cssIcon option exists + // add icon if cssIcon option exists + i = c.cssIcon ? '<i class="' + ( c.cssIcon === ts.css.icon ? ts.css.icon : c.cssIcon + ' ' + ts.css.icon ) + '"></i>' : ''; c.$headers = $(table).find(c.selectorHeaders).each(function(index) { $t = $(this); ch = c.headers[index]; @@ -442,7 +445,7 @@ // add to parent in case there are multiple rows $t.parent().addClass(ts.css.headerRow + ' ' + c.cssHeaderRow); // allow keyboard cursor to focus on element - $t.attr("tabindex", 0); + if (c.tabIndex) { $t.attr("tabindex", 0); } }); // enable/disable sorting updateHeader(table); @@ -638,7 +641,7 @@ // sort multiple columns function multisort(table) { /*jshint loopfunc:true */ - var i, k, e, num, col, colMax, cache, lc, + var i, k, num, col, colMax, cache, lc, order, orgOrderCol, sortTime, sort, x, y, dir = 0, c = table.config, @@ -663,14 +666,9 @@ // sort direction, true = asc, false = desc dir = order === 0; - // set a & b depending on sort direction - x = dir ? a : b; - y = dir ? b : a; - - // determine how to sort empty cells - e = c.string[ (c.empties[col] || c.emptyTo ) ]; - if (x[col] === '' && e !== 0) { return ((typeof(e) === 'boolean') ? (e ? -1 : 1) : (e || 1)) * (dir ? 1 : -1); } - if (y[col] === '' && e !== 0) { return ((typeof(e) === 'boolean') ? (e ? 1 : -1) : (-e || -1)) * (dir ? 1 : -1); } + if (c.sortStable && a[col] === b[col] && l === 1) { + return a[orgOrderCol] - b[orgOrderCol]; + } // fallback to natural sort since it is more robust num = /n/i.test(getCachedSortType(c.parsers, col)); @@ -683,8 +681,12 @@ } // fall back to built-in numeric sort // var sort = $.tablesorter["sort" + s](table, a[c], b[c], c, colMax[c], dir); - sort = c.numberSorter ? c.numberSorter(x[col], y[col], dir, colMax[col], table) : ts.sortNumeric(x[col], y[col], num, colMax[col]); + sort = c.numberSorter ? c.numberSorter(x[col], y[col], dir, colMax[col], table) : + ts[ 'sortNumeric' + (dir ? 'Asc' : 'Desc') ](a[col], b[col], num, colMax[col], col, table); } else { + // set a & b depending on sort direction + x = dir ? a : b; + y = dir ? b : a; // text sort function if (typeof(cts) === 'function') { // custom OVERALL text sorter @@ -694,7 +696,7 @@ sort = cts[col](x[col], y[col], dir, col, table); } else { // fall back to natural sort - sort = ts.sortNatural(x[col], y[col]); + sort = ts[ 'sortNatural' + (dir ? 'Asc' : 'Desc') ](a[col], b[col], col, table, c); } } if (sort) { return sort; } @@ -1080,6 +1082,7 @@ if ( xD < yD ) { return -1; } if ( xD > yD ) { return 1; } } + // chunk/tokenize xN = a.replace(r.chunk, '\\0$1\\0').replace(/\\0$/, '').replace(/^\\0/, '').split('\\0'); yN = b.replace(r.chunk, '\\0$1\\0').replace(/\\0$/, '').replace(/^\\0/, '').split('\\0'); @@ -1102,6 +1105,22 @@ return 0; }; + ts.sortNaturalAsc = function(a, b, col, table, c) { + if (a === b) { return 0; } + var e = c.string[ (c.empties[col] || c.emptyTo ) ]; + if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : -e || -1; } + if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : e || 1; } + return ts.sortNatural(a, b); + }; + + ts.sortNaturalDesc = function(a, b, col, table, c) { + if (a === b) { return 0; } + var e = c.string[ (c.empties[col] || c.emptyTo ) ]; + if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : e || 1; } + if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : -e || -1; } + return ts.sortNatural(b, a); + }; + // basic alphabetical sort ts.sortText = function(a, b) { return a > b ? 1 : (a < b ? -1 : 0); @@ -1110,22 +1129,41 @@ // return text string value by adding up ascii value // so the text is somewhat sorted when using a digital sort // this is NOT an alphanumeric sort - ts.getTextValue = function(a, d, mx) { + ts.getTextValue = function(a, num, mx) { if (mx) { // make sure the text value is greater than the max numerical value (mx) - var i, l = a ? a.length : 0, n = mx + d; + var i, l = a ? a.length : 0, n = mx + num; for (i = 0; i < l; i++) { n += a.charCodeAt(i); } - return d * n; + return num * n; } return 0; }; - ts.sortNumeric = function(a, b, dir, mx) { + ts.sortNumericAsc = function(a, b, num, mx, col, table) { if (a === b) { return 0; } - if (isNaN(a)) { a = ts.getTextValue(a, dir, mx); } - if (isNaN(b)) { b = ts.getTextValue(b, dir, mx); } + var c = table.config, + e = c.string[ (c.empties[col] || c.emptyTo ) ]; + if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : -e || -1; } + if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : e || 1; } + if (isNaN(a)) { a = ts.getTextValue(a, num, mx); } + if (isNaN(b)) { b = ts.getTextValue(b, num, mx); } + return a - b; + }; + + ts.sortNumericDesc = function(a, b, num, mx, col, table) { + if (a === b) { return 0; } + var c = table.config, + e = c.string[ (c.empties[col] || c.emptyTo ) ]; + if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : e || 1; } + if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : -e || -1; } + if (isNaN(a)) { a = ts.getTextValue(a, num, mx); } + if (isNaN(b)) { b = ts.getTextValue(b, num, mx); } + return b - a; + }; + + ts.sortNumeric = function(a, b) { return a - b; }; @@ -1515,7 +1553,7 @@ l = $tb.children('tr').length; if (l > 1) { row = 0; - $tv = $tb.children('tr:visible'); + $tv = $tb.children('tr:visible').not(c.selectorRemove); // revered back to using jQuery each - strangely it's the fastest method /*jshint loopfunc:true */ $tv.each(function(){ diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index fe2d31d..7582191 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.8+ widgets - updated 11/9/2013 +/*! tableSorter 2.8+ widgets - updated 11/19/2013 * * Column Styles * Column Filters @@ -10,7 +10,7 @@ */ /*jshint browser:true, jquery:true, unused:false, loopfunc:true */ /*global jQuery: false, localStorage: false, navigator: false */ -;(function($){ +;(function($) { "use strict"; var ts = $.tablesorter = $.tablesorter || {}; @@ -68,86 +68,95 @@ ts.themes = { val = (v && v.hasOwnProperty('mywidget')) ? v.mywidget : ''; alert(val); // "data1" if saved, or "" if not */ -ts.storage = function(table, key, val, options){ - var d, k, ls = false, v = {}, - c = table.config, - id = options && options.id || $(table).attr(options && options.group || 'data-table-group') || table.id || $('.tablesorter').index( $(table) ), - url = options && options.url || $(table).attr(options && options.page || 'data-table-page') || c && c.fixedUrl || window.location.pathname; +ts.storage = function(table, key, value, options) { + table = $(table)[0]; + var cookieIndex, cookies, date, + hasLocalStorage = false, + values = {}, + c = table.config, + $table = $(table), + id = options && options.id || $table.attr(options && options.group || + 'data-table-group') || table.id || $('.tablesorter').index( $table ), + url = options && options.url || $table.attr(options && options.page || + 'data-table-page') || c && c.fixedUrl || window.location.pathname; // https://gist.github.com/paulirish/5558557 if ("localStorage" in window) { try { window.localStorage.setItem('_tmptest', 'temp'); - ls = true; + hasLocalStorage = true; window.localStorage.removeItem('_tmptest'); - } catch(e) {} + } catch(error) {} } - // *** get val *** - if ($.parseJSON){ - if (ls){ - v = $.parseJSON(localStorage[key] || '{}'); + // *** get value *** + if ($.parseJSON) { + if (hasLocalStorage) { + values = $.parseJSON(localStorage[key] || '{}'); } else { - k = document.cookie.split(/[;\s|=]/); // cookie - d = $.inArray(key, k) + 1; // add one to get from the key to the value - v = (d !== 0) ? $.parseJSON(k[d] || '{}') : {}; + // old browser, using cookies + cookies = document.cookie.split(/[;\s|=]/); + // add one to get from the key to the value + cookieIndex = $.inArray(key, cookies) + 1; + values = (cookieIndex !== 0) ? $.parseJSON(cookies[cookieIndex] || '{}') : {}; } } - // allow val to be an empty string to - if ((val || val === '') && window.JSON && JSON.hasOwnProperty('stringify')){ + // allow value to be an empty string too + if ((value || value === '') && window.JSON && JSON.hasOwnProperty('stringify')) { // add unique identifiers = url pathname > table ID/index on page > data - if (!v[url]) { - v[url] = {}; + if (!values[url]) { + values[url] = {}; } - v[url][id] = val; - // *** set val *** - if (ls){ - localStorage[key] = JSON.stringify(v); + values[url][id] = value; + // *** set value *** + if (hasLocalStorage) { + localStorage[key] = JSON.stringify(values); } else { - d = new Date(); - d.setTime(d.getTime() + (31536e+6)); // 365 days - document.cookie = key + '=' + (JSON.stringify(v)).replace(/\"/g,'\"') + '; expires=' + d.toGMTString() + '; path=/'; + date = new Date(); + date.setTime(date.getTime() + (31536e+6)); // 365 days + document.cookie = key + '=' + (JSON.stringify(values)).replace(/\"/g,'\"') + '; expires=' + date.toGMTString() + '; path=/'; } } else { - return v && v[url] ? v[url][id] : {}; + return values && values[url] ? values[url][id] : {}; } }; // Add a resize event to table headers // ************************** -ts.addHeaderResizeEvent = function(table, disable, options){ - var defaults = { - timer : 250 - }, - o = $.extend({}, defaults, options), - c = table.config, - wo = c.widgetOptions, - headers, - checkSizes = function(){ - wo.resize_flag = true; - headers = []; - c.$headers.each(function(){ - var d = $.data(this, 'savedSizes') || [0,0], // fixes #394 - w = this.offsetWidth, - h = this.offsetHeight; - if (w !== d[0] || h !== d[1]) { - $.data(this, 'savedSizes', [ w, h ]); - headers.push(this); +ts.addHeaderResizeEvent = function(table, disable, settings) { + var headers, + defaults = { + timer : 250 + }, + options = $.extend({}, defaults, settings), + c = table.config, + wo = c.widgetOptions, + checkSizes = function(triggerEvent) { + wo.resize_flag = true; + headers = []; + c.$headers.each(function() { + var $header = $(this), + sizes = $header.data('savedSizes') || [0,0], // fixes #394 + width = this.offsetWidth, + height = this.offsetHeight; + if (width !== sizes[0] || height !== sizes[1]) { + $header.data('savedSizes', [ width, height ]); + headers.push(this); + } + }); + if (headers.length && triggerEvent !== false) { + c.$table.trigger('resize', [ headers ]); } - }); - if (headers.length) { c.$table.trigger('resize', [ headers ]); } - wo.resize_flag = false; - }; - c.$headers.each(function(){ - $.data(this, 'savedSizes', [ this.offsetWidth, this.offsetHeight ]); - }); + wo.resize_flag = false; + }; + checkSizes(false); clearInterval(wo.resize_timer); if (disable) { wo.resize_flag = false; return false; } - wo.resize_timer = setInterval(function(){ + wo.resize_timer = setInterval(function() { if (wo.resize_flag) { return; } checkSizes(); - }, o.timer); + }, options.timer); }; // Widget: General UI theme @@ -156,89 +165,88 @@ ts.addHeaderResizeEvent = function(table, disable, options){ ts.addWidget({ id: "uitheme", priority: 10, - options: { - uitheme : 'jui' - }, - format: function(table, c, wo){ - var time, klass, $el, $tar, - t = ts.themes, - $t = c.$table, - theme = c.theme !== 'default' ? c.theme : wo.uitheme || 'jui', - o = t[ t[theme] ? theme : t[wo.uitheme] ? wo.uitheme : 'jui'], - $h = c.$headers, - sh = 'tr.' + (wo.stickyHeaders || 'tablesorter-stickyHeader'), - rmv = o.sortNone + ' ' + o.sortDesc + ' ' + o.sortAsc; + format: function(table, c, wo) { + var time, classes, $header, $icon, $tfoot, + themesAll = ts.themes, + $table = c.$table, + $headers = c.$headers, + theme = c.theme || 'jui', + themes = themesAll[theme] || themesAll.jui, + remove = themes.sortNone + ' ' + themes.sortDesc + ' ' + themes.sortAsc; if (c.debug) { time = new Date(); } // initialization code - run once - if (!$t.hasClass('tablesorter-' + theme) || c.theme === theme || !table.hasInitialized){ + if (!$table.hasClass('tablesorter-' + theme) || c.theme === theme || !table.hasInitialized) { // update zebra stripes - if (o.even !== '') { wo.zebra[0] += ' ' + o.even; } - if (o.odd !== '') { wo.zebra[1] += ' ' + o.odd; } + if (themes.even !== '') { wo.zebra[0] += ' ' + themes.even; } + if (themes.odd !== '') { wo.zebra[1] += ' ' + themes.odd; } // add caption style - $t.find('caption').addClass(o.caption); + $table.find('caption').addClass(themes.caption); // add table/footer class names - t = $t - // remove other selected themes; use widgetOptions.theme_remove + $tfoot = $table + // remove other selected themes .removeClass( c.theme === '' ? '' : 'tablesorter-' + c.theme ) - .addClass('tablesorter-' + theme + ' ' + o.table) // add theme widget class name + .addClass('tablesorter-' + theme + ' ' + themes.table) // add theme widget class name .find('tfoot'); - if (t.length) { - t - .find('tr').addClass(o.footerRow) - .children('th, td').addClass(o.footerCells); + if ($tfoot.length) { + $tfoot + .find('tr').addClass(themes.footerRow) + .children('th, td').addClass(themes.footerCells); } // update header classes - $h - .addClass(o.header) - .filter(':not(.sorter-false)') - .bind('mouseenter.tsuitheme mouseleave.tsuitheme', function(e){ + $headers + .addClass(themes.header) + .not('.sorter-false') + .bind('mouseenter.tsuitheme mouseleave.tsuitheme', function(event) { // toggleClass with switch added in jQuery 1.3 - $(this)[ e.type === 'mouseenter' ? 'addClass' : 'removeClass' ](o.hover); + $(this)[ event.type === 'mouseenter' ? 'addClass' : 'removeClass' ](themes.hover); }); - if (!$h.find('.tablesorter-wrapper').length) { - // Firefox needs this inner div to position the icon/resizer correctly - $h.wrapInner('<div class="tablesorter-wrapper" style="position:relative;height:100%;width:100%"></div>'); + if (!$headers.find('.tablesorter-wrapper').length) { + // Firefox needs this inner div to position the resizer correctly + $headers.wrapInner('<div class="tablesorter-wrapper" style="position:relative;height:100%;width:100%"></div>'); } - if (c.cssIcon){ + if (c.cssIcon) { // if c.cssIcon is '', then no <i> is added to the header - $h.find('.' + ts.css.icon).addClass(o.icons); + $headers.find('.' + ts.css.icon).addClass(themes.icons); } - if ($t.hasClass('hasFilters')){ - $h.find('.tablesorter-filter-row').addClass(o.filterRow); + if ($table.hasClass('hasFilters')) { + $headers.find('.tablesorter-filter-row').addClass(themes.filterRow); } } - $.each($h, function(i){ - $el = $(this); - $tar = (ts.css.icon) ? $el.find('.' + ts.css.icon) : $el; - if (this.sortDisabled){ + $.each($headers, function() { + $header = $(this); + $icon = (ts.css.icon) ? $header.find('.' + ts.css.icon) : $header; + if (this.sortDisabled) { // no sort arrows for disabled columns! - $el.removeClass(rmv); - $tar.removeClass(rmv + ' tablesorter-icon ' + o.icons); + $header.removeClass(remove); + $icon.removeClass(remove + ' tablesorter-icon ' + themes.icons); } else { - t = ($t.hasClass('hasStickyHeaders')) ? $t.find(sh).find('th').eq(i).add($el) : $el; - klass = ($el.hasClass(ts.css.sortAsc)) ? o.sortAsc : ($el.hasClass(ts.css.sortDesc)) ? o.sortDesc : $el.hasClass(ts.css.header) ? o.sortNone : ''; - $el[klass === o.sortNone ? 'removeClass' : 'addClass'](o.active); - $tar.removeClass(rmv).addClass(klass); + classes = ($header.hasClass(ts.css.sortAsc)) ? + themes.sortAsc : + ($header.hasClass(ts.css.sortDesc)) ? themes.sortDesc : + $header.hasClass(ts.css.header) ? themes.sortNone : ''; + $header[classes === themes.sortNone ? 'removeClass' : 'addClass'](themes.active); + $icon.removeClass(remove).addClass(classes); } }); - if (c.debug){ + if (c.debug) { ts.benchmark("Applying " + theme + " theme", time); } }, - remove: function(table, c, wo){ - var $t = c.$table, - theme = typeof wo.uitheme === 'object' ? 'jui' : wo.uitheme || 'jui', - o = typeof wo.uitheme === 'object' ? wo.uitheme : ts.themes[ ts.themes.hasOwnProperty(theme) ? theme : 'jui'], - $h = $t.children('thead').children(), - rmv = o.sortNone + ' ' + o.sortDesc + ' ' + o.sortAsc; - $t - .removeClass('tablesorter-' + theme + ' ' + o.table) - .find(ts.css.header).removeClass(o.header); - $h + remove: function(table, c, wo) { + var $table = c.$table, + theme = c.theme || 'jui', + themes = ts.themes[ theme ] || ts.themes.jui, + $headers = $table.children('thead').children(), + remove = themes.sortNone + ' ' + themes.sortDesc + ' ' + themes.sortAsc; + $table + .removeClass('tablesorter-' + theme + ' ' + themes.table) + .find(ts.css.header).removeClass(themes.header); + $headers .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') // remove hover - .removeClass(o.hover + ' ' + rmv + ' ' + o.active) - .find('.tablesorter-filter-row').removeClass(o.filterRow); - $h.find('.tablesorter-icon').removeClass(o.icons); + .removeClass(themes.hover + ' ' + remove + ' ' + themes.active) + .find('.tablesorter-filter-row') + .removeClass(themes.filterRow); + $headers.find('.tablesorter-icon').removeClass(themes.icons); } }); @@ -252,76 +260,74 @@ ts.addWidget({ options : { columns : [ "primary", "secondary", "tertiary" ] }, - format: function(table, c, wo){ - var $tb, $tr, $td, $t, time, last, rmv, i, k, l, - $tbl = c.$table, - b = c.$tbodies, - list = c.sortList, - len = list.length, - // keep backwards compatibility, for now - css = (c.widgetColumns && c.widgetColumns.hasOwnProperty('css')) ? c.widgetColumns.css || css : - (wo && wo.hasOwnProperty('columns')) ? wo.columns || css : css; - last = css.length-1; - rmv = css.join(' '); - if (c.debug){ + format: function(table, c, wo) { + var time, $tbody, tbodyIndex, $rows, rows, $row, $cells, remove, indx, + $table = c.$table, + $tbodies = c.$tbodies, + sortList = c.sortList, + len = sortList.length, + // removed c.widgetColumns support + css = wo && wo.columns || [ "primary", "secondary", "tertiary" ], + last = css.length - 1; + remove = css.join(' '); + if (c.debug) { time = new Date(); } // check if there is a sort (on initialization there may not be one) - for (k = 0; k < b.length; k++ ){ - $tb = ts.processTbody(table, b.eq(k), true); // detach tbody - $tr = $tb.children('tr'); - l = $tr.length; + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // detach tbody + $rows = $tbody.children('tr'); // loop through the visible rows - $tr.each(function(){ - $t = $(this); - if (this.style.display !== 'none'){ + $rows.each(function() { + $row = $(this); + if (this.style.display !== 'none') { // remove all columns class names - $td = $t.children().removeClass(rmv); + $cells = $row.children().removeClass(remove); // add appropriate column class names - if (list && list[0]){ + if (sortList && sortList[0]) { // primary sort column class - $td.eq(list[0][0]).addClass(css[0]); - if (len > 1){ - for (i = 1; i < len; i++){ + $cells.eq(sortList[0][0]).addClass(css[0]); + if (len > 1) { + for (indx = 1; indx < len; indx++) { // secondary, tertiary, etc sort column classes - $td.eq(list[i][0]).addClass( css[i] || css[last] ); + $cells.eq(sortList[indx][0]).addClass( css[indx] || css[last] ); } } } } }); - ts.processTbody(table, $tb, false); + ts.processTbody(table, $tbody, false); } // add classes to thead and tfoot - $tr = wo.columns_thead !== false ? ['thead tr'] : []; + rows = wo.columns_thead !== false ? ['thead tr'] : []; if (wo.columns_tfoot !== false) { - $tr.push('tfoot tr'); + rows.push('tfoot tr'); } - if ($tr.length) { - $t = $tbl.find($tr.join(',')).children().removeClass(rmv); - if (len){ - for (i = 0; i < len; i++){ + if (rows.length) { + $rows = $table.find( rows.join(',') ).children().removeClass(remove); + if (len) { + for (indx = 0; indx < len; indx++) { // add primary. secondary, tertiary, etc sort column classes - $t.filter('[data-column="' + list[i][0] + '"]').addClass(css[i] || css[last]); + $rows.filter('[data-column="' + sortList[indx][0] + '"]').addClass(css[indx] || css[last]); } } } - if (c.debug){ + if (c.debug) { ts.benchmark("Applying Columns widget", time); } }, - remove: function(table, c, wo){ - var k, $tb, - b = c.$tbodies, - rmv = (wo.columns || [ "primary", "secondary", "tertiary" ]).join(' '); - c.$headers.removeClass(rmv); - c.$table.children('tfoot').children('tr').children('th, td').removeClass(rmv); - for (k = 0; k < b.length; k++ ){ - $tb = ts.processTbody(table, b.eq(k), true); // remove tbody - $tb.children('tr').each(function(){ - $(this).children().removeClass(rmv); + remove: function(table, c, wo) { + var tbodyIndex, $tbody, + $tbodies = c.$tbodies, + remove = (wo.columns || [ "primary", "secondary", "tertiary" ]).join(' '); + c.$headers.removeClass(remove); + c.$table.children('tfoot').children('tr').children('th, td').removeClass(remove); + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody + $tbody.children('tr').each(function() { + $(this).children().removeClass(remove); }); - ts.processTbody(table, $tb, false); // restore tbody + ts.processTbody(table, $tbody, false); // restore tbody } } }); @@ -344,6 +350,7 @@ ts.addWidget({ filter_liveSearch : true, // if true, search column content while the user types (with a delay) filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available (visible) options within the drop down filter_reset : null, // jQuery selector string of an element used to reset the filters + filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters filter_searchDelay : 300, // typing delay in milliseconds before starting a search filter_startsWith : false, // if true, filter start from the beginning of the cell contents filter_useParsedData : false, // filter all data using parsed content @@ -563,7 +570,8 @@ ts.filter = { // reset button/link if (wo.filter_reset) { $(document).delegate(wo.filter_reset, 'click.tsfilter', function() { - ts.filter.searching(table, []); + // trigger a reset event, so other functions (filterFormatter) know when to reset + c.$table.trigger('filterReset'); }); } if (wo.filter_functions) { @@ -573,7 +581,7 @@ ts.filter = { $header = c.$headers.filter('[data-column="' + column + '"]:last'); options = ''; if (wo.filter_functions[column] === true && !$header.hasClass('filter-false')) { - ts.filter.buildSelect(column); + ts.filter.buildSelect(table, column); } else if (typeof column === 'string' && !$header.hasClass('filter-false')) { // add custom drop down list for (string in wo.filter_functions[column]) { @@ -610,6 +618,7 @@ ts.filter = { ts.isProcessing(table, event.type === 'filterStart', columns ? $header : ''); }); } + if (c.debug) { ts.benchmark("Applying Filter widget", time); } @@ -619,18 +628,26 @@ ts.filter = { if (filters.length) { ts.setFilters(table, filters, true); } + ts.filter.checkFilters(table, filters); }); // filter widget initialized wo.filter_Initialized = true; c.$table.trigger('filterInit'); - ts.filter.checkFilters(table); }, - setDefaults: function(table, c, wo){ + setDefaults: function(table, c, wo) { var indx, filters = [], columns = c.columns; - for (indx = 0; indx < columns; indx++) { - filters[indx] = c.$headers.filter('[data-column="' + indx + '"]:last').attr(wo.filter_defaultAttrib) || filters[indx]; + if (wo.filter_saveFilters && ts.storage) { + filters = ts.storage( table, 'tablesorter-filters' ) || []; + // make sure we're not just saving an empty array + if (filters.join('') === '') { filters = []; } + } + // if not filters saved, then check default settings + if (!filters.length) { + for (indx = 0; indx < columns; indx++) { + filters[indx] = c.$headers.filter('[data-column="' + indx + '"]:last').attr(wo.filter_defaultAttrib) || filters[indx]; + } } $(table).data('lastSearch', filters); return filters; @@ -692,7 +709,7 @@ ts.filter = { bindSearch: function(table, $el) { table = $(table)[0]; var external, wo = table.config.widgetOptions; - $el.bind('keyup search', function(event, filter) { + $el.unbind('keyup search').bind('keyup search', function(event, filter) { // emulate what webkit does.... escape clears the filter if (event.which === 27) { this.value = ''; @@ -703,7 +720,15 @@ ts.filter = { return; } // external searches won't have a filter parameter, so grab the value - external = $(this).hasClass('tablesorter-filter') ? filter : [ $(this).val() ]; + if ($(this).hasClass('tablesorter-filter')) { + external = filter; + } else { + external = []; + $el.each(function(){ + // target the appropriate column if the external input has a data-column attribute + external[ $(this).data('column') || 0 ] = $(this).val(); + }); + } ts.filter.searching(table, filter, external); }); }, @@ -782,7 +807,7 @@ ts.filter = { columns = c.columns, $tbodies = c.$tbodies, // anyMatch really screws up with these types of filters - anyMatchNotAllowedTypes = [ 'range', 'operators' ], + anyMatchNotAllowedTypes = [ 'range', 'notMatch', 'operators' ], // parse columns after formatter, in case the class is added at that point parsed = c.$headers.map(function(columnIndex) { return (ts.getData) ? @@ -793,7 +818,7 @@ ts.filter = { for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { if ($tbodies.eq(tbodyIndex).hasClass(ts.css.info)) { continue; } // ignore info blocks, issue #264 $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); - $rows = $tbody.children('tr').not('.' + c.cssChildRow); + $rows = $tbody.children('tr').not('.' + c.cssChildRow).not('.group-header'); len = $rows.length; if (combinedFilters === '' || wo.filter_serversideFiltering) { $tbody.children().show().removeClass(wo.filter_filteredRow); @@ -902,6 +927,9 @@ ts.filter = { c.lastCombinedFilter = combinedFilters; // save last search c.lastSearch = filters; c.$table.data('lastSearch', filters); + if (wo.filter_saveFilters && ts.storage) { + ts.storage( table, 'tablesorter-filters', filters ); + } if (c.debug) { ts.benchmark("Completed filter widget search", time); } @@ -1015,7 +1043,7 @@ ts.setFilters = function(table, filter, apply) { // ************************** ts.addWidget({ id: "stickyHeaders", - priority: 60, + priority: 60, // sticky widget must be initialized after the filter widget! options: { stickyHeaders : '', // extra class name added to the sticky header row stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element @@ -1024,55 +1052,54 @@ ts.addWidget({ stickyHeaders_includeCaption : true, // if false and a caption exist, it won't be included in the sticky header stickyHeaders_zIndex : 2 // The zIndex of the stickyHeaders, allows the user to adjust this to their needs }, - format: function(table, c, wo){ + format: function(table, c, wo) { if (c.$table.hasClass('hasStickyHeaders')) { return; } - var $t = c.$table, + var $cell, + $table = c.$table, $win = $(window), - header = $t.children('thead:first'), - hdrCells = header.children('tr:not(.sticky-false)').children(), - innr = '.tablesorter-header-inner', - tfoot = $t.find('tfoot'), + $thead = $table.children('thead:first'), + $header = $thead.children('tr').not('.sticky-false').children(), + innerHeader = '.tablesorter-header-inner', + $tfoot = $table.find('tfoot'), filterInputs = '.tablesorter-filter', $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '', stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, - stickyzIndex = wo.stickyHeaders_zIndex ? wo.stickyHeaders_zIndex : 2, - $stickyTable = wo.$sticky = $t.clone() + $stickyTable = wo.$sticky = $table.clone() .addClass('containsStickyHeaders') .css({ position : 'fixed', margin : 0, top : stickyOffset, visibility : 'hidden', - zIndex : stickyzIndex + zIndex : wo.stickyHeaders_zIndex ? wo.stickyHeaders_zIndex : 2 }), - stkyHdr = $stickyTable.children('thead:first').addClass('tablesorter-stickyHeader ' + wo.stickyHeaders), - stkyCells, + $stickyThead = $stickyTable.children('thead:first').addClass('tablesorter-stickyHeader ' + wo.stickyHeaders), + $stickyCells, laststate = '', spacing = 0, - flag = false, - resizeHdr = function(){ + updatingStickyFilters = false, + resizeHeader = function() { stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0; - var bwsr = navigator.userAgent; spacing = 0; // yes, I dislike browser sniffing, but it really is needed here :( // webkit automatically compensates for border spacing - if ($t.css('border-collapse') !== 'collapse' && !/(webkit|msie)/i.test(bwsr)) { + if ($table.css('border-collapse') !== 'collapse' && !/(webkit|msie)/i.test(navigator.userAgent)) { // Firefox & Opera use the border-spacing // update border-spacing here because of demos that switch themes - spacing = parseInt(hdrCells.eq(0).css('border-left-width'), 10) * 2; + spacing = parseInt($header.eq(0).css('border-left-width'), 10) * 2; } $stickyTable.css({ - left : header.offset().left - $win.scrollLeft() - spacing, - width: $t.width() + left : $thead.offset().left - $win.scrollLeft() - spacing, + width: $table.width() }); - stkyCells.filter(':visible').each(function(i){ - var $h = hdrCells.filter(':visible').eq(i); + $stickyCells.filter(':visible').each(function(i) { + var $cell = $header.filter(':visible').eq(i); $(this) .css({ - width: $h.width() - spacing, - height: $h.height() + width: $cell.width() - spacing, + height: $cell.height() }) - .find(innr).width( $h.find(innr).width() ); + .find(innerHeader).width( $cell.find(innerHeader).width() ); }); }; // fix clone ID, if it exists - fixes #271 @@ -1084,71 +1111,70 @@ ts.addWidget({ $stickyTable.find('caption').remove(); } // issue #172 - find td/th in sticky header - stkyCells = stkyHdr.children().children(); + $stickyCells = $stickyThead.children().children(); $stickyTable.css({ height:0, width:0, padding:0, margin:0, border:0 }); // remove resizable block - stkyCells.find('.tablesorter-resizer').remove(); + $stickyCells.find('.tablesorter-resizer').remove(); // update sticky header class names to match real header after sorting - $t - .addClass('hasStickyHeaders') - .bind('sortEnd.tsSticky', function(){ - hdrCells.filter(':visible').each(function(i){ - var t = stkyCells.filter(':visible').eq(i); - t - .attr('class', $(this).attr('class')) - // remove processing icon - .removeClass(ts.css.processing + ' ' + c.cssProcessing); - if (c.cssIcon){ - t - .find('.' + ts.css.icon) - .attr('class', $(this).find('.' + ts.css.icon).attr('class')); - } + $table + .addClass('hasStickyHeaders') + .bind('sortEnd.tsSticky', function() { + $header.filter(':visible').each(function(indx) { + $cell = $stickyCells.filter(':visible').eq(indx) + .attr('class', $(this).attr('class')) + // remove processing icon + .removeClass(ts.css.processing + ' ' + c.cssProcessing); + if (c.cssIcon) { + $cell + .find('.' + ts.css.icon) + .attr('class', $(this).find('.' + ts.css.icon).attr('class')); + } + }); + }) + .bind('pagerComplete.tsSticky', function() { + resizeHeader(); }); - }) - .bind('pagerComplete.tsSticky', function(){ - resizeHdr(); - }); - // http://stackoverflow.com/questions/5312849/jquery-find-self; - hdrCells.find(c.selectorSort).add( c.$headers.filter(c.selectorSort) ).each(function(i){ - var t = $(this), - // clicking on sticky will trigger sort - $cell = stkyHdr.children('tr.tablesorter-headerRow').children().eq(i).bind('mouseup', function(e){ - t.trigger(e, true); // external mouseup flag (click timer is ignored) + // http://stackoverflow.com/questions/5312849/jquery-find-self; + $header.find(c.selectorSort).add( c.$headers.filter(c.selectorSort) ).each(function(indx) { + var $header = $(this), + // clicking on sticky will trigger sort + $cell = $stickyThead.children('tr.tablesorter-headerRow').children().eq(indx).bind('mouseup', function(event) { + $header.trigger(event, true); // external mouseup flag (click timer is ignored) + }); + // prevent sticky header text selection + if (c.cancelSelection) { + $cell + .attr('unselectable', 'on') + .bind('selectstart', false) + .css({ + 'user-select': 'none', + 'MozUserSelect': 'none' + }); + } }); - // prevent sticky header text selection - if (c.cancelSelection) { - $cell - .attr('unselectable', 'on') - .bind('selectstart', false) - .css({ - 'user-select': 'none', - 'MozUserSelect': 'none' - }); - } - }); // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. - $t.after( $stickyTable ); + $table.after( $stickyTable ); // make it sticky! - $win.bind('scroll.tsSticky resize.tsSticky', function(e){ - if (!$t.is(':visible')) { return; } // fixes #278 - var pre = 'tablesorter-sticky-', - offset = $t.offset(), - cap = (wo.stickyHeaders_includeCaption ? 0 : $t.find('caption').outerHeight(true)), - sTop = $win.scrollTop() + stickyOffset - cap, - tableHt = $t.height() - ($stickyTable.height() + (tfoot.height() || 0)), - vis = (sTop > offset.top) && (sTop < offset.top + tableHt) ? 'visible' : 'hidden'; + $win.bind('scroll.tsSticky resize.tsSticky', function(event) { + if (!$table.is(':visible')) { return; } // fixes #278 + var prefix = 'tablesorter-sticky-', + offset = $table.offset(), + captionHeight = (wo.stickyHeaders_includeCaption ? 0 : $table.find('caption').outerHeight(true)), + scrollTop = $win.scrollTop() + stickyOffset - captionHeight, + tableHeight = $table.height() - ($stickyTable.height() + ($tfoot.height() || 0)), + isVisible = (scrollTop > offset.top) && (scrollTop < offset.top + tableHeight) ? 'visible' : 'hidden'; $stickyTable - .removeClass(pre + 'visible ' + pre + 'hidden') - .addClass(pre + vis) - .css({ - // adjust when scrolling horizontally - fixes issue #143 - left : header.offset().left - $win.scrollLeft() - spacing, - visibility : vis - }); - if (vis !== laststate || e.type === 'resize'){ + .removeClass(prefix + 'visible ' + prefix + 'hidden') + .addClass(prefix + isVisible) + .css({ + // adjust when scrolling horizontally - fixes issue #143 + left : $thead.offset().left - $win.scrollLeft() - spacing, + visibility : isVisible + }); + if (isVisible !== laststate || event.type === 'resize') { // make sure the column widths match - resizeHdr(); - laststate = vis; + resizeHeader(); + laststate = isVisible; } }); if (wo.stickyHeaders_addResizeEvent) { @@ -1156,28 +1182,28 @@ ts.addWidget({ } // look for filter widget - $t.bind('filterEnd', function(){ - if (flag) { return; } - stkyHdr.find('.tablesorter-filter-row').children().each(function(i){ - $(this).find(filterInputs).val( c.$filters.find(filterInputs).eq(i).val() ); + $table.bind('filterEnd', function() { + if (updatingStickyFilters) { return; } + $stickyThead.find('.tablesorter-filter-row').children().each(function(indx) { + $(this).find(filterInputs).val( c.$filters.find(filterInputs).eq(indx).val() ); }); }); - stkyCells.find(filterInputs).bind('keyup search change', function(e){ + $stickyCells.find(filterInputs).bind('keyup search change', function(event) { // ignore arrow and meta keys; allow backspace - if ((e.which < 32 && e.which !== 8) || (e.which >= 37 && e.which <=40)) { return; } - flag = true; - var $f = $(this), col = $f.attr('data-column'); - c.$filters.find(filterInputs).eq(col) + if ((event.which < 32 && event.which !== 8) || (event.which >= 37 && event.which <=40)) { return; } + updatingStickyFilters = true; + var $f = $(this), column = $f.attr('data-column'); + c.$filters.find(filterInputs).eq(column) .val( $f.val() ) .trigger('search'); - setTimeout(function(){ - flag = false; + setTimeout(function() { + updatingStickyFilters = false; }, wo.filter_searchDelay); }); - $t.trigger('stickyHeadersInit'); + $table.trigger('stickyHeadersInit'); }, - remove: function(table, c, wo){ + remove: function(table, c, wo) { c.$table .removeClass('hasStickyHeaders') .unbind('sortEnd.tsSticky pagerComplete.tsSticky') @@ -1202,119 +1228,120 @@ ts.addWidget({ resizable : true, resizable_addLastColumn : false }, - format: function(table, c, wo){ + format: function(table, c, wo) { if (c.$table.hasClass('hasResizable')) { return; } c.$table.addClass('hasResizable'); - var $t, t, i, j, s = {}, $c, $cols, w, tw, - $tbl = c.$table, - position = 0, + var $rows, $columns, $column, column, + storedSizes = {}, + $table = c.$table, + mouseXPosition = 0, $target = null, $next = null, - fullWidth = Math.abs($tbl.parent().width() - $tbl.width()) < 20, - stopResize = function(){ - if (ts.storage && $target){ - s[$target.index()] = $target.width(); - s[$next.index()] = $next.width(); - $target.width( s[$target.index()] ); - $next.width( s[$next.index()] ); - if (wo.resizable !== false){ - ts.storage(table, 'tablesorter-resizable', s); + fullWidth = Math.abs($table.parent().width() - $table.width()) < 20, + stopResize = function() { + if (ts.storage && $target) { + storedSizes[$target.index()] = $target.width(); + storedSizes[$next.index()] = $next.width(); + $target.width( storedSizes[$target.index()] ); + $next.width( storedSizes[$next.index()] ); + if (wo.resizable !== false) { + ts.storage(table, 'tablesorter-resizable', storedSizes); } } - position = 0; + mouseXPosition = 0; $target = $next = null; $(window).trigger('resize'); // will update stickyHeaders, just in case }; - s = (ts.storage && wo.resizable !== false) ? ts.storage(table, 'tablesorter-resizable') : {}; + storedSizes = (ts.storage && wo.resizable !== false) ? ts.storage(table, 'tablesorter-resizable') : {}; // process only if table ID or url match - if (s){ - for (j in s){ - if (!isNaN(j) && j < c.$headers.length){ - c.$headers.eq(j).width(s[j]); // set saved resizable widths + if (storedSizes) { + for (column in storedSizes) { + if (!isNaN(column) && column < c.$headers.length) { + c.$headers.eq(column).width(storedSizes[column]); // set saved resizable widths } } } - $t = $tbl.children('thead:first').children('tr'); + $rows = $table.children('thead:first').children('tr'); // add resizable-false class name to headers (across rows as needed) - $t.children().each(function(){ - t = $(this); - i = t.attr('data-column'); - j = ts.getData( t, c.headers[i], 'resizable') === "false"; - $t.children().filter('[data-column="' + i + '"]').toggleClass('resizable-false', j); + $rows.children().each(function() { + var canResize, + $column = $(this); + column = $column.attr('data-column'); + canResize = ts.getData( $column, c.headers[column], 'resizable') === "false"; + $rows.children().filter('[data-column="' + column + '"]')[canResize ? 'addClass' : 'removeClass']('resizable-false'); }); // add wrapper inside each cell to allow for positioning of the resizable target block - $t.each(function(){ - $c = $(this).children(':not(.resizable-false)'); + $rows.each(function() { + $column = $(this).children().not('.resizable-false'); if (!$(this).find('.tablesorter-wrapper').length) { // Firefox needs this inner div to position the resizer correctly - $c.wrapInner('<div class="tablesorter-wrapper" style="position:relative;height:100%;width:100%"></div>'); + $column.wrapInner('<div class="tablesorter-wrapper" style="position:relative;height:100%;width:100%"></div>'); } // don't include the last column of the row - if (!wo.resizable_addLastColumn) { $c = $c.slice(0,-1); } - $cols = $cols ? $cols.add($c) : $c; + if (!wo.resizable_addLastColumn) { $column = $column.slice(0,-1); } + $columns = $columns ? $columns.add($column) : $column; }); - $cols - .each(function(){ - $t = $(this); - j = parseInt($t.css('padding-right'), 10) + 10; // 8 is 1/2 of the 16px wide resizer grip - t = '<div class="tablesorter-resizer" style="cursor:w-resize;position:absolute;z-index:1;right:-' + j + - 'px;top:0;height:100%;width:20px;"></div>'; - $t + $columns + .each(function() { + var $column = $(this), + padding = parseInt($column.css('padding-right'), 10) + 10; // 10 is 1/2 of the 20px wide resizer grip + $column .find('.tablesorter-wrapper') - .append(t); + .append('<div class="tablesorter-resizer" style="cursor:w-resize;position:absolute;z-index:1;right:-' + + padding + 'px;top:0;height:100%;width:20px;"></div>'); }) - .bind('mousemove.tsresize', function(e){ + .bind('mousemove.tsresize', function(event) { // ignore mousemove if no mousedown - if (position === 0 || !$target) { return; } + if (mouseXPosition === 0 || !$target) { return; } // resize columns - w = e.pageX - position; - tw = $target.width(); - $target.width( tw + w ); - if ($target.width() !== tw && fullWidth){ - $next.width( $next.width() - w ); + var leftEdge = event.pageX - mouseXPosition, + targetWidth = $target.width(); + $target.width( targetWidth + leftEdge ); + if ($target.width() !== targetWidth && fullWidth) { + $next.width( $next.width() - leftEdge ); } - position = e.pageX; + mouseXPosition = event.pageX; }) - .bind('mouseup.tsresize', function(){ + .bind('mouseup.tsresize', function() { stopResize(); }) .find('.tablesorter-resizer,.tablesorter-resizer-grip') - .bind('mousedown', function(e){ + .bind('mousedown', function(event) { // save header cell and mouse position; closest() not supported by jQuery v1.2.6 - $target = $(e.target).closest('th'); - t = c.$headers.filter('[data-column="' + $target.attr('data-column') + '"]'); - if (t.length > 1) { $target = $target.add(t); } + $target = $(event.target).closest('th'); + var $header = c.$headers.filter('[data-column="' + $target.attr('data-column') + '"]'); + if ($header.length > 1) { $target = $target.add($header); } // if table is not as wide as it's parent, then resize the table - $next = e.shiftKey ? $target.parent().find('th:not(.resizable-false)').filter(':last') : $target.nextAll(':not(.resizable-false)').eq(0); - position = e.pageX; + $next = event.shiftKey ? $target.parent().find('th').not('.resizable-false').filter(':last') : $target.nextAll(':not(.resizable-false)').eq(0); + mouseXPosition = event.pageX; }); - $tbl.find('thead:first') - .bind('mouseup.tsresize mouseleave.tsresize', function(){ + $table.find('thead:first') + .bind('mouseup.tsresize mouseleave.tsresize', function() { stopResize(); }) // right click to reset columns to default widths - .bind('contextmenu.tsresize', function(){ + .bind('contextmenu.tsresize', function() { ts.resizableReset(table); - // $.isEmptyObject() needs jQuery 1.4+ - var rtn = $.isEmptyObject ? $.isEmptyObject(s) : s === {}; // allow right click if already reset - s = {}; - return rtn; + // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset + var allowClick = $.isEmptyObject ? $.isEmptyObject(storedSizes) : true; + storedSizes = {}; + return allowClick; }); }, - remove: function(table, c, wo){ + remove: function(table, c) { c.$table .removeClass('hasResizable') - .find('thead') + .children('thead') .unbind('mouseup.tsresize mouseleave.tsresize contextmenu.tsresize') - .find('tr').children() + .children('tr').children() .unbind('mousemove.tsresize mouseup.tsresize') // don't remove "tablesorter-wrapper" as uitheme uses it too .find('.tablesorter-resizer,.tablesorter-resizer-grip').remove(); ts.resizableReset(table); } }); -ts.resizableReset = function(table){ - table.config.$headers.filter(':not(.resizable-false)').css('width',''); +ts.resizableReset = function(table) { + table.config.$headers.not('.resizable-false').css('width',''); if (ts.storage) { ts.storage(table, 'tablesorter-resizable', {}); } }; @@ -1329,52 +1356,52 @@ ts.addWidget({ options: { saveSort : true }, - init: function(table, thisWidget, c, wo){ + init: function(table, thisWidget, c, wo) { // run widget format before all other widgets are applied to the table thisWidget.format(table, c, wo, true); }, - format: function(table, c, wo, init){ - var sl, time, - $t = c.$table, - ss = wo.saveSort !== false, // make saveSort active/inactive; default to true + format: function(table, c, wo, init) { + var stored, time, + $table = c.$table, + saveSort = wo.saveSort !== false, // make saveSort active/inactive; default to true sortList = { "sortList" : c.sortList }; - if (c.debug){ + if (c.debug) { time = new Date(); } - if ($t.hasClass('hasSaveSort')){ - if (ss && table.hasInitialized && ts.storage){ + if ($table.hasClass('hasSaveSort')) { + if (saveSort && table.hasInitialized && ts.storage) { ts.storage( table, 'tablesorter-savesort', sortList ); - if (c.debug){ + if (c.debug) { ts.benchmark('saveSort widget: Saving last sort: ' + c.sortList, time); } } } else { // set table sort on initial run of the widget - $t.addClass('hasSaveSort'); + $table.addClass('hasSaveSort'); sortList = ''; // get data - if (ts.storage){ - sl = ts.storage( table, 'tablesorter-savesort' ); - sortList = (sl && sl.hasOwnProperty('sortList') && $.isArray(sl.sortList)) ? sl.sortList : ''; - if (c.debug){ + if (ts.storage) { + stored = ts.storage( table, 'tablesorter-savesort' ); + sortList = (stored && stored.hasOwnProperty('sortList') && $.isArray(stored.sortList)) ? stored.sortList : ''; + if (c.debug) { ts.benchmark('saveSort: Last sort loaded: "' + sortList + '"', time); } - $t.bind('saveSortReset', function(e){ - e.stopPropagation(); + $table.bind('saveSortReset', function(event) { + event.stopPropagation(); ts.storage( table, 'tablesorter-savesort', '' ); }); } // init is true when widget init is run, this will run this widget before all other widgets have initialized // this method allows using this widget in the original tablesorter plugin; but then it will run all widgets twice. - if (init && sortList && sortList.length > 0){ + if (init && sortList && sortList.length > 0) { c.sortList = sortList; - } else if (table.hasInitialized && sortList && sortList.length > 0){ + } else if (table.hasInitialized && sortList && sortList.length > 0) { // update sort change - $t.trigger('sorton', [sortList]); + $table.trigger('sorton', [sortList]); } } }, - remove: function(table){ + remove: function(table) { // clear storage if (ts.storage) { ts.storage( table, 'tablesorter-savesort', '' ); } } From 74cfe7b223e345dabb818fd8ed687d6d09a9f869 Mon Sep 17 00:00:00 2001 From: Erik Ernst <github@black-milk.de> Date: Sat, 23 Nov 2013 09:48:24 +0100 Subject: [PATCH 008/138] * updated tablesorter to latest version (2.14.1) --- CHANGELOG.markdown | 4 ++++ README.markdown | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 4 ++-- .../jquery-tablesorter/jquery.tablesorter.js | 4 ++-- ...ry.tablesorter.widgets-filter-formatter.js | 2 +- .../jquery.tablesorter.widgets.js | 21 +++++++++++++------ .../jquery-tablesorter/theme.black-ice.css | 1 + .../jquery-tablesorter/theme.blue.css | 1 + .../jquery-tablesorter/theme.dark.css | 1 + .../jquery-tablesorter/theme.default.css | 1 + .../jquery-tablesorter/theme.dropbox.css | 12 ++++++++--- .../jquery-tablesorter/theme.green.css | 8 +++---- .../jquery-tablesorter/theme.grey.css | 8 ++++++- .../jquery-tablesorter/theme.ice.css | 7 +++++++ .../jquery-tablesorter/theme.jui.css | 4 ++++ 17 files changed, 62 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index 0a6ee80..d5859d4 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -1,5 +1,9 @@ Changelog === +#### v1.9.1 (2013-11-23) + +* Upgrade tablesorter to v2.14.1 + #### v1.9.0 (2013-11-20) * Upgrade tablesorter to v2.14.0 diff --git a/README.markdown b/README.markdown index f207edb..ea9bc30 100644 --- a/README.markdown +++ b/README.markdown @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.14.0 (11/19/2013), [documentation] +Current tablesorter version: 2.14.1 (11/22/2013), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 68d9694..3e7759d 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = "1.9.0" + VERSION = "1.9.1" end diff --git a/tablesorter b/tablesorter index f68b389..6519576 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit f68b3898c01bc668bb9c577ea64ec0f3c14aedbe +Subproject commit 651957606daff02493a13e72f9f90d06437c1770 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 014339d..7fd4d47 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 11/9/2013 + * updated 11/22/2013 (v2.14.1) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -585,7 +585,7 @@ } p.oldAjaxSuccess = p.oldAjaxSuccess || p.ajaxObject.success; c.appender = $this.appender; - if (ts.filter && c.widgets.indexOf('filter') >= 0) { + if (ts.filter && $.inArray('filter', c.widgets) >= 0) { // get any default filter settings (data-value attribute) fixes #388 p.currentFilters = c.$table.data('lastSearch') || ts.filter.setDefaults(table, c, c.widgetOptions) || []; // set, but don't apply current filters diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 8147aef..4f5288f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.14.0 - Client-side table sorting with ease! +* TableSorter 2.14.1 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.14.0"; + ts.version = "2.14.1"; ts.parsers = []; ts.widgets = []; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js index 601a17e..da8ef74 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js @@ -1,4 +1,4 @@ -/*! Filter widget formatter functions - updated 11/9/2013 +/*! Filter widget formatter functions - updated 11/9/2013 (v2.13.3) * requires: tableSorter 2.7.7+ and jQuery 1.4.3+ * * uiSpinner (jQuery UI spinner) diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 7582191..a8a8d16 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.8+ widgets - updated 11/19/2013 +/*! tableSorter 2.8+ widgets - updated 11/22/2013 (v2.14.1) * * Column Styles * Column Filters @@ -709,7 +709,8 @@ ts.filter = { bindSearch: function(table, $el) { table = $(table)[0]; var external, wo = table.config.widgetOptions; - $el.unbind('keyup search').bind('keyup search', function(event, filter) { + $el.unbind('keyup search filterReset') + .bind('keyup search', function(event, filter) { // emulate what webkit does.... escape clears the filter if (event.which === 27) { this.value = ''; @@ -730,6 +731,9 @@ ts.filter = { }); } ts.filter.searching(table, filter, external); + }) + .bind('filterReset', function(){ + $el.val(''); }); }, checkFilters: function(table, filter) { @@ -886,7 +890,7 @@ ts.filter = { // cycle through the different filters // filters return a boolean or null if nothing matches $.each(ts.filter.types, function(type, typeFunction) { - if (!wo.filter_anyMatch || (wo.filter_anyMatch && anyMatchNotAllowedTypes.indexOf(type) < 0)) { + if (!wo.filter_anyMatch || (wo.filter_anyMatch && $.inArray(type, anyMatchNotAllowedTypes) < 0)) { matches = typeFunction( filters[columnIndex], iFilter, exact, iExact, cached, columnIndex, table, wo, parsed ); if (matches !== null) { filterMatched = matches; @@ -1078,12 +1082,13 @@ ts.addWidget({ laststate = '', spacing = 0, updatingStickyFilters = false, + nonwkie = $table.css('border-collapse') !== 'collapse' && !/(webkit|msie)/i.test(navigator.userAgent), resizeHeader = function() { stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0; spacing = 0; // yes, I dislike browser sniffing, but it really is needed here :( // webkit automatically compensates for border spacing - if ($table.css('border-collapse') !== 'collapse' && !/(webkit|msie)/i.test(navigator.userAgent)) { + if (nonwkie) { // Firefox & Opera use the border-spacing // update border-spacing here because of demos that switch themes spacing = parseInt($header.eq(0).css('border-left-width'), 10) * 2; @@ -1093,13 +1098,15 @@ ts.addWidget({ width: $table.width() }); $stickyCells.filter(':visible').each(function(i) { - var $cell = $header.filter(':visible').eq(i); + var $cell = $header.filter(':visible').eq(i), + // some wibbly-wobbly... timey-wimey... stuff, to make columns line up in Firefox + offset = nonwkie && $(this).attr('data-column') === ( '' + parseInt(c.columns/2, 10) ) ? 1 : 0; $(this) .css({ width: $cell.width() - spacing, height: $cell.height() }) - .find(innerHeader).width( $cell.find(innerHeader).width() ); + .find(innerHeader).width( $cell.find(innerHeader).width() - offset ); }); }; // fix clone ID, if it exists - fixes #271 @@ -1109,6 +1116,8 @@ ts.addWidget({ $stickyTable.find('thead:gt(0), tr.sticky-false, tbody, tfoot').remove(); if (!wo.stickyHeaders_includeCaption) { $stickyTable.find('caption').remove(); + } else { + $stickyTable.find('caption').css( 'margin-left', '-1px' ); } // issue #172 - find td/th in sticky header $stickyCells = $stickyThead.children().children(); diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css index 5b46fc1..869125b 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css @@ -46,6 +46,7 @@ } .tablesorter-blackice thead .sorter-false { background-image: none; + cursor: default; padding: 4px; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css index 8e11a35..b40a32c 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css @@ -71,6 +71,7 @@ } .tablesorter-blue thead .sorter-false { background-image: none; + cursor: default; padding: 4px; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css index f1cf1ae..c104960 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css @@ -45,6 +45,7 @@ } .tablesorter-dark thead .sorter-false { background-image: none; + cursor: default; padding: 4px; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css index 2f158b7..cc59d20 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css @@ -49,6 +49,7 @@ Default Theme } .tablesorter-default thead .sorter-false { background-image: none; + cursor: default; padding: 4px; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css index 0315b41..82269b5 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css @@ -35,7 +35,6 @@ background-color: #f0f9ff; border-bottom: 1px solid #96c4ea; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.12), 0 0 0 #000000 inset; - cursor: pointer; white-space: normal; } .tablesorter-dropbox .tablesorter-headerSortUp, @@ -44,11 +43,14 @@ .tablesorter-dropbox .tablesorter-headerDesc { font-weight: 600; } +.tablesorter-dropbox .tablesorter-header { + cursor: pointer; +} .tablesorter-dropbox .tablesorter-header i { width: 9px; height: 9px; - padding: 4px 20px 4px 4px; - cursor: pointer; + padding: 0 10px 0 4px; + display: inline-block; background-position: center right; background-repeat: no-repeat; content: ""; @@ -73,6 +75,10 @@ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAALNJREFUeNpi/P//PwMhwBJf3uP879e3PUzMzBiS//7+ZWBi43JhBJmU2z7nIzMzEx8jIyNcAUj8799/nyZXpvCzgARYuXjTWBkZVjCzIEz7++cvw+//DGkgNiPMTWVT1l5hZvynDTINbMp/pqtdOcE6IDkmmM5fv3/5//v37z9QBQOIBvFhcnBFEwoj7/5jZFnz9+8fBhAN4sN9h+ybH9++JrGxscr/+vE1CVmckZhwAggwANvlUyq5Dd1wAAAAAElFTkSuQmCC'); /* background-image: url(/assets/jquery-tablesorter/dropbox-desc-hovered.png); */ } +.tablesorter-dropbox thead .sorter-false { + cursor: default; +} + .tablesorter-dropbox thead .sorter-false i, .tablesorter-dropbox thead .sorter-false:hover i { background-image: none; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css index 00b2c10..e119b2a 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css @@ -6,15 +6,14 @@ width: 100%; text-align: left; border-spacing: 0; + border: #cdcdcd 1px solid; + border-width: 1px 0 0 1px; } -.tablesorter-green, .tablesorter-green th, .tablesorter-green td { font: 12px/18px Arial, Sans-serif; border: #cdcdcd 1px solid; - border-spacing: 0; - padding: 0; - text-align: left; + border-width: 0 1px 1px 0; } /* header */ @@ -58,6 +57,7 @@ } .tablesorter-green thead .tablesorter-header.sorter-false { background-image: none; + cursor: default; padding: 4px; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css index 2ec3669..9477104 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css @@ -36,6 +36,10 @@ position: relative; padding: 4px 15px 4px 4px; } +.tablesorter-grey .header, +.tablesorter-grey .tablesorter-header { + cursor: pointer; +} .tablesorter-grey .header i, .tablesorter-grey .tablesorter-header i { width: 18px; @@ -50,7 +54,6 @@ background-position: center right; padding: 4px; white-space: normal; - cursor: pointer; } .tablesorter-grey th.headerSortUp, .tablesorter-grey th.tablesorter-headerSortUp, @@ -78,6 +81,9 @@ /* white desc arrow */ background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7); } +.tablesorter-grey thead .sorter-false { + cursor: default; +} .tablesorter-grey thead .sorter-false i { background-image: none; padding: 4px; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css index 7c1a9cb..93a13ab 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css @@ -60,6 +60,7 @@ } .tablesorter-ice thead .sorter-false { background-image: none; + cursor: default; padding: 4px; } @@ -122,6 +123,12 @@ background-color: #ebfafa; } +/* sticky headers */ +.tablesorter-ice.containsStickyHeaders thead tr:nth-child(1) th, +.tablesorter-ice.containsStickyHeaders thead tr:nth-child(1) td { + border-top: #ccc 1px solid; +} + /* caption */ caption { background: #fff; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css index 2108f71..20a9022 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css @@ -38,6 +38,10 @@ margin-top: -8px; /* half the icon height; older IE doesn't like this */ } +.tablesorter-jui thead .sorter-false { + cursor: default; +} + /* tfoot */ .tablesorter-jui tfoot th, .tablesorter-jui tfoot td { From 31ddab71cfb2d640a10b76e9e5534957b4937372 Mon Sep 17 00:00:00 2001 From: Erik Ernst <github@black-milk.de> Date: Mon, 25 Nov 2013 20:38:39 +0100 Subject: [PATCH 009/138] * updated tablesorter to latest version (2.14.2) --- CHANGELOG.markdown | 4 ++++ README.markdown | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../javascripts/jquery-tablesorter/jquery.tablesorter.js | 4 ++-- .../jquery-tablesorter/jquery.tablesorter.widgets.js | 8 +++++--- .../stylesheets/jquery-tablesorter/theme.bootstrap.css | 1 - 7 files changed, 14 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index d5859d4..2e727ef 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -1,5 +1,9 @@ Changelog === +#### v1.9.2 (2013-11-25) + +* Upgrade tablesorter to v2.14.2 + #### v1.9.1 (2013-11-23) * Upgrade tablesorter to v2.14.1 diff --git a/README.markdown b/README.markdown index ea9bc30..171e1b3 100644 --- a/README.markdown +++ b/README.markdown @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.14.1 (11/22/2013), [documentation] +Current tablesorter version: 2.14.2 (11/25/2013), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 3e7759d..b383130 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = "1.9.1" + VERSION = "1.9.2" end diff --git a/tablesorter b/tablesorter index 6519576..0d565d3 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 651957606daff02493a13e72f9f90d06437c1770 +Subproject commit 0d565d334046515b4e10bfc0bc9ed7f48fd4f4cc diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 4f5288f..1111b6b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.14.1 - Client-side table sorting with ease! +* TableSorter 2.14.2 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.14.1"; + ts.version = "2.14.2"; ts.parsers = []; ts.widgets = []; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index a8a8d16..8a65fe2 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.8+ widgets - updated 11/22/2013 (v2.14.1) +/*! tableSorter 2.8+ widgets - updated 11/24/2013 (v2.14.2) * * Column Styles * Column Filters @@ -635,13 +635,14 @@ ts.filter = { c.$table.trigger('filterInit'); }, setDefaults: function(table, c, wo) { - var indx, + var indx, isArray, filters = [], columns = c.columns; if (wo.filter_saveFilters && ts.storage) { filters = ts.storage( table, 'tablesorter-filters' ) || []; + isArray = $.isArray(filters); // make sure we're not just saving an empty array - if (filters.join('') === '') { filters = []; } + if (isArray && filters.join('') === '' || !isArray ) { filters = []; } } // if not filters saved, then check default settings if (!filters.length) { @@ -803,6 +804,7 @@ ts.filter = { }); }, findRows: function(table, filters, combinedFilters) { + if (table.config.lastCombinedFilter === combinedFilters) { return; } var cached, len, $rows, rowIndex, tbodyIndex, $tbody, $cells, columnIndex, childRow, childRowText, exact, iExact, iFilter, lastSearch, matches, result, searchFiltered, filterMatched, showRow, time, diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index 2ce7c9f..c1bf02f 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -72,7 +72,6 @@ height: auto; margin: 0 auto; padding: 4px 6px; - background-color: #fff; color: #333; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; From b49d8b42af477f1ac7b3b217b5398008658d704a Mon Sep 17 00:00:00 2001 From: Erik Ernst <github@black-milk.de> Date: Wed, 4 Dec 2013 22:54:58 +0100 Subject: [PATCH 010/138] * updated tablesorter to latest version (2.14.3) --- CHANGELOG.markdown | 5 ++ README.markdown | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 23 +++++++-- .../jquery-tablesorter/jquery.tablesorter.js | 9 ++-- .../jquery.tablesorter.widgets.js | 51 +++++++++---------- 7 files changed, 55 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index 2e727ef..db73087 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -1,5 +1,10 @@ Changelog === + +#### v1.9.3 (2013-12-04) + +* Upgrade tablesorter to v2.14.3 + #### v1.9.2 (2013-11-25) * Upgrade tablesorter to v2.14.2 diff --git a/README.markdown b/README.markdown index 171e1b3..32d5a44 100644 --- a/README.markdown +++ b/README.markdown @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.14.2 (11/25/2013), [documentation] +Current tablesorter version: 2.14.3 (12/2/2013), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index b383130..e3ee5fb 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = "1.9.2" + VERSION = "1.9.3" end diff --git a/tablesorter b/tablesorter index 0d565d3..d81d64f 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 0d565d334046515b4e10bfc0bc9ed7f48fd4f4cc +Subproject commit d81d64f4182104fa3f704391c7808207afc8a4bd diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 7fd4d47..1f2ef4b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 11/22/2013 (v2.14.1) + * updated 12/2/2013 (v2.14.3) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -92,6 +92,7 @@ totalPages: 0, filteredRows: 0, filteredPages: 0, + ajaxCounter: 0, currentFilters: [], startRow: 0, endRow: 0, @@ -138,8 +139,12 @@ }) // {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object) .replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m){ - var t = m.replace(/[{}\s]/g,''), a = t.split(':'), d = p.ajaxData; - return a.length > 1 && d && d[a[0]] ? d[a[0]][a[1]] : p[t] || (d ? d[t] : '') || ''; + var str = m.replace(/[{}\s]/g,''), + extra = str.split(':'), + data = p.ajaxData, + // return zero for default page/row numbers + deflt = /(rows?|pages?)$/i.test(str) ? 0 : ''; + return extra.length > 1 && data && data[extra[0]] ? data[extra[0]][extra[1]] : p[str] || (data ? data[str] : deflt) || deflt; }); if (out.length) { out[ (out[0].tagName === 'INPUT') ? 'val' : 'html' ](s); @@ -278,8 +283,8 @@ for ( i = 0; i < l; i++ ) { tds += '<tr>'; for ( j = 0; j < d[i].length; j++ ) { - // build tbody cells - tds += '<td>' + d[i][j] + '</td>'; + // build tbody cells; watch for data containing HTML markup - see #434 + tds += /^\s*<td/.test(d[i][j]) ? $.trim(d[i][j]) : '<td>' + d[i][j] + '</td>'; } tds += '</tr>'; } @@ -340,6 +345,7 @@ getAjax = function(table, p){ var url = getAjaxUrl(table, p), $doc = $(document), + counter, c = table.config; if ( url !== '' ) { if (c.showProcessing) { @@ -349,8 +355,15 @@ renderAjax(null, table, p, xhr, exception); $doc.unbind('ajaxError.pager'); }); + + counter = ++p.ajaxCounter; + p.ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl p.ajaxObject.success = function(data) { + // Refuse to process old ajax commands that were overwritten by new ones - see #443 + if (counter < p.ajaxCounter){ + return; + } renderAjax(data, table, p); $doc.unbind('ajaxError.pager'); if (typeof p.oldAjaxSuccess === 'function') { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 1111b6b..899630c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.14.2 - Client-side table sorting with ease! +* TableSorter 2.14.3 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.14.2"; + ts.version = "2.14.3"; ts.parsers = []; ts.widgets = []; @@ -1284,11 +1284,11 @@ if (init || !(c.widgetInit[w.id])) { if (w.hasOwnProperty('options')) { wo = table.config.widgetOptions = $.extend( true, {}, w.options, wo ); - c.widgetInit[w.id] = true; } if (w.hasOwnProperty('init')) { w.init(table, w, c, wo); } + c.widgetInit[w.id] = true; } if (!init && w.hasOwnProperty('format')) { w.format(table, c, wo, false); @@ -1311,7 +1311,8 @@ for (i = 0; i < l; i++){ if ( w[i] && w[i].id && (doAll || $.inArray( w[i].id, cw ) < 0) ) { if (c.debug) { log( 'Refeshing widgets: Removing ' + w[i].id ); } - if (w[i].hasOwnProperty('remove')) { + // only remove widgets that have been initialized - fixes #442 + if (w[i].hasOwnProperty('remove') && c.widgetInit[w[i].id]) { w[i].remove(table, c, c.widgetOptions); c.widgetInit[w[i].id] = false; } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 8a65fe2..153cb81 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.8+ widgets - updated 11/24/2013 (v2.14.2) +/*! tableSorter 2.8+ widgets - updated 12/2/2013 (v2.14.3) * * Column Styles * Column Filters @@ -422,8 +422,8 @@ ts.filter = { // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric exact: function( filter, iFilter, exact, iExact ) { /*jshint eqeqeq:false */ - if ( iFilter.replace(ts.filter.regex.exact, '') == iExact ) { - return true; + if (ts.filter.regex.exact.test(iFilter)) { + return iFilter.replace(ts.filter.regex.exact, '') == iExact; } return null; }, @@ -479,7 +479,7 @@ ts.filter = { // Look for a range (using " to " or " - ") - see issue #166; thanks matzhu! range : function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed ) { if ( /\s+(-|to)\s+/.test(iFilter) ) { - var result, + var result, tmp, c = table.config, query = iFilter.split(/(?: - | to )/), // make sure the dash is for a range and not indicating a negative number range1 = ts.formatFloat(query[0].replace(ts.filter.regex.nondigit, ''), table), @@ -494,7 +494,7 @@ ts.filter = { result = ( parsed[index] || c.parsers[index].type === 'numeric' ) && !isNaN(range1) && !isNaN(range2) ? cached : isNaN(iExact) ? ts.formatFloat( iExact.replace(ts.filter.regex.nondigit, ''), table) : ts.formatFloat( iExact, table ); - if (range1 > range2) { result = range1; range1 = range2; range2 = result; } // swap + if (range1 > range2) { tmp = range1; range1 = range2; range2 = tmp; } // swap return (result >= range1 && result <= range2) || (range1 === '' || range2 === ''); } return null; @@ -605,7 +605,7 @@ ts.filter = { }); if (wo.filter_hideFilters) { - ts.filter.hideFilters(table, c, wo); + ts.filter.hideFilters(table, c); } // show processing icon @@ -712,6 +712,7 @@ ts.filter = { var external, wo = table.config.widgetOptions; $el.unbind('keyup search filterReset') .bind('keyup search', function(event, filter) { + var $this = $(this); // emulate what webkit does.... escape clears the filter if (event.which === 27) { this.value = ''; @@ -722,7 +723,7 @@ ts.filter = { return; } // external searches won't have a filter parameter, so grab the value - if ($(this).hasClass('tablesorter-filter')) { + if ($this.hasClass('tablesorter-filter') && !$this.hasClass('tablesorter-external-filter')) { external = filter; } else { external = []; @@ -766,7 +767,7 @@ ts.filter = { return false; } }, - hideFilters: function(table, c, wo) { + hideFilters: function(table, c) { var $filterRow, $filterRow2, timer; c.$table .find('.tablesorter-filter-row') @@ -824,7 +825,8 @@ ts.filter = { for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { if ($tbodies.eq(tbodyIndex).hasClass(ts.css.info)) { continue; } // ignore info blocks, issue #264 $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); - $rows = $tbody.children('tr').not('.' + c.cssChildRow).not('.group-header'); + // skip child rows & widget added (removable) rows - fixes #448 thanks to @hempel! + $rows = $tbody.children('tr').not('.' + c.cssChildRow).not(c.selectorRemove); len = $rows.length; if (combinedFilters === '' || wo.filter_serversideFiltering) { $tbody.children().show().removeClass(wo.filter_filteredRow); @@ -1083,7 +1085,6 @@ ts.addWidget({ $stickyCells, laststate = '', spacing = 0, - updatingStickyFilters = false, nonwkie = $table.css('border-collapse') !== 'collapse' && !/(webkit|msie)/i.test(navigator.userAgent), resizeHeader = function() { stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0; @@ -1193,24 +1194,20 @@ ts.addWidget({ } // look for filter widget - $table.bind('filterEnd', function() { - if (updatingStickyFilters) { return; } - $stickyThead.find('.tablesorter-filter-row').children().each(function(indx) { - $(this).find(filterInputs).val( c.$filters.find(filterInputs).eq(indx).val() ); + if ($table.hasClass('hasFilters')) { + $table.bind('filterEnd', function() { + // $(':focus') needs jQuery 1.6+ + if ( $(document.activeElement).closest('thead')[0] !== $stickyThead[0] ) { + // don't update the stickyheader filter row if it already has focus + $stickyThead.find('.tablesorter-filter-row').children().each(function(indx) { + $(this).find(filterInputs).val( c.$filters.find(filterInputs).eq(indx).val() ); + }); + } }); - }); - $stickyCells.find(filterInputs).bind('keyup search change', function(event) { - // ignore arrow and meta keys; allow backspace - if ((event.which < 32 && event.which !== 8) || (event.which >= 37 && event.which <=40)) { return; } - updatingStickyFilters = true; - var $f = $(this), column = $f.attr('data-column'); - c.$filters.find(filterInputs).eq(column) - .val( $f.val() ) - .trigger('search'); - setTimeout(function() { - updatingStickyFilters = false; - }, wo.filter_searchDelay); - }); + + ts.filter.bindSearch( $table, $stickyCells.find('.tablesorter-filter').addClass('tablesorter-external-filter') ); + } + $table.trigger('stickyHeadersInit'); }, From 432956250b9fc2dee4608a1161b829c64d6c57b1 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Sun, 15 Dec 2013 21:34:37 +0100 Subject: [PATCH 011/138] * updated tablesorter to latest version (2.14.4) --- CHANGELOG.markdown | 4 + README.markdown | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 26 ++-- .../jquery-tablesorter/jquery.tablesorter.js | 50 ++++--- .../jquery.tablesorter.widgets.js | 50 ++++--- .../jquery-tablesorter/theme.bootstrap_2.css | 139 ++++++++++++++++++ 8 files changed, 220 insertions(+), 55 deletions(-) create mode 100644 vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index db73087..379a1a8 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -1,6 +1,10 @@ Changelog === +#### v1.9.4 (2013-12-15) + +* Upgrade tablesorter to v2.14.4 + #### v1.9.3 (2013-12-04) * Upgrade tablesorter to v2.14.3 diff --git a/README.markdown b/README.markdown index 32d5a44..b269762 100644 --- a/README.markdown +++ b/README.markdown @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.14.3 (12/2/2013), [documentation] +Current tablesorter version: 2.14.4 (12/14/2013), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index e3ee5fb..0c1eaf5 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = "1.9.3" + VERSION = "1.9.4" end diff --git a/tablesorter b/tablesorter index d81d64f..173db94 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit d81d64f4182104fa3f704391c7808207afc8a4bd +Subproject commit 173db948c6bd40be403da9da4d8d873e37cd9856 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 1f2ef4b..d04b6b4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 12/2/2013 (v2.14.3) + * updated 12/14/2013 (v2.14.4) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -120,10 +120,11 @@ var i, pg, s, out, c = table.config, f = c.$table.hasClass('hasFilters') && !p.ajaxUrl, - t = (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered') + ',' + c.selectorRemove, + t = (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered') + ',' + c.selectorRemove + + (p.countChildRows ? '' : ',.' + c.cssChildRow), sz = p.size || 10; // don't allow dividing by zero p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method - p.filteredRows = (f) ? c.$tbodies.eq(0).children('tr:not(.' + t + ')').length : p.totalRows; + p.filteredRows = (f) ? c.$tbodies.eq(0).children('tr').not('.' + t ).length : p.totalRows; p.filteredPages = (f) ? Math.ceil( p.filteredRows / sz ) || 1 : p.totalPages; if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) { t = (p.size * p.page > p.filteredRows); @@ -271,7 +272,7 @@ //ensure a zero returned row count doesn't fail the logical || rr_count = result[t ? 1 : 0]; p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count; - d = result[t ? 0 : 1] || []; // row data + d = p.totalRows === 0 ? [] : result[t ? 0 : 1] || []; // row data th = result[2]; // headers } l = d.length; @@ -329,12 +330,12 @@ fixHeight(table, p); // apply widgets after table has rendered $t.trigger('applyWidgets'); - if (p.initialized) { - $t.trigger('pagerChange', p); - $t.trigger('updateComplete'); - } else { - $t.trigger('update'); - } + $t.trigger('update', [false, function(){ + if (p.initialized) { + $t.trigger('updateComplete'); + $t.trigger('pagerChange', p); + } + }]); } if (!p.initialized) { p.initialized = true; @@ -612,9 +613,10 @@ } $t - .unbind('filterStart filterEnd sortEnd disable enable destroy update pageSize '.split(' ').join('.pager ')) + .unbind('filterStart filterEnd sortEnd disable enable destroy update updateRows updateAll addRows pageSize '.split(' ').join('.pager ')) .bind('filterStart.pager', function(e, filters) { p.currentFilters = filters; + p.page = 0; // fixes #456 }) // update pager after filter widget completes .bind('filterEnd.pager sortEnd.pager', function() { @@ -636,7 +638,7 @@ e.stopPropagation(); destroyPager(table, p); }) - .bind('update.pager', function(e){ + .bind('update updateRows updateAll addRows '.split(' ').join('.pager '), function(e){ e.stopPropagation(); hideRows(table, p); }) diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 899630c..e0b4417 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.14.3 - Client-side table sorting with ease! +* TableSorter 2.14.4 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.14.3"; + ts.version = "2.14.4"; ts.parsers = []; ts.widgets = []; @@ -814,28 +814,34 @@ }) .bind("addRows.tablesorter", function(e, $row, resort, callback) { e.stopPropagation(); - var i, rows = $row.filter('tr').length, - dat = [], l = $row[0].cells.length, - tbdy = $this.find('tbody').index( $row.parents('tbody').filter(':first') ); - // fixes adding rows to an empty table - see issue #179 - if (!c.parsers) { - buildParserCache(table); - } - // add each row - for (i = 0; i < rows; i++) { - // add each cell - for (j = 0; j < l; j++) { - dat[j] = c.parsers[j].format( getElementText(table, $row[i].cells[j], j), table, $row[i].cells[j], j ); + if (isEmptyObject(c.cache)) { + // empty table, do an update instead - fixes #450 + updateHeader(table); + commonUpdate(table, resort, callback); + } else { + var i, rows = $row.filter('tr').length, + dat = [], l = $row[0].cells.length, + tbdy = $this.find('tbody').index( $row.parents('tbody').filter(':first') ); + // fixes adding rows to an empty table - see issue #179 + if (!c.parsers) { + buildParserCache(table); } - // add the row index to the end - dat.push(c.cache[tbdy].row.length); - // update cache - c.cache[tbdy].row.push([$row[i]]); - c.cache[tbdy].normalized.push(dat); - dat = []; + // add each row + for (i = 0; i < rows; i++) { + // add each cell + for (j = 0; j < l; j++) { + dat[j] = c.parsers[j].format( getElementText(table, $row[i].cells[j], j), table, $row[i].cells[j], j ); + } + // add the row index to the end + dat.push(c.cache[tbdy].row.length); + // update cache + c.cache[tbdy].row.push([$row[i]]); + c.cache[tbdy].normalized.push(dat); + dat = []; + } + // resort using current settings + checkResort($this, resort, callback); } - // resort using current settings - checkResort($this, resort, callback); }) .bind("sorton.tablesorter", function(e, list, callback, init) { var c = table.config; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 153cb81..5e65293 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.8+ widgets - updated 12/2/2013 (v2.14.3) +/*! tableSorter 2.8+ widgets - updated 12/14/2013 (v2.14.4) * * Column Styles * Column Filters @@ -371,7 +371,7 @@ ts.addWidget({ $table .removeClass('hasFilters') // add .tsfilter namespace to all BUT search - .unbind('addRows updateCell update updateComplete appendCache search filterStart filterEnd '.split(' ').join('.tsfilter ')) + .unbind('addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join('.tsfilter ')) .find('.tablesorter-filter-row').remove(); for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody @@ -555,8 +555,7 @@ ts.filter = { } if (event.type === 'filterReset') { ts.filter.searching(table, []); - } - if (event.type === 'filterEnd') { + } else if (event.type === 'filterEnd') { ts.filter.buildDefault(table, true); } else { // send false argument to force a new search; otherwise if the filter hasn't changed, it will return @@ -754,7 +753,12 @@ ts.filter = { } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons - if (c.lastCombinedFilter === combinedFilters && filter !== false) { return; } + if (c.lastCombinedFilter === combinedFilters && filter !== false) { + return; + } else if (filter === false) { + // force filter refresh + c.lastCombinedFilter = null; + } c.$table.trigger('filterStart', [filters]); if (c.showProcessing) { // give it time for the processing icon to kick in @@ -923,7 +927,7 @@ ts.filter = { $rows[rowIndex].style.display = (showRow ? '' : 'none'); $rows.eq(rowIndex)[showRow ? 'removeClass' : 'addClass'](wo.filter_filteredRow); if (childRow.length) { - if (c.pager && c.pager.countChildRows || wo.pager_countChildRows) { + if (c.pager && c.pager.countChildRows || wo.pager_countChildRows || wo.filter_childRows) { childRow[showRow ? 'removeClass' : 'addClass'](wo.filter_filteredRow); // see issue #396 } childRow.toggle(showRow); @@ -1054,6 +1058,7 @@ ts.addWidget({ priority: 60, // sticky widget must be initialized after the filter widget! options: { stickyHeaders : '', // extra class name added to the sticky header row + stickyHeaders_attachTo : null, // jQuery selector or object to attach sticky header to stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element stickyHeaders_cloneId : '-sticky', // added to table ID, if it exists stickyHeaders_addResizeEvent : true, // trigger "resize" event on headers @@ -1064,21 +1069,24 @@ ts.addWidget({ if (c.$table.hasClass('hasStickyHeaders')) { return; } var $cell, $table = c.$table, - $win = $(window), + $attach = $(wo.stickyHeaders_attachTo), $thead = $table.children('thead:first'), + $win = $attach.length ? $attach : $(window), $header = $thead.children('tr').not('.sticky-false').children(), innerHeader = '.tablesorter-header-inner', $tfoot = $table.find('tfoot'), filterInputs = '.tablesorter-filter', $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '', - stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, + stickyOffset = $attach.length ? 0 : $stickyOffset.length ? + $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, $stickyTable = wo.$sticky = $table.clone() .addClass('containsStickyHeaders') .css({ - position : 'fixed', + position : $attach.length ? 'absolute' : 'fixed', margin : 0, top : stickyOffset, - visibility : 'hidden', + left : 0, + visibility : $attach.length ? 'visible' : 'hidden', zIndex : wo.stickyHeaders_zIndex ? wo.stickyHeaders_zIndex : 2 }), $stickyThead = $stickyTable.children('thead:first').addClass('tablesorter-stickyHeader ' + wo.stickyHeaders), @@ -1097,7 +1105,9 @@ ts.addWidget({ spacing = parseInt($header.eq(0).css('border-left-width'), 10) * 2; } $stickyTable.css({ - left : $thead.offset().left - $win.scrollLeft() - spacing, + left : $attach.length ? parseInt($table.css('padding-left'), 10) + + parseInt($table.css('margin-left'), 10) + parseInt($table.css('border-left-width'), 10) : + $thead.offset().left - $win.scrollLeft() - spacing, width: $table.width() }); $stickyCells.filter(':visible').each(function(i) { @@ -1166,23 +1176,27 @@ ts.addWidget({ }); // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. $table.after( $stickyTable ); + // make it sticky! $win.bind('scroll.tsSticky resize.tsSticky', function(event) { if (!$table.is(':visible')) { return; } // fixes #278 var prefix = 'tablesorter-sticky-', offset = $table.offset(), captionHeight = (wo.stickyHeaders_includeCaption ? 0 : $table.find('caption').outerHeight(true)), - scrollTop = $win.scrollTop() + stickyOffset - captionHeight, + scrollTop = ($attach.length ? $attach.offset().top : $win.scrollTop()) + stickyOffset - captionHeight, tableHeight = $table.height() - ($stickyTable.height() + ($tfoot.height() || 0)), - isVisible = (scrollTop > offset.top) && (scrollTop < offset.top + tableHeight) ? 'visible' : 'hidden'; + isVisible = (scrollTop > offset.top) && (scrollTop < offset.top + tableHeight) ? 'visible' : 'hidden', + cssSettings = { visibility : isVisible }; + if ($attach.length) { + cssSettings.top = $attach.scrollTop(); + } else { + // adjust when scrolling horizontally - fixes issue #143 + cssSettings.left = $thead.offset().left - $win.scrollLeft() - spacing; + } $stickyTable .removeClass(prefix + 'visible ' + prefix + 'hidden') .addClass(prefix + isVisible) - .css({ - // adjust when scrolling horizontally - fixes issue #143 - left : $thead.offset().left - $win.scrollLeft() - spacing, - visibility : isVisible - }); + .css(cssSettings); if (isVisible !== laststate || event.type === 'resize') { // make sure the column widths match resizeHeader(); diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css new file mode 100644 index 0000000..74e143d --- /dev/null +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css @@ -0,0 +1,139 @@ +/************* + Bootstrap 2 Theme + *************/ +/* jQuery Bootstrap 2 Theme */ +.tablesorter-bootstrap { + width: 100%; +} +.tablesorter-bootstrap .tablesorter-header, +.tablesorter-bootstrap tfoot th, +.tablesorter-bootstrap tfoot td { + font: bold 14px/20px Arial, Sans-serif; + position: relative; + padding: 8px; + margin: 0 0 18px; + list-style: none; + background-color: #FBFBFB; + background-image: -moz-linear-gradient(top, white, #efefef); + background-image: -ms-linear-gradient(top, white, #efefef); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(white), to(#efefef)); + background-image: -webkit-linear-gradient(top, white, #efefef); + background-image: -o-linear-gradient(top, white, #efefef); + background-image: linear-gradient(to bottom, white, #efefef); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#efefef', GradientType=0); + background-repeat: repeat-x; + -webkit-box-shadow: inset 0 1px 0 white; + -moz-box-shadow: inset 0 1px 0 #ffffff; + box-shadow: inset 0 1px 0 white; +} + +.tablesorter-bootstrap .tablesorter-header { + cursor: pointer; +} + +.tablesorter-bootstrap .tablesorter-header-inner { + position: relative; + padding: 4px 18px 4px 4px; +} + +/* bootstrap uses <i> for icons */ +.tablesorter-bootstrap .tablesorter-header i { + position: absolute; + right: 2px; + top: 50%; + margin-top: -7px; /* half the icon height; older IE doesn't like this */ + width: 14px; + height: 14px; + background-repeat: no-repeat; + line-height: 14px; + display: inline-block; +} +.tablesorter-bootstrap .bootstrap-icon-unsorted { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAYAAAD5YeaVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAWVJREFUeNqUUL9Lw2AUTGP8mqGlpBQkNeCSRcckEBcHq1jImMElToKuDvpHFMGhU0BQcHBwLji6CE1B4uB/INQsDi4d2jQ/fPeZxo764OV6915f7lLJ81xot9tCURXqdVEUr7IsO6ffH9Q5BlEUCaLwWxWqTcbYnaIoh0Dw4gAvcWlxq1qt9hqNxg6hUGAP+uIPUrGs0qXLer2+v/pTX6QpxLtkc2U2m53ACb8sSdIDXerSEms2m6+DweAICA4d89KGbduf9MpEVdXQ9/2LVqv1CASHjjn3iq/x1xKFfxQPqGnada1W86bT6SiO42OS3qk3KPStLMvbk8nkfjwen/LLuq6blFymMB0KdUPSGhAcOualjX6/f0bCiC7NaWGPQr0BwaFjzn0gYJqmLAiCA8/zni3LmhuGkQPBoWPOPwQeaPIqD4fDruu6L6Zp5kBw6IudchmdJAkLw3DXcZwnIPjy/FuAAQCiqqWWCAFKcwAAAABJRU5ErkJggg==); +} + +/* since bootstrap (table-striped) uses nth-child(), we just use this to add a zebra stripe color */ +.tablesorter-bootstrap tr.odd td { + background-color: #f9f9f9; +} +.tablesorter-bootstrap tbody > .odd:hover > td, +.tablesorter-bootstrap tbody > .even:hover > td { + background-color: #f5f5f5; +} +.tablesorter-bootstrap tr.even td { + background-color: #fff; +} + +/* processing icon */ +.tablesorter-bootstrap .tablesorter-processing { + background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs='); + position: absolute; + z-index: 1000; +} + +/* caption */ +caption { + background: #fff; +} + +/* filter widget */ +.tablesorter-bootstrap .tablesorter-filter-row .tablesorter-filter { + width: 98%; + height: auto; + margin: 0 auto; + padding: 4px 6px; + background-color: #fff; + color: #333; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition: height 0.1s ease; + -moz-transition: height 0.1s ease; + -o-transition: height 0.1s ease; + transition: height 0.1s ease; +} +.tablesorter-bootstrap .tablesorter-filter-row .tablesorter-filter.disabled { + background: #eee; + cursor: not-allowed; +} +.tablesorter-bootstrap .tablesorter-filter-row td { + background: #eee; + line-height: normal; + text-align: center; + padding: 4px 6px; + vertical-align: middle; + -webkit-transition: line-height 0.1s ease; + -moz-transition: line-height 0.1s ease; + -o-transition: line-height 0.1s ease; + transition: line-height 0.1s ease; +} +/* hidden filter row */ +.tablesorter-bootstrap .tablesorter-filter-row.hideme td { + padding: 2px; /* change this to modify the thickness of the closed border row */ + margin: 0; + line-height: 0; +} +.tablesorter-bootstrap .tablesorter-filter-row.hideme .tablesorter-filter { + height: 1px; + min-height: 0; + border: 0; + padding: 0; + margin: 0; + /* don't use visibility: hidden because it disables tabbing */ + opacity: 0; + filter: alpha(opacity=0); +} + +/* pager plugin */ +.tablesorter-bootstrap .tablesorter-pager select { + padding: 4px 6px; +} +.tablesorter-bootstrap .tablesorter-pager .pagedisplay { + border: 0; +} + +/* ajax error row */ +.tablesorter .tablesorter-errorRow td { + cursor: pointer; + background-color: #e6bf99; +} From 62757e7298b8b2b8c137af0ff3d81f5c6d149c8e Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Tue, 17 Dec 2013 20:31:05 +0100 Subject: [PATCH 012/138] * updated tablesorter to latest version (2.14.5) --- CHANGELOG.markdown | 4 ++++ README.markdown | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 15 +++++++++------ .../jquery-tablesorter/jquery.tablesorter.js | 4 ++-- .../jquery.tablesorter.widgets.js | 13 ++++++++----- 7 files changed, 26 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index 379a1a8..1237315 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -1,6 +1,10 @@ Changelog === +#### v1.9.5 (2013-12-17) + +* Upgrade tablesorter to v2.14.5 + #### v1.9.4 (2013-12-15) * Upgrade tablesorter to v2.14.4 diff --git a/README.markdown b/README.markdown index b269762..3480399 100644 --- a/README.markdown +++ b/README.markdown @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.14.4 (12/14/2013), [documentation] +Current tablesorter version: 2.14.5 (12/16/2013), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 0c1eaf5..5799def 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = "1.9.4" + VERSION = "1.9.5" end diff --git a/tablesorter b/tablesorter index 173db94..d1a9f2e 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 173db948c6bd40be403da9da4d8d873e37cd9856 +Subproject commit d1a9f2ee22de5a25794c2df2c9bcaf2fcf346f6d diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index d04b6b4..5a6f6fa 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 12/14/2013 (v2.14.4) + * updated 12/16/2013 (v2.14.5) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -30,6 +30,9 @@ dataType: 'json' }, + // set this to false if you want to block ajax loading on init + processAjaxOnInit: true, + // process ajax so that the following information is returned: // [ total_rows (number), rows (array of arrays), headers (array; optional) ] // example: @@ -112,7 +115,7 @@ tp = Math.min( p.totalPages, p.filteredPages ); if ( p.updateArrows ) { p.$container.find(p.cssFirst + ',' + p.cssPrev)[ ( dis || p.page === 0 ) ? a : r ](d); - p.$container.find(p.cssNext + ',' + p.cssLast)[ ( dis || p.page === tp - 1 ) ? a : r ](d); + p.$container.find(p.cssNext + ',' + p.cssLast)[ ( dis || p.page === tp - 1 || p.totalPages === 0 ) ? a : r ](d); } }, @@ -136,7 +139,7 @@ s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || p.output : p.output ) // {page} = one-based index; {page+#} = zero based index +/- value .replace(/\{page([\-+]\d+)?\}/gi, function(m,n){ - return p.page + (n ? parseInt(n, 10) : 1); + return p.totalPages ? p.page + (n ? parseInt(n, 10) : 1) : 0; }) // {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object) .replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m){ @@ -272,7 +275,7 @@ //ensure a zero returned row count doesn't fail the logical || rr_count = result[t ? 1 : 0]; p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count; - d = p.totalRows === 0 ? [] : result[t ? 0 : 1] || []; // row data + d = p.totalRows === 0 ? [""] : result[t ? 0 : 1] || []; // row data th = result[2]; // headers } l = d.length; @@ -290,12 +293,12 @@ tds += '</tr>'; } // add rows to first tbody - c.$tbodies.eq(0).html( tds ); + p.processAjaxOnInit ? c.$tbodies.eq(0).html( tds ) : p.processAjaxOnInit = true; } // only add new header text if the length matches if ( th && th.length === hl ) { hsh = $t.hasClass('hasStickyHeaders'); - $sh = hsh ? c.$sticky.children('thead:first').children().children() : ''; + $sh = hsh ? c.widgetOptions.$sticky.children('thead:first').children().children() : ''; $f = $t.find('tfoot tr:first').children(); // don't change td headers (may contain pager) c.$headers.filter('th').each(function(j){ diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index e0b4417..d3d7312 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.14.4 - Client-side table sorting with ease! +* TableSorter 2.14.5 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.14.4"; + ts.version = "2.14.5"; ts.parsers = []; ts.widgets = []; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 5e65293..8ac0ed9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.8+ widgets - updated 12/14/2013 (v2.14.4) +/*! tableSorter 2.8+ widgets - updated 12/16/2013 (v2.14.5) * * Column Styles * Column Filters @@ -1066,7 +1066,10 @@ ts.addWidget({ stickyHeaders_zIndex : 2 // The zIndex of the stickyHeaders, allows the user to adjust this to their needs }, format: function(table, c, wo) { - if (c.$table.hasClass('hasStickyHeaders')) { return; } + // filter widget doesn't initialize on an empty table. Fixes #449 + if ( c.$table.hasClass('hasStickyHeaders') || ($.inArray('filter', c.widgets) >= 0 && !c.$table.hasClass('hasFilters')) ) { + return; + } var $cell, $table = c.$table, $attach = $(wo.stickyHeaders_attachTo), @@ -1086,7 +1089,7 @@ ts.addWidget({ margin : 0, top : stickyOffset, left : 0, - visibility : $attach.length ? 'visible' : 'hidden', + visibility : 'hidden', zIndex : wo.stickyHeaders_zIndex ? wo.stickyHeaders_zIndex : 2 }), $stickyThead = $stickyTable.children('thead:first').addClass('tablesorter-stickyHeader ' + wo.stickyHeaders), @@ -1105,8 +1108,8 @@ ts.addWidget({ spacing = parseInt($header.eq(0).css('border-left-width'), 10) * 2; } $stickyTable.css({ - left : $attach.length ? parseInt($table.css('padding-left'), 10) + - parseInt($table.css('margin-left'), 10) + parseInt($table.css('border-left-width'), 10) : + left : $attach.length ? parseInt($attach.css('padding-left'), 10) + + parseInt($attach.css('margin-left'), 10) + parseInt($table.css('border-left-width'), 10) : $thead.offset().left - $win.scrollLeft() - spacing, width: $table.width() }); From 0f123f42986b00a9d68d067e0462feeba03a23b4 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Tue, 17 Dec 2013 20:38:17 +0100 Subject: [PATCH 013/138] * added Bootstrap 2 theme (bootstrap_2) note in README --- README.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/README.markdown b/README.markdown index 3480399..d65070d 100644 --- a/README.markdown +++ b/README.markdown @@ -62,6 +62,7 @@ Avaliable theme names: * theme.black-ice * theme.blue * theme.bootstrap +* theme.bootstrap_2 * theme.dark * theme.default * theme.dropbox From 7c9eae1f650b6e10dc1e12c1b0ab972275540977 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Wed, 19 Feb 2014 22:16:14 +0100 Subject: [PATCH 014/138] * updated tablesorter to latest version (2.15.0) * updated docs --- CHANGELOG.markdown | 5 + MIT-LICENSE | 2 +- README.markdown | 8 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 102 ++- .../jquery-tablesorter/jquery.tablesorter.js | 246 ++++--- ...ry.tablesorter.widgets-filter-formatter.js | 672 ++++++++++++------ .../jquery.tablesorter.widgets.js | 424 ++++++----- .../jquery-tablesorter/theme.black-ice.css | 1 + .../jquery-tablesorter/theme.blue.css | 1 + .../jquery-tablesorter/theme.bootstrap.css | 9 +- .../jquery-tablesorter/theme.bootstrap_2.css | 1 + .../jquery-tablesorter/theme.dark.css | 1 + .../jquery-tablesorter/theme.default.css | 1 + .../jquery-tablesorter/theme.dropbox.css | 1 + .../jquery-tablesorter/theme.green.css | 1 + .../jquery-tablesorter/theme.grey.css | 1 + .../jquery-tablesorter/theme.ice.css | 1 + .../jquery-tablesorter/theme.jui.css | 1 + 20 files changed, 955 insertions(+), 527 deletions(-) diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index 1237315..d23520f 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -1,6 +1,11 @@ Changelog === +#### v1.10.0 (2014-02-19) + +* Upgrade tablesorter to v2.15.0 +* Updated copyright year, license information and added note about Bootstrap 2 theme in Readme + #### v1.9.5 (2013-12-17) * Upgrade tablesorter to v2.14.5 diff --git a/MIT-LICENSE b/MIT-LICENSE index 59b76f9..8016b6d 100644 --- a/MIT-LICENSE +++ b/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright 2013 Jun Lin, Erik-B. Ernst +Copyright 2014 Jun Lin, Erik-B. Ernst Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.markdown b/README.markdown index d65070d..8d8a83f 100644 --- a/README.markdown +++ b/README.markdown @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.14.5 (12/16/2013), [documentation] +Current tablesorter version: 2.15.0 (2/19/2014), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. @@ -95,6 +95,10 @@ pager theme: 4. Update `README.md` and `CHANGELOG.md` +### Licensing + +* Licensed under the [MIT](http://www.opensource.org/licenses/mit-license.php) license. +* Original jquery-tablesorter code is dual licensed under the [MIT](http://www.opensource.org/licenses/mit-license.php) and [GPL](http://www.gnu.org/licenses/gpl.html) licenses (see [Mottie's fork]). + [Mottie's fork]: https://github.com/Mottie/tablesorter [documentation]: http://mottie.github.com/tablesorter/docs/index.html - diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 5799def..25c710e 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = "1.9.5" + VERSION = "1.10.0" end diff --git a/tablesorter b/tablesorter index d1a9f2e..15bf11f 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit d1a9f2ee22de5a25794c2df2c9bcaf2fcf346f6d +Subproject commit 15bf11f0d4163a01bc7ad51d5208e292549cd24f diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 5a6f6fa..20ca3ec 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 12/16/2013 (v2.14.5) + * updated 2/19/2014 (v2.15.0) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -63,6 +63,9 @@ // Save pager page & size if the storage script is loaded (requires $.tablesorter.storage in jquery.tablesorter.widgets.js) savePages: true, + + // defines custom storage key + storageKey: 'tablesorter-pager', // if true, the table will remain the same height no matter how many records are displayed. The space is made up by an empty // table row set to a height to compensate; default is false @@ -112,10 +115,12 @@ r = 'removeClass', d = p.cssDisabled, dis = !!disable, - tp = Math.min( p.totalPages, p.filteredPages ); + first = ( dis || p.page === 0 ), + tp = Math.min( p.totalPages, p.filteredPages ), + last = ( dis || (p.page === tp - 1) || p.totalPages === 0 ); if ( p.updateArrows ) { - p.$container.find(p.cssFirst + ',' + p.cssPrev)[ ( dis || p.page === 0 ) ? a : r ](d); - p.$container.find(p.cssNext + ',' + p.cssLast)[ ( dis || p.page === tp - 1 || p.totalPages === 0 ) ? a : r ](d); + p.$container.find(p.cssFirst + ',' + p.cssPrev)[ first ? a : r ](d).attr('aria-disabled', first); + p.$container.find(p.cssNext + ',' + p.cssLast)[ last ? a : r ](d).attr('aria-disabled', last); } }, @@ -167,7 +172,7 @@ c.$table.trigger('pagerComplete', p); // save pager info to storage if (p.savePages && ts.storage) { - ts.storage(table, 'tablesorter-pager', { + ts.storage(table, p.storageKey, { page : p.page, size : p.size }); @@ -235,32 +240,28 @@ // process data if ( typeof(p.ajaxProcessing) === "function" ) { // ajaxProcessing result: [ total, rows, headers ] - var i, j, hsh, $f, $sh, t, th, d, l, $err, rr_count, + var i, j, hsh, $f, $sh, t, th, d, l, rr_count, c = table.config, $t = c.$table, tds = '', result = p.ajaxProcessing(data, table) || [ 0, [] ], hl = $t.find('thead th').length; - $t.find('thead tr.' + p.cssErrorRow).remove(); // Clean up any previous error. + // Clean up any previous error. + ts.showError(table); if ( exception ) { if (c.debug) { ts.log('Ajax Error', xhr, exception); } - $err = $('<tr class="' + p.cssErrorRow + '"><td style="text-align:center;" colspan="' + hl + '">' + ( + ts.showError(table, xhr.status === 0 ? 'Not connected, verify Network' : xhr.status === 404 ? 'Requested page not found [404]' : xhr.status === 500 ? 'Internal Server Error [500]' : exception === 'parsererror' ? 'Requested JSON parse failed' : exception === 'timeout' ? 'Time out error' : exception === 'abort' ? 'Ajax Request aborted' : - 'Uncaught error: ' + xhr.statusText + ' [' + xhr.status + ']' ) + '</td></tr>') - .click(function(){ - $(this).remove(); - }) - // add error row to thead instead of tbody, or clicking on the header will result in a parser error - .appendTo( $t.find('thead:first') ); + 'Uncaught error: ' + xhr.statusText + ' [' + xhr.status + ']' ); c.$tbodies.eq(0).empty(); } else { // process ajax object @@ -293,7 +294,11 @@ tds += '</tr>'; } // add rows to first tbody - p.processAjaxOnInit ? c.$tbodies.eq(0).html( tds ) : p.processAjaxOnInit = true; + if (p.processAjaxOnInit) { + c.$tbodies.eq(0).html( tds ); + } else { + p.processAjaxOnInit = true; + } } // only add new header text if the length matches if ( th && th.length === hl ) { @@ -333,7 +338,7 @@ fixHeight(table, p); // apply widgets after table has rendered $t.trigger('applyWidgets'); - $t.trigger('update', [false, function(){ + $t.trigger('updateRows', [false, function(){ if (p.initialized) { $t.trigger('updateComplete'); $t.trigger('pagerChange', p); @@ -463,7 +468,10 @@ p.page = 0; p.size = p.totalRows; p.totalPages = 1; - $(table).addClass('pagerDisabled').find('tr.pagerSavedHeightSpacer').remove(); + $(table) + .addClass('pagerDisabled') + .removeAttr('aria-describedby') + .find('tr.pagerSavedHeightSpacer').remove(); renderTable(table, table.config.rowsCopy, p); if (table.config.debug) { ts.log('pager disabled'); @@ -471,7 +479,7 @@ } // disable size selector p.$size.add(p.$goto).each(function(){ - $(this).addClass(p.cssDisabled)[0].disabled = true; + $(this).attr('aria-disabled', 'true').addClass(p.cssDisabled)[0].disabled = true; }); }, @@ -551,24 +559,31 @@ p.initialized = false; $(table).unbind('destroy.pager sortEnd.pager filterEnd.pager enable.pager disable.pager'); if (ts.storage) { - ts.storage(table, 'tablesorter-pager', ''); + ts.storage(table, p.storageKey, ''); } }, enablePager = function(table, p, triggered){ - var pg = p.$size.removeClass(p.cssDisabled).removeAttr('disabled'); - p.$goto.removeClass(p.cssDisabled).removeAttr('disabled'); + var info, + c = table.config; + p.$size.add(p.$goto).removeClass(p.cssDisabled).removeAttr('disabled').attr('aria-disabled', 'false'); p.isDisabled = false; p.page = $.data(table, 'pagerLastPage') || p.page || 0; - p.size = $.data(table, 'pagerLastSize') || parseInt(pg.find('option[selected]').val(), 10) || p.size || 10; - pg.val(p.size); // set page size - p.totalPages = Math.ceil( Math.min( p.totalPages, p.filteredPages ) / p.size ); + p.size = $.data(table, 'pagerLastSize') || parseInt(p.$size.find('option[selected]').val(), 10) || p.size || 10; + p.$size.val(p.size); // set page size + p.totalPages = Math.ceil( Math.min( p.totalRows, p.filteredRows ) / p.size ); + // if table id exists, include page display with aria info + if ( table.id ) { + info = table.id + '_pager_info'; + p.$container.find(p.cssPageDisplay).attr('id', info); + c.$table.attr('aria-describedby', info); + } if ( triggered ) { - $(table).trigger('update'); + c.$table.trigger('updateRows'); setPageSize(table, p.size, p); hideRowsSetup(table, p); fixHeight(table, p); - if (table.config.debug) { + if (c.debug) { ts.log('pager enabled'); } } @@ -583,6 +598,8 @@ p.size = $.data(table, 'pagerLastSize') || p.size || 10; p.totalPages = Math.ceil( p.totalRows / p.size ); renderTable(table, rows, p); + // update display here in case all rows are removed + updatePageDisplay(table, p, false); } }; @@ -609,7 +626,7 @@ ts.setFilters(table, p.currentFilters, false); } if (p.savePages && ts.storage) { - t = ts.storage(table, 'tablesorter-pager') || {}; // fixes #387 + t = ts.storage(table, p.storageKey) || {}; // fixes #387 p.page = isNaN(t.page) ? p.page : t.page; p.size = ( isNaN(t.size) ? p.size : t.size ) || 10; $.data(table, 'pagerLastSize', p.size); @@ -664,6 +681,7 @@ ctrls = [ p.cssFirst, p.cssPrev, p.cssNext, p.cssLast ]; fxn = [ moveToFirstPage, moveToPrevPage, moveToNextPage, moveToLastPage ]; pager.find(ctrls.join(',')) + .attr("tabindex", 0) .unbind('click.pager') .bind('click.pager', function(e){ e.stopPropagation(); @@ -734,8 +752,34 @@ }); }; - }() -}); + }() }); + + // see #486 + ts.showError = function(table, message){ + $(table).each(function(){ + var $row, + c = this.config, + errorRow = c.pager && c.pager.cssErrorRow || c.widgetOptions.pager_css && c.widgetOptions.pager_css.errorRow || 'tablesorter-errorRow'; + if (c) { + if (typeof message === 'undefined') { + c.$table.find('thead').find(c.selectorRemove).remove(); + } else { + $row = ( /tr\>/.test(message) ? $(message) : $('<tr><td colspan="' + c.columns + '">' + message + '</td></tr>') ) + .click(function(){ + $(this).remove(); + }) + // add error row to thead instead of tbody, or clicking on the header will result in a parser error + .appendTo( c.$table.find('thead:first') ) + .addClass( errorRow + ' ' + c.selectorRemove.replace(/^[.#]/, '') ) + .attr({ + role : 'alert', + 'aria-live' : 'assertive' + }); + } + } + }); + }; + // extend plugin scope $.fn.extend({ tablesorterPager: $.tablesorterPager.construct diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index d3d7312..8be48ae 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.14.5 - Client-side table sorting with ease! +* TableSorter 2.15.0 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.14.5"; + ts.version = "2.15.0"; ts.parsers = []; ts.widgets = []; @@ -82,6 +82,7 @@ tableClass : '', cssAsc : '', cssDesc : '', + cssNone : '', cssHeader : '', cssHeaderRow : '', cssProcessing : '', // processing icon applied to header during sort/filter @@ -116,18 +117,31 @@ childRow : 'tablesorter-childRow', header : 'tablesorter-header', headerRow : 'tablesorter-headerRow', + headerIn : 'tablesorter-header-inner', icon : 'tablesorter-icon', info : 'tablesorter-infoOnly', processing : 'tablesorter-processing', sortAsc : 'tablesorter-headerAsc', - sortDesc : 'tablesorter-headerDesc' + sortDesc : 'tablesorter-headerDesc', + sortNone : 'tablesorter-headerUnSorted' + }; + + // labels applied to sortable headers for accessibility (aria) support + ts.language = { + sortAsc : 'Ascending sort applied, ', + sortDesc : 'Descending sort applied, ', + sortNone : 'No sort applied, ', + nextAsc : 'activate to apply an ascending sort', + nextDesc : 'activate to apply a descending sort', + nextNone : 'activate to remove the sort' }; /* debuging utils */ function log() { - var s = arguments.length > 1 ? Array.prototype.slice.call(arguments) : arguments[0]; + var a = arguments[0], + s = arguments.length > 1 ? Array.prototype.slice.call(arguments) : a; if (typeof console !== "undefined" && typeof console.log !== "undefined") { - console.log(s); + console[ /error/i.test(a) ? 'error' : /warn/i.test(a) ? 'warn' : 'log' ](s); } else { alert(s); } @@ -206,7 +220,7 @@ tb = c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'), rows, list, l, i, h, ch, p, time, parsersDebug = ""; if ( tb.length === 0) { - return c.debug ? log('*Empty table!* Not building a parser cache') : ''; + return c.debug ? log('Warning: *Empty table!* Not building a parser cache') : ''; } else if (c.debug) { time = new Date(); log('Detecting parsers for each column'); @@ -255,7 +269,7 @@ tc.cache = {}; // if no parsers found, return - it's an empty table. if (!parsers) { - return tc.debug ? log('*Empty table!* Not building a cache') : ''; + return tc.debug ? log('Warning: *Empty table!* Not building a cache') : ''; } if (tc.debug) { cacheTime = new Date(); @@ -314,7 +328,11 @@ c2 = c.cache, r, n, totalRows, checkCell, $bk, $tb, i, j, k, l, pos, appendTime; - if (isEmptyObject(c2)) { return; } // empty table - fixes #206/#346 + // empty table - fixes #206/#346 + if (isEmptyObject(c2)) { + // run pager appender in case the table was just emptied + return c.appender ? c.appender(table, rows) : ''; + } if (c.debug) { appendTime = new Date(); } @@ -427,7 +445,7 @@ h = c.onRenderTemplate.apply($t, [index, t]); if (h && typeof h === 'string') { t = h; } // only change t if something is returned } - $(this).html('<div class="tablesorter-header-inner">' + t + '</div>'); // faster than wrapInner + $(this).html('<div class="' + ts.css.headerIn + '">' + t + '</div>'); // faster than wrapInner if (c.onRenderHeader) { c.onRenderHeader.apply($t, [index]); } @@ -443,9 +461,12 @@ // add cell to headerList c.headerList[index] = this; // add to parent in case there are multiple rows - $t.parent().addClass(ts.css.headerRow + ' ' + c.cssHeaderRow); + $t.parent().addClass(ts.css.headerRow + ' ' + c.cssHeaderRow).attr('role', 'row'); // allow keyboard cursor to focus on element if (c.tabIndex) { $t.attr("tabindex", 0); } + }).attr({ + scope: 'col', + role : 'columnheader' }); // enable/disable sorting updateHeader(table); @@ -467,11 +488,20 @@ } function updateHeader(table) { - var s, c = table.config; + var s, $th, c = table.config; c.$headers.each(function(index, th){ + $th = $(th); s = ts.getData( th, c.headers[index], 'sorter' ) === 'false'; th.sortDisabled = s; - $(th)[ s ? 'addClass' : 'removeClass' ]('sorter-false'); + $th[ s ? 'addClass' : 'removeClass' ]('sorter-false').attr('aria-disabled', '' + s); + // aria-controls - requires table ID + if (table.id) { + if (s) { + $th.removeAttr('aria-controls'); + } else { + $th.attr('aria-controls', table.id); + } + } }); } @@ -479,11 +509,15 @@ var f, i, j, l, c = table.config, list = c.sortList, + none = ts.css.sortNone + ' ' + c.cssNone, css = [ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc], + aria = ['ascending', 'descending'], // find the footer $t = $(table).find('tfoot tr').children().removeClass(css.join(' ')); // remove all header information - c.$headers.removeClass(css.join(' ')); + c.$headers + .removeClass(css.join(' ')) + .addClass(none).attr('aria-sort', 'none'); l = list.length; for (i = 0; i < l; i++) { // direction = 2 means reset! @@ -493,7 +527,7 @@ if (f.length) { for (j = 0; j < f.length; j++) { if (!f[j].sortDisabled) { - f.eq(j).addClass(css[list[i][1]]); + f.eq(j).removeClass(none).addClass(css[list[i][1]]).attr('aria-sort', aria[list[i][1]]); // add sorted class to footer, if it exists if ($t.length) { $t.filter('[data-column="' + list[i][0] + '"]').eq(j).addClass(css[list[i][1]]); @@ -503,6 +537,15 @@ } } } + // add verbose aria labels + c.$headers.not('.sorter-false').each(function(){ + var $this = $(this), + nextSort = this.order[(this.count + 1) % (c.sortReset ? 3 : 2)], + txt = $this.text() + ': ' + + ts.language[ $this.hasClass(ts.css.sortAsc) ? 'sortAsc' : $this.hasClass(ts.css.sortDesc) ? 'sortDesc' : 'sortNone' ] + + ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ]; + $this.attr('aria-label', txt ); + }); } // automatically add col group, and column sizes if set @@ -543,9 +586,9 @@ var a, i, j, o, s, c = table.config, k = !e[c.sortMultiSortKey], - $this = $(table); + $table = $(table); // Only call sortStart if sorting is enabled - $this.trigger("sortStart", table); + $table.trigger("sortStart", table); // get current column sort order cell.count = e[c.sortResetKey] ? 2 : (cell.count + 1) % (c.sortReset ? 3 : 2); // reset all sorts on non-current column - issue #30 @@ -629,7 +672,7 @@ } } // sortBegin event triggered immediately before the sort - $this.trigger("sortBegin", table); + $table.trigger("sortBegin", table); // setTimeout needed so the processing icon shows up setTimeout(function(){ // set css for headers @@ -729,46 +772,11 @@ } } - function bindEvents(table){ + function bindMethods(table){ var c = table.config, - $this = c.$table, - j, downTime; - // apply event handling to headers - c.$headers - // http://stackoverflow.com/questions/5312849/jquery-find-self; - .find(c.selectorSort).add( c.$headers.filter(c.selectorSort) ) - .unbind('mousedown.tablesorter mouseup.tablesorter sort.tablesorter keypress.tablesorter') - .bind('mousedown.tablesorter mouseup.tablesorter sort.tablesorter keypress.tablesorter', function(e, external) { - // only recognize left clicks or enter - if ( ((e.which || e.button) !== 1 && !/sort|keypress/.test(e.type)) || (e.type === 'keypress' && e.which !== 13) ) { - return; - } - // ignore long clicks (prevents resizable widget from initializing a sort) - if (e.type === 'mouseup' && external !== true && (new Date().getTime() - downTime > 250)) { return; } - // set timer on mousedown - if (e.type === 'mousedown') { - downTime = new Date().getTime(); - return e.target.tagName === "INPUT" ? '' : !c.cancelSelection; - } - if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); } - // jQuery v1.2.6 doesn't have closest() - var $cell = /TH|TD/.test(this.tagName) ? $(this) : $(this).parents('th, td').filter(':first'), cell = $cell[0]; - if (!cell.sortDisabled) { - initSort(table, cell, e); - } - }); - if (c.cancelSelection) { - // cancel selection - c.$headers - .attr('unselectable', 'on') - .bind('selectstart', false) - .css({ - 'user-select': 'none', - 'MozUserSelect': 'none' // not needed for jQuery 1.8+ - }); - } + $table = c.$table; // apply easy methods that trigger bound events - $this + $table .unbind('sortReset update updateRows updateCell updateAll addRows sorton appendCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave '.split(' ').join('.tablesorter ')) .bind("sortReset.tablesorter", function(e){ e.stopPropagation(); @@ -782,7 +790,8 @@ ts.refreshWidgets(table, true, true); ts.restoreHeaders(table); buildHeaders(table); - bindEvents(table); + ts.bindEvents(table, c.$headers); + bindMethods(table); commonUpdate(table, resort, callback); }) .bind("update.tablesorter updateRows.tablesorter", function(e, resort, callback) { @@ -793,10 +802,10 @@ }) .bind("updateCell.tablesorter", function(e, cell, resort, callback) { e.stopPropagation(); - $this.find(c.selectorRemove).remove(); + $table.find(c.selectorRemove).remove(); // get position from the dom var l, row, icell, - $tb = $this.find('tbody'), + $tb = $table.find('tbody'), // update cache - format: function(s, table, cell, cellIndex) // no closest in jQuery v1.2.6 - tbdy = $tb.index( $(cell).closest('tbody') ),$row = $(cell).closest('tr'); tbdy = $tb.index( $(cell).parents('tbody').filter(':first') ), @@ -809,7 +818,7 @@ l = c.cache[tbdy].normalized[row].length - 1; c.cache[tbdy].row[table.config.cache[tbdy].normalized[row][l]] = $row; c.cache[tbdy].normalized[row][icell] = c.parsers[icell].format( getElementText(table, cell, icell), table, cell, icell ); - checkResort($this, resort, callback); + checkResort($table, resort, callback); } }) .bind("addRows.tablesorter", function(e, $row, resort, callback) { @@ -819,9 +828,10 @@ updateHeader(table); commonUpdate(table, resort, callback); } else { - var i, rows = $row.filter('tr').length, + var i, j, + rows = $row.filter('tr').length, dat = [], l = $row[0].cells.length, - tbdy = $this.find('tbody').index( $row.parents('tbody').filter(':first') ); + tbdy = $table.find('tbody').index( $row.parents('tbody').filter(':first') ); // fixes adding rows to an empty table - see issue #179 if (!c.parsers) { buildParserCache(table); @@ -840,20 +850,20 @@ dat = []; } // resort using current settings - checkResort($this, resort, callback); + checkResort($table, resort, callback); } }) .bind("sorton.tablesorter", function(e, list, callback, init) { var c = table.config; e.stopPropagation(); - $this.trigger("sortStart", this); + $table.trigger("sortStart", this); // update header count index updateHeaderSortCount(table, list); // set css for headers setHeadersCss(table); // fixes #346 if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); } - $this.trigger("sortBegin", this); + $table.trigger("sortBegin", this); // sort the table and append it to the dom multisort(table); appendToTable(table, init); @@ -905,11 +915,11 @@ ts.setup = function(table, c) { // if no thead or tbody, or tablesorter is already present, quit if (!table || !table.tHead || table.tBodies.length === 0 || table.hasInitialized === true) { - return c.debug ? log('stopping initialization! No table, thead, tbody or tablesorter has already been initialized') : ''; + return c.debug ? log('ERROR: stopping initialization! No table, thead, tbody or tablesorter has already been initialized') : ''; } var k = '', - $this = $(table), + $table = $(table), m = $.metadata; // initialization flag table.hasInitialized = false; @@ -931,11 +941,20 @@ // digit sort text location; keeping max+/- for backwards compatibility c.string = { 'max': 1, 'min': -1, 'max+': 1, 'max-': -1, 'zero': 0, 'none': 0, 'null': 0, 'top': true, 'bottom': false }; // add table theme class only if there isn't already one there - if (!/tablesorter\-/.test($this.attr('class'))) { + if (!/tablesorter\-/.test($table.attr('class'))) { k = (c.theme !== '' ? ' tablesorter-' + c.theme : ''); } - c.$table = $this.addClass(ts.css.table + ' ' + c.tableClass + k); - c.$tbodies = $this.children('tbody:not(.' + c.cssInfoBlock + ')'); + c.$table = $table + .addClass(ts.css.table + ' ' + c.tableClass + k) + .attr({ role : 'grid'}); + c.$tbodies = $table.children('tbody:not(.' + c.cssInfoBlock + ')').attr({ + 'aria-live' : 'polite', + 'aria-relevant' : 'all' + }); + // + if (c.$table.find('caption').length) { + c.$table.attr('aria-labelledby', 'theCaption'); + } c.widgetInit = {}; // keep a list of initialized widgets // build headers buildHeaders(table); @@ -948,27 +967,31 @@ // delayInit will delay building the cache until the user starts a sort if (!c.delayInit) { buildCache(table); } // bind all header events and methods - bindEvents(table); + ts.bindEvents(table, c.$headers); + bindMethods(table); // get sort list from jQuery data or metadata - // in jQuery < 1.4, an error occurs when calling $this.data() - if (c.supportsDataObject && typeof $this.data().sortlist !== 'undefined') { - c.sortList = $this.data().sortlist; - } else if (m && ($this.metadata() && $this.metadata().sortlist)) { - c.sortList = $this.metadata().sortlist; + // in jQuery < 1.4, an error occurs when calling $table.data() + if (c.supportsDataObject && typeof $table.data().sortlist !== 'undefined') { + c.sortList = $table.data().sortlist; + } else if (m && ($table.metadata() && $table.metadata().sortlist)) { + c.sortList = $table.metadata().sortlist; } // apply widget init code ts.applyWidget(table, true); // if user has supplied a sort list to constructor if (c.sortList.length > 0) { - $this.trigger("sorton", [c.sortList, {}, !c.initWidgets]); - } else if (c.initWidgets) { - // apply widget format - ts.applyWidget(table); + $table.trigger("sorton", [c.sortList, {}, !c.initWidgets]); + } else { + setHeadersCss(table); + if (c.initWidgets) { + // apply widget format + ts.applyWidget(table); + } } // show processesing icon if (c.showProcessing) { - $this + $table .unbind('sortBegin.tablesorter sortEnd.tablesorter') .bind('sortBegin.tablesorter sortEnd.tablesorter', function(e) { ts.isProcessing(table, e.type === 'sortBegin'); @@ -981,7 +1004,7 @@ if (c.debug) { ts.benchmark("Overall initialization time", $.data( table, 'startoveralltimer')); } - $this.trigger('tablesorter-initialized', table); + $table.trigger('tablesorter-initialized', table); if (typeof c.initialized === 'function') { c.initialized(table); } }; @@ -993,7 +1016,8 @@ // default to all headers $h = $ths || table.find('.' + ts.css.header); if (toggle) { - if (c.sortList.length > 0) { + // don't use sortList if custom $ths used + if (typeof $ths !== 'undefined' && c.sortList.length > 0) { // get headers from the sortList $h = $h.filter(function(){ // get data-column from attr to keep compatibility with jQuery 1.2.6 @@ -1009,6 +1033,7 @@ // detach tbody but save the position // don't use tbody because there are portions that look for a tbody index (updateCell) ts.processTbody = function(table, $tb, getIt){ + table = $(table)[0]; var holdr; if (getIt) { table.isProcessing = true; @@ -1026,14 +1051,55 @@ $(table)[0].config.$tbodies.empty(); }; + ts.bindEvents = function(table, $headers){ + table = $(table)[0]; + var downTime, + c = table.config; + // apply event handling to headers and/or additional headers (stickyheaders, scroller, etc) + $headers + // http://stackoverflow.com/questions/5312849/jquery-find-self; + .find(c.selectorSort).add( $headers.filter(c.selectorSort) ) + .unbind('mousedown.tablesorter mouseup.tablesorter sort.tablesorter keyup.tablesorter') + .bind('mousedown.tablesorter mouseup.tablesorter sort.tablesorter keyup.tablesorter', function(e, external) { + var cell, type = e.type; + // only recognize left clicks or enter + if ( ((e.which || e.button) !== 1 && !/sort|keyup/.test(type)) || (type === 'keyup' && e.which !== 13) ) { + return; + } + // ignore long clicks (prevents resizable widget from initializing a sort) + if (type === 'mouseup' && external !== true && (new Date().getTime() - downTime > 250)) { return; } + // set timer on mousedown + if (type === 'mousedown') { + downTime = new Date().getTime(); + return e.target.tagName === "INPUT" ? '' : !c.cancelSelection; + } + if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); } + // jQuery v1.2.6 doesn't have closest() + cell = /TH|TD/.test(this.tagName) ? this : $(this).parents('th, td')[0]; + if (!cell.sortDisabled) { + initSort(table, cell, e); + } + }); + if (c.cancelSelection) { + // cancel selection + $headers + .attr('unselectable', 'on') + .bind('selectstart', false) + .css({ + 'user-select': 'none', + 'MozUserSelect': 'none' // not needed for jQuery 1.8+ + }); + } + }; + // restore headers ts.restoreHeaders = function(table){ - var c = table.config; + var c = $(table)[0].config; // don't use c.$headers here in case header cells were swapped c.$table.find(c.selectorHeaders).each(function(i){ // only restore header cells if it is wrapped // because this is also used by the updateAll method - if ($(this).find('.tablesorter-header-inner').length){ + if ($(this).find('.' + ts.css.headerIn).length){ $(this).html( c.headerContent[i] ); } }); @@ -1055,7 +1121,7 @@ .removeData('tablesorter') .unbind('sortReset update updateAll updateRows updateCell addRows sorton appendCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress sortBegin sortEnd '.split(' ').join('.tablesorter ')); c.$headers.add($f) - .removeClass( [ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc].join(' ') ) + .removeClass( [ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone].join(' ') ) .removeAttr('data-column'); $r.find(c.selectorSort).unbind('mousedown.tablesorter mouseup.tablesorter keypress.tablesorter'); ts.restoreHeaders(table); @@ -1088,7 +1154,6 @@ if ( xD < yD ) { return -1; } if ( xD > yD ) { return 1; } } - // chunk/tokenize xN = a.replace(r.chunk, '\\0$1\\0').replace(/\\0$/, '').replace(/^\\0/, '').split('\\0'); yN = b.replace(r.chunk, '\\0$1\\0').replace(/\\0$/, '').replace(/^\\0/, '').split('\\0'); @@ -1316,7 +1381,7 @@ // remove previous widgets for (i = 0; i < l; i++){ if ( w[i] && w[i].id && (doAll || $.inArray( w[i].id, cw ) < 0) ) { - if (c.debug) { log( 'Refeshing widgets: Removing ' + w[i].id ); } + if (c.debug) { log( 'Refeshing widgets: Removing "' + w[i].id + '"' ); } // only remove widgets that have been initialized - fixes #442 if (w[i].hasOwnProperty('remove') && c.widgetInit[w[i].id]) { w[i].remove(table, c, c.widgetOptions); @@ -1423,7 +1488,7 @@ ts.addParser({ id: "currency", is: function(s) { - return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test((s || '').replace(/[,. ]/g,'')); // £$€¤¥¢ + return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test((s || '').replace(/[+\-,. ]/g,'')); // £$€¤¥¢ }, format: function(s, table) { var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ""), table); @@ -1503,8 +1568,9 @@ }, format: function(s, table, cell, cellIndex) { if (s) { - var c = table.config, ci = c.headerList[cellIndex], - format = ci.dateFormat || ts.getData( ci, c.headers[cellIndex], 'dateFormat') || c.dateFormat; + var c = table.config, + ci = c.$headers.filter('[data-column=' + cellIndex + ']:last'), + format = ci.length && ci[0].dateFormat || ts.getData( ci, c.headers[cellIndex], 'dateFormat') || c.dateFormat; s = s.replace(/\s+/g," ").replace(/[\-.,]/g, "/"); // escaped - because JSHint in Firefox was showing it as an error if (format === "mmddyyyy") { s = s.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$3/$1/$2"); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js index da8ef74..d23106e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js @@ -1,12 +1,12 @@ -/*! Filter widget formatter functions - updated 11/9/2013 (v2.13.3) - * requires: tableSorter 2.7.7+ and jQuery 1.4.3+ +/*! Filter widget formatter functions - updated 2/19/2014 (v2.15) + * requires: tableSorter 2.15+ and jQuery 1.4.3+ * * uiSpinner (jQuery UI spinner) * uiSlider (jQuery UI slider) * uiRange (jQuery UI range slider) - * uiDateCompare (jQuery UI datepicker+compare selector; 1 input) + * uiDateCompare (jQuery UI datepicker; 1 input) * uiDatepicker (jQuery UI datepicker; 2 inputs, filter range) - * html5Number (spinner+compare selector) + * html5Number (spinner) * html5Range (slider) * html5Color (color) */ @@ -14,24 +14,61 @@ /*global jQuery: false */ ;(function($){ "use strict"; -$.tablesorter = $.tablesorter || {}; -$.tablesorter.filterFormatter = { +var ts = $.tablesorter || {}, + +// compare option selector class name (jQuery selector) +compareSelect = '.compare-select', + +tsff = ts.filterFormatter = { + + addCompare: function($cell, indx, options){ + if (options.compare && $.isArray(options.compare) && options.compare.length > 1) { + var opt = '', + compareSelectClass = [ compareSelect.slice(1), ' ' + compareSelect.slice(1), '' ], + txt = options.cellText ? '<label class="' + compareSelectClass.join('-label') + indx + '">' + options.cellText + '</label>' : ''; + $.each(options.compare, function(i, c){ + opt += '<option ' + (options.selected === i ? 'selected' : '') + '>' + c + '</option>'; + }); + $cell + .wrapInner('<div class="' + compareSelectClass.join('-wrapper') + indx + '" />') + .prepend( txt + '<select class="' + compareSelectClass.join('') + indx + '" />' ) + .find('select') + .append(opt); + } + }, + + updateCompare : function($cell, $input, o) { + var val = $input.val() || '', + num = val.replace(/\s*?[><=]\s*?/g, ''), + compare = val.match(/[><=]/g) || ''; + if (o.compare) { + if ($.isArray(o.compare)){ + compare = (compare || []).join('') || o.compare[o.selected || 0]; + } + $cell.find(compareSelect).val( compare ); + } + return [ val, num ]; + }, /**********************\ jQuery UI Spinner \**********************/ uiSpinner: function($cell, indx, spinnerDef) { var o = $.extend({ - min : 0, - max : 100, - step : 1, - value : 1, + // filter formatter options delayed : true, addToggle : true, - disabled : false, exactMatch : true, - compare : '' + value : 1, + cellText : '', + compare : '', + // include ANY jQuery UI spinner options below + min : 0, + max : 100, + step : 1, + disabled : false + }, spinnerDef ), // Add a hidden input to hold the range values $input = $('<input class="filter" type="hidden">') @@ -44,22 +81,27 @@ $.tablesorter.filterFormatter = { c = $cell.closest('table')[0].config, // this function updates the hidden input and adds the current values to the header cell text - updateSpinner = function(ui) { + updateSpinner = function(ui, notrigger) { var chkd = true, state, // ui is not undefined on create - v = ui && ui.value && $.tablesorter.formatFloat((ui.value + '').replace(/[><=]/g,'')) || $cell.find('.spinner').val() || o.value; + v = ui && ui.value && ts.formatFloat((ui.value + '').replace(/[><=]/g,'')) || + $cell.find('.spinner').val() || o.value, + compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', + searchType = ui && typeof ui.delayed === 'boolean' ? ui.delayed : c.$table[0].hasInitialized ? o.delayed : true; if (o.addToggle) { chkd = $cell.find('.toggle').is(':checked'); } state = o.disabled || !chkd ? 'disable' : 'enable'; $cell.find('.filter') // add equal to the beginning, so we filter exact numbers - .val( chkd ? (o.compare ? o.compare : o.exactMatch ? '=' : '') + v : '' ) - .trigger('search', ui && typeof ui.delayed === 'boolean' ? ui.delayed : o.delayed).end() + .val( chkd ? (compare ? compare : o.exactMatch ? '=' : '') + v : '' ) + .trigger( notrigger ? '' : 'search', searchType ).end() .find('.spinner').spinner(state).val(v); // update sticky header cell if ($shcell.length) { - $shcell.find('.spinner').spinner(state).val(v); + $shcell + .find('.spinner').spinner(state).val(v).end() + .find(compareSelect).val( compare ); if (o.addToggle) { $shcell.find('.toggle')[0].checked = chkd; } @@ -78,7 +120,8 @@ $.tablesorter.filterFormatter = { if (typeof o.oldspin === 'function') { o.oldspin(event, ui); } }; if (o.addToggle) { - $('<div class="button"><input id="uispinnerbutton' + indx + '" type="checkbox" class="toggle" /><label for="uispinnerbutton' + indx + '"></label></div>') + $('<div class="button"><input id="uispinnerbutton' + indx + '" type="checkbox" class="toggle" />' + + '<label for="uispinnerbutton' + indx + '"></label></div>') .appendTo($cell) .find('.toggle') .bind('change', function(){ @@ -96,11 +139,27 @@ $.tablesorter.filterFormatter = { updateSpinner(); }); + // update spinner from hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + var val = tsff.updateCompare($cell, $input, o)[0]; + $cell.find('.spinner').val( val ); + updateSpinner({ value: val }, true); + }); + + if (o.compare) { + // add compare select + tsff.addCompare($cell, indx, o); + $cell.find(compareSelect).bind('change', function(){ + updateSpinner(); + }); + } + // has sticky headers? c.$table.bind('stickyHeadersInit', function(){ $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); if (o.addToggle) { - $('<div class="button"><input id="stickyuispinnerbutton' + indx + '" type="checkbox" class="toggle" /><label for="stickyuispinnerbutton' + indx + '"></label></div>') + $('<div class="button"><input id="stickyuispinnerbutton' + indx + '" type="checkbox" class="toggle" />' + + '<label for="stickyuispinnerbutton' + indx + '"></label></div>') .appendTo($shcell) .find('.toggle') .bind('change', function(){ @@ -117,15 +176,31 @@ $.tablesorter.filterFormatter = { $cell.find('.spinner').val( this.value ); updateSpinner(); }); + + if (o.compare) { + // add compare select + tsff.addCompare($shcell, indx, o); + $shcell.find(compareSelect).bind('change', function(){ + $cell.find(compareSelect).val( $(this).val() ); + updateSpinner(); + }); + } + }); // on reset c.$table.bind('filterReset', function(){ + if ($.isArray(o.compare)) { + $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); + } // turn off the toggle checkbox if (o.addToggle) { $cell.find('.toggle')[0].checked = false; } - updateSpinner(); + $cell.find('.spinner').spinner('value', o.value); + setTimeout(function(){ + updateSpinner(); + }, 0); }); updateSpinner(); @@ -137,16 +212,20 @@ $.tablesorter.filterFormatter = { \**********************/ uiSlider: function($cell, indx, sliderDef) { var o = $.extend({ - value : 0, - min : 0, - max : 100, - step : 1, - range : "min", + // filter formatter options delayed : true, valueToHeader : false, exactMatch : true, + cellText : '', compare : '', - allText : 'all' + allText : 'all', + // include ANY jQuery UI spinner options below + // except values, since this is a non-range setup + value : 0, + min : 0, + max : 100, + step : 1, + range : "min" }, sliderDef ), // Add a hidden input to hold the range values $input = $('<input class="filter" type="hidden">') @@ -159,11 +238,13 @@ $.tablesorter.filterFormatter = { c = $cell.closest('table')[0].config, // this function updates the hidden input and adds the current values to the header cell text - updateSlider = function(ui) { + updateSlider = function(ui, notrigger) { // ui is not undefined on create - var v = typeof ui !== "undefined" ? $.tablesorter.formatFloat((ui.value + '').replace(/[><=]/g,'')) || o.min : o.value, + var v = typeof ui !== "undefined" ? ts.formatFloat((ui.value + '').replace(/[><=]/g,'')) || o.value : o.value, val = o.compare ? v : v === o.min ? o.allText : v, - result = o.compare + val; + compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', + result = compare + val, + searchType = ui && typeof ui.delayed === 'boolean' ? ui.delayed : c.$table[0].hasInitialized ? o.delayed : true; if (o.valueToHeader) { // add range indication to the header cell above! $cell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(' (' + result + ')'); @@ -174,14 +255,17 @@ $.tablesorter.filterFormatter = { // update the hidden input; // ****** ADD AN EQUAL SIGN TO THE BEGINNING! <- this makes the slide exactly match the number ****** // when the value is at the minimum, clear the hidden input so all rows will be seen + $cell.find('.filter') - .val( ( o.compare ? o.compare + v : v === o.min ? '' : (o.exactMatch ? '=' : '') + v ) ) - .trigger('search', ui && typeof ui.delayed === 'boolean' ? ui.delayed : o.delayed).end() + .val( ( compare ? compare + v : v === o.min ? '' : (o.exactMatch ? '=' : '') + v ) ) + .trigger( notrigger ? '' : 'search', searchType ).end() .find('.slider').slider('value', v); // update sticky header cell if ($shcell.length) { - $shcell.find('.slider').slider('value', v); + $shcell + .find(compareSelect).val( compare ).end() + .find('.slider').slider('value', v); if (o.valueToHeader) { $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(' (' + result + ')'); } else { @@ -213,25 +297,53 @@ $.tablesorter.filterFormatter = { .appendTo($cell) .slider(o); + // update slider from hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + var val = tsff.updateCompare($cell, $input, o)[0]; + $cell.find('.slider').slider('value', val ); + updateSlider({ value: val }, false); + }); + + if (o.compare) { + // add compare select + tsff.addCompare($cell, indx, o); + $cell.find(compareSelect).bind('change', function(){ + updateSlider({ value: $cell.find('.slider').slider('value') }); + }); + } + // on reset c.$table.bind('filterReset', function(){ - $cell.find('.slider').slider('value', o.value); - updateSlider(); + if ($.isArray(o.compare)) { + $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); + } + setTimeout(function(){ + updateSlider({ value: o.value }); + }, 0); }); // has sticky headers? c.$table.bind('stickyHeadersInit', function(){ $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); - // add a jQuery UI slider! - $('<div class="slider slider' + indx + '"/>') - .val(o.value) - .appendTo($shcell) - .slider(o) - .bind('change keyup', function(){ - $cell.find('.slider').val( this.value ); - updateSlider(); - }); + // add a jQuery UI slider! + $('<div class="slider slider' + indx + '"/>') + .val(o.value) + .appendTo($shcell) + .slider(o) + .bind('change keyup', function(){ + $cell.find('.slider').slider('value', this.value ); + updateSlider(); + }); + + if (o.compare) { + // add compare select + tsff.addCompare($shcell, indx, o); + $shcell.find(compareSelect).bind('change', function(){ + $cell.find(compareSelect).val( $(this).val() ); + updateSlider(); + }); + } }); @@ -243,34 +355,43 @@ $.tablesorter.filterFormatter = { \*************************/ uiRange: function($cell, indx, rangeDef) { var o = $.extend({ + // filter formatter options + delayed : true, + valueToHeader : false, + // include ANY jQuery UI spinner options below + // except value, since this one is range specific) values : [0, 100], min : 0, max : 100, - range : true, - delayed : true, - valueToHeader : false + range : true }, rangeDef ), // Add a hidden input to hold the range values $input = $('<input class="filter" type="hidden">') .appendTo($cell) // hidden filter update (.tsfilter) namespace trigger by filter widget .bind('change.tsfilter', function(){ - var v = this.value.split(' - '); - if (this.value === '') { v = [ o.min, o.max ]; } - if (v && v[1]) { - updateUiRange({ values: v, delay: false }); - } + getRange(); }), $shcell = [], c = $cell.closest('table')[0].config, + getRange = function(){ + var val = $input.val(), + v = val.split(' - '); + if (val === '') { v = [ o.min, o.max ]; } + if (v && v[1]) { + updateUiRange({ values: v, delay: false }, true); + } + }, + // this function updates the hidden input and adds the current values to the header cell text - updateUiRange = function(ui) { + updateUiRange = function(ui, notrigger) { // ui.values are undefined for some reason on create var val = ui && ui.values || o.values, result = val[0] + ' - ' + val[1], // make range an empty string if entire range is covered so the filter row will hide (if set) - range = val[0] === o.min && val[1] === o.max ? '' : result; + range = val[0] === o.min && val[1] === o.max ? '' : result, + searchType = ui && typeof ui.delayed === 'boolean' ? ui.delayed : c.$table[0].hasInitialized ? o.delayed : true; if (o.valueToHeader) { // add range indication to the header cell above (if not using the css method)! $cell.closest('thead').find('th[data-column=' + indx + ']').find('.currange').html(' (' + result + ')'); @@ -283,7 +404,7 @@ $.tablesorter.filterFormatter = { } // update the hidden input $cell.find('.filter').val(range) - .trigger('search', ui && typeof ui.delayed === 'boolean' ? ui.delayed : o.delayed).end() + .trigger(notrigger ? '' : 'search', searchType).end() .find('.range').slider('values', val); // update sticky header cell if ($shcell.length) { @@ -322,10 +443,17 @@ $.tablesorter.filterFormatter = { .appendTo($cell) .slider(o); + // update slider from hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + getRange(); + }); + // on reset c.$table.bind('filterReset', function(){ $cell.find('.range').slider('values', o.values); - updateUiRange(); + setTimeout(function(){ + updateUiRange(); + }, 0); }); // has sticky headers? @@ -353,15 +481,22 @@ $.tablesorter.filterFormatter = { \*************************/ uiDateCompare: function($cell, indx, defDate) { var o = $.extend({ - defaultDate : '', + // filter formatter options cellText : '', + compare : '', + endOfDay : true, + // include ANY jQuery UI spinner options below + + defaultDate : '', // ******************************** FIX THIS ******************************************* + changeMonth : true, changeYear : true, - numberOfMonths : 1, - compare : '', - compareOptions : false + numberOfMonths : 1 }, defDate), - $hdr = $cell.closest('thead').find('th[data-column=' + indx + ']'), + + $date, + // make sure we're using parsed dates in the search + $hdr = $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed'), // Add a hidden input to hold the range values $input = $('<input class="dateCompare" type="hidden">') .appendTo($cell) @@ -372,103 +507,105 @@ $.tablesorter.filterFormatter = { o.onClose(v); } }), - t, l, $shcell = [], + t, $shcell = [], c = $cell.closest('table')[0].config, // this function updates the hidden input - updateCompare = function(v) { - var date = new Date($cell.find('.date').datepicker('getDate')).getTime(); - - $cell.find('.compare').val(v); + date1Compare = function(v, notrigger) { + var date, query, + getdate = v || $date.datepicker('getDate') || '', + compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', + searchType = c.$table[0].hasInitialized ? o.delayed : true; + $date.datepicker('setDate', getdate === '' ? o.defaultDate || '' : getdate); + if (getdate === '') { notrigger = false; } + date = $date.datepicker('getDate'); + query = date ? ( o.endOfDay && /<=/.test(compare) ? date.setHours(23, 59, 59) : date.getTime() ) || '' : ''; + if (date && o.endOfDay && compare === '=') { + compare = ''; + query += ' - ' + date.setHours(23, 59, 59); + notrigger = false; + } $cell.find('.dateCompare') // add equal to the beginning, so we filter exact numbers - .val(v + date) - .trigger('search', o.delayed).end(); + .val(compare + query) + .trigger( notrigger ? '' : 'search', searchType ).end(); // update sticky header cell if ($shcell.length) { - $shcell.find('.compare').val(v); + $shcell + .find('.dateCompare').val(compare + query).end() + .find(compareSelect).val(compare); } }; - // make sure we're using parsed dates in the search - $hdr.addClass('filter-parsed'); - // Add date range picker - if (o.compareOptions) { - l = '<select class="compare">'; - for(var myOption in o.compareOptions) { - l += '<option value="' + myOption + '"'; - if (myOption === o.compare) - l += ' selected="selected"'; - l += '>' + myOption + '</option>'; - } - l += '</select>'; - $cell.append(l) - .find('.compare') - .bind('change', function(){ - updateCompare($(this).val()); - }); - } else if (o.cellText) { - l = '<label>' + o.cellText + '</label>'; - $cell.append(l); - } - - t = '<input type="text" class="date date' + indx + - '" placeholder="' + ($hdr.data('placeholder') || $hdr.attr('data-placeholder') || '') + '" />'; - $(t).appendTo($cell); + t = '<input type="text" class="date date' + indx + '" placeholder="' + + ($hdr.data('placeholder') || $hdr.attr('data-placeholder') || '') + '" />'; + $date = $(t).appendTo($cell); // add callbacks; preserve added callbacks o.oldonClose = o.onClose; o.onClose = function( selectedDate, ui ) { - var date = new Date($cell.find('.date').datepicker('getDate')).getTime() || ''; - var compare = ( $cell.find('.compare').val() || o.compare); - $cell - // update hidden input - .find('.dateCompare').val( compare + date ) - .trigger('search').end() - .find('.date') - .datepicker('setDate', selectedDate); - - // update sticky header cell - if ($shcell.length) { - $shcell.find('.date').datepicker('setDate', selectedDate); - } - + date1Compare(); if (typeof o.oldonClose === 'function') { o.oldonClose(selectedDate, ui); } }; - $cell.find('.date').datepicker(o); - - if (o.filterDate) { - $cell.find('.date').datepicker('setDate', o.filterDate); - } + $date.datepicker(o); // on reset c.$table.bind('filterReset', function(){ - $cell.find('.date').val('').datepicker('option', 'currentText', '' ); - if ($shcell.length) { - $shcell.find('.date').val('').datepicker('option', 'currentText', '' ); + if ($.isArray(o.compare)) { + $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); + } + $cell.add($shcell).find('.date').val(o.defaultDate).datepicker('setDate', ''); + setTimeout(function(){ + date1Compare(); + }, 0); + }); + + // update date compare from hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + var num, v = $input.val(); + if (/\s+-\s+/.test(v)) { + // date range found; assume an exact match on one day + $cell.find(compareSelect).val('='); + num = new Date ( Number( v.split(/\s+-\s+/)[0] ) ); + $date.datepicker( 'setDate', num ); + } else { + num = (tsff.updateCompare($cell, $input, o)[1]).toString() || ''; + // differeniate 1388556000000 from 1/1/2014 using \d{5} regex + num = num !== '' ? new Date( /\d{5}/g.test(num) ? Number(num) : num ) || '' : ''; } + $cell.add($shcell).find('.date').datepicker( 'setDate', num ); + date1Compare(num, true); }); + if (o.compare) { + // add compare select + tsff.addCompare($cell, indx, o); + $cell.find(compareSelect).bind('change', function(){ + date1Compare(); + }); + } + // has sticky headers? c.$table.bind('stickyHeadersInit', function(){ $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); - if (o.compareOptions) { - $shcell.append(l) - .find('.compare') - .bind('change', function(){ - updateCompare($(this).val()); - }); - } else if (o.cellText) { - $shcell.append(l); - } // add a jQuery datepicker! $shcell .append(t) .find('.date') .datepicker(o); + + if (o.compare) { + // add compare select + tsff.addCompare($shcell, indx, o); + $shcell.find(compareSelect).bind('change', function(){ + $cell.find(compareSelect).val( $(this).val() ); + date1Compare(); + }); + } + }); // return the hidden input so the filter widget has a reference to it @@ -480,10 +617,13 @@ $.tablesorter.filterFormatter = { \*************************/ uiDatepicker: function($cell, indx, defDate) { var o = $.extend({ - from : '', - to : '', + // filter formatter options + endOfDay : true, textFrom : 'from', textTo : 'to', + from : '', // defaultDate "from" input + to : '', // defaultDate "to" input + // include ANY jQuery UI spinner options below changeMonth : true, changeYear : true, numberOfMonths : 1 @@ -519,9 +659,11 @@ $.tablesorter.filterFormatter = { var localfrom = o.defaultDate = o.from || o.defaultDate; closeFrom = o.onClose = function( selectedDate, ui ) { - var from = new Date( $cell.find('.dateFrom').datepicker('getDate') ).getTime() || '', - to = new Date( $cell.find('.dateTo').datepicker('getDate') ).getTime() || '', - range = from ? ( to ? from + ' - ' + to : '>=' + from ) : (to ? '<=' + to : ''); + var range, + from = new Date( $cell.find('.dateFrom').datepicker('getDate') ).getTime() || '', + to = $cell.find('.dateTo').datepicker('getDate') || ''; + to = to ? ( o.endOfDay ? to.setHours(23, 59, 59) : to.getTime() ) || '' : ''; + range = from ? ( to ? from + ' - ' + to : '>=' + from ) : (to ? '<=' + to : ''); $cell .find('.dateRange').val(range) .trigger('search').end() @@ -541,9 +683,11 @@ $.tablesorter.filterFormatter = { o.defaultDate = o.to || '+7d'; // set to date +7 days from today (if not defined) closeTo = o.onClose = function( selectedDate, ui ) { - var from = new Date( $cell.find('.dateFrom').datepicker('getDate') ).getTime() || '', - to = new Date( $cell.find('.dateTo').datepicker('getDate') ).getTime() || '', - range = from ? ( to ? from + ' - ' + to : '>=' + from ) : (to ? '<=' + to : ''); + var range, + from = new Date( $cell.find('.dateFrom').datepicker('getDate') ).getTime() || '', + to = $cell.find('.dateTo').datepicker('getDate') || ''; + to = to ? ( o.endOfDay ? to.setHours(23, 59, 59) : to.getTime() ) || '' : ''; + range = from ? ( to ? from + ' - ' + to : '>=' + from ) : (to ? '<=' + to : ''); $cell .find('.dateRange').val(range) .trigger('search').end() @@ -560,6 +704,29 @@ $.tablesorter.filterFormatter = { }; $cell.find('.dateTo').datepicker(o); + // update date compare from hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + var val = $input.val() || '', + from = '', + to = ''; + + // date range + if (/\s+-\s+/.test(val)){ + val = val.split(/\s+-\s+/) || []; + from = val[0] || ''; + to = val[1] || ''; + } else if (/>=/.test(val)) { + // greater than date (to date empty) + from = new Date(Number( val.replace(/>=/, '') )) || ''; + } else if (/<=/.test(val)) { + // less than date (from date empty) + to = new Date(Number( val.replace(/<=/, '') )) || ''; + } + $cell.add($shcell).find('.dateFrom').datepicker('setDate', from); + $cell.add($shcell).find('.dateTo').datepicker('setDate', to); + closeTo(to); + }); + // has sticky headers? c.$table.bind('stickyHeadersInit', function(){ $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); @@ -576,10 +743,8 @@ $.tablesorter.filterFormatter = { // on reset $cell.closest('table').bind('filterReset', function(){ - $cell.find('.dateFrom, .dateTo').val('').datepicker('option', 'currentText', '' ); - if ($shcell.length) { - $shcell.find('.dateFrom, .dateTo').val('').datepicker('option', 'currentText', '' ); - } + $cell.add($shcell).find('.dateFrom').val('').datepicker('setDate', o.from ); + $cell.add($shcell).find('.dateTo').val('').datepicker('setDate', o.to ); }); // return the hidden input so the filter widget has a reference to it @@ -597,41 +762,30 @@ $.tablesorter.filterFormatter = { step : 1, delayed : true, disabled : false, - addToggle : true, - exactMatch : true, + addToggle : false, + exactMatch : false, + cellText : '', compare : '', - compareOptions : false, skipTest: false }, def5Num), + $input, // test browser for HTML5 range support $number = $('<input type="number" style="visibility:hidden;" value="test">').appendTo($cell), // test if HTML5 number is supported - from Modernizr numberSupported = o.skipTest || $number.attr('type') === 'number' && $number.val() !== 'test', - l, $shcell = [], + $shcell = [], c = $cell.closest('table')[0].config, - updateCompare = function(v) { - var number = $cell.find('.number').val(); - - $cell.find('.compare').val(v); - $cell.find('input[type=hidden]') - // add equal to the beginning, so we filter exact numbers - .val(v + number) - .trigger('search', o.delayed).end(); - // update sticky header cell - if ($shcell.length) { - $shcell.find('.compare').val(v); - } - }, - - updateNumber = function(v, delayed){ - var chkd = o.addToggle ? $cell.find('.toggle').is(':checked') : true; - var compare = ( $cell.find('.compare').val() || o.compare); - $cell.find('input[type=hidden]') + updateNumber = function(delayed, notrigger){ + var chkd = o.addToggle ? $cell.find('.toggle').is(':checked') : true, + v = $cell.find('.number').val(), + compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', + searchType = c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) : true; + $input // add equal to the beginning, so we filter exact numbers .val( !o.addToggle || chkd ? (compare ? compare : o.exactMatch ? '=' : '') + v : '' ) - .trigger('search', delayed ? delayed : o.delayed).end() + .trigger( notrigger ? '' : 'search', searchType ).end() .find('.number').val(v); if ($cell.find('.number').length) { $cell.find('.number')[0].disabled = (o.disabled || !chkd); @@ -639,6 +793,7 @@ $.tablesorter.filterFormatter = { // update sticky header cell if ($shcell.length) { $shcell.find('.number').val(v)[0].disabled = (o.disabled || !chkd); + $shcell.find(compareSelect).val(compare); if (o.addToggle) { $shcell.find('.toggle')[0].checked = chkd; } @@ -647,41 +802,23 @@ $.tablesorter.filterFormatter = { $number.remove(); if (numberSupported) { - l = o.addToggle ? '<div class="button"><input id="html5button' + indx + '" type="checkbox" class="toggle" /><label for="html5button' + indx + '"></label></div>' : ''; - } - - if (o.compareOptions) { - l = '<select class="compare">'; - for(var myOption in o.compareOptions) { - l += '<option value="' + myOption + '"'; - if (myOption === o.compare) - l += ' selected="selected"'; - l += '>' + myOption + '</option>'; - } - l += '</select>'; - $cell.append(l) - .find('.compare') - .bind('change', function(){ - updateCompare($(this).val()); - }); - } else { - if (l) - $cell.append(l); - } - - if (numberSupported) { - t = '<input class="number" type="number" min="' + o.min + '" max="' + o.max + '" value="' + + t = o.addToggle ? '<div class="button"><input id="html5button' + indx + '" type="checkbox" class="toggle" />' + + '<label for="html5button' + indx + '"></label></div>' : ''; + t += '<input class="number" type="number" min="' + o.min + '" max="' + o.max + '" value="' + o.value + '" step="' + o.step + '" />'; // add HTML5 number (spinner) $cell .append(t + '<input type="hidden" />') .find('.toggle, .number').bind('change', function(){ - updateNumber( $cell.find('.number').val() ); + updateNumber(); }) .closest('thead').find('th[data-column=' + indx + ']') .addClass('filter-parsed') // get exact numbers from column // on reset .closest('table').bind('filterReset', function(){ + if ($.isArray(o.compare)) { + $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); + } // turn off the toggle checkbox if (o.addToggle) { $cell.find('.toggle')[0].checked = false; @@ -689,38 +826,54 @@ $.tablesorter.filterFormatter = { $shcell.find('.toggle')[0].checked = false; } } - updateNumber( $cell.find('.number').val() ); + $cell.find('.number').val( o.value ); + setTimeout(function(){ + updateNumber(); + }, 0); }); + $input = $cell.find('input[type=hidden]').bind('change', function(){ + $cell.find('.number').val( this.value ); + updateNumber(); + }); - // hidden filter update (.tsfilter) namespace trigger by filter widget - // FIXME TheSin, Not sure why but this breaks updates - // Commenting out till it's fixed. - //$cell.find('input[type=hidden]').bind('change.tsfilter', function(){ - // updateNumber( this.value ); - //}); + // update slider from hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + var val = tsff.updateCompare($cell, $input, o)[0] || o.value; + $cell.find('.number').val( ((val || '') + '').replace(/[><=]/g,'') ); + updateNumber(false, true); + }); + + if (o.compare) { + // add compare select + tsff.addCompare($cell, indx, o); + $cell.find(compareSelect).bind('change', function(){ + updateNumber(); + }); + } // has sticky headers? c.$table.bind('stickyHeadersInit', function(){ $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); - if (o.compareOptions) { - $shcell.append(l) - .find('.compare') - .bind('change', function(){ - updateCompare($(this).val()); - }); - } else { - $shcell.append(l); - } - $shcell .append(t) .find('.toggle, .number').bind('change', function(){ - updateNumber( $shcell.find('.number').val() ); + $cell.find('.number').val( $(this).val() ); + updateNumber(); }); - updateNumber( $cell.find('.number').val() ); + + if (o.compare) { + // add compare select + tsff.addCompare($shcell, indx, o); + $shcell.find(compareSelect).bind('change', function(){ + $cell.find(compareSelect).val( $(this).val() ); + updateNumber(); + }); + } + + updateNumber(); }); - updateNumber( $cell.find('.number').val() ); + updateNumber(); } @@ -739,11 +892,13 @@ $.tablesorter.filterFormatter = { delayed : true, valueToHeader : true, exactMatch : true, + cellText : '', compare : '', allText : 'all', skipTest : false }, def5Range), + $input, // test browser for HTML5 range support $range = $('<input type="range" style="visibility:hidden;" value="test">').appendTo($cell), // test if HTML5 range is supported - from Modernizr (but I left out the method to detect in Safari 2-4) @@ -752,21 +907,26 @@ $.tablesorter.filterFormatter = { $shcell = [], c = $cell.closest('table')[0].config, - updateRange = function(v, delayed){ + updateRange = function(v, delayed, notrigger){ /*jshint eqeqeq:false */ - v = (v + '').replace(/[<>=]/g,'') || o.min; // hidden input changes may include compare symbols - var t = ' (' + (o.compare ? o.compare + v : v == o.min ? o.allText : v) + ')'; + // hidden input changes may include compare symbols + v = ( typeof v === "undefined" ? $input.val() : v ).toString().replace(/[<>=]/g,'') || o.value; + var compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', + t = ' (' + (compare ? compare + v : v == o.min ? o.allText : v) + ')', + searchType = c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) : true; $cell.find('input[type=hidden]') // add equal to the beginning, so we filter exact numbers - .val( ( o.compare ? o.compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ) ) + .val( ( compare ? compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ) ) //( val == o.min ? '' : val + (o.exactMatch ? '=' : '')) - .trigger('search', delayed ? delayed : o.delayed).end() + .trigger( notrigger ? '' : 'search', searchType ).end() .find('.range').val(v); // or add current value to the header cell, if desired $cell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(t); // update sticky header cell if ($shcell.length) { - $shcell.find('.range').val(v); + $shcell + .find('.range').val(v).end() + .find(compareSelect).val( compare ); $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(t); } }; @@ -780,22 +940,37 @@ $.tablesorter.filterFormatter = { .addClass('filter-parsed') // get exact numbers from column // add span to header for the current slider value .find('.tablesorter-header-inner').append('<span class="curvalue" />'); - - $cell.find('.range').bind('change', function(){ - updateRange( this.value ); - }); - // hidden filter update (.tsfilter) namespace trigger by filter widget - $cell.find('input[type=hidden]').bind('change.tsfilter', function(){ + $input = $cell.find('input[type=hidden]').bind('change.tsfilter', function(){ /*jshint eqeqeq:false */ - var v = this.value; + var v = this.value, + compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || ''; if (v !== this.lastValue) { - this.lastValue = ( o.compare ? o.compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ); + this.lastValue = ( compare ? compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ); this.value = this.lastValue; updateRange( v ); } }); + $cell.find('.range').bind('change', function(){ + updateRange( this.value ); + }); + + // update spinner from hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + var val = tsff.updateCompare($cell, $input, o)[0]; + $cell.find('.range').val( val ); + updateRange(val, false, true); + }); + + if (o.compare) { + // add compare select + tsff.addCompare($cell, indx, o); + $cell.find(compareSelect).bind('change', function(){ + updateRange(); + }); + } + // has sticky headers? c.$table.bind('stickyHeadersInit', function(){ $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); @@ -804,16 +979,29 @@ $.tablesorter.filterFormatter = { .find('.range').bind('change', function(){ updateRange( $shcell.find('.range').val() ); }); - updateRange( $cell.find('.range').val() ); + updateRange(); + + if (o.compare) { + // add compare select + tsff.addCompare($shcell, indx, o); + $shcell.find(compareSelect).bind('change', function(){ + $cell.find(compareSelect).val( $(this).val() ); + updateRange(); + }); + } + }); // on reset $cell.closest('table').bind('filterReset', function(){ - // just turn off the colorpicker - updateRange(o.value); + if ($.isArray(o.compare)) { + $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); + } + setTimeout(function(){ + updateRange(o.value, false, true); + }, 0); }); - - updateRange( $cell.find('.range').val() ); + updateRange(); } @@ -832,6 +1020,7 @@ $.tablesorter.filterFormatter = { valueToHeader : false, skipTest : false }, defColor), + $input, // Add a hidden input to hold the range values $color = $('<input type="color" style="visibility:hidden;" value="test">').appendTo($cell), // test if HTML5 color is supported - from Modernizr @@ -839,8 +1028,8 @@ $.tablesorter.filterFormatter = { $shcell = [], c = $cell.closest('table')[0].config, - updateColor = function(v){ - v = v || o.value; + updateColor = function(v, notrigger){ + v = ( typeof v === "undefined" ? $input.val() : v ).toString().replace('=','') || o.value; var chkd = true, t = ' (' + v + ')'; if (o.addToggle) { @@ -850,9 +1039,9 @@ $.tablesorter.filterFormatter = { $cell.find('.colorpicker').val(v)[0].disabled = (o.disabled || !chkd); } - $cell.find('input[type=hidden]') + $input .val( chkd ? v + (o.exactMatch ? '=' : '') : '' ) - .trigger('search'); + .trigger( !c.$table[0].hasInitialized || notrigger ? '' : 'search' ); if (o.valueToHeader) { // add current color to the header cell $cell.closest('thead').find('th[data-column=' + indx + ']').find('.curcolor').html(t); @@ -879,13 +1068,14 @@ $.tablesorter.filterFormatter = { $color.remove(); if (colorSupported) { + t = '' + indx + Math.round(Math.random() * 100); // add HTML5 color picker - t = '<div class="color-controls-wrapper">'; - t += o.addToggle ? '<div class="button"><input id="colorbutton' + indx + '" type="checkbox" class="toggle" /><label for="colorbutton' + indx + '"></label></div>' : ''; - t += '<input type="hidden"><input class="colorpicker" type="color" />'; - t += (o.valueToHeader ? '' : '<span class="currentColor">(#000000)</span>') + '</div>'; + t = '<div class="color-controls-wrapper">' + + (o.addToggle ? '<div class="button"><input id="colorbutton' + t + '" type="checkbox" class="toggle" /><label for="colorbutton' + + t + '"></label></div>' : '') + + '<input type="hidden"><input class="colorpicker" type="color" />' + + (o.valueToHeader ? '' : '<span class="currentColor">(#000000)</span>') + '</div>'; $cell.html(t); - // add span to header for the current color value - only works if the line in the updateColor() function is also un-commented out if (o.valueToHeader) { $cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="curcolor" />'); @@ -896,15 +1086,27 @@ $.tablesorter.filterFormatter = { }); // hidden filter update (.tsfilter) namespace trigger by filter widget - $cell.find('input[type=hidden]').bind('change.tsfilter', function(){ + $input = $cell.find('input[type=hidden]').bind('change.tsfilter', function(){ updateColor( this.value ); }); + // update slider from hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + updateColor( $input.val(), true ); + }); + // on reset $cell.closest('table').bind('filterReset', function(){ // just turn off the colorpicker - $cell.find('.toggle')[0].checked = false; - updateColor( $cell.find('.colorpicker').val() ); + if (o.addToggle) { + $cell.find('.toggle')[0].checked = false; + } + // delay needed because default color needs to be set in the filter + // there is no compare option here, so if addToggle = false, + // default color is #000000 (even with no value set) + setTimeout(function(){ + updateColor(); + }, 0); }); // has sticky headers? diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 8ac0ed9..99ce215 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.8+ widgets - updated 12/16/2013 (v2.14.5) +/*! tableSorter 2.8+ widgets - updated 2/19/2014 (v2.15.0) * * Column Styles * Column Filters @@ -49,6 +49,16 @@ ts.themes = { } }; +$.extend(ts.css, { + filterRow : 'tablesorter-filter-row', // filter + filter : 'tablesorter-filter', + wrapper : 'tablesorter-wrapper', // ui theme & resizable + resizer : 'tablesorter-resizer', // resizable + grip : 'tablesorter-resizer-grip', + sticky : 'tablesorter-stickyHeader', // stickyHeader + stickyVis : 'tablesorter-sticky-visible' +}); + // *** Store data in local storage, with a cookie fallback *** /* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json) if you need it, then include https://github.com/douglascrockford/JSON-js @@ -200,16 +210,16 @@ ts.addWidget({ // toggleClass with switch added in jQuery 1.3 $(this)[ event.type === 'mouseenter' ? 'addClass' : 'removeClass' ](themes.hover); }); - if (!$headers.find('.tablesorter-wrapper').length) { + if (!$headers.find('.' + ts.css.wrapper).length) { // Firefox needs this inner div to position the resizer correctly - $headers.wrapInner('<div class="tablesorter-wrapper" style="position:relative;height:100%;width:100%"></div>'); + $headers.wrapInner('<div class="' + ts.css.wrapper + '" style="position:relative;height:100%;width:100%"></div>'); } if (c.cssIcon) { // if c.cssIcon is '', then no <i> is added to the header $headers.find('.' + ts.css.icon).addClass(themes.icons); } if ($table.hasClass('hasFilters')) { - $headers.find('.tablesorter-filter-row').addClass(themes.filterRow); + $headers.find('.' + ts.css.filterRow).addClass(themes.filterRow); } } $.each($headers, function() { @@ -218,7 +228,7 @@ ts.addWidget({ if (this.sortDisabled) { // no sort arrows for disabled columns! $header.removeClass(remove); - $icon.removeClass(remove + ' tablesorter-icon ' + themes.icons); + $icon.removeClass(remove + ' ' + themes.icons); } else { classes = ($header.hasClass(ts.css.sortAsc)) ? themes.sortAsc : @@ -244,9 +254,9 @@ ts.addWidget({ $headers .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') // remove hover .removeClass(themes.hover + ' ' + remove + ' ' + themes.active) - .find('.tablesorter-filter-row') + .find('.' + ts.css.filterRow) .removeClass(themes.filterRow); - $headers.find('.tablesorter-icon').removeClass(themes.icons); + $headers.find('.' + ts.css.icon).removeClass(themes.icons); } }); @@ -338,13 +348,14 @@ ts.addWidget({ id: "filter", priority: 50, options : { - filter_anyMatch : false, // if true overrides default find rows behaviours and if any column matches query it returns that row filter_childRows : false, // if true, filter includes child row content in the search filter_columnFilters : true, // if true, a filter will be added to the top of each table column filter_cssFilter : '', // css class name added to the filter row & each input in the row (tablesorter-filter is ALWAYS added) + filter_external : '', // jQuery selector string (or jQuery object) of external filters filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin filter_formatter : null, // add custom filter elements to the filter row filter_functions : null, // add custom filter functions using this option + filter_hideEmpty : true, // hide filter row when table is empty filter_hideFilters : false, // collapse filter row when mouse leaves the area filter_ignoreCase : true, // if true, make all searches case-insensitive filter_liveSearch : true, // if true, search column content while the user types (with a delay) @@ -359,9 +370,7 @@ ts.addWidget({ }, format: function(table, c, wo) { if (!c.$table.hasClass('hasFilters')) { - if (c.parsers || !c.parsers && wo.filter_serversideFiltering) { - ts.filter.init(table, c, wo); - } + ts.filter.init(table, c, wo); } }, remove: function(table, c, wo) { @@ -372,7 +381,7 @@ ts.addWidget({ .removeClass('hasFilters') // add .tsfilter namespace to all BUT search .unbind('addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join('.tsfilter ')) - .find('.tablesorter-filter-row').remove(); + .find('.' + ts.css.filterRow).remove(); for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody $tbody.children().removeClass(wo.filter_filteredRow).show(); @@ -419,23 +428,6 @@ ts.filter = { } return null; }, - // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric - exact: function( filter, iFilter, exact, iExact ) { - /*jshint eqeqeq:false */ - if (ts.filter.regex.exact.test(iFilter)) { - return iFilter.replace(ts.filter.regex.exact, '') == iExact; - } - return null; - }, - // Look for a not match - notMatch: function( filter, iFilter, exact, iExact, cached, index, table, wo ) { - if ( /^\!/.test(iFilter) ) { - iFilter = iFilter.replace('!', ''); - var indx = iExact.search( $.trim(iFilter) ); - return iFilter === '' ? true : !(wo.filter_startsWith ? indx === 0 : indx >= 0); - } - return null; - }, // Look for operators >, >=, < or <= operators: function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed ) { if ( /^[<>]=?/.test(iFilter) ) { @@ -462,6 +454,24 @@ ts.filter = { } return null; }, + // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric + exact: function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed, rowArray ) { + /*jshint eqeqeq:false */ + if (ts.filter.regex.exact.test(iFilter)) { + var fltr = iFilter.replace(ts.filter.regex.exact, ''); + return rowArray ? $.inArray(fltr, rowArray) >= 0 : fltr == iExact; + } + return null; + }, + // Look for a not match + notMatch: function( filter, iFilter, exact, iExact, cached, index, table, wo ) { + if ( /^\!/.test(iFilter) ) { + iFilter = iFilter.replace('!', ''); + var indx = iExact.search( $.trim(iFilter) ); + return iFilter === '' ? true : !(wo.filter_startsWith ? indx === 0 : indx >= 0); + } + return null; + }, // Look for an AND or && operator (logical and) and : function( filter, iFilter, exact, iExact ) { if ( /\s+(AND|&&)\s+/g.test(filter) ) { @@ -500,13 +510,13 @@ ts.filter = { return null; }, // Look for wild card: ? = single, * = multiple, or | = logical OR - wild : function( filter, iFilter, exact, iExact, cached, index, table ) { - if ( /[\?|\*]/.test(iFilter) || /\s+OR\s+/.test(filter) ) { + wild : function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed, rowArray ) { + if ( /[\?|\*]/.test(iFilter) || /\s+OR\s+/i.test(filter) ) { var c = table.config, query = iFilter.replace(/\s+OR\s+/gi,"|"); // look for an exact match with the "or" unless the "filter-match" class is found if (!c.$headers.filter('[data-column="' + index + '"]:last').hasClass('filter-match') && /\|/.test(query)) { - query = '^(' + query + ')$'; + query = $.isArray(rowArray) ? '(' + query + ')' : '^(' + query + ')$'; } return new RegExp( query.replace(/\?/g, '\\S{1}').replace(/\*/g, '\\S*') ).test(iExact); } @@ -549,7 +559,8 @@ ts.filter = { } c.$table.bind('addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join('.tsfilter '), function(event, filter) { - if ( !/(search|filterReset|filterEnd)/.test(event.type) ) { + c.$table.find('.' + ts.css.filterRow).toggle( !(wo.filter_hideEmpty && $.isEmptyObject(c.cache)) ); // fixes #450 + if ( !/(search|filter)/.test(event.type) ) { event.stopPropagation(); ts.filter.buildDefault(table, true); } @@ -560,15 +571,26 @@ ts.filter = { } else { // send false argument to force a new search; otherwise if the filter hasn't changed, it will return filter = event.type === 'search' ? filter : event.type === 'updateComplete' ? c.$table.data('lastSearch') : ''; - ts.filter.searching(table, filter); + if (/(update|add)/.test(event.type)) { + // force a new search since content has changed + c.lastCombinedFilter = null; + } + // pass true (dontSkip) to prevent the tablesorter.setFilters function from skipping the first input + // ensures all inputs are updated when a search is triggered on the table $('table').trigger('search', [...]); + ts.filter.searching(table, filter, true); } return false; }); - ts.filter.bindSearch( table, c.$table.find('input.tablesorter-filter') ); + ts.filter.bindSearch( table, c.$table.find('input.' + ts.css.filter), true ); + if (wo.filter_external) { + ts.filter.bindSearch( table, wo.filter_external ); + } // reset button/link if (wo.filter_reset) { - $(document).delegate(wo.filter_reset, 'click.tsfilter', function() { + $(document) + .undelegate(wo.filter_reset, 'click.tsfilter') + .delegate(wo.filter_reset, 'click.tsfilter', function() { // trigger a reset event, so other functions (filterFormatter) know when to reset c.$table.trigger('filterReset'); }); @@ -590,7 +612,7 @@ ts.filter = { options += '<option value="' + string + '">' + string + '</option>'; } } - c.$table.find('thead').find('select.tablesorter-filter[data-column="' + column + '"]').append(options); + c.$table.find('thead').find('select.' + ts.css.filter + '[data-column="' + column + '"]').append(options); } } } @@ -599,8 +621,8 @@ ts.filter = { // it would append the same options twice. ts.filter.buildDefault(table, true); - c.$table.find('select.tablesorter-filter').bind('change search', function(event, filter) { - ts.filter.checkFilters(table, filter); + c.$table.find('select.' + ts.css.filter).bind('change search', function(event, filter) { + ts.filter.checkFilters(table, filter, true); }); if (wo.filter_hideFilters) { @@ -627,36 +649,31 @@ ts.filter = { if (filters.length) { ts.setFilters(table, filters, true); } + c.$table.trigger('filterFomatterUpdate'); ts.filter.checkFilters(table, filters); }); // filter widget initialized - wo.filter_Initialized = true; + wo.filter_initialized = true; c.$table.trigger('filterInit'); }, setDefaults: function(table, c, wo) { - var indx, isArray, - filters = [], - columns = c.columns; + var isArray, saved, + // get current (default) filters + filters = ts.getFilters(table); if (wo.filter_saveFilters && ts.storage) { - filters = ts.storage( table, 'tablesorter-filters' ) || []; - isArray = $.isArray(filters); - // make sure we're not just saving an empty array - if (isArray && filters.join('') === '' || !isArray ) { filters = []; } - } - // if not filters saved, then check default settings - if (!filters.length) { - for (indx = 0; indx < columns; indx++) { - filters[indx] = c.$headers.filter('[data-column="' + indx + '"]:last').attr(wo.filter_defaultAttrib) || filters[indx]; - } + saved = ts.storage( table, 'tablesorter-filters' ) || []; + isArray = $.isArray(saved); + // make sure we're not just getting an empty array + if ( !(isArray && saved.join('') === '' || !isArray) ) { filters = saved; } } - $(table).data('lastSearch', filters); + c.$table.data('lastSearch', filters); return filters; }, buildRow: function(table, c, wo) { - var column, $header, buildSelect, disabled, + var column, $header, buildSelect, disabled, name, // c.columns defined in computeThIndexes() columns = c.columns, - buildFilter = '<tr class="tablesorter-filter-row">'; + buildFilter = '<tr class="' + ts.css.filterRow + '">'; for (column = 0; column < columns; column++) { buildFilter += '<td></td>'; } @@ -699,19 +716,41 @@ ts.filter = { } } if (buildFilter) { - buildFilter.addClass('tablesorter-filter ' + wo.filter_cssFilter).attr('data-column', column); + // add filter class name + name = ( $.isArray(wo.filter_cssFilter) ? + (typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '') : + wo.filter_cssFilter ) || ''; + buildFilter.addClass( ts.css.filter + ' ' + name ).attr('data-column', column); if (disabled) { buildFilter.addClass('disabled')[0].disabled = true; // disabled! } } } }, - bindSearch: function(table, $el) { + bindSearch: function(table, $el, internal) { table = $(table)[0]; - var external, wo = table.config.widgetOptions; - $el.unbind('keyup search filterReset') - .bind('keyup search', function(event, filter) { - var $this = $(this); + $el = $($el); // allow passing a selector string + if (!$el.length) { return; } + var c = table.config, + wo = c.widgetOptions, + $ext = wo.filter_$externalFilters; + if (internal !== true) { + // save anyMatch element + wo.filter_$anyMatch = $el.filter('[data-column="all"]'); + if ($ext && $ext.length) { + wo.filter_$externalFilters = wo.filter_$externalFilters.add( $el ); + } else { + wo.filter_$externalFilters = $el; + } + // update values (external filters added after table initialization) + ts.setFilters(table, c.$table.data('lastSearch') || [], internal === false); + } + $el + .data('lastSearchTime', new Date().getTime()) + .unbind('keyup search change') + // include change for select - fixes #473 + .bind('keyup search change', function(event, filters) { + $(this).data('lastSearchTime', new Date().getTime()); // emulate what webkit does.... escape clears the filter if (event.which === 27) { this.value = ''; @@ -721,23 +760,14 @@ ts.filter = { ( event.which >= 37 && event.which <= 40 ) || (event.which !== 13 && wo.filter_liveSearch === false) ) ) ) { return; } - // external searches won't have a filter parameter, so grab the value - if ($this.hasClass('tablesorter-filter') && !$this.hasClass('tablesorter-external-filter')) { - external = filter; - } else { - external = []; - $el.each(function(){ - // target the appropriate column if the external input has a data-column attribute - external[ $(this).data('column') || 0 ] = $(this).val(); - }); - } - ts.filter.searching(table, filter, external); - }) - .bind('filterReset', function(){ + // true flag in getFilters forces obtaining the latest values + ts.filter.searching( table, filters || ts.getFilters( table, true ), true ); + }); + c.$table.bind('filterReset', function(){ $el.val(''); }); }, - checkFilters: function(table, filter) { + checkFilters: function(table, filter, dontSkip) { var c = table.config, wo = c.widgetOptions, filterArray = $.isArray(filter), @@ -745,11 +775,11 @@ ts.filter = { combinedFilters = (filters || []).join(''); // combined filter values // add filter array back into inputs if (filterArray) { - ts.setFilters( table, filters ); + ts.setFilters( table, filters, false, dontSkip !== true ); } if (wo.filter_hideFilters) { // show/hide filter row as needed - c.$table.find('.tablesorter-filter-row').trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); + c.$table.find('.' + ts.css.filterRow).trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons @@ -774,7 +804,7 @@ ts.filter = { hideFilters: function(table, c) { var $filterRow, $filterRow2, timer; c.$table - .find('.tablesorter-filter-row') + .find('.' + ts.css.filterRow) .addClass('hideme') .bind('mouseenter mouseleave', function(e) { // save event object - http://bugs.jquery.com/ticket/12140 @@ -810,9 +840,10 @@ ts.filter = { }, findRows: function(table, filters, combinedFilters) { if (table.config.lastCombinedFilter === combinedFilters) { return; } - var cached, len, $rows, rowIndex, tbodyIndex, $tbody, $cells, columnIndex, + var cached, len, $rows, cacheIndex, rowIndex, tbodyIndex, $tbody, $cells, columnIndex, childRow, childRowText, exact, iExact, iFilter, lastSearch, matches, result, searchFiltered, filterMatched, showRow, time, + anyMatch, iAnyMatch, rowArray, rowText, iRowText, rowCache, c = table.config, wo = c.widgetOptions, columns = c.columns, @@ -821,19 +852,19 @@ ts.filter = { anyMatchNotAllowedTypes = [ 'range', 'notMatch', 'operators' ], // parse columns after formatter, in case the class is added at that point parsed = c.$headers.map(function(columnIndex) { - return (ts.getData) ? + return c.parsers[columnIndex].parsed || ( ts.getData ? ts.getData(c.$headers.filter('[data-column="' + columnIndex + '"]:last'), c.headers[columnIndex], 'filter') === 'parsed' : - $(this).hasClass('filter-parsed'); + $(this).hasClass('filter-parsed') ); }).get(); if (c.debug) { time = new Date(); } for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { if ($tbodies.eq(tbodyIndex).hasClass(ts.css.info)) { continue; } // ignore info blocks, issue #264 $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // skip child rows & widget added (removable) rows - fixes #448 thanks to @hempel! - $rows = $tbody.children('tr').not('.' + c.cssChildRow).not(c.selectorRemove); + $rows = $tbody.children('tr').not(c.selectorRemove); len = $rows.length; if (combinedFilters === '' || wo.filter_serversideFiltering) { - $tbody.children().show().removeClass(wo.filter_filteredRow); + $tbody.children().not('.' + c.cssChildRow).show().removeClass(wo.filter_filteredRow); } else { // optimize searching only through already filtered rows - see #313 searchFiltered = true; @@ -844,7 +875,17 @@ ts.filter = { }); // can't search when all rows are hidden - this happens when looking for exact matches if (searchFiltered && $rows.filter(':visible').length === 0) { searchFiltered = false; } + if ((wo.filter_$anyMatch && wo.filter_$anyMatch.length) || filters[c.columns]) { + anyMatch = wo.filter_$anyMatch && wo.filter_$anyMatch.val() || filters[c.columns] || ''; + if (c.sortLocaleCompare) { + // replace accents + anyMatch = ts.replaceAccents(anyMatch); + } + iAnyMatch = anyMatch.toLowerCase(); + } + // loop through the rows + cacheIndex = 0; for (rowIndex = 0; rowIndex < len; rowIndex++) { childRow = $rows[rowIndex].className; // skip child rows & already filtered rows @@ -858,10 +899,44 @@ ts.filter = { childRowText = (childRow.length && wo.filter_childRows) ? childRow.text() : ''; childRowText = wo.filter_ignoreCase ? childRowText.toLocaleLowerCase() : childRowText; $cells = $rows.eq(rowIndex).children('td'); + + if (anyMatch) { + rowArray = $cells.map(function(i){ + var txt; + if (parsed[i]) { + txt = c.cache[tbodyIndex].normalized[cacheIndex][i]; + } else { + txt = wo.filter_ignoreCase ? $(this).text().toLowerCase() : $(this).text(); + if (c.sortLocaleCompare) { + txt = ts.replaceAccents(txt); + } + } + return txt; + }).get(); + rowText = rowArray.join(' '); + iRowText = rowText.toLowerCase(); + rowCache = c.cache[tbodyIndex].normalized[cacheIndex].join(' '); + filterMatched = null; + $.each(ts.filter.types, function(type, typeFunction) { + if ($.inArray(type, anyMatchNotAllowedTypes) < 0) { + matches = typeFunction( anyMatch, iAnyMatch, rowText, iRowText, rowCache, columns, table, wo, parsed, rowArray ); + if (matches !== null) { + filterMatched = matches; + return false; + } + } + }); + if (filterMatched !== null) { + showRow = filterMatched; + } else { + showRow = (iRowText + childRowText).indexOf(iAnyMatch) >= 0; + } + } + for (columnIndex = 0; columnIndex < columns; columnIndex++) { // ignore if filter is empty or disabled - if (filters[columnIndex] || wo.filter_anyMatch) { - cached = c.cache[tbodyIndex].normalized[rowIndex][columnIndex]; + if (filters[columnIndex]) { + cached = c.cache[tbodyIndex].normalized[cacheIndex][columnIndex]; // check if column data should be from the cell or from parsed data if (wo.filter_useParsedData || parsed[columnIndex]) { exact = cached; @@ -873,14 +948,10 @@ ts.filter = { iExact = !ts.filter.regex.type.test(typeof exact) && wo.filter_ignoreCase ? exact.toLocaleLowerCase() : exact; result = showRow; // if showRow is true, show that row - if (typeof filters[columnIndex] === "undefined" || filters[columnIndex] === null) { - filters[columnIndex] = wo.filter_anyMatch ? combinedFilters : filters[columnIndex]; - } - // replace accents - see #357 filters[columnIndex] = c.sortLocaleCompare ? ts.replaceAccents(filters[columnIndex]) : filters[columnIndex]; // val = case insensitive, filters[columnIndex] = case sensitive - iFilter = wo.filter_ignoreCase ? filters[columnIndex].toLocaleLowerCase() : filters[columnIndex]; + iFilter = wo.filter_ignoreCase ? (filters[columnIndex] || '').toLocaleLowerCase() : filters[columnIndex]; if (wo.filter_functions && wo.filter_functions[columnIndex]) { if (wo.filter_functions[columnIndex] === true) { // default selector; no "filter-select" class @@ -898,12 +969,10 @@ ts.filter = { // cycle through the different filters // filters return a boolean or null if nothing matches $.each(ts.filter.types, function(type, typeFunction) { - if (!wo.filter_anyMatch || (wo.filter_anyMatch && $.inArray(type, anyMatchNotAllowedTypes) < 0)) { - matches = typeFunction( filters[columnIndex], iFilter, exact, iExact, cached, columnIndex, table, wo, parsed ); - if (matches !== null) { - filterMatched = matches; - return false; - } + matches = typeFunction( filters[columnIndex], iFilter, exact, iExact, cached, columnIndex, table, wo, parsed ); + if (matches !== null) { + filterMatched = matches; + return false; } }); if (filterMatched !== null) { @@ -914,14 +983,7 @@ ts.filter = { result = ( (!wo.filter_startsWith && exact >= 0) || (wo.filter_startsWith && exact === 0) ); } } - if (wo.filter_anyMatch) { - showRow = result; - if (showRow){ - break; - } - } else { - showRow = (result) ? showRow : false; - } + showRow = (result) ? showRow : false; } } $rows[rowIndex].style.display = (showRow ? '' : 'none'); @@ -932,6 +994,7 @@ ts.filter = { } childRow.toggle(showRow); } + cacheIndex++; } } ts.processTbody(table, $tbody, false); @@ -984,7 +1047,7 @@ ts.filter = { arry = (ts.sortNatural) ? arry.sort(function(a, b) { return ts.sortNatural(a, b); }) : arry.sort(true); // Get curent filter value - currentValue = c.$table.find('thead').find('select.tablesorter-filter[data-column="' + column + '"]').val(); + currentValue = c.$table.find('thead').find('select.' + ts.css.filter + '[data-column="' + column + '"]').val(); // build option list for (indx = 0; indx < arry.length; indx++) { @@ -993,7 +1056,7 @@ ts.filter = { options += arry[indx] !== '' ? '<option value="' + txt + '"' + (currentValue === txt ? ' selected="selected"' : '') + '>' + arry[indx] + '</option>' : ''; } - c.$table.find('thead').find('select.tablesorter-filter[data-column="' + column + '"]')[ updating ? 'html' : 'append' ](options); + c.$table.find('thead').find('select.' + ts.css.filter + '[data-column="' + column + '"]')[ updating ? 'html' : 'append' ](options); }, buildDefault: function(table, updating) { var columnIndex, $header, @@ -1012,39 +1075,75 @@ ts.filter = { } } }, - searching: function(table, filter, external) { - if (typeof filter === 'undefined' || filter === true || external) { + searching: function(table, filter, dontSkip) { + if (typeof filter === 'undefined' || filter === true) { var wo = table.config.widgetOptions; // delay filtering clearTimeout(wo.searchTimer); wo.searchTimer = setTimeout(function() { - ts.filter.checkFilters(table, external || filter); + ts.filter.checkFilters(table, filter, dontSkip ); }, wo.filter_liveSearch ? wo.filter_searchDelay : 10); } else { // skip delay - ts.filter.checkFilters(table, filter); + ts.filter.checkFilters(table, filter, dontSkip); } } }; -ts.getFilters = function(table) { - var c = table ? $(table)[0].config : {}; - if (c && c.widgetOptions && !c.widgetOptions.filter_columnFilters) { - // no filter row +ts.getFilters = function(table, getRaw, setFilters, skipFirst) { + var i, $filters, $column, + filters = false, + c = table ? $(table)[0].config : '', + wo = table ? c.widgetOptions : ''; + if (getRaw !== true && wo && !wo.filter_columnFilters) { return $(table).data('lastSearch'); } - return c && c.$filters ? c.$filters.map(function(indx, el) { - return $(el).find('.tablesorter-filter').val() || ''; - }).get() || [] : false; + if (c) { + if (c.$filters) { + $filters = c.$filters.find('.' + ts.css.filter); + } + if (wo.filter_$externalFilters) { + $filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters; + } + if ($filters && $filters.length) { + filters = setFilters || []; + for (i = 0; i < c.columns + 1; i++) { + $column = $filters.filter('[data-column="' + (i === c.columns ? 'all' : i) + '"]'); + if ($column.length) { + // move the latest search to the first slot in the array + $column = $column.sort(function(a, b){ + return $(a).data('lastSearchTime') <= $(b).data('lastSearchTime'); + }); + if ($.isArray(setFilters)) { + // skip first (latest input) to maintain cursor position while typing + (skipFirst ? $column.slice(1) : $column).val( setFilters[i] ).trigger('change.tsfilter'); + } else { + filters[i] = $column.val() || ''; + // don't change the first... it will move the cursor + $column.slice(1).val( filters[i] ); + } + // save any match input dynamically + if (i === c.columns && $column.length) { + wo.filter_$anyMatch = $column; + } + } + } + } + } + if (filters.length === 0) { + filters = false; + } + return filters; }; -ts.setFilters = function(table, filter, apply) { - var $table = $(table), - c = $table.length ? $table[0].config : {}, - valid = c && c.$filters ? c.$filters.each(function(indx, el) { - $(el).find('.tablesorter-filter').val(filter[indx] || ''); - }).trigger('change.tsfilter') || false : false; - if (apply) { $table.trigger('search', [filter, false]); } +ts.setFilters = function(table, filter, apply, skipFirst) { + var c = table ? $(table)[0].config : '', + valid = ts.getFilters(table, true, filter, skipFirst); + if (c && apply) { + // ensure new set filters are applied, even if the search is the same + c.lastCombinedFilter = null; + c.$table.trigger('search', [filter, false]).trigger('filterFomatterUpdate'); + } return !!valid; }; @@ -1076,9 +1175,8 @@ ts.addWidget({ $thead = $table.children('thead:first'), $win = $attach.length ? $attach : $(window), $header = $thead.children('tr').not('.sticky-false').children(), - innerHeader = '.tablesorter-header-inner', + innerHeader = '.' + ts.css.headerIn, $tfoot = $table.find('tfoot'), - filterInputs = '.tablesorter-filter', $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '', stickyOffset = $attach.length ? 0 : $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, @@ -1092,7 +1190,7 @@ ts.addWidget({ visibility : 'hidden', zIndex : wo.stickyHeaders_zIndex ? wo.stickyHeaders_zIndex : 2 }), - $stickyThead = $stickyTable.children('thead:first').addClass('tablesorter-stickyHeader ' + wo.stickyHeaders), + $stickyThead = $stickyTable.children('thead:first').addClass(ts.css.sticky + ' ' + wo.stickyHeaders), $stickyCells, laststate = '', spacing = 0, @@ -1108,8 +1206,8 @@ ts.addWidget({ spacing = parseInt($header.eq(0).css('border-left-width'), 10) * 2; } $stickyTable.css({ - left : $attach.length ? parseInt($attach.css('padding-left'), 10) + - parseInt($attach.css('margin-left'), 10) + parseInt($table.css('border-left-width'), 10) : + left : $attach.length ? (parseInt($attach.css('padding-left'), 10) || 0) + parseInt(c.$table.css('padding-left'), 10) + + parseInt(c.$table.css('margin-left'), 10) + parseInt($table.css('border-left-width'), 10) : $thead.offset().left - $win.scrollLeft() - spacing, width: $table.width() }); @@ -1139,7 +1237,7 @@ ts.addWidget({ $stickyCells = $stickyThead.children().children(); $stickyTable.css({ height:0, width:0, padding:0, margin:0, border:0 }); // remove resizable block - $stickyCells.find('.tablesorter-resizer').remove(); + $stickyCells.find('.' + ts.css.resizer).remove(); // update sticky header class names to match real header after sorting $table .addClass('hasStickyHeaders') @@ -1159,27 +1257,11 @@ ts.addWidget({ .bind('pagerComplete.tsSticky', function() { resizeHeader(); }); - // http://stackoverflow.com/questions/5312849/jquery-find-self; - $header.find(c.selectorSort).add( c.$headers.filter(c.selectorSort) ).each(function(indx) { - var $header = $(this), - // clicking on sticky will trigger sort - $cell = $stickyThead.children('tr.tablesorter-headerRow').children().eq(indx).bind('mouseup', function(event) { - $header.trigger(event, true); // external mouseup flag (click timer is ignored) - }); - // prevent sticky header text selection - if (c.cancelSelection) { - $cell - .attr('unselectable', 'on') - .bind('selectstart', false) - .css({ - 'user-select': 'none', - 'MozUserSelect': 'none' - }); - } - }); + + ts.bindEvents(table, $stickyThead.children().children('.tablesorter-header')); + // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. $table.after( $stickyTable ); - // make it sticky! $win.bind('scroll.tsSticky resize.tsSticky', function(event) { if (!$table.is(':visible')) { return; } // fixes #278 @@ -1212,17 +1294,22 @@ ts.addWidget({ // look for filter widget if ($table.hasClass('hasFilters')) { + // scroll table into view after filtering, if sticky header is active - #482 $table.bind('filterEnd', function() { // $(':focus') needs jQuery 1.6+ - if ( $(document.activeElement).closest('thead')[0] !== $stickyThead[0] ) { - // don't update the stickyheader filter row if it already has focus - $stickyThead.find('.tablesorter-filter-row').children().each(function(indx) { - $(this).find(filterInputs).val( c.$filters.find(filterInputs).eq(indx).val() ); - }); + var $td = $(document.activeElement).closest('td'), + column = $td.parent().children().index($td); + // only scroll if sticky header is active + if ($stickyTable.hasClass(ts.css.stickyVis)) { + // scroll to original table (not sticky clone) + window.scrollTo(0, $table.position().top); + // give same input/select focus + if (column >= 0) { + c.$filters.eq(column).find('a, select, input').filter(':visible').focus(); + } } }); - - ts.filter.bindSearch( $table, $stickyCells.find('.tablesorter-filter').addClass('tablesorter-external-filter') ); + ts.filter.bindSearch( $table, $stickyCells.find('.' + ts.css.filter) ); } $table.trigger('stickyHeadersInit'); @@ -1232,7 +1319,7 @@ ts.addWidget({ c.$table .removeClass('hasStickyHeaders') .unbind('sortEnd.tsSticky pagerComplete.tsSticky') - .find('.tablesorter-stickyHeader').remove(); + .find('.' + ts.css.sticky).remove(); if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table // don't unbind if any table on the page still has stickyheaders applied if (!$('.hasStickyHeaders').length) { @@ -1264,7 +1351,8 @@ ts.addWidget({ $next = null, fullWidth = Math.abs($table.parent().width() - $table.width()) < 20, stopResize = function() { - if (ts.storage && $target) { + if (ts.storage && $target && $next) { + storedSizes = {}; storedSizes[$target.index()] = $target.width(); storedSizes[$next.index()] = $next.width(); $target.width( storedSizes[$target.index()] ); @@ -1298,9 +1386,9 @@ ts.addWidget({ // add wrapper inside each cell to allow for positioning of the resizable target block $rows.each(function() { $column = $(this).children().not('.resizable-false'); - if (!$(this).find('.tablesorter-wrapper').length) { + if (!$(this).find('.' + ts.css.wrapper).length) { // Firefox needs this inner div to position the resizer correctly - $column.wrapInner('<div class="tablesorter-wrapper" style="position:relative;height:100%;width:100%"></div>'); + $column.wrapInner('<div class="' + ts.css.wrapper + '" style="position:relative;height:100%;width:100%"></div>'); } // don't include the last column of the row if (!wo.resizable_addLastColumn) { $column = $column.slice(0,-1); } @@ -1311,8 +1399,8 @@ ts.addWidget({ var $column = $(this), padding = parseInt($column.css('padding-right'), 10) + 10; // 10 is 1/2 of the 20px wide resizer grip $column - .find('.tablesorter-wrapper') - .append('<div class="tablesorter-resizer" style="cursor:w-resize;position:absolute;z-index:1;right:-' + + .find('.' + ts.css.wrapper) + .append('<div class="' + ts.css.resizer + '" style="cursor:w-resize;position:absolute;z-index:1;right:-' + padding + 'px;top:0;height:100%;width:20px;"></div>'); }) .bind('mousemove.tsresize', function(event) { @@ -1330,7 +1418,7 @@ ts.addWidget({ .bind('mouseup.tsresize', function() { stopResize(); }) - .find('.tablesorter-resizer,.tablesorter-resizer-grip') + .find('.' + ts.css.resizer + ',.' + ts.css.grip) .bind('mousedown', function(event) { // save header cell and mouse position; closest() not supported by jQuery v1.2.6 $target = $(event.target).closest('th'); @@ -1361,13 +1449,15 @@ ts.addWidget({ .children('tr').children() .unbind('mousemove.tsresize mouseup.tsresize') // don't remove "tablesorter-wrapper" as uitheme uses it too - .find('.tablesorter-resizer,.tablesorter-resizer-grip').remove(); + .find('.' + ts.css.resizer + ',.' + ts.css.grip).remove(); ts.resizableReset(table); } }); ts.resizableReset = function(table) { - table.config.$headers.not('.resizable-false').css('width',''); - if (ts.storage) { ts.storage(table, 'tablesorter-resizable', {}); } + $(table).each(function(){ + this.config.$headers.not('.resizable-false').css('width',''); + if (ts.storage) { ts.storage(this, 'tablesorter-resizable', {}); } + }); }; // Save table sort widget diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css index 869125b..62ca0ff 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css @@ -176,6 +176,7 @@ caption { /* ajax error row */ .tablesorter .tablesorter-errorRow td { + text-align: center; cursor: pointer; background-color: #e6bf99; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css index b40a32c..1990f7d 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css @@ -211,6 +211,7 @@ caption { /* ajax error row */ .tablesorter .tablesorter-errorRow td { + text-align: center; cursor: pointer; background-color: #e6bf99; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index c1bf02f..631f168 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -82,8 +82,14 @@ transition: height 0.1s ease; } .tablesorter-bootstrap .tablesorter-filter-row .tablesorter-filter.disabled { - background: #eee; + background-color: #eee; + color: #555; cursor: not-allowed; + border: 1px solid #ccc; + border-radius: 4px; + box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.075) inset; + box-sizing: border-box; + transition: height 0.1s ease; } .tablesorter-bootstrap .tablesorter-filter-row td { background: #efefef; @@ -127,6 +133,7 @@ /* ajax error row */ .tablesorter .tablesorter-errorRow td { + text-align: center; cursor: pointer; background-color: #e6bf99; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css index 74e143d..c927014 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css @@ -134,6 +134,7 @@ caption { /* ajax error row */ .tablesorter .tablesorter-errorRow td { + text-align: center; cursor: pointer; background-color: #e6bf99; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css index c104960..e887a6f 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css @@ -177,6 +177,7 @@ caption { /* ajax error row */ .tablesorter .tablesorter-errorRow td { + text-align: center; cursor: pointer; background-color: #e6bf99; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css index cc59d20..0110931 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css @@ -179,6 +179,7 @@ caption { /* ajax error row */ .tablesorter .tablesorter-errorRow td { + text-align: center; cursor: pointer; background-color: #e6bf99; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css index 82269b5..f6cf8bf 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css @@ -202,6 +202,7 @@ caption { /* ajax error row */ .tablesorter .tablesorter-errorRow td { + text-align: center; cursor: pointer; background-color: #e6bf99; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css index e119b2a..c9a5a15 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css @@ -193,6 +193,7 @@ caption { /* ajax error row */ .tablesorter .tablesorter-errorRow td { + text-align: center; cursor: pointer; background-color: #e6bf99; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css index 9477104..6ee1b3f 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css @@ -235,6 +235,7 @@ caption { /* ajax error row */ .tablesorter .tablesorter-errorRow td { + text-align: center; cursor: pointer; background-color: #e6bf99; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css index 93a13ab..6f73279 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css @@ -191,6 +191,7 @@ caption { /* ajax error row */ .tablesorter .tablesorter-errorRow td { + text-align: center; cursor: pointer; background-color: #e6bf99; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css index 20a9022..24538e9 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css @@ -144,6 +144,7 @@ /* ajax error row */ .tablesorter .tablesorter-errorRow td { + text-align: center; cursor: pointer; background-color: #e6bf99; } From fca235452ce7698a9b95635b150ea28c1a103161 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Thu, 20 Feb 2014 21:52:23 +0100 Subject: [PATCH 015/138] * updated tablesorter to latest version (2.15.1) --- CHANGELOG.markdown | 4 ++++ README.markdown | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery-tablesorter/jquery.tablesorter.js | 4 ++-- .../jquery.tablesorter.widgets.js | 22 +++++++++---------- 6 files changed, 19 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index d23520f..8cfcf04 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -1,6 +1,10 @@ Changelog === +#### v1.10.1 (2014-02-20) + +* Upgrade tablesorter to v2.15.1 + #### v1.10.0 (2014-02-19) * Upgrade tablesorter to v2.15.0 diff --git a/README.markdown b/README.markdown index 8d8a83f..7c651e8 100644 --- a/README.markdown +++ b/README.markdown @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.15.0 (2/19/2014), [documentation] +Current tablesorter version: 2.15.1 (2/19/2014), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 25c710e..24c07df 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = "1.10.0" + VERSION = "1.10.1" end diff --git a/tablesorter b/tablesorter index 15bf11f..08b9808 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 15bf11f0d4163a01bc7ad51d5208e292549cd24f +Subproject commit 08b98084b0bbf76754550221d8c79016188b52b5 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 8be48ae..e3b338e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.15.0 - Client-side table sorting with ease! +* TableSorter 2.15.1 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.15.0"; + ts.version = "2.15.1"; ts.parsers = []; ts.widgets = []; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 99ce215..6ae93dd 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.8+ widgets - updated 2/19/2014 (v2.15.0) +/*! tableSorter 2.8+ widgets - updated 2/19/2014 (v2.15.1) * * Column Styles * Column Filters @@ -581,10 +581,6 @@ ts.filter = { } return false; }); - ts.filter.bindSearch( table, c.$table.find('input.' + ts.css.filter), true ); - if (wo.filter_external) { - ts.filter.bindSearch( table, wo.filter_external ); - } // reset button/link if (wo.filter_reset) { @@ -621,9 +617,10 @@ ts.filter = { // it would append the same options twice. ts.filter.buildDefault(table, true); - c.$table.find('select.' + ts.css.filter).bind('change search', function(event, filter) { - ts.filter.checkFilters(table, filter, true); - }); + ts.filter.bindSearch( table, c.$table.find('.' + ts.css.filter), true ); + if (wo.filter_external) { + ts.filter.bindSearch( table, wo.filter_external ); + } if (wo.filter_hideFilters) { ts.filter.hideFilters(table, c); @@ -746,11 +743,12 @@ ts.filter = { ts.setFilters(table, c.$table.data('lastSearch') || [], internal === false); } $el - .data('lastSearchTime', new Date().getTime()) + // use data attribute instead of jQuery data since the head is cloned without including the data/binding + .attr('data-lastSearchTime', new Date().getTime()) .unbind('keyup search change') // include change for select - fixes #473 .bind('keyup search change', function(event, filters) { - $(this).data('lastSearchTime', new Date().getTime()); + $(this).attr('data-lastSearchTime', new Date().getTime()); // emulate what webkit does.... escape clears the filter if (event.which === 27) { this.value = ''; @@ -852,7 +850,7 @@ ts.filter = { anyMatchNotAllowedTypes = [ 'range', 'notMatch', 'operators' ], // parse columns after formatter, in case the class is added at that point parsed = c.$headers.map(function(columnIndex) { - return c.parsers[columnIndex].parsed || ( ts.getData ? + return c.parsers && c.parsers[columnIndex].parsed || ( ts.getData ? ts.getData(c.$headers.filter('[data-column="' + columnIndex + '"]:last'), c.headers[columnIndex], 'filter') === 'parsed' : $(this).hasClass('filter-parsed') ); }).get(); @@ -1112,7 +1110,7 @@ ts.getFilters = function(table, getRaw, setFilters, skipFirst) { if ($column.length) { // move the latest search to the first slot in the array $column = $column.sort(function(a, b){ - return $(a).data('lastSearchTime') <= $(b).data('lastSearchTime'); + return $(a).attr('data-lastSearchTime') <= $(b).attr('data-lastSearchTime'); }); if ($.isArray(setFilters)) { // skip first (latest input) to maintain cursor position while typing From 10f94a8c164677b830b7a243004d12b3634a7b2f Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Sat, 22 Feb 2014 19:12:01 +0100 Subject: [PATCH 016/138] * updated tablesorter to latest version (2.15.4) --- CHANGELOG.markdown | 4 ++ README.markdown | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 13 +++--- .../jquery-tablesorter/jquery.tablesorter.js | 19 +++++++-- .../jquery.tablesorter.widgets.js | 41 +++++++++++-------- 7 files changed, 53 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index 8cfcf04..aacdf62 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -1,6 +1,10 @@ Changelog === +#### v1.10.2 (2014-02-22) + +* Upgrade tablesorter to v2.15.4 + #### v1.10.1 (2014-02-20) * Upgrade tablesorter to v2.15.1 diff --git a/README.markdown b/README.markdown index 7c651e8..21536ee 100644 --- a/README.markdown +++ b/README.markdown @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.15.1 (2/19/2014), [documentation] +Current tablesorter version: 2.15.4 (2/22/2014), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 24c07df..f6ec6f0 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = "1.10.1" + VERSION = "1.10.2" end diff --git a/tablesorter b/tablesorter index 08b9808..556bcc2 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 08b98084b0bbf76754550221d8c79016188b52b5 +Subproject commit 556bcc2d48ecdf826cd6c6e3f6549d7660ff7a79 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 20ca3ec..a09b792 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 2/19/2014 (v2.15.0) + * updated 2/22/2014 (v2.15.4) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -336,18 +336,19 @@ p.last.sortList = (c.sortList || []).join(','); updatePageDisplay(table, p); fixHeight(table, p); - // apply widgets after table has rendered - $t.trigger('applyWidgets'); - $t.trigger('updateRows', [false, function(){ + $t.trigger('updateCache', [function(){ if (p.initialized) { - $t.trigger('updateComplete'); + // apply widgets after table has rendered + $t.trigger('applyWidgets'); $t.trigger('pagerChange', p); } }]); } if (!p.initialized) { p.initialized = true; - $(table).trigger('pagerInitialized', p); + $(table) + .trigger('applyWidgets') + .trigger('pagerInitialized', p); } }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index e3b338e..e7ef967 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.15.1 - Client-side table sorting with ease! +* TableSorter 2.15.4 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.15.1"; + ts.version = "2.15.4"; ts.parsers = []; ts.widgets = []; @@ -777,7 +777,7 @@ $table = c.$table; // apply easy methods that trigger bound events $table - .unbind('sortReset update updateRows updateCell updateAll addRows sorton appendCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave '.split(' ').join('.tablesorter ')) + .unbind('sortReset update updateRows updateCell updateAll addRows sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave '.split(' ').join('.tablesorter ')) .bind("sortReset.tablesorter", function(e){ e.stopPropagation(); c.sortList = []; @@ -878,6 +878,17 @@ callback(table); } }) + .bind("updateCache.tablesorter", function(e, callback){ + // rebuild parsers + if (!c.parsers) { + buildParserCache(table); + } + // rebuild the cache map + buildCache(table); + if (typeof callback === "function") { + callback(table); + } + }) .bind("applyWidgetId.tablesorter", function(e, id) { e.stopPropagation(); ts.getWidgetById(id).format(table, c, c.widgetOptions); @@ -1076,6 +1087,8 @@ if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); } // jQuery v1.2.6 doesn't have closest() cell = /TH|TD/.test(this.tagName) ? this : $(this).parents('th, td')[0]; + // reference original table headers and find the same cell + cell = c.$headers[ $headers.index( cell ) ]; if (!cell.sortDisabled) { initSort(table, cell, e); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 6ae93dd..ca9fd65 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.8+ widgets - updated 2/19/2014 (v2.15.1) +/*! tableSorter 2.8+ widgets - updated 2/21/2014 (v2.15.3) * * Column Styles * Column Filters @@ -571,11 +571,11 @@ ts.filter = { } else { // send false argument to force a new search; otherwise if the filter hasn't changed, it will return filter = event.type === 'search' ? filter : event.type === 'updateComplete' ? c.$table.data('lastSearch') : ''; - if (/(update|add)/.test(event.type)) { + if (/(update|add)/.test(event.type) && event.type !== "updateComplete") { // force a new search since content has changed c.lastCombinedFilter = null; } - // pass true (dontSkip) to prevent the tablesorter.setFilters function from skipping the first input + // pass true (skipFirst) to prevent the tablesorter.setFilters function from skipping the first input // ensures all inputs are updated when a search is triggered on the table $('table').trigger('search', [...]); ts.filter.searching(table, filter, true); } @@ -758,22 +758,22 @@ ts.filter = { ( event.which >= 37 && event.which <= 40 ) || (event.which !== 13 && wo.filter_liveSearch === false) ) ) ) { return; } - // true flag in getFilters forces obtaining the latest values - ts.filter.searching( table, filters || ts.getFilters( table, true ), true ); + // true flag tells getFilters to skip newest timed input + ts.filter.searching( table, '', true ); }); c.$table.bind('filterReset', function(){ $el.val(''); }); }, - checkFilters: function(table, filter, dontSkip) { + checkFilters: function(table, filter, skipFirst) { var c = table.config, wo = c.widgetOptions, filterArray = $.isArray(filter), - filters = (filterArray) ? filter : ts.getFilters(table), + filters = (filterArray) ? filter : ts.getFilters(table, true), combinedFilters = (filters || []).join(''); // combined filter values // add filter array back into inputs if (filterArray) { - ts.setFilters( table, filters, false, dontSkip !== true ); + ts.setFilters( table, filters, false, skipFirst !== true ); } if (wo.filter_hideFilters) { // show/hide filter row as needed @@ -817,7 +817,7 @@ ts.filter = { // $(':focus') needs jQuery 1.6+ if ( $(document.activeElement).closest('tr')[0] !== $filterRow[0] ) { // don't hide row if any filter has a value - if (ts.getFilters(table).join('') === '') { + if (c.lastCombinedFilter === '') { $filterRow.addClass('hideme'); } } @@ -850,7 +850,7 @@ ts.filter = { anyMatchNotAllowedTypes = [ 'range', 'notMatch', 'operators' ], // parse columns after formatter, in case the class is added at that point parsed = c.$headers.map(function(columnIndex) { - return c.parsers && c.parsers[columnIndex].parsed || ( ts.getData ? + return c.parsers && c.parsers[columnIndex] && c.parsers[columnIndex].parsed || ( ts.getData ? ts.getData(c.$headers.filter('[data-column="' + columnIndex + '"]:last'), c.headers[columnIndex], 'filter') === 'parsed' : $(this).hasClass('filter-parsed') ); }).get(); @@ -1011,7 +1011,7 @@ ts.filter = { }, buildSelect: function(table, column, updating, onlyavail) { column = parseInt(column, 10); - var indx, rowIndex, tbodyIndex, len, currentValue, txt, + var indx, rowIndex, tbodyIndex, len, currentValue, txt, $filters, c = table.config, wo = c.widgetOptions, $tbodies = c.$tbodies, @@ -1054,7 +1054,12 @@ ts.filter = { options += arry[indx] !== '' ? '<option value="' + txt + '"' + (currentValue === txt ? ' selected="selected"' : '') + '>' + arry[indx] + '</option>' : ''; } - c.$table.find('thead').find('select.' + ts.css.filter + '[data-column="' + column + '"]')[ updating ? 'html' : 'append' ](options); + // update all selects in the same column (clone thead in sticky headers & any external selects) - fixes 473 + $filters = ( c.$filters ? c.$filters : c.$table.children('thead') ).find('.' + ts.css.filter); + if (wo.filter_$externalFilters) { + $filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters; + } + $filters.filter('select[data-column="' + column + '"]')[ updating ? 'html' : 'append' ](options); }, buildDefault: function(table, updating) { var columnIndex, $header, @@ -1073,17 +1078,17 @@ ts.filter = { } } }, - searching: function(table, filter, dontSkip) { + searching: function(table, filter, skipFirst) { if (typeof filter === 'undefined' || filter === true) { var wo = table.config.widgetOptions; // delay filtering clearTimeout(wo.searchTimer); wo.searchTimer = setTimeout(function() { - ts.filter.checkFilters(table, filter, dontSkip ); + ts.filter.checkFilters(table, filter, skipFirst ); }, wo.filter_liveSearch ? wo.filter_searchDelay : 10); } else { // skip delay - ts.filter.checkFilters(table, filter, dontSkip); + ts.filter.checkFilters(table, filter, skipFirst); } } }; @@ -1110,7 +1115,7 @@ ts.getFilters = function(table, getRaw, setFilters, skipFirst) { if ($column.length) { // move the latest search to the first slot in the array $column = $column.sort(function(a, b){ - return $(a).attr('data-lastSearchTime') <= $(b).attr('data-lastSearchTime'); + return $(b).attr('data-lastSearchTime') - $(a).attr('data-lastSearchTime'); }); if ($.isArray(setFilters)) { // skip first (latest input) to maintain cursor position while typing @@ -1224,8 +1229,8 @@ ts.addWidget({ // fix clone ID, if it exists - fixes #271 if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; } // clear out cloned table, except for sticky header - // include caption & filter row (fixes #126 & #249) - $stickyTable.find('thead:gt(0), tr.sticky-false, tbody, tfoot').remove(); + // include caption & filter row (fixes #126 & #249) - don't remove cells to get correct cell indexing + $stickyTable.find('thead:gt(0), tr.sticky-false, tbody, tfoot').hide(); if (!wo.stickyHeaders_includeCaption) { $stickyTable.find('caption').remove(); } else { From 60564a5bae9ae96b5012be2b9ec11c3c09cfcf6c Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Sun, 23 Feb 2014 22:23:47 +0100 Subject: [PATCH 017/138] * updated tablesorter to latest version (2.15.5) * fixed accidentally missing parsers and widgets to gem * minor code changes --- CHANGELOG.markdown | 6 + README.markdown | 9 +- Rakefile | 46 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../extras/jquery.quicksearch.js | 191 +++ .../jquery-tablesorter/extras/semver-mod.js | 1026 +++++++++++++++++ .../jquery-tablesorter/extras/semver.js | 1011 ++++++++++++++++ .../jquery-tablesorter/jquery.tablesorter.js | 4 +- .../parsers/parser-date-iso8601.js | 34 + .../parsers/parser-date-month.js | 33 + .../parsers/parser-date-two-digit-year.js | 74 ++ .../parsers/parser-date-weekday.js | 33 + .../jquery-tablesorter/parsers/parser-date.js | 36 + .../parsers/parser-feet-inch-fraction.js | 63 + .../parsers/parser-file-type.js | 73 ++ .../parsers/parser-ignore-articles.js | 47 + .../parsers/parser-input-select.js | 86 ++ .../jquery-tablesorter/parsers/parser-ipv6.js | 76 ++ .../parsers/parser-metric.js | 77 ++ .../widgets/widget-build-table.js | 441 +++++++ .../widgets/widget-columnSelector.js | 291 +++++ .../widgets/widget-cssStickyHeaders.js | 67 ++ .../widgets/widget-editable.js | 89 ++ .../widgets/widget-grouping.js | 183 +++ .../widgets/widget-pager.js | 834 ++++++++++++++ .../widgets/widget-repeatheaders.js | 50 + .../widgets/widget-scroller.js | 241 ++++ 28 files changed, 5102 insertions(+), 23 deletions(-) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/extras/jquery.quicksearch.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/extras/semver-mod.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/extras/semver.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/parsers/parser-feet-inch-fraction.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ipv6.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index aacdf62..2713a3d 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -1,6 +1,12 @@ Changelog === +#### v1.10.3 (2014-02-23) + +* Upgrade tablesorter to v2.15.5 +* FIX: Added accidentally missing parsers and widgets to gem +* Minor structural code changes in Rakefile, updated Readme + #### v1.10.2 (2014-02-22) * Upgrade tablesorter to v2.15.4 diff --git a/README.markdown b/README.markdown index 21536ee..df9789f 100644 --- a/README.markdown +++ b/README.markdown @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.15.4 (2/22/2014), [documentation] +Current tablesorter version: 2.15.5 (2/23/2014), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. @@ -24,7 +24,8 @@ Or install it yourself as: ## Requirements -Rails 3.1 and higher +Rails 3.1 and higher (tested up to 4.1) +Tested with ruby 1.9.3 - 2.1.0 ## Usage @@ -36,7 +37,7 @@ In your `application.js` //= require jquery-tablesorter ``` -This will require all jquery-tablesorter files (exclude addons). +This will require all jquery-tablesorter files (exclude addons, widgets, ...). Or you can include single file with: @@ -45,6 +46,8 @@ Or you can include single file with: //= require jquery-tablesorter/jquery.tablesorter //= require jquery-tablesorter/jquery.tablesorter.widgets //= require jquery-tablesorter/addons/pager/jquery.tablesorter.pager +//= require jquery-tablesorter/widgets/widget-repeatheaders +//= require jquery-tablesorter/parsers/parser-metric ``` ### Stylesheet files diff --git a/Rakefile b/Rakefile index 6621e31..1f22924 100755 --- a/Rakefile +++ b/Rakefile @@ -4,58 +4,72 @@ require 'bundler' Bundler::GemHelper.install_tasks namespace :jquery_tablesorter do - desc 'update tablesorter' + + desc 'Update tablesorter files' task :update do + # javascripts # - javascript_dir = 'vendor/assets/javascripts/jquery-tablesorter' + javascript_dir = File.join('vendor', 'assets', 'javascripts', 'jquery-tablesorter') FileUtils.mkdir_p(javascript_dir) - Dir.glob('tablesorter/js/*.js').reject{ |file| file =~ /.min.js\Z/}.each do |file| + Dir.glob(File.join('tablesorter', 'js', '*.js')).reject{|file| file =~ /.min.js\Z/}.each do |file| FileUtils.cp file, javascript_dir, :verbose => true end # stylesheets # - stylesheet_dir = 'vendor/assets/stylesheets/jquery-tablesorter' + stylesheet_dir = File.join('vendor', 'assets', 'stylesheets', 'jquery-tablesorter') FileUtils.mkdir_p(stylesheet_dir) - Dir.glob('tablesorter/css/*.css').each do |file| + Dir.glob(File.join('tablesorter', 'css', '*.css')).each do |file| FileUtils.cp file, stylesheet_dir, :verbose => true end # images # - images_dir = 'vendor/assets/images/jquery-tablesorter' + images_dir = File.join('vendor', 'assets', 'images', 'jquery-tablesorter') FileUtils.mkdir_p(images_dir) - Dir.glob('tablesorter/css/images/*').each do |file| + Dir.glob(File.join('tablesorter', 'css', 'images', '*')).each do |file| FileUtils.cp file, images_dir, :verbose => true end # addons # ## pager - pager_stylesheet_dir = stylesheet_dir + '/addons/pager' + pager_stylesheet_dir = File.join(stylesheet_dir, 'addons', 'pager') FileUtils.mkdir_p(pager_stylesheet_dir) - FileUtils.cp 'tablesorter/addons/pager/jquery.tablesorter.pager.css', + FileUtils.cp File.join('tablesorter', 'addons', 'pager', 'jquery.tablesorter.pager.css'), pager_stylesheet_dir, :verbose => true - pager_javascript_dir = javascript_dir + '/addons/pager' + pager_javascript_dir = File.join(javascript_dir, 'addons', 'pager') FileUtils.mkdir_p(pager_javascript_dir) - FileUtils.cp 'tablesorter/addons/pager/jquery.tablesorter.pager.js', + FileUtils.cp File.join('tablesorter', 'addons', 'pager', 'jquery.tablesorter.pager.js'), pager_javascript_dir, :verbose => true - pager_images_dir = images_dir + '/addons/pager' + pager_images_dir = File.join(images_dir, 'addons', 'pager') FileUtils.mkdir_p(pager_images_dir) - FileUtils.cp_r 'tablesorter/addons/pager/icons', pager_images_dir, + FileUtils.cp_r File.join('tablesorter', 'addons', 'pager', 'icons'), pager_images_dir, :verbose => true + + + # parsers and widgets + # + %w(parsers widgets extras).each do |folder| + folder_javascript_dir = File.join(javascript_dir, folder) + FileUtils.mkdir_p(folder_javascript_dir) + Dir.glob(File.join('tablesorter', 'js', folder, '*.js')).reject{|file| file =~ /.min.js\Z/}.each do |file| + FileUtils.cp file, folder_javascript_dir, :verbose => true + end + end + end desc 'Sanitize image paths' task :sanitize_image_paths do - Dir.glob('vendor/assets/stylesheets/jquery-tablesorter/*.css').each do |file_path| - content = File.read(file_path).gsub(/url\(images\//, "url(/assets/jquery-tablesorter/") - File.open(file_path, "w") {|file| file.write content} + Dir.glob(File.join('vendor', 'assets', 'stylesheets', 'jquery-tablesorter', '*.css')).each do |file_path| + content = File.read(file_path).gsub(/url\(images\//, 'url(/assets/jquery-tablesorter/') + File.open(file_path, 'w') {|file| file.write content} end end end diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index f6ec6f0..cd4cc30 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = "1.10.2" + VERSION = "1.10.3" end diff --git a/tablesorter b/tablesorter index 556bcc2..cf1ea13 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 556bcc2d48ecdf826cd6c6e3f6549d7660ff7a79 +Subproject commit cf1ea135cbb30ac78e99f4a7ad33f790a33db64f diff --git a/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.quicksearch.js b/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.quicksearch.js new file mode 100644 index 0000000..ee783ac --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.quicksearch.js @@ -0,0 +1,191 @@ +/* jQuery Quicksearch plugin + by riklomas https://github.com/riklomas/quicksearch + Modified to include childRows (for tablesorter) + + See http://stackoverflow.com/q/20342203/145346 for + more details +*/ +(function($, window, document, undefined) { + $.fn.quicksearch = function (target, opt) { + + var timeout, cache, rowcache, jq_results, val = '', e = this, options = $.extend({ + delay: 100, + selector: null, + stripeRows: null, + loader: null, + noResults: '', + childRow: 'tablesorter-childRow', // include child row with search results + matchedResultsCount: 0, + bind: 'keyup', + onBefore: function () { + return; + }, + onAfter: function () { + return; + }, + show: function () { + this.style.display = ""; + }, + hide: function () { + this.style.display = "none"; + }, + prepareQuery: function (val) { + return val.toLowerCase().split(' '); + }, + testQuery: function (query, txt, _row) { + for (var i = 0; i < query.length; i += 1) { + if (txt.indexOf(query[i]) === -1) { + return false; + } + } + return true; + } + }, opt); + + this.go = function () { + + var i = 0, + numMatchedRows = 0, + noresults = true, + query = options.prepareQuery(val), + val_empty = (val.replace(' ', '').length === 0); + + for (var i = 0, len = rowcache.length; i < len; i++) { + if (val_empty || options.testQuery(query, cache[i], rowcache[i]) || + ($(rowcache[i]).hasClass(options.childRow) && $(rowcache[i > 1 ? i - 1 : 0]).is(':visible'))) { + options.show.apply(rowcache[i]); + noresults = false; + numMatchedRows++; + } else { + options.hide.apply(rowcache[i]); + } + } + + if (noresults) { + this.results(false); + } else { + this.results(true); + this.stripe(); + } + + this.matchedResultsCount = numMatchedRows; + this.loader(false); + options.onAfter(); + + return this; + }; + + /* + * External API so that users can perform search programatically. + * */ + this.search = function (submittedVal) { + val = submittedVal; + e.trigger(); + }; + + /* + * External API to get the number of matched results as seen in + * https://github.com/ruiz107/quicksearch/commit/f78dc440b42d95ce9caed1d087174dd4359982d6 + * */ + this.currentMatchedResults = function() { + return this.matchedResultsCount; + }; + + this.stripe = function () { + + if (typeof options.stripeRows === "object" && options.stripeRows !== null) + { + var joined = options.stripeRows.join(' '); + var stripeRows_length = options.stripeRows.length; + + jq_results.not(':hidden').each(function (i) { + $(this).removeClass(joined).addClass(options.stripeRows[i % stripeRows_length]); + }); + } + + return this; + }; + + this.strip_html = function (input) { + var output = input.replace(new RegExp('<[^<]+\>', 'g'), ""); + output = $.trim(output.toLowerCase()); + return output; + }; + + this.results = function (bool) { + if (typeof options.noResults === "string" && options.noResults !== "") { + if (bool) { + $(options.noResults).hide(); + } else { + $(options.noResults).show(); + } + } + return this; + }; + + this.loader = function (bool) { + if (typeof options.loader === "string" && options.loader !== "") { + (bool) ? $(options.loader).show() : $(options.loader).hide(); + } + return this; + }; + + this.cache = function () { + + jq_results = $(target); + + if (typeof options.noResults === "string" && options.noResults !== "") { + jq_results = jq_results.not(options.noResults); + } + + var t = (typeof options.selector === "string") ? + jq_results.find(options.selector) : $(target).not(options.noResults); + cache = t.map(function () { + return e.strip_html(this.innerHTML); + }); + + rowcache = jq_results.map(function () { + return this; + }); + + /* + * Modified fix for sync-ing "val". + * Original fix https://github.com/michaellwest/quicksearch/commit/4ace4008d079298a01f97f885ba8fa956a9703d1 + * */ + val = val || this.val() || ""; + + return this.go(); + }; + + this.trigger = function () { + this.loader(true); + options.onBefore(); + + window.clearTimeout(timeout); + timeout = window.setTimeout(function () { + e.go(); + }, options.delay); + + return this; + }; + + this.cache(); + this.results(true); + this.stripe(); + this.loader(false); + + return this.each(function () { + + /* + * Changed from .bind to .on. + * */ + $(this).on(options.bind, function () { + + val = $(this).val(); + e.trigger(); + }); + }); + + }; + +}(jQuery, this, document)); diff --git a/vendor/assets/javascripts/jquery-tablesorter/extras/semver-mod.js b/vendor/assets/javascripts/jquery-tablesorter/extras/semver-mod.js new file mode 100644 index 0000000..ac64303 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/extras/semver-mod.js @@ -0,0 +1,1026 @@ +/** + Modified semver.js for node.js by R.Garrison (@Mottie) + Original by @isaacs: https://github.com/isaacs/node-semver + ( all modifications have been labeled ) + */ +// ***** MODIFIED LINE BELOW ***** +(function(){ +// ***** MODIFIED LINE BELOW ***** +var module = { exports : {} }; + +// export the class if we are in a Node-like system. +// ***** MODIFIED LINE BELOW ***** +// if (typeof module === 'object' && module.exports === exports) +// ***** MODIFIED LINE BELOW ***** + var exports = module.exports = SemVer; + +// The debug function is excluded entirely from the minified version. +/* nomin */ var debug; +/* nomin */ if (typeof process === 'object' && + /* nomin */ process.env && + /* nomin */ process.env.NODE_DEBUG && + /* nomin */ /\bsemver\b/i.test(process.env.NODE_DEBUG)) + /* nomin */ debug = function() { + /* nomin */ var args = Array.prototype.slice.call(arguments, 0); + /* nomin */ args.unshift('SEMVER'); + /* nomin */ console.log.apply(console, args); + /* nomin */ }; +/* nomin */ else + /* nomin */ debug = function() {}; + +// Note: this is the semver.org version of the spec that it implements +// Not necessarily the package version of this code. +exports.SEMVER_SPEC_VERSION = '2.0.0'; + +// The actual regexps go on exports.re +var re = exports.re = []; +var src = exports.src = []; +var R = 0; + +// The following Regular Expressions can be used for tokenizing, +// validating, and parsing SemVer version strings. + +// ## Numeric Identifier +// A single `0`, or a non-zero digit followed by zero or more digits. + +var NUMERICIDENTIFIER = R++; +src[NUMERICIDENTIFIER] = '0|[1-9]\\d*'; +var NUMERICIDENTIFIERLOOSE = R++; +src[NUMERICIDENTIFIERLOOSE] = '[0-9]+'; + + +// ## Non-numeric Identifier +// Zero or more digits, followed by a letter or hyphen, and then zero or +// more letters, digits, or hyphens. + +var NONNUMERICIDENTIFIER = R++; +src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*'; + + +// ## Main Version +// Three dot-separated numeric identifiers. + +var MAINVERSION = R++; +src[MAINVERSION] = '(' + src[NUMERICIDENTIFIER] + ')\\.' + + '(' + src[NUMERICIDENTIFIER] + ')\\.' + + '(' + src[NUMERICIDENTIFIER] + ')'; + +var MAINVERSIONLOOSE = R++; +src[MAINVERSIONLOOSE] = '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[NUMERICIDENTIFIERLOOSE] + ')'; + +// ## Pre-release Version Identifier +// A numeric identifier, or a non-numeric identifier. + +var PRERELEASEIDENTIFIER = R++; +src[PRERELEASEIDENTIFIER] = '(?:' + src[NUMERICIDENTIFIER] + + '|' + src[NONNUMERICIDENTIFIER] + ')'; + +var PRERELEASEIDENTIFIERLOOSE = R++; +src[PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[NUMERICIDENTIFIERLOOSE] + + '|' + src[NONNUMERICIDENTIFIER] + ')'; + + +// ## Pre-release Version +// Hyphen, followed by one or more dot-separated pre-release version +// identifiers. + +var PRERELEASE = R++; +src[PRERELEASE] = '(?:-(' + src[PRERELEASEIDENTIFIER] + + '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))'; + +var PRERELEASELOOSE = R++; +src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] + + '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))'; + +// ## Build Metadata Identifier +// Any combination of digits, letters, or hyphens. + +var BUILDIDENTIFIER = R++; +src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+'; + +// ## Build Metadata +// Plus sign, followed by one or more period-separated build metadata +// identifiers. + +var BUILD = R++; +src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] + + '(?:\\.' + src[BUILDIDENTIFIER] + ')*))'; + + +// ## Full Version String +// A main version, followed optionally by a pre-release version and +// build metadata. + +// Note that the only major, minor, patch, and pre-release sections of +// the version string are capturing groups. The build metadata is not a +// capturing group, because it should not ever be used in version +// comparison. + +var FULL = R++; +var FULLPLAIN = 'v?' + src[MAINVERSION] + + src[PRERELEASE] + '?' + + src[BUILD] + '?'; + +src[FULL] = '^' + FULLPLAIN + '$'; + +// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. +// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty +// common in the npm registry. +var LOOSEPLAIN = '[v=\\s]*' + src[MAINVERSIONLOOSE] + + src[PRERELEASELOOSE] + '?' + + src[BUILD] + '?'; + +var LOOSE = R++; +src[LOOSE] = '^' + LOOSEPLAIN + '$'; + +var GTLT = R++; +src[GTLT] = '((?:<|>)?=?)'; + +// Something like "2.*" or "1.2.x". +// Note that "x.x" is a valid xRange identifer, meaning "any version" +// Only the first item is strictly required. +var XRANGEIDENTIFIERLOOSE = R++; +src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*'; +var XRANGEIDENTIFIER = R++; +src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*'; + +var XRANGEPLAIN = R++; +src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + + '(?:(' + src[PRERELEASE] + ')' + + ')?)?)?'; + +var XRANGEPLAINLOOSE = R++; +src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:(' + src[PRERELEASELOOSE] + ')' + + ')?)?)?'; + +// >=2.x, for example, means >=2.0.0-0 +// <1.x would be the same as "<1.0.0-0", though. +var XRANGE = R++; +src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$'; +var XRANGELOOSE = R++; +src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$'; + +// Tilde ranges. +// Meaning is "reasonably at or greater than" +var LONETILDE = R++; +src[LONETILDE] = '(?:~>?)'; + +var TILDETRIM = R++; +src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+'; +re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g'); +var tildeTrimReplace = '$1~'; + +var TILDE = R++; +src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$'; +var TILDELOOSE = R++; +src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$'; + +// Caret ranges. +// Meaning is "at least and backwards compatible with" +var LONECARET = R++; +src[LONECARET] = '(?:\\^)'; + +var CARETTRIM = R++; +src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+'; +re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g'); +var caretTrimReplace = '$1^'; + +var CARET = R++; +src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$'; +var CARETLOOSE = R++; +src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$'; + +// A simple gt/lt/eq thing, or just "" to indicate "any version" +var COMPARATORLOOSE = R++; +src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$'; +var COMPARATOR = R++; +src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$'; + + +// An expression to strip any whitespace between the gtlt and the thing +// it modifies, so that `> 1.2.3` ==> `>1.2.3` +var COMPARATORTRIM = R++; +src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] + + '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')'; + +// this one has to use the /g flag +re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g'); +var comparatorTrimReplace = '$1$2$3'; + + +// Something like `1.2.3 - 1.2.4` +// Note that these all use the loose form, because they'll be +// checked against either the strict or loose comparator form +// later. +var HYPHENRANGE = R++; +src[HYPHENRANGE] = '^\\s*(' + src[XRANGEPLAIN] + ')' + + '\\s+-\\s+' + + '(' + src[XRANGEPLAIN] + ')' + + '\\s*$'; + +var HYPHENRANGELOOSE = R++; +src[HYPHENRANGELOOSE] = '^\\s*(' + src[XRANGEPLAINLOOSE] + ')' + + '\\s+-\\s+' + + '(' + src[XRANGEPLAINLOOSE] + ')' + + '\\s*$'; + +// Star ranges basically just allow anything at all. +var STAR = R++; +src[STAR] = '(<|>)?=?\\s*\\*'; + +// Compile to actual regexp objects. +// All are flag-free, unless they were created above with a flag. +for (var i = 0; i < R; i++) { + debug(i, src[i]); + if (!re[i]) + re[i] = new RegExp(src[i]); +} + +exports.parse = parse; +function parse(version, loose) { + var r = loose ? re[LOOSE] : re[FULL]; + return (r.test(version)) ? new SemVer(version, loose) : null; +} + +exports.valid = valid; +function valid(version, loose) { + var v = parse(version, loose); + return v ? v.version : null; +} + + +exports.clean = clean; +function clean(version, loose) { + var s = parse(version, loose); + return s ? s.version : null; +} + +// ***** MODIFIED LINE BELOW ***** +window.semver = exports.SemVer = SemVer; + +function SemVer(version, loose) { + if (version instanceof SemVer) { + if (version.loose === loose) + return version; + else + version = version.version; + } + + if (!(this instanceof SemVer)) + return new SemVer(version, loose); + + debug('SemVer', version, loose); + this.loose = loose; + var m = version.trim().match(loose ? re[LOOSE] : re[FULL]); + + if (!m) + throw new TypeError('Invalid Version: ' + version); + + this.raw = version; + + // these are actually numbers + this.major = +m[1]; + this.minor = +m[2]; + this.patch = +m[3]; + + // numberify any prerelease numeric ids + if (!m[4]) + this.prerelease = []; + else + this.prerelease = m[4].split('.').map(function(id) { + return (/^[0-9]+$/.test(id)) ? +id : id; + }); + + this.build = m[5] ? m[5].split('.') : []; + this.format(); +} + +SemVer.prototype.format = function() { + this.version = this.major + '.' + this.minor + '.' + this.patch; + if (this.prerelease.length) + this.version += '-' + this.prerelease.join('.'); + return this.version; +}; + +SemVer.prototype.inspect = function() { + return '<SemVer "' + this + '">'; +}; + +SemVer.prototype.toString = function() { + return this.version; +}; + +SemVer.prototype.compare = function(other) { + debug('SemVer.compare', this.version, this.loose, other); + if (!(other instanceof SemVer)) + other = new SemVer(other, this.loose); + + return this.compareMain(other) || this.comparePre(other); +}; + +SemVer.prototype.compareMain = function(other) { + if (!(other instanceof SemVer)) + other = new SemVer(other, this.loose); + + return compareIdentifiers(this.major, other.major) || + compareIdentifiers(this.minor, other.minor) || + compareIdentifiers(this.patch, other.patch); +}; + +SemVer.prototype.comparePre = function(other) { + if (!(other instanceof SemVer)) + other = new SemVer(other, this.loose); + + // NOT having a prerelease is > having one + if (this.prerelease.length && !other.prerelease.length) + return -1; + else if (!this.prerelease.length && other.prerelease.length) + return 1; + else if (!this.prerelease.lenth && !other.prerelease.length) + return 0; + + var i = 0; + do { + var a = this.prerelease[i]; + var b = other.prerelease[i]; + debug('prerelease compare', i, a, b); + if (a === undefined && b === undefined) + return 0; + else if (b === undefined) + return 1; + else if (a === undefined) + return -1; + else if (a === b) + continue; + else + return compareIdentifiers(a, b); + } while (++i); +}; + +SemVer.prototype.inc = function(release) { + switch (release) { + case 'major': + this.major++; + this.minor = -1; + case 'minor': + this.minor++; + this.patch = -1; + case 'patch': + this.patch++; + this.prerelease = []; + break; + case 'prerelease': + if (this.prerelease.length === 0) + this.prerelease = [0]; + else { + var i = this.prerelease.length; + while (--i >= 0) { + if (typeof this.prerelease[i] === 'number') { + this.prerelease[i]++; + i = -2; + } + } + if (i === -1) // didn't increment anything + this.prerelease.push(0); + } + break; + + default: + throw new Error('invalid increment argument: ' + release); + } + this.format(); + return this; +}; + +exports.inc = inc; +function inc(version, release, loose) { + try { + return new SemVer(version, loose).inc(release).version; + } catch (er) { + return null; + } +} + +exports.compareIdentifiers = compareIdentifiers; + +var numeric = /^[0-9]+$/; +function compareIdentifiers(a, b) { + var anum = numeric.test(a); + var bnum = numeric.test(b); + + if (anum && bnum) { + a = +a; + b = +b; + } + + return (anum && !bnum) ? -1 : + (bnum && !anum) ? 1 : + a < b ? -1 : + a > b ? 1 : + 0; +} + +exports.rcompareIdentifiers = rcompareIdentifiers; +function rcompareIdentifiers(a, b) { + return compareIdentifiers(b, a); +} + +exports.compare = compare; +function compare(a, b, loose) { + return new SemVer(a, loose).compare(b); +} + +exports.compareLoose = compareLoose; +function compareLoose(a, b) { + return compare(a, b, true); +} + +exports.rcompare = rcompare; +function rcompare(a, b, loose) { + return compare(b, a, loose); +} + +exports.sort = sort; +function sort(list, loose) { + return list.sort(function(a, b) { + return exports.compare(a, b, loose); + }); +} + +exports.rsort = rsort; +function rsort(list, loose) { + return list.sort(function(a, b) { + return exports.rcompare(a, b, loose); + }); +} + +exports.gt = gt; +function gt(a, b, loose) { + return compare(a, b, loose) > 0; +} + +exports.lt = lt; +function lt(a, b, loose) { + return compare(a, b, loose) < 0; +} + +exports.eq = eq; +function eq(a, b, loose) { + return compare(a, b, loose) === 0; +} + +exports.neq = neq; +function neq(a, b, loose) { + return compare(a, b, loose) !== 0; +} + +exports.gte = gte; +function gte(a, b, loose) { + return compare(a, b, loose) >= 0; +} + +exports.lte = lte; +function lte(a, b, loose) { + return compare(a, b, loose) <= 0; +} + +exports.cmp = cmp; +function cmp(a, op, b, loose) { + var ret; + switch (op) { + case '===': ret = a === b; break; + case '!==': ret = a !== b; break; + case '': case '=': case '==': ret = eq(a, b, loose); break; + case '!=': ret = neq(a, b, loose); break; + case '>': ret = gt(a, b, loose); break; + case '>=': ret = gte(a, b, loose); break; + case '<': ret = lt(a, b, loose); break; + case '<=': ret = lte(a, b, loose); break; + default: throw new TypeError('Invalid operator: ' + op); + } + return ret; +} + +exports.Comparator = Comparator; +function Comparator(comp, loose) { + if (comp instanceof Comparator) { + if (comp.loose === loose) + return comp; + else + comp = comp.value; + } + + if (!(this instanceof Comparator)) + return new Comparator(comp, loose); + + debug('comparator', comp, loose); + this.loose = loose; + this.parse(comp); + + if (this.semver === ANY) + this.value = ''; + else + this.value = this.operator + this.semver.version; +} + +var ANY = {}; +Comparator.prototype.parse = function(comp) { + var r = this.loose ? re[COMPARATORLOOSE] : re[COMPARATOR]; + var m = comp.match(r); + + if (!m) + throw new TypeError('Invalid comparator: ' + comp); + + this.operator = m[1]; + // if it literally is just '>' or '' then allow anything. + if (!m[2]) + this.semver = ANY; + else { + this.semver = new SemVer(m[2], this.loose); + + // <1.2.3-rc DOES allow 1.2.3-beta (has prerelease) + // >=1.2.3 DOES NOT allow 1.2.3-beta + // <=1.2.3 DOES allow 1.2.3-beta + // However, <1.2.3 does NOT allow 1.2.3-beta, + // even though `1.2.3-beta < 1.2.3` + // The assumption is that the 1.2.3 version has something you + // *don't* want, so we push the prerelease down to the minimum. + if (this.operator === '<' && !this.semver.prerelease.length) { + this.semver.prerelease = ['0']; + this.semver.format(); + } + } +}; + +Comparator.prototype.inspect = function() { + return '<SemVer Comparator "' + this + '">'; +}; + +Comparator.prototype.toString = function() { + return this.value; +}; + +Comparator.prototype.test = function(version) { + debug('Comparator.test', version, this.loose); + return (this.semver === ANY) ? true : + cmp(version, this.operator, this.semver, this.loose); +}; + + +exports.Range = Range; +function Range(range, loose) { + if ((range instanceof Range) && range.loose === loose) + return range; + + if (!(this instanceof Range)) + return new Range(range, loose); + + this.loose = loose; + + // First, split based on boolean or || + this.raw = range; + this.set = range.split(/\s*\|\|\s*/).map(function(range) { + return this.parseRange(range.trim()); + }, this).filter(function(c) { + // throw out any that are not relevant for whatever reason + return c.length; + }); + + if (!this.set.length) { + throw new TypeError('Invalid SemVer Range: ' + range); + } + + this.format(); +} + +Range.prototype.inspect = function() { + return '<SemVer Range "' + this.range + '">'; +}; + +Range.prototype.format = function() { + this.range = this.set.map(function(comps) { + return comps.join(' ').trim(); + }).join('||').trim(); + return this.range; +}; + +Range.prototype.toString = function() { + return this.range; +}; + +Range.prototype.parseRange = function(range) { + var loose = this.loose; + range = range.trim(); + debug('range', range, loose); + // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` + var hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE]; + range = range.replace(hr, hyphenReplace); + debug('hyphen replace', range); + // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` + range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace); + debug('comparator trim', range, re[COMPARATORTRIM]); + + // `~ 1.2.3` => `~1.2.3` + range = range.replace(re[TILDETRIM], tildeTrimReplace); + + // `^ 1.2.3` => `^1.2.3` + range = range.replace(re[CARETTRIM], caretTrimReplace); + + // normalize spaces + range = range.split(/\s+/).join(' '); + + // At this point, the range is completely trimmed and + // ready to be split into comparators. + + var compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR]; + var set = range.split(' ').map(function(comp) { + return parseComparator(comp, loose); + }).join(' ').split(/\s+/); + if (this.loose) { + // in loose mode, throw out any that are not valid comparators + set = set.filter(function(comp) { + return !!comp.match(compRe); + }); + } + set = set.map(function(comp) { + return new Comparator(comp, loose); + }); + + return set; +}; + +// Mostly just for testing and legacy API reasons +exports.toComparators = toComparators; +function toComparators(range, loose) { + return new Range(range, loose).set.map(function(comp) { + return comp.map(function(c) { + return c.value; + }).join(' ').trim().split(' '); + }); +} + +// comprised of xranges, tildes, stars, and gtlt's at this point. +// already replaced the hyphen ranges +// turn into a set of JUST comparators. +function parseComparator(comp, loose) { + debug('comp', comp); + comp = replaceCarets(comp, loose); + debug('caret', comp); + comp = replaceTildes(comp, loose); + debug('tildes', comp); + comp = replaceXRanges(comp, loose); + debug('xrange', comp); + comp = replaceStars(comp, loose); + debug('stars', comp); + return comp; +} + +function isX(id) { + return !id || id.toLowerCase() === 'x' || id === '*'; +} + +// ~, ~> --> * (any, kinda silly) +// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 +// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 +// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 +// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 +// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 +function replaceTildes(comp, loose) { + return comp.trim().split(/\s+/).map(function(comp) { + return replaceTilde(comp, loose); + }).join(' '); +} + +function replaceTilde(comp, loose) { + var r = loose ? re[TILDELOOSE] : re[TILDE]; + return comp.replace(r, function(_, M, m, p, pr) { + debug('tilde', comp, _, M, m, p, pr); + var ret; + + if (isX(M)) + ret = ''; + else if (isX(m)) + ret = '>=' + M + '.0.0-0 <' + (+M + 1) + '.0.0-0'; + else if (isX(p)) + // ~1.2 == >=1.2.0- <1.3.0- + ret = '>=' + M + '.' + m + '.0-0 <' + M + '.' + (+m + 1) + '.0-0'; + else if (pr) { + debug('replaceTilde pr', pr); + if (pr.charAt(0) !== '-') + pr = '-' + pr; + ret = '>=' + M + '.' + m + '.' + p + pr + + ' <' + M + '.' + (+m + 1) + '.0-0'; + } else + // ~1.2.3 == >=1.2.3-0 <1.3.0-0 + ret = '>=' + M + '.' + m + '.' + p + '-0' + + ' <' + M + '.' + (+m + 1) + '.0-0'; + + debug('tilde return', ret); + return ret; + }); +} + +// ^ --> * (any, kinda silly) +// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 +// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 +// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 +// ^1.2.3 --> >=1.2.3 <2.0.0 +// ^1.2.0 --> >=1.2.0 <2.0.0 +function replaceCarets(comp, loose) { + return comp.trim().split(/\s+/).map(function(comp) { + return replaceCaret(comp, loose); + }).join(' '); +} + +function replaceCaret(comp, loose) { + var r = loose ? re[CARETLOOSE] : re[CARET]; + return comp.replace(r, function(_, M, m, p, pr) { + debug('caret', comp, _, M, m, p, pr); + var ret; + + if (isX(M)) + ret = ''; + else if (isX(m)) + ret = '>=' + M + '.0.0-0 <' + (+M + 1) + '.0.0-0'; + else if (isX(p)) { + if (M === '0') + ret = '>=' + M + '.' + m + '.0-0 <' + M + '.' + (+m + 1) + '.0-0'; + else + ret = '>=' + M + '.' + m + '.0-0 <' + (+M + 1) + '.0.0-0'; + } else if (pr) { + debug('replaceCaret pr', pr); + if (pr.charAt(0) !== '-') + pr = '-' + pr; + if (M === '0') { + if (m === '0') + ret = '=' + M + '.' + m + '.' + p + pr; + else + ret = '>=' + M + '.' + m + '.' + p + pr + + ' <' + M + '.' + (+m + 1) + '.0-0'; + } else + ret = '>=' + M + '.' + m + '.' + p + pr + + ' <' + (+M + 1) + '.0.0-0'; + } else { + if (M === '0') { + if (m === '0') + ret = '=' + M + '.' + m + '.' + p; + else + ret = '>=' + M + '.' + m + '.' + p + '-0' + + ' <' + M + '.' + (+m + 1) + '.0-0'; + } else + ret = '>=' + M + '.' + m + '.' + p + '-0' + + ' <' + (+M + 1) + '.0.0-0'; + } + + debug('caret return', ret); + return ret; + }); +} + +function replaceXRanges(comp, loose) { + debug('replaceXRanges', comp, loose); + return comp.split(/\s+/).map(function(comp) { + return replaceXRange(comp, loose); + }).join(' '); +} + +function replaceXRange(comp, loose) { + comp = comp.trim(); + var r = loose ? re[XRANGELOOSE] : re[XRANGE]; + return comp.replace(r, function(ret, gtlt, M, m, p, pr) { + debug('xRange', comp, ret, gtlt, M, m, p, pr); + var xM = isX(M); + var xm = xM || isX(m); + var xp = xm || isX(p); + var anyX = xp; + + if (gtlt === '=' && anyX) + gtlt = ''; + + if (gtlt && anyX) { + // replace X with 0, and then append the -0 min-prerelease + if (xM) + M = 0; + if (xm) + m = 0; + if (xp) + p = 0; + + if (gtlt === '>') { + // >1 => >=2.0.0-0 + // >1.2 => >=1.3.0-0 + // >1.2.3 => >= 1.2.4-0 + gtlt = '>='; + if (xM) { + // no change + } else if (xm) { + M = +M + 1; + m = 0; + p = 0; + } else if (xp) { + m = +m + 1; + p = 0; + } + } + + + ret = gtlt + M + '.' + m + '.' + p + '-0'; + } else if (xM) { + // allow any + ret = '*'; + } else if (xm) { + // append '-0' onto the version, otherwise + // '1.x.x' matches '2.0.0-beta', since the tag + // *lowers* the version value + ret = '>=' + M + '.0.0-0 <' + (+M + 1) + '.0.0-0'; + } else if (xp) { + ret = '>=' + M + '.' + m + '.0-0 <' + M + '.' + (+m + 1) + '.0-0'; + } + + debug('xRange return', ret); + + return ret; + }); +} + +// Because * is AND-ed with everything else in the comparator, +// and '' means "any version", just remove the *s entirely. +function replaceStars(comp, loose) { + debug('replaceStars', comp, loose); + // Looseness is ignored here. star is always as loose as it gets! + return comp.trim().replace(re[STAR], ''); +} + +// This function is passed to string.replace(re[HYPHENRANGE]) +// M, m, patch, prerelease, build +// 1.2 - 3.4.5 => >=1.2.0-0 <=3.4.5 +// 1.2.3 - 3.4 => >=1.2.0-0 <3.5.0-0 Any 3.4.x will do +// 1.2 - 3.4 => >=1.2.0-0 <3.5.0-0 +function hyphenReplace($0, + from, fM, fm, fp, fpr, fb, + to, tM, tm, tp, tpr, tb) { + + if (isX(fM)) + from = ''; + else if (isX(fm)) + from = '>=' + fM + '.0.0-0'; + else if (isX(fp)) + from = '>=' + fM + '.' + fm + '.0-0'; + else + from = '>=' + from; + + if (isX(tM)) + to = ''; + else if (isX(tm)) + to = '<' + (+tM + 1) + '.0.0-0'; + else if (isX(tp)) + to = '<' + tM + '.' + (+tm + 1) + '.0-0'; + else if (tpr) + to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr; + else + to = '<=' + to; + + return (from + ' ' + to).trim(); +} + + +// if ANY of the sets match ALL of its comparators, then pass +Range.prototype.test = function(version) { + if (!version) + return false; + for (var i = 0; i < this.set.length; i++) { + if (testSet(this.set[i], version)) + return true; + } + return false; +}; + +function testSet(set, version) { + for (var i = 0; i < set.length; i++) { + if (!set[i].test(version)) + return false; + } + return true; +} + +exports.satisfies = satisfies; +function satisfies(version, range, loose) { + try { + range = new Range(range, loose); + } catch (er) { + return false; + } + return range.test(version); +} + +exports.maxSatisfying = maxSatisfying; +function maxSatisfying(versions, range, loose) { + return versions.filter(function(version) { + return satisfies(version, range, loose); + }).sort(function(a, b) { + return rcompare(a, b, loose); + })[0] || null; +} + +exports.validRange = validRange; +function validRange(range, loose) { + try { + // Return '*' instead of '' so that truthiness works. + // This will throw if it's invalid anyway + return new Range(range, loose).range || '*'; + } catch (er) { + return null; + } +} + +// Determine if version is less than all the versions possible in the range +exports.ltr = ltr; +function ltr(version, range, loose) { + return outside(version, range, '<', loose); +} + +// Determine if version is greater than all the versions possible in the range. +exports.gtr = gtr; +function gtr(version, range, loose) { + return outside(version, range, '>', loose); +} + +exports.outside = outside; +function outside(version, range, hilo, loose) { + version = new SemVer(version, loose); + range = new Range(range, loose); + + var gtfn, ltefn, ltfn, comp, ecomp; + switch (hilo) { + case '>': + gtfn = gt; + ltefn = lte; + ltfn = lt; + comp = '>'; + ecomp = '>='; + break; + case '<': + gtfn = lt; + ltefn = gte; + ltfn = gt; + comp = '<'; + ecomp = '<='; + break; + default: + throw new TypeError('Must provide a hilo val of "<" or ">"'); + } + + // If it satisifes the range it is not outside + if (satisfies(version, range, loose)) { + return false; + } + + // From now on, variable terms are as if we're in "gtr" mode. + // but note that everything is flipped for the "ltr" function. + + for (var i = 0; i < range.set.length; ++i) { + var comparators = range.set[i]; + + var high = null; + var low = null; + + comparators.forEach(function(comparator) { + high = high || comparator; + low = low || comparator; + if (gtfn(comparator.semver, high.semver, loose)) { + high = comparator; + } else if (ltfn(comparator.semver, low.semver, loose)) { + low = comparator; + } + }); + + // If the edge version comparator has a operator then our version + // isn't outside it + if (high.operator === comp || high.operator === ecomp) { + return false; + } + + // If the lowest version comparator has an operator and our version + // is less than it then it isn't higher than the range + if ((!low.operator || low.operator === comp) && + ltefn(version, low.semver)) { + return false; + } else if (low.operator === ecomp && ltfn(version, low.semver)) { + return false; + } + } + return true; +} + +// Use the define() function if we're in AMD land +if (typeof define === 'function' && define.amd) + define(exports); +// ***** MODIFIED LINE BELOW ***** +})(); \ No newline at end of file diff --git a/vendor/assets/javascripts/jquery-tablesorter/extras/semver.js b/vendor/assets/javascripts/jquery-tablesorter/extras/semver.js new file mode 100644 index 0000000..9e9470d --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/extras/semver.js @@ -0,0 +1,1011 @@ +// export the class if we are in a Node-like system. +if (typeof module === 'object' && module.exports === exports) + exports = module.exports = SemVer; + +// The debug function is excluded entirely from the minified version. +/* nomin */ var debug; +/* nomin */ if (typeof process === 'object' && + /* nomin */ process.env && + /* nomin */ process.env.NODE_DEBUG && + /* nomin */ /\bsemver\b/i.test(process.env.NODE_DEBUG)) + /* nomin */ debug = function() { + /* nomin */ var args = Array.prototype.slice.call(arguments, 0); + /* nomin */ args.unshift('SEMVER'); + /* nomin */ console.log.apply(console, args); + /* nomin */ }; +/* nomin */ else + /* nomin */ debug = function() {}; + +// Note: this is the semver.org version of the spec that it implements +// Not necessarily the package version of this code. +exports.SEMVER_SPEC_VERSION = '2.0.0'; + +// The actual regexps go on exports.re +var re = exports.re = []; +var src = exports.src = []; +var R = 0; + +// The following Regular Expressions can be used for tokenizing, +// validating, and parsing SemVer version strings. + +// ## Numeric Identifier +// A single `0`, or a non-zero digit followed by zero or more digits. + +var NUMERICIDENTIFIER = R++; +src[NUMERICIDENTIFIER] = '0|[1-9]\\d*'; +var NUMERICIDENTIFIERLOOSE = R++; +src[NUMERICIDENTIFIERLOOSE] = '[0-9]+'; + + +// ## Non-numeric Identifier +// Zero or more digits, followed by a letter or hyphen, and then zero or +// more letters, digits, or hyphens. + +var NONNUMERICIDENTIFIER = R++; +src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*'; + + +// ## Main Version +// Three dot-separated numeric identifiers. + +var MAINVERSION = R++; +src[MAINVERSION] = '(' + src[NUMERICIDENTIFIER] + ')\\.' + + '(' + src[NUMERICIDENTIFIER] + ')\\.' + + '(' + src[NUMERICIDENTIFIER] + ')'; + +var MAINVERSIONLOOSE = R++; +src[MAINVERSIONLOOSE] = '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[NUMERICIDENTIFIERLOOSE] + ')'; + +// ## Pre-release Version Identifier +// A numeric identifier, or a non-numeric identifier. + +var PRERELEASEIDENTIFIER = R++; +src[PRERELEASEIDENTIFIER] = '(?:' + src[NUMERICIDENTIFIER] + + '|' + src[NONNUMERICIDENTIFIER] + ')'; + +var PRERELEASEIDENTIFIERLOOSE = R++; +src[PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[NUMERICIDENTIFIERLOOSE] + + '|' + src[NONNUMERICIDENTIFIER] + ')'; + + +// ## Pre-release Version +// Hyphen, followed by one or more dot-separated pre-release version +// identifiers. + +var PRERELEASE = R++; +src[PRERELEASE] = '(?:-(' + src[PRERELEASEIDENTIFIER] + + '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))'; + +var PRERELEASELOOSE = R++; +src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] + + '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))'; + +// ## Build Metadata Identifier +// Any combination of digits, letters, or hyphens. + +var BUILDIDENTIFIER = R++; +src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+'; + +// ## Build Metadata +// Plus sign, followed by one or more period-separated build metadata +// identifiers. + +var BUILD = R++; +src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] + + '(?:\\.' + src[BUILDIDENTIFIER] + ')*))'; + + +// ## Full Version String +// A main version, followed optionally by a pre-release version and +// build metadata. + +// Note that the only major, minor, patch, and pre-release sections of +// the version string are capturing groups. The build metadata is not a +// capturing group, because it should not ever be used in version +// comparison. + +var FULL = R++; +var FULLPLAIN = 'v?' + src[MAINVERSION] + + src[PRERELEASE] + '?' + + src[BUILD] + '?'; + +src[FULL] = '^' + FULLPLAIN + '$'; + +// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. +// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty +// common in the npm registry. +var LOOSEPLAIN = '[v=\\s]*' + src[MAINVERSIONLOOSE] + + src[PRERELEASELOOSE] + '?' + + src[BUILD] + '?'; + +var LOOSE = R++; +src[LOOSE] = '^' + LOOSEPLAIN + '$'; + +var GTLT = R++; +src[GTLT] = '((?:<|>)?=?)'; + +// Something like "2.*" or "1.2.x". +// Note that "x.x" is a valid xRange identifer, meaning "any version" +// Only the first item is strictly required. +var XRANGEIDENTIFIERLOOSE = R++; +src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*'; +var XRANGEIDENTIFIER = R++; +src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*'; + +var XRANGEPLAIN = R++; +src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + + '(?:(' + src[PRERELEASE] + ')' + + ')?)?)?'; + +var XRANGEPLAINLOOSE = R++; +src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:(' + src[PRERELEASELOOSE] + ')' + + ')?)?)?'; + +// >=2.x, for example, means >=2.0.0-0 +// <1.x would be the same as "<1.0.0-0", though. +var XRANGE = R++; +src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$'; +var XRANGELOOSE = R++; +src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$'; + +// Tilde ranges. +// Meaning is "reasonably at or greater than" +var LONETILDE = R++; +src[LONETILDE] = '(?:~>?)'; + +var TILDETRIM = R++; +src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+'; +re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g'); +var tildeTrimReplace = '$1~'; + +var TILDE = R++; +src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$'; +var TILDELOOSE = R++; +src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$'; + +// Caret ranges. +// Meaning is "at least and backwards compatible with" +var LONECARET = R++; +src[LONECARET] = '(?:\\^)'; + +var CARETTRIM = R++; +src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+'; +re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g'); +var caretTrimReplace = '$1^'; + +var CARET = R++; +src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$'; +var CARETLOOSE = R++; +src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$'; + +// A simple gt/lt/eq thing, or just "" to indicate "any version" +var COMPARATORLOOSE = R++; +src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$'; +var COMPARATOR = R++; +src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$'; + + +// An expression to strip any whitespace between the gtlt and the thing +// it modifies, so that `> 1.2.3` ==> `>1.2.3` +var COMPARATORTRIM = R++; +src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] + + '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')'; + +// this one has to use the /g flag +re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g'); +var comparatorTrimReplace = '$1$2$3'; + + +// Something like `1.2.3 - 1.2.4` +// Note that these all use the loose form, because they'll be +// checked against either the strict or loose comparator form +// later. +var HYPHENRANGE = R++; +src[HYPHENRANGE] = '^\\s*(' + src[XRANGEPLAIN] + ')' + + '\\s+-\\s+' + + '(' + src[XRANGEPLAIN] + ')' + + '\\s*$'; + +var HYPHENRANGELOOSE = R++; +src[HYPHENRANGELOOSE] = '^\\s*(' + src[XRANGEPLAINLOOSE] + ')' + + '\\s+-\\s+' + + '(' + src[XRANGEPLAINLOOSE] + ')' + + '\\s*$'; + +// Star ranges basically just allow anything at all. +var STAR = R++; +src[STAR] = '(<|>)?=?\\s*\\*'; + +// Compile to actual regexp objects. +// All are flag-free, unless they were created above with a flag. +for (var i = 0; i < R; i++) { + debug(i, src[i]); + if (!re[i]) + re[i] = new RegExp(src[i]); +} + +exports.parse = parse; +function parse(version, loose) { + var r = loose ? re[LOOSE] : re[FULL]; + return (r.test(version)) ? new SemVer(version, loose) : null; +} + +exports.valid = valid; +function valid(version, loose) { + var v = parse(version, loose); + return v ? v.version : null; +} + + +exports.clean = clean; +function clean(version, loose) { + var s = parse(version, loose); + return s ? s.version : null; +} + +exports.SemVer = SemVer; + +function SemVer(version, loose) { + if (version instanceof SemVer) { + if (version.loose === loose) + return version; + else + version = version.version; + } + + if (!(this instanceof SemVer)) + return new SemVer(version, loose); + + debug('SemVer', version, loose); + this.loose = loose; + var m = version.trim().match(loose ? re[LOOSE] : re[FULL]); + + if (!m) + throw new TypeError('Invalid Version: ' + version); + + this.raw = version; + + // these are actually numbers + this.major = +m[1]; + this.minor = +m[2]; + this.patch = +m[3]; + + // numberify any prerelease numeric ids + if (!m[4]) + this.prerelease = []; + else + this.prerelease = m[4].split('.').map(function(id) { + return (/^[0-9]+$/.test(id)) ? +id : id; + }); + + this.build = m[5] ? m[5].split('.') : []; + this.format(); +} + +SemVer.prototype.format = function() { + this.version = this.major + '.' + this.minor + '.' + this.patch; + if (this.prerelease.length) + this.version += '-' + this.prerelease.join('.'); + return this.version; +}; + +SemVer.prototype.inspect = function() { + return '<SemVer "' + this + '">'; +}; + +SemVer.prototype.toString = function() { + return this.version; +}; + +SemVer.prototype.compare = function(other) { + debug('SemVer.compare', this.version, this.loose, other); + if (!(other instanceof SemVer)) + other = new SemVer(other, this.loose); + + return this.compareMain(other) || this.comparePre(other); +}; + +SemVer.prototype.compareMain = function(other) { + if (!(other instanceof SemVer)) + other = new SemVer(other, this.loose); + + return compareIdentifiers(this.major, other.major) || + compareIdentifiers(this.minor, other.minor) || + compareIdentifiers(this.patch, other.patch); +}; + +SemVer.prototype.comparePre = function(other) { + if (!(other instanceof SemVer)) + other = new SemVer(other, this.loose); + + // NOT having a prerelease is > having one + if (this.prerelease.length && !other.prerelease.length) + return -1; + else if (!this.prerelease.length && other.prerelease.length) + return 1; + else if (!this.prerelease.lenth && !other.prerelease.length) + return 0; + + var i = 0; + do { + var a = this.prerelease[i]; + var b = other.prerelease[i]; + debug('prerelease compare', i, a, b); + if (a === undefined && b === undefined) + return 0; + else if (b === undefined) + return 1; + else if (a === undefined) + return -1; + else if (a === b) + continue; + else + return compareIdentifiers(a, b); + } while (++i); +}; + +SemVer.prototype.inc = function(release) { + switch (release) { + case 'major': + this.major++; + this.minor = -1; + case 'minor': + this.minor++; + this.patch = -1; + case 'patch': + this.patch++; + this.prerelease = []; + break; + case 'prerelease': + if (this.prerelease.length === 0) + this.prerelease = [0]; + else { + var i = this.prerelease.length; + while (--i >= 0) { + if (typeof this.prerelease[i] === 'number') { + this.prerelease[i]++; + i = -2; + } + } + if (i === -1) // didn't increment anything + this.prerelease.push(0); + } + break; + + default: + throw new Error('invalid increment argument: ' + release); + } + this.format(); + return this; +}; + +exports.inc = inc; +function inc(version, release, loose) { + try { + return new SemVer(version, loose).inc(release).version; + } catch (er) { + return null; + } +} + +exports.compareIdentifiers = compareIdentifiers; + +var numeric = /^[0-9]+$/; +function compareIdentifiers(a, b) { + var anum = numeric.test(a); + var bnum = numeric.test(b); + + if (anum && bnum) { + a = +a; + b = +b; + } + + return (anum && !bnum) ? -1 : + (bnum && !anum) ? 1 : + a < b ? -1 : + a > b ? 1 : + 0; +} + +exports.rcompareIdentifiers = rcompareIdentifiers; +function rcompareIdentifiers(a, b) { + return compareIdentifiers(b, a); +} + +exports.compare = compare; +function compare(a, b, loose) { + return new SemVer(a, loose).compare(b); +} + +exports.compareLoose = compareLoose; +function compareLoose(a, b) { + return compare(a, b, true); +} + +exports.rcompare = rcompare; +function rcompare(a, b, loose) { + return compare(b, a, loose); +} + +exports.sort = sort; +function sort(list, loose) { + return list.sort(function(a, b) { + return exports.compare(a, b, loose); + }); +} + +exports.rsort = rsort; +function rsort(list, loose) { + return list.sort(function(a, b) { + return exports.rcompare(a, b, loose); + }); +} + +exports.gt = gt; +function gt(a, b, loose) { + return compare(a, b, loose) > 0; +} + +exports.lt = lt; +function lt(a, b, loose) { + return compare(a, b, loose) < 0; +} + +exports.eq = eq; +function eq(a, b, loose) { + return compare(a, b, loose) === 0; +} + +exports.neq = neq; +function neq(a, b, loose) { + return compare(a, b, loose) !== 0; +} + +exports.gte = gte; +function gte(a, b, loose) { + return compare(a, b, loose) >= 0; +} + +exports.lte = lte; +function lte(a, b, loose) { + return compare(a, b, loose) <= 0; +} + +exports.cmp = cmp; +function cmp(a, op, b, loose) { + var ret; + switch (op) { + case '===': ret = a === b; break; + case '!==': ret = a !== b; break; + case '': case '=': case '==': ret = eq(a, b, loose); break; + case '!=': ret = neq(a, b, loose); break; + case '>': ret = gt(a, b, loose); break; + case '>=': ret = gte(a, b, loose); break; + case '<': ret = lt(a, b, loose); break; + case '<=': ret = lte(a, b, loose); break; + default: throw new TypeError('Invalid operator: ' + op); + } + return ret; +} + +exports.Comparator = Comparator; +function Comparator(comp, loose) { + if (comp instanceof Comparator) { + if (comp.loose === loose) + return comp; + else + comp = comp.value; + } + + if (!(this instanceof Comparator)) + return new Comparator(comp, loose); + + debug('comparator', comp, loose); + this.loose = loose; + this.parse(comp); + + if (this.semver === ANY) + this.value = ''; + else + this.value = this.operator + this.semver.version; +} + +var ANY = {}; +Comparator.prototype.parse = function(comp) { + var r = this.loose ? re[COMPARATORLOOSE] : re[COMPARATOR]; + var m = comp.match(r); + + if (!m) + throw new TypeError('Invalid comparator: ' + comp); + + this.operator = m[1]; + // if it literally is just '>' or '' then allow anything. + if (!m[2]) + this.semver = ANY; + else { + this.semver = new SemVer(m[2], this.loose); + + // <1.2.3-rc DOES allow 1.2.3-beta (has prerelease) + // >=1.2.3 DOES NOT allow 1.2.3-beta + // <=1.2.3 DOES allow 1.2.3-beta + // However, <1.2.3 does NOT allow 1.2.3-beta, + // even though `1.2.3-beta < 1.2.3` + // The assumption is that the 1.2.3 version has something you + // *don't* want, so we push the prerelease down to the minimum. + if (this.operator === '<' && !this.semver.prerelease.length) { + this.semver.prerelease = ['0']; + this.semver.format(); + } + } +}; + +Comparator.prototype.inspect = function() { + return '<SemVer Comparator "' + this + '">'; +}; + +Comparator.prototype.toString = function() { + return this.value; +}; + +Comparator.prototype.test = function(version) { + debug('Comparator.test', version, this.loose); + return (this.semver === ANY) ? true : + cmp(version, this.operator, this.semver, this.loose); +}; + + +exports.Range = Range; +function Range(range, loose) { + if ((range instanceof Range) && range.loose === loose) + return range; + + if (!(this instanceof Range)) + return new Range(range, loose); + + this.loose = loose; + + // First, split based on boolean or || + this.raw = range; + this.set = range.split(/\s*\|\|\s*/).map(function(range) { + return this.parseRange(range.trim()); + }, this).filter(function(c) { + // throw out any that are not relevant for whatever reason + return c.length; + }); + + if (!this.set.length) { + throw new TypeError('Invalid SemVer Range: ' + range); + } + + this.format(); +} + +Range.prototype.inspect = function() { + return '<SemVer Range "' + this.range + '">'; +}; + +Range.prototype.format = function() { + this.range = this.set.map(function(comps) { + return comps.join(' ').trim(); + }).join('||').trim(); + return this.range; +}; + +Range.prototype.toString = function() { + return this.range; +}; + +Range.prototype.parseRange = function(range) { + var loose = this.loose; + range = range.trim(); + debug('range', range, loose); + // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` + var hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE]; + range = range.replace(hr, hyphenReplace); + debug('hyphen replace', range); + // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` + range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace); + debug('comparator trim', range, re[COMPARATORTRIM]); + + // `~ 1.2.3` => `~1.2.3` + range = range.replace(re[TILDETRIM], tildeTrimReplace); + + // `^ 1.2.3` => `^1.2.3` + range = range.replace(re[CARETTRIM], caretTrimReplace); + + // normalize spaces + range = range.split(/\s+/).join(' '); + + // At this point, the range is completely trimmed and + // ready to be split into comparators. + + var compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR]; + var set = range.split(' ').map(function(comp) { + return parseComparator(comp, loose); + }).join(' ').split(/\s+/); + if (this.loose) { + // in loose mode, throw out any that are not valid comparators + set = set.filter(function(comp) { + return !!comp.match(compRe); + }); + } + set = set.map(function(comp) { + return new Comparator(comp, loose); + }); + + return set; +}; + +// Mostly just for testing and legacy API reasons +exports.toComparators = toComparators; +function toComparators(range, loose) { + return new Range(range, loose).set.map(function(comp) { + return comp.map(function(c) { + return c.value; + }).join(' ').trim().split(' '); + }); +} + +// comprised of xranges, tildes, stars, and gtlt's at this point. +// already replaced the hyphen ranges +// turn into a set of JUST comparators. +function parseComparator(comp, loose) { + debug('comp', comp); + comp = replaceCarets(comp, loose); + debug('caret', comp); + comp = replaceTildes(comp, loose); + debug('tildes', comp); + comp = replaceXRanges(comp, loose); + debug('xrange', comp); + comp = replaceStars(comp, loose); + debug('stars', comp); + return comp; +} + +function isX(id) { + return !id || id.toLowerCase() === 'x' || id === '*'; +} + +// ~, ~> --> * (any, kinda silly) +// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 +// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 +// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 +// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 +// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 +function replaceTildes(comp, loose) { + return comp.trim().split(/\s+/).map(function(comp) { + return replaceTilde(comp, loose); + }).join(' '); +} + +function replaceTilde(comp, loose) { + var r = loose ? re[TILDELOOSE] : re[TILDE]; + return comp.replace(r, function(_, M, m, p, pr) { + debug('tilde', comp, _, M, m, p, pr); + var ret; + + if (isX(M)) + ret = ''; + else if (isX(m)) + ret = '>=' + M + '.0.0-0 <' + (+M + 1) + '.0.0-0'; + else if (isX(p)) + // ~1.2 == >=1.2.0- <1.3.0- + ret = '>=' + M + '.' + m + '.0-0 <' + M + '.' + (+m + 1) + '.0-0'; + else if (pr) { + debug('replaceTilde pr', pr); + if (pr.charAt(0) !== '-') + pr = '-' + pr; + ret = '>=' + M + '.' + m + '.' + p + pr + + ' <' + M + '.' + (+m + 1) + '.0-0'; + } else + // ~1.2.3 == >=1.2.3-0 <1.3.0-0 + ret = '>=' + M + '.' + m + '.' + p + '-0' + + ' <' + M + '.' + (+m + 1) + '.0-0'; + + debug('tilde return', ret); + return ret; + }); +} + +// ^ --> * (any, kinda silly) +// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 +// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 +// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 +// ^1.2.3 --> >=1.2.3 <2.0.0 +// ^1.2.0 --> >=1.2.0 <2.0.0 +function replaceCarets(comp, loose) { + return comp.trim().split(/\s+/).map(function(comp) { + return replaceCaret(comp, loose); + }).join(' '); +} + +function replaceCaret(comp, loose) { + var r = loose ? re[CARETLOOSE] : re[CARET]; + return comp.replace(r, function(_, M, m, p, pr) { + debug('caret', comp, _, M, m, p, pr); + var ret; + + if (isX(M)) + ret = ''; + else if (isX(m)) + ret = '>=' + M + '.0.0-0 <' + (+M + 1) + '.0.0-0'; + else if (isX(p)) { + if (M === '0') + ret = '>=' + M + '.' + m + '.0-0 <' + M + '.' + (+m + 1) + '.0-0'; + else + ret = '>=' + M + '.' + m + '.0-0 <' + (+M + 1) + '.0.0-0'; + } else if (pr) { + debug('replaceCaret pr', pr); + if (pr.charAt(0) !== '-') + pr = '-' + pr; + if (M === '0') { + if (m === '0') + ret = '=' + M + '.' + m + '.' + p + pr; + else + ret = '>=' + M + '.' + m + '.' + p + pr + + ' <' + M + '.' + (+m + 1) + '.0-0'; + } else + ret = '>=' + M + '.' + m + '.' + p + pr + + ' <' + (+M + 1) + '.0.0-0'; + } else { + if (M === '0') { + if (m === '0') + ret = '=' + M + '.' + m + '.' + p; + else + ret = '>=' + M + '.' + m + '.' + p + '-0' + + ' <' + M + '.' + (+m + 1) + '.0-0'; + } else + ret = '>=' + M + '.' + m + '.' + p + '-0' + + ' <' + (+M + 1) + '.0.0-0'; + } + + debug('caret return', ret); + return ret; + }); +} + +function replaceXRanges(comp, loose) { + debug('replaceXRanges', comp, loose); + return comp.split(/\s+/).map(function(comp) { + return replaceXRange(comp, loose); + }).join(' '); +} + +function replaceXRange(comp, loose) { + comp = comp.trim(); + var r = loose ? re[XRANGELOOSE] : re[XRANGE]; + return comp.replace(r, function(ret, gtlt, M, m, p, pr) { + debug('xRange', comp, ret, gtlt, M, m, p, pr); + var xM = isX(M); + var xm = xM || isX(m); + var xp = xm || isX(p); + var anyX = xp; + + if (gtlt === '=' && anyX) + gtlt = ''; + + if (gtlt && anyX) { + // replace X with 0, and then append the -0 min-prerelease + if (xM) + M = 0; + if (xm) + m = 0; + if (xp) + p = 0; + + if (gtlt === '>') { + // >1 => >=2.0.0-0 + // >1.2 => >=1.3.0-0 + // >1.2.3 => >= 1.2.4-0 + gtlt = '>='; + if (xM) { + // no change + } else if (xm) { + M = +M + 1; + m = 0; + p = 0; + } else if (xp) { + m = +m + 1; + p = 0; + } + } + + + ret = gtlt + M + '.' + m + '.' + p + '-0'; + } else if (xM) { + // allow any + ret = '*'; + } else if (xm) { + // append '-0' onto the version, otherwise + // '1.x.x' matches '2.0.0-beta', since the tag + // *lowers* the version value + ret = '>=' + M + '.0.0-0 <' + (+M + 1) + '.0.0-0'; + } else if (xp) { + ret = '>=' + M + '.' + m + '.0-0 <' + M + '.' + (+m + 1) + '.0-0'; + } + + debug('xRange return', ret); + + return ret; + }); +} + +// Because * is AND-ed with everything else in the comparator, +// and '' means "any version", just remove the *s entirely. +function replaceStars(comp, loose) { + debug('replaceStars', comp, loose); + // Looseness is ignored here. star is always as loose as it gets! + return comp.trim().replace(re[STAR], ''); +} + +// This function is passed to string.replace(re[HYPHENRANGE]) +// M, m, patch, prerelease, build +// 1.2 - 3.4.5 => >=1.2.0-0 <=3.4.5 +// 1.2.3 - 3.4 => >=1.2.0-0 <3.5.0-0 Any 3.4.x will do +// 1.2 - 3.4 => >=1.2.0-0 <3.5.0-0 +function hyphenReplace($0, + from, fM, fm, fp, fpr, fb, + to, tM, tm, tp, tpr, tb) { + + if (isX(fM)) + from = ''; + else if (isX(fm)) + from = '>=' + fM + '.0.0-0'; + else if (isX(fp)) + from = '>=' + fM + '.' + fm + '.0-0'; + else + from = '>=' + from; + + if (isX(tM)) + to = ''; + else if (isX(tm)) + to = '<' + (+tM + 1) + '.0.0-0'; + else if (isX(tp)) + to = '<' + tM + '.' + (+tm + 1) + '.0-0'; + else if (tpr) + to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr; + else + to = '<=' + to; + + return (from + ' ' + to).trim(); +} + + +// if ANY of the sets match ALL of its comparators, then pass +Range.prototype.test = function(version) { + if (!version) + return false; + for (var i = 0; i < this.set.length; i++) { + if (testSet(this.set[i], version)) + return true; + } + return false; +}; + +function testSet(set, version) { + for (var i = 0; i < set.length; i++) { + if (!set[i].test(version)) + return false; + } + return true; +} + +exports.satisfies = satisfies; +function satisfies(version, range, loose) { + try { + range = new Range(range, loose); + } catch (er) { + return false; + } + return range.test(version); +} + +exports.maxSatisfying = maxSatisfying; +function maxSatisfying(versions, range, loose) { + return versions.filter(function(version) { + return satisfies(version, range, loose); + }).sort(function(a, b) { + return rcompare(a, b, loose); + })[0] || null; +} + +exports.validRange = validRange; +function validRange(range, loose) { + try { + // Return '*' instead of '' so that truthiness works. + // This will throw if it's invalid anyway + return new Range(range, loose).range || '*'; + } catch (er) { + return null; + } +} + +// Determine if version is less than all the versions possible in the range +exports.ltr = ltr; +function ltr(version, range, loose) { + return outside(version, range, '<', loose); +} + +// Determine if version is greater than all the versions possible in the range. +exports.gtr = gtr; +function gtr(version, range, loose) { + return outside(version, range, '>', loose); +} + +exports.outside = outside; +function outside(version, range, hilo, loose) { + version = new SemVer(version, loose); + range = new Range(range, loose); + + var gtfn, ltefn, ltfn, comp, ecomp; + switch (hilo) { + case '>': + gtfn = gt; + ltefn = lte; + ltfn = lt; + comp = '>'; + ecomp = '>='; + break; + case '<': + gtfn = lt; + ltefn = gte; + ltfn = gt; + comp = '<'; + ecomp = '<='; + break; + default: + throw new TypeError('Must provide a hilo val of "<" or ">"'); + } + + // If it satisifes the range it is not outside + if (satisfies(version, range, loose)) { + return false; + } + + // From now on, variable terms are as if we're in "gtr" mode. + // but note that everything is flipped for the "ltr" function. + + for (var i = 0; i < range.set.length; ++i) { + var comparators = range.set[i]; + + var high = null; + var low = null; + + comparators.forEach(function(comparator) { + high = high || comparator; + low = low || comparator; + if (gtfn(comparator.semver, high.semver, loose)) { + high = comparator; + } else if (ltfn(comparator.semver, low.semver, loose)) { + low = comparator; + } + }); + + // If the edge version comparator has a operator then our version + // isn't outside it + if (high.operator === comp || high.operator === ecomp) { + return false; + } + + // If the lowest version comparator has an operator and our version + // is less than it then it isn't higher than the range + if ((!low.operator || low.operator === comp) && + ltefn(version, low.semver)) { + return false; + } else if (low.operator === ecomp && ltfn(version, low.semver)) { + return false; + } + } + return true; +} + +// Use the define() function if we're in AMD land +if (typeof define === 'function' && define.amd) + define(exports); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index e7ef967..0375d87 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.15.4 - Client-side table sorting with ease! +* TableSorter 2.15.5 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.15.4"; + ts.version = "2.15.5"; ts.parsers = []; ts.widgets = []; diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js new file mode 100644 index 0000000..fe38417 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js @@ -0,0 +1,34 @@ +/*! ISO-8601 date parser + * This parser will work with dates in ISO8601 format + * 2013-02-18T18:18:44+00:00 + * Written by Sean Ellingham :https://github.com/seanellingham + * See https://github.com/Mottie/tablesorter/issues/247 + */ +/*global jQuery: false */ +;(function($){ +"use strict"; + + var iso8601date = /^([0-9]{4})(-([0-9]{2})(-([0-9]{2})(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?$/; + $.tablesorter.addParser({ + id : 'iso8601date', + is : function(s) { + return s.match(iso8601date); + }, + format : function(s) { + var result = s.match(iso8601date); + if (result) { + var date = new Date(result[1], 0, 1); + if (result[3]) { date.setMonth(result[3] - 1); } + if (result[5]) { date.setDate(result[5]); } + if (result[7]) { date.setHours(result[7]); } + if (result[8]) { date.setMinutes(result[8]); } + if (result[10]) { date.setSeconds(result[10]); } + if (result[12]) { date.setMilliseconds(Number('0.' + result[12]) * 1000); } + return date; + } + return 0; + }, + type : 'numeric' + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js new file mode 100644 index 0000000..68689fc --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js @@ -0,0 +1,33 @@ +/*! Month parser + * Demo: http://jsfiddle.net/Mottie/abkNM/477/ + */ +/*jshint jquery:true */ +;(function($){ +"use strict"; + + var ts = $.tablesorter; + ts.dates = $.extend({}, ts.dates, { + // *** modify this array to change match the language *** + monthCased : [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] + }); + ts.dates.monthLower = ts.dates.monthCased.join(',').toLocaleLowerCase().split(','); + + ts.addParser({ + id: "month", + is: function(){ + return false; + }, + format: function(s, table) { + var j = -1, c = table.config; + s = c.ignoreCase ? s.toLocaleLowerCase() : s; + $.each(ts.dates[ 'month' + (c.ignoreCase ? 'Lower' : 'Cased') ], function(i,v){ + if (j < 0 && s.match(v)) { j = i; } + }); + // return s (original string) if there isn't a match + // (non-weekdays will sort separately and empty cells will sort as expected) + return j < 0 ? s : j; + }, + type: "numeric" + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js new file mode 100644 index 0000000..932a32f --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js @@ -0,0 +1,74 @@ +/*! Two digit year parser + * Demo: http://jsfiddle.net/Mottie/abkNM/427/ + */ +/*jshint jquery:true */ +;(function($){ +"use strict"; + + var ts = $.tablesorter, + + // Make the date be within +/- range of the 2 digit year + // so if the current year is 2020, and the 2 digit year is 80 (2080 - 2020 > 50), it becomes 1980 + // if the 2 digit year is 50 (2050 - 2020 < 50), then it becomes 2050. + range = 50; + + ts.dates = $.extend({}, ts.dates, { + regxxxxyy: /(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{2})/, + regyyxxxx: /(\d{2})[\/\s](\d{1,2})[\/\s](\d{1,2})/ + }); + + ts.formatDate = function(s, regex, format, table){ + s = s + // replace separators + .replace(/\s+/g," ").replace(/[-.,]/g, "/") + // reformat xx/xx/xx to mm/dd/19yy; + .replace(regex, format); + var d = new Date(s), + y = d.getFullYear(), + rng = table && table.config.dateRange || range, + now = new Date().getFullYear(); + // if date > 50 years old (set range), add 100 years + // this will work when people start using "50" and mean "2050" + while (now - y > rng) { + y += 100; + } + return d.setFullYear(y); + }; + + $.tablesorter.addParser({ + id: "ddmmyy", + is: function() { + return false; + }, + format: function(s, table) { + // reformat dd/mm/yy to mm/dd/19yy; + return ts.formatDate(s, ts.dates.regxxxxyy, "$2/$1/19$3", table); + }, + type: "numeric" + }); + + $.tablesorter.addParser({ + id: "mmddyy", + is: function() { + return false; + }, + format: function(s, table) { + // reformat mm/dd/yy to mm/dd/19yy + return ts.formatDate(s, ts.dates.regxxxxyy, "$1/$2/19$3", table); + }, + type: "numeric" + }); + + $.tablesorter.addParser({ + id: "yymmdd", + is: function() { + return false; + }, + format: function(s, table) { + // reformat yy/mm/dd to mm/dd/19yy + return ts.formatDate(s, ts.dates.regyyxxxx, "$2/$3/19$1", table); + }, + type: "numeric" + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js new file mode 100644 index 0000000..62f4504 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js @@ -0,0 +1,33 @@ +/*! Weekday parser + * Demo: http://jsfiddle.net/Mottie/abkNM/477/ + */ +/*jshint jquery:true */ +;(function($){ +"use strict"; + + var ts = $.tablesorter; + ts.dates = $.extend({}, ts.dates, { + // *** modify this array to change match the language *** + weekdayCased : [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ] + }); + ts.dates.weekdayLower = ts.dates.weekdayCased.join(',').toLocaleLowerCase().split(','); + + ts.addParser({ + id: "weekday", + is: function(){ + return false; + }, + format: function(s, table) { + var j = -1, c = table.config; + s = c.ignoreCase ? s.toLocaleLowerCase() : s; + $.each(ts.dates[ 'weekday' + (c.ignoreCase ? 'Lower' : 'Cased') ], function(i,v){ + if (j < 0 && s.match(v)) { j = i; } + }); + // return s (original string) if there isn't a match + // (non-weekdays will sort separately and empty cells will sort as expected) + return j < 0 ? s : j; + }, + type: "numeric" + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js new file mode 100644 index 0000000..52ece6c --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js @@ -0,0 +1,36 @@ +/*! + * Extract dates using popular natural language date parsers + */ +/*jshint jquery:true */ +;(function($){ +"use strict"; + + /*! Sugar (http://sugarjs.com/dates#comparing_dates) + * demo: http://jsfiddle.net/Mottie/abkNM/551/ + */ + $.tablesorter.addParser({ + id: "sugar", + is: function() { + return false; + }, + format: function(s) { + return Date.create ? Date.create(s).getTime() || s : new Date(s).getTime() || s; + }, + type: "numeric" + }); + + /*! Datejs (http://www.datejs.com/) + * demo: http://jsfiddle.net/Mottie/abkNM/550/ + */ + $.tablesorter.addParser({ + id: "datejs", + is: function() { + return false; + }, + format: function(s) { + return Date.parse && Date.parse(s) || s; + }, + type: "numeric" + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-feet-inch-fraction.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-feet-inch-fraction.js new file mode 100644 index 0000000..79fdea8 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-feet-inch-fraction.js @@ -0,0 +1,63 @@ +/*! Distance parser +* This parser will parser numbers like 5'10" (5 foot 10 inches) +* and 31½ into sortable values. +* Demo: http://jsfiddle.net/Mottie/abkNM/154/ +*/ +/*global jQuery: false */ +;(function($){ + "use strict"; + + var ts = $.tablesorter; + ts.symbolRegex = /[\u215b\u215c\u215d\u215e\u00bc\u00bd\u00be]/g; + ts.processFractions = function(n, table) { + if (n) { + var t, p = 0; + n = $.trim(n.replace(/\"/,'')); + // look for a space in the first part of the number: "10 3/4" and save the "10" + if (/\s/.test(n)) { + p = ts.formatFloat(n.split(' ')[0], table); + // remove stuff to the left of the space + n = $.trim(n.substring(n.indexOf(' '), n.length)); + } + // look for a "/" to calculate fractions + if (/\//g.test(n)) { + t = n.split('/'); + // turn 3/4 into .75; make sure we don't divide by zero + n = p + parseInt(t[0], 10) / parseInt(t[1] || 1, 10); + // look for fraction symbols + } else if (ts.symbolRegex.test(n)) { + n = p + n.replace(ts.symbolRegex, function(m){ + return { + '\u215b' : '.125', // 1/8 + '\u215c' : '.375', // 3/8 + '\u215d' : '.625', // 5/8 + '\u215e' : '.875', // 7/8 + '\u00bc' : '.25', // 1/4 + '\u00bd' : '.5', // 1/2 + '\u00be' : '.75' // 3/4 + }[m]; + }); + } + } + return n || 0; + }; + + $.tablesorter.addParser({ + id: 'distance', + is: function() { + // return false so this parser is not auto detected + return false; + }, + format: function(s, table) { + if (s === '') { return ''; } + // look for feet symbol = ' + // very generic test to catch 1.1', 1 1/2' and 1½' + var d = (/^\s*\S*(\s+\S+)?\s*\'/.test(s)) ? s.split("'") : [0,s], + f = ts.processFractions(d[0], table), // feet + i = ts.processFractions(d[1], table); // inches + return (/[\'\"]/).test(s) ? parseFloat(f) + (parseFloat(i)/12 || 0) : parseFloat(f) + parseFloat(i); + }, + type: 'numeric' + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js new file mode 100644 index 0000000..c64a82c --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js @@ -0,0 +1,73 @@ +/*! File Type parser + * When a file type extension is found, the equivalent name is + * prefixed into the parsed data, so sorting occurs in groups + */ +/*global jQuery: false */ +;(function($){ +"use strict"; + + // basic list from http://en.wikipedia.org/wiki/List_of_file_formats + // To add a custom equivalent, define: + // $.tablesorter.fileTypes.equivalents['xx'] = "A|B|C"; + $.tablesorter.fileTypes = { + // divides filetype extensions in the equivalent list below + separator : '|', + equivalents : { + "3D Image" : "3dm|3ds|dwg|max|obj", + "Audio" : "aif|aac|ape|flac|la|m4a|mid|midi|mp2|mp3|ogg|ra|raw|rm|wav|wma", + "Compressed" : "7z|bin|cab|cbr|gz|gzip|iso|lha|lz|rar|tar|tgz|zip|zipx|zoo", + "Database" : "csv|dat|db|dbf|json|ldb|mdb|myd|pdb|sql|tsv|wdb|wmdb|xlr|xls|xlsx|xml", + "Development" : "asm|c|class|cls|cpp|cc|cs|cxx|cbp|cs|dba|fla|h|java|lua|pl|py|pyc|pyo|sh|sln|r|rb|vb", + "Document" : "doc|docx|odt|ott|pages|pdf|rtf|tex|wpd|wps|wrd|wri", + "Executable" : "apk|app|com|exe|gadget|lnk|msi", + "Fonts" : "eot|fnt|fon|otf|ttf|woff", + "Icons" : "ani|cur|icns|ico", + "Images" : "bmp|gif|jpg|jpeg|jpe|jp2|pic|png|psd|tga|tif|tiff|wmf|webp", + "Presentation" : "pps|ppt", + "Published" : "chp|epub|lit|pub|ppp|fm|mobi", + "Script" : "as|bat|cgi|cmd|jar|js|lua|scpt|scptd|sh|vbs|vb|wsf", + "Styles" : "css|less|sass", + "Text" : "info|log|md|markdown|nfo|tex|text|txt", + "Vectors" : "awg|ai|eps|cdr|ps|svg", + "Video" : "asf|avi|flv|m4v|mkv|mov|mp4|mpe|mpeg|mpg|ogg|rm|rv|swf|vob|wmv", + "Web" : "asp|aspx|cer|cfm|htm|html|php|url|xhtml" + } + }; + + $.tablesorter.addParser({ + id: 'filetype', + is: function() { + return false; + }, + format: function(s, table) { + var t, + c = table.config, + wo = c.widgetOptions, + i = s.lastIndexOf('.'), + sep = $.tablesorter.fileTypes.separator, + m = $.tablesorter.fileTypes.matching, + types = $.tablesorter.fileTypes.equivalents; + if (!m) { + // make a string to "quick" match the existing equivalents + var t = []; + $.each(types, function(i,v){ + t.push(v); + }); + m = $.tablesorter.fileTypes.matching = sep + t.join(sep) + sep; + } + if (i >= 0) { + t = sep + s.substring(i + 1, s.length) + sep; + if (m.indexOf(t) >= 0) { + for (i in types) { + if ((sep + types[i] + sep).indexOf(t) >= 0) { + return i + (wo.group_separator ? wo.group_separator : '-') + s; + } + } + } + } + return s; + }, + type: 'text' + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js new file mode 100644 index 0000000..d842980 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js @@ -0,0 +1,47 @@ +/*! Title parser + * This parser will remove "The", "A" and "An" from the beginning of a book + * or movie title, so it sorts by the second word or number + * Demo: http://jsfiddle.net/Mottie/abkNM/5/ + */ +/*global jQuery: false */ +;(function($){ +"use strict"; + + // basic list from http://en.wikipedia.org/wiki/Article_%28grammar%29 + $.tablesorter.ignoreArticles = { + "en" : "the, a, an", + "de" : "der, die, das, des, dem, den, ein, eine, einer, eines, einem, einen", + "nl" : "de, het, de, een", + "es" : "el, la, lo, los, las, un, una, unos, unas", + "pt" : "o, a, os, as, um, uma, uns, umas", + "fr" : "le, la, l'_, les, un, une, des", + "it" : "il, lo, la, l'_, i, gli, le, un', uno, una, un", + "hu" : "a, az, egy" + }; + + // To add a custom parser, define: + // $.tablesorter.ignoreArticles['xx'] = "A, B, C"; + // and then set the language id 'xx' in the headers option + // ignoreArticles : 'xx' + + $.tablesorter.addParser({ + id: 'ignoreArticles', + is: function() { + return false; + }, + format: function(s, table, cell, cellIndex) { + var c = table.config, art, lang; + if ( !(c.headers && c.headers[cellIndex] && c.headers[cellIndex].ignoreArticlesRegex) ) { + // initialize - save regex in c.headers[cellIndex].ignoreArticles + if (!c.headers) { c.headers = {}; } + if (!c.headers[cellIndex]) { c.headers[cellIndex] = {}; } + lang = $.tablesorter.getData(c.$headers.eq(cellIndex), c.headers[cellIndex], 'ignoreArticles'); + art = ($.tablesorter.ignoreArticles[lang] || "the, a, an" ) + ""; + c.headers[cellIndex].ignoreArticlesRegex = new RegExp('^(' + $.trim( art.split(/\s*\,\s*/).join('\\s|') + "\\s" ).replace("_\\s","") + ')', 'i'); + } + return (s || '').replace(c.headers[cellIndex].ignoreArticlesRegex, ''); + }, + type: 'text' + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js new file mode 100644 index 0000000..929e4b5 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -0,0 +1,86 @@ +/*! input & select parsers for jQuery 1.7+ & tablesorter 2.7.11+ + * Updated 2/19/2014 (v2.15.0) + * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html + */ +/*jshint browser: true, jquery:true, unused:false */ +;(function($){ +"use strict"; + + var resort = true, // resort table after update + updateServer = function(event, $table, $input){ + // do something here to update your server, if needed + // event = change event object + // $table = jQuery object of the table that was just updated + // $input = jQuery object of the input or select that was modified + }; + + // Custom parser for parsing input values + // updated dynamically using the "change" function below + $.tablesorter.addParser({ + id: "inputs", + is: function(){ + return false; + }, + format: function(s, table, cell) { + return $(cell).find('input').val() || s; + }, + parsed : true, // filter widget flag + type: "text" + }); + + // Custom parser for including checkbox status if using the grouping widget + // updated dynamically using the "change" function below + $.tablesorter.addParser({ + id: "checkbox", + is: function(){ + return false; + }, + format: function(s, table, cell, cellIndex) { + var $c = $(cell).find('input'), + isChecked = $c[0].checked; + // adding class to row, indicating that a checkbox is checked; includes + // a column index in case more than one checkbox happens to be in a row + $c.closest('tr').toggleClass('checked-' + cellIndex, isChecked); + // returning plain language here because this is what is shown in the + // group headers - change it as desired + return $c.length ? isChecked ? 'checked' : 'unchecked' : s; + }, + parsed : true, // filter widget flag + type: "text" + }); + + // Custom parser which returns the currently selected options + // updated dynamically using the "change" function below + $.tablesorter.addParser({ + id: "select", + is: function(){ + return false; + }, + format: function(s, table, cell) { + return $(cell).find('select').val() || s; + }, + parsed : true, // filter widget flag + type: "text" + }); + + // update select and all input types in the tablesorter cache when the change event fires. + // This method only works with jQuery 1.7+ + // you can change it to use delegate (v1.4.3+) or live (v1.3+) as desired + // if this code interferes somehow, target the specific table $('#mytable'), instead of $('table') + $(window).load(function(){ + // this flag prevents the updateCell event from being spammed + // it happens when you modify input text and hit enter + var alreadyUpdating = false; + $('table').find('tbody').on('change', 'select, input', function(e){ + if (!alreadyUpdating) { + var $tar = $(e.target), + $table = $tar.closest('table'); + alreadyUpdating = true; + $table.trigger('updateCell', [ $tar.closest('td'), resort ]); + updateServer(e, $table, $tar); + setTimeout(function(){ alreadyUpdating = false; }, 10); + } + }); + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ipv6.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ipv6.js new file mode 100644 index 0000000..121bc37 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ipv6.js @@ -0,0 +1,76 @@ +/*! IPv6 Address parser (WIP) +* IPv6 Address (ffff:0000:0000:0000:0000:0000:0000:0000) +* needs to support short versions like "::8" or "1:2::7:8" +* and "::00:192.168.10.184" (embedded IPv4 address) +* see http://www.intermapper.com/support/tools/IPV6-Validator.aspx +*/ +/*global jQuery: false */ +;(function($){ + "use strict"; + + var ts = $.tablesorter; + + $.extend( ts.regex, {}, { + ipv4Validate : /((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})/, + ipv4Extract : /([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/, + + // simplified regex from http://www.intermapper.com/support/tools/IPV6-Validator.aspx + // (specifically from http://download.dartware.com/thirdparty/ipv6validator.js) + ipv6Validate : /^\s*((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/i + }); + + ts.addParser({ + id: "ipv6Address", + is: function(s) { + return ts.regex.ipv6Validate.test(s); + }, + format: function(address, table) { + // code modified from http://forrst.com/posts/JS_Expand_Abbreviated_IPv6_Addresses-1OR + var i, t, sides, groups, groupsPresent, + hex = table ? (typeof table === "boolean" ? table : table && table.config.ipv6HexFormat || false) : false, + fullAddress = '', + expandedAddress = '', + validGroupCount = 8, + validGroupSize = 4; + // remove any extra spaces + address = address.replace(/\s*/g, ''); + // look for embedded ipv4 + if (ts.regex.ipv4Validate.test(address)) { + groups = address.match(ts.regex.ipv4Extract); + t = ''; + for (i = 1; i < groups.length; i++){ + t += ('00' + (parseInt(groups[i], 10).toString(16)) ).slice(-2) + ( i === 2 ? ':' : '' ); + } + address = address.replace( ts.regex.ipv4Extract, t ); + } + + if (address.indexOf("::") == -1) { + // All eight groups are present + fullAddress = address; + } else { + // Consecutive groups of zeroes have been collapsed with "::". + sides = address.split("::"); + groupsPresent = 0; + for (i = 0; i < sides.length; i++) { + groupsPresent += sides[i].split(":").length; + } + fullAddress += sides[0] + ":"; + for (i = 0; i < validGroupCount - groupsPresent; i++) { + fullAddress += "0000:"; + } + fullAddress += sides[1]; + } + groups = fullAddress.split(":"); + for (i = 0; i < validGroupCount; i++) { + // it's fastest & easiest for tablesorter to sort decimal values (vs hex) + groups[i] = hex ? ('0000' + groups[i]).slice(-4) : + ('00000' + (parseInt(groups[i], 16) || 0)).slice(-5); + expandedAddress += ( i != validGroupCount-1) ? groups[i] + ':' : groups[i]; + } + return hex ? expandedAddress : expandedAddress.replace(/:/g, ''); + }, + // uses natural sort hex compare + type: "numeric" + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js new file mode 100644 index 0000000..db1f85a --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js @@ -0,0 +1,77 @@ +/*! Metric parser + * Demo: http://jsfiddle.net/Mottie/abkNM/382/ + * Set the metric name in the header (defaults to "m|meter"), e.g. + * <th data-metric-name="b|byte">HDD Size</th> + * <th data-metric-name="m|meter">Distance</th> + */ +/*jshint jquery:true */ +;(function($){ +"use strict"; + + var prefixes = { + // "prefix" : [ base 10, base 2 ] + // skipping IEEE 1541 defined prefixes: kibibyte, mebibyte, etc, for now. + "Y|Yotta|yotta" : [ 1e24, Math.pow(1024, 8) ], // 1024^8 + "Z|Zetta|zetta" : [ 1e21, Math.pow(1024, 7) ], // 1024^7 + "E|Exa|exa" : [ 1e18, Math.pow(1024, 6) ], // 1024^6 + "P|Peta|peta" : [ 1e15, Math.pow(1024, 5) ], // 1024^5 + "T|Tera|tera" : [ 1e12, Math.pow(1024, 4) ], // 1024^4 + "G|Giga|giga" : [ 1e9, Math.pow(1024, 3) ], // 1024^3 + "M|Mega|mega" : [ 1e6, Math.pow(1024, 2) ], // 1024^2 + "k|Kilo|kilo" : [ 1e3, 1024 ], // 1024 + // prefixes below here are rarely, if ever, used in binary + "h|hecto" : [ 1e2, 1e2 ], + "da|deka" : [ 1e1, 1e1 ], + "d|deci" : [ 1e-1, 1e-1 ], + "c|centi" : [ 1e-2, 1e-2], + "m|milli" : [ 1e-3, 1e-3 ], + "µ|micro" : [ 1e-6, 1e-6 ], + "n|nano" : [ 1e-9, 1e-9 ], + "p|pico" : [ 1e-12, 1e-12 ], + "f|femto" : [ 1e-15, 1e-15 ], + "a|atto" : [ 1e-18, 1e-18 ], + "z|zepto" : [ 1e-21, 1e-21 ], + "y|yocto" : [ 1e-24, 1e-24 ] + }, + // the \\d+ will not catch digits with spaces, commas or decimals; so use the value from n instead + RegLong = "(\\d+)(\\s+)?([Zz]etta|[Ee]xa|[Pp]eta|[Tt]era|[Gg]iga|[Mm]ega|kilo|hecto|deka|deci|centi|milli|micro|nano|pico|femto|atto|zepto|yocto)(", + RegAbbr = "(\\d+)(\\s+)?(Z|E|P|T|G|M|k|h|da|d|c|m|µ|n|p|f|a|z|y)("; + + $.tablesorter.addParser({ + id: 'metric', + is: function() { + return false; + }, + format: function(s, table, cell, cellIndex) { + var v = 'm|meter', + b, t, + // process number here to get a numerical format (us or eu) + n = $.tablesorter.formatFloat(s.replace(/[^\w,. \-()]/g, ""), table), + $t = table.config.$headers.filter('[data-column="' + cellIndex + '"]'), + m = $t.data('metric'); + if (!m) { + // stored values + t = ($t.attr('data-metric-name') || v).split('|'); + m = [ t[1] || t[0].substring(1), t[0] ]; + m[2] = new RegExp(RegLong + m[0] + "|" + m[1] + ")"); + m[3] = new RegExp(RegAbbr + m[1] + ")"); + $t.data('metric', m); + } + // find match to full name or abbreviation + t = s.match(m[2]) || s.match(m[3]); + if (t) { + for (v in prefixes) { + if (t[3].match(v)) { + // exception when using binary prefix + // change base for binary use + b = /^[b|bit|byte|o|octet]/.test(t[4]) ? 1 : 0; + return n * prefixes[v][b]; + } + } + } + return n; + }, + type: 'numeric' + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js new file mode 100644 index 0000000..1922cc5 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js @@ -0,0 +1,441 @@ +/*! Build Table widget * by Rob Garrison */ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;(function($){ +"use strict"; +var ts = $.tablesorter = $.tablesorter || {}, + + // build a table from data (requires existing <table> tag) + // data.header contains an array of header titles + // data.rows contains an array of rows which contains an array of cells + bt = ts.buildTable = function(tar, c){ + // add table if one doesn't exist + var $tbl = tar.tagName === 'TABLE' ? $(tar) : $('<table>').appendTo(tar), + table = $tbl[0], + wo = c.widgetOptions = $.extend( true, {}, bt.defaults, c.widgetOptions ), + p = wo.build_processing, + typ = wo.build_type, + d = wo.build_source || c.data, + + // determine type: html, json, array, csv, object + runType = function(d){ + var t = $.type(d), + jq = d instanceof jQuery; + // run any processing if set + if ( typeof p === 'function' ) { d = p(d, wo); } + // store processed data in table.config.data + c.data = d; + // String (html or unprocessed json) or jQuery object + if ( jq || t === 'string' ) { + // look for </tr> closing tag, then we have an HTML string + if ( jq || /<\s*\/tr\s*>/.test(d) ) { + return bt.html( table, d, wo ); + } + try { + d = $.parseJSON(d); + if (d) { + // valid JSON! + return bt.object( table, d, wo ); + } + } catch(ignore) {} + // fall through in case it's a csv string + } + // Array + if (t === 'array' || t === 'string' || typ === 'array' || typ === 'csv') { + // build table using an array (csv & array combined script) + return bt.csv( table, d, wo ); + } + // if we got here, it's an object, or nothing + return bt.object( table, d, wo ); + }; + + // store config + table.config = c; + + // even if wo.build_type is undefined, we can try to figure out the type + if ( !ts.buildTable.hasOwnProperty(typ) && typ !== '' ) { + if (c.debug) { ts.log('aborting build table widget, incorrect build type'); } + return false; + } + + if ( d instanceof jQuery ) { + // get data from within a jQuery object (csv) + runType( $.trim( d.html() ) ); + } else if ( d && ( d.hasOwnProperty('url') || typ === 'json' ) ) { + // load data via ajax + $.ajax( wo.build_source ) + .done(function(data) { + runType(data); + }) + .fail(function( jqXHR, textStatus, errorThrown) { + if (c.debug) { ts.log('aborting build table widget, failed ajax load'); } + $tbl.html('<tr><td class="error">' + jqXHR.status + ' ' + textStatus + '</td></tr>'); + }); + } else { + runType(d); + } + }; + + bt.defaults = { + // *** build widget core *** + build_type : '', // array, csv, object, json, html + build_source : '', // array, object, jQuery Object or ajaxObject { url: '', dataType: 'json' }, + build_processing : null, // function that returns a useable build_type (e.g. string to array) + build_complete : 'tablesorter-build-complete', // triggered event when build completes + + // *** CSV & Array *** + build_headers : { + rows : 1, // Number of header rows from the csv + classes : [], // Header classes to apply to cells + text : [], // Header cell text + widths : [] // set header cell widths (set in colgroup) + }, + build_footers : { + rows : 1, // Number of header rows from the csv + classes : [], // Footer classes to apply to cells + text : [] // Footer cell text + }, + build_numbers : { + addColumn : false, // include row numbering column? + sortable : false // make column sortable? + }, + + // *** CSV only options *** + build_csvStartLine : 0, // line within the csv to start adding to table + build_csvSeparator : ",", // csv separator + + // *** build object options *** + build_objectRowKey : 'rows', // object key containing table rows + build_objectCellKey : 'cells', // object key containing table cells (within the rows object) + build_objectHeaderKey : 'headers', // object key containing table headers + build_objectFooterKey : 'footers' // object key containing table footers + }; + + bt.build = { + colgroup : function(widths) { + var t = ''; + // add colgroup if widths set + if (widths && widths.length) { + t += '<colgroup>'; + $.each(widths, function(i, w){ + t += '<col' + ( w ? ' style="width:' + w + '"' : '' ) + '>'; + }); + t += '</colgroup>'; + } + return t; + }, + // d = cell data; typ = 'th' or 'td'; first = save widths from first header row only + cell : function(d, wo, typ, col, first){ + var j, $td, + $col = first ? $('<col>') : '', + cls = wo.build_headers.classes, + cw = wo.build_headers.widths; + // d is just an array + if (/string|number/.test(typeof d)) { + // add classes from options, but not text + $td = $('<' + typ + (cls && cls[col] ? ' class="' + cls[col] + '"' : '') + '>' + d + '</' + typ + '>'); + // get widths from options (only from first row) + if (first && cw && cw[col]) { + $col.width(cw[col] || ''); + } + } else { + // assume we have an object + $td = $('<' + typ + '>'); + for (j in d) { + if (d.hasOwnProperty(j)){ + if (j === 'text' || j === 'html') { + $td[j]( d[j] ); + } else if (first && j === 'width') { + // set column width, but only from first row + $col.width(d[j] || ''); + } else { + $td.attr(j, d[j]); + } + } + } + } + return [ $td, $col ]; + }, + // h1 = header text from data + header : function(h1, wo){ + var h2 = wo.build_headers.text, + cls = wo.build_headers.classes, + t = '<tr>' + (wo.build_numbers.addColumn ? '<th' + (wo.build_numbers.sortable ? '' : + ' class="sorter-false"') + '>' + wo.build_numbers.addColumn + '</th>' : ''); + $.each(h1, function(i, h) { + t += '<th' + (cls && cls[i] ? ' class="' + cls[i] + '"' : '') + '>' + + (h2 && h2[i] ? h2[i] : h) + '</th>'; + }); + return t + '</tr>'; + }, + rows : function(items, txt, c, wo, num, ftr){ + var h = (ftr ? 'th' : 'td'), + t = '<tr>' + (wo.build_numbers.addColumn ? '<' + h + '>' + (ftr ? '' : num) + '</' + h + '>' : ''); + $.each(items, function(i, item) { + t += '<' + (ftr ? h + (c && c[i] ? ' class="' + c[i] + '"' : '') : h) + '>' + + (ftr && txt && txt.length && txt[i] ? txt[i] : item) + '</' + h + '>'; + }); + return t + '</tr>'; + } + }; + + bt.buildComplete = function(table, wo){ + $(table).trigger(wo.build_complete); + ts.setup(table, table.config); + }; + + /* ==== Array example ==== + [ + [ "header1", "header2", ... "headerN" ], + [ "row1cell1", "row1cell2", ... "row1cellN" ], + [ "row2cell1", "row2cell2", ... "row2cellN" ], + ... + [ "rowNcell1", "rowNcell2", ... "rowNcellN" ] + ] + */ + bt.array = function(table, data, wo) { + return bt.csv(table, data, wo); + }; + + /* ==== CSV example ==== + ID, Name, Age, Date + A42b, Parker, 28, "Jul 6, 2006 8:14 AM" + A255, Hood, 33, "Dec 10, 2002 5:14 AM" + A33, Kent, 18, "Jan 12, 2003 11:14 AM" + A1, Franklin, 45, "Jan 18, 2001 9:12 AM" + A102, Evans, 22, "Jan 18, 2007 9:12 AM" + A42a, Everet, 22, "Jan 18, 2007 9:12 AM" + ID, Name, Age, Date + */ + // Adapted & modified from csvToTable.js by Steve Sobel + // MIT license: https://code.google.com/p/jquerycsvtotable/ + bt.csv = function(table, data, wo) { + var c, h, + csv = wo.build_type === 'csv' || typeof data === 'string', + $t = $(table), + lines = csv ? data.replace('\r','').split('\n') : data, + len = lines.length, + printedLines = 0, + infooter = false, + r = wo.build_headers.rows + (csv ? wo.build_csvStartLine : 0), + f = wo.build_footers.rows, + headerCount = 0, + error = '', + items, + tableHTML = bt.build.colgroup( wo.build_headers.widths ) + '<thead>'; + + $.each(lines, function(n, line) { + if ( n >= len - f ) { infooter = true; } + // build header + if ( (csv ? n >= wo.build_csvStartLine : true) && ( n < r ) ) { + h = csv ? bt.splitCSV( line, wo.build_csvSeparator ) : line; + headerCount = h.length; + tableHTML += bt.build.header(h, wo); + } else if ( n >= r ) { + // build tbody & tfoot rows + if (n === r) { + tableHTML += '</thead><tbody>'; + } + items = csv ? bt.splitCSV( line, wo.build_csvSeparator ) : line; + if (infooter && f > 0) { + tableHTML += (n === len - f ? '</tbody><tfoot>' : '') + + (n === len ? '</tfoot>' : ''); + } + if (items.length > 1) { + printedLines++; + if ( items.length !== headerCount ) { + error += 'error on line ' + n + ': Item count (' + items.length + + ') does not match header count (' + headerCount + ') \n'; + } + c = infooter ? wo.build_footers.classes : ''; + tableHTML += bt.build.rows(items, wo.build_footers.text, c, wo, printedLines, infooter); + } + } + }); + tableHTML += (f > 0 ? '' : '</tbody>'); + if (error) { + $t.html(error); + } else { + $t.html(tableHTML); + bt.buildComplete(table, wo); + } + }; + + // CSV Parser by Brian Huisman (http://www.greywyvern.com/?post=258) + bt.splitCSV = function(str, sep) { + var x, tl, + thisCSV = $.trim(str).split(sep = sep || ","); + for ( x = thisCSV.length - 1; x >= 0; x-- ) { + if ( thisCSV[x].replace(/\"\s+$/, '"').charAt(thisCSV[x].length - 1) === '"' ) { + if ( (tl = thisCSV[x].replace(/^\s+\"/, '"')).length > 1 && tl.charAt(0) === '"' ) { + thisCSV[x] = thisCSV[x].replace(/^\s*"|"\s*$/g, '').replace(/""/g, '"'); + } else if (x) { + thisCSV.splice(x - 1, 2, [thisCSV[x - 1], thisCSV[x]].join(sep)); + } else { + thisCSV = thisCSV.shift().split(sep).concat(thisCSV); + } + } else { + thisCSV[x].replace(/""/g, '"'); + } + } + return thisCSV; + }; + + // data may be a jQuery object after processing + bt.html = function(table, data, wo) { + var $t = $(table); + if ( data instanceof jQuery ) { + $t.empty().append(data); + } else { + $t.html(data); + } + bt.buildComplete(table, wo); + }; + +/* ==== Object example ==== + data : { + headers : [ + [ + { text: 'First Name', class: 'fname', width: '20%' }, // row 1 cell 1 + 'Last Name', + { text: 'Age', class: 'age', 'data-sorter' : false }, + 'Total', + { text: 'Discount', class : 'sorter-false' }, + { text: 'Date', class : 'date' } // row 1 cell 6 + ] + ], + footers : 'clone', // clone headers or assign array like headers + rows : [ + // TBODY 1 + [ 'Peter', 'Parker', 28, '$9.99', '20%', 'Jul 6, 2006 8:14 AM' ], // row 1 + [ 'John', 'Hood', 33, '$19.99', '25%', 'Dec 10, 2002 5:14 AM' ], // row 2 + [ 'Clark', 'Kent', 18, '$15.89', '44%', 'Jan 12, 2003 11:14 AM' ], // row 3 + + // TBODY 2 + { newTbody: true, class: 'tablesorter-infoOnly' }, + { cells : [ { text: 'Info Row', colSpan: 6 } ] }, // row 4 + + // TBODY 3 + { newTbody: true }, + [ 'Bruce', 'Evans', 22, '$13.19', '11%', 'Jan 18, 2007 9:12 AM' ], // row 5 + [ 'Brice', 'Almighty', 45, '$153.19', '44%', 'Jan 18, 2001 9:12 AM' ], // row 6 + + { class: 'specialRow', // row 7 + cells: [ + { text: 'Fred', class: 'fname' }, + { text: 'Smith', class: 'lname' }, + { text: 18, class: 'age', 'data-info': 'fake ID!, he is really 16' }, + { text: '$22.44', class: 'total' }, + { text: '8%', class: 'discount' }, + { text: 'Aug 20, 2012 10:15 AM', class: 'date' } + ], + 'data-info' : 'This row likes turtles' + } + ] + } +*/ + bt.object = function(table, data, wo) { + // "rows" + var j, l, t, $c, $t, $tb, $tr, + c = table.config, + kh = wo.build_objectHeaderKey, + kr = wo.build_objectRowKey, + h = data.hasOwnProperty(kh) && !$.isEmptyObject(data.kh) ? data.kh : data.hasOwnProperty('headers') ? data.headers : false, + r = data.hasOwnProperty(kr) && !$.isEmptyObject(data.kr) ? data.kr : data.hasOwnProperty('rows') ? data.rows : false; + + if (!h || !r || h.length === 0 || r.length === 0) { + if (c.debug) { ts.log('aborting build table widget, missing data for object build'); } + return false; + } + + $c = $('<colgroup>'); + $t = $('<table><thead/></table>'); + + // Build thead + // h = [ ['headerRow1Cell1', 'headerRow1Cell2', ... 'headerRow1CellN' ], ['headerRow2Cell1', ... ] ] + // or h = [ [ { text: 'firstCell', class: 'fc', width: '20%' }, ..., { text: 'last Cell' } ], [ /* second row */ ] ] + $.each(h, function(i, d){ + $tr = $('<tr>').appendTo( $t.find('thead') ); + l = d.length; // header row + for ( j = 0; j < l; j++ ) { + // cell(cellData, widgetOptions, 'th', first row) + t = bt.build.cell(d[j], wo, 'th', j, i === 0); + if (t[0] && t[0].length) { t[0].appendTo( $tr ); } // add cell + if (i === 0 && t[1]) { t[1].appendTo( $c ); } // add col to colgroup + } + }); + if ($c.find('col[style]').length) { + // add colgroup if it contains col elements + $t.prepend( $c ); + } + + $tb = $('<tbody>'); + // Build tbody + $.each(r, function(i, d){ + t = $.type(d) === 'object'; + // add new tbody + if (t && d.newTbody) { + $tb = $('<tbody>').appendTo( $t ); + for (j in d) { + if (d.hasOwnProperty(j) && j !== 'newTbody'){ + $tb.attr(j, d[j]); + } + } + } else { + if (i === 0) { + // add tbody, if the first item in the object isn't a call for a new tbody + $tb.appendTo( $t ); + } + + $tr = $('<tr>').appendTo( $tb ); + if (t) { + // row defined by object + for (j in d) { + if (d.hasOwnProperty(j) && j !== wo.build_objectCellKey){ + $tr.attr(j, d[j]); + } + } + if (d.hasOwnProperty(wo.build_objectCellKey)) { + // cells contains each cell info + d = d.cells; + } + } + + l = d.length; + for ( j = 0; j < l; j++ ) { + // cell(cellData, widgetOptions, 'td') + $c = bt.build.cell(d[j], wo, 'td', j); + if ($c[0] && $c[0].length) { $c[0].appendTo( $tr ); } // add cell + } + } + }); + + // add footer + if (data.hasOwnProperty(wo.build_objectFooterKey)) { + t = data[wo.build_objectFooterKey]; + if (t === 'clone') { + $c = $t.find('thead').html(); + $t.append('<tfoot>' + $c + '</tfoot>'); + } else { + $c = $('<tfoot>').appendTo( $t ); + $.each(t, function(i, d) { + $tr = $('<tr>').appendTo( $c ); + l = d.length; // footer cells + for ( j = 0; j < l; j++ ) { + // cell(cellData, widgetOptions, 'th') + $tb = bt.build.cell(d[j], wo, 'th', j); + if ($tb[0] && $tb[0].length) { $tb[0].appendTo( $tr ); } // add cell + } + }); + } + } + + $(table).html( $t.html() ); + bt.buildComplete(table, wo); + }; + + bt.ajax = bt.json = function(table, data, wo) { + return bt.object(table, data, wo); + }; + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js new file mode 100644 index 0000000..cdf240a --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -0,0 +1,291 @@ +/* Column Selector/Responsive table widget (beta) for TableSorter 12/17/2013 (v2.15.0) + * Requires tablesorter v2.8+ and jQuery 1.7+ + * by Justin Hallett & Rob Garrison + */ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;(function($){ +"use strict"; + +var ts = $.tablesorter, +namespace = '.tscolsel', +tsColSel = ts.columnSelector = { + + queryAll : '@media only all { [columns] { display: none; } }', + queryBreak : '@media screen and (min-width: [size]) { [columns] { display: table-cell; } }', + + init: function(table, c, wo) { + var $t, colSel; + + // abort if no input is contained within the layout + $t = $(wo.columnSelector_layout); + if (!$t.find('input').add( $t.filter('input') ).length) { + if (c.debug) { + ts.log('*** ERROR: Column Selector aborting, no input found in the layout! ***'); + } + return; + } + + // unique table class name + c.tableId = 'tablesorter' + new Date().getTime(); + c.$table.addClass( c.tableId ); + + // build column selector/state array + colSel = c.selector = { $container : $(wo.columnSelector_container || '<div>') }; + tsColSel.setupSelector(table, c, wo); + + if (wo.columnSelector_mediaquery) { + tsColSel.setupBreakpoints(c, wo); + } + + if (colSel.$container.length) { + colSel.$style = $('<style></style>').prop('disabled', true).appendTo('head'); + tsColSel.updateCols(c, wo); + } + + }, + + setupSelector: function(table, c, wo) { + var name, + colSel = c.selector, + $container = colSel.$container, + // get stored column states + saved = wo.columnSelector_saveColumns && ts.storage ? ts.storage( table, 'tablesorter-columnSelector' ) : []; + + // initial states + colSel.states = []; + colSel.$column = []; + colSel.$wrapper = []; + colSel.$checkbox = []; + // populate the selector container + c.$table.children('thead').find('tr:first th', table).each(function() { + var $this = $(this), + // if no data-priority is assigned, default to 1, but don't remove it from the selector list + priority = $this.attr(wo.columnSelector_priority) || 1, + colId = $this.attr('data-column'); + + // if this column not hidable at all + // include getData check (includes "columnSelector-false" class, data attribute, etc) + if ( isNaN(priority) && priority.length > 0 || ts.getData(this, c.headers[colId], 'columnSelector') == 'false' || + ( wo.columnSelector_columns[colId] && wo.columnSelector_columns[colId] === 'disable') ) { + return true; // goto next + } + + // set default state + colSel.states[colId] = saved && typeof(saved[colId]) !== 'undefined' ? + saved[colId] : typeof(wo.columnSelector_columns[colId]) !== 'undefined' ? wo.columnSelector_columns[colId] : true; + colSel.$column[colId] = $(this); + + // set default col title + name = $this.attr(wo.columnSelector_name) || $this.text(); + + if ($container.length) { + colSel.$wrapper[colId] = $(wo.columnSelector_layout.replace(/\{name\}/g, name)).appendTo($container); + colSel.$checkbox[colId] = colSel.$wrapper[colId] + // input may not be wrapped within the layout template + .find('input').add( colSel.$wrapper[colId].filter('input') ) + .attr('data-column', colId) + .prop('checked', colSel.states[colId]) + .bind('change', function(){ + colSel.states[colId] = this.checked; + tsColSel.updateCols(c, wo); + }).change(); + } + }); + + }, + + setupBreakpoints: function(c, wo){ + var $auto, colSel = c.selector; + + // add responsive breakpoints + if (wo.columnSelector_mediaquery) { + // used by window resize function + colSel.lastIndex = -1; + wo.columnSelector_breakpoints.sort(); + colSel.$breakpoints = $('<style></style>').prop('disabled', true).appendTo('head'); + tsColSel.updateBreakpoints(c, wo); + c.$table.unbind('updateAll' + namespace).bind('updateAll' + namespace, function(){ + tsColSel.updateBreakpoints(c, wo); + tsColSel.updateCols(c, wo); + }); + } + + if (colSel.$container.length) { + // Add media queries toggle + if (wo.columnSelector_mediaquery && wo.columnSelector_mediaquery) { + $auto = $( wo.columnSelector_layout.replace(/\{name\}/g, wo.columnSelector_mediaqueryName) ).prependTo(colSel.$container); + $auto + // needed in case the input in the layout is not wrapped + .find('input').add( $auto.filter('input') ) + .attr('data-column', 'auto') + .prop('checked', wo.columnSelector_mediaqueryState) + .bind('change', function(){ + wo.columnSelector_mediaqueryState = this.checked; + $.each( colSel.$checkbox, function(i, $cb){ + if ($cb) { + $cb[0].disabled = wo.columnSelector_mediaqueryState; + colSel.$wrapper[i].toggleClass('disabled', wo.columnSelector_mediaqueryState); + } + }); + tsColSel.updateBreakpoints(c, wo); + tsColSel.updateCols(c, wo); + // copy the column selector to a popup/tooltip + if (c.selector.$popup) { + c.selector.$popup.find('.tablesorter-column-selector') + .html( colSel.$container.html() ) + .find('input').each(function(){ + var indx = $(this).attr('data-column') + $(this).prop( 'checked', indx === 'auto' ? wo.columnSelector_mediaqueryState : colSel.states[indx] ) + }); + } + }).change(); + } + // Add a bind on update to re-run col setup + c.$table.unbind('update' + namespace).bind('update' + namespace, function() { + tsColSel.updateCols(c, wo); + }); + } + }, + + updateBreakpoints: function(c, wo) { + var priority, column, breaks, + colSel = c.selector, + prefix = '.' + c.tableId, + mediaAll = [], + breakpts = ''; + if (wo.columnSelector_mediaquery && !wo.columnSelector_mediaqueryState) { + colSel.$breakpoints.prop('disabled', true); + colSel.$style.prop('disabled', false); + return; + } + + // only 6 breakpoints (same as jQuery Mobile) + for (priority = 0; priority < 6; priority++){ + /*jshint loopfunc:true */ + breaks = []; + c.$headers.filter('[' + wo.columnSelector_priority + '=' + (priority + 1) + ']').each(function(){ + column = parseInt($(this).attr('data-column'), 10) + 1; + breaks.push(prefix + ' tr th:nth-child(' + column + ')'); + breaks.push(prefix + ' tr td:nth-child(' + column + ')'); + }); + if (breaks.length) { + mediaAll = mediaAll.concat( breaks ); + breakpts += tsColSel.queryBreak + .replace(/\[size\]/g, wo.columnSelector_breakpoints[priority]) + .replace(/\[columns\]/g, breaks.join(',')); + } + } + if (colSel.$style) { + colSel.$style.prop('disabled', true); + } + colSel.$breakpoints + .prop('disabled', false) + .html( tsColSel.queryAll.replace(/\[columns\]/g, mediaAll.join(',')) + breakpts ); + }, + + updateCols: function(c, wo) { + if (wo.columnSelector_mediaquery && wo.columnSelector_mediaqueryState) { + return; + } + var column, + styles = [], + prefix = '.' + c.tableId; + c.selector.$container.find('input[data-column]').filter('[data-column!="auto"]').each(function(){ + if (!this.checked) { + column = parseInt( $(this).attr('data-column'), 10 ) + 1; + styles.push(prefix + ' tr th:nth-child(' + column + ')'); + styles.push(prefix + ' tr td:nth-child(' + column + ')'); + } + }); + if (wo.columnSelector_mediaquery){ + c.selector.$breakpoints.prop('disabled', true); + } + if (c.selector.$style) { + c.selector.$style.prop('disabled', false).html( styles.length ? styles.join(',') + ' { display: none; }' : '' ); + } + if (wo.columnSelector_saveColumns && ts.storage) { + ts.storage( c.$table[0], 'tablesorter-columnSelector', c.selector.states ); + } + }, + + attachTo : function(table, elm) { + var colSel, wo, indx, + table = $(table)[0], + c = table.config, + $popup = $(elm); + if ($popup.length && c) { + if (!$popup.find('.tablesorter-column-selector').length) { + // add a wrapper to add the selector into, in case the popup has other content + $popup.append('<span class="tablesorter-column-selector"></span>'); + } + colSel = c.selector; + wo = c.widgetOptions; + $popup.find('.tablesorter-column-selector') + .html( colSel.$container.html() ) + .find('input').each(function(){ + var indx = $(this).attr('data-column'); + $(this).prop( 'checked', indx === 'auto' ? wo.columnSelector_mediaqueryState : colSel.states[indx] ) + }); + colSel.$popup = $popup.on('change', 'input', function(){ + // data input + indx = $(this).attr('data-column'); + // update original popup + colSel.$container.find('input[data-column="' + indx + '"]') + .prop('checked', this.checked) + .trigger('change'); + }); + } + } + +}; + +ts.addWidget({ + id: "columnSelector", + priority: 10, + options: { + // target the column selector markup + columnSelector_container : null, + // column status, true = display, false = hide + // disable = do not display on list + columnSelector_columns : {}, + // remember selected columns + columnSelector_saveColumns: true, + + // container layout + columnSelector_layout : '<label><input type="checkbox">{name}</label>', + // data attribute containing column name to use in the selector container + columnSelector_name : 'data-selector-name', + + /* Responsive Media Query settings */ + // enable/disable mediaquery breakpoints + columnSelector_mediaquery: true, + // toggle checkbox name + columnSelector_mediaqueryName: 'Auto: ', + // breakpoints checkbox initial setting + columnSelector_mediaqueryState: true, + // responsive table hides columns with priority 1-6 at these breakpoints + // see http://view.jquerymobile.com/1.3.2/dist/demos/widgets/table-column-toggle/#Applyingapresetbreakpoint + // *** set to false to disable *** + columnSelector_breakpoints : [ '20em', '30em', '40em', '50em', '60em', '70em' ], + // data attribute containing column priority + // duplicates how jQuery mobile uses priorities: + // http://view.jquerymobile.com/1.3.2/dist/demos/widgets/table-column-toggle/ + columnSelector_priority : 'data-priority' + + }, + init: function(table, thisWidget, c, wo) { + tsColSel.init(table, c, wo); + }, + remove: function(table, c){ + var csel = c.selector; + csel.$container.empty(); + csel.$popup.empty(); + csel.$style.remove(); + csel.$breakpoints.remove(); + c.$table.unbind('updateAll' + namespace + ',update' + namespace); + } + +}); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js new file mode 100644 index 0000000..94bf6a1 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js @@ -0,0 +1,67 @@ +/*! tablesorter CSS Sticky Headers widget - updated 12/17/2013 (v2.15.0) +* Requires a modern browser, tablesorter v2.8+ +*/ +/*global jQuery: false, unused:false */ +;(function($){ + "use strict"; + + $.tablesorter.addWidget({ + id: "cssStickyHeaders", + priority: 10, + options: { + cssStickyHeaders_offset : 0, + cssStickyHeaders_addCaption : false, + cssStickyHeaders_attachTo : null, + cssStickyHeaders_zIndex : 10 + }, + init : function(table, thisWidget, c, wo) { + var $attach = $(wo.cssStickyHeaders_attachTo), + namespace = '.cssstickyheader', + $thead = c.$table.children('thead'), + $caption = c.$table.find('caption'), + $win = $attach.length ? $attach : $(window); + $win.bind('scroll resize '.split(' ').join(namespace + ' '), function() { + var top = $attach.length ? $attach.offset().top : $win.scrollTop(), + // add caption height; include table padding top & border-spacing or text may be above the fold (jQuery UI themes) + // border-spacing needed in Firefox, but not webkit... not sure if I should account for that + captionTop = wo.cssStickyHeaders_addCaption ? $caption.outerHeight(true) + + (parseInt(c.$table.css('padding-top'), 10) || 0) + (parseInt(c.$table.css('border-spacing'), 10) || 0) : 0, + bottom = c.$table.height() - $thead.height() - (c.$table.find('tfoot').height() || 0) - captionTop, + deltaY = top - $thead.offset().top + (parseInt(c.$table.css('border-top-width'), 10) || 0) + + (wo.cssStickyHeaders_offset || 0) + captionTop, + finalY = (deltaY > 0 && deltaY <= bottom ? deltaY : 0), + // IE can only transform header cells - fixes #447 thanks to @gakreol! + $cells = $thead.children().children(); + if (wo.cssStickyHeaders_addCaption) { + $cells = $cells.add($caption); + } + $cells.css({ + "position" : "relative", + "z-index" : wo.cssStickyHeaders_zIndex, + "transform" : finalY === 0 ? "" : "translate(0px," + finalY + "px)", + "-ms-transform" : finalY === 0 ? "" : "translate(0px," + finalY + "px)", + "-webkit-transform" : finalY === 0 ? "" : "translate(0px," + finalY + "px)" + }); + }); + c.$table.bind('filterEnd', function() { + // scroll top of table into view + window.scrollTo(0, c.$table.position().top); + }); + + }, + remove: function(table, c, wo){ + var namespace = '.cssstickyheader'; + $(window).unbind('scroll resize '.split(' ').join(namespace + ' ')); + c.$table + .unbind('update updateAll '.split(' ').join(namespace + ' ')) + .children('thead, caption').css({ + "position" : "", + "z-index" : "", + "transform" : "", + "-ms-transform" : "", + "-webkit-transform" : "" + }); + } + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js new file mode 100644 index 0000000..44b95ef --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js @@ -0,0 +1,89 @@ +/*! tablesorter Editable Content widget - updated 1/24/2014 (core v2.15.0) + * Requires tablesorter v2.8+ and jQuery 1.7+ + * by Rob Garrison + */ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;(function($){ + "use strict"; + + $.tablesorter.addWidget({ + id: 'editable', + options : { + editable_columns : [], + editable_enterToAccept : true, + editable_autoResort : false, + editable_noEdit : 'no-edit', + editable_editComplete : 'editComplete' + }, + init: function(table, thisWidget, c, wo){ + if (!wo.editable_columns.length) { return; } + var indx, tmp, $t, cols = []; + if (wo.editable_columns.indexOf('-') >= 0) { + // editable_columns can contain a range string (i.e. "2-4" ) + tmp = wo.editable_columns.split('-'); + indx = parseInt(tmp[0],10) || 0; + tmp = parseInt(tmp[1],10) || (c.columns - 1); + if (tmp > c.columns) { tmp = c.columns - 1; } + for (; indx <= tmp; indx++) { + cols.push('td:nth-child(' + (indx + 1) + ')'); + } + } else if ($.isArray(wo.editable_columns)) { + $.each(wo.editable_columns, function(i, col){ + cols.push('td:nth-child(' + (col + 1) + ')'); + }); + } + // IE does not allow making TR/TH/TD cells directly editable (issue #404) + // so add a div or span inside ( it's faster than using wrapInner() ) + c.$tbodies.find( cols.join(',') ).not('.' + wo.editable_noEdit).each(function(){ + // test for children, if they exist, then make the children editable + $t = $(this); + ( $t.children().length ? $t.children() : $t ).prop('contenteditable', true); + }); + c.$tbodies + .on('mouseleave.tseditable', function(){ + if (c.$table.data('contentFocused')) { + $(':focus').trigger('blur'); + } + }) + .on('focus.tseditable', '[contenteditable]', function(){ + c.$table.data('contentFocused', true); + var $this = $(this), v = $this.html(); + if (wo.editable_enterToAccept) { + // prevent enter from adding into the content + $this.on('keydown.tseditable', function(e){ + if (e.which === 13) { + e.preventDefault(); + } + }); + } + $this.data({ before : v, original: v }); + }) + .on('blur focusout keyup '.split(' ').join('.tseditable '), '[contenteditable]', function(e){ + if (!c.$table.data('contentFocused')) { return; } + var $this = $(e.target), t; + if (e.which === 27) { + // user cancelled + $this.html( $this.data('original') ).trigger('blur.tseditable'); + c.$table.data('contentFocused', false); + return false; + } + t = e.type !== 'keyup' || (wo.editable_enterToAccept && e.which === 13); + // change if new or user hits enter (if option set) + if ($this.data('before') !== $this.html() || t) { + $this.data('before', $this.html()).trigger('change'); + if (t) { + c.$table + .data('contentFocused', false) + .trigger('updateCell', [ $this.closest('td'), wo.editable_autoResort, function(table){ + $this.trigger( wo.editable_editComplete ); + c.$table.trigger('applyWidgets'); + } ]); + $this.trigger('blur.tseditable'); + } + } + }); + } + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js new file mode 100644 index 0000000..76f9bbd --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -0,0 +1,183 @@ +/*! tablesorter Grouping widget - updated 12/18/2013 (core v2.15.0) + * Requires tablesorter v2.8+ and jQuery 1.7+ + * by Rob Garrison + */ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;(function($){ +"use strict"; +var ts = $.tablesorter; + +ts.grouping = { + + types : { + number : function(c, $column, txt, num, group){ + var value, word; + if (num > 1 && txt !== '') { + if ($column.hasClass(ts.css.sortAsc)) { + value = Math.floor(parseFloat(txt)/num) * num; + return value > parseFloat(group || 0) ? value : parseFloat(group || 0); + } else { + value = Math.ceil(parseFloat(txt)/num) * num; + return value < parseFloat(group || num) - value ? parseFloat(group || num) - value : value; + } + } else { + word = (txt + '').match(/\d+/g); + return word && word.length >= num ? word[num - 1] : txt || ''; + } + }, + separator : function(c, $column, txt, num){ + var word = (txt + '').split(c.widgetOptions.group_separator); + return $.trim(word && num > 0 && word.length >= num ? word[(num || 1) - 1] : ''); + }, + word : function(c, $column, txt, num){ + var word = (txt + ' ').match(/\w+/g); + return word && word.length >= num ? word[num - 1] : txt || ''; + }, + letter : function(c, $column, txt, num){ + return txt ? (txt + ' ').substring(0, num) : ''; + }, + date : function(c, $column, txt, part, group){ + var wo = c.widgetOptions, + time = new Date(txt || ''), + hours = time.getHours(); + return part === 'year' ? time.getFullYear() : + part === 'month' ? wo.group_months[time.getMonth()] : + part === 'day' ? wo.group_months[time.getMonth()] + ' ' + time.getDate() : + part === 'week' ? wo.group_week[time.getDay()] : + part === 'time' ? ('00' + (hours > 12 ? hours - 12 : hours === 0 ? hours + 12 : hours)).slice(-2) + ':' + + ('00' + time.getMinutes()).slice(-2) + ' ' + ('00' + wo.group_time[hours >= 12 ? 1 : 0]).slice(-2) : + wo.group_dateString(time); + } + }, + + update : function(table, c, wo){ + if ($.isEmptyObject(c.cache)) { return; } + var rowIndex, tbodyIndex, currentGroup, $rows, groupClass, grouping, time, cache, + lang = wo.grouping_language, + group = '', + column = c.sortList[0] ? c.sortList[0][0] : -1; + c.$table + .find('tr.group-hidden').removeClass('group-hidden').end() + .find('tr.group-header').remove(); + if (wo.group_collapsible) { + // clear pager saved spacer height (in case the rows are collapsed) + c.$table.data('pagerSavedHeight', 0); + } + if (column >= 0 && !c.$headers.filter('[data-column="' + column + '"]:last').hasClass('group-false')) { + if (c.debug){ time = new Date(); } + for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++) { + cache = c.cache[tbodyIndex].normalized; + group = ''; // clear grouping across tbodies + $rows = c.$tbodies.eq(tbodyIndex).children('tr').not('.' + c.cssChildRow); + if (wo.group_collapsed && wo.group_collapsible) { + $rows.addClass('group-hidden'); + } + for (rowIndex = 0; rowIndex < $rows.length; rowIndex++) { + if ( $rows.eq(rowIndex).is(':visible') ) { + // group class finds "group-{word/separator/letter/number/date/false}-{optional:#/year/month/day/week/time}" + groupClass = (c.$headers.filter('[data-column="' + column + '"]:last').attr('class') || '').match(/(group-\w+(-\w+)?)/g); + // grouping = [ 'group', '{word/separator/letter/number/date/false}', '{#/year/month/day/week/time}' ] + grouping = groupClass ? groupClass[0].split('-') : ['','letter',1]; // default to letter 1 + // fixes #438 + if (ts.grouping.types[grouping[1]]) { + currentGroup = cache[rowIndex] ? + ts.grouping.types[grouping[1]]( c, c.$headers.filter('[data-column="' + column + '"]:last'), cache[rowIndex][column], /date/.test(groupClass) ? + grouping[2] : parseInt(grouping[2] || 1, 10) || 1, group, lang ) : currentGroup; + if (group !== currentGroup) { + group = currentGroup; + // show range if number > 1 + if (grouping[1] === 'number' && grouping[2] > 1 && currentGroup !== '') { + currentGroup += ' - ' + (parseInt(currentGroup, 10) + + ((parseInt(grouping[2],10) - 1) * (c.$headers.filter('[data-column="' + column + '"]:last').hasClass(ts.css.sortAsc) ? 1 : -1))); + } + if ($.isFunction(wo.group_formatter)) { + currentGroup = wo.group_formatter((currentGroup || '').toString(), column, table, c, wo) || currentGroup; + } + $rows.eq(rowIndex).before('<tr class="group-header ' + c.selectorRemove.slice(1) + + (wo.group_collapsed && wo.group_collapsible ? ' collapsed' : '') + '" unselectable="on"><td colspan="' + + c.columns + '">' + (wo.group_collapsible ? '<i/>' : '') + '<span class="group-name">' + + currentGroup + '</span><span class="group-count"></span></td></tr>'); + } + } + } + } + } + $rows = c.$table.find('tr.group-header').bind('selectstart', false); + if (wo.group_count || $.isFunction(wo.group_callback)) { + $rows.each(function(){ + var $rows, + $row = $(this), + $label = $row.find('.group-count'); + if ($label.length) { + $rows = $row.nextUntil('tr.group-header').filter(':visible'); + if (wo.group_count) { + $label.html( wo.group_count.replace(/\{num\}/g, $rows.length) ); + } + if ($.isFunction(wo.group_callback)) { + wo.group_callback($row.find('td'), $rows, column, table); + } + } + }); + } + c.$table.trigger(wo.group_complete); + if (c.debug) { + $.tablesorter.benchmark("Applying groups widget: ", time); + } + } + } + +}; + +ts.addWidget({ + id: 'group', + priority: 100, + options: { + group_collapsible : true, // make the group header clickable and collapse the rows below it. + group_collapsed : false, // start with all groups collapsed + group_count : ' ({num})', // if not false, the "{num}" string is replaced with the number of rows in the group + group_separator : '-', // group name separator; used when group-separator-# class is used. + group_formatter : null, // function(txt, column, table, c, wo) { return txt; } + group_callback : null, // function($cell, $rows, column, table){}, callback allowing modification of the group header labels + group_complete : 'groupingComplete', // event triggered on the table when the grouping widget has finished work + + // change these default date names based on your language preferences + group_months : [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ], + group_week : [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ], + group_time : [ 'AM', 'PM' ], + // this function is used when "group-date" is set to create the date string + // you can just return date, date.toLocaleString(), date.toLocaleDateString() or d.toLocaleTimeString() + // reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#Conversion_getter + group_dateString : function(date) { return date.toLocaleString(); } + }, + init: function(table, thisWidget, c, wo){ + if (wo.group_collapsible) { + // .on() requires jQuery 1.7+ + c.$table.on('click toggleGroup', 'tr.group-header', function(event){ + event.stopPropagation(); + var $this = $(this); + // use shift-click to toggle ALL groups + if (event.type === 'click' && event.shiftKey) { + $this.siblings('.group-header').trigger('toggleGroup'); + } + $this.toggleClass('collapsed'); + // nextUntil requires jQuery 1.4+ + $this.nextUntil('tr.group-header').toggleClass('group-hidden', $this.hasClass('collapsed') ); + }); + } + c.$table.on('pagerChange', function(){ + ts.grouping.update(table, c, wo); + }); + }, + format: function(table, c, wo) { + ts.grouping.update(table, c, wo); + }, + remove : function(table, c, wo){ + c.$table + .off('click', 'tr.group-header') + .find('.group-hidden').removeClass('group-hidden').end() + .find('tr.group-header').remove(); + } +}); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js new file mode 100644 index 0000000..ebb7281 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -0,0 +1,834 @@ +/* Pager widget (beta) for TableSorter 2/23/2014 (v2.15.5) */ +/*jshint browser:true, jquery:true, unused:false */ +;(function($){ +"use strict"; +var tsp, + ts = $.tablesorter; + +ts.addWidget({ + id: "pager", + priority: 55, // load pager after filter widget + options : { + // output default: '{page}/{totalPages}' + // possible variables: {page}, {totalPages}, {filteredPages}, {startRow}, {endRow}, {filteredRows} and {totalRows} + pager_output: '{startRow} to {endRow} of {totalRows} rows', // '{page}/{totalPages}' + + // apply disabled classname to the pager arrows when the rows at either extreme is visible + pager_updateArrows: true, + + // starting page of the pager (zero based index) + pager_startPage: 0, + + // Number of visible rows + pager_size: 10, + + // Save pager page & size if the storage script is loaded (requires $.tablesorter.storage in jquery.tablesorter.widgets.js) + pager_savePages: true, + + //defines custom storage key + pager_storageKey: 'tablesorter-pager', + + // if true, the table will remain the same height no matter how many records are displayed. The space is made up by an empty + // table row set to a height to compensate; default is false + pager_fixedHeight: false, + + // count child rows towards the set page size? (set true if it is a visible table row within the pager) + // if true, child row(s) may not appear to be attached to its parent row, may be split across pages or + // may distort the table if rowspan or cellspans are included. + pager_countChildRows: false, + + // remove rows from the table to speed up the sort of large tables. + // setting this to false, only hides the non-visible rows; needed if you plan to add/remove rows with the pager enabled. + pager_removeRows: false, // removing rows in larger tables speeds up the sort + + // use this format: "http://mydatabase.com?page={page}&size={size}&{sortList:col}&{filterList:fcol}" + // where {page} is replaced by the page number, {size} is replaced by the number of records to show, + // {sortList:col} adds the sortList to the url into a "col" array, and {filterList:fcol} adds + // the filterList to the url into an "fcol" array. + // So a sortList = [[2,0],[3,0]] becomes "&col[2]=0&col[3]=0" in the url + // and a filterList = [[2,Blue],[3,13]] becomes "&fcol[2]=Blue&fcol[3]=13" in the url + pager_ajaxUrl: null, + + // modify the url after all processing has been applied + pager_customAjaxUrl: function(table, url) { return url; }, + + // modify the $.ajax object to allow complete control over your ajax requests + pager_ajaxObject: { + dataType: 'json' + }, + + // set this to false if you want to block ajax loading on init + pager_processAjaxOnInit: true, + + // process ajax so that the following information is returned: + // [ total_rows (number), rows (array of arrays), headers (array; optional) ] + // example: + // [ + // 100, // total rows + // [ + // [ "row1cell1", "row1cell2", ... "row1cellN" ], + // [ "row2cell1", "row2cell2", ... "row2cellN" ], + // ... + // [ "rowNcell1", "rowNcell2", ... "rowNcellN" ] + // ], + // [ "header1", "header2", ... "headerN" ] // optional + // ] + pager_ajaxProcessing: function(ajax){ return [ 0, [], null ]; }, + + // css class names of pager arrows + pager_css: { + container : 'tablesorter-pager', + errorRow : 'tablesorter-errorRow', // error information row (don't include period at beginning) + disabled : 'disabled' // class added to arrows @ extremes (i.e. prev/first arrows "disabled" on first page) + }, + + // jQuery selectors + pager_selectors: { + container : '.pager', // target the pager markup + first : '.first', // go to first page arrow + prev : '.prev', // previous page arrow + next : '.next', // next page arrow + last : '.last', // go to last page arrow + goto : '.gotoPage', // go to page selector - select dropdown that sets the current page + pageDisplay : '.pagedisplay', // location of where the "output" is displayed + pageSize : '.pagesize' // page size selector - select dropdown that sets the "size" option + } + }, + init: function(table){ + tsp.init(table); + }, + // only update to complete sorter initialization + format: function(table, c){ + if (!(c.pager && c.pager.initialized)){ + return tsp.initComplete(table, c); + } + tsp.moveToPage(table, c.pager, false); + }, + remove: function(table, c){ + tsp.destroyPager(table, c); + } +}); + +/* pager widget functions */ +tsp = ts.pager = { + + init: function(table) { + // check if tablesorter has initialized + if (table.hasInitialized && table.config.pager.initialized) { return; } + var t, + c = table.config, + wo = c.widgetOptions, + s = wo.pager_selectors, + + // save pager variables + p = c.pager = $.extend({ + totalPages: 0, + filteredRows: 0, + filteredPages: 0, + currentFilters: [], + page: wo.pager_startPage, + size: wo.pager_size, + startRow: 0, + endRow: 0, + ajaxCounter: 0, + $size: null, + last: {} + }, c.pager); + + // pager initializes multiple times before table has completed initialization + if (p.isInitializing) { return; } + + p.isInitializing = true; + if (c.debug) { + ts.log('Pager initializing'); + } + + // added in case the pager is reinitialized after being destroyed. + p.$container = $(s.container).addClass(wo.pager_css.container).show(); + // goto selector + p.$goto = p.$container.find(s.goto); + // page size selector + p.$size = p.$container.find(s.pageSize); + p.totalRows = c.$tbodies.eq(0).children().length; + + p.oldAjaxSuccess = p.oldAjaxSuccess || wo.pager_ajaxObject.success; + c.appender = tsp.appender; + if (ts.filter && $.inArray('filter', c.widgets) >= 0) { + // get any default filter settings (data-value attribute) fixes #388 + p.currentFilters = c.$table.data('lastSearch') || ts.filter.setDefaults(table, c, wo) || []; + // set, but don't apply current filters + ts.setFilters(table, p.currentFilters, false); + } + if (wo.pager_savePages && ts.storage) { + t = ts.storage(table, wo.pager_storageKey) || {}; // fixes #387 + p.page = isNaN(t.page) ? p.page : t.page; + p.size = ( isNaN(t.size) ? p.size : t.size ) || 10; + $.data(table, 'pagerLastSize', p.size); + } + // clear initialized flag + p.initialized = false; + // before initialization event + c.$table.trigger('pagerBeforeInitialized', c); + + tsp.enablePager(table, c, false); + + if ( typeof(wo.pager_ajaxUrl) === 'string' ) { + // ajax pager; interact with database + p.ajax = true; + // When filtering with ajax, allow only custom filtering function, disable default filtering since it will be done server side. + wo.filter_serversideFiltering = true; + c.serverSideSorting = true; + tsp.moveToPage(table, p); + } else { + p.ajax = false; + // Regular pager; all rows stored in memory + c.$table.trigger("appendCache", true); + tsp.hideRowsSetup(table, c); + } + + }, + + initComplete: function(table, c){ + var p = c.pager; + tsp.changeHeight(table, c); + tsp.bindEvents(table, c); + + // pager initialized + p.initialized = true; + p.isInitializing = false; + tsp.setPageSize(table, 0, c); // page size 0 is ignored + c.$table.trigger('pagerInitialized', c); + + }, + + bindEvents: function(table, c){ + var ctrls, fxn, + p = c.pager, + wo = c.widgetOptions, + s = wo.pager_selectors; + + c.$table + .unbind('filterStart filterEnd sortEnd disable enable destroy update updateRows updateAll addRows pageSize '.split(' ').join('.pager ')) + .bind('filterStart.pager', function(e, filters) { + p.currentFilters = filters; + p.page = 0; // fixes #456 + }) + // update pager after filter widget completes + .bind('filterEnd.pager sortEnd.pager', function() { + if (p.initialized) { + tsp.moveToPage(table, p, false); + tsp.updatePageDisplay(table, c, false); + tsp.fixHeight(table, c); + } + }) + .bind('disable.pager', function(e){ + e.stopPropagation(); + tsp.showAllRows(table, c); + }) + .on('enable.pager', function(e){ + e.stopPropagation(); + tsp.enablePager(table, c, true); + }) + .on('destroy.pager', function(e){ + e.stopPropagation(); + tsp.destroyPager(table, c); + }) + .on('update updateRows updateAll addRows '.split(' ').join('.pager '), function(e){ + e.stopPropagation(); + tsp.hideRows(table, c); + // make sure widgets are applied - fixes #450 + c.$table.trigger('applyWidgets'); + }) + .on('pageSize.pager', function(e,v){ + e.stopPropagation(); + tsp.setPageSize(table, parseInt(v, 10) || 10, c); + tsp.hideRows(table, c); + tsp.updatePageDisplay(table, c, false); + if (p.$size.length) { p.$size.val(p.size); } // twice? + }) + .on('pageSet.pager', function(e,v){ + e.stopPropagation(); + p.page = (parseInt(v, 10) || 1) - 1; + if (p.$goto.length) { p.$goto.val(c.size); } // twice? + tsp.moveToPage(table, p); + tsp.updatePageDisplay(table, c, false); + }); + + // clicked controls + ctrls = [ s.first, s.prev, s.next, s.last ]; + fxn = [ 'moveToFirstPage', 'moveToPrevPage', 'moveToNextPage', 'moveToLastPage' ]; + p.$container.find(ctrls.join(',')) + .attr("tabindex", 0) + .unbind('click.pager') + .bind('click.pager', function(e){ + e.stopPropagation(); + var i, + $c = $(this), + l = ctrls.length; + if ( !$c.hasClass(wo.pager_css.disabled) ) { + for (i = 0; i < l; i++) { + if ($c.is(ctrls[i])) { + tsp[fxn[i]](table, p); + break; + } + } + } + }); + + if ( p.$goto.length ) { + p.$goto + .unbind('change') + .bind('change', function(){ + p.page = $(this).val() - 1; + tsp.moveToPage(table, p); + tsp.updatePageDisplay(table, c, false); + }); + } + + if ( p.$size.length ) { + p.$size + .unbind('change.pager') + .bind('change.pager', function() { + p.$size.val( $(this).val() ); // in case there are more than one pagers + if ( !$(this).hasClass(wo.pager_css.disabled) ) { + tsp.setPageSize(table, parseInt( $(this).val(), 10 ), c); + tsp.changeHeight(table, c); + } + return false; + }); + } + + }, + + // hide arrows at extremes + pagerArrows: function(c, disable) { + var p = c.pager, + dis = !!disable, + first = dis || p.page === 0, + tp = Math.min( p.totalPages, p.filteredPages ), + last = dis || p.page === tp - 1 || p.totalPages === 0, + wo = c.widgetOptions, + s = wo.pager_selectors; + if ( wo.pager_updateArrows ) { + p.$container.find(s.first + ',' + s.prev).toggleClass(wo.pager_css.disabled, first).attr('aria-disabled', first); + p.$container.find(s.next + ',' + s.last).toggleClass(wo.pager_css.disabled, last).attr('aria-disabled', last); + } + }, + + updatePageDisplay: function(table, c, flag) { + var i, pg, s, out, + wo = c.widgetOptions, + p = c.pager, + f = c.$table.hasClass('hasFilters') && !wo.pager_ajaxUrl, + t = (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered') + ',' + c.selectorRemove + + (wo.pager_countChildRows ? '' : ',.' + c.cssChildRow), + sz = p.size || 10; // don't allow dividing by zero + p.$size.add(p.$goto).removeClass(wo.pager_css.disabled).removeAttr('disabled').attr('aria-disabled', 'false'); + p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method + p.filteredRows = (f) ? c.$tbodies.eq(0).children('tr').not('.' + t).length : p.totalRows; + p.filteredPages = (f) ? Math.ceil( p.filteredRows / sz ) || 1 : p.totalPages; + if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) { + t = (p.size * p.page > p.filteredRows); + p.startRow = (t) ? 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1); + p.page = (t) ? 0 : p.page; + p.endRow = Math.min( p.filteredRows, p.totalRows, p.size * ( p.page + 1 ) ); + out = p.$container.find(wo.pager_selectors.pageDisplay); + // form the output string (can now get a new output string from the server) + s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || wo.pager_output : wo.pager_output ) + // {page} = one-based index; {page+#} = zero based index +/- value + .replace(/\{page([\-+]\d+)?\}/gi, function(m,n){ + return p.totalPages ? p.page + (n ? parseInt(n, 10) : 1) : 0; + }) + // {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object) + .replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m){ + var str = m.replace(/[{}\s]/g,''), + extra = str.split(':'), + data = p.ajaxData, + // return zero for default page/row numbers + deflt = /(rows?|pages?)$/i.test(str) ? 0 : ''; + return extra.length > 1 && data && data[extra[0]] ? data[extra[0]][extra[1]] : p[str] || (data ? data[str] : deflt) || deflt; + }); + if (out.length) { + out[ (out[0].tagName === 'INPUT') ? 'val' : 'html' ](s); + if ( p.$goto.length ) { + t = ''; + pg = Math.min( p.totalPages, p.filteredPages ); + for ( i = 1; i <= pg; i++ ) { + t += '<option>' + i + '</option>'; + } + p.$goto.html(t).val( p.page + 1 ); + } + } + } + tsp.pagerArrows(c); + if (p.initialized && flag !== false) { + c.$table.trigger('pagerComplete', c); + // save pager info to storage + if (wo.pager_savePages && ts.storage) { + ts.storage(table, wo.pager_storageKey, { + page : p.page, + size : p.size + }); + } + } + }, + + fixHeight: function(table, c) { + var d, h, + p = c.pager, + wo = c.widgetOptions, + $b = c.$tbodies.eq(0); + if (wo.pager_fixedHeight) { + $b.find('tr.pagerSavedHeightSpacer').remove(); + h = $.data(table, 'pagerSavedHeight'); + if (h) { + d = h - $b.height(); + if ( d > 5 && $.data(table, 'pagerLastSize') === p.size && $b.children('tr:visible').length < p.size ) { + $b.append('<tr class="pagerSavedHeightSpacer ' + wo.pager_selectors.remove.replace(/(tr)?\./g,'') + '" style="height:' + d + 'px;"></tr>'); + } + } + } + }, + + changeHeight: function(table, c) { + var $b = c.$tbodies.eq(0); + $b.find('tr.pagerSavedHeightSpacer').remove(); + $.data(table, 'pagerSavedHeight', $b.height()); + tsp.fixHeight(table, c); + $.data(table, 'pagerLastSize', c.pager.size); + }, + + hideRows: function(table, c){ + if (!c.widgetOptions.pager_ajaxUrl) { + var i, + p = c.pager, + wo = c.widgetOptions, + rows = c.$tbodies.eq(0).children(), + l = rows.length, + s = ( p.page * p.size ), + e = s + p.size, + f = wo && wo.filter_filteredRow || 'filtered', + j = 0; // size counter + for ( i = 0; i < l; i++ ){ + if ( !rows[i].className.match(f) ) { + rows[i].style.display = ( j >= s && j < e ) ? '' : 'none'; + // don't count child rows + j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) && !wo.pager_countChildRows ? 0 : 1; + } + } + } + }, + + hideRowsSetup: function(table, c){ + var p = c.pager; + p.size = parseInt( p.$size.val(), 10 ) || p.size; + $.data(table, 'pagerLastSize', p.size); + tsp.pagerArrows(c); + if ( !c.widgetOptions.pager_removeRows ) { + tsp.hideRows(table, c); + c.$table.on('sortEnd.pager filterEnd.pager', function(){ + tsp.hideRows(table, c); + }); + } + }, + + renderAjax: function(data, table, c, xhr, exception){ + var p = c.pager, + wo = c.widgetOptions; + // process data + if ( $.isFunction(wo.pager_ajaxProcessing) ) { + // ajaxProcessing result: [ total, rows, headers ] + var i, j, t, hsh, $f, $sh, th, d, l, rr_count, + $t = c.$table, + tds = '', + result = wo.pager_ajaxProcessing(data, table) || [ 0, [] ], + hl = $t.find('thead th').length; + + // Clean up any previous error. + ts.showError(table); + + if ( exception ) { + if (c.debug) { + ts.log('Ajax Error', xhr, exception); + } + ts.showError(table, exception.message + ' (' + xhr.status + ')'); + c.$tbodies.eq(0).empty(); + } else { + // process ajax object + if (!$.isArray(result)) { + p.ajaxData = result; + p.totalRows = result.total; + th = result.headers; + d = result.rows; + } else { + // allow [ total, rows, headers ] or [ rows, total, headers ] + t = isNaN(result[0]) && !isNaN(result[1]); + //ensure a zero returned row count doesn't fail the logical || + rr_count = result[t ? 1 : 0]; + p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count; + d = p.totalRows === 0 ? [""] : result[t ? 0 : 1] || []; // row data + th = result[2]; // headers + } + l = d.length; + if (d instanceof jQuery) { + // append jQuery object + c.$tbodies.eq(0).empty().append(d); + } else if (l) { + // build table from array + for ( i = 0; i < l; i++ ) { + tds += '<tr>'; + for ( j = 0; j < d[i].length; j++ ) { + // build tbody cells; watch for data containing HTML markup - see #434 + tds += /^\s*<td/.test(d[i][j]) ? $.trim(d[i][j]) : '<td>' + d[i][j] + '</td>'; + } + tds += '</tr>'; + } + // add rows to first tbody + if (wo.pager_processAjaxOnInit) { + c.$tbodies.eq(0).html( tds ); + } else { + wo.pager_processAjaxOnInit = true; + } + } + // only add new header text if the length matches + if ( th && th.length === hl ) { + hsh = $t.hasClass('hasStickyHeaders'); + $sh = hsh ? wo.$sticky.children('thead:first').children().children() : ''; + $f = $t.find('tfoot tr:first').children(); + // don't change td headers (may contain pager) + c.$headers.filter('th').each(function(j){ + var $t = $(this), icn; + // add new test within the first span it finds, or just in the header + if ( $t.find('.' + ts.css.icon).length ) { + icn = $t.find('.' + ts.css.icon).clone(true); + $t.find('.tablesorter-header-inner').html( th[j] ).append(icn); + if ( hsh && $sh.length ) { + icn = $sh.eq(j).find('.' + ts.css.icon).clone(true); + $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ).append(icn); + } + } else { + $t.find('.tablesorter-header-inner').html( th[j] ); + if (hsh && $sh.length) { + $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ); + } + } + $f.eq(j).html( th[j] ); + }); + } + } + if (c.showProcessing) { + ts.isProcessing(table); // remove loading icon + } + // make sure last pager settings are saved, prevents multiple server side calls with + // the same parameters + p.last.totalPages = p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) ); + p.last.currentFilters = p.currentFilters; + p.last.sortList = (c.sortList || []).join(','); + tsp.updatePageDisplay(table, c); + tsp.fixHeight(table, c); + $t.trigger('updateCache', [function(){ + if (p.initialized) { + // apply widgets after table has rendered + $t.trigger('applyWidgets'); + $t.trigger('pagerChange', p); + } + }]); + } + if (!p.initialized) { + c.$table.trigger('applyWidgets'); + } + }, + + getAjax: function(table, c){ + var counter, + url = tsp.getAjaxUrl(table, c), + $doc = $(document), + wo = c.widgetOptions, + p = c.pager; + if ( url !== '' ) { + if (c.showProcessing) { + ts.isProcessing(table, true); // show loading icon + } + $doc.on('ajaxError.pager', function(e, xhr, settings, exception) { + tsp.renderAjax(null, table, c, xhr, exception); + $doc.unbind('ajaxError.pager'); + }); + counter = ++p.ajaxCounter; + wo.pager_ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl + wo.pager_ajaxObject.success = function(data) { + // Refuse to process old ajax commands that were overwritten by new ones - see #443 + if (counter < p.ajaxCounter){ + return; + } + tsp.renderAjax(data, table, c); + $doc.unbind('ajaxError.pager'); + if (typeof p.oldAjaxSuccess === 'function') { + p.oldAjaxSuccess(data); + } + }; + if (c.debug) { + ts.log('ajax initialized', wo.pager_ajaxObject); + } + $.ajax(wo.pager_ajaxObject); + } + }, + + getAjaxUrl: function(table, c) { + var p = c.pager, + wo = c.widgetOptions, + url = (wo.pager_ajaxUrl) ? wo.pager_ajaxUrl + // allow using "{page+1}" in the url string to switch to a non-zero based index + .replace(/\{page([\-+]\d+)?\}/, function(s,n){ return p.page + (n ? parseInt(n, 10) : 0); }) + .replace(/\{size\}/g, p.size) : '', + sl = c.sortList, + fl = p.currentFilters || $(table).data('lastSearch') || [], + sortCol = url.match(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/), + filterCol = url.match(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/), + arry = []; + if (sortCol) { + sortCol = sortCol[1]; + $.each(sl, function(i,v){ + arry.push(sortCol + '[' + v[0] + ']=' + v[1]); + }); + // if the arry is empty, just add the col parameter... "&{sortList:col}" becomes "&col" + url = url.replace(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : sortCol ); + arry = []; + } + if (filterCol) { + filterCol = filterCol[1]; + $.each(fl, function(i,v){ + if (v) { + arry.push(filterCol + '[' + i + ']=' + encodeURIComponent(v)); + } + }); + // if the arry is empty, just add the fcol parameter... "&{filterList:fcol}" becomes "&fcol" + url = url.replace(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : filterCol ); + p.currentFilters = fl; + } + if ( $.isFunction(wo.pager_customAjaxUrl) ) { + url = wo.pager_customAjaxUrl(table, url); + } + if (c.debug) { + ts.log('Pager ajax url: ' + url); + } + return url; + }, + + renderTable: function(table, rows) { + var i, $tb, + c = table.config, + p = c.pager, + wo = c.widgetOptions, + l = rows && rows.length || 0, // rows may be undefined + s = ( p.page * p.size ), + e = ( s + p.size ); + if ( l < 1 ) { return; } // empty table, abort! + if ( p.page >= p.totalPages ) { + // lets not render the table more than once + return tsp.moveToLastPage(table, p); + } + p.isDisabled = false; // needed because sorting will change the page and re-enable the pager + if (p.initialized) { c.$table.trigger('pagerChange', c); } + + if ( !wo.pager_removeRows ) { + tsp.hideRows(table, c); + } else { + if ( e > rows.length ) { + e = rows.length; + } + ts.clearTableBody(table); + $tb = ts.processTbody(table, c.$tbodies.eq(0), true); + for ( i = s; i < e; i++ ) { + $tb.append(rows[i]); + } + ts.processTbody(table, $tb, false); + } + + tsp.updatePageDisplay(table, c); + if ( !p.isDisabled ) { tsp.fixHeight(table, c); } + + wo.pager_startPage = p.page; + wo.pager_size = p.size; + c.$table.trigger('applyWidgets'); + + }, + + showAllRows: function(table, c){ + var p = c.pager, + wo = c.widgetOptions; + if ( p.ajax ) { + tsp.pagerArrows(c, true); + } else { + p.isDisabled = true; + $.data(table, 'pagerLastPage', p.page); + $.data(table, 'pagerLastSize', p.size); + p.page = 0; + p.size = p.totalRows; + p.totalPages = 1; + c.$table + .addClass('pagerDisabled') + .removeAttr('aria-describedby') + .find('tr.pagerSavedHeightSpacer').remove(); + tsp.renderTable(table, c.rowsCopy); + if (c.debug) { + ts.log('pager disabled'); + } + } + // disable size selector + p.$size.add(p.$goto).each(function(){ + $(this).attr('aria-disabled', 'true').addClass(wo.pager_css.disabled)[0].disabled = true; + }); + }, + + moveToPage: function(table, p, flag) { + if ( p.isDisabled ) { return; } + var c = table.config, + l = p.last, + pg = Math.min( p.totalPages, p.filteredPages ); + if ( p.page < 0 ) { p.page = 0; } + if ( p.page > ( pg - 1 ) && pg !== 0 ) { p.page = pg - 1; } + // don't allow rendering multiple times on the same page/size/totalpages/filters/sorts + if ( l.page === p.page && l.size === p.size && l.totalPages === p.totalPages && + (l.currentFilters || []).join(',') === (p.currentFilters || []).join(',') && + l.sortList === (c.sortList || []).join(',') ) { + return; + } + if (c.debug) { + ts.log('Pager changing to page ' + p.page); + } + p.last = { + page : p.page, + size : p.size, + // fixes #408; modify sortList otherwise it auto-updates + sortList : (c.sortList || []).join(','), + totalPages : p.totalPages, + currentFilters : p.currentFilters || [] + }; + if (p.ajax) { + tsp.getAjax(table, c); + } else if (!p.ajax) { + tsp.renderTable(table, c.rowsCopy); + } + $.data(table, 'pagerLastPage', p.page); + if (p.initialized && flag !== false) { + c.$table.trigger('pageMoved', c); + } + }, + + setPageSize: function(table, size, c) { + var p = c.pager; + p.size = size || p.size || 10; + p.$size.val(p.size); + $.data(table, 'pagerLastPage', p.page); + $.data(table, 'pagerLastSize', p.size); + p.totalPages = Math.ceil( p.totalRows / p.size ); + tsp.moveToPage(table, p); + }, + + moveToFirstPage: function(table, p) { + p.page = 0; + tsp.moveToPage(table, p); + }, + + moveToLastPage: function(table, p) { + p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 ); + tsp.moveToPage(table, p); + }, + + moveToNextPage: function(table, p) { + p.page++; + if ( p.page >= ( Math.min( p.totalPages, p.filteredPages ) - 1 ) ) { + p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 ); + } + tsp.moveToPage(table, p); + }, + + moveToPrevPage: function(table, p) { + p.page--; + if ( p.page <= 0 ) { + p.page = 0; + } + tsp.moveToPage(table, p); + }, + + destroyPager: function(table, c){ + var p = c.pager; + tsp.showAllRows(table, c); + p.$container.hide(); // hide pager + c.appender = null; // remove pager appender function + p.initialized = false; + c.$table.unbind('destroy.pager sortEnd.pager filterEnd.pager enable.pager disable.pager'); + if (ts.storage) { + ts.storage(table, c.widgetOptions.pager_storageKey, ''); + } + }, + + enablePager: function(table, c, triggered){ + var info, p = c.pager; + p.isDisabled = false; + p.page = $.data(table, 'pagerLastPage') || p.page || 0; + p.size = $.data(table, 'pagerLastSize') || parseInt(p.$size.find('option[selected]').val(), 10) || p.size || 10; + p.$size.val(p.size); // set page size + p.totalPages = Math.ceil( Math.min( p.totalRows, p.filteredRows ) / p.size ); + c.$table.removeClass('pagerDisabled'); + // if table id exists, include page display with aria info + if ( table.id ) { + info = table.id + '_pager_info'; + p.$container.find(c.widgetOptions.pager_selectors.pageDisplay).attr('id', info); + c.$table.attr('aria-describedby', info); + } + if ( triggered ) { + c.$table.trigger('updateRows'); + tsp.setPageSize(table, p.size, c); + tsp.hideRowsSetup(table, c); + tsp.fixHeight(table, c); + if (c.debug) { + ts.log('pager enabled'); + } + } + }, + + appender: function(table, rows) { + var c = table.config, + wo = c.widgetOptions, + p = c.pager; + if ( !p.ajax ) { + c.rowsCopy = rows; + p.totalRows = c.widgetOptions.pager_countChildRows ? c.$tbodies.eq(0).children().length : rows.length; + p.size = $.data(table, 'pagerLastSize') || p.size || wo.pager_size || 10; + p.totalPages = Math.ceil( p.totalRows / p.size ); + tsp.moveToPage(table, p, true); + // update display here in case all rows are removed + tsp.updatePageDisplay(table, c, false); + } + } + +}; + +// see #486 +ts.showError = function(table, message){ + $(table).each(function(){ + var $row, + c = this.config, + errorRow = c.pager && c.pager.cssErrorRow || c.widgetOptions.pager_css && c.widgetOptions.pager_css.errorRow || 'tablesorter-errorRow'; + if (c) { + if (typeof message === 'undefined') { + c.$table.find('thead').find(c.selectorRemove).remove(); + } else { + $row = ( /tr\>/.test(message) ? $(message) : $('<tr><td colspan="' + c.columns + '">' + message + '</td></tr>') ) + .click(function(){ + $(this).remove(); + }) + // add error row to thead instead of tbody, or clicking on the header will result in a parser error + .appendTo( c.$table.find('thead:first') ) + .addClass( errorRow + ' ' + c.selectorRemove.replace(/^[.#]/, '') ) + .attr({ + role : 'alert', + 'aria-live' : 'assertive' + }); + } + } + }); +}; + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js new file mode 100644 index 0000000..c551333 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js @@ -0,0 +1,50 @@ +/*! tablesorter repeatHeaders widget - updated 4/12/2013 +* Requires tablesorter v2.8+ and jQuery 1.7+ +* Original by Christian Bach from the example-widgets.html demo +*/ +/*global jQuery: false */ +;(function($){ + "use strict"; + + $.tablesorter.addWidget({ + id: "repeatHeaders", + priority: 10, + options: { + rowsToSkip : 4 + }, + // format is called on init and when a sorting has finished + format: function(table, c, wo) { + var h = '', i, $tr, l, skip; + // cache and collect all TH headers + if (!wo.repeatHeaders) { + h = '<tr class="repeated-header remove-me">'; + $.each(c.headerContent, function(i,t) { + h += '<th>' + t + '</th>'; + }); + // "remove-me" class was added in case the table needs to be updated, the "remove-me" rows will be + // removed prior to the update to prevent including the rows in the update - see "selectorRemove" option + wo.repeatHeaders = h + '</tr>'; + } + + // number of rows to skip + skip = wo && wo.rowsToSkip || 4; + + // remove appended headers by classname + c.$table.find("tr.repeated-header").remove(); + $tr = c.$tbodies.find('tr'); + l = $tr.length; + // loop all tr elements and insert a copy of the "headers" + for (i = skip; i < l; i += skip) { + // insert a copy of the table head every X rows + $tr.eq(i).before(wo.repeatHeaders); + } + }, + // this remove function is called when using the refreshWidgets method or when destroying the tablesorter plugin + // this function only applies to tablesorter v2.4+ + remove: function(table, c){ + c.$table.find("tr.repeated-header").remove(); + } + + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js new file mode 100644 index 0000000..cc66726 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -0,0 +1,241 @@ +/*! + Copyright (C) 2011 T. Connell & Associates, Inc. + + Dual-licensed under the MIT and GPL licenses + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Resizable scroller widget for the jQuery tablesorter plugin + + Version 2.0 - modified by Rob Garrison (4/12/2013) + Requires jQuery, v1.2.3 or higher + Requires the tablesorter plugin, v2.0 or higher, available at http://mottie.github.com/tablesorter/docs/ + + Usage: + + $(function() { + + $('table.tablesorter').tablesorter({ + widgets: ['zebra', 'scroller'], + widgetOptions : { + scroller_height : 300, // height of scroll window + scroller_barWidth : 17, // scroll bar width + scroller_jumpToHeader : true, // header snap to browser top when scrolling the tbody + scroller_idPrefix : 's_' // cloned thead id prefix (random number added to end) + } + }); + + }); + + Website: www.tconnell.com +*/ +/*jshint browser:true, jquery:true, unused:false */ +;(function($){ +"use strict"; + +$.fn.hasScrollBar = function(){ + return this.get(0).scrollHeight > this.height(); +}; +var ts = $.tablesorter; + +ts.window_resize = function(){ + if (this.resize_timer) { + clearTimeout(this.resize_timer); + } + this.resize_timer = setTimeout(function(){ + $(this).trigger('resizeEnd'); + }, 250); +}; + +// Add extra scroller css +$(function(){ + var s = '<style>' + + '.tablesorter-scroller-header table.tablesorter { margin-bottom: 0; }' + + '.tablesorter-scroller-table table.tablesorter { margin-top: 0; } ' + + '.tablesorter-scroller-table .tablesorter-filter-row, .tablesorter-scroller-table tfoot { display: none; }' + + '.tablesorter-scroller-table table.tablesorter thead tr.tablesorter-headerRow * {' + + 'line-height:0;height:0;border:none;background-image:none;padding-top:0;padding-bottom:0;margin-top:0;margin-bottom:0;overflow:hidden;' + + '}</style>'; + $(s).appendTo('body'); +}); + +ts.addWidget({ + id: 'scroller', + priority: 60, // run after the filter widget + options: { + scroller_height : 300, + scroller_barWidth : 17, + scroller_jumpToHeader: true, + scroller_idPrefix : 's_' + }, + init: function(table, thisWidget, c, wo){ + var $win = $(window); + //Setup window.resizeEnd event + $win + .bind('resize', ts.window_resize) + .bind('resizeEnd', function(e) { + // init is run before format, so scroller_resizeWidth + // won't be defined within the "c" or "wo" parameters + if (typeof table.config.widgetOptions.scroller_resizeWidth === 'function') { + //IE calls resize when you modify content, so we have to unbind the resize event + //so we don't end up with an infinite loop. we can rebind after we're done. + $win.unbind('resize', ts.window_resize); + table.config.widgetOptions.scroller_resizeWidth(); + $win.bind('resize', ts.window_resize); + } + }); + }, + format: function(table, c, wo) { + var h, $hdr, id, t, resize, $cells, + $win = $(window), + $tbl = c.$table, + flag = false, + filterInputs = 'input, select'; + + if (!c.isScrolling) { + h = wo.scroller_height || 300; + t = $tbl.find('tbody').height(); + if (t !== 0 && h > t) { h = t + 10; } // Table is less than h px + id = wo.scroller_id = wo.scroller_idPrefix + Math.floor(Math.random() * 101); + + $hdr = $('<table class="' + $tbl.attr('class') + '" cellpadding=0 cellspacing=0><thead>' + $tbl.find('thead:first').html() + '</thead></table>'); + $tbl + .wrap('<div id="' + id + '" class="tablesorter-scroller" style="text-align:left;" />') + .before($hdr) + .find('.tablesorter-filter-row').addClass('hideme'); + + $cells = $hdr + .wrap('<div class="tablesorter-scroller-header" style="width:' + $tbl.width() + ';" />') + .find('.' + ts.css.header) + .bind('mousedown', function(){ + this.onselectstart = function(){ return false; }; + return false; + }); + + $tbl + .wrap('<div class="tablesorter-scroller-table" style="height:' + h + 'px;width:' + $tbl.width() + ';overflow-y:scroll;" />') + .unbind('sortEnd.tsScroller') + .bind('sortEnd.tsScroller', function(){ + c.$headers.each(function(i){ + var t = $cells.eq(i); + t + .attr('class', $(this).attr('class')) + // remove processing icon + .removeClass(ts.css.processing + ' ' + c.cssProcessing); + if (ts.css.icon){ + t + .find('.' + ts.css.icon) + .attr('class', $(this).find('.' + ts.css.icon).attr('class')); + } + }); + }); + + // make scroller header sortable + c.$headers.find(c.selectorSort).add( c.$headers.filter(c.selectorSort) ).each(function(i){ + var t = $(this); + $cells.eq(i) + // clicking on new header will trigger a sort + .bind('mouseup', function(e){ + t.trigger(e, true); // external mouseup flag (click timer is ignored) + }) + // prevent header text selection + .bind('mousedown', function(){ + this.onselectstart = function(){ return false; }; + return false; + }); + }); + + // look for filter widget + $tbl.bind('filterEnd', function(){ + if (flag) { return; } + $cells.each(function(i){ + $(this).find(filterInputs).val( c.$filters.find(filterInputs).eq(i).val() ); + }); + }); + $hdr.find(filterInputs).bind('keyup search', function(e){ + // ignore arrow and meta keys; allow backspace + if ((e.which < 32 && e.which !== 8) || (e.which >= 37 && e.which <=40)) { return; } + flag = true; + var $f = $(this), col = $f.attr('data-column'); + c.$filters.find(filterInputs).eq(col) + .val( $f.val() ) + .trigger('search'); + setTimeout(function(){ + flag = false; + }, wo.filter_searchDelay); + }); + + resize = function(){ + var d, + //Hide other scrollers so we can resize + $div = $('div.scroller[id != "' + id + '"]').hide(); + + $tbl.find('thead').show(); + + //Reset sizes so parent can resize. + $hdr + .width(0) + .parent().width(0) + .find('th,td').width(0); + + $tbl + .width(0) + .find('thead').find('th,td').width(0); + d = $tbl.parent(); + d.width(0); + + d.parent().trigger('resize'); + // Shrink a bit to accommodate scrollbar + d.width( d.parent().innerWidth() - ( d.parent().hasScrollBar() ? wo.scroller_barWidth : 0 ) ); + + $tbl.width( d.innerWidth() - ( d.hasScrollBar() ? wo.scroller_barWidth : 0 ) ); + $tbl.find('thead').find('th,td').filter(':visible').each(function(i, c){ + var $th = $(c), + //Wrap in browser detect?? + w = parseInt( $th.css('min-width').replace('auto', '0').replace(/(px|em)/, ''), 10 ); + if ( $th.width() < w ) { + $th.width(w); + } else { + w = $th.width(); + } + $hdr.find('th,td').eq(i).width(w); + }); + + $hdr.width($tbl.innerWidth()); + $div.show(); + }; + + //Expose to external calls + wo.scroller_resizeWidth = resize; + + resize(); + + $tbl.find('thead').css('visibility', 'hidden'); + c.isScrolling = true; + + t = $tbl.parent().parent().height(); + // The header will always jump into view if scrolling the table body + $tbl.parent().bind('scroll', function(){ + if (wo.scroller_jumpToHeader) { + var pos = $win.scrollTop() - $hdr.offset().top; + if ($(this).scrollTop() !== 0 && pos < t && pos > 0) { + $win.scrollTop( $hdr.offset().top ); + } + } + }); + + } + + //Sorting, so scroll to top + $tbl.parent().animate({ scrollTop: 0 }, 'fast'); + + }, + remove : function(table, c, wo){ + + } + }); + +})(jQuery); From 266ef051c0570f6fd9c3048d846d9f95f27cdd15 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Sat, 8 Mar 2014 09:20:11 +0100 Subject: [PATCH 018/138] * updated tablesorter to latest version (2.15.6) --- CHANGELOG.markdown => CHANGELOG.md | 4 + README.markdown => README.md | 6 +- jquery-tablesorter.gemspec | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 54 ++++--- .../jquery-tablesorter/jquery.tablesorter.js | 148 ++++++++++-------- ...ry.tablesorter.widgets-filter-formatter.js | 2 +- .../jquery.tablesorter.widgets.js | 13 +- .../parsers/parser-date-iso8601.js | 2 +- .../parsers/parser-date-month.js | 6 +- .../parsers/parser-date-two-digit-year.js | 14 +- .../parsers/parser-input-select.js | 7 +- .../widgets/widget-columnSelector.js | 19 ++- .../widgets/widget-grouping.js | 139 +++++++++++----- .../widgets/widget-headerTitles.js | 32 ++++ .../widgets/widget-pager.js | 35 +++-- .../jquery-tablesorter/theme.bootstrap.css | 8 +- 18 files changed, 329 insertions(+), 166 deletions(-) rename CHANGELOG.markdown => CHANGELOG.md (97%) rename README.markdown => README.md (92%) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js diff --git a/CHANGELOG.markdown b/CHANGELOG.md similarity index 97% rename from CHANGELOG.markdown rename to CHANGELOG.md index 2713a3d..fae01fd 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.10.4 (2014-03-08) + +* Upgrade tablesorter to v2.15.6 + #### v1.10.3 (2014-02-23) * Upgrade tablesorter to v2.15.5 diff --git a/README.markdown b/README.md similarity index 92% rename from README.markdown rename to README.md index df9789f..a696ae0 100644 --- a/README.markdown +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.15.5 (2/23/2014), [documentation] +Current tablesorter version: 2.15.6 (3/7/2014), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. @@ -25,6 +25,7 @@ Or install it yourself as: ## Requirements Rails 3.1 and higher (tested up to 4.1) + Tested with ruby 1.9.3 - 2.1.0 ## Usage @@ -37,7 +38,7 @@ In your `application.js` //= require jquery-tablesorter ``` -This will require all jquery-tablesorter files (exclude addons, widgets, ...). +This will require all jquery-tablesorter files (excluding addons, widgets, ...). Or you can include single file with: @@ -48,6 +49,7 @@ Or you can include single file with: //= require jquery-tablesorter/addons/pager/jquery.tablesorter.pager //= require jquery-tablesorter/widgets/widget-repeatheaders //= require jquery-tablesorter/parsers/parser-metric +//= require jquery-tablesorter/extras/jquery.quicksearch ``` ### Stylesheet files diff --git a/jquery-tablesorter.gemspec b/jquery-tablesorter.gemspec index de158b1..54f3edd 100644 --- a/jquery-tablesorter.gemspec +++ b/jquery-tablesorter.gemspec @@ -14,7 +14,7 @@ Gem::Specification.new do |s| s.description = "Simple integration of jquery-tablesorter into the asset pipeline." s.license = "MIT" - s.files = Dir["{vendor,lib}/**/*"] + ["MIT-LICENSE", "Rakefile", "README.markdown"] + s.files = Dir["{vendor,lib}/**/*"] + ["MIT-LICENSE", "Rakefile", "README.md"] s.add_dependency "railties", ">= 3.1", "< 5" end diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index cd4cc30..58f4459 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = "1.10.3" + VERSION = "1.10.4" end diff --git a/tablesorter b/tablesorter index cf1ea13..35be668 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit cf1ea135cbb30ac78e99f4a7ad33f790a33db64f +Subproject commit 35be6689fbef80d2381c709d108d91d1ac47e00e diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index a09b792..2ed9373 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 2/22/2014 (v2.15.4) + * updated 3/7/2014 (v2.15.6) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -124,7 +124,7 @@ } }, - updatePageDisplay = function(table, p, flag) { + updatePageDisplay = function(table, p, completed) { var i, pg, s, out, c = table.config, f = c.$table.hasClass('hasFilters') && !p.ajaxUrl, @@ -168,7 +168,7 @@ } } pagerArrows(p); - if (p.initialized && flag !== false) { + if (p.initialized && completed !== false) { c.$table.trigger('pagerComplete', p); // save pager info to storage if (p.savePages && ts.storage) { @@ -263,6 +263,7 @@ exception === 'abort' ? 'Ajax Request aborted' : 'Uncaught error: ' + xhr.statusText + ' [' + xhr.status + ']' ); c.$tbodies.eq(0).empty(); + p.totalRows = 0; } else { // process ajax object if (!$.isArray(result)) { @@ -273,7 +274,7 @@ } else { // allow [ total, rows, headers ] or [ rows, total, headers ] t = isNaN(result[0]) && !isNaN(result[1]); - //ensure a zero returned row count doesn't fail the logical || + // ensure a zero returned row count doesn't fail the logical || rr_count = result[t ? 1 : 0]; p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count; d = p.totalRows === 0 ? [""] : result[t ? 0 : 1] || []; // row data @@ -331,7 +332,8 @@ } // make sure last pager settings are saved, prevents multiple server side calls with // the same parameters - p.last.totalPages = p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) ); + p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) ); + p.last.totalRows = p.totalRows; p.last.currentFilters = p.currentFilters; p.last.sortList = (c.sortList || []).join(','); updatePageDisplay(table, p); @@ -339,10 +341,12 @@ $t.trigger('updateCache', [function(){ if (p.initialized) { // apply widgets after table has rendered - $t.trigger('applyWidgets'); - $t.trigger('pagerChange', p); + $t + .trigger('applyWidgets') + .trigger('pagerChange', p); } }]); + } if (!p.initialized) { p.initialized = true; @@ -429,6 +433,8 @@ renderTable = function(table, rows, p) { var i, $tb, + $t = $(table), + c = table.config, l = rows && rows.length || 0, // rows may be undefined s = ( p.page * p.size ), e = ( s + p.size ); @@ -438,7 +444,7 @@ moveToLastPage(table, p); } p.isDisabled = false; // needed because sorting will change the page and re-enable the pager - if (p.initialized) { $(table).trigger('pagerChange', p); } + if (p.initialized) { $t.trigger('pagerChange', p); } if ( !p.removeRows ) { hideRows(table, p); @@ -447,7 +453,7 @@ e = rows.length; } ts.clearTableBody(table); - $tb = ts.processTbody(table, table.config.$tbodies.eq(0), true); + $tb = ts.processTbody(table, c.$tbodies.eq(0), true); for ( i = s; i < e; i++ ) { $tb.append(rows[i]); } @@ -456,7 +462,10 @@ updatePageDisplay(table, p); if ( !p.isDisabled ) { fixHeight(table, p); } - $(table).trigger('applyWidgets'); + $t.trigger('applyWidgets'); + if (table.isUpdating) { + $t.trigger("updateComplete", table); + } }, showAllRows = function(table, p){ @@ -484,15 +493,20 @@ }); }, - moveToPage = function(table, p, flag) { + moveToPage = function(table, p, pageMoved) { if ( p.isDisabled ) { return; } var c = table.config, + $t = $(table), l = p.last, pg = Math.min( p.totalPages, p.filteredPages ); if ( p.page < 0 ) { p.page = 0; } if ( p.page > ( pg - 1 ) && pg !== 0 ) { p.page = pg - 1; } - // don't allow rendering multiple times on the same page/size/totalpages/filters/sorts - if ( l.page === p.page && l.size === p.size && l.totalPages === p.totalPages && + // fixes issue where one currentFilter is [] and the other is ['','',''], + // making the next if comparison think the filters are different (joined by commas). Fixes #202. + l.currentFilters = (l.currentFilters || []).join('') === '' ? [] : l.currentFilters; + p.currentFilters = (p.currentFilters || []).join('') === '' ? [] : p.currentFilters; + // don't allow rendering multiple times on the same page/size/totalRows/filters/sorts + if ( l.page === p.page && l.size === p.size && l.totalRows === p.totalRows && (l.currentFilters || []).join(',') === (p.currentFilters || []).join(',') && l.sortList === (c.sortList || []).join(',') ) { return; } if (c.debug) { @@ -503,18 +517,22 @@ size : p.size, // fixes #408; modify sortList otherwise it auto-updates sortList : (c.sortList || []).join(','), - totalPages : p.totalPages, + totalRows : p.totalRows, currentFilters : p.currentFilters || [] }; if (p.ajax) { getAjax(table, p); } else if (!p.ajax) { - renderTable(table, table.config.rowsCopy, p); + renderTable(table, c.rowsCopy, p); } $.data(table, 'pagerLastPage', p.page); - if (p.initialized && flag !== false) { - c.$table.trigger('pageMoved', p); - c.$table.trigger('applyWidgets'); + if (p.initialized && pageMoved !== false) { + $t + .trigger('pageMoved', p) + .trigger('applyWidgets'); + if (table.isUpdating) { + $t.trigger('updateComplete'); + } } }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 0375d87..e11f9c6 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.15.5 - Client-side table sorting with ease! +* TableSorter 2.15.6 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.15.5"; + ts.version = "2.15.6"; ts.parsers = []; ts.widgets = []; @@ -331,7 +331,8 @@ // empty table - fixes #206/#346 if (isEmptyObject(c2)) { // run pager appender in case the table was just emptied - return c.appender ? c.appender(table, rows) : ''; + return c.appender ? c.appender(table, rows) : + table.isUpdating ? c.$table.trigger("updateComplete", table) : ''; // Fixes #532 } if (c.debug) { appendTime = new Date(); @@ -368,9 +369,9 @@ } // apply table widgets; but not before ajax completes if (!init && !c.appender) { ts.applyWidget(table); } - // trigger sortend - $(table).trigger("sortEnd", table); - $(table).trigger("updateComplete", table); + if (table.isUpdating) { + c.$table.trigger("updateComplete", table); + } } // computeTableHeaderCellIndexes from: @@ -582,92 +583,95 @@ return (parsers && parsers[i]) ? parsers[i].type || '' : ''; } - function initSort(table, cell, e){ - var a, i, j, o, s, + function initSort(table, cell, event){ + var arry, indx, col, order, s, c = table.config, - k = !e[c.sortMultiSortKey], - $table = $(table); + key = !event[c.sortMultiSortKey], + $table = c.$table; // Only call sortStart if sorting is enabled $table.trigger("sortStart", table); // get current column sort order - cell.count = e[c.sortResetKey] ? 2 : (cell.count + 1) % (c.sortReset ? 3 : 2); + cell.count = event[c.sortResetKey] ? 2 : (cell.count + 1) % (c.sortReset ? 3 : 2); // reset all sorts on non-current column - issue #30 if (c.sortRestart) { - i = cell; + indx = cell; c.$headers.each(function() { // only reset counts on columns that weren't just clicked on and if not included in a multisort - if (this !== i && (k || !$(this).is('.' + ts.css.sortDesc + ',.' + ts.css.sortAsc))) { + if (this !== indx && (key || !$(this).is('.' + ts.css.sortDesc + ',.' + ts.css.sortAsc))) { this.count = -1; } }); } // get current column index - i = cell.column; + indx = cell.column; // user only wants to sort on one column - if (k) { + if (key) { // flush the sort list c.sortList = []; if (c.sortForce !== null) { - a = c.sortForce; - for (j = 0; j < a.length; j++) { - if (a[j][0] !== i) { - c.sortList.push(a[j]); + arry = c.sortForce; + for (col = 0; col < arry.length; col++) { + if (arry[col][0] !== indx) { + c.sortList.push(arry[col]); } } } // add column to sort list - o = cell.order[cell.count]; - if (o < 2) { - c.sortList.push([i, o]); + order = cell.order[cell.count]; + if (order < 2) { + c.sortList.push([indx, order]); // add other columns if header spans across multiple if (cell.colSpan > 1) { - for (j = 1; j < cell.colSpan; j++) { - c.sortList.push([i + j, o]); + for (col = 1; col < cell.colSpan; col++) { + c.sortList.push([indx + col, order]); } } } // multi column sorting } else { - // get rid of the sortAppend before adding more - fixes issue #115 + // get rid of the sortAppend before adding more - fixes issue #115 & #523 if (c.sortAppend && c.sortList.length > 1) { - if (ts.isValueInArray(c.sortAppend[0][0], c.sortList)) { - c.sortList.pop(); + for (col = 0; col < c.sortAppend.length; col++) { + s = ts.isValueInArray(c.sortAppend[col][0], c.sortList); + if (s >= 0) { + c.sortList.splice(s,1); + } } } // the user has clicked on an already sorted column - if (ts.isValueInArray(i, c.sortList)) { + if (ts.isValueInArray(indx, c.sortList) >= 0) { // reverse the sorting direction - for (j = 0; j < c.sortList.length; j++) { - s = c.sortList[j]; - o = c.$headers[s[0]]; - if (s[0] === i) { - // o.count seems to be incorrect when compared to cell.count - s[1] = o.order[cell.count]; + for (col = 0; col < c.sortList.length; col++) { + s = c.sortList[col]; + order = c.$headers[s[0]]; + if (s[0] === indx) { + // order.count seems to be incorrect when compared to cell.count + s[1] = order.order[cell.count]; if (s[1] === 2) { - c.sortList.splice(j,1); - o.count = -1; + c.sortList.splice(col,1); + order.count = -1; } } } } else { // add column to sort list array - o = cell.order[cell.count]; - if (o < 2) { - c.sortList.push([i, o]); + order = cell.order[cell.count]; + if (order < 2) { + c.sortList.push([indx, order]); // add other columns if header spans across multiple if (cell.colSpan > 1) { - for (j = 1; j < cell.colSpan; j++) { - c.sortList.push([i + j, o]); + for (col = 1; col < cell.colSpan; col++) { + c.sortList.push([indx + col, order]); } } } } } if (c.sortAppend !== null) { - a = c.sortAppend; - for (j = 0; j < a.length; j++) { - if (a[j][0] !== i) { - c.sortList.push(a[j]); + arry = c.sortAppend; + for (col = 0; col < arry.length; col++) { + if (arry[col][0] !== indx) { + c.sortList.push(arry[col]); } } } @@ -679,6 +683,7 @@ setHeadersCss(table); multisort(table); appendToTable(table); + $table.trigger("sortEnd", table); }, 1); } @@ -751,8 +756,8 @@ } function resortComplete($table, callback){ - var c = $table[0].config; - if (c.pager && !c.pager.ajax) { + var table = $table[0]; + if (table.isUpdating) { $table.trigger('updateComplete'); } if (typeof callback === "function") { @@ -761,12 +766,13 @@ } function checkResort($table, flag, callback) { + var sl = $table[0].config.sortList; // don't try to resort if the table is still processing // this will catch spamming of the updateCell method - if (flag !== false && !$table[0].isProcessing) { - $table.trigger("sorton", [$table[0].config.sortList, function(){ + if (flag !== false && !$table[0].isProcessing && sl.length) { + $table.trigger("sorton", [sl, function(){ resortComplete($table, callback); - }]); + }, true]); } else { resortComplete($table, callback); } @@ -777,7 +783,7 @@ $table = c.$table; // apply easy methods that trigger bound events $table - .unbind('sortReset update updateRows updateCell updateAll addRows sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave '.split(' ').join('.tablesorter ')) + .unbind('sortReset update updateRows updateCell updateAll addRows updateComplete sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave '.split(' ').join('.tablesorter ')) .bind("sortReset.tablesorter", function(e){ e.stopPropagation(); c.sortList = []; @@ -787,6 +793,7 @@ }) .bind("updateAll.tablesorter", function(e, resort, callback){ e.stopPropagation(); + table.isUpdating = true; ts.refreshWidgets(table, true, true); ts.restoreHeaders(table); buildHeaders(table); @@ -796,12 +803,14 @@ }) .bind("update.tablesorter updateRows.tablesorter", function(e, resort, callback) { e.stopPropagation(); + table.isUpdating = true; // update sorting (if enabled/disabled) updateHeader(table); commonUpdate(table, resort, callback); }) .bind("updateCell.tablesorter", function(e, cell, resort, callback) { e.stopPropagation(); + table.isUpdating = true; $table.find(c.selectorRemove).remove(); // get position from the dom var l, row, icell, @@ -823,6 +832,7 @@ }) .bind("addRows.tablesorter", function(e, $row, resort, callback) { e.stopPropagation(); + table.isUpdating = true; if (isEmptyObject(c.cache)) { // empty table, do an update instead - fixes #450 updateHeader(table); @@ -853,6 +863,9 @@ checkResort($table, resort, callback); } }) + .bind("updateComplete.tablesorter", function(){ + table.isUpdating = false; + }) .bind("sorton.tablesorter", function(e, list, callback, init) { var c = table.config; e.stopPropagation(); @@ -867,6 +880,7 @@ // sort the table and append it to the dom multisort(table); appendToTable(table, init); + $table.trigger("sortEnd", this); if (typeof callback === "function") { callback(table); } @@ -991,7 +1005,7 @@ ts.applyWidget(table, true); // if user has supplied a sort list to constructor if (c.sortList.length > 0) { - $table.trigger("sorton", [c.sortList, {}, !c.initWidgets]); + $table.trigger("sorton", [c.sortList, {}, !c.initWidgets, true]); } else { setHeadersCss(table); if (c.initWidgets) { @@ -1032,7 +1046,7 @@ // get headers from the sortList $h = $h.filter(function(){ // get data-column from attr to keep compatibility with jQuery 1.2.6 - return this.sortDisabled ? false : ts.isValueInArray( parseFloat($(this).attr('data-column')), c.sortList); + return this.sortDisabled ? false : ts.isValueInArray( parseFloat($(this).attr('data-column')), c.sortList) >= 0; }); } $h.addClass(ts.css.processing + ' ' + c.cssProcessing); @@ -1132,7 +1146,7 @@ // disable tablesorter $t .removeData('tablesorter') - .unbind('sortReset update updateAll updateRows updateCell addRows sorton appendCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress sortBegin sortEnd '.split(' ').join('.tablesorter ')); + .unbind('sortReset update updateAll updateRows updateCell addRows updateComplete sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress sortBegin sortEnd '.split(' ').join('.tablesorter ')); c.$headers.add($f) .removeClass( [ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone].join(' ') ) .removeAttr('data-column'); @@ -1152,6 +1166,8 @@ // regex used in natural sort ts.regex = { chunk : /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi, // chunk/tokenize numbers & letters + schunk: /^\\0/, // start chunk + echunk: /\\0$/, // end chunk hex: /^0x[0-9a-f]+$/i // hex }; @@ -1168,8 +1184,8 @@ if ( xD > yD ) { return 1; } } // chunk/tokenize - xN = a.replace(r.chunk, '\\0$1\\0').replace(/\\0$/, '').replace(/^\\0/, '').split('\\0'); - yN = b.replace(r.chunk, '\\0$1\\0').replace(/\\0$/, '').replace(/^\\0/, '').split('\\0'); + xN = a.replace(r.chunk, '\\0$1\\0').replace(r.echunk, '').replace(r.schunk, '').split('\\0'); + yN = b.replace(r.chunk, '\\0$1\\0').replace(r.echunk, '').replace(r.schunk, '').split('\\0'); mx = Math.max(xN.length, yN.length); // natural sorting through split numeric strings and default strings for (i = 0; i < mx; i++) { @@ -1291,14 +1307,14 @@ }; // *** utilities *** - ts.isValueInArray = function(v, a) { - var i, l = a.length; - for (i = 0; i < l; i++) { - if (a[i][0] === v) { - return true; + ts.isValueInArray = function(column, arry) { + var indx, len = arry.length; + for (indx = 0; indx < len; indx++) { + if (arry[indx][0] === column) { + return indx; } } - return false; + return -1; }; ts.addParser = function(parser) { @@ -1544,7 +1560,7 @@ return (/^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}/).test(s); }, format: function(s, table) { - return s ? ts.formatFloat((s !== "") ? (new Date(s.replace(/-/g, "/")).getTime() || "") : "", table) : s; + return s ? ts.formatFloat((s !== "") ? (new Date(s.replace(/-/g, "/")).getTime() || s) : "", table) : s; }, type: "numeric" }); @@ -1568,7 +1584,7 @@ return (/^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i).test(s) || (/^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i).test(s); }, format: function(s, table) { - return s ? ts.formatFloat( (new Date(s.replace(/(\S)([AP]M)$/i, "$1 $2")).getTime() || ''), table) : s; + return s ? ts.formatFloat( (new Date(s.replace(/(\S)([AP]M)$/i, "$1 $2")).getTime() || s), table) : s; }, type: "numeric" }); @@ -1593,7 +1609,7 @@ s = s.replace(/(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/, "$1/$2/$3"); } } - return s ? ts.formatFloat( (new Date(s).getTime() || ''), table) : s; + return s ? ts.formatFloat( (new Date(s).getTime() || s), table) : s; }, type: "numeric" }); @@ -1604,7 +1620,7 @@ return (/^(([0-2]?\d:[0-5]\d)|([0-1]?\d:[0-5]\d\s?([AP]M)))$/i).test(s); }, format: function(s, table) { - return s ? ts.formatFloat( (new Date("2000/01/01 " + s.replace(/(\S)([AP]M)$/i, "$1 $2")).getTime() || ""), table) : s; + return s ? ts.formatFloat( (new Date("2000/01/01 " + s.replace(/(\S)([AP]M)$/i, "$1 $2")).getTime() || s), table) : s; }, type: "numeric" }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js index d23106e..ba5ef58 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js @@ -487,7 +487,7 @@ tsff = ts.filterFormatter = { endOfDay : true, // include ANY jQuery UI spinner options below - defaultDate : '', // ******************************** FIX THIS ******************************************* + defaultDate : '', changeMonth : true, changeYear : true, diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index ca9fd65..71bea63 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.8+ widgets - updated 2/21/2014 (v2.15.3) +/*! tableSorter 2.8+ widgets - updated 3/7/2014 (v2.15.6) * * Column Styles * Column Filters @@ -654,15 +654,21 @@ ts.filter = { c.$table.trigger('filterInit'); }, setDefaults: function(table, c, wo) { - var isArray, saved, + var isArray, saved, indx, // get current (default) filters - filters = ts.getFilters(table); + filters = ts.getFilters(table) || []; if (wo.filter_saveFilters && ts.storage) { saved = ts.storage( table, 'tablesorter-filters' ) || []; isArray = $.isArray(saved); // make sure we're not just getting an empty array if ( !(isArray && saved.join('') === '' || !isArray) ) { filters = saved; } } + // if no filters saved, then check default settings + if (filters.join('') === '') { + for (indx = 0; indx < c.columns; indx++) { + filters[indx] = c.$headers.filter('[data-column="' + indx + '"]:last').attr(wo.filter_defaultAttrib) || filters[indx]; + } + } c.$table.data('lastSearch', filters); return filters; }, @@ -1010,6 +1016,7 @@ ts.filter = { c.$table.trigger('filterEnd'); }, buildSelect: function(table, column, updating, onlyavail) { + if (!table.config.cache || $.isEmptyObject(table.config.cache)) { return; } column = parseInt(column, 10); var indx, rowIndex, tbodyIndex, len, currentValue, txt, $filters, c = table.config, diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js index fe38417..0a133c3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js @@ -26,7 +26,7 @@ if (result[12]) { date.setMilliseconds(Number('0.' + result[12]) * 1000); } return date; } - return 0; + return s; }, type : 'numeric' }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js index 68689fc..4c725d6 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js @@ -18,10 +18,10 @@ return false; }, format: function(s, table) { - var j = -1, c = table.config; - s = c.ignoreCase ? s.toLocaleLowerCase() : s; + var j = -1, c = table.config, + n = c.ignoreCase ? s.toLocaleLowerCase() : s; $.each(ts.dates[ 'month' + (c.ignoreCase ? 'Lower' : 'Cased') ], function(i,v){ - if (j < 0 && s.match(v)) { j = i; } + if (j < 0 && n.match(v)) { j = i; } }); // return s (original string) if there isn't a match // (non-weekdays will sort separately and empty cells will sort as expected) diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js index 932a32f..8d322bc 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js @@ -18,12 +18,12 @@ }); ts.formatDate = function(s, regex, format, table){ - s = s - // replace separators - .replace(/\s+/g," ").replace(/[-.,]/g, "/") - // reformat xx/xx/xx to mm/dd/19yy; - .replace(regex, format); - var d = new Date(s), + var n = s + // replace separators + .replace(/\s+/g," ").replace(/[-.,]/g, "/") + // reformat xx/xx/xx to mm/dd/19yy; + .replace(regex, format), + d = new Date(n), y = d.getFullYear(), rng = table && table.config.dateRange || range, now = new Date().getFullYear(); @@ -32,7 +32,7 @@ while (now - y > rng) { y += 100; } - return d.setFullYear(y); + return d.setFullYear(y) || s; }; $.tablesorter.addParser({ diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index 929e4b5..6d79c9a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -36,14 +36,15 @@ return false; }, format: function(s, table, cell, cellIndex) { - var $c = $(cell).find('input'), - isChecked = $c[0].checked; + var $c = $(cell), + $input = $c.find('input[type="checkbox"]'), + isChecked = $input.length ? $input[0].checked : ''; // adding class to row, indicating that a checkbox is checked; includes // a column index in case more than one checkbox happens to be in a row $c.closest('tr').toggleClass('checked-' + cellIndex, isChecked); // returning plain language here because this is what is shown in the // group headers - change it as desired - return $c.length ? isChecked ? 'checked' : 'unchecked' : s; + return $input.length ? isChecked ? 'checked' : 'unchecked' : s; }, parsed : true, // filter widget flag type: "text" diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index cdf240a..38afa54 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -62,18 +62,21 @@ tsColSel = ts.columnSelector = { var $this = $(this), // if no data-priority is assigned, default to 1, but don't remove it from the selector list priority = $this.attr(wo.columnSelector_priority) || 1, - colId = $this.attr('data-column'); + colId = $this.attr('data-column'), + state = ts.getData(this, c.headers[colId], 'columnSelector'); + // if this column not hidable at all // include getData check (includes "columnSelector-false" class, data attribute, etc) - if ( isNaN(priority) && priority.length > 0 || ts.getData(this, c.headers[colId], 'columnSelector') == 'false' || + if ( isNaN(priority) && priority.length > 0 || state === 'disable' || ( wo.columnSelector_columns[colId] && wo.columnSelector_columns[colId] === 'disable') ) { return true; // goto next } - // set default state + // set default state; storage takes priority colSel.states[colId] = saved && typeof(saved[colId]) !== 'undefined' ? - saved[colId] : typeof(wo.columnSelector_columns[colId]) !== 'undefined' ? wo.columnSelector_columns[colId] : true; + saved[colId] : typeof(wo.columnSelector_columns[colId]) !== 'undefined' ? + wo.columnSelector_columns[colId] : state === 'true' || !(state === 'false'); colSel.$column[colId] = $(this); // set default col title @@ -135,8 +138,8 @@ tsColSel = ts.columnSelector = { c.selector.$popup.find('.tablesorter-column-selector') .html( colSel.$container.html() ) .find('input').each(function(){ - var indx = $(this).attr('data-column') - $(this).prop( 'checked', indx === 'auto' ? wo.columnSelector_mediaqueryState : colSel.states[indx] ) + var indx = $(this).attr('data-column'); + $(this).prop( 'checked', indx === 'auto' ? wo.columnSelector_mediaqueryState : colSel.states[indx] ); }); } }).change(); @@ -210,8 +213,8 @@ tsColSel = ts.columnSelector = { }, attachTo : function(table, elm) { + table = $(table)[0]; var colSel, wo, indx, - table = $(table)[0], c = table.config, $popup = $(elm); if ($popup.length && c) { @@ -225,7 +228,7 @@ tsColSel = ts.columnSelector = { .html( colSel.$container.html() ) .find('input').each(function(){ var indx = $(this).attr('data-column'); - $(this).prop( 'checked', indx === 'auto' ? wo.columnSelector_mediaqueryState : colSel.states[indx] ) + $(this).prop( 'checked', indx === 'auto' ? wo.columnSelector_mediaqueryState : colSel.states[indx] ); }); colSel.$popup = $popup.on('change', 'input', function(){ // data input diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index 76f9bbd..b5a6b80 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -1,4 +1,4 @@ -/*! tablesorter Grouping widget - updated 12/18/2013 (core v2.15.0) +/*! tablesorter Grouping widget - updated 3/7/2014 (core v2.15.6) * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -53,9 +53,10 @@ ts.grouping = { update : function(table, c, wo){ if ($.isEmptyObject(c.cache)) { return; } - var rowIndex, tbodyIndex, currentGroup, $rows, groupClass, grouping, time, cache, + var rowIndex, tbodyIndex, currentGroup, $rows, groupClass, grouping, time, cache, saveName, direction, lang = wo.grouping_language, group = '', + savedGroup = false, column = c.sortList[0] ? c.sortList[0][0] : -1; c.$table .find('tr.group-hidden').removeClass('group-hidden').end() @@ -66,19 +67,33 @@ ts.grouping = { } if (column >= 0 && !c.$headers.filter('[data-column="' + column + '"]:last').hasClass('group-false')) { if (c.debug){ time = new Date(); } + wo.group_currentGroup = ''; // save current groups + wo.group_currentGroups = {}; + + // group class finds "group-{word/separator/letter/number/date/false}-{optional:#/year/month/day/week/time}" + groupClass = (c.$headers.filter('[data-column="' + column + '"]:last').attr('class') || '').match(/(group-\w+(-\w+)?)/g); + // grouping = [ 'group', '{word/separator/letter/number/date/false}', '{#/year/month/day/week/time}' ] + grouping = groupClass ? groupClass[0].split('-') : ['group','letter',1]; // default to letter 1 + + // save current grouping + if (wo.group_collapsible && wo.group_saveGroups && ts.storage) { + wo.group_currentGroups = ts.storage( table, 'tablesorter-groups' ) || {}; + // include direction when grouping numbers > 1 (reversed direction shows different range values) + direction = (grouping[1] === 'number' && grouping[2] > 1) ? 'dir' + c.sortList[0][1] : ''; + // combine column, sort direction & grouping as save key + saveName = wo.group_currentGroup = '' + column + direction + grouping.join(''); + if (!wo.group_currentGroups[saveName]) { + wo.group_currentGroups[saveName] = []; + } else { + savedGroup = true; + } + } for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++) { cache = c.cache[tbodyIndex].normalized; group = ''; // clear grouping across tbodies $rows = c.$tbodies.eq(tbodyIndex).children('tr').not('.' + c.cssChildRow); - if (wo.group_collapsed && wo.group_collapsible) { - $rows.addClass('group-hidden'); - } for (rowIndex = 0; rowIndex < $rows.length; rowIndex++) { if ( $rows.eq(rowIndex).is(':visible') ) { - // group class finds "group-{word/separator/letter/number/date/false}-{optional:#/year/month/day/week/time}" - groupClass = (c.$headers.filter('[data-column="' + column + '"]:last').attr('class') || '').match(/(group-\w+(-\w+)?)/g); - // grouping = [ 'group', '{word/separator/letter/number/date/false}', '{#/year/month/day/week/time}' ] - grouping = groupClass ? groupClass[0].split('-') : ['','letter',1]; // default to letter 1 // fixes #438 if (ts.grouping.types[grouping[1]]) { currentGroup = cache[rowIndex] ? @@ -95,22 +110,29 @@ ts.grouping = { currentGroup = wo.group_formatter((currentGroup || '').toString(), column, table, c, wo) || currentGroup; } $rows.eq(rowIndex).before('<tr class="group-header ' + c.selectorRemove.slice(1) + - (wo.group_collapsed && wo.group_collapsible ? ' collapsed' : '') + '" unselectable="on"><td colspan="' + + // (wo.group_collapsed && wo.group_collapsible ? ' collapsed' : '') + + '" unselectable="on"><td colspan="' + c.columns + '">' + (wo.group_collapsible ? '<i/>' : '') + '<span class="group-name">' + currentGroup + '</span><span class="group-count"></span></td></tr>'); + if (wo.group_saveGroups && !savedGroup && wo.group_collapsed && wo.group_collapsible) { + // all groups start collapsed + wo.group_currentGroups[wo.group_currentGroup].push(currentGroup); + } } } } } } - $rows = c.$table.find('tr.group-header').bind('selectstart', false); - if (wo.group_count || $.isFunction(wo.group_callback)) { - $rows.each(function(){ - var $rows, - $row = $(this), - $label = $row.find('.group-count'); + c.$table.find('tr.group-header') + .bind('selectstart', false) + .each(function(){ + var isHidden, $label, + $row = $(this), + name = $row.find('.group-name').text().toLowerCase(), + $rows = $row.nextUntil('tr.group-header').filter(':visible'); + if (wo.group_count || $.isFunction(wo.group_callback)) { + $label = $row.find('.group-count'); if ($label.length) { - $rows = $row.nextUntil('tr.group-header').filter(':visible'); if (wo.group_count) { $label.html( wo.group_count.replace(/\{num\}/g, $rows.length) ); } @@ -118,13 +140,71 @@ ts.grouping = { wo.group_callback($row.find('td'), $rows, column, table); } } - }); - } + } + if (wo.group_saveGroups && wo.group_currentGroups[wo.group_currentGroup].length) { + isHidden = $.inArray( name, wo.group_currentGroups[wo.group_currentGroup] ) > -1; + $row.toggleClass('collapsed', isHidden); + $rows.toggleClass('group-hidden', isHidden); + } else if (wo.group_collapsed && wo.group_collapsible) { + $row.addClass('collapsed'); + $rows.addClass('group-hidden'); + } + }); c.$table.trigger(wo.group_complete); if (c.debug) { $.tablesorter.benchmark("Applying groups widget: ", time); } } + }, + + bindEvents : function(table, c, wo){ + if (wo.group_collapsible) { + wo.group_currentGroups = []; + // .on() requires jQuery 1.7+ + c.$table.on('click toggleGroup', 'tr.group-header', function(event){ + event.stopPropagation(); + var isCollapsed, $groups, indx, + $this = $(this), + name = $this.find('.group-name').text(); + // use shift-click to toggle ALL groups + if (event.type === 'click' && event.shiftKey) { + $this.siblings('.group-header').trigger('toggleGroup'); + } + $this.toggleClass('collapsed'); + // nextUntil requires jQuery 1.4+ + $this.nextUntil('tr.group-header').toggleClass('group-hidden', $this.hasClass('collapsed') ); + // save collapsed groups + if (wo.group_saveGroups && ts.storage) { + $groups = c.$table.find('.group-header'); + isCollapsed = $this.hasClass('collapsed'); + if (!wo.group_currentGroups[wo.group_currentGroup]) { + wo.group_currentGroups[wo.group_currentGroup] = []; + } + if (isCollapsed && wo.group_currentGroup) { + wo.group_currentGroups[wo.group_currentGroup].push( name ); + } else if (wo.group_currentGroup) { + indx = $.inArray( name, wo.group_currentGroups[wo.group_currentGroup] ); + if (indx > -1) { + wo.group_currentGroups[wo.group_currentGroup].splice( indx, 1 ); + } + } + ts.storage( table, 'tablesorter-groups', wo.group_currentGroups ); + } + }); + } + $(wo.group_saveReset).on('click', function(){ + ts.grouping.clearSavedGroups(table); + }); + c.$table.on('pagerChange.tsgrouping', function(){ + ts.grouping.update(table, c, wo); + }); + }, + + clearSavedGroups: function(table){ + if (table && ts.storage) { + ts.storage(table, 'tablesorter-groups', ''); + ts.grouping.update(table, table.config, table.config.widgetOptions); + } } }; @@ -135,6 +215,8 @@ ts.addWidget({ options: { group_collapsible : true, // make the group header clickable and collapse the rows below it. group_collapsed : false, // start with all groups collapsed + group_saveGroups : true, // remember collapsed groups + group_saveReset : null, // element to clear saved collapsed groups group_count : ' ({num})', // if not false, the "{num}" string is replaced with the number of rows in the group group_separator : '-', // group name separator; used when group-separator-# class is used. group_formatter : null, // function(txt, column, table, c, wo) { return txt; } @@ -151,23 +233,7 @@ ts.addWidget({ group_dateString : function(date) { return date.toLocaleString(); } }, init: function(table, thisWidget, c, wo){ - if (wo.group_collapsible) { - // .on() requires jQuery 1.7+ - c.$table.on('click toggleGroup', 'tr.group-header', function(event){ - event.stopPropagation(); - var $this = $(this); - // use shift-click to toggle ALL groups - if (event.type === 'click' && event.shiftKey) { - $this.siblings('.group-header').trigger('toggleGroup'); - } - $this.toggleClass('collapsed'); - // nextUntil requires jQuery 1.4+ - $this.nextUntil('tr.group-header').toggleClass('group-hidden', $this.hasClass('collapsed') ); - }); - } - c.$table.on('pagerChange', function(){ - ts.grouping.update(table, c, wo); - }); + ts.grouping.bindEvents(table, c, wo); }, format: function(table, c, wo) { ts.grouping.update(table, c, wo); @@ -175,6 +241,7 @@ ts.addWidget({ remove : function(table, c, wo){ c.$table .off('click', 'tr.group-header') + .off('pagerChange.tsgrouping') .find('.group-hidden').removeClass('group-hidden').end() .find('tr.group-header').remove(); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js new file mode 100644 index 0000000..d7f1b6d --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js @@ -0,0 +1,32 @@ +/*! tablesorter headerTitles widget - updated 3/5/2014 (core v2.15.6) + * Requires tablesorter v2.8+ and jQuery 1.7+ + * by Rob Garrison + */ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;(function($){ +"use strict"; + + $.tablesorter.addWidget({ + id: 'headerTitles', + options: { + headerTitle_prefix : 'Sort: ', + headerTitle_text : [ 'A - Z', 'Z - A' ], + headerTitle_numeric : [ '0 - 9', '9 - 0' ] + }, + format: function (table, c, wo) { + var txt; + // clear out all titles + c.$headers.attr('title', ''); + // only add titles to sorted columns + $.each(c.sortList, function(indx, group) { + txt = wo.headerTitle_prefix + wo['headerTitle_' + (c.parsers[ group[0] ].type || 'text')][ group[1] ]; + c.$headers.filter('[data-column="' + group[0] + '"]').attr('title', txt); + }); + }, + remove: function (table, c) { + c.$headers.attr('title', ''); + } + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index ebb7281..4730a3b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/* Pager widget (beta) for TableSorter 2/23/2014 (v2.15.5) */ +/* Pager widget (beta) for TableSorter 3/7/2014 (v2.15.6) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ "use strict"; @@ -150,7 +150,6 @@ tsp = ts.pager = { // page size selector p.$size = p.$container.find(s.pageSize); p.totalRows = c.$tbodies.eq(0).children().length; - p.oldAjaxSuccess = p.oldAjaxSuccess || wo.pager_ajaxObject.success; c.appender = tsp.appender; if (ts.filter && $.inArray('filter', c.widgets) >= 0) { @@ -315,7 +314,7 @@ tsp = ts.pager = { } }, - updatePageDisplay: function(table, c, flag) { + updatePageDisplay: function(table, c, completed) { var i, pg, s, out, wo = c.widgetOptions, p = c.pager, @@ -361,7 +360,7 @@ tsp = ts.pager = { } } tsp.pagerArrows(c); - if (p.initialized && flag !== false) { + if (p.initialized && completed !== false) { c.$table.trigger('pagerComplete', c); // save pager info to storage if (wo.pager_savePages && ts.storage) { @@ -453,6 +452,7 @@ tsp = ts.pager = { } ts.showError(table, exception.message + ' (' + xhr.status + ')'); c.$tbodies.eq(0).empty(); + p.totalRows = 0; } else { // process ajax object if (!$.isArray(result)) { @@ -463,7 +463,7 @@ tsp = ts.pager = { } else { // allow [ total, rows, headers ] or [ rows, total, headers ] t = isNaN(result[0]) && !isNaN(result[1]); - //ensure a zero returned row count doesn't fail the logical || + // ensure a zero returned row count doesn't fail the logical || rr_count = result[t ? 1 : 0]; p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count; d = p.totalRows === 0 ? [""] : result[t ? 0 : 1] || []; // row data @@ -521,7 +521,8 @@ tsp = ts.pager = { } // make sure last pager settings are saved, prevents multiple server side calls with // the same parameters - p.last.totalPages = p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) ); + p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) ); + p.last.totalRows = p.totalRows; p.last.currentFilters = p.currentFilters; p.last.sortList = (c.sortList || []).join(','); tsp.updatePageDisplay(table, c); @@ -650,6 +651,9 @@ tsp = ts.pager = { wo.pager_startPage = p.page; wo.pager_size = p.size; c.$table.trigger('applyWidgets'); + if (table.isUpdating) { + c.$table.trigger('updateComplete'); + } }, @@ -680,15 +684,19 @@ tsp = ts.pager = { }); }, - moveToPage: function(table, p, flag) { + moveToPage: function(table, p, pageMoved) { if ( p.isDisabled ) { return; } var c = table.config, l = p.last, pg = Math.min( p.totalPages, p.filteredPages ); if ( p.page < 0 ) { p.page = 0; } if ( p.page > ( pg - 1 ) && pg !== 0 ) { p.page = pg - 1; } - // don't allow rendering multiple times on the same page/size/totalpages/filters/sorts - if ( l.page === p.page && l.size === p.size && l.totalPages === p.totalPages && + // fixes issue where one current filter is [] and the other is ['','',''], + // making the next if comparison think the filters as different. Fixes #202. + l.currentFilters = (l.currentFilters || []).join('') === '' ? [] : l.currentFilters; + p.currentFilters = (p.currentFilters || []).join('') === '' ? [] : p.currentFilters; + // don't allow rendering multiple times on the same page/size/totalRows/filters/sorts + if ( l.page === p.page && l.size === p.size && l.totalRows === p.totalRows && (l.currentFilters || []).join(',') === (p.currentFilters || []).join(',') && l.sortList === (c.sortList || []).join(',') ) { return; @@ -701,7 +709,7 @@ tsp = ts.pager = { size : p.size, // fixes #408; modify sortList otherwise it auto-updates sortList : (c.sortList || []).join(','), - totalPages : p.totalPages, + totalRows : p.totalRows, currentFilters : p.currentFilters || [] }; if (p.ajax) { @@ -710,8 +718,11 @@ tsp = ts.pager = { tsp.renderTable(table, c.rowsCopy); } $.data(table, 'pagerLastPage', p.page); - if (p.initialized && flag !== false) { + if (p.initialized && pageMoved !== false) { c.$table.trigger('pageMoved', c); + if (!p.ajax && table.isUpdating) { + c.$table.trigger('updateComplete'); + } } }, @@ -800,6 +811,8 @@ tsp = ts.pager = { tsp.moveToPage(table, p, true); // update display here in case all rows are removed tsp.updatePageDisplay(table, c, false); + } else { + tsp.moveToPage(table, p, true); } } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index 631f168..e32e3ed 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -41,14 +41,14 @@ } /* since bootstrap (table-striped) uses nth-child(), we just use this to add a zebra stripe color */ -.tablesorter-bootstrap tr.odd td { +.tablesorter-bootstrap > tbody > tr.odd > td { background-color: #f9f9f9; } -.tablesorter-bootstrap tbody > .odd:hover > td, -.tablesorter-bootstrap tbody > .even:hover > td { +.tablesorter-bootstrap > tbody > tr.odd:hover > td, +.tablesorter-bootstrap > tbody > tr.even:hover > td { background-color: #f5f5f5; } -.tablesorter-bootstrap tr.even td { +.tablesorter-bootstrap > tbody > tr.even > td { background-color: #fff; } From 8f3c539138df38a221c399c1da37aef9bf1fdbef Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Tue, 11 Mar 2014 16:26:41 +0100 Subject: [PATCH 019/138] * updated tablesorter to latest version (2.15.7) --- CHANGELOG.md | 4 + README.md | 4 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery-tablesorter/jquery.tablesorter.js | 64 ++++++++------- ...ry.tablesorter.widgets-filter-formatter.js | 30 +++---- .../jquery.tablesorter.widgets.js | 14 ++-- .../widgets/widget-columnSelector.js | 56 ++++++++----- .../widgets/widget-headerTitles.js | 81 ++++++++++++++++--- 9 files changed, 170 insertions(+), 87 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fae01fd..70d12dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.10.5 (2014-03-11) + +* Upgrade tablesorter to v2.15.7 + #### v1.10.4 (2014-03-08) * Upgrade tablesorter to v2.15.6 diff --git a/README.md b/README.md index a696ae0..6958a3d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.15.6 (3/7/2014), [documentation] +Current tablesorter version: 2.15.7 (3/10/2014), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. @@ -26,7 +26,7 @@ Or install it yourself as: Rails 3.1 and higher (tested up to 4.1) -Tested with ruby 1.9.3 - 2.1.0 +Tested with ruby 1.9.3 - 2.1.1 ## Usage diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 58f4459..414b9c1 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = "1.10.4" + VERSION = "1.10.5" end diff --git a/tablesorter b/tablesorter index 35be668..261b009 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 35be6689fbef80d2381c709d108d91d1ac47e00e +Subproject commit 261b009af12bea8d7e873636659b11b98556a066 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index e11f9c6..7c2e480 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.15.6 - Client-side table sorting with ease! +* TableSorter 2.15.7 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.15.6"; + ts.version = "2.15.7"; ts.parsers = []; ts.widgets = []; @@ -783,15 +783,15 @@ $table = c.$table; // apply easy methods that trigger bound events $table - .unbind('sortReset update updateRows updateCell updateAll addRows updateComplete sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave '.split(' ').join('.tablesorter ')) - .bind("sortReset.tablesorter", function(e){ + .unbind('sortReset update updateRows updateCell updateAll addRows updateComplete sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave '.split(' ').join(c.namespace + ' ')) + .bind("sortReset" + c.namespace, function(e){ e.stopPropagation(); c.sortList = []; setHeadersCss(table); multisort(table); appendToTable(table); }) - .bind("updateAll.tablesorter", function(e, resort, callback){ + .bind("updateAll" + c.namespace, function(e, resort, callback){ e.stopPropagation(); table.isUpdating = true; ts.refreshWidgets(table, true, true); @@ -801,14 +801,14 @@ bindMethods(table); commonUpdate(table, resort, callback); }) - .bind("update.tablesorter updateRows.tablesorter", function(e, resort, callback) { + .bind("update" + c.namespace + " updateRows" + c.namespace, function(e, resort, callback) { e.stopPropagation(); table.isUpdating = true; // update sorting (if enabled/disabled) updateHeader(table); commonUpdate(table, resort, callback); }) - .bind("updateCell.tablesorter", function(e, cell, resort, callback) { + .bind("updateCell" + c.namespace, function(e, cell, resort, callback) { e.stopPropagation(); table.isUpdating = true; $table.find(c.selectorRemove).remove(); @@ -830,7 +830,7 @@ checkResort($table, resort, callback); } }) - .bind("addRows.tablesorter", function(e, $row, resort, callback) { + .bind("addRows" + c.namespace, function(e, $row, resort, callback) { e.stopPropagation(); table.isUpdating = true; if (isEmptyObject(c.cache)) { @@ -863,10 +863,10 @@ checkResort($table, resort, callback); } }) - .bind("updateComplete.tablesorter", function(){ + .bind("updateComplete" + c.namespace, function(){ table.isUpdating = false; }) - .bind("sorton.tablesorter", function(e, list, callback, init) { + .bind("sorton" + c.namespace, function(e, list, callback, init) { var c = table.config; e.stopPropagation(); $table.trigger("sortStart", this); @@ -885,14 +885,14 @@ callback(table); } }) - .bind("appendCache.tablesorter", function(e, callback, init) { + .bind("appendCache" + c.namespace, function(e, callback, init) { e.stopPropagation(); appendToTable(table, init); if (typeof callback === "function") { callback(table); } }) - .bind("updateCache.tablesorter", function(e, callback){ + .bind("updateCache" + c.namespace, function(e, callback){ // rebuild parsers if (!c.parsers) { buildParserCache(table); @@ -903,20 +903,20 @@ callback(table); } }) - .bind("applyWidgetId.tablesorter", function(e, id) { + .bind("applyWidgetId" + c.namespace, function(e, id) { e.stopPropagation(); ts.getWidgetById(id).format(table, c, c.widgetOptions); }) - .bind("applyWidgets.tablesorter", function(e, init) { + .bind("applyWidgets" + c.namespace, function(e, init) { e.stopPropagation(); // apply widgets ts.applyWidget(table, init); }) - .bind("refreshWidgets.tablesorter", function(e, all, dontapply){ + .bind("refreshWidgets" + c.namespace, function(e, all, dontapply){ e.stopPropagation(); ts.refreshWidgets(table, all, dontapply); }) - .bind("destroy.tablesorter", function(e, c, cb){ + .bind("destroy" + c.namespace, function(e, c, cb){ e.stopPropagation(); ts.destroy(table, c, cb); }); @@ -932,8 +932,9 @@ if (!table.hasInitialized && ts.buildTable && this.tagName !== 'TABLE') { // return the table (in case the original target is the table's container) ts.buildTable(table, c); + } else { + ts.setup(table, c); } - ts.setup(table, c); }); }; @@ -972,11 +973,19 @@ c.$table = $table .addClass(ts.css.table + ' ' + c.tableClass + k) .attr({ role : 'grid'}); + + // give the table a unique id, which will be used in namespace binding + if (!c.namespace) { + c.namespace = '.tablesorter' + Math.random().toString(16).slice(2); + } else { + // make sure namespace starts with a period & doesn't have weird characters + c.namespace = '.' + c.namespace.replace(/\W/g,''); + } + c.$tbodies = $table.children('tbody:not(.' + c.cssInfoBlock + ')').attr({ 'aria-live' : 'polite', 'aria-relevant' : 'all' }); - // if (c.$table.find('caption').length) { c.$table.attr('aria-labelledby', 'theCaption'); } @@ -1017,8 +1026,8 @@ // show processesing icon if (c.showProcessing) { $table - .unbind('sortBegin.tablesorter sortEnd.tablesorter') - .bind('sortBegin.tablesorter sortEnd.tablesorter', function(e) { + .unbind('sortBegin' + c.namespace + ' sortEnd' + c.namespace) + .bind('sortBegin' + c.namespace + ' sortEnd' + c.namespace, function(e) { ts.isProcessing(table, e.type === 'sortBegin'); }); } @@ -1084,8 +1093,8 @@ $headers // http://stackoverflow.com/questions/5312849/jquery-find-self; .find(c.selectorSort).add( $headers.filter(c.selectorSort) ) - .unbind('mousedown.tablesorter mouseup.tablesorter sort.tablesorter keyup.tablesorter') - .bind('mousedown.tablesorter mouseup.tablesorter sort.tablesorter keyup.tablesorter', function(e, external) { + .unbind('mousedown mouseup sort keyup '.split(' ').join(c.namespace + ' ')) + .bind('mousedown mouseup sort keyup '.split(' ').join(c.namespace + ' '), function(e, external) { var cell, type = e.type; // only recognize left clicks or enter if ( ((e.which || e.button) !== 1 && !/sort|keyup/.test(type)) || (type === 'keyup' && e.which !== 13) ) { @@ -1146,11 +1155,11 @@ // disable tablesorter $t .removeData('tablesorter') - .unbind('sortReset update updateAll updateRows updateCell addRows updateComplete sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress sortBegin sortEnd '.split(' ').join('.tablesorter ')); + .unbind('sortReset update updateAll updateRows updateCell addRows updateComplete sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress sortBegin sortEnd '.split(' ').join(c.namespace + ' ')); c.$headers.add($f) .removeClass( [ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone].join(' ') ) .removeAttr('data-column'); - $r.find(c.selectorSort).unbind('mousedown.tablesorter mouseup.tablesorter keypress.tablesorter'); + $r.find(c.selectorSort).unbind('mousedown mouseup keypress '.split(' ').join(c.namespace + ' ')); ts.restoreHeaders(table); if (removeClasses !== false) { $t.removeClass(ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme); @@ -1166,8 +1175,7 @@ // regex used in natural sort ts.regex = { chunk : /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi, // chunk/tokenize numbers & letters - schunk: /^\\0/, // start chunk - echunk: /\\0$/, // end chunk + chunks: /(^\\0|\\0$)/, // replace chunks @ ends hex: /^0x[0-9a-f]+$/i // hex }; @@ -1184,8 +1192,8 @@ if ( xD > yD ) { return 1; } } // chunk/tokenize - xN = a.replace(r.chunk, '\\0$1\\0').replace(r.echunk, '').replace(r.schunk, '').split('\\0'); - yN = b.replace(r.chunk, '\\0$1\\0').replace(r.echunk, '').replace(r.schunk, '').split('\\0'); + xN = a.replace(r.chunk, '\\0$1\\0').replace(r.chunks, '').split('\\0'); + yN = b.replace(r.chunk, '\\0$1\\0').replace(r.chunks, '').split('\\0'); mx = Math.max(xN.length, yN.length); // natural sorting through split numeric strings and default strings for (i = 0; i < mx; i++) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js index ba5ef58..0fb68f9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js @@ -1,4 +1,4 @@ -/*! Filter widget formatter functions - updated 2/19/2014 (v2.15) +/*! Filter widget formatter functions - updated 3/9/2014 (v2.15.7) * requires: tableSorter 2.15+ and jQuery 1.4.3+ * * uiSpinner (jQuery UI spinner) @@ -73,8 +73,8 @@ tsff = ts.filterFormatter = { // Add a hidden input to hold the range values $input = $('<input class="filter" type="hidden">') .appendTo($cell) - // hidden filter update (.tsfilter) namespace trigger by filter widget - .bind('change.tsfilter', function(){ + // hidden filter update namespace trigger by filter widget + .bind('change' + c.namespace + 'filter', function(){ updateSpinner({ value: this.value, delayed: false }); }), $shcell = [], @@ -230,8 +230,8 @@ tsff = ts.filterFormatter = { // Add a hidden input to hold the range values $input = $('<input class="filter" type="hidden">') .appendTo($cell) - // hidden filter update (.tsfilter) namespace trigger by filter widget - .bind('change.tsfilter', function(){ + // hidden filter update namespace trigger by filter widget + .bind('change' + c.namespace + 'filter', function(){ updateSlider({ value: this.value }); }), $shcell = [], @@ -368,8 +368,8 @@ tsff = ts.filterFormatter = { // Add a hidden input to hold the range values $input = $('<input class="filter" type="hidden">') .appendTo($cell) - // hidden filter update (.tsfilter) namespace trigger by filter widget - .bind('change.tsfilter', function(){ + // hidden filter update namespace trigger by filter widget + .bind('change' + c.namespace + 'filter', function(){ getRange(); }), $shcell = [], @@ -500,8 +500,8 @@ tsff = ts.filterFormatter = { // Add a hidden input to hold the range values $input = $('<input class="dateCompare" type="hidden">') .appendTo($cell) - // hidden filter update (.tsfilter) namespace trigger by filter widget - .bind('change.tsfilter', function(){ + // hidden filter update namespace trigger by filter widget + .bind('change' + c.namespace + 'filter', function(){ var v = this.value; if (v) { o.onClose(v); @@ -632,8 +632,8 @@ tsff = ts.filterFormatter = { // Add a hidden input to hold the range values $input = $('<input class="dateRange" type="hidden">') .appendTo($cell) - // hidden filter update (.tsfilter) namespace trigger by filter widget - .bind('change.tsfilter', function(){ + // hidden filter update namespace trigger by filter widget + .bind('change' + c.namespace + 'filter', function(){ var v = this.value; if (v.match(' - ')) { v = v.split(' - '); @@ -940,8 +940,8 @@ tsff = ts.filterFormatter = { .addClass('filter-parsed') // get exact numbers from column // add span to header for the current slider value .find('.tablesorter-header-inner').append('<span class="curvalue" />'); - // hidden filter update (.tsfilter) namespace trigger by filter widget - $input = $cell.find('input[type=hidden]').bind('change.tsfilter', function(){ + // hidden filter update namespace trigger by filter widget + $input = $cell.find('input[type=hidden]').bind('change' + c.namespace + 'filter', function(){ /*jshint eqeqeq:false */ var v = this.value, compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || ''; @@ -1085,8 +1085,8 @@ tsff = ts.filterFormatter = { updateColor( $cell.find('.colorpicker').val() ); }); - // hidden filter update (.tsfilter) namespace trigger by filter widget - $input = $cell.find('input[type=hidden]').bind('change.tsfilter', function(){ + // hidden filter update namespace trigger by filter widget + $input = $cell.find('input[type=hidden]').bind('change' + c.namespace + 'filter', function(){ updateColor( this.value ); }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 71bea63..30113df 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.8+ widgets - updated 3/7/2014 (v2.15.6) +/*! tableSorter 2.15+ widgets - updated 3/9/2014 (v2.15.7) * * Column Styles * Column Filters @@ -125,7 +125,7 @@ ts.storage = function(table, key, value, options) { document.cookie = key + '=' + (JSON.stringify(values)).replace(/\"/g,'\"') + '; expires=' + date.toGMTString() + '; path=/'; } } else { - return values && values[url] ? values[url][id] : {}; + return values && values[url] ? values[url][id] : ''; } }; @@ -380,7 +380,7 @@ ts.addWidget({ $table .removeClass('hasFilters') // add .tsfilter namespace to all BUT search - .unbind('addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join('.tsfilter ')) + .unbind('addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter ')) .find('.' + ts.css.filterRow).remove(); for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody @@ -558,7 +558,7 @@ ts.filter = { ts.filter.buildRow(table, c, wo); } - c.$table.bind('addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join('.tsfilter '), function(event, filter) { + c.$table.bind('addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter '), function(event, filter) { c.$table.find('.' + ts.css.filterRow).toggle( !(wo.filter_hideEmpty && $.isEmptyObject(c.cache)) ); // fixes #450 if ( !/(search|filter)/.test(event.type) ) { event.stopPropagation(); @@ -628,7 +628,7 @@ ts.filter = { // show processing icon if (c.showProcessing) { - c.$table.bind('filterStart.tsfilter filterEnd.tsfilter', function(event, columns) { + c.$table.bind('filterStart' + c.namespace + 'filter filterEnd' + c.namespace + 'filter', function(event, columns) { // only add processing to certain columns to all columns $header = (columns) ? c.$table.find('.' + ts.css.header).filter('[data-column]').filter(function() { return columns[$(this).data('column')] !== ''; @@ -751,9 +751,9 @@ ts.filter = { $el // use data attribute instead of jQuery data since the head is cloned without including the data/binding .attr('data-lastSearchTime', new Date().getTime()) - .unbind('keyup search change') + .unbind('keyup search change '.split(' ').join(c.namespace + 'filter ')) // include change for select - fixes #473 - .bind('keyup search change', function(event, filters) { + .bind('keyup search change '.split(' ').join(c.namespace + 'filter '), function(event, filters) { $(this).attr('data-lastSearchTime', new Date().getTime()); // emulate what webkit does.... escape clears the filter if (event.which === 27) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index 38afa54..3718721 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -32,14 +32,18 @@ tsColSel = ts.columnSelector = { // build column selector/state array colSel = c.selector = { $container : $(wo.columnSelector_container || '<div>') }; + colSel.$style = $('<style></style>').prop('disabled', true).appendTo('head'); + colSel.$breakpoints = $('<style></style>').prop('disabled', true).appendTo('head'); + + colSel.isInitializing = true; tsColSel.setupSelector(table, c, wo); if (wo.columnSelector_mediaquery) { tsColSel.setupBreakpoints(c, wo); } + colSel.isInitializing = false; if (colSel.$container.length) { - colSel.$style = $('<style></style>').prop('disabled', true).appendTo('head'); tsColSel.updateCols(c, wo); } @@ -49,10 +53,13 @@ tsColSel = ts.columnSelector = { var name, colSel = c.selector, $container = colSel.$container, + useStorage = wo.columnSelector_saveColumns && ts.storage, // get stored column states - saved = wo.columnSelector_saveColumns && ts.storage ? ts.storage( table, 'tablesorter-columnSelector' ) : []; + saved = useStorage ? ts.storage( table, 'tablesorter-columnSelector' ) : [], + state = useStorage ? ts.storage( table, 'tablesorter-columnSelector-auto') : {}; // initial states + colSel.auto = $.isEmptyObject(state) || $.type(state.auto) !== "boolean" ? wo.columnSelector_mediaqueryState : state.auto; colSel.states = []; colSel.$column = []; colSel.$wrapper = []; @@ -99,14 +106,13 @@ tsColSel = ts.columnSelector = { }, setupBreakpoints: function(c, wo){ - var $auto, colSel = c.selector; + var colSel = c.selector; // add responsive breakpoints if (wo.columnSelector_mediaquery) { // used by window resize function colSel.lastIndex = -1; wo.columnSelector_breakpoints.sort(); - colSel.$breakpoints = $('<style></style>').prop('disabled', true).appendTo('head'); tsColSel.updateBreakpoints(c, wo); c.$table.unbind('updateAll' + namespace).bind('updateAll' + namespace, function(){ tsColSel.updateBreakpoints(c, wo); @@ -116,22 +122,24 @@ tsColSel = ts.columnSelector = { if (colSel.$container.length) { // Add media queries toggle - if (wo.columnSelector_mediaquery && wo.columnSelector_mediaquery) { - $auto = $( wo.columnSelector_layout.replace(/\{name\}/g, wo.columnSelector_mediaqueryName) ).prependTo(colSel.$container); - $auto + if (wo.columnSelector_mediaquery) { + colSel.$auto = $( wo.columnSelector_layout.replace(/\{name\}/g, wo.columnSelector_mediaqueryName) ).prependTo(colSel.$container); + colSel.$auto // needed in case the input in the layout is not wrapped - .find('input').add( $auto.filter('input') ) + .find('input').add( colSel.$auto.filter('input') ) .attr('data-column', 'auto') - .prop('checked', wo.columnSelector_mediaqueryState) + .prop('checked', colSel.auto) .bind('change', function(){ - wo.columnSelector_mediaqueryState = this.checked; + colSel.auto = this.checked; $.each( colSel.$checkbox, function(i, $cb){ if ($cb) { - $cb[0].disabled = wo.columnSelector_mediaqueryState; - colSel.$wrapper[i].toggleClass('disabled', wo.columnSelector_mediaqueryState); + $cb[0].disabled = colSel.auto; + colSel.$wrapper[i].toggleClass('disabled', colSel.auto); } }); - tsColSel.updateBreakpoints(c, wo); + if (wo.columnSelector_mediaquery) { + tsColSel.updateBreakpoints(c, wo); + } tsColSel.updateCols(c, wo); // copy the column selector to a popup/tooltip if (c.selector.$popup) { @@ -139,9 +147,12 @@ tsColSel = ts.columnSelector = { .html( colSel.$container.html() ) .find('input').each(function(){ var indx = $(this).attr('data-column'); - $(this).prop( 'checked', indx === 'auto' ? wo.columnSelector_mediaqueryState : colSel.states[indx] ); + $(this).prop( 'checked', indx === 'auto' ? colSel.auto : colSel.states[indx] ); }); } + if (wo.columnSelector_saveColumns && ts.storage) { + ts.storage( c.$table[0], 'tablesorter-columnSelector-auto', { auto : colSel.auto } ); + } }).change(); } // Add a bind on update to re-run col setup @@ -157,7 +168,7 @@ tsColSel = ts.columnSelector = { prefix = '.' + c.tableId, mediaAll = [], breakpts = ''; - if (wo.columnSelector_mediaquery && !wo.columnSelector_mediaqueryState) { + if (wo.columnSelector_mediaquery && !colSel.auto) { colSel.$breakpoints.prop('disabled', true); colSel.$style.prop('disabled', false); return; @@ -188,13 +199,14 @@ tsColSel = ts.columnSelector = { }, updateCols: function(c, wo) { - if (wo.columnSelector_mediaquery && wo.columnSelector_mediaqueryState) { + if (wo.columnSelector_mediaquery && c.selector.auto || c.selector.isInitializing) { return; } var column, + colSel = c.selector, styles = [], prefix = '.' + c.tableId; - c.selector.$container.find('input[data-column]').filter('[data-column!="auto"]').each(function(){ + colSel.$container.find('input[data-column]').filter('[data-column!="auto"]').each(function(){ if (!this.checked) { column = parseInt( $(this).attr('data-column'), 10 ) + 1; styles.push(prefix + ' tr th:nth-child(' + column + ')'); @@ -202,13 +214,13 @@ tsColSel = ts.columnSelector = { } }); if (wo.columnSelector_mediaquery){ - c.selector.$breakpoints.prop('disabled', true); + colSel.$breakpoints.prop('disabled', true); } - if (c.selector.$style) { - c.selector.$style.prop('disabled', false).html( styles.length ? styles.join(',') + ' { display: none; }' : '' ); + if (colSel.$style) { + colSel.$style.prop('disabled', false).html( styles.length ? styles.join(',') + ' { display: none; }' : '' ); } if (wo.columnSelector_saveColumns && ts.storage) { - ts.storage( c.$table[0], 'tablesorter-columnSelector', c.selector.states ); + ts.storage( c.$table[0], 'tablesorter-columnSelector', colSel.states ); } }, @@ -228,7 +240,7 @@ tsColSel = ts.columnSelector = { .html( colSel.$container.html() ) .find('input').each(function(){ var indx = $(this).attr('data-column'); - $(this).prop( 'checked', indx === 'auto' ? wo.columnSelector_mediaqueryState : colSel.states[indx] ); + $(this).prop( 'checked', indx === 'auto' ? colSel.auto : colSel.states[indx] ); }); colSel.$popup = $popup.on('change', 'input', function(){ // data input diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js index d7f1b6d..39d2970 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js @@ -6,26 +6,85 @@ /*global jQuery: false */ ;(function($){ "use strict"; +var ts = $.tablesorter; - $.tablesorter.addWidget({ + ts.addWidget({ id: 'headerTitles', options: { - headerTitle_prefix : 'Sort: ', - headerTitle_text : [ 'A - Z', 'Z - A' ], - headerTitle_numeric : [ '0 - 9', '9 - 0' ] + // use aria-label text + // e.g. "First Name: Ascending sort applied, activate to apply a descending sort" + headerTitle_useAria : false, + // add tooltip class + headerTitle_tooltip : '', + // custom titles [ ascending, descending, unsorted ] + headerTitle_cur_text : [ ' sort: A - Z', ' sort: Z - A', 'ly unsorted' ], + headerTitle_cur_numeric : [ ' sort: 0 - 9', ' sort: 9 - 0', 'ly unsorted' ], + headerTitle_nxt_text : [ ' sort: A - Z', ' sort: Z - A', 'remove sort' ], + headerTitle_nxt_numeric : [ ' sort: 0 - 9', ' sort: 9 - 0', 'remove sort' ], + + // title display; {prefix} adds above prefix + // {type} adds the current sort order from above (text or numeric) + // {next} adds the next sort direction using the sort order above + headerTitle_output_sorted : 'current{current}; activate to {next}', + headerTitle_output_unsorted : 'current{current}; activate to {next} ', + headerTitle_output_nosort : 'No sort available', + // use this type to override the parser detection result + // e.g. use for numerically parsed columns (e.g. dates), but you + // want the user to see a text sort, e.g. [ 'text', 'numeric' ] + headerTitle_type : [], + // manipulate the title as desired + headerTitle_callback : null // function($cell, txt) { return txt; } + }, + init: function(table, thisWidget, c, wo){ + // force refresh + c.$table.on('refreshHeaderTitle', function(){ + thisWidget.format(table, c, wo); + }); + // add tooltip class + if ($.isArray(wo.headerTitle_tooltip)) { + c.$headers.each(function(){ + $(this).addClass( wo.headerTitle_tooltip[this.column] || '' ); + }); + } else if (wo.headerTitle_tooltip !== '') { + c.$headers.addClass( wo.headerTitle_tooltip ); + } }, format: function (table, c, wo) { var txt; - // clear out all titles - c.$headers.attr('title', ''); - // only add titles to sorted columns - $.each(c.sortList, function(indx, group) { - txt = wo.headerTitle_prefix + wo['headerTitle_' + (c.parsers[ group[0] ].type || 'text')][ group[1] ]; - c.$headers.filter('[data-column="' + group[0] + '"]').attr('title', txt); + c.$headers.each(function(){ + var t = this, + $this = $(this), + sortType = wo.headerTitle_type[t.column] || c.parsers[ t.column ].type || 'text', + sortDirection = $this.hasClass(ts.css.sortAsc) ? 0 : $this.hasClass(ts.css.sortDesc) ? 1 : 2, + sortNext = t.order[(t.count + 1) % (c.sortReset ? 3 : 2)]; + if (wo.headerTitle_useAria) { + txt = $this.hasClass('sorter-false') ? wo.headerTitle_output_nosort : $this.attr('aria-label') || ''; + } else { + txt = (wo.headerTitle_prefix || '') + // now deprecated + ($this.hasClass('sorter-false') ? wo.headerTitle_output_nosort : + ts.isValueInArray( t.column, c.sortList ) >= 0 ? wo.headerTitle_output_sorted : wo.headerTitle_output_unsorted); + txt = txt.replace(/\{(current|next|name)\}/gi, function(m){ + return { + '{name}' : $this.text(), + '{current}' : wo[ 'headerTitle_cur_' + sortType ][ sortDirection ] || '', + '{next}' : wo[ 'headerTitle_nxt_' + sortType ][ sortNext ] || '' + }[m.toLowerCase()]; + }); + } + $this.attr('title', $.isFunction(wo.headerTitle_callback) ? wo.headerTitle_callback($this, txt) : txt); }); }, - remove: function (table, c) { + remove: function (table, c, wo) { c.$headers.attr('title', ''); + c.$table.off('refreshHeaderTitle'); + // remove tooltip class + if ($.isArray(wo.headerTitle_tooltip)) { + c.$headers.each(function(){ + $(this).removeClass( wo.headerTitle_tooltip[this.column] || '' ); + }); + } else if (wo.headerTitle_tooltip !== '') { + c.$headers.removeClass( wo.headerTitle_tooltip ); + } } }); From 48ce336106bceeb590f43a4384c868d1a4aa95c3 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Fri, 14 Mar 2014 20:59:05 +0100 Subject: [PATCH 020/138] * updated tablesorter to latest version (2.15.10) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery-tablesorter/jquery.tablesorter.js | 6 +- ...ry.tablesorter.widgets-filter-formatter.js | 14 +- .../jquery.tablesorter.widgets.js | 4 +- .../widgets/widget-alignChar.js | 145 ++++++++++++++++++ 8 files changed, 164 insertions(+), 15 deletions(-) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 70d12dd..5915108 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.10.6 (2014-03-11) + +* Upgrade tablesorter to v2.15.10 + #### v1.10.5 (2014-03-11) * Upgrade tablesorter to v2.15.7 diff --git a/README.md b/README.md index 6958a3d..1093dc5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.15.7 (3/10/2014), [documentation] +Current tablesorter version: 2.15.10 (3/13/2014), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 414b9c1..2781e02 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = "1.10.5" + VERSION = '1.10.6' end diff --git a/tablesorter b/tablesorter index 261b009..694357c 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 261b009af12bea8d7e873636659b11b98556a066 +Subproject commit 694357cd2e6839857547bb7d75171cc1d03d5a60 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 7c2e480..e4bc2b8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.15.7 - Client-side table sorting with ease! +* TableSorter 2.15.10 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.15.7"; + ts.version = "2.15.10"; ts.parsers = []; ts.widgets = []; @@ -729,7 +729,7 @@ } // fall back to built-in numeric sort // var sort = $.tablesorter["sort" + s](table, a[c], b[c], c, colMax[c], dir); - sort = c.numberSorter ? c.numberSorter(x[col], y[col], dir, colMax[col], table) : + sort = c.numberSorter ? c.numberSorter(a[col], b[col], dir, colMax[col], table) : ts[ 'sortNumeric' + (dir ? 'Asc' : 'Desc') ](a[col], b[col], num, colMax[col], col, table); } else { // set a & b depending on sort direction diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js index 0fb68f9..42408ca 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js @@ -1,4 +1,4 @@ -/*! Filter widget formatter functions - updated 3/9/2014 (v2.15.7) +/*! Filter widget formatter functions - updated 3/12/2014 (v2.15.9) * requires: tableSorter 2.15+ and jQuery 1.4.3+ * * uiSpinner (jQuery UI spinner) @@ -70,6 +70,7 @@ tsff = ts.filterFormatter = { disabled : false }, spinnerDef ), + c = $cell.closest('table')[0].config, // Add a hidden input to hold the range values $input = $('<input class="filter" type="hidden">') .appendTo($cell) @@ -78,7 +79,6 @@ tsff = ts.filterFormatter = { updateSpinner({ value: this.value, delayed: false }); }), $shcell = [], - c = $cell.closest('table')[0].config, // this function updates the hidden input and adds the current values to the header cell text updateSpinner = function(ui, notrigger) { @@ -227,6 +227,7 @@ tsff = ts.filterFormatter = { step : 1, range : "min" }, sliderDef ), + c = $cell.closest('table')[0].config, // Add a hidden input to hold the range values $input = $('<input class="filter" type="hidden">') .appendTo($cell) @@ -235,7 +236,6 @@ tsff = ts.filterFormatter = { updateSlider({ value: this.value }); }), $shcell = [], - c = $cell.closest('table')[0].config, // this function updates the hidden input and adds the current values to the header cell text updateSlider = function(ui, notrigger) { @@ -365,6 +365,7 @@ tsff = ts.filterFormatter = { max : 100, range : true }, rangeDef ), + c = $cell.closest('table')[0].config, // Add a hidden input to hold the range values $input = $('<input class="filter" type="hidden">') .appendTo($cell) @@ -373,7 +374,6 @@ tsff = ts.filterFormatter = { getRange(); }), $shcell = [], - c = $cell.closest('table')[0].config, getRange = function(){ var val = $input.val(), @@ -495,6 +495,7 @@ tsff = ts.filterFormatter = { }, defDate), $date, + c = $cell.closest('table')[0].config, // make sure we're using parsed dates in the search $hdr = $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed'), // Add a hidden input to hold the range values @@ -508,7 +509,6 @@ tsff = ts.filterFormatter = { } }), t, $shcell = [], - c = $cell.closest('table')[0].config, // this function updates the hidden input date1Compare = function(v, notrigger) { @@ -629,6 +629,7 @@ tsff = ts.filterFormatter = { numberOfMonths : 1 }, defDate), t, closeTo, closeFrom, $shcell = [], + c = $cell.closest('table')[0].config, // Add a hidden input to hold the range values $input = $('<input class="dateRange" type="hidden">') .appendTo($cell) @@ -644,8 +645,7 @@ tsff = ts.filterFormatter = { } else if (v.match('<=')) { closeTo( v.replace('<=', '') ); } - }), - c = $cell.closest('table')[0].config; + }); // make sure we're using parsed dates in the search $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed'); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 30113df..6d6b15e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.15+ widgets - updated 3/9/2014 (v2.15.7) +/*! tableSorter 2.15+ widgets - updated 3/12/2014 (v2.15.8) * * Column Styles * Column Filters @@ -765,7 +765,7 @@ ts.filter = { return; } // true flag tells getFilters to skip newest timed input - ts.filter.searching( table, '', true ); + ts.filter.searching( table, true, true ); }); c.$table.bind('filterReset', function(){ $el.val(''); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js new file mode 100644 index 0000000..982db1b --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js @@ -0,0 +1,145 @@ +/*! tablesorter Align Character widget - updated 3/12/2014 (core v2.15.8) + * Requires tablesorter v2.8+ and jQuery 1.7+ + * by Rob Garrison + */ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;(function($){ +"use strict"; +var ts = $.tablesorter; + +ts.alignChar = { + + init : function(table, c, wo) { + c.$headers.filter('[' + wo.alignChar_charAttrib + ']').each(function(){ + var $this = $(this), + vars = { + column : this.column, + align : $this.attr(wo.alignChar_charAttrib), + alignIndex : parseInt( $this.attr(wo.alignChar_indexAttrib) || 0, 10), + adjust : parseFloat($this.attr(wo.alignChar_adjustAttrib)) || 0, + }; + vars.regex = new RegExp('\\' + vars.align, 'g'); + if (typeof vars.align !== 'undefined') { + wo.alignChar_savedVars[this.column] = vars; + ts.alignChar.setup(table, c, wo, vars); + } + }); + }, + + setup: function(table, c, wo, v){ + // do nothing for empty tables + if ($.isEmptyObject(c.cache)) { return; } + var tbodyIndex, rowIndex, start, end, last, index, rows, val, count, + len, wLeft, wRight, alignChar, $row, + left = [], + right = []; + for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++){ + rows = c.cache[tbodyIndex]; + len = rows.normalized.length; + for (rowIndex = 0; rowIndex < len; rowIndex++) { + // set up to work with modified cache v2.16.0+ + $row = rows.row ? rows.row[rowIndex] : rows.normalized[rowIndex][c.columns].$row; + val = $row.find('td').eq(v.column).text().replace(/[ ]/g, "\u00a0"); + // count how many "align" characters are in the string + count = (val.match( v.regex ) || []).length; + // set alignment @ alignIndex (one-based index) + if (count > 0 && v.alignIndex > 0) { + end = Math.min(v.alignIndex, count); + start = 0; + index = 0; + last = 0; + // find index of nth align character based on alignIndex (data-align-index) + while (start++ < end) { + last = val.indexOf(v.align, last + 1); + index = last < 0 ? index : last; + } + } else { + index = val.indexOf(v.align); + } + if ( index >= 0 ) { + left.push( val.substring(0, index) || '' ); + right.push( val.substring(index, val.length) || '' ); + } else { + // no align character found! + // put val in right or left based on the align index + left.push( (count >= 1 && v.alignIndex >= count) ? '' : val || '' ); + right.push( (count >= 1 && v.alignIndex >= count) ? val || '' : '' ); + } + } + } + + // find widest segments + wLeft = ($.extend([], left)).sort(function(a,b){ return b.length - a.length; })[0]; + wRight = ($.extend([], right)).sort(function(a,b){ return b.length - a.length; })[0]; + // calculate percentage widths + v.width = v.width || ( Math.floor(wLeft.length / (wLeft.length + wRight.length) * 100) + v.adjust ); + wLeft = 'min-width:' + v.width + '%'; + wRight = 'min-width:' + (100 - v.width) + '%'; + + for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++){ + rows = c.cache[tbodyIndex]; + len = rows.normalized.length; + for (rowIndex = 0; rowIndex < len; rowIndex++) { + alignChar = $(wo.alignChar_wrap).length ? $(wo.alignChar_wrap).html(v.align)[0].outerHTML : v.align; + $row = rows.row ? rows.row[rowIndex] : rows.normalized[rowIndex][c.columns].$row; + $row.find('td').eq(v.column).html( + '<span class="ts-align-wrap"><span class="ts-align-left" style="' + wLeft + '">' + left[rowIndex] + '</span>' + + '<span class="ts-align-right" style="' + wRight + '">' + alignChar + + right[rowIndex].slice(v.align.length) + '</span></span>' + ); + } + } + wo.alignChar_initialized = true; + + }, + remove: function(table, c, column){ + if ($.isEmptyObject(c.cache)) { return; } + var tbodyIndex, rowIndex, len, rows, $row, $cell; + for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++){ + rows = c.cache[tbodyIndex]; + len = rows.normalized.length; + for (rowIndex = 0; rowIndex < len; rowIndex++) { + $row = rows.row ? rows.row[rowIndex] : rows.normalized[rowIndex][c.columns].$row; + $cell = $row.find('td').eq(column); + $cell.html( $cell.text().replace(/\s/g, ' ') ); + } + } + } +}; + +ts.addWidget({ + id: 'alignChar', + priority: 100, + options: { + alignChar_wrap : '', + alignChar_charAttrib : 'data-align-char', + alignChar_indexAttrib : 'data-align-index', + alignChar_adjustAttrib : 'data-align-adjust' // percentage width adjustments + }, + init: function(table, thisWidget, c, wo){ + wo.alignChar_initialized = false; + wo.alignChar_savedVars = []; + ts.alignChar.init(table, c, wo); + c.$table.on('pagerEnd refreshAlign', function(){ + c.$headers.filter('[' + wo.alignChar_charAttrib + ']').each(function(){ + ts.alignChar.remove(table, c, this.column); + }); + ts.alignChar.init(table, c, wo); + }); + }, + format : function(table, c, wo){ + // reinitialize in case table is empty when first initialized + if (!wo.alignChar_initialized) { + c.$table.trigger('refreshAlign'); + } + }, + remove : function(table, c, wo){ + c.$headers.filter('[' + wo.alignChar_charAttrib + ']').each(function(){ + ts.alignChar.remove(table, c, this.column); + }); + wo.alignChar_initialized = false; + } +}); + +})(jQuery); \ No newline at end of file From 06709f18eed4bb017b6c975fb3c51141be02db72 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Wed, 19 Mar 2014 02:10:49 +0100 Subject: [PATCH 021/138] * updated tablesorter to latest version (2.15.11) --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 3 ++- .../jquery-tablesorter/jquery.tablesorter.js | 12 +++++++++--- .../jquery-tablesorter/jquery.tablesorter.widgets.js | 4 ++-- .../jquery-tablesorter/widgets/widget-pager.js | 3 ++- 8 files changed, 22 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5915108..4ba70b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.10.7 (2014-03-11) + +* Upgrade tablesorter to v2.15.11 + #### v1.10.6 (2014-03-11) * Upgrade tablesorter to v2.15.10 diff --git a/README.md b/README.md index 1093dc5..2ddcf73 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.15.10 (3/13/2014), [documentation] +Current tablesorter version: 2.15.11 (3/18/2014), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 2781e02..8d06c9d 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.10.6' + VERSION = '1.10.7' end diff --git a/tablesorter b/tablesorter index 694357c..fdae3bf 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 694357cd2e6839857547bb7d75171cc1d03d5a60 +Subproject commit fdae3bfcd57006d62f5b46f2ef96f8416c50c1b4 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 2ed9373..4706da4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 3/7/2014 (v2.15.6) + * updated 3/18/2014 (v2.15.11) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -542,6 +542,7 @@ $.data(table, 'pagerLastPage', p.page); $.data(table, 'pagerLastSize', p.size); p.totalPages = Math.ceil( p.totalRows / p.size ); + p.filteredPages = Math.ceil( p.filteredRows / p.size ); moveToPage(table, p); }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index e4bc2b8..817bd93 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.15.10 - Client-side table sorting with ease! +* TableSorter 2.15.11 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.15.10"; + ts.version = "2.15.11"; ts.parsers = []; ts.widgets = []; @@ -296,6 +296,12 @@ } tc.cache[k].row.push(c); for (j = 0; j < totalCells; ++j) { + if (typeof parsers[j] === 'undefined') { + if (tc.debug) { + log('No parser found for cell:', c[0].cells[j], 'does it have a header?'); + } + continue; + } t = getElementText(table, c[0].cells[j], j); // allow parsing if the string is empty, previously parsing would change it to zero, // in case the parser needs to extract data from the table cell attributes @@ -381,7 +387,7 @@ var matrix = [], lookup = {}, cols = 0, // determine the number of columns - trs = $(t).find('thead:eq(0), tfoot').children('tr'), // children tr in tfoot - see issue #196 + trs = $(t).children('thead, tfoot').children('tr'), // children tr in tfoot - see issue #196 & #547 i, j, k, l, c, cells, rowIndex, cellId, rowSpan, colSpan, firstAvailCol, matrixrow; for (i = 0; i < trs.length; i++) { cells = trs[i].cells; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 6d6b15e..8695a3d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.15+ widgets - updated 3/12/2014 (v2.15.8) +/*! tableSorter 2.15+ widgets - updated 3/18/2014 (v2.15.11) * * Column Styles * Column Filters @@ -680,7 +680,7 @@ ts.filter = { for (column = 0; column < columns; column++) { buildFilter += '<td></td>'; } - c.$filters = $(buildFilter += '</tr>').appendTo( c.$table.find('thead').eq(0) ).find('td'); + c.$filters = $(buildFilter += '</tr>').appendTo( c.$table.children('thead').eq(0) ).find('td'); // build each filter input for (column = 0; column < columns; column++) { disabled = false; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 4730a3b..72b7a5d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/* Pager widget (beta) for TableSorter 3/7/2014 (v2.15.6) */ +/* Pager widget (beta) for TableSorter 3/18/2014 (v2.15.11) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ "use strict"; @@ -733,6 +733,7 @@ tsp = ts.pager = { $.data(table, 'pagerLastPage', p.page); $.data(table, 'pagerLastSize', p.size); p.totalPages = Math.ceil( p.totalRows / p.size ); + p.filteredPages = Math.ceil( p.filteredRows / p.size ); tsp.moveToPage(table, p); }, From 785e40ec6eeac01e6c363268e63d01ae678dff6e Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Thu, 27 Mar 2014 21:26:01 +0100 Subject: [PATCH 022/138] * remove all vendor files before updating from tablesorter repository ** removed unused images --- CHANGELOG.md | 5 +++++ Rakefile | 9 ++++++++- .../images/jquery-tablesorter/dropbox-asc1.png | Bin 277 -> 0 bytes .../images/jquery-tablesorter/dropbox-asc2.png | Bin 273 -> 0 bytes .../images/jquery-tablesorter/dropbox-desc1.png | Bin 278 -> 0 bytes .../images/jquery-tablesorter/dropbox-desc2.png | Bin 275 -> 0 bytes .../images/jquery-tablesorter/green-asc.png | Bin 3229 -> 0 bytes .../images/jquery-tablesorter/green-desc.png | Bin 3212 -> 0 bytes .../images/jquery-tablesorter/green-header.png | Bin 3247 -> 0 bytes .../images/jquery-tablesorter/green-unsorted.png | Bin 3513 -> 0 bytes 10 files changed, 13 insertions(+), 1 deletion(-) delete mode 100644 vendor/assets/images/jquery-tablesorter/dropbox-asc1.png delete mode 100644 vendor/assets/images/jquery-tablesorter/dropbox-asc2.png delete mode 100644 vendor/assets/images/jquery-tablesorter/dropbox-desc1.png delete mode 100644 vendor/assets/images/jquery-tablesorter/dropbox-desc2.png delete mode 100644 vendor/assets/images/jquery-tablesorter/green-asc.png delete mode 100644 vendor/assets/images/jquery-tablesorter/green-desc.png delete mode 100644 vendor/assets/images/jquery-tablesorter/green-header.png delete mode 100644 vendor/assets/images/jquery-tablesorter/green-unsorted.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ba70b6..273e226 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ Changelog === +#### v1.x.x (not released yet) + +* Remove all vendor files before updating from tablesorter repository +** Removed unused images + #### v1.10.7 (2014-03-11) * Upgrade tablesorter to v2.15.11 diff --git a/Rakefile b/Rakefile index 1f22924..9c16f50 100755 --- a/Rakefile +++ b/Rakefile @@ -12,6 +12,7 @@ namespace :jquery_tablesorter do # javascript_dir = File.join('vendor', 'assets', 'javascripts', 'jquery-tablesorter') FileUtils.mkdir_p(javascript_dir) + FileUtils.rm_rf("#{javascript_dir}/.", :secure => true) Dir.glob(File.join('tablesorter', 'js', '*.js')).reject{|file| file =~ /.min.js\Z/}.each do |file| FileUtils.cp file, javascript_dir, :verbose => true end @@ -20,6 +21,7 @@ namespace :jquery_tablesorter do # stylesheet_dir = File.join('vendor', 'assets', 'stylesheets', 'jquery-tablesorter') FileUtils.mkdir_p(stylesheet_dir) + FileUtils.rm_rf("#{stylesheet_dir}/.", :secure => true) Dir.glob(File.join('tablesorter', 'css', '*.css')).each do |file| FileUtils.cp file, stylesheet_dir, :verbose => true end @@ -28,6 +30,7 @@ namespace :jquery_tablesorter do # images_dir = File.join('vendor', 'assets', 'images', 'jquery-tablesorter') FileUtils.mkdir_p(images_dir) + FileUtils.rm_rf("#{images_dir}/.", :secure => true) Dir.glob(File.join('tablesorter', 'css', 'images', '*')).each do |file| FileUtils.cp file, images_dir, :verbose => true end @@ -37,27 +40,31 @@ namespace :jquery_tablesorter do ## pager pager_stylesheet_dir = File.join(stylesheet_dir, 'addons', 'pager') FileUtils.mkdir_p(pager_stylesheet_dir) + FileUtils.rm_rf("#{pager_stylesheet_dir}/.", :secure => true) FileUtils.cp File.join('tablesorter', 'addons', 'pager', 'jquery.tablesorter.pager.css'), pager_stylesheet_dir, :verbose => true pager_javascript_dir = File.join(javascript_dir, 'addons', 'pager') FileUtils.mkdir_p(pager_javascript_dir) + FileUtils.rm_rf("#{pager_javascript_dir}/.", :secure => true) FileUtils.cp File.join('tablesorter', 'addons', 'pager', 'jquery.tablesorter.pager.js'), pager_javascript_dir, :verbose => true pager_images_dir = File.join(images_dir, 'addons', 'pager') FileUtils.mkdir_p(pager_images_dir) + FileUtils.rm_rf("#{pager_images_dir}/.", :secure => true) FileUtils.cp_r File.join('tablesorter', 'addons', 'pager', 'icons'), pager_images_dir, :verbose => true - # parsers and widgets + # parsers, widgets and extras # %w(parsers widgets extras).each do |folder| folder_javascript_dir = File.join(javascript_dir, folder) FileUtils.mkdir_p(folder_javascript_dir) + FileUtils.rm_rf("#{folder_javascript_dir}/.", :secure => true) Dir.glob(File.join('tablesorter', 'js', folder, '*.js')).reject{|file| file =~ /.min.js\Z/}.each do |file| FileUtils.cp file, folder_javascript_dir, :verbose => true end diff --git a/vendor/assets/images/jquery-tablesorter/dropbox-asc1.png b/vendor/assets/images/jquery-tablesorter/dropbox-asc1.png deleted file mode 100644 index 0bce65a07a86cfaab08ac6bac4959e42fd31ca4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 277 zcmV+w0qXvVP)<h;3K|Lk000e1NJLTq000R9000RH1^@s6;E@Ip0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUyw@E}nRCwBA{Qv(y10lc?-L+ALg`M*@ z6Eh2j<iEdv82<eHuF429DQDT0!|YsqHjGS6a0UPW{bTs~?bDGdQ98C@?MzH8x$NA0 zXP8;pm|=?k{{Hp%_m6M6V0jQRDMI7IukT-G{r~?DVhWH3@j>EnMIi9w^M{(hzka;~ zs$c-oAU;eEu4qcM&O4wR>i_=v1#(M0h!6K2vTwe9d^_vk-@kG{zI>R)z@PvZXCx+^ b00ImE*WX@&i7{I-00000NkvXXu0mjf>Ct&J diff --git a/vendor/assets/images/jquery-tablesorter/dropbox-asc2.png b/vendor/assets/images/jquery-tablesorter/dropbox-asc2.png deleted file mode 100644 index 4930942048212f647f27b8c8f06ec2737a7ee016..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 273 zcmV+s0q*{ZP)<h;3K|Lk000e1NJLTq000R9000RH1^@s6;E@Ip0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUyvq?ljRCwBA{Qv(y10lc?U*6;V@7K3I zOw7y}lK=kxWng0Ca$y9Sl-oY%BQrA-A0s0pTmeY@@85r)CRe8Lvw#Fxxp>o985veE zvmi|S`{ysi@Ba*GAT}e+O=VNoUSVeZrwuX<Y|4M8t6e$4Iv_bFn9g6nfBFCW_wPSY z1p|l%@nLdsMH31m@BU+CS@ZYr9|jN&;=_H1?3*9oz9zA;vC9AY@ihrqoROGt0thew X+vQU#xeeWL00000NkvXXu0mjfn!s^J diff --git a/vendor/assets/images/jquery-tablesorter/dropbox-desc1.png b/vendor/assets/images/jquery-tablesorter/dropbox-desc1.png deleted file mode 100644 index 0d6ee15054013562d87105ebf9a5915b093b745a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 278 zcmV+x0qOpUP)<h;3K|Lk000e1NJLTq000R9000RH1^@s6;E@Ip0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUyxJg7oRCwBA{Qv(y10leI%!%w+$HvIS z)bjt|znbM0LBC+)OvsAZIC;`I`GiYBG_p7&%(|G~twKD)61O>c1cZKk`S9+`+ZU=! zioM^Vn8eP-+rq^!EX2hx!~mi}d=%^AChRca<P%C`=i*~vW?^Fh(I7rZ9Il9+n{P4) zuK+VM8ynC8j0_+e#0QCk<eBdaTB+~|N%R19GB7biJj)1F$i&3V@c-{$xfOfQEoCGo coB#q00A{8yymYN>J^%m!07*qoM6N<$f|sClJ^%m! diff --git a/vendor/assets/images/jquery-tablesorter/dropbox-desc2.png b/vendor/assets/images/jquery-tablesorter/dropbox-desc2.png deleted file mode 100644 index bc45223f1dec5faab874457b501249849a5ad67a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 275 zcmV+u0qp*XP)<h;3K|Lk000e1NJLTq000R9000RH1^@s6;E@Ip0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUywMj%lRCwBA{Qv(y10leI%n8q`WMgDv zYWe@~U(L#C?Z05+OvsAZ*g4ZUxp_-LG_p7&%(|$&Iw2l@!P^{M+(JLTeSP=k<9pR* zlbYV4n8eP()xyccCj>N!0YroNDAvUkG#GGj^Q5tJa56A6voL^Y5FaECSH#ZAHJO8p z2Pn@9bO0j*hz9XN;vjkEyC0cUxcLQpK-MrZGeaB-RLI1{#PIL`Ke-j_w=ZQRCY%5Q Z3;^4XF8Gf^mEZsX002ovPDHLkV1l>1alZfn diff --git a/vendor/assets/images/jquery-tablesorter/green-asc.png b/vendor/assets/images/jquery-tablesorter/green-asc.png deleted file mode 100644 index 1367ba9fabf956b0ecbb88fa16d9438b34de7686..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3229 zcmV;O3}W+%P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00009a7bBm000XU z000XU0RWnu7ytkYPiaF#P*7-ZbZ>KLZ*U+<Lqi~Na&Km7Y-Iodc-oy)XH-+^7Crag z^g>IBfRsybQWXdwQbLP>6p<z>Aqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uh<iVD~V z<RPMtgQJLw%KPDaqifc@_vX$1wbwr9tn;0-&j-K=43<bUQ8j=JsX`tR;Dg7+#^K~H zK!FM*Z~zbpvt%K2{UZSY_<lS*D<Z%Lz5oGu(+dayz)hRLFdT>f59&ghTmgWD0l;*T zI7<kC6aYYajzXpYKt=(8otP$50H6c_V9R4-;{Z@C0AMG7=F<Rxo%or10RUT+Ar%3j zkpLhQWr#!oXgdI`&sK^>09Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-<?i z0%4j!F2Z@488U%158(66005wo6%pWr^Zj_v4zAA5HjcIqUoGmt2LB>rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_<lS*MWK+n+1cgf z<k(8YLR(?VSAG6x!e78w{cQPuJpA|d;J)G{fihizM+Erb!p!tcr5w+a34~(Y=8s4G zw+sLL9n&JjNn*KJDiq^U5^;`1nvC-@r6P$!k}1U{(*I=Q-z@tBKHoI}uxdU5dyy@u zU1J0GOD7Ombim^G008p4Z^6_k2m^p<gW=D2|L;HjN1!DDfM!XOaR2~bL?kX$%CkSm z2mk;?pn)o|K^yeJ7%adB9Ki+L!3+FgHiSYX#KJ-lLJDMn9CBbOtb#%)hRv`YDqt_v zKpix|QD}yfa1JiQRk#j4a1Z)n2%f<xynzV>LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_Ifq<Ex{*7`05XF7hP+2Hl!3BQJ=6@fL%FCo z8iYoo3(#bAF`ADSpqtQgv>H8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ<AYmRsNLWl*PS{AOARHt#5!wki2?K;t z!Y3k=s7tgax)J%r7-BLphge7~Bi0g+6E6^Zh(p9TBoc{3GAFr^0!gu?RMHaCM$&Fl zBk3%un>0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 z<uv66WtcKSRim0x-Ke2d5jBrmLam{;Qm;{ms1r1GnmNsb7D-E`t)i9F8fX`2_i3-_ zbh;7Ul^#x)&{xvS=|||7=mYe33=M`AgU5(xC>fg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vF<Q0r40Q)j6=sE4X&sBct1q<&fbi3VB2Ov6t@q*0);U*o*SAPZv|vv@2aYYnT0 zb%8a+Cb7-ge0D0knEf5Qi#@8Tp*ce{N;6lpQuCB%KL_KOarm5cP6_8Ir<e17iry6O zDdH&`rZh~sF=bq9s+O0QSgS~@QL9Jmy*94xr=6y~MY~!1fet~(N+(<=M`w@D1)b+p z*;C!83a1uLJv#NSE~;y#8=<>IcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a<fJbF^|4I#xQ~n$Dc= zKYhjYmgz5NSkDm8*fZm{6U!;YX`NG>(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-k<Mujg;0Lz*3buG=3$G&ehepthlN*$KaOySSQ^nWmo<0M+(UEUMEXRQ zMBbZcF;6+KElM>iKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BK<z=<L*0kfKU@CX*zeqbYQT4(^U>T#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot<a{81DF0~rvGr5Xr~8u`lav1h z1DNytV>2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0005VNkl<Zc-q91y=zlZ7{-6^%{}+#HuUz^4=P5CG*GRdv{VN{L6F*^e?S+Vij&&W z!GFL>5S^TyoTR$Ah~N^mV2vM0W22%;ZHsz!s3tK<y|05e5KHTl!3WN8KAs;u=R8ML zl}=>Qp*jZGfB=X{0`TIsjU@4C0ySXOjtwS0up3CX&DWX}Km>dQioiD;@X9Nt%+Z@i zMoTNDRPZU-V`@$yDptGK!6dp!xlY3U_4Ho;c323@tq9IeKeOY|kD-xbp*T@F@tyv3 zmcRsvSXc;N!lTdKMqqk*{@}%vun;c95mZ#w2Mz<r&s;n`erI-i*dI&b4JX;;Y$j<X z@ogVt8N!!eALS-zt^v=04?ta1)sAx?14hOsN5^w>xor2TKajxh!B~dM``Y8e**90B zdNc==fGVJth^zujzzpze`ug4bgXz6Xm0Xo(xk)2xP+k0)pFjWhYE+NrfrXa&uPatn zS5^0^>bd1F%QyN@?)toSs#i0&w$GX^W&$_}bpNwEs_Logkg8tDzs_GCyfpC8&6)$i zexPSPv#bFK&;<+teZV^rS&ZMm5_e!@KpP|pxIjZhYOSqxtNGu5<L?0gw$Y%$2XQ=J P00000NkvXXu0mjfF#rlV diff --git a/vendor/assets/images/jquery-tablesorter/green-desc.png b/vendor/assets/images/jquery-tablesorter/green-desc.png deleted file mode 100644 index d32a3aa932f09c9d3eb4e64173d5370a0aee8c97..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3212 zcmV;740H2|P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00009a7bBm000XU z000XU0RWnu7ytkYPiaF#P*7-ZbZ>KLZ*U+<Lqi~Na&Km7Y-Iodc-oy)XH-+^7Crag z^g>IBfRsybQWXdwQbLP>6p<z>Aqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uh<iVD~V z<RPMtgQJLw%KPDaqifc@_vX$1wbwr9tn;0-&j-K=43<bUQ8j=JsX`tR;Dg7+#^K~H zK!FM*Z~zbpvt%K2{UZSY_<lS*D<Z%Lz5oGu(+dayz)hRLFdT>f59&ghTmgWD0l;*T zI7<kC6aYYajzXpYKt=(8otP$50H6c_V9R4-;{Z@C0AMG7=F<Rxo%or10RUT+Ar%3j zkpLhQWr#!oXgdI`&sK^>09Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-<?i z0%4j!F2Z@488U%158(66005wo6%pWr^Zj_v4zAA5HjcIqUoGmt2LB>rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_<lS*MWK+n+1cgf z<k(8YLR(?VSAG6x!e78w{cQPuJpA|d;J)G{fihizM+Erb!p!tcr5w+a34~(Y=8s4G zw+sLL9n&JjNn*KJDiq^U5^;`1nvC-@r6P$!k}1U{(*I=Q-z@tBKHoI}uxdU5dyy@u zU1J0GOD7Ombim^G008p4Z^6_k2m^p<gW=D2|L;HjN1!DDfM!XOaR2~bL?kX$%CkSm z2mk;?pn)o|K^yeJ7%adB9Ki+L!3+FgHiSYX#KJ-lLJDMn9CBbOtb#%)hRv`YDqt_v zKpix|QD}yfa1JiQRk#j4a1Z)n2%f<xynzV>LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_Ifq<Ex{*7`05XF7hP+2Hl!3BQJ=6@fL%FCo z8iYoo3(#bAF`ADSpqtQgv>H8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ<AYmRsNLWl*PS{AOARHt#5!wki2?K;t z!Y3k=s7tgax)J%r7-BLphge7~Bi0g+6E6^Zh(p9TBoc{3GAFr^0!gu?RMHaCM$&Fl zBk3%un>0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 z<uv66WtcKSRim0x-Ke2d5jBrmLam{;Qm;{ms1r1GnmNsb7D-E`t)i9F8fX`2_i3-_ zbh;7Ul^#x)&{xvS=|||7=mYe33=M`AgU5(xC>fg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vF<Q0r40Q)j6=sE4X&sBct1q<&fbi3VB2Ov6t@q*0);U*o*SAPZv|vv@2aYYnT0 zb%8a+Cb7-ge0D0knEf5Qi#@8Tp*ce{N;6lpQuCB%KL_KOarm5cP6_8Ir<e17iry6O zDdH&`rZh~sF=bq9s+O0QSgS~@QL9Jmy*94xr=6y~MY~!1fet~(N+(<=M`w@D1)b+p z*;C!83a1uLJv#NSE~;y#8=<>IcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a<fJbF^|4I#xQ~n$Dc= zKYhjYmgz5NSkDm8*fZm{6U!;YX`NG>(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-k<Mujg;0Lz*3buG=3$G&ehepthlN*$KaOySSQ^nWmo<0M+(UEUMEXRQ zMBbZcF;6+KElM>iKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BK<z=<L*0kfKU@CX*zeqbYQT4(^U>T#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot<a{81DF0~rvGr5Xr~8u`lav1h z1DNytV>2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0005ENkl<Zc-q91PfJu`6vlt&z4zwanZ`eJFF7#@4TVXyvPEf>qz1L=8w7oWAPQPX zv<RU^qKp1~fkJ9go1#rY5)^?_L^D|nCygAVac1t=cu$)*3Qac>J?rQB9o}=ES5%eF z$ncLPc8F|bSR+++ffW(S03ARDC<8xMd!2Q{zKArp4Th(xGqs~Y7Fe{PcwK<1IzS3Y zj5kJicAwdO_u!sG``$cxn*)}C&p;K>H2@n<0{(@2=d#mFQxDqm>FkNVlkH(TVCw1g zcHlcO2h@H+92+)vvVY~#<;?BpH}9K_$(v9Jynw#L{T<O_)F{3#X6-KKfe65{;Uti} zHF+aF^7!fmq4sz=m$)S+iX(g{$-wYHP<mHtlnSLz;4AP2SQb_Ff!xQ1nXchW$Hpp& zN{`#=Vp4(_K|I0hav2Jbv)9|-yncH0W%1RAJHQ0+NmMl`*B1JR&JJEHyf5rBtp<~j z-xU)<JlL|sBXz{5ck6*~&(2R?oO?0<5SSHJ4S-%C57h1Z>VK(61DF+&D$r(ku_UV6 y0%U+zYi#~`rd6r~Wf57ieTe11f2K_XehvW8YnqtZvd#|x0000<MNUMnLSTY3Z2MUN diff --git a/vendor/assets/images/jquery-tablesorter/green-header.png b/vendor/assets/images/jquery-tablesorter/green-header.png deleted file mode 100644 index ccf62ace2dc983c422f92b5d8670cd86bdbe5968..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3247 zcmV;g3{dllP)<h;3K|Lk000e1NJLTq000F5003kN1^@s6PRIHh00009a7bBm000XU z000XU0RWnu7ytkYPiaF#P*7-ZbZ>KLZ*U+<Lqi~Na&Km7Y-Iodc-oy)XH-+^7Crag z^g>IBfRsybQWXdwQbLP>6p<z>Aqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uh<iVD~V z<RPMtgQJLw%KPDaqifc@_vX$1wbwr9tn;0-&j-K=43<bUQ8j=JsX`tR;Dg7+#^K~H zK!FM*Z~zbpvt%K2{UZSY_<lS*D<Z%Lz5oGu(+dayz)hRLFdT>f59&ghTmgWD0l;*T zI7<kC6aYYajzXpYKt=(8otP$50H6c_V9R4-;{Z@C0AMG7=F<Rxo%or10RUT+Ar%3j zkpLhQWr#!oXgdI`&sK^>09Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-<?i z0%4j!F2Z@488U%158(66005wo6%pWr^Zj_v4zAA5HjcIqUoGmt2LB>rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_<lS*MWK+n+1cgf z<k(8YLR(?VSAG6x!e78w{cQPuJpA|d;J)G{fihizM+Erb!p!tcr5w+a34~(Y=8s4G zw+sLL9n&JjNn*KJDiq^U5^;`1nvC-@r6P$!k}1U{(*I=Q-z@tBKHoI}uxdU5dyy@u zU1J0GOD7Ombim^G008p4Z^6_k2m^p<gW=D2|L;HjN1!DDfM!XOaR2~bL?kX$%CkSm z2mk;?pn)o|K^yeJ7%adB9Ki+L!3+FgHiSYX#KJ-lLJDMn9CBbOtb#%)hRv`YDqt_v zKpix|QD}yfa1JiQRk#j4a1Z)n2%f<xynzV>LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_Ifq<Ex{*7`05XF7hP+2Hl!3BQJ=6@fL%FCo z8iYoo3(#bAF`ADSpqtQgv>H8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ<AYmRsNLWl*PS{AOARHt#5!wki2?K;t z!Y3k=s7tgax)J%r7-BLphge7~Bi0g+6E6^Zh(p9TBoc{3GAFr^0!gu?RMHaCM$&Fl zBk3%un>0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 z<uv66WtcKSRim0x-Ke2d5jBrmLam{;Qm;{ms1r1GnmNsb7D-E`t)i9F8fX`2_i3-_ zbh;7Ul^#x)&{xvS=|||7=mYe33=M`AgU5(xC>fg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vF<Q0r40Q)j6=sE4X&sBct1q<&fbi3VB2Ov6t@q*0);U*o*SAPZv|vv@2aYYnT0 zb%8a+Cb7-ge0D0knEf5Qi#@8Tp*ce{N;6lpQuCB%KL_KOarm5cP6_8Ir<e17iry6O zDdH&`rZh~sF=bq9s+O0QSgS~@QL9Jmy*94xr=6y~MY~!1fet~(N+(<=M`w@D1)b+p z*;C!83a1uLJv#NSE~;y#8=<>IcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a<fJbF^|4I#xQ~n$Dc= zKYhjYmgz5NSkDm8*fZm{6U!;YX`NG>(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-k<Mujg;0Lz*3buG=3$G&ehepthlN*$KaOySSQ^nWmo<0M+(UEUMEXRQ zMBbZcF;6+KElM>iKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BK<z=<L*0kfKU@CX*zeqbYQT4(^U>T#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot<a{81DF0~rvGr5Xr~8u`lav1h z1DNytV>2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0005nNkl<Zc-o}Z%d(;{5C-6W|Mzm^VgwPz3nHi_fH$r#|5;EZO;@VsoSMbApsNhA z)A^zA_X~-f#bFpAQQ!BF+{LczAW_@4kbY5guIViPe4qW$qaQ@k`QP0QasAxgkiJ9L z%gz71*tHnV`q^N_y417{nzljHSX4JPB>L1JNc4WcA<^sgf<({f6B0ch4@gv16(p+4 z5>;8EWT-3(ltqDpp+ajET0=9Wm4a4kUQ~GwmFG~(qWk?0iEg(WB+9Z3S(f2?y&9CJ zDbh4WmRWSUBp6)`N|G~1i9vB3BaUO7&u4?;_=Hhx(CKu<=w#6Gc!WfU!vPZQ_j^dR z+wCCHW)otw2@x{1UaujM=XsE5wOT=9#Xp8AdJJ20WsvWA@I4Q{Z;|i&@O>X^#_Tg- zC<p=sL4XZIVHhF|Lu?t^ZMWEMxATTXQG_Up=B=9=JL~*(F0+H1jx{+a2Jy)-6EY20 z%Fr}v8Dr-2%9u&MF_h;y@;t|#A*B?QQc&8WLR_q(C_v`=RhGTQPs@hD+p=--v8b+V z)U`+(lSi#(bH20wJJQX=VEiJ||MB5tYdYWV{BoIwFz%#v=BF0nr?s}T#JS^Qx<?!> hmrF>rSS%p@9ROHBK6t9j)v^Ep002ovPDHLkV1j5Q8m|BV diff --git a/vendor/assets/images/jquery-tablesorter/green-unsorted.png b/vendor/assets/images/jquery-tablesorter/green-unsorted.png deleted file mode 100644 index 0afbf71da00587b1d4038b1942348870c5df36d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3513 zcmV;q4My^bP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00009a7bBm000XU z000XU0RWnu7ytkYPiaF#P*7-ZbZ>KLZ*U+<Lqi~Na&Km7Y-Iodc-oy)XH-+^7Crag z^g>IBfRsybQWXdwQbLP>6p<z>Aqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uh<iVD~V z<RPMtgQJLw%KPDaqifc@_vX$1wbwr9tn;0-&j-K=43<bUQ8j=JsX`tR;Dg7+#^K~H zK!FM*Z~zbpvt%K2{UZSY_<lS*D<Z%Lz5oGu(+dayz)hRLFdT>f59&ghTmgWD0l;*T zI7<kC6aYYajzXpYKt=(8otP$50H6c_V9R4-;{Z@C0AMG7=F<Rxo%or10RUT+Ar%3j zkpLhQWr#!oXgdI`&sK^>09Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-<?i z0%4j!F2Z@488U%158(66005wo6%pWr^Zj_v4zAA5HjcIqUoGmt2LB>rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_<lS*MWK+n+1cgf z<k(8YLR(?VSAG6x!e78w{cQPuJpA|d;J)G{fihizM+Erb!p!tcr5w+a34~(Y=8s4G zw+sLL9n&JjNn*KJDiq^U5^;`1nvC-@r6P$!k}1U{(*I=Q-z@tBKHoI}uxdU5dyy@u zU1J0GOD7Ombim^G008p4Z^6_k2m^p<gW=D2|L;HjN1!DDfM!XOaR2~bL?kX$%CkSm z2mk;?pn)o|K^yeJ7%adB9Ki+L!3+FgHiSYX#KJ-lLJDMn9CBbOtb#%)hRv`YDqt_v zKpix|QD}yfa1JiQRk#j4a1Z)n2%f<xynzV>LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_Ifq<Ex{*7`05XF7hP+2Hl!3BQJ=6@fL%FCo z8iYoo3(#bAF`ADSpqtQgv>H8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ<AYmRsNLWl*PS{AOARHt#5!wki2?K;t z!Y3k=s7tgax)J%r7-BLphge7~Bi0g+6E6^Zh(p9TBoc{3GAFr^0!gu?RMHaCM$&Fl zBk3%un>0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 z<uv66WtcKSRim0x-Ke2d5jBrmLam{;Qm;{ms1r1GnmNsb7D-E`t)i9F8fX`2_i3-_ zbh;7Ul^#x)&{xvS=|||7=mYe33=M`AgU5(xC>fg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vF<Q0r40Q)j6=sE4X&sBct1q<&fbi3VB2Ov6t@q*0);U*o*SAPZv|vv@2aYYnT0 zb%8a+Cb7-ge0D0knEf5Qi#@8Tp*ce{N;6lpQuCB%KL_KOarm5cP6_8Ir<e17iry6O zDdH&`rZh~sF=bq9s+O0QSgS~@QL9Jmy*94xr=6y~MY~!1fet~(N+(<=M`w@D1)b+p z*;C!83a1uLJv#NSE~;y#8=<>IcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a<fJbF^|4I#xQ~n$Dc= zKYhjYmgz5NSkDm8*fZm{6U!;YX`NG>(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-k<Mujg;0Lz*3buG=3$G&ehepthlN*$KaOySSQ^nWmo<0M+(UEUMEXRQ zMBbZcF;6+KElM>iKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BK<z=<L*0kfKU@CX*zeqbYQT4(^U>T#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot<a{81DF0~rvGr5Xr~8u`lav1h z1DNytV>2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0008yNkl<Zc-nN6O=whS6o#Mg-aGfs<jyqFj7d#QYy}BIj7qhJW>G8{5)rHxr4}RD zMRg~LinuXqaA6_RHna=Vg4o1XB1Fw93n6H!nD|R8H44g1GC%$$lbK9zW-|A<xhX;8 zS)b=UocBFP@m7h52KbdyQ@~oF7Kj6PfR}(fx8Il#1_H%fL)MCxn%@fDGBwz9`w?IQ z*MV_B-VqQH<-zvQ$e>xY-v6_{NUPn^w%wgdIQl;~{@o5t0=Iz7tcd<LJd%p~nts~& zqZ@R$njac04SuW|H#dgwUwe>Cy-bw?3E&lw2hcs(0)l|H=2YG3X?uF7S!p1);NlXt z!}=G^8%qDadb8kUoMO+DF<?gX)+hz48jjbWNoO;=m8EbU!5Nw6UgR!)$txs7iNL|G z11l}tss&a7C4f>Q5&~AXwC_56xxaUd9@Nl6nztM%1)8m4)tD?_wVbN4>L-8f>^YfB z=dJ-UB_a#5`Rpe>0~ebz<CDJli+DPnotSb(+&Ko5$!DCQ#H~c^b*v;eI1|kbPu>QS z2B3<3MX%OXuJ7v}=&l_eeq8A|P8d*gBQ6vQV#WhYvJa<Ij^j*tzG+HC7DdM+tGAu~ z@>see6)am&UL00ozk&Y0fI?RxBdE%Za$RP~pFjHh52Hh|3qaHWw5pQo4^A|+J!m<( zYftL43IFqk1XY1*e3}mpjgS$-GA%NaE)29^KQ}rQ8w4f*SBZ%2xw@h2YFGWfW8XDf zpPG8`^CGNri;xk-)@_t1H}dD`FHbunSAd5=63FYVt*tXa7EpCHb$-)lO8=$)k5HhO zY4~kFf)MLVo_%&EdKvf+cs_ebhEhsIWDK|q_z!-2$cV%v_OHF2>wFbH(hJkLf2s4& zPDguzVQ+G7?mx^%rHjZC5&2p~zTdoe)1A_NB~sa5)&VpE%e-?}yst#W5Rqjf@{N;o n_V4KY>IkqHSPBH@gWn7Q1OY#gF%Tv+00000NkvXXu0mjfm}7=I From 6b7f5c947dc7bc98070d0a0f1384abfff3915c63 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Fri, 28 Mar 2014 21:18:51 +0100 Subject: [PATCH 023/138] * minor code changes and optimizations, updated Readme --- CHANGELOG.md | 1 + README.md | 2 +- Rakefile | 55 +++++++++++++------------------------- jquery-tablesorter.gemspec | 22 +++++++-------- lib/jquery-tablesorter.rb | 2 +- 5 files changed, 33 insertions(+), 49 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 273e226..57e3784 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Changelog * Remove all vendor files before updating from tablesorter repository ** Removed unused images +* Some minor code changes and optimizations, updated Readme #### v1.10.7 (2014-03-11) diff --git a/README.md b/README.md index 2ddcf73..8ca0efc 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ In your `application.js` //= require jquery-tablesorter ``` -This will require all jquery-tablesorter files (excluding addons, widgets, ...). +This will require all jquery-tablesorter files (excluding addons and extras). Or you can include single file with: diff --git a/Rakefile b/Rakefile index 9c16f50..ed79d3c 100755 --- a/Rakefile +++ b/Rakefile @@ -11,67 +11,50 @@ namespace :jquery_tablesorter do # javascripts # javascript_dir = File.join('vendor', 'assets', 'javascripts', 'jquery-tablesorter') - FileUtils.mkdir_p(javascript_dir) - FileUtils.rm_rf("#{javascript_dir}/.", :secure => true) - Dir.glob(File.join('tablesorter', 'js', '*.js')).reject{|file| file =~ /.min.js\Z/}.each do |file| - FileUtils.cp file, javascript_dir, :verbose => true - end + copy_files(Dir.glob(File.join('tablesorter', 'js', '*.js')).reject{|file| file =~ /.min.js\Z/}, javascript_dir) # stylesheets # stylesheet_dir = File.join('vendor', 'assets', 'stylesheets', 'jquery-tablesorter') - FileUtils.mkdir_p(stylesheet_dir) - FileUtils.rm_rf("#{stylesheet_dir}/.", :secure => true) - Dir.glob(File.join('tablesorter', 'css', '*.css')).each do |file| - FileUtils.cp file, stylesheet_dir, :verbose => true - end + copy_files(Dir.glob(File.join('tablesorter', 'css', '*.css')), stylesheet_dir) # images # images_dir = File.join('vendor', 'assets', 'images', 'jquery-tablesorter') - FileUtils.mkdir_p(images_dir) - FileUtils.rm_rf("#{images_dir}/.", :secure => true) - Dir.glob(File.join('tablesorter', 'css', 'images', '*')).each do |file| - FileUtils.cp file, images_dir, :verbose => true - end + copy_files(Dir.glob(File.join('tablesorter', 'css', 'images', '*')), images_dir) # addons # ## pager - pager_stylesheet_dir = File.join(stylesheet_dir, 'addons', 'pager') - FileUtils.mkdir_p(pager_stylesheet_dir) - FileUtils.rm_rf("#{pager_stylesheet_dir}/.", :secure => true) - FileUtils.cp File.join('tablesorter', 'addons', 'pager', 'jquery.tablesorter.pager.css'), - pager_stylesheet_dir, - :verbose => true + pager_stylesheet_dir = File.join(stylesheet_dir, 'addons', 'pager') + copy_files([File.join('tablesorter', 'addons', 'pager', 'jquery.tablesorter.pager.css')], pager_stylesheet_dir) pager_javascript_dir = File.join(javascript_dir, 'addons', 'pager') - FileUtils.mkdir_p(pager_javascript_dir) - FileUtils.rm_rf("#{pager_javascript_dir}/.", :secure => true) - FileUtils.cp File.join('tablesorter', 'addons', 'pager', 'jquery.tablesorter.pager.js'), - pager_javascript_dir, - :verbose => true + copy_files([File.join('tablesorter', 'addons', 'pager', 'jquery.tablesorter.pager.js')], pager_javascript_dir) - pager_images_dir = File.join(images_dir, 'addons', 'pager') - FileUtils.mkdir_p(pager_images_dir) - FileUtils.rm_rf("#{pager_images_dir}/.", :secure => true) - FileUtils.cp_r File.join('tablesorter', 'addons', 'pager', 'icons'), pager_images_dir, - :verbose => true + pager_images_dir = File.join(images_dir, 'addons', 'pager', 'icons') + copy_files(Dir.glob(File.join('tablesorter', 'addons', 'pager', 'icons', '*')), pager_images_dir) # parsers, widgets and extras # %w(parsers widgets extras).each do |folder| folder_javascript_dir = File.join(javascript_dir, folder) - FileUtils.mkdir_p(folder_javascript_dir) - FileUtils.rm_rf("#{folder_javascript_dir}/.", :secure => true) - Dir.glob(File.join('tablesorter', 'js', folder, '*.js')).reject{|file| file =~ /.min.js\Z/}.each do |file| - FileUtils.cp file, folder_javascript_dir, :verbose => true - end + files = Dir.glob(File.join('tablesorter', 'js', folder, '*.js')).reject{|file| file =~ /.min.js\Z/} + copy_files(files, folder_javascript_dir) end end + def copy_files(files, target_dir) + FileUtils.mkdir_p(target_dir) + FileUtils.rm_rf("#{target_dir}/.", :secure => true) + + files.each do |file| + FileUtils.cp(file, target_dir, :verbose => true) + end + end + desc 'Sanitize image paths' task :sanitize_image_paths do Dir.glob(File.join('vendor', 'assets', 'stylesheets', 'jquery-tablesorter', '*.css')).each do |file_path| diff --git a/jquery-tablesorter.gemspec b/jquery-tablesorter.gemspec index 54f3edd..9d74ef3 100644 --- a/jquery-tablesorter.gemspec +++ b/jquery-tablesorter.gemspec @@ -1,20 +1,20 @@ -$:.push File.expand_path("../lib", __FILE__) +$:.push File.expand_path('../lib', __FILE__) # Maintain your gem's version: -require "jquery-tablesorter/version" +require 'jquery-tablesorter/version' # Describe your gem and declare its dependencies: Gem::Specification.new do |s| - s.name = "jquery-tablesorter" + s.name = 'jquery-tablesorter' s.version = JqueryTablesorter::VERSION - s.authors = ["Jun Lin", "Erik-B. Ernst"] - s.email = ["github@black-milk.de"] - s.homepage = "https://github.com/themilkman/jquery-tablesorter-rails" - s.summary = "Simple integration of jquery-tablesorter into the asset pipeline." - s.description = "Simple integration of jquery-tablesorter into the asset pipeline." - s.license = "MIT" + s.authors = ['Jun Lin', 'Erik-B. Ernst'] + s.email = ['github@black-milk.de'] + s.homepage = 'https://github.com/themilkman/jquery-tablesorter-rails' + s.summary = 'Simple integration of jquery-tablesorter into the asset pipeline.' + s.description = 'Simple integration of jquery-tablesorter into the asset pipeline.' + s.license = 'MIT' - s.files = Dir["{vendor,lib}/**/*"] + ["MIT-LICENSE", "Rakefile", "README.md"] + s.files = Dir["{vendor,lib}/**/*"] + %w( MIT-LICENSE Rakefile README.md ) - s.add_dependency "railties", ">= 3.1", "< 5" + s.add_dependency 'railties', '>= 3.1', '< 5' end diff --git a/lib/jquery-tablesorter.rb b/lib/jquery-tablesorter.rb index 8edba7a..57b3680 100644 --- a/lib/jquery-tablesorter.rb +++ b/lib/jquery-tablesorter.rb @@ -1,4 +1,4 @@ -require "jquery-tablesorter/engine" +require 'jquery-tablesorter/engine' module JqueryTablesorter end From 6a40a396b69c1deccd5a1b54b2b980d942e41cd4 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Mon, 31 Mar 2014 20:28:54 +0200 Subject: [PATCH 024/138] * updated tablesorter to latest version (2.15.12) --- CHANGELOG.md | 3 +- README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 24 +++++++++-- .../jquery-tablesorter/jquery.tablesorter.js | 25 +++++++----- .../jquery.tablesorter.widgets.js | 40 +++++++++++++------ .../widgets/widget-columnSelector.js | 14 +++---- .../widgets/widget-pager.js | 25 +++++++++--- .../jquery-tablesorter/theme.black-ice.css | 4 ++ .../jquery-tablesorter/theme.blue.css | 4 ++ .../jquery-tablesorter/theme.bootstrap.css | 4 ++ .../jquery-tablesorter/theme.bootstrap_2.css | 4 ++ .../jquery-tablesorter/theme.dark.css | 4 ++ .../jquery-tablesorter/theme.default.css | 4 ++ .../jquery-tablesorter/theme.dropbox.css | 4 ++ .../jquery-tablesorter/theme.green.css | 4 ++ .../jquery-tablesorter/theme.grey.css | 4 ++ .../jquery-tablesorter/theme.ice.css | 4 ++ .../jquery-tablesorter/theme.jui.css | 4 ++ 20 files changed, 138 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57e3784..f8b8f99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ Changelog === -#### v1.x.x (not released yet) +#### v1.10.8 (2014-03-31) +* Upgrade tablesorter to v2.15.12 * Remove all vendor files before updating from tablesorter repository ** Removed unused images * Some minor code changes and optimizations, updated Readme diff --git a/README.md b/README.md index 8ca0efc..4aab58f 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.15.11 (3/18/2014), [documentation] +Current tablesorter version: 2.15.12 (3/31/2014), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 8d06c9d..2c1e719 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.10.7' + VERSION = '1.10.8' end diff --git a/tablesorter b/tablesorter index fdae3bf..4a984eb 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit fdae3bfcd57006d62f5b46f2ef96f8416c50c1b4 +Subproject commit 4a984eb68124be1c1170d116c6f6a2d84979963f diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 4706da4..c155562 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 3/18/2014 (v2.15.11) + * updated 3/31/2014 (v2.15.12) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -207,6 +207,7 @@ hideRows = function(table, p){ if (!p.ajaxUrl) { var i, + lastIndex = 0, c = table.config, rows = c.$tbodies.eq(0).children(), l = rows.length, @@ -216,9 +217,23 @@ j = 0; // size counter for ( i = 0; i < l; i++ ){ if ( !rows[i].className.match(f) ) { - rows[i].style.display = ( j >= s && j < e ) ? '' : 'none'; - // don't count child rows - j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) && !p.countChildRows ? 0 : 1; + if (j === s && rows[i].className.match(c.cssChildRow)) { + // hide child rows @ start of pager (if already visible) + rows[i].style.display = 'none'; + } else { + rows[i].style.display = ( j >= s && j < e ) ? '' : 'none'; + // don't count child rows + j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) && !p.countChildRows ? 0 : 1; + if ( j === e && rows[i].style.display !== 'none' && rows[i].className.match(ts.css.cssHasChild) ) { + lastIndex = i; + } + } + } + } + // add any attached child rows to last row of pager. Fixes part of issue #396 + if ( lastIndex > 0 && rows[lastIndex].className.match(ts.css.cssHasChild) ) { + while ( ++lastIndex < l && rows[lastIndex].className.match(c.cssChildRow) ) { + rows[lastIndex].style.display = ''; } } } @@ -457,6 +472,7 @@ for ( i = s; i < e; i++ ) { $tb.append(rows[i]); } + ts.processTbody(table, $tb, false); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 817bd93..89b7f61 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.15.11 - Client-side table sorting with ease! +* TableSorter 2.15.12 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.15.11"; + ts.version = "2.15.12"; ts.parsers = []; ts.widgets = []; @@ -114,6 +114,7 @@ // the table and MUST only contain one class name - fixes #381 ts.css = { table : 'tablesorter', + cssHasChild: 'tablesorter-hasChildRow', childRow : 'tablesorter-childRow', header : 'tablesorter-header', headerRow : 'tablesorter-headerRow', @@ -291,6 +292,10 @@ // if this is a child row, add it to the last row's children and continue to the next row if (c.hasClass(tc.cssChildRow)) { tc.cache[k].row[tc.cache[k].row.length - 1] = tc.cache[k].row[tc.cache[k].row.length - 1].add(c); + // add "hasChild" class name to parent row + if (!c.prev().hasClass(tc.cssChildRow)) { + c.prev().addClass(ts.css.cssHasChild); + } // go to the next for loop continue; } @@ -394,7 +399,7 @@ for (j = 0; j < cells.length; j++) { c = cells[j]; rowIndex = c.parentNode.rowIndex; - cellId = rowIndex + "-" + c.cellIndex; + cellId = rowIndex + "-" + $(c).index(); rowSpan = c.rowSpan || 1; colSpan = c.colSpan || 1; if (typeof(matrix[rowIndex]) === "undefined") { @@ -424,7 +429,6 @@ } // may not be accurate if # header columns !== # tbody columns t.config.columns = cols + 1; // add one because it's a zero-based index - return lookup; } function formatSortingOrder(v) { @@ -433,13 +437,15 @@ } function buildHeaders(table) { - var header_index = computeThIndexes(table), ch, $t, - h, i, t, lock, time, c = table.config; + var ch, $t, + h, i, t, lock, time, + c = table.config; c.headerList = []; c.headerContent = []; if (c.debug) { time = new Date(); } + computeThIndexes(table); // add icon if cssIcon option exists i = c.cssIcon ? '<i class="' + ( c.cssIcon === ts.css.icon ? ts.css.icon : c.cssIcon + ' ' + ts.css.icon ) + '"></i>' : ''; c.$headers = $(table).find(c.selectorHeaders).each(function(index) { @@ -455,8 +461,7 @@ $(this).html('<div class="' + ts.css.headerIn + '">' + t + '</div>'); // faster than wrapInner if (c.onRenderHeader) { c.onRenderHeader.apply($t, [index]); } - - this.column = header_index[this.parentNode.rowIndex + "-" + this.cellIndex]; + this.column = parseInt( $(this).attr('data-column'), 10); this.order = formatSortingOrder( ts.getData($t, ch, 'sortInitialOrder') || c.sortInitialOrder ) ? [1,0,2] : [0,1,2]; this.count = -1; // set to -1 because clicking on the header automatically adds one this.lockedOrder = false; @@ -829,9 +834,9 @@ // tbody may not exist if update is initialized while tbody is removed for processing if ($tb.length && tbdy >= 0) { row = $tb.eq(tbdy).find('tr').index( $row ); - icell = cell.cellIndex; + icell = $(cell).index(); l = c.cache[tbdy].normalized[row].length - 1; - c.cache[tbdy].row[table.config.cache[tbdy].normalized[row][l]] = $row; + c.cache[tbdy].row[ c.cache[tbdy].normalized[row][l] ] = $row; c.cache[tbdy].normalized[row][icell] = c.parsers[icell].format( getElementText(table, cell, icell), table, cell, icell ); checkResort($table, resort, callback); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 8695a3d..a081851 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.15+ widgets - updated 3/18/2014 (v2.15.11) +/*! tableSorter 2.15+ widgets - updated 3/31/2014 (v2.15.12) * * Column Styles * Column Filters @@ -868,7 +868,7 @@ ts.filter = { $rows = $tbody.children('tr').not(c.selectorRemove); len = $rows.length; if (combinedFilters === '' || wo.filter_serversideFiltering) { - $tbody.children().not('.' + c.cssChildRow).show().removeClass(wo.filter_filteredRow); + $tbody.children().removeClass(wo.filter_filteredRow).not('.' + c.cssChildRow).show(); } else { // optimize searching only through already filtered rows - see #313 searchFiltered = true; @@ -990,13 +990,11 @@ ts.filter = { showRow = (result) ? showRow : false; } } - $rows[rowIndex].style.display = (showRow ? '' : 'none'); - $rows.eq(rowIndex)[showRow ? 'removeClass' : 'addClass'](wo.filter_filteredRow); + $rows.eq(rowIndex) + .toggle(showRow) + .toggleClass(wo.filter_filteredRow, !showRow); if (childRow.length) { - if (c.pager && c.pager.countChildRows || wo.pager_countChildRows || wo.filter_childRows) { - childRow[showRow ? 'removeClass' : 'addClass'](wo.filter_filteredRow); // see issue #396 - } - childRow.toggle(showRow); + childRow.toggleClass(wo.filter_filteredRow, !showRow); } cacheIndex++; } @@ -1348,11 +1346,13 @@ ts.addWidget({ priority: 40, options: { resizable : true, - resizable_addLastColumn : false + resizable_addLastColumn : false, + resizable_widths : [] }, format: function(table, c, wo) { if (c.$table.hasClass('hasResizable')) { return; } c.$table.addClass('hasResizable'); + ts.resizableReset(table, true); // set default widths var $rows, $columns, $column, column, storedSizes = {}, $table = c.$table, @@ -1368,7 +1368,8 @@ ts.addWidget({ $target.width( storedSizes[$target.index()] ); $next.width( storedSizes[$next.index()] ); if (wo.resizable !== false) { - ts.storage(table, 'tablesorter-resizable', storedSizes); + // save all column widths + ts.storage(table, 'tablesorter-resizable', c.$headers.map(function(){ return $(this).width(); }).get() ); } } mouseXPosition = 0; @@ -1463,10 +1464,23 @@ ts.addWidget({ ts.resizableReset(table); } }); -ts.resizableReset = function(table) { +ts.resizableReset = function(table, nosave) { $(table).each(function(){ - this.config.$headers.not('.resizable-false').css('width',''); - if (ts.storage) { ts.storage(this, 'tablesorter-resizable', {}); } + var $t, + c = this.config, + wo = c && c.widgetOptions; + if (table && c) { + c.$headers.each(function(i){ + $t = $(this); + if (wo.resizable_widths[i]) { + $t.css('width', wo.resizable_widths[i]); + } else if (!$t.hasClass('resizable-false')) { + // don't clear the width of any column that is not resizable + $t.css('width',''); + } + }); + if (ts.storage && !nosave) { ts.storage(this, 'tablesorter-resizable', {}); } + } }); }; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index 3718721..89d6279 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Column Selector/Responsive table widget (beta) for TableSorter 12/17/2013 (v2.15.0) +/* Column Selector/Responsive table widget (beta) for TableSorter 3/31/2014 (v2.15.12) * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -83,7 +83,7 @@ tsColSel = ts.columnSelector = { // set default state; storage takes priority colSel.states[colId] = saved && typeof(saved[colId]) !== 'undefined' ? saved[colId] : typeof(wo.columnSelector_columns[colId]) !== 'undefined' ? - wo.columnSelector_columns[colId] : state === 'true' || !(state === 'false'); + wo.columnSelector_columns[colId] : (state === 'true' || !(state === 'false')); colSel.$column[colId] = $(this); // set default col title @@ -96,7 +96,7 @@ tsColSel = ts.columnSelector = { .find('input').add( colSel.$wrapper[colId].filter('input') ) .attr('data-column', colId) .prop('checked', colSel.states[colId]) - .bind('change', function(){ + .on('change', function(){ colSel.states[colId] = this.checked; tsColSel.updateCols(c, wo); }).change(); @@ -114,7 +114,7 @@ tsColSel = ts.columnSelector = { colSel.lastIndex = -1; wo.columnSelector_breakpoints.sort(); tsColSel.updateBreakpoints(c, wo); - c.$table.unbind('updateAll' + namespace).bind('updateAll' + namespace, function(){ + c.$table.off('updateAll' + namespace).on('updateAll' + namespace, function(){ tsColSel.updateBreakpoints(c, wo); tsColSel.updateCols(c, wo); }); @@ -129,7 +129,7 @@ tsColSel = ts.columnSelector = { .find('input').add( colSel.$auto.filter('input') ) .attr('data-column', 'auto') .prop('checked', colSel.auto) - .bind('change', function(){ + .on('change', function(){ colSel.auto = this.checked; $.each( colSel.$checkbox, function(i, $cb){ if ($cb) { @@ -156,7 +156,7 @@ tsColSel = ts.columnSelector = { }).change(); } // Add a bind on update to re-run col setup - c.$table.unbind('update' + namespace).bind('update' + namespace, function() { + c.$table.off('update' + namespace).on('update' + namespace, function() { tsColSel.updateCols(c, wo); }); } @@ -298,7 +298,7 @@ ts.addWidget({ csel.$popup.empty(); csel.$style.remove(); csel.$breakpoints.remove(); - c.$table.unbind('updateAll' + namespace + ',update' + namespace); + c.$table.off('updateAll' + namespace + ' update' + namespace); } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 72b7a5d..43e879c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/* Pager widget (beta) for TableSorter 3/18/2014 (v2.15.11) */ +/* Pager widget (beta) for TableSorter 3/31/2014 (v2.15.12) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ "use strict"; @@ -181,7 +181,7 @@ tsp = ts.pager = { } else { p.ajax = false; // Regular pager; all rows stored in memory - c.$table.trigger("appendCache", true); + c.$table.trigger("appendCache", [{}, true]); tsp.hideRowsSetup(table, c); } @@ -400,6 +400,7 @@ tsp = ts.pager = { hideRows: function(table, c){ if (!c.widgetOptions.pager_ajaxUrl) { var i, + lastIndex = 0, p = c.pager, wo = c.widgetOptions, rows = c.$tbodies.eq(0).children(), @@ -410,9 +411,23 @@ tsp = ts.pager = { j = 0; // size counter for ( i = 0; i < l; i++ ){ if ( !rows[i].className.match(f) ) { - rows[i].style.display = ( j >= s && j < e ) ? '' : 'none'; - // don't count child rows - j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) && !wo.pager_countChildRows ? 0 : 1; + if (j === s && rows[i].className.match(c.cssChildRow)) { + // hide child rows @ start of pager (if already visible) + rows[i].style.display = 'none'; + } else { + rows[i].style.display = ( j >= s && j < e ) ? '' : 'none'; + // don't count child rows + j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) && !wo.pager_countChildRows ? 0 : 1; + if ( j === e && rows[i].style.display !== 'none' && rows[i].className.match(ts.css.cssHasChild) ) { + lastIndex = i; + } + } + } + } + // add any attached child rows to last row of pager. Fixes part of issue #396 + if ( lastIndex > 0 && rows[lastIndex].className.match(ts.css.cssHasChild) ) { + while ( ++lastIndex < l && rows[lastIndex].className.match(c.cssChildRow) ) { + rows[lastIndex].style.display = ''; } } } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css index 62ca0ff..a5911aa 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css @@ -173,6 +173,10 @@ caption { -o-transition: height 0.1s ease; transition: height 0.1s ease; } +/* rows hidden by filtering (needed for child rows) */ +.tablesorter .filtered { + display: none; +} /* ajax error row */ .tablesorter .tablesorter-errorRow td { diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css index 1990f7d..fdd0a6b 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css @@ -208,6 +208,10 @@ caption { -o-transition: height 0.1s ease; transition: height 0.1s ease; } +/* rows hidden by filtering (needed for child rows) */ +.tablesorter .filtered { + display: none; +} /* ajax error row */ .tablesorter .tablesorter-errorRow td { diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index e32e3ed..e1a30f7 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -118,6 +118,10 @@ opacity: 0; filter: alpha(opacity=0); } +/* rows hidden by filtering (needed for child rows) */ +.tablesorter .filtered { + display: none; +} /* pager plugin */ .tablesorter-bootstrap .tablesorter-pager select { diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css index c927014..1ecd8d1 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css @@ -123,6 +123,10 @@ caption { opacity: 0; filter: alpha(opacity=0); } +/* rows hidden by filtering (needed for child rows) */ +.tablesorter .filtered { + display: none; +} /* pager plugin */ .tablesorter-bootstrap .tablesorter-pager select { diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css index e887a6f..091d7f7 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css @@ -174,6 +174,10 @@ caption { -o-transition: height 0.1s ease; transition: height 0.1s ease; } +/* rows hidden by filtering (needed for child rows) */ +.tablesorter .filtered { + display: none; +} /* ajax error row */ .tablesorter .tablesorter-errorRow td { diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css index 0110931..468df0a 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css @@ -176,6 +176,10 @@ caption { -o-transition: height 0.1s ease; transition: height 0.1s ease; } +/* rows hidden by filtering (needed for child rows) */ +.tablesorter .filtered { + display: none; +} /* ajax error row */ .tablesorter .tablesorter-errorRow td { diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css index f6cf8bf..e145c6d 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css @@ -199,6 +199,10 @@ caption { -o-transition: height 0.1s ease; transition: height 0.1s ease; } +/* rows hidden by filtering (needed for child rows) */ +.tablesorter .filtered { + display: none; +} /* ajax error row */ .tablesorter .tablesorter-errorRow td { diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css index c9a5a15..c7dc6e2 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css @@ -190,6 +190,10 @@ caption { -o-transition: height 0.1s ease; transition: height 0.1s ease; } +/* rows hidden by filtering (needed for child rows) */ +.tablesorter .filtered { + display: none; +} /* ajax error row */ .tablesorter .tablesorter-errorRow td { diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css index 6ee1b3f..ae62ee7 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css @@ -232,6 +232,10 @@ caption { -o-transition: height 0.1s ease; transition: height 0.1s ease; } +/* rows hidden by filtering (needed for child rows) */ +.tablesorter .filtered { + display: none; +} /* ajax error row */ .tablesorter .tablesorter-errorRow td { diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css index 6f73279..9150a92 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css @@ -188,6 +188,10 @@ caption { -o-transition: height 0.1s ease; transition: height 0.1s ease; } +/* rows hidden by filtering (needed for child rows) */ +.tablesorter .filtered { + display: none; +} /* ajax error row */ .tablesorter .tablesorter-errorRow td { diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css index 24538e9..f15eb9a 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css @@ -141,6 +141,10 @@ -o-transition: height 0.1s ease; transition: height 0.1s ease; } +/* rows hidden by filtering (needed for child rows) */ +.tablesorter .filtered { + display: none; +} /* ajax error row */ .tablesorter .tablesorter-errorRow td { From 3a057b11c14283ba0d6aacdeee7e89cf15022a6d Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Thu, 3 Apr 2014 21:52:09 +0200 Subject: [PATCH 025/138] * updated tablesorter to latest version (2.15.13) --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery-tablesorter/jquery.tablesorter.js | 12 ++++++++---- .../jquery-tablesorter/jquery.tablesorter.widgets.js | 6 +++--- .../jquery-tablesorter/theme.bootstrap.css | 10 +++++++--- 7 files changed, 25 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8b8f99..9e46279 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.10.9 (2014-04-03) + +* Upgrade tablesorter to v2.15.13 + #### v1.10.8 (2014-03-31) * Upgrade tablesorter to v2.15.12 diff --git a/README.md b/README.md index 4aab58f..b8275ba 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.15.12 (3/31/2014), [documentation] +Current tablesorter version: 2.15.13 (4/3/2014), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 2c1e719..e22379f 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.10.8' + VERSION = '1.10.9' end diff --git a/tablesorter b/tablesorter index 4a984eb..44abae9 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 4a984eb68124be1c1170d116c6f6a2d84979963f +Subproject commit 44abae9814328fdf81dc855ad2458800335c2a3d diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 89b7f61..7a9bdd6 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.15.12 - Client-side table sorting with ease! +* TableSorter 2.15.13 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.15.12"; + ts.version = "2.15.13"; ts.parsers = []; ts.widgets = []; @@ -290,7 +290,8 @@ c = $(b[k].rows[i]); cols = []; // if this is a child row, add it to the last row's children and continue to the next row - if (c.hasClass(tc.cssChildRow)) { + // ignore child row class, if it is the first row + if (c.hasClass(tc.cssChildRow) && i !== 0) { tc.cache[k].row[tc.cache[k].row.length - 1] = tc.cache[k].row[tc.cache[k].row.length - 1].add(c); // add "hasChild" class name to parent row if (!c.prev().hasClass(tc.cssChildRow)) { @@ -785,6 +786,7 @@ resortComplete($table, callback); }, true]); } else { + $table.trigger('applyWidgets'); resortComplete($table, callback); } } @@ -891,7 +893,9 @@ // sort the table and append it to the dom multisort(table); appendToTable(table, init); - $table.trigger("sortEnd", this); + $table + .trigger("sortEnd", this) + .trigger('applyWidgets'); if (typeof callback === "function") { callback(table); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index a081851..d9fe769 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.15+ widgets - updated 3/31/2014 (v2.15.12) +/*! tableSorter 2.15+ widgets - updated 4/3/2014 (v2.15.13) * * Column Styles * Column Filters @@ -851,7 +851,7 @@ ts.filter = { c = table.config, wo = c.widgetOptions, columns = c.columns, - $tbodies = c.$tbodies, + $tbodies = c.$table.children('tbody'), // target all tbodies #568 // anyMatch really screws up with these types of filters anyMatchNotAllowedTypes = [ 'range', 'notMatch', 'operators' ], // parse columns after formatter, in case the class is added at that point @@ -862,7 +862,7 @@ ts.filter = { }).get(); if (c.debug) { time = new Date(); } for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { - if ($tbodies.eq(tbodyIndex).hasClass(ts.css.info)) { continue; } // ignore info blocks, issue #264 + if ($tbodies.eq(tbodyIndex).hasClass(c.cssInfoBlock || ts.css.info)) { continue; } // ignore info blocks, issue #264 $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // skip child rows & widget added (removable) rows - fixes #448 thanks to @hempel! $rows = $tbody.children('tr').not(c.selectorRemove); diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index e1a30f7..acae1b8 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -41,14 +41,18 @@ } /* since bootstrap (table-striped) uses nth-child(), we just use this to add a zebra stripe color */ -.tablesorter-bootstrap > tbody > tr.odd > td { +.tablesorter-bootstrap > tbody > tr.odd > td, +.tablesorter-bootstrap > tbody > tr.tablesorter-hasChildRow.odd:hover ~ tr.tablesorter-hasChildRow.odd ~ .tablesorter-childRow.odd > td { background-color: #f9f9f9; } .tablesorter-bootstrap > tbody > tr.odd:hover > td, -.tablesorter-bootstrap > tbody > tr.even:hover > td { +.tablesorter-bootstrap > tbody > tr.even:hover > td, +.tablesorter-bootstrap > tbody > tr.tablesorter-hasChildRow.odd:hover ~ .tablesorter-childRow.odd > td, +.tablesorter-bootstrap > tbody > tr.tablesorter-hasChildRow.even:hover ~ .tablesorter-childRow.even > td { background-color: #f5f5f5; } -.tablesorter-bootstrap > tbody > tr.even > td { +.tablesorter-bootstrap > tbody > tr.even > td, +.tablesorter-bootstrap > tbody > tr.tablesorter-hasChildRow.even:hover ~ tr.tablesorter-hasChildRow.even ~ .tablesorter-childRow.even > td { background-color: #fff; } From 487e2f3dcf12e9d90f1b1efe13fd6c682665d770 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Thu, 10 Apr 2014 22:16:17 +0200 Subject: [PATCH 026/138] * updated tablesorter to latest version (2.15.14) --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 11 ++++++----- .../jquery-tablesorter/jquery.tablesorter.js | 17 +++++++++++------ .../jquery.tablesorter.widgets.js | 2 +- .../widgets/widget-grouping.js | 7 +++---- .../jquery-tablesorter/widgets/widget-pager.js | 11 ++++++----- .../jquery-tablesorter/theme.bootstrap.css | 3 ++- .../jquery-tablesorter/theme.jui.css | 8 +++++--- 11 files changed, 41 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e46279..43f59a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.10.10 (2014-04-10) + +* Upgrade tablesorter to v2.15.14 + #### v1.10.9 (2014-04-03) * Upgrade tablesorter to v2.15.13 diff --git a/README.md b/README.md index b8275ba..9ea5101 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.15.13 (4/3/2014), [documentation] +Current tablesorter version: 2.15.14 (4/10/2014), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index e22379f..89fc7fe 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.10.9' + VERSION = '1.10.10' end diff --git a/tablesorter b/tablesorter index 44abae9..8f88f72 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 44abae9814328fdf81dc855ad2458800335c2a3d +Subproject commit 8f88f7289957fc4b09743c665b8add84eb561938 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index c155562..54d7d38 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 3/31/2014 (v2.15.12) + * updated 4/10/2014 (v2.15.14) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -297,8 +297,10 @@ } l = d.length; if (d instanceof jQuery) { - // append jQuery object - c.$tbodies.eq(0).empty().append(d); + if (p.processAjaxOnInit) { + // append jQuery object + c.$tbodies.eq(0).empty().append(d); + } } else if (l) { // build table from array for ( i = 0; i < l; i++ ) { @@ -312,10 +314,9 @@ // add rows to first tbody if (p.processAjaxOnInit) { c.$tbodies.eq(0).html( tds ); - } else { - p.processAjaxOnInit = true; } } + p.processAjaxOnInit = true; // only add new header text if the length matches if ( th && th.length === hl ) { hsh = $t.hasClass('hasStickyHeaders'); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 7a9bdd6..aa788ca 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.15.13 - Client-side table sorting with ease! +* TableSorter 2.15.14 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.15.13"; + ts.version = "2.15.14"; ts.parsers = []; ts.widgets = []; @@ -1165,6 +1165,11 @@ $h = $t.find('thead:first'), $r = $h.find('tr.' + ts.css.headerRow).removeClass(ts.css.headerRow + ' ' + c.cssHeaderRow), $f = $t.find('tfoot:first > tr').children('th, td'); + if (removeClasses === false && $.inArray('uitheme', c.widgets) >= 0) { + // reapply uitheme classes, in case we want to maintain appearance + $t.trigger('applyWidgetId', ['uitheme']); + $t.trigger('applyWidgetId', ['zebra']); + } // remove widget added rows, just in case $h.find('tr').not($r).remove(); // disable tablesorter @@ -1173,12 +1178,12 @@ .unbind('sortReset update updateAll updateRows updateCell addRows updateComplete sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress sortBegin sortEnd '.split(' ').join(c.namespace + ' ')); c.$headers.add($f) .removeClass( [ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone].join(' ') ) - .removeAttr('data-column'); + .removeAttr('data-column') + .removeAttr('aria-label') + .attr('aria-disabled', 'true'); $r.find(c.selectorSort).unbind('mousedown mouseup keypress '.split(' ').join(c.namespace + ' ')); ts.restoreHeaders(table); - if (removeClasses !== false) { - $t.removeClass(ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme); - } + $t.toggleClass(ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme, removeClasses === false); // clear flag in case the plugin is initialized again table.hasInitialized = false; if (typeof callback === 'function') { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index d9fe769..e992a13 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1102,7 +1102,7 @@ ts.getFilters = function(table, getRaw, setFilters, skipFirst) { var i, $filters, $column, filters = false, c = table ? $(table)[0].config : '', - wo = table ? c.widgetOptions : ''; + wo = c ? c.widgetOptions : ''; if (getRaw !== true && wo && !wo.filter_columnFilters) { return $(table).data('lastSearch'); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index b5a6b80..7813b36 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -110,7 +110,6 @@ ts.grouping = { currentGroup = wo.group_formatter((currentGroup || '').toString(), column, table, c, wo) || currentGroup; } $rows.eq(rowIndex).before('<tr class="group-header ' + c.selectorRemove.slice(1) + - // (wo.group_collapsed && wo.group_collapsible ? ' collapsed' : '') + '" unselectable="on"><td colspan="' + c.columns + '">' + (wo.group_collapsible ? '<i/>' : '') + '<span class="group-name">' + currentGroup + '</span><span class="group-count"></span></td></tr>'); @@ -126,9 +125,8 @@ ts.grouping = { c.$table.find('tr.group-header') .bind('selectstart', false) .each(function(){ - var isHidden, $label, + var isHidden, $label, name, $row = $(this), - name = $row.find('.group-name').text().toLowerCase(), $rows = $row.nextUntil('tr.group-header').filter(':visible'); if (wo.group_count || $.isFunction(wo.group_callback)) { $label = $row.find('.group-count'); @@ -142,6 +140,7 @@ ts.grouping = { } } if (wo.group_saveGroups && wo.group_currentGroups[wo.group_currentGroup].length) { + name = $row.find('.group-name').text().toLowerCase(); isHidden = $.inArray( name, wo.group_currentGroups[wo.group_currentGroup] ) > -1; $row.toggleClass('collapsed', isHidden); $rows.toggleClass('group-hidden', isHidden); @@ -165,7 +164,7 @@ ts.grouping = { event.stopPropagation(); var isCollapsed, $groups, indx, $this = $(this), - name = $this.find('.group-name').text(); + name = $this.find('.group-name').text().toLowerCase(); // use shift-click to toggle ALL groups if (event.type === 'click' && event.shiftKey) { $this.siblings('.group-header').trigger('toggleGroup'); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 43e879c..5d24257 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/* Pager widget (beta) for TableSorter 3/31/2014 (v2.15.12) */ +/* Pager widget (beta) for TableSorter 4/10/2014 (v2.15.14) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ "use strict"; @@ -486,8 +486,10 @@ tsp = ts.pager = { } l = d.length; if (d instanceof jQuery) { - // append jQuery object - c.$tbodies.eq(0).empty().append(d); + if (wo.pager_processAjaxOnInit) { + // append jQuery object + c.$tbodies.eq(0).empty().append(d); + } } else if (l) { // build table from array for ( i = 0; i < l; i++ ) { @@ -501,10 +503,9 @@ tsp = ts.pager = { // add rows to first tbody if (wo.pager_processAjaxOnInit) { c.$tbodies.eq(0).html( tds ); - } else { - wo.pager_processAjaxOnInit = true; } } + wo.pager_processAjaxOnInit = true; // only add new header text if the length matches if ( th && th.length === hl ) { hsh = $t.hasClass('hasStickyHeaders'); diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index acae1b8..cab2b0d 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -5,7 +5,8 @@ .tablesorter-bootstrap { width: 100%; } -.tablesorter-bootstrap .tablesorter-header, +.tablesorter-bootstrap thead th, +.tablesorter-bootstrap thead td, .tablesorter-bootstrap tfoot th, .tablesorter-bootstrap tfoot td { font: bold 14px/20px Arial, Sans-serif; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css index f15eb9a..ea33277 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css @@ -12,8 +12,10 @@ } /* header */ -.tablesorter-jui th, -.tablesorter-jui thead td { +.tablesorter-jui thead th, +.tablesorter-jui thead td, +.tablesorter-jui tfoot th, +.tablesorter-jui tfoot td { position: relative; background-repeat: no-repeat; background-position: right center; @@ -21,12 +23,12 @@ font-weight: bold !important; border-width: 1px !important; text-align: left; + padding: 8px; /* wider than the icon */ } .tablesorter-jui .header, .tablesorter-jui .tablesorter-header { cursor: pointer; white-space: normal; - padding: 8px; /* wider than the icon */ } .tablesorter-jui .tablesorter-header-inner { padding-right: 20px; From 369b912608270791b9392c050929cbc3cfed2636 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Thu, 24 Apr 2014 20:52:53 +0200 Subject: [PATCH 027/138] * updated tablesorter to latest version (2.16.1) --- CHANGELOG.md | 4 + README.md | 2 +- jquery-tablesorter.gemspec | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 65 ++- .../jquery-tablesorter/jquery.tablesorter.js | 373 ++++++++++-------- ...sorter.widgets-filter-formatter-select2.js | 134 +++++++ ...ry.tablesorter.widgets-filter-formatter.js | 136 +++---- .../jquery.tablesorter.widgets.js | 149 ++++--- .../parsers/parser-date-extract.js | 82 ++++ .../widgets/widget-build-table.js | 22 +- .../widgets/widget-cssStickyHeaders.js | 2 +- .../jquery-tablesorter/widgets/widget-math.js | 359 +++++++++++++++++ .../widgets/widget-output.js | 272 +++++++++++++ .../widgets/widget-pager.js | 67 +++- .../widgets/widget-reflow.js | 179 +++++++++ .../widgets/widget-scroller.js | 63 +-- .../widgets/widget-staticRow.js | 123 ++++++ 19 files changed, 1662 insertions(+), 376 deletions(-) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-reflow.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 43f59a8..bf8c674 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.11.0 (2014-04-24) + +* Upgrade tablesorter to v2.16.1 + #### v1.10.10 (2014-04-10) * Upgrade tablesorter to v2.15.14 diff --git a/README.md b/README.md index 9ea5101..47a2124 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.15.14 (4/10/2014), [documentation] +Current tablesorter version: 2.16.1 (4/24/2014), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. diff --git a/jquery-tablesorter.gemspec b/jquery-tablesorter.gemspec index 9d74ef3..b1d9955 100644 --- a/jquery-tablesorter.gemspec +++ b/jquery-tablesorter.gemspec @@ -14,7 +14,7 @@ Gem::Specification.new do |s| s.description = 'Simple integration of jquery-tablesorter into the asset pipeline.' s.license = 'MIT' - s.files = Dir["{vendor,lib}/**/*"] + %w( MIT-LICENSE Rakefile README.md ) + s.files = Dir['{vendor,lib}/**/*'] + %w( MIT-LICENSE Rakefile README.md ) s.add_dependency 'railties', '>= 3.1', '< 5' end diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 89fc7fe..5a62f49 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.10.10' + VERSION = '1.11.0' end diff --git a/tablesorter b/tablesorter index 8f88f72..e658868 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 8f88f7289957fc4b09743c665b8add84eb561938 +Subproject commit e658868573097c6ecb9eedfaf42ee62faf7a816b diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 54d7d38..fa2c711 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 4/10/2014 (v2.15.14) + * updated 4/23/2014 (v2.16.0) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -58,6 +58,10 @@ // starting page of the pager (zero based index) page: 0, + // reset pager after filtering; set to desired page # + // set to false to not change page at filter start + pageReset: 0, + // Number of visible rows size: 10, @@ -125,15 +129,23 @@ }, updatePageDisplay = function(table, p, completed) { - var i, pg, s, out, + var i, pg, s, out, regex, c = table.config, f = c.$table.hasClass('hasFilters') && !p.ajaxUrl, - t = (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered') + ',' + c.selectorRemove + - (p.countChildRows ? '' : ',.' + c.cssChildRow), + t = [], sz = p.size || 10; // don't allow dividing by zero + t = [ (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered'), c.selectorRemove ]; + if (p.countChildRows) { t.push(c.cssChildRow); } + regex = new RegExp( '(' + t.join('|') + ')' ); p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method - p.filteredRows = (f) ? c.$tbodies.eq(0).children('tr').not('.' + t ).length : p.totalRows; - p.filteredPages = (f) ? Math.ceil( p.filteredRows / sz ) || 1 : p.totalPages; + p.filteredRows = (f) ? 0 : p.totalRows; + p.filteredPages = p.totalPages; + if (f) { + $.each(c.cache[0].normalized, function(i, el) { + p.filteredRows += p.regexRows.test(el[c.columns].$row[0].className) ? 0 : 1; + }); + p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; + } if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) { t = (p.size * p.page > p.filteredRows); p.startRow = (t) ? 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1); @@ -448,12 +460,13 @@ }, renderTable = function(table, rows, p) { - var i, $tb, + var $tb, index, count, added, $t = $(table), c = table.config, + f = c.$table.hasClass('hasFilters'), l = rows && rows.length || 0, // rows may be undefined s = ( p.page * p.size ), - e = ( s + p.size ); + e = p.size; if ( l < 1 ) { return; } // empty table, abort! if ( p.page >= p.totalPages ) { // lets not render the table more than once @@ -465,18 +478,25 @@ if ( !p.removeRows ) { hideRows(table, p); } else { - if ( e > rows.length ) { - e = rows.length; - } ts.clearTableBody(table); $tb = ts.processTbody(table, c.$tbodies.eq(0), true); - for ( i = s; i < e; i++ ) { - $tb.append(rows[i]); + // not filtered, start from the calculated starting point (s) + // if filtered, start from zero + index = f ? 0 : s; + count = f ? 0 : s; + added = 0; + while (added < e && index < rows.length) { + if (!f || !/filtered/.test(rows[index][0].className)){ + count++; + if (count > s && added <= e) { + added++; + $tb.append(rows[index]); + } + } + index++; } - ts.processTbody(table, $tb, false); } - updatePageDisplay(table, p); if ( !p.isDisabled ) { fixHeight(table, p); } $t.trigger('applyWidgets'); @@ -594,6 +614,7 @@ p.$container.hide(); // hide pager table.config.appender = null; // remove pager appender function p.initialized = false; + delete table.config.rowsCopy; $(table).unbind('destroy.pager sortEnd.pager filterEnd.pager enable.pager disable.pager'); if (ts.storage) { ts.storage(table, p.storageKey, ''); @@ -647,7 +668,8 @@ var t, ctrls, fxn, table = this, c = table.config, - p = c.pager = $.extend( {}, $.tablesorterPager.defaults, settings ), + wo = c.widgetOptions, + p = c.pager = $.extend( true, {}, $.tablesorterPager.defaults, settings ), $t = c.$table, // added in case the pager is reinitialized after being destroyed. pager = p.$container = $(p.container).addClass('tablesorter-pager').show(); @@ -669,17 +691,24 @@ $.data(table, 'pagerLastSize', p.size); } + // skipped rows + p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.substring(1) + '|' + c.cssChildRow + ')'); + $t .unbind('filterStart filterEnd sortEnd disable enable destroy update updateRows updateAll addRows pageSize '.split(' ').join('.pager ')) .bind('filterStart.pager', function(e, filters) { p.currentFilters = filters; - p.page = 0; // fixes #456 + // don't change page is filters are the same (pager updating, etc) + if (p.pageReset !== false && (c.lastCombinedFilter || '') !== (filters || []).join('')) { + p.page = p.pageReset; // fixes #456 & #565 + } }) // update pager after filter widget completes .bind('filterEnd.pager sortEnd.pager', function() { if (p.initialized) { - moveToPage(table, p, false); + // update page display first, so we update p.filteredPages updatePageDisplay(table, p, false); + moveToPage(table, p, false); fixHeight(table, p); } }) diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index aa788ca..c1796ae 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.15.14 - Client-side table sorting with ease! +* TableSorter 2.16.1 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.15.14"; + ts.version = "2.16.1"; ts.parsers = []; ts.widgets = []; @@ -64,7 +64,8 @@ emptyTo : 'bottom', // sort empty cell to bottom, top, none, zero stringTo : 'max', // sort strings in numerical column as max, min, top, bottom, zero - textExtraction : 'simple', // text extraction method/function - function(node, table, cellIndex){} + textExtraction : 'basic', // text extraction method/function - function(node, table, cellIndex){} + textAttribute : 'data-text',// data-attribute that contains alternate cell text (used in textExtraction function) textSorter : null, // choose overall or specific column sorter function(a, b, direction, table, columnIndex) [alt: ts.sortText] numberSorter : null, // choose overall numeric sorter function(a, b, direction, maxColumnValue) @@ -167,20 +168,19 @@ function getElementText(table, node, cellIndex) { if (!node) { return ""; } var c = table.config, - t = c.textExtraction, text = ""; - if (t === "simple") { - if (c.supportsTextContent) { - text = node.textContent; // newer browsers support this - } else { - text = $(node).text(); - } + t = c.textExtraction || '', + text = ""; + if (t === "basic") { + // check data-attribute first + text = $(node).attr(c.textAttribute) || node.textContent || node.innerText || $(node).text() || ""; } else { - if (typeof t === "function") { + if (typeof(t) === "function") { text = t(node, table, cellIndex); - } else if (typeof t === "object" && t.hasOwnProperty(cellIndex)) { + } else if (typeof(t) === "object" && t.hasOwnProperty(cellIndex)) { text = t[cellIndex](node, table, cellIndex); } else { - text = c.supportsTextContent ? node.textContent : $(node).text(); + // previous "simple" method + text = node.textContent || node.innerText || $(node).text() || ""; } } return $.trim(text); @@ -219,41 +219,47 @@ var c = table.config, // update table bodies in case we start with an empty table tb = c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'), - rows, list, l, i, h, ch, p, time, parsersDebug = ""; - if ( tb.length === 0) { + rows, list, l, i, h, ch, p, time, + j = 0, + parsersDebug = "", + len = tb.length; + if ( len === 0) { return c.debug ? log('Warning: *Empty table!* Not building a parser cache') : ''; } else if (c.debug) { time = new Date(); log('Detecting parsers for each column'); } - rows = tb[0].rows; - if (rows[0]) { - list = []; - l = rows[0].cells.length; - for (i = 0; i < l; i++) { - // tons of thanks to AnthonyM1229 for working out the following selector (issue #74) to make this work in IE8! - // More fixes to this selector to work properly in iOS and jQuery 1.8+ (issue #132 & #174) - h = c.$headers.filter(':not([colspan])'); - h = h.add( c.$headers.filter('[colspan="1"]') ) // ie8 fix - .filter('[data-column="' + i + '"]:last'); - ch = c.headers[i]; - // get column parser - p = ts.getParserById( ts.getData(h, ch, 'sorter') ); - // empty cells behaviour - keeping emptyToBottom for backwards compatibility - c.empties[i] = ts.getData(h, ch, 'empty') || c.emptyTo || (c.emptyToBottom ? 'bottom' : 'top' ); - // text strings behaviour in numerical sorts - c.strings[i] = ts.getData(h, ch, 'string') || c.stringTo || 'max'; - if (!p) { - p = detectParserForColumn(table, rows, -1, i); - } - if (c.debug) { - parsersDebug += "column:" + i + "; parser:" + p.id + "; string:" + c.strings[i] + '; empty: ' + c.empties[i] + "\n"; + list = []; + while (j < len) { + rows = tb[j].rows; + if (rows[j]) { + l = rows[j].cells.length; + for (i = 0; i < l; i++) { + // tons of thanks to AnthonyM1229 for working out the following selector (issue #74) to make this work in IE8! + // More fixes to this selector to work properly in iOS and jQuery 1.8+ (issue #132 & #174) + h = c.$headers.filter(':not([colspan])'); + h = h.add( c.$headers.filter('[colspan="1"]') ) // ie8 fix + .filter('[data-column="' + i + '"]:last'); + ch = c.headers[i]; + // get column parser + p = ts.getParserById( ts.getData(h, ch, 'sorter') ); + // empty cells behaviour - keeping emptyToBottom for backwards compatibility + c.empties[i] = ts.getData(h, ch, 'empty') || c.emptyTo || (c.emptyToBottom ? 'bottom' : 'top' ); + // text strings behaviour in numerical sorts + c.strings[i] = ts.getData(h, ch, 'string') || c.stringTo || 'max'; + if (!p) { + p = detectParserForColumn(table, rows, -1, i); + } + if (c.debug) { + parsersDebug += "column:" + i + "; parser:" + p.id + "; string:" + c.strings[i] + '; empty: ' + c.empties[i] + "\n"; + } + list.push(p); } - list.push(p); } + j += (list.length) ? len : 1; } if (c.debug) { - log(parsersDebug); + log(parsersDebug ? parsersDebug : "No parsers detected"); benchmark("Completed detecting parsers", time); } c.parsers = list; @@ -261,72 +267,87 @@ /* utils */ function buildCache(table) { - var b = table.tBodies, - tc = table.config, - totalRows, - totalCells, - parsers = tc.parsers, - t, v, i, j, k, c, cols, cacheTime, colMax = []; - tc.cache = {}; + var cc, t, v, i, j, k, $row, rows, cols, cacheTime, + totalRows, rowData, colMax, + c = table.config, + $tb = c.$table.children('tbody'), + parsers = c.parsers; + c.cache = {}; // if no parsers found, return - it's an empty table. if (!parsers) { - return tc.debug ? log('Warning: *Empty table!* Not building a cache') : ''; + return c.debug ? log('Warning: *Empty table!* Not building a cache') : ''; } - if (tc.debug) { + if (c.debug) { cacheTime = new Date(); } // processing icon - if (tc.showProcessing) { + if (c.showProcessing) { ts.isProcessing(table, true); } - for (k = 0; k < b.length; k++) { - tc.cache[k] = { row: [], normalized: [] }; + for (k = 0; k < $tb.length; k++) { + colMax = []; // column max value per tbody + cc = c.cache[k] = { + normalized: [] // array of normalized row data; last entry contains "rowData" above + // colMax: # // added at the end + }; + // ignore tbodies with class name from c.cssInfoBlock - if (!$(b[k]).hasClass(tc.cssInfoBlock)) { - totalRows = (b[k] && b[k].rows.length) || 0; - totalCells = (b[k].rows[0] && b[k].rows[0].cells.length) || 0; + if (!$tb.eq(k).hasClass(c.cssInfoBlock)) { + totalRows = ($tb[k] && $tb[k].rows.length) || 0; for (i = 0; i < totalRows; ++i) { + rowData = { + // order: original row order # + // $row : jQuery Object[] + child: [] // child row text (filter widget) + }; /** Add the table data to main data array */ - c = $(b[k].rows[i]); + $row = $($tb[k].rows[i]); + rows = [ new Array(c.columns) ]; cols = []; // if this is a child row, add it to the last row's children and continue to the next row // ignore child row class, if it is the first row - if (c.hasClass(tc.cssChildRow) && i !== 0) { - tc.cache[k].row[tc.cache[k].row.length - 1] = tc.cache[k].row[tc.cache[k].row.length - 1].add(c); + if ($row.hasClass(c.cssChildRow) && i !== 0) { + t = cc.normalized.length - 1; + cc.normalized[t][c.columns].$row = cc.normalized[t][c.columns].$row.add($row); // add "hasChild" class name to parent row - if (!c.prev().hasClass(tc.cssChildRow)) { - c.prev().addClass(ts.css.cssHasChild); + if (!$row.prev().hasClass(c.cssChildRow)) { + $row.prev().addClass(ts.css.cssHasChild); } + // save child row content (un-parsed!) + rowData.child[t] = $.trim( $row[0].textContent || $row[0].innerText || $row.text() || "" ); // go to the next for loop continue; } - tc.cache[k].row.push(c); - for (j = 0; j < totalCells; ++j) { + rowData.$row = $row; + rowData.order = i; // add original row position to rowCache + for (j = 0; j < c.columns; ++j) { if (typeof parsers[j] === 'undefined') { - if (tc.debug) { - log('No parser found for cell:', c[0].cells[j], 'does it have a header?'); + if (c.debug) { + log('No parser found for cell:', $row[0].cells[j], 'does it have a header?'); } continue; } - t = getElementText(table, c[0].cells[j], j); + t = getElementText(table, $row[0].cells[j], j); // allow parsing if the string is empty, previously parsing would change it to zero, // in case the parser needs to extract data from the table cell attributes - v = parsers[j].format(t, table, c[0].cells[j], j); + v = parsers[j].format(t, table, $row[0].cells[j], j); cols.push(v); if ((parsers[j].type || '').toLowerCase() === "numeric") { - colMax[j] = Math.max(Math.abs(v) || 0, colMax[j] || 0); // determine column max value (ignore sign) + // determine column max value (ignore sign) + colMax[j] = Math.max(Math.abs(v) || 0, colMax[j] || 0); } } - cols.push(tc.cache[k].normalized.length); // add position for rowCache - tc.cache[k].normalized.push(cols); + // ensure rowData is always in the same location (after the last column) + cols[c.columns] = rowData; + cc.normalized.push(cols); } - tc.cache[k].colMax = colMax; + cc.colMax = colMax; } } - if (tc.showProcessing) { + if (c.showProcessing) { ts.isProcessing(table); // remove processing icon } - if (tc.debug) { + if (c.debug) { benchmark("Building cache for " + totalRows + " rows", cacheTime); } } @@ -337,11 +358,11 @@ wo = c.widgetOptions, b = table.tBodies, rows = [], - c2 = c.cache, - r, n, totalRows, checkCell, $bk, $tb, - i, j, k, l, pos, appendTime; + cc = c.cache, + n, totalRows, $bk, $tb, + i, k, appendTime; // empty table - fixes #206/#346 - if (isEmptyObject(c2)) { + if (isEmptyObject(cc)) { // run pager appender in case the table was just emptied return c.appender ? c.appender(table, rows) : table.isUpdating ? c.$table.trigger("updateComplete", table) : ''; // Fixes #532 @@ -354,19 +375,13 @@ if ($bk.length && !$bk.hasClass(c.cssInfoBlock)) { // get tbody $tb = ts.processTbody(table, $bk, true); - r = c2[k].row; - n = c2[k].normalized; + n = cc[k].normalized; totalRows = n.length; - checkCell = totalRows ? (n[0].length - 1) : 0; for (i = 0; i < totalRows; i++) { - pos = n[i][checkCell]; - rows.push(r[pos]); + rows.push(n[i][c.columns].$row); // removeRows used by the pager plugin; don't render if using ajax - fixes #411 if (!c.appender || (c.pager && (!c.pager.removeRows || !wo.pager_removeRows) && !c.pager.ajax)) { - l = r[pos].length; - for (j = 0; j < l; j++) { - $tb.append(r[pos][j]); - } + $tb.append(n[i][c.columns].$row); } } // restore tbody @@ -386,52 +401,6 @@ } } - // computeTableHeaderCellIndexes from: - // http://www.javascripttoolbox.com/lib/table/examples.php - // http://www.javascripttoolbox.com/temp/table_cellindex.html - function computeThIndexes(t) { - var matrix = [], - lookup = {}, - cols = 0, // determine the number of columns - trs = $(t).children('thead, tfoot').children('tr'), // children tr in tfoot - see issue #196 & #547 - i, j, k, l, c, cells, rowIndex, cellId, rowSpan, colSpan, firstAvailCol, matrixrow; - for (i = 0; i < trs.length; i++) { - cells = trs[i].cells; - for (j = 0; j < cells.length; j++) { - c = cells[j]; - rowIndex = c.parentNode.rowIndex; - cellId = rowIndex + "-" + $(c).index(); - rowSpan = c.rowSpan || 1; - colSpan = c.colSpan || 1; - if (typeof(matrix[rowIndex]) === "undefined") { - matrix[rowIndex] = []; - } - // Find first available column in the first row - for (k = 0; k < matrix[rowIndex].length + 1; k++) { - if (typeof(matrix[rowIndex][k]) === "undefined") { - firstAvailCol = k; - break; - } - } - lookup[cellId] = firstAvailCol; - cols = Math.max(firstAvailCol, cols); - // add data-column - $(c).attr({ 'data-column' : firstAvailCol }); // 'data-row' : rowIndex - for (k = rowIndex; k < rowIndex + rowSpan; k++) { - if (typeof(matrix[k]) === "undefined") { - matrix[k] = []; - } - matrixrow = matrix[k]; - for (l = firstAvailCol; l < firstAvailCol + colSpan; l++) { - matrixrow[l] = "x"; - } - } - } - } - // may not be accurate if # header columns !== # tbody columns - t.config.columns = cols + 1; // add one because it's a zero-based index - } - function formatSortingOrder(v) { // look for "d" in "desc" order; return true return (/^d/i.test(v) || v === 1); @@ -446,7 +415,8 @@ if (c.debug) { time = new Date(); } - computeThIndexes(table); + // children tr in tfoot - see issue #196 & #547 + c.columns = ts.computeColumnIndex( c.$table.children('thead, tfoot').children('tr') ); // add icon if cssIcon option exists i = c.cssIcon ? '<i class="' + ( c.cssIcon === ts.css.icon ? ts.css.icon : c.cssIcon + ' ' + ts.css.icon ) + '"></i>' : ''; c.$headers = $(table).find(c.selectorHeaders).each(function(index) { @@ -701,8 +671,8 @@ // sort multiple columns function multisort(table) { /*jshint loopfunc:true */ - var i, k, num, col, colMax, cache, lc, - order, orgOrderCol, sortTime, sort, x, y, + var i, k, num, col, sortTime, colMax, + cache, order, sort, x, y, dir = 0, c = table.config, cts = c.textSorter || '', @@ -716,8 +686,7 @@ for (k = 0; k < bl; k++) { colMax = c.cache[k].colMax; cache = c.cache[k].normalized; - lc = cache.length; - orgOrderCol = (cache && cache[0]) ? cache[0].length - 1 : 0; + cache.sort(function(a, b) { // cache is undefined here in IE, so don't use it! for (i = 0; i < l; i++) { @@ -727,7 +696,7 @@ dir = order === 0; if (c.sortStable && a[col] === b[col] && l === 1) { - return a[orgOrderCol] - b[orgOrderCol]; + return a[c.columns].order - b[c.columns].order; } // fallback to natural sort since it is more robust @@ -761,7 +730,7 @@ } if (sort) { return sort; } } - return a[orgOrderCol] - b[orgOrderCol]; + return a[c.columns].order - b[c.columns].order; }); } if (c.debug) { benchmark("Sorting on " + sortList.toString() + " and dir " + order + " time", sortTime); } @@ -772,7 +741,7 @@ if (table.isUpdating) { $table.trigger('updateComplete'); } - if (typeof callback === "function") { + if ($.isFunction(callback)) { callback($table[0]); } } @@ -786,8 +755,8 @@ resortComplete($table, callback); }, true]); } else { - $table.trigger('applyWidgets'); resortComplete($table, callback); + ts.applyWidget($table[0], false); } } @@ -797,12 +766,15 @@ // apply easy methods that trigger bound events $table .unbind('sortReset update updateRows updateCell updateAll addRows updateComplete sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave '.split(' ').join(c.namespace + ' ')) - .bind("sortReset" + c.namespace, function(e){ + .bind("sortReset" + c.namespace, function(e, callback){ e.stopPropagation(); c.sortList = []; setHeadersCss(table); multisort(table); appendToTable(table); + if ($.isFunction(callback)) { + callback(table); + } }) .bind("updateAll" + c.namespace, function(e, resort, callback){ e.stopPropagation(); @@ -826,20 +798,24 @@ table.isUpdating = true; $table.find(c.selectorRemove).remove(); // get position from the dom - var l, row, icell, + var v, row, icell, $tb = $table.find('tbody'), + $cell = $(cell), // update cache - format: function(s, table, cell, cellIndex) // no closest in jQuery v1.2.6 - tbdy = $tb.index( $(cell).closest('tbody') ),$row = $(cell).closest('tr'); - tbdy = $tb.index( $(cell).parents('tbody').filter(':first') ), - $row = $(cell).parents('tr').filter(':first'); - cell = $(cell)[0]; // in case cell is a jQuery object + tbdy = $tb.index( $cell.parents('tbody').filter(':first') ), + $row = $cell.parents('tr').filter(':first'); + cell = $cell[0]; // in case cell is a jQuery object // tbody may not exist if update is initialized while tbody is removed for processing if ($tb.length && tbdy >= 0) { row = $tb.eq(tbdy).find('tr').index( $row ); - icell = $(cell).index(); - l = c.cache[tbdy].normalized[row].length - 1; - c.cache[tbdy].row[ c.cache[tbdy].normalized[row][l] ] = $row; - c.cache[tbdy].normalized[row][icell] = c.parsers[icell].format( getElementText(table, cell, icell), table, cell, icell ); + icell = $cell.index(); + c.cache[tbdy].normalized[row][c.columns].$row = $row; + v = c.cache[tbdy].normalized[row][icell] = c.parsers[icell].format( getElementText(table, cell, icell), table, cell, icell ); + if ((c.parsers[icell].type || '').toLowerCase() === "numeric") { + // update column max value (ignore sign) + c.cache[tbdy].colMax[icell] = Math.max(Math.abs(v) || 0, c.cache[tbdy].colMax[icell] || 0); + } checkResort($table, resort, callback); } }) @@ -851,26 +827,34 @@ updateHeader(table); commonUpdate(table, resort, callback); } else { - var i, j, + var i, j, l, rowData, cells, rows = $row.filter('tr').length, - dat = [], l = $row[0].cells.length, tbdy = $table.find('tbody').index( $row.parents('tbody').filter(':first') ); // fixes adding rows to an empty table - see issue #179 - if (!c.parsers) { + if (!(c.parsers && c.parsers.length)) { buildParserCache(table); } // add each row for (i = 0; i < rows; i++) { + l = $row[i].cells.length; + cells = []; + rowData = { + child: [], + $row : $row.eq(i), + order: c.cache[tbdy].normalized.length + }; // add each cell for (j = 0; j < l; j++) { - dat[j] = c.parsers[j].format( getElementText(table, $row[i].cells[j], j), table, $row[i].cells[j], j ); + cells[j] = c.parsers[j].format( getElementText(table, $row[i].cells[j], j), table, $row[i].cells[j], j ); + if ((c.parsers[j].type || '').toLowerCase() === "numeric") { + // update column max value (ignore sign) + c.cache[tbdy].colMax[j] = Math.max(Math.abs(cells[j]) || 0, c.cache[tbdy].colMax[j] || 0); + } } - // add the row index to the end - dat.push(c.cache[tbdy].row.length); + // add the row data to the end + cells.push(rowData); // update cache - c.cache[tbdy].row.push([$row[i]]); - c.cache[tbdy].normalized.push(dat); - dat = []; + c.cache[tbdy].normalized.push(cells); } // resort using current settings checkResort($table, resort, callback); @@ -893,28 +877,27 @@ // sort the table and append it to the dom multisort(table); appendToTable(table, init); - $table - .trigger("sortEnd", this) - .trigger('applyWidgets'); - if (typeof callback === "function") { + $table.trigger("sortEnd", this); + ts.applyWidget(table); + if ($.isFunction(callback)) { callback(table); } }) .bind("appendCache" + c.namespace, function(e, callback, init) { e.stopPropagation(); appendToTable(table, init); - if (typeof callback === "function") { + if ($.isFunction(callback)) { callback(table); } }) .bind("updateCache" + c.namespace, function(e, callback){ // rebuild parsers - if (!c.parsers) { + if (!(c.parsers && c.parsers.length)) { buildParserCache(table); } // rebuild the cache map buildCache(table); - if (typeof callback === "function") { + if ($.isFunction(callback)) { callback(table); } }) @@ -972,8 +955,6 @@ $.data(table, "tablesorter", c); if (c.debug) { $.data( table, 'startoveralltimer', new Date()); } - // constants - c.supportsTextContent = $('<span>x</span>')[0].textContent === 'x'; // removing this in version 3 (only supports jQuery 1.7+) c.supportsDataObject = (function(version) { version[0] = parseInt(version[0], 10); @@ -1005,6 +986,8 @@ c.$table.attr('aria-labelledby', 'theCaption'); } c.widgetInit = {}; // keep a list of initialized widgets + // change textExtraction via data-attribute + c.textExtraction = c.$table.attr('data-text-extraction') || c.textExtraction || 'basic'; // build headers buildHeaders(table); // fixate columns if the users supplies the fixedWidth option @@ -1034,7 +1017,9 @@ setHeadersCss(table); if (c.initWidgets) { // apply widget format - ts.applyWidget(table); + setTimeout(function(){ + ts.applyWidget(table, false); + }, 0); } } @@ -1057,6 +1042,53 @@ if (typeof c.initialized === 'function') { c.initialized(table); } }; + + // computeTableHeaderCellIndexes from: + // http://www.javascripttoolbox.com/lib/table/examples.php + // http://www.javascripttoolbox.com/temp/table_cellindex.html + ts.computeColumnIndex = function(trs) { + var matrix = [], + lookup = {}, + cols = 0, // determine the number of columns + i, j, k, l, $cell, cell, cells, rowIndex, cellId, rowSpan, colSpan, firstAvailCol, matrixrow; + for (i = 0; i < trs.length; i++) { + cells = trs[i].cells; + for (j = 0; j < cells.length; j++) { + cell = cells[j]; + $cell = $(cell); + rowIndex = cell.parentNode.rowIndex; + cellId = rowIndex + "-" + $cell.index(); + rowSpan = cell.rowSpan || 1; + colSpan = cell.colSpan || 1; + if (typeof(matrix[rowIndex]) === "undefined") { + matrix[rowIndex] = []; + } + // Find first available column in the first row + for (k = 0; k < matrix[rowIndex].length + 1; k++) { + if (typeof(matrix[rowIndex][k]) === "undefined") { + firstAvailCol = k; + break; + } + } + lookup[cellId] = firstAvailCol; + cols = Math.max(firstAvailCol, cols); + // add data-column + $cell.attr({ 'data-column' : firstAvailCol }); // 'data-row' : rowIndex + for (k = rowIndex; k < rowIndex + rowSpan; k++) { + if (typeof(matrix[k]) === "undefined") { + matrix[k] = []; + } + matrixrow = matrix[k]; + for (l = firstAvailCol; l < firstAvailCol + colSpan; l++) { + matrixrow[l] = "x"; + } + } + } + } + // may not be accurate if # header columns !== # tbody columns + return cols + 1; // add one because it's a zero-based index + }; + // *** Process table *** // add processing indicator ts.isProcessing = function(table, toggle, $ths) { @@ -1186,6 +1218,7 @@ $t.toggleClass(ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme, removeClasses === false); // clear flag in case the plugin is initialized again table.hasInitialized = false; + delete table.config.cache; if (typeof callback === 'function') { callback(table); } @@ -1387,8 +1420,11 @@ wo = c.widgetOptions, widgets = [], time, w, wd; + // prevent numerous consecutive widget applications + if (init !== false && table.hasInitialized && (table.isApplyingWidgets || table.isUpdating)) { return; } if (c.debug) { time = new Date(); } if (c.widgets.length) { + table.isApplyingWidgets = true; // ensure unique widget ids c.widgets = $.grep(c.widgets, function(v, k){ return $.inArray(v, c.widgets) === k; @@ -1424,6 +1460,9 @@ } }); } + setTimeout(function(){ + table.isApplyingWidgets = false; + }, 0); if (c.debug) { w = c.widgets.length; benchmark("Completed " + (init === true ? "initializing " : "applying ") + w + " widget" + (w !== 1 ? "s" : ""), time); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js new file mode 100644 index 0000000..771e8c3 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js @@ -0,0 +1,134 @@ +/*! Filter widget formatter functions - updated 4/22/2014 (v2.16.1-beta) + * requires: jQuery 1.7.2+, tableSorter 2.16+, filter widget 2.16+ and select2 v3.4.6+ plugin + */ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;(function($){ +"use strict"; + +var ts = $.tablesorter || {}; +ts.filterFormatter = ts.filterFormatter || {}; + +/************************\ + Select2 Filter Formatter +\************************/ +ts.filterFormatter.select2 = function($cell, indx, select2Def) { + var o = $.extend({ + // select2 filter formatter options + cellText : '', // Text (wrapped in a label element) + match : true, // adds "filter-match" to header + // include ANY select2 options below + multiple : true, + width : '100%' + + }, select2Def ), + arry, data, + c = $cell.closest('table')[0].config, + wo = c.widgetOptions, + // Add a hidden input to hold the range values + $input = $('<input class="filter" type="hidden">') + .appendTo($cell) + // hidden filter update namespace trigger by filter widget + .bind('change' + c.namespace + 'filter', function(){ + var val = this.value; + val = val.replace(/[/()$]/g, '').split('|'); + updateSelect2(val); + }), + $header = c.$headers.filter('[data-column="' + indx + '"]:last'), + onlyAvail = $header.hasClass(wo.filter_onlyAvail), + $shcell = [], + match = o.match ? '' : '$', + + // this function updates the hidden input and adds the current values to the header cell text + updateSelect2 = function(v, notrigger) { + v = typeof v === "undefined" || v === '' ? $cell.find('.select2').select2('val') || o.value || '' : v || ''; + $input + // add equal to the beginning, so we filter exact numbers + .val( $.isArray(v) && v.length ? '/(' + (v || []).join(match + '|') + match + ')/' : '' ) + .trigger( notrigger ? '' : 'search' ).end() + .find('.select2').select2('val', v); + // update sticky header cell + if ($shcell.length) { + $shcell + .find('.select2').select2('val', v); + } + }, + + // get options from table cell content or filter_selectSource (v2.16) + updateOptions = function(){ + data = []; + arry = ts.filter.getOptionSource(c.$table[0], indx, onlyAvail) || []; + // build select2 data option + $.each(arry, function(i,v){ + data.push({id: v, text: v}); + }); + o.data = data; + }; + + // get filter-match class from option + $header.toggleClass('filter-match', o.match); + if (o.cellText) { + $cell.prepend('<label>' + o.cellText + '</label>'); + } + + // don't add default in table options if either ajax or + // data options are already defined + if (!(o.ajax && !$.isEmptyObject(o.ajax)) && !o.data) { + updateOptions(); + if (onlyAvail) { + c.$table.bind('filterEnd', function(){ + updateOptions(); + $cell.add($shcell).find('.select2').select2(o); + }); + } + } + + // add a select2 hidden input! + $('<input class="select2 select2-' + indx + '" type="hidden" />') + .val(o.value) + .appendTo($cell) + .select2(o) + .bind('change', function(){ + updateSelect2(); + }); + + // update select2 from filter hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + // value = '/(x$|y$)/' => 'x,y' + var val = c.$table.data('lastSearch')[indx] || ''; + val = val.replace(/[/()$]/g, '').split('|'); + $cell.find('.select2').select2('val', val); + updateSelect2(val, true); + }); + + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + // add a select2! + $('<input class="select2 select2-' + indx + '" type="hidden">') + .val(o.value) + .appendTo($shcell) + .select2(o) + .bind('change', function(){ + $cell.find('.select2').select2('val', $shcell.find('.select2').select2('val') ); + updateSelect2(); + }); + if (o.cellText) { + $shcell.prepend('<label>' + o.cellText + '</label>'); + } + + }); + + // on reset + c.$table.bind('filterReset', function(){ + $cell.find('.select2').select2('val', o.value || ''); + setTimeout(function(){ + updateSelect2(); + }, 0); + }); + + updateSelect2(); + return $input; +}; + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js index 42408ca..9f3a3db 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js @@ -1,4 +1,4 @@ -/*! Filter widget formatter functions - updated 3/12/2014 (v2.15.9) +/*! Filter widget formatter functions - updated 4/23/2014 (v2.16.0) * requires: tableSorter 2.15+ and jQuery 1.4.3+ * * uiSpinner (jQuery UI spinner) @@ -20,6 +20,7 @@ var ts = $.tablesorter || {}, // compare option selector class name (jQuery selector) compareSelect = '.compare-select', + tsff = ts.filterFormatter = { addCompare: function($cell, indx, options){ @@ -87,7 +88,7 @@ tsff = ts.filterFormatter = { v = ui && ui.value && ts.formatFloat((ui.value + '').replace(/[><=]/g,'')) || $cell.find('.spinner').val() || o.value, compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', - searchType = ui && typeof ui.delayed === 'boolean' ? ui.delayed : c.$table[0].hasInitialized ? o.delayed : true; + searchType = ui && typeof ui.delayed === 'boolean' ? ui.delayed : c.$table[0].hasInitialized ? o.delayed || '' : true; if (o.addToggle) { chkd = $cell.find('.toggle').is(':checked'); } @@ -244,7 +245,7 @@ tsff = ts.filterFormatter = { val = o.compare ? v : v === o.min ? o.allText : v, compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', result = compare + val, - searchType = ui && typeof ui.delayed === 'boolean' ? ui.delayed : c.$table[0].hasInitialized ? o.delayed : true; + searchType = ui && typeof ui.delayed === 'boolean' ? ui.delayed : c.$table[0].hasInitialized ? o.delayed || '' : true; if (o.valueToHeader) { // add range indication to the header cell above! $cell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(' (' + result + ')'); @@ -391,7 +392,7 @@ tsff = ts.filterFormatter = { result = val[0] + ' - ' + val[1], // make range an empty string if entire range is covered so the filter row will hide (if set) range = val[0] === o.min && val[1] === o.max ? '' : result, - searchType = ui && typeof ui.delayed === 'boolean' ? ui.delayed : c.$table[0].hasInitialized ? o.delayed : true; + searchType = ui && typeof ui.delayed === 'boolean' ? ui.delayed : c.$table[0].hasInitialized ? o.delayed || '': true; if (o.valueToHeader) { // add range indication to the header cell above (if not using the css method)! $cell.closest('thead').find('th[data-column=' + indx + ']').find('.currange').html(' (' + result + ')'); @@ -511,12 +512,12 @@ tsff = ts.filterFormatter = { t, $shcell = [], // this function updates the hidden input - date1Compare = function(v, notrigger) { + date1Compare = function(notrigger) { var date, query, - getdate = v || $date.datepicker('getDate') || '', + getdate = $date.datepicker('getDate') || '', compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', - searchType = c.$table[0].hasInitialized ? o.delayed : true; - $date.datepicker('setDate', getdate === '' ? o.defaultDate || '' : getdate); + searchType = c.$table[0].hasInitialized ? o.delayed || '': true; + $date.datepicker('setDate', getdate === '' ? '' : getdate); if (getdate === '') { notrigger = false; } date = $date.datepicker('getDate'); query = date ? ( o.endOfDay && /<=/.test(compare) ? date.setHours(23, 59, 59) : date.getTime() ) || '' : ''; @@ -539,7 +540,7 @@ tsff = ts.filterFormatter = { // Add date range picker t = '<input type="text" class="date date' + indx + '" placeholder="' + - ($hdr.data('placeholder') || $hdr.attr('data-placeholder') || '') + '" />'; + ($hdr.data('placeholder') || $hdr.attr('data-placeholder') || c.widgetOptions.filter_placeholder.search || '') + '" />'; $date = $(t).appendTo($cell); // add callbacks; preserve added callbacks @@ -556,7 +557,7 @@ tsff = ts.filterFormatter = { if ($.isArray(o.compare)) { $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); } - $cell.add($shcell).find('.date').val(o.defaultDate).datepicker('setDate', ''); + $cell.add($shcell).find('.date').val(o.defaultDate).datepicker('setDate', o.defaultDate); setTimeout(function(){ date1Compare(); }, 0); @@ -568,15 +569,17 @@ tsff = ts.filterFormatter = { if (/\s+-\s+/.test(v)) { // date range found; assume an exact match on one day $cell.find(compareSelect).val('='); - num = new Date ( Number( v.split(/\s+-\s+/)[0] ) ); + num = v.split(/\s+-\s+/)[0]; $date.datepicker( 'setDate', num ); } else { num = (tsff.updateCompare($cell, $input, o)[1]).toString() || ''; // differeniate 1388556000000 from 1/1/2014 using \d{5} regex - num = num !== '' ? new Date( /\d{5}/g.test(num) ? Number(num) : num ) || '' : ''; + num = num !== '' ? /\d{5}/g.test(num) ? Number(num) : num || '' : ''; } $cell.add($shcell).find('.date').datepicker( 'setDate', num ); - date1Compare(num, true); + setTimeout(function(){ + date1Compare(true); + }, 0); }); if (o.compare) { @@ -628,8 +631,11 @@ tsff = ts.filterFormatter = { changeYear : true, numberOfMonths : 1 }, defDate), - t, closeTo, closeFrom, $shcell = [], + t, closeDate, $shcell = [], c = $cell.closest('table')[0].config, + validDate = function(d){ + return d instanceof Date && isFinite(d); + }, // Add a hidden input to hold the range values $input = $('<input class="dateRange" type="hidden">') .appendTo($cell) @@ -639,69 +645,60 @@ tsff = ts.filterFormatter = { if (v.match(' - ')) { v = v.split(' - '); $cell.find('.dateTo').val(v[1]); - closeFrom(v[0]); + closeDate(v[0]); } else if (v.match('>=')) { - closeFrom( v.replace('>=', '') ); + closeDate( v.replace('>=', '') ); } else if (v.match('<=')) { - closeTo( v.replace('<=', '') ); + closeDate( v.replace('<=', '') ); } - }); + }), // make sure we're using parsed dates in the search - $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed'); + $hdr = $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed'); // Add date range picker - t = '<label>' + o.textFrom + '</label><input type="text" class="dateFrom" /><label>' + o.textTo + '</label><input type="text" class="dateTo" />'; + t = '<label>' + o.textFrom + '</label><input type="text" class="dateFrom" placeholder="' + + ($hdr.data('placeholderFrom') || $hdr.attr('data-placeholder-from') || c.widgetOptions.filter_placeholder.from || '') + '" />' + + '<label>' + o.textTo + '</label><input type="text" class="dateTo" placeholder="' + + ($hdr.data('placeholderTo') || $hdr.attr('data-placeholder-to') || c.widgetOptions.filter_placeholder.to || '') + '" />'; $(t).appendTo($cell); // add callbacks; preserve added callbacks o.oldonClose = o.onClose; - var localfrom = o.defaultDate = o.from || o.defaultDate; - - closeFrom = o.onClose = function( selectedDate, ui ) { + closeDate = o.onClose = function( selectedDate, ui ) { var range, - from = new Date( $cell.find('.dateFrom').datepicker('getDate') ).getTime() || '', - to = $cell.find('.dateTo').datepicker('getDate') || ''; - to = to ? ( o.endOfDay ? to.setHours(23, 59, 59) : to.getTime() ) || '' : ''; + from = $cell.find('.dateFrom').datepicker('getDate'), + to = $cell.find('.dateTo').datepicker('getDate'); + from = validDate(from) ? from.getTime() : ''; + to = validDate(to) ? ( o.endOfDay ? to.setHours(23, 59, 59) : to.getTime() ) || '' : ''; range = from ? ( to ? from + ' - ' + to : '>=' + from ) : (to ? '<=' + to : ''); - $cell + $cell.add( $shcell ) .find('.dateRange').val(range) - .trigger('search').end() - .find('.dateTo').datepicker('option', 'minDate', selectedDate ).end() - .find('.dateFrom').val(selectedDate); - - // update sticky header cell - if ($shcell.length) { - $shcell - .find('.dateTo').datepicker('option', 'minDate', selectedDate ).end() - .find('.dateFrom').val(selectedDate); + .trigger('search'); + // date picker needs date objects + from = from ? new Date(from) : ''; + to = to ? new Date(to) : ''; + + if (/<=/.test(range)) { + $cell.add( $shcell ) + .find('.dateFrom').datepicker('option', 'maxDate', to ).end() + .find('.dateTo').datepicker('option', 'minDate', null).datepicker('setDate', to); + } else if (/>=/.test(range)) { + $cell.add( $shcell ) + .find('.dateFrom').datepicker('option', 'maxDate', null).datepicker('setDate', from).end() + .find('.dateTo').datepicker('option', 'minDate', from ); + } else { + $cell.add( $shcell ) + .find('.dateFrom').datepicker('option', 'maxDate', null).datepicker('setDate', from ).end() + .find('.dateTo').datepicker('option', 'minDate', null).datepicker('setDate', to); } + if (typeof o.oldonClose === 'function') { o.oldonClose(selectedDate, ui); } }; + o.defaultDate = o.from || ''; $cell.find('.dateFrom').datepicker(o); - o.defaultDate = o.to || '+7d'; // set to date +7 days from today (if not defined) - closeTo = o.onClose = function( selectedDate, ui ) { - var range, - from = new Date( $cell.find('.dateFrom').datepicker('getDate') ).getTime() || '', - to = $cell.find('.dateTo').datepicker('getDate') || ''; - to = to ? ( o.endOfDay ? to.setHours(23, 59, 59) : to.getTime() ) || '' : ''; - range = from ? ( to ? from + ' - ' + to : '>=' + from ) : (to ? '<=' + to : ''); - $cell - .find('.dateRange').val(range) - .trigger('search').end() - .find('.dateFrom').datepicker('option', 'maxDate', selectedDate ).end() - .find('.dateTo').val(selectedDate); - - // update sticky header cell - if ($shcell.length) { - $shcell - .find('.dateFrom').datepicker('option', 'maxDate', selectedDate ).end() - .find('.dateTo').val(selectedDate); - } - if (typeof o.oldonClose === 'function') { o.oldonClose(selectedDate, ui); } - }; $cell.find('.dateTo').datepicker(o); // update date compare from hidden input, in case of saved filters @@ -709,7 +706,6 @@ tsff = ts.filterFormatter = { var val = $input.val() || '', from = '', to = ''; - // date range if (/\s+-\s+/.test(val)){ val = val.split(/\s+-\s+/) || []; @@ -717,14 +713,17 @@ tsff = ts.filterFormatter = { to = val[1] || ''; } else if (/>=/.test(val)) { // greater than date (to date empty) - from = new Date(Number( val.replace(/>=/, '') )) || ''; + from = val.replace(/>=/, '') || ''; } else if (/<=/.test(val)) { // less than date (from date empty) - to = new Date(Number( val.replace(/<=/, '') )) || ''; + to = val.replace(/<=/, '') || ''; } $cell.add($shcell).find('.dateFrom').datepicker('setDate', from); $cell.add($shcell).find('.dateTo').datepicker('setDate', to); - closeTo(to); + // give datepicker time to process + setTimeout(function(){ + closeDate(); + }, 0); }); // has sticky headers? @@ -733,18 +732,21 @@ tsff = ts.filterFormatter = { $shcell.append(t); // add a jQuery datepicker! - o.onClose = closeTo; + o.defaultDate = o.from || ''; + $shcell.find('.dateFrom').datepicker(o); + + o.defaultDate = o.to || '+7d'; $shcell.find('.dateTo').datepicker(o); - o.defaultDate = localfrom; - o.onClose = closeFrom; - $shcell.find('.dateFrom').datepicker(o); }); // on reset $cell.closest('table').bind('filterReset', function(){ $cell.add($shcell).find('.dateFrom').val('').datepicker('setDate', o.from ); $cell.add($shcell).find('.dateTo').val('').datepicker('setDate', o.to ); + setTimeout(function(){ + closeDate(); + }, 0); }); // return the hidden input so the filter widget has a reference to it @@ -781,7 +783,7 @@ tsff = ts.filterFormatter = { var chkd = o.addToggle ? $cell.find('.toggle').is(':checked') : true, v = $cell.find('.number').val(), compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', - searchType = c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) : true; + searchType = c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) || '' : true; $input // add equal to the beginning, so we filter exact numbers .val( !o.addToggle || chkd ? (compare ? compare : o.exactMatch ? '=' : '') + v : '' ) @@ -913,7 +915,7 @@ tsff = ts.filterFormatter = { v = ( typeof v === "undefined" ? $input.val() : v ).toString().replace(/[<>=]/g,'') || o.value; var compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', t = ' (' + (compare ? compare + v : v == o.min ? o.allText : v) + ')', - searchType = c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) : true; + searchType = c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) || '' : true; $cell.find('input[type=hidden]') // add equal to the beginning, so we filter exact numbers .val( ( compare ? compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ) ) diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index e992a13..eefd396 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.15+ widgets - updated 4/3/2014 (v2.15.13) +/*! tableSorter 2.16+ widgets - updated 4/23/2014 (v2.16.0) * * Column Styles * Column Filters @@ -360,9 +360,11 @@ ts.addWidget({ filter_ignoreCase : true, // if true, make all searches case-insensitive filter_liveSearch : true, // if true, search column content while the user types (with a delay) filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available (visible) options within the drop down + filter_placeholder : { search : '', select : '' }, // default placeholder text (overridden by any header "data-placeholder" setting) filter_reset : null, // jQuery selector string of an element used to reset the filters filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters filter_searchDelay : 300, // typing delay in milliseconds before starting a search + filter_selectSource : null, // include a function to return an array of values to be added to the column filter select filter_startsWith : false, // if true, filter start from the beginning of the cell contents filter_useParsedData : false, // filter all data using parsed content filter_serversideFiltering : false, // if true, server-side filtering should be performed because client-side filtering will be disabled, but the ui and events will still be used. @@ -584,12 +586,20 @@ ts.filter = { // reset button/link if (wo.filter_reset) { - $(document) - .undelegate(wo.filter_reset, 'click.tsfilter') - .delegate(wo.filter_reset, 'click.tsfilter', function() { - // trigger a reset event, so other functions (filterFormatter) know when to reset - c.$table.trigger('filterReset'); - }); + if (wo.filter_reset instanceof $) { + // reset contains a jQuery object, bind to it + wo.filter_reset.click(function(){ + c.$table.trigger('filterReset'); + }); + } else if ($(wo.filter_reset).length) { + // reset is a jQuery selector, use event delegation + $(document) + .undelegate(wo.filter_reset, 'click.tsfilter') + .delegate(wo.filter_reset, 'click.tsfilter', function() { + // trigger a reset event, so other functions (filterFormatter) know when to reset + c.$table.trigger('filterReset'); + }); + } } if (wo.filter_functions) { // column = column # (string) @@ -604,7 +614,7 @@ ts.filter = { for (string in wo.filter_functions[column]) { if (typeof string === 'string') { options += options === '' ? - '<option value="">' + ($header.data('placeholder') || $header.attr('data-placeholder') || '') + '</option>' : ''; + '<option value="">' + ($header.data('placeholder') || $header.attr('data-placeholder') || wo.filter_placeholder.select || '') + '</option>' : ''; options += '<option value="' + string + '">' + string + '</option>'; } } @@ -715,7 +725,7 @@ ts.filter = { buildFilter = $('<input type="search">').appendTo( c.$filters.eq(column) ); } if (buildFilter) { - buildFilter.attr('placeholder', $header.data('placeholder') || $header.attr('data-placeholder') || ''); + buildFilter.attr('placeholder', $header.data('placeholder') || $header.attr('data-placeholder') || wo.filter_placeholder.search || ''); } } if (buildFilter) { @@ -725,7 +735,7 @@ ts.filter = { wo.filter_cssFilter ) || ''; buildFilter.addClass( ts.css.filter + ' ' + name ).attr('data-column', column); if (disabled) { - buildFilter.addClass('disabled')[0].disabled = true; // disabled! + buildFilter.attr('placeholder', '').addClass('disabled')[0].disabled = true; // disabled! } } } @@ -753,7 +763,7 @@ ts.filter = { .attr('data-lastSearchTime', new Date().getTime()) .unbind('keyup search change '.split(' ').join(c.namespace + 'filter ')) // include change for select - fixes #473 - .bind('keyup search change '.split(' ').join(c.namespace + 'filter '), function(event, filters) { + .bind('keyup search change '.split(' ').join(c.namespace + 'filter '), function(event) { $(this).attr('data-lastSearchTime', new Date().getTime()); // emulate what webkit does.... escape clears the filter if (event.which === 27) { @@ -865,20 +875,23 @@ ts.filter = { if ($tbodies.eq(tbodyIndex).hasClass(c.cssInfoBlock || ts.css.info)) { continue; } // ignore info blocks, issue #264 $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // skip child rows & widget added (removable) rows - fixes #448 thanks to @hempel! - $rows = $tbody.children('tr').not(c.selectorRemove); + // $rows = $tbody.children('tr').not(c.selectorRemove); + columnIndex = c.columns; + // convert stored rows into a jQuery object + $rows = true ? $( $.map(c.cache[tbodyIndex].normalized, function(el){ return el[columnIndex].$row.get(); }) ) : $tbody.children('tr').not(c.selectorRemove); len = $rows.length; if (combinedFilters === '' || wo.filter_serversideFiltering) { - $tbody.children().removeClass(wo.filter_filteredRow).not('.' + c.cssChildRow).show(); + $rows.removeClass(wo.filter_filteredRow).not('.' + c.cssChildRow).show(); } else { // optimize searching only through already filtered rows - see #313 searchFiltered = true; lastSearch = c.lastSearch || c.$table.data('lastSearch') || []; $.each(filters, function(indx, val) { // check for changes from beginning of filter; but ignore if there is a logical "or" in the string - searchFiltered = (val || '').indexOf(lastSearch[indx] || '') === 0 && searchFiltered && !/(\s+or\s+|\|)/g.test(val || ''); + searchFiltered = (val || '').indexOf(lastSearch[indx]) === 0 && searchFiltered && !/(\s+or\s+|\|)/g.test(val || ''); }); // can't search when all rows are hidden - this happens when looking for exact matches - if (searchFiltered && $rows.filter(':visible').length === 0) { searchFiltered = false; } + if (searchFiltered && $rows.not('.' + wo.filter_filteredRow).length === 0) { searchFiltered = false; } if ((wo.filter_$anyMatch && wo.filter_$anyMatch.length) || filters[c.columns]) { anyMatch = wo.filter_$anyMatch && wo.filter_$anyMatch.val() || filters[c.columns] || ''; if (c.sortLocaleCompare) { @@ -887,7 +900,6 @@ ts.filter = { } iAnyMatch = anyMatch.toLowerCase(); } - // loop through the rows cacheIndex = 0; for (rowIndex = 0; rowIndex < len; rowIndex++) { @@ -902,7 +914,7 @@ ts.filter = { // checked here so the option can be changed dynamically childRowText = (childRow.length && wo.filter_childRows) ? childRow.text() : ''; childRowText = wo.filter_ignoreCase ? childRowText.toLocaleLowerCase() : childRowText; - $cells = $rows.eq(rowIndex).children('td'); + $cells = $rows.eq(rowIndex).children(); if (anyMatch) { rowArray = $cells.map(function(i){ @@ -919,7 +931,7 @@ ts.filter = { }).get(); rowText = rowArray.join(' '); iRowText = rowText.toLowerCase(); - rowCache = c.cache[tbodyIndex].normalized[cacheIndex].join(' '); + rowCache = c.cache[tbodyIndex].normalized[cacheIndex].slice(0,-1).join(' '); filterMatched = null; $.each(ts.filter.types, function(type, typeFunction) { if ($.inArray(type, anyMatchNotAllowedTypes) < 0) { @@ -1010,47 +1022,80 @@ ts.filter = { if (c.debug) { ts.benchmark("Completed filter widget search", time); } - c.$table.trigger('applyWidgets'); // make sure zebra widget is applied c.$table.trigger('filterEnd'); + setTimeout(function(){ + c.$table.trigger('applyWidgets'); // make sure zebra widget is applied + }, 0); }, - buildSelect: function(table, column, updating, onlyavail) { - if (!table.config.cache || $.isEmptyObject(table.config.cache)) { return; } - column = parseInt(column, 10); - var indx, rowIndex, tbodyIndex, len, currentValue, txt, $filters, - c = table.config, + getOptionSource: function(table, column, onlyAvail) { + var c = table.config, wo = c.widgetOptions, - $tbodies = c.$tbodies, - arry = [], - node = c.$headers.filter('[data-column="' + column + '"]:last'), - // t.data('placeholder') won't work in jQuery older than 1.4.3 - options = '<option value="">' + ( node.data('placeholder') || node.attr('data-placeholder') || '' ) + '</option>'; - for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { - len = c.cache[tbodyIndex].row.length; - // loop through the rows - for (rowIndex = 0; rowIndex < len; rowIndex++) { - // check if has class filtered - if (onlyavail && c.cache[tbodyIndex].row[rowIndex][0].className.match(wo.filter_filteredRow)) { continue; } - // get non-normalized cell content - if (wo.filter_useParsedData) { - arry.push( '' + c.cache[tbodyIndex].normalized[rowIndex][column] ); - } else { - node = c.cache[tbodyIndex].row[rowIndex][0].cells[column]; - if (node) { - arry.push( $.trim( node.textContent || node.innerText || $(node).text() ) ); - } - } - } + arry = false, + source = wo.filter_selectSource; + + // filter select source option + if ($.isFunction(source)) { + // OVERALL source + arry = source(table, column, onlyAvail); + } else if ($.type(source) === 'object' && source.hasOwnProperty(column)) { + // custom select source function for a SPECIFIC COLUMN + arry = source[column](table, column, onlyAvail); } + if (arry === false) { + // fall back to original method + arry = ts.filter.getOptions(table, column, onlyAvail); + } + // get unique elements and sort the list // if $.tablesorter.sortText exists (not in the original tablesorter), // then natural sort the list otherwise use a basic sort arry = $.grep(arry, function(value, indx) { return $.inArray(value, arry) === indx; }); - arry = (ts.sortNatural) ? arry.sort(function(a, b) { return ts.sortNatural(a, b); }) : arry.sort(true); - - // Get curent filter value - currentValue = c.$table.find('thead').find('select.' + ts.css.filter + '[data-column="' + column + '"]').val(); + return (ts.sortNatural) ? arry.sort(function(a, b) { return ts.sortNatural(a, b); }) : arry.sort(true); + }, + getOptions: function(table, column, onlyAvail) { + var rowIndex, tbodyIndex, len, row, cache, cell, + c = table.config, + wo = c.widgetOptions, + $tbodies = c.$table.children('tbody'), + arry = []; + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + if (!$tbodies.eq(tbodyIndex).hasClass(c.cssInfoBlock)) { + cache = c.cache[tbodyIndex]; + len = c.cache[tbodyIndex].normalized.length; + // loop through the rows + for (rowIndex = 0; rowIndex < len; rowIndex++) { + // get cached row from cache.row (old) or row data object (new; last item in normalized array) + row = cache.row ? cache.row[rowIndex] : cache.normalized[rowIndex][c.columns].$row[0]; + // check if has class filtered + if (onlyAvail && row.className.match(wo.filter_filteredRow)) { continue; } + // get non-normalized cell content + if (wo.filter_useParsedData) { + arry.push( '' + cache.normalized[rowIndex][column] ); + } else { + cell = row.cells[column]; + if (cell) { + arry.push( $.trim( cell.textContent || cell.innerText || $(cell).text() ) ); + } + } + } + } + } + return arry; + }, + buildSelect: function(table, column, updating, onlyAvail) { + if (!table.config.cache || $.isEmptyObject(table.config.cache)) { return; } + column = parseInt(column, 10); + var indx, txt, $filters, + c = table.config, + wo = c.widgetOptions, + node = c.$headers.filter('[data-column="' + column + '"]:last'), + // t.data('placeholder') won't work in jQuery older than 1.4.3 + options = '<option value="">' + ( node.data('placeholder') || node.attr('data-placeholder') || wo.filter_placeholder.select || '' ) + '</option>', + arry = ts.filter.getOptionSource(table, column, onlyAvail), + // Get curent filter value + currentValue = c.$table.find('thead').find('select.' + ts.css.filter + '[data-column="' + column + '"]').val(); // build option list for (indx = 0; indx < arry.length; indx++) { @@ -1150,7 +1195,8 @@ ts.setFilters = function(table, filter, apply, skipFirst) { if (c && apply) { // ensure new set filters are applied, even if the search is the same c.lastCombinedFilter = null; - c.$table.trigger('search', [filter, false]).trigger('filterFomatterUpdate'); + ts.filter.searching(c.$table[0], filter, skipFirst); + c.$table.trigger('filterFomatterUpdate'); } return !!valid; }; @@ -1235,7 +1281,8 @@ ts.addWidget({ if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; } // clear out cloned table, except for sticky header // include caption & filter row (fixes #126 & #249) - don't remove cells to get correct cell indexing - $stickyTable.find('thead:gt(0), tr.sticky-false, tbody, tfoot').hide(); + $stickyTable.find('thead:gt(0), tr.sticky-false').hide(); + $stickyTable.find('tbody, tfoot').remove(); if (!wo.stickyHeaders_includeCaption) { $stickyTable.find('caption').remove(); } else { diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js new file mode 100644 index 0000000..05c8bcb --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js @@ -0,0 +1,82 @@ +/*! + * Extract out date parsers + */ +/*jshint jquery:true */ +;(function($){ +"use strict"; + + /*! extract US Long Date (ignore any other text) + * e.g. "Sue's Birthday! Jun 26, 2004 7:22 AM (8# 2oz)" + * demo: http://jsfiddle.net/abkNM/2293/ + */ + $.tablesorter.addParser({ + id: "extractUSLongDate", + is: function (s) { + // don't auto detect this parser + return false; + }, + format: function (s, table) { + var date = s.match(/[A-Z]{3,10}\.?\s+\d{1,2},?\s+(?:\d{4})(?:\s+\d{1,2}:\d{2}(?::\d{2})?(?:\s+[AP]M)?)?/i); + return date ? $.tablesorter.formatFloat((new Date(date[0]).getTime() || ''), table) || s : s; + }, + type: "numeric" + }); + + /*! extract MMDDYYYY (ignore any other text) + * demo: http://jsfiddle.net/Mottie/abkNM/2418/ + */ + $.tablesorter.addParser({ + id: "extractMMDDYYYY", + is: function (s) { + // don't auto detect this parser + return false; + }, + format: function (s, table) { + var date = s.replace(/\s+/g," ").replace(/[\-.,]/g, "/").match(/(\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4}(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?)/i); + return date ? $.tablesorter.formatFloat((new Date(date[0]).getTime() || ''), table) || s : s; + }, + type: "numeric" + }); + + /*! extract DDMMYYYY (ignore any other text) + * demo: http://jsfiddle.net/Mottie/abkNM/2419/ + */ + $.tablesorter.addParser({ + id: "extractDDMMYYYY", + is: function (s) { + // don't auto detect this parser + return false; + }, + format: function (s, table) { + var date = s.replace(/\s+/g," ").replace(/[\-.,]/g, "/").match(/(\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4}(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?)/i); + if (date) { + date = date[0].replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$2/$1/$3"); + return $.tablesorter.formatFloat((new Date(date).getTime() || ''), table) || s; + } + return s; + }, + type: "numeric" + }); + + /*! extract YYYYMMDD (ignore any other text) + * demo: http://jsfiddle.net/Mottie/abkNM/2420/ + */ + $.tablesorter.addParser({ + id: "extractYYYYMMDD", + is: function (s) { + // don't auto detect this parser + return false; + }, + format: function (s, table) { + var date = s.replace(/\s+/g," ").replace(/[\-.,]/g, "/").match(/(\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2}(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?)/i); + if (date) { + date = date[0].replace(/(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/, "$2/$3/$1"); + console.log(date); + return $.tablesorter.formatFloat((new Date(date).getTime() || ''), table) || s; + } + return s; + }, + type: "numeric" + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js index 1922cc5..695514e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js @@ -1,4 +1,6 @@ -/*! Build Table widget * by Rob Garrison */ +/*! Build Table widget for tableSorter v2.16.0 (4/23/2014) + * by Rob Garrison + */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ ;(function($){ @@ -163,8 +165,12 @@ var ts = $.tablesorter = $.tablesorter || {}, t = '<tr>' + (wo.build_numbers.addColumn ? '<th' + (wo.build_numbers.sortable ? '' : ' class="sorter-false"') + '>' + wo.build_numbers.addColumn + '</th>' : ''); $.each(h1, function(i, h) { - t += '<th' + (cls && cls[i] ? ' class="' + cls[i] + '"' : '') + '>' + - (h2 && h2[i] ? h2[i] : h) + '</th>'; + if (/<\s*\/t(d|h)\s*>/.test(h)) { + t += h; + } else { + t += '<th' + (cls && cls[i] ? ' class="' + cls[i] + '"' : '') + '>' + + (h2 && h2[i] ? h2[i] : h) + '</th>'; + } }); return t + '</tr>'; }, @@ -172,8 +178,13 @@ var ts = $.tablesorter = $.tablesorter || {}, var h = (ftr ? 'th' : 'td'), t = '<tr>' + (wo.build_numbers.addColumn ? '<' + h + '>' + (ftr ? '' : num) + '</' + h + '>' : ''); $.each(items, function(i, item) { - t += '<' + (ftr ? h + (c && c[i] ? ' class="' + c[i] + '"' : '') : h) + '>' + - (ftr && txt && txt.length && txt[i] ? txt[i] : item) + '</' + h + '>'; + // test if HTML is already included; look for closing </td> + if (/<\s*\/t(d|h)\s*>/.test(item)) { + t += item; + } else { + t += '<' + (ftr ? h + (c && c[i] ? ' class="' + c[i] + '"' : '') : h) + '>' + + (ftr && txt && txt.length && txt[i] ? txt[i] : item) + '</' + h + '>'; + } }); return t + '</tr>'; } @@ -372,6 +383,7 @@ var ts = $.tablesorter = $.tablesorter || {}, $tb = $('<tbody>'); // Build tbody $.each(r, function(i, d){ + var j; t = $.type(d) === 'object'; // add new tbody if (t && d.newTbody) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js index 94bf6a1..b616670 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js @@ -1,7 +1,7 @@ /*! tablesorter CSS Sticky Headers widget - updated 12/17/2013 (v2.15.0) * Requires a modern browser, tablesorter v2.8+ */ -/*global jQuery: false, unused:false */ +/*jshint jquery:true, unused:false */ ;(function($){ "use strict"; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js new file mode 100644 index 0000000..050ec68 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -0,0 +1,359 @@ +/*! tablesorter math widget - beta testing +* Requires tablesorter v2.16+ and jQuery 1.7+ +* by Rob Garrison +*/ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;(function($){ + "use strict"; + + var ts = $.tablesorter, + math = { + + // get all of the row numerical values in an arry + getRow : function(table, wo, $el, dataAttrib) { + var txt, + arry = [], + $row = $el.closest('tr'), + $cells = $row.children(); + if (!$row.hasClass(wo.filter_filteredRow || 'filtered')) { + if (wo.math_ignore.length) { + $cells = $cells.not('[' + dataAttrib + '=ignore]').not('[data-column=' + wo.math_ignore.join('],[data-column=') + ']'); + } + arry = $cells.not($el).map(function(){ + txt = this.textContent || $(this).text(); + txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table); + return isNaN(txt) ? 0 : txt; + }).get(); + } + return arry; + }, + + // get all of the column numerical values in an arry + getColumn : function(table, wo, $el, type, dataAttrib){ + var i, txt, $t, len, mathAbove, + arry = [], + c = table.config, + filtered = wo.filter_filteredRow || 'filtered', + cIndex = parseInt( $el.attr('data-column'), 10 ), + $rows = c.$table.children('tbody').children(), + $row = $el.closest('tr'); + // make sure tfoot rows are AFTER the tbody rows + // $rows.add( c.$table.children('tfoot').children() ); + if (type === 'above') { + len = $rows.index($row); + i = len; + while (i >= 0) { + $t = $rows.eq(i).children().filter('[data-column=' + cIndex + ']'); + mathAbove = $t.filter('[' + dataAttrib + '^=above]').length; + // ignore filtered rows & rows with data-math="ignore" (and starting row) + if ( ( !$rows.eq(i).hasClass(filtered) && $rows.eq(i).not('[' + dataAttrib + '=ignore]').length && i !== len ) || mathAbove && i !== len ) { + // stop calculating "above", when encountering another "above" + if (mathAbove) { + i = 0; + } else if ($t.length) { + txt = $t[0].textContent || $t.text(); + txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table); + arry.push(isNaN(txt) ? 0 : txt); + } + } + i--; + } + } else { + $rows.each(function(){ + $t = $(this).children().filter('[data-column=' + cIndex + ']'); + if (!$(this).hasClass(filtered) && $t.not('[' + dataAttrib + '^=above],[' + dataAttrib + '^=col]').length && !$t.is($el)) { + txt = ($t[0] ? $t[0].textContent : '') || $t.text(); + txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table); + arry.push(isNaN(txt) ? 0 : txt); + } + }); + } + return arry; + }, + + // get all of the column numerical values in an arry + getAll : function(table, wo, dataAttrib){ + var txt, $t, col, + arry = [], + c = table.config, + filtered = wo.filter_filteredRow || 'filtered', + $rows = c.$table.children('tbody').children(); + $rows.each(function(){ + if (!$(this).hasClass(filtered)) { + $(this).children().each(function(){ + $t = $(this); + col = parseInt( $t.attr('data-column'), 10); + if (!$t.filter('[' + dataAttrib + ']').length && $.inArray(col, wo.math_ignore) < 0) { + txt = ($t[0] ? $t[0].textContent : '') || $t.text(); + txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table); + arry.push(isNaN(txt) ? 0 : txt); + } + }); + } + }); + return arry; + }, + + recalculate : function(table, c, wo, init){ + if (c && !wo.math_isUpdating) { + + // add data-column attributes to all table cells + if (init) { + ts.computeColumnIndex( c.$table.children('tbody').children() ); + } + + // data-attribute name (defaults to data-math) + var dataAttrib = 'data-' + (wo.math_data || 'math'), + + // all non-info tbody cells + $mathCells = c.$tbodies.find('[' + dataAttrib + ']'); + math.mathType( table, wo, $mathCells, wo.math_priority, dataAttrib ); + + // only info tbody cells + $mathCells = c.$table.find('.' + c.cssInfoBlock + ', tfoot').find('[' + dataAttrib + ']'); + math.mathType( table, wo, $mathCells, wo.math_priority, dataAttrib ); + + // find the "all" total + math.mathType( table, wo, c.$table.find('[' + dataAttrib + '^=all]'), ['all'], dataAttrib ); + + wo.math_isUpdating = true; + c.$table.trigger('update'); + } + }, + + mathType : function(table, wo, $cells, priority, dataAttrib) { + if ($cells.length) { + var formula, t, $t, arry, getAll, + eq = ts.equations; + if (priority[0] === 'all') { + // no need to get all cells more than once + getAll = math.getAll(table, wo, dataAttrib); + } + $.each( priority, function(i, type) { + $cells.filter('[' + dataAttrib + '^=' + type + ']').each(function(){ + $t = $(this); + formula = ($t.attr(dataAttrib) || '').replace(type + '-', ''); + arry = (type === "row") ? math.getRow(table, wo, $t, dataAttrib) : + (type === "all") ? getAll : math.getColumn(table, wo, $t, type, dataAttrib); + if (eq[formula]) { + t = eq[formula](arry); + if (table.config.debug && console && console.log) { + console.log($t.attr(dataAttrib), arry, '=', t); + } + math.output( $t, wo, t, arry ); + } + }); + }); + } + }, + + output : function($cell, wo, value, arry) { + var result = ts.formatMask( wo.math_mask, value ); + if ($.isFunction(wo.math_complete)) { + result = wo.math_complete($cell, wo, result, value, arry); + } + if (result !== false) { + $cell.html(result); + } + } + + }; + + // Modified from https://code.google.com/p/javascript-number-formatter/ + /** + * @preserve IntegraXor Web SCADA - JavaScript Number Formatter + * http:// www.integraxor.com/ + * author: KPL, KHL + * (c)2011 ecava + * Dual licensed under the MIT or GPL Version 2 licenses. + */ + ts.formatMask = function(m, v){ + var isNegative, result, decimal, group, pos_lead_zero, pos_trail_zero, pos_separator, part, szSep, + integer, str, offset, i, l; + if ( !m || isNaN(+v) ) { + return v; // return as it is. + } + // convert any string to number according to formation sign. + v = m.charAt(0) == '-'? -v : +v; + isNegative = v < 0 ? v = -v : 0; // process only abs(), and turn on flag. + + // search for separator for grp & decimal, anything not digit, not +/- sign, not #. + result = m.match(/[^\d\-\+#]/g); + decimal = (result && result[result.length-1]) || '.'; // treat the right most symbol as decimal + group = (result && result[1] && result[0]) || ','; // treat the left most symbol as group separator + + // split the decimal for the format string if any. + m = m.split( decimal ); + // Fix the decimal first, toFixed will auto fill trailing zero. + v = v.toFixed( m[1] && m[1].length ); + v = +(v) + ''; // convert number to string to trim off *all* trailing decimal zero(es) + + // fill back any trailing zero according to format + pos_trail_zero = m[1] && m[1].lastIndexOf('0'); // look for last zero in format + part = v.split('.'); + // integer will get !part[1] + if ( !part[1] || part[1] && part[1].length <= pos_trail_zero ) { + v = (+v).toFixed( pos_trail_zero + 1 ); + } + szSep = m[0].split( group ); // look for separator + m[0] = szSep.join(''); // join back without separator for counting the pos of any leading 0. + + pos_lead_zero = m[0] && m[0].indexOf('0'); + if ( pos_lead_zero > -1 ) { + while ( part[0].length < ( m[0].length - pos_lead_zero ) ) { + part[0] = '0' + part[0]; + } + } else if ( +part[0] === 0 ) { + part[0] = ''; + } + + v = v.split('.'); + v[0] = part[0]; + + // process the first group separator from decimal (.) only, the rest ignore. + // get the length of the last slice of split result. + pos_separator = ( szSep[1] && szSep[ szSep.length - 1 ].length ); + if (pos_separator) { + integer = v[0]; + str = ''; + offset = integer.length % pos_separator; + l = integer.length; + for ( i = 0; i < l; i++ ) { + str += integer.charAt(i); // ie6 only support charAt for sz. + // -pos_separator so that won't trail separator on full length + if ( !(( i - offset + 1 ) % pos_separator) && i < l - pos_separator ) { + str += group; + } + } + v[0] = str; + } + + v[1] = ( m[1] && v[1] ) ? decimal + v[1] : ""; + return ( isNegative ? '-' : '' ) + v[0] + v[1]; // put back any negation and combine integer and fraction. + }; + + ts.equations = { + count : function(arry) { + return arry.length; + }, + sum : function(arry) { + var total = 0; + $.each( arry, function(i) { + total += arry[i]; + }); + return total; + }, + mean : function(arry) { + var total = ts.equations.sum( arry ); + return total / arry.length; + }, + median : function(arry) { + // https://gist.github.com/caseyjustus/1166258 + arry.sort( function(a,b){ return a - b; } ); + var half = Math.floor( arry.length / 2 ); + return (arry.length % 2) ? arry[half] : ( arry[half - 1] + arry[half] ) / 2.0; + }, + mode : function(arry) { + // http://stackoverflow.com/a/3451640/145346 + if ( arry.length === 0 ) { return 'none'; } + var i, el, + modeMap = {}, + maxCount = 1, + modes = [arry[0]]; + for (i = 0; i < arry.length; i++) { + el = arry[i]; + modeMap[el] = modeMap[el] ? modeMap[el] + 1 : 1; + if ( modeMap[el] > maxCount ) { + modes = [el]; + maxCount = modeMap[el]; + } else if (modeMap[el] === maxCount) { + modes.push(el); + maxCount = modeMap[el]; + } + } + // returns arry of modes if there is a tie + return modes.sort( function(a,b){ return a - b; } ); + }, + max : function(arry) { + return Math.max.apply( Math, arry ); + }, + min : function(arry) { + return Math.min.apply( Math, arry ); + }, + range: function(arry) { + var v = arry.sort(function(a,b){ return a - b; }); + return v[ arry.length - 1 ] - v[0]; + }, + // common variance equation + // (not accessible via data-attribute setting) + variance: function(arry, population) { + var avg = ts.equations.mean( arry ), + v = 0, + i = arry.length; + while (i--) { + v += Math.pow( ( arry[i] - avg ), 2 ); + } + v /= ( arry.length - (population ? 0 : 1) ); + return v; + }, + // variance (population) + varp : function(arry) { + return ts.equations.variance(arry, true); + }, + // variance (sample) + vars : function(arry) { + return ts.equations.variance(arry); + }, + // standard deviation (sample) + stdevs : function(arry) { + var vars = ts.equations.variance(arry); + return Math.sqrt( vars ); + }, + // standard deviation (population) + stdevp : function(arry){ + var varp = ts.equations.variance(arry, true); + return Math.sqrt( varp ); + } + }; + + // add new widget called repeatHeaders + // ************************************ + ts.addWidget({ + id: "math", + priority: 100, + options: { + math_data : 'math', + // column index to ignore + math_ignore : [], + // mask info: https://code.google.com/p/javascript-number-formatter/ + math_mask : '#,##0.00', + // complete executed after each fucntion + math_complete : null, // function($cell, wo, result, value, arry){ return result; }, + // order of calculation; "all" is last + math_priority : [ 'row', 'above', 'col' ] + }, + init : function(table, thisWidget, c, wo){ + c.$table + .bind('tablesorter-initialized update updateRows addRows updateCell filterReset filterEnd '.split(' ').join('.tsmath '), function(e){ + if (!wo.math_isUpdating) { + math.recalculate(table, c, wo, e.type === 'tablesorter-initialized'); + } + }) + .bind('updateComplete.tsmath', function(){ + setTimeout(function(){ + wo.math_isUpdating = false; + }, 500); + }); + wo.math_isUpdating = false; + }, + // this remove function is called when using the refreshWidgets method or when destroying the tablesorter plugin + // this function only applies to tablesorter v2.4+ + remove: function(table, c, wo){ + $(table) + .unbind('tablesorter-initialized update updateRows addRows updateCell filterReset filterEnd '.split(' ').join('.tsmath ')) + .find('[data-' + wo.math_data + ']').empty(); + } + }); + +})(jQuery); \ No newline at end of file diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js new file mode 100644 index 0000000..c1cdb21 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -0,0 +1,272 @@ +/* Output widget (beta) for TableSorter 4/14/2014 (v2.16.0) + * Requires tablesorter v2.8+ and jQuery 1.7+ + * Modified from: + * HTML Table to CSV: http://www.kunalbabre.com/projects/table2CSV.php (License unknown?) + * Download-File-JS: https://github.com/PixelsCommander/Download-File-JS (http://www.apache.org/licenses/LICENSE-2.0) + */ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;(function($){ +"use strict"; + +var ts = $.tablesorter, + +output = ts.output = { + + event : 'outputTable', + + // wrap line breaks & tabs in quotes + regexQuote : /([\n\t]|<[^<]+>)/, // test + regexBR : /(<br([\s\/])?>|\n)/g, // replace + regexIMG : /<img[^>]+alt\s*=\s*['"]([^'"]+)['"][^>]*>/i, // match + regexHTML : /<[^<]+>/g, // replace + + replaceCR : '\\n', + replaceTab : '\\t', + + popupTitle : 'Output', + popupStyle : 'width:100%;height:100%;', // for textarea + message : 'Your device does not support downloading. Please try again in desktop browser.', + + init : function(c) { + c.$table + .off(output.event) + .on(output.event, function(){ + // explicitly use table.config.widgetOptions because we want + // the most up-to-date values; not the "wo" from initialization + output.process(c, c.widgetOptions); + }); + }, + + processRow: function(c, $rows, isHeader, isJSON) { + var $this, row, col, rowlen, collen, txt, + wo = c.widgetOptions, + tmpRow = [], + addSpanIndex = isHeader && isJSON && wo.output_headerRows && $.isFunction(wo.output_callbackJSON), + rowIndex = 0, + cellIndex = 0; + $rows.each(function(rowIndex) { + if (!tmpRow[rowIndex]) { tmpRow[rowIndex] = []; } + cellIndex = 0; + $(this).children().each(function(){ + $this = $(this); + // process rowspans + if ($this.filter('[rowspan]').length) { + rowlen = parseInt( $this.attr('rowspan'), 10) - 1; + txt = output.formatData( wo, $this.attr(wo.output_dataAttrib) || $this.html(), isHeader ); + for (row = 1; row <= rowlen; row++) { + if (!tmpRow[rowIndex + row]) { tmpRow[rowIndex + row] = []; } + tmpRow[rowIndex + row][cellIndex] = txt; + } + } + // process colspans + if ($this.filter('[colspan]').length) { + collen = parseInt( $this.attr('colspan'), 10) - 1; + txt = output.formatData( wo, $this.attr(wo.output_dataAttrib) || $this.html(), isHeader ); + for (col = 1; col <= collen; col++) { + // if we're processing the header & making JSON, the header names need to be unique + if ($this.filter('[rowspan]').length) { + rowlen = parseInt( $this.attr('rowspan'), 10); + for (row = 0; row < rowlen; row++) { + if (!tmpRow[rowIndex + row]) { tmpRow[rowIndex + row] = []; } + tmpRow[rowIndex + row][cellIndex + col] = addSpanIndex ? + wo.output_callbackJSON($this, txt, cellIndex + col) || txt + '(' + (cellIndex + col) + ')' : txt; + } + } else { + tmpRow[rowIndex][cellIndex + col] = addSpanIndex ? + wo.output_callbackJSON($this, txt, cellIndex + col) || txt + '(' + (cellIndex + col) + ')' : txt; + } + } + } + + // don't include hidden columns + if ( $this.css('display') !== 'none' ) { + // skip column if already defined + while (tmpRow[rowIndex][cellIndex]) { cellIndex++; } + tmpRow[rowIndex][cellIndex] = tmpRow[rowIndex][cellIndex] || + output.formatData( wo, $this.attr(wo.output_dataAttrib) || $this.html(), isHeader ); + cellIndex++; + } + }); + }); + return tmpRow; + }, + + process : function(c, wo) { + var mydata, $this, $rows, headers, csvData, len, + hasStringify = window.JSON && JSON.hasOwnProperty('stringify'), + indx = 0, + tmpData = (wo.output_separator || ',').toLowerCase(), + outputJSON = tmpData === 'json', + outputArray = tmpData === 'array', + separator = outputJSON || outputArray ? ',' : wo.output_separator, + $el = c.$table; + // regex to look for the set separator or HTML + wo.output_regex = new RegExp('(' + (/\\/.test(separator) ? '\\' : '' ) + separator + ')' ); + + // get header cells + $this = $el.find('thead tr:visible').not('.' + (ts.css.filterRow || 'tablesorter-filter-row') ); + headers = output.processRow(c, $this, true, outputJSON); + + // all tbody rows + $rows = $el.children('tbody').children('tr'); + // get (f)iltered, (v)isible or all rows (look for the first letter only) + $rows = /f/.test(wo.output_saveRows) ? $rows.not('.' + (wo.filter_filteredRow || 'filtered') ) : + /v/.test(wo.output_saveRows) ? $rows.filter(':visible') : $rows; + + // process to array of arrays + csvData = output.processRow(c, $rows); + len = headers.length; + + if (outputJSON) { + tmpData = []; + $.each( csvData, function(indx, val){ + // multiple header rows & output_headerRows = true, pick the last row... + tmpData.push( output.row2Hash( headers[ (len > 1 && wo.output_headerRows) ? indx % len : len - 1], val ) ); + }); + // requires JSON stringify; if it doesn't exist, the output will show [object Object],... in the output window + mydata = hasStringify ? JSON.stringify(tmpData) : tmpData; + } else { + tmpData = output.row2CSV(wo, wo.output_headerRows ? headers : [ headers[ (len > 1 && wo.output_headerRows) ? indx % len : len - 1] ], outputArray) + .concat( output.row2CSV(wo, csvData, outputArray) ); + // stringify the array; if stringify doesn't exist the array will be flattened + mydata = outputArray && hasStringify ? JSON.stringify(tmpData) : tmpData.join('\n'); + } + + // callback; if true returned, continue processing + if (!wo.output_callback(mydata)) { return; } + + if ( /p/.test( (wo.output_delivery || '').toLowerCase() ) ) { + output.popup(mydata, wo.output_popupStyle, outputJSON || outputArray); + } else { + output.download(wo, mydata); + } + + }, // end process + + row2CSV : function(wo, tmpRow, outputArray) { + var tmp, rowIndex, + csvData = [], + rowLen = tmpRow.length; + for (rowIndex = 0; rowIndex < rowLen; rowIndex++) { + // remove any blank rows + tmp = tmpRow[rowIndex].join('').replace(/\"/g,''); + if (tmpRow[rowIndex].length > 0 && tmp !== '') { + csvData[csvData.length] = outputArray ? tmpRow[rowIndex] : tmpRow[rowIndex].join(wo.output_separator); + } + } + return csvData; + }, + + row2Hash : function(keys, values) { + var json = {}; + $.each(values, function(indx, val) { + if ( indx < keys.length ) { + json[ keys[indx] ] = val; + } + }); + return json; + }, + + formatData : function(wo, input, isHeader) { + var txt, + quotes = (wo.output_separator || ',').toLowerCase(), + separator = quotes === 'json' || quotes === 'array', + // replace " with “ if undefined + result = input.replace(/\"/g, wo.output_replaceQuote || '\u201c'); + // replace line breaks with \\n & tabs with \\t + result = result.replace(output.regexBR, output.replaceCR).replace(/\t/g, output.replaceTab); + // extract img alt text + txt = result.match(output.regexIMG); + if (!wo.output_includeHTML && txt !== null) { + result = txt[1]; + } + // replace/remove html + result = wo.output_includeHTML && !isHeader ? result : result.replace(output.regexHTML, ''); + result = wo.output_trimSpaces || isHeader ? $.trim(result) : result; + // JSON & array outputs don't need quotes + quotes = separator ? false : wo.output_wrapQuotes || wo.output_regex.test(result) || output.regexQuote.test(result); + return quotes ? '"' + result + '"' : result; + }, + + popup : function(data, style, wrap) { + var generator = window.open('', output.popupTitle, style); + generator.document.write( + '<html><head><title>' + output.popupTitle + '</title></head><body>' + + '<textarea wrap="' + (wrap ? 'on' : 'off') + '" style="' + output.popupStyle + '">' + data + '\n</textarea>' + + '</body></html>' + ); + generator.document.close(); + generator.focus(); + // select all text and focus within the textarea in the popup + // $(generator.document).find('textarea').select().focus(); + return true; + }, + + // modified from https://github.com/PixelsCommander/Download-File-JS + download : function (wo, data){ + var e, link, + processedData = 'data:text/csv;charset=utf8,' + encodeURIComponent(data); + + // iOS devices do not support downloading. We have to inform user about this. + if (/(iP)/g.test(navigator.userAgent)) { + alert(output.message); + return false; + } + // If in Chrome or Safari - download via virtual link click + if ( /(chrome|safari)/.test(navigator.userAgent.toLowerCase()) ) { + // Creating new link node. + link = document.createElement('a'); + link.href = processedData; + link.download = wo.output_saveFileName; + // Dispatching click event. + if (document.createEvent) { + e = document.createEvent('MouseEvents'); + e.initEvent('click', true, true); + link.dispatchEvent(e); + return true; + } + } + // Force file download (whether supported by server). + processedData += '?download'; + window.open(processedData, '_self'); + return true; + }, + + remove : function(c) { + c.$table.off(output.event); + } + +}; + +ts.addWidget({ + id: "output", + options: { + output_separator : ',', // set to "json", "array" or any separator + output_dataAttrib : 'data-name', // header attrib containing modified header name + output_headerRows : false, // if true, include multiple header rows (JSON only) + output_delivery : 'popup', // popup, download + output_saveRows : 'filtered', // all, visible or filtered + output_replaceQuote : '\u201c;', // left double quote + output_includeHTML : false, + output_trimSpaces : true, + output_wrapQuotes : false, + output_popupStyle : 'width=500,height=300', + output_saveFileName : 'mytable.csv', + // callback executed when processing completes + // return true to continue download/output + // return false to stop delivery & do something else with the data + output_callback : function(data){ return true; }, + // JSON callback executed when a colspan is encountered in the header + output_callbackJSON : function($cell, txt, cellIndex) { return txt + '(' + (cellIndex + col) + ')'; } + }, + init: function(table, thisWidget, c) { + output.init(c); + }, + remove: function(table, c){ + output.remove(c); + } + +}); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 5d24257..4cb4c3b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/* Pager widget (beta) for TableSorter 4/10/2014 (v2.15.14) */ +/* Pager widget (beta) for TableSorter 4/23/2014 (v2.16.0) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ "use strict"; @@ -19,6 +19,10 @@ ts.addWidget({ // starting page of the pager (zero based index) pager_startPage: 0, + // reset pager after filtering; set to desired page # + // set to false to not change page at filter start + pager_pageReset: 0, + // Number of visible rows pager_size: 10, @@ -164,6 +168,10 @@ tsp = ts.pager = { p.size = ( isNaN(t.size) ? p.size : t.size ) || 10; $.data(table, 'pagerLastSize', p.size); } + + // skipped rows + p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.substring(1) + '|' + c.cssChildRow + ')'); + // clear initialized flag p.initialized = false; // before initialization event @@ -210,13 +218,17 @@ tsp = ts.pager = { .unbind('filterStart filterEnd sortEnd disable enable destroy update updateRows updateAll addRows pageSize '.split(' ').join('.pager ')) .bind('filterStart.pager', function(e, filters) { p.currentFilters = filters; - p.page = 0; // fixes #456 + // don't change page is filters are the same (pager updating, etc) + if (wo.pager_pageReset !== false && (c.lastCombinedFilter || '') !== (filters || []).join('')) { + p.page = wo.pager_pageReset; // fixes #456 & #565 + } }) // update pager after filter widget completes .bind('filterEnd.pager sortEnd.pager', function() { if (p.initialized) { - tsp.moveToPage(table, p, false); + // update page display first, so we update p.filteredPages tsp.updatePageDisplay(table, c, false); + tsp.moveToPage(table, p, false); tsp.fixHeight(table, c); } }) @@ -315,17 +327,25 @@ tsp = ts.pager = { }, updatePageDisplay: function(table, c, completed) { - var i, pg, s, out, + var i, pg, s, out, regex, wo = c.widgetOptions, p = c.pager, f = c.$table.hasClass('hasFilters') && !wo.pager_ajaxUrl, - t = (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered') + ',' + c.selectorRemove + - (wo.pager_countChildRows ? '' : ',.' + c.cssChildRow), + t = [], sz = p.size || 10; // don't allow dividing by zero + t = [ wo && wo.filter_filteredRow || 'filtered', c.selectorRemove ]; + if (wo.pager_countChildRows) { t.push(c.cssChildRow); } + regex = new RegExp( '(' + t.join('|') + ')' ); p.$size.add(p.$goto).removeClass(wo.pager_css.disabled).removeAttr('disabled').attr('aria-disabled', 'false'); p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method - p.filteredRows = (f) ? c.$tbodies.eq(0).children('tr').not('.' + t).length : p.totalRows; - p.filteredPages = (f) ? Math.ceil( p.filteredRows / sz ) || 1 : p.totalPages; + p.filteredRows = (f) ? 0 : p.totalRows; + p.filteredPages = p.totalPages; + if (f) { + $.each(c.cache[0].normalized, function(i, el) { + p.filteredRows += p.regexRows.test(el[c.columns].$row[0].className) ? 0 : 1; + }); + p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; + } if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) { t = (p.size * p.page > p.filteredRows); p.startRow = (t) ? 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1); @@ -579,9 +599,9 @@ tsp = ts.pager = { } tsp.renderAjax(data, table, c); $doc.unbind('ajaxError.pager'); - if (typeof p.oldAjaxSuccess === 'function') { - p.oldAjaxSuccess(data); - } + if (typeof p.oldAjaxSuccess === 'function') { + p.oldAjaxSuccess(data); + } }; if (c.debug) { ts.log('ajax initialized', wo.pager_ajaxObject); @@ -632,13 +652,14 @@ tsp = ts.pager = { }, renderTable: function(table, rows) { - var i, $tb, + var $tb, index, count, added, c = table.config, p = c.pager, wo = c.widgetOptions, + f = c.$table.hasClass('hasFilters'), l = rows && rows.length || 0, // rows may be undefined s = ( p.page * p.size ), - e = ( s + p.size ); + e = p.size; if ( l < 1 ) { return; } // empty table, abort! if ( p.page >= p.totalPages ) { // lets not render the table more than once @@ -650,13 +671,22 @@ tsp = ts.pager = { if ( !wo.pager_removeRows ) { tsp.hideRows(table, c); } else { - if ( e > rows.length ) { - e = rows.length; - } ts.clearTableBody(table); $tb = ts.processTbody(table, c.$tbodies.eq(0), true); - for ( i = s; i < e; i++ ) { - $tb.append(rows[i]); + // not filtered, start from the calculated starting point (s) + // if filtered, start from zero + index = f ? 0 : s; + count = f ? 0 : s; + added = 0; + while (added < e && index < rows.length) { + if (!f || !/filtered/.test(rows[index][0].className)){ + count++; + if (count > s && added <= e) { + added++; + $tb.append(rows[index]); + } + } + index++; } ts.processTbody(table, $tb, false); } @@ -785,6 +815,7 @@ tsp = ts.pager = { p.$container.hide(); // hide pager c.appender = null; // remove pager appender function p.initialized = false; + delete table.config.rowsCopy; c.$table.unbind('destroy.pager sortEnd.pager filterEnd.pager enable.pager disable.pager'); if (ts.storage) { ts.storage(table, c.widgetOptions.pager_storageKey, ''); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-reflow.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-reflow.js new file mode 100644 index 0000000..5e163c9 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-reflow.js @@ -0,0 +1,179 @@ +/* table reflow widget (beta) for TableSorter 3/22/2014 (v2.16.0) + * Requires tablesorter v2.8+ and jQuery 1.7+ + * Also, this widget requires the following default css (modify as desired) + + / * REQUIRED CSS: change your reflow breakpoint here (35em below) * / + @media ( max-width: 35em ) { + .ui-table-reflow td, + .ui-table-reflow th { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + float: right; + / * if not using the stickyHeaders widget (not the css3 version) + * the "!important" flag, and "height: auto" can be removed * / + width: 100% !important; + height: auto !important; + } + / * reflow widget * / + .ui-table-reflow tbody td[data-title]:before { + color: #469; + font-size: .9em; + content: attr(data-title); + float: left; + width: 50%; + white-space: pre-wrap; + text-align: bottom; + display: inline-block; + } + / * reflow2 widget * / + table.ui-table-reflow .ui-table-cell-label.ui-table-cell-label-top { + display: block; + padding: .4em 0; + margin: .4em 0; + text-transform: uppercase; + font-size: .9em; + font-weight: 400; + } + table.ui-table-reflow .ui-table-cell-label { + padding: .4em; + min-width: 30%; + display: inline-block; + margin: -.4em 1em -.4em -.4em; + } + } + .ui-table-reflow .ui-table-cell-label { + display: none; + } + + */ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;(function($){ +"use strict"; + +var ts = $.tablesorter, + +tablereflow = { + // simple reflow + // add data-attribute to each cell which shows when media query is active + // this widget DOES NOT WORK on a table with multiple thead rows + init : function(table, c, wo) { + var $this, + title = wo.reflow_dataAttrib, + header = wo.reflow_headerAttrib, + headers = []; + c.$table + .addClass(wo.reflow_className) + .off('refresh.tsreflow updateComplete.tsreflow2') + // emulate jQuery Mobile refresh + // https://api.jquerymobile.com/table-reflow/#method-refresh + .on('refresh.tsreflow updateComplete.tsreflow2', function(){ + tablereflow.init(table, c, wo); + }); + c.$headers.each(function(){ + $this = $(this); + headers.push( $this.attr(header) || $this.text() ); + }); + c.$tbodies.children().each(function(){ + $(this).children().each(function(i){ + $(this).attr(title, headers[i]); + }); + }); + }, + init2: function(table, c, wo) { + var $this, $tbody, i, $hdr, txt, len, + cols = c.columns, + header = wo.reflow2_headerAttrib, + headers = []; + c.$table + .addClass(wo.reflow2_className) + .off('refresh.tsreflow2 updateComplete.tsreflow2') + // emulate jQuery Mobile refresh + // https://api.jquerymobile.com/table-reflow/#method-refresh + .on('refresh.tsreflow2 updateComplete.tsreflow2', function(){ + tablereflow.init2(table, c, wo); + }); + + // add <b> to every table cell with thead cell contents + for (i = 0; i < cols; i++) { + $hdr = c.$headers.filter('[data-column="' + i + '"]'); + if ($hdr.length > 1) { + txt = []; + $hdr.each(function(){ + $this = $(this); + if (!$this.hasClass(wo.reflow2_classIgnore)) { + txt.push( $this.attr(header) || $this.text() ); + } + }); + } else { + txt = [ $hdr.attr(header) || $hdr.text() ]; + } + headers.push( txt ); + } + // include "remove-me" class so these additional elements are removed before updating + txt = '<b class="' + c.selectorRemove.slice(1) + ' ' + wo.reflow2_labelClass; + c.$tbodies.children().each(function(){ + $tbody = ts.processTbody(table, $(this), true); + $tbody.children().each(function(j){ + $this = $(this); + len = headers[j].length + i = len - 1; + while (i >= 0) { + $this.prepend(txt + (i === 0 && len > 1 ? ' ' + wo.reflow2_labelTop : '') + '">' + headers[j][i] + '</b>'); + i--; + } + }); + ts.processTbody(table, $tbody, false); + }); + }, + remove : function(table, c, wo) { + c.$table.removeClass(wo.reflow_className); + }, + remove2 : function(table, c, wo) { + c.$table.removeClass(wo.reflow2_className); + } +}; + +ts.addWidget({ + id: "reflow", + options: { + // class name added to make it responsive (class name within media query) + reflow_className : 'ui-table-reflow', + // header attribute containing modified header name + reflow_headerAttrib : 'data-name', + // data attribute added to each tbody cell + reflow_dataAttrib : 'data-title' + }, + init: function(table, thisWidget, c, wo) { + tablereflow.init(table, c, wo); + }, + remove: function(table, c, wo){ + tablereflow.remove(table, c, wo); + } +}); + +ts.addWidget({ + id: "reflow2", + options: { + // class name added to make it responsive (class name within media query) + reflow2_className : 'ui-table-reflow', + // ignore header cell content with this class name + reflow2_classIgnore : 'ui-table-reflow-ignore', + // header attribute containing modified header name + reflow2_headerAttrib : 'data-name', + // class name applied to thead labels + reflow2_labelClass : 'ui-table-cell-label', + // class name applied to first row thead label + reflow2_labelTop : 'ui-table-cell-label-top' + }, + init: function(table, thisWidget, c, wo) { + tablereflow.init2(table, c, wo); + }, + remove: function(table, c, wo){ + tablereflow.remove2(table, c, wo); + } +}); + + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index cc66726..dc9fe36 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -10,9 +10,9 @@ Resizable scroller widget for the jQuery tablesorter plugin - Version 2.0 - modified by Rob Garrison (4/12/2013) - Requires jQuery, v1.2.3 or higher - Requires the tablesorter plugin, v2.0 or higher, available at http://mottie.github.com/tablesorter/docs/ + Version 2.0 - modified by Rob Garrison (4/12/2013; updated 4/20/2014 for tablesorter v2.16.0) + Requires jQuery v1.7+ + Requires the tablesorter plugin, v2.8+, available at http://mottie.github.com/tablesorter/docs/ Usage: @@ -76,7 +76,7 @@ ts.addWidget({ //Setup window.resizeEnd event $win .bind('resize', ts.window_resize) - .bind('resizeEnd', function(e) { + .bind('resizeEnd', function() { // init is run before format, so scroller_resizeWidth // won't be defined within the "c" or "wo" parameters if (typeof table.config.widgetOptions.scroller_resizeWidth === 'function') { @@ -91,9 +91,7 @@ ts.addWidget({ format: function(table, c, wo) { var h, $hdr, id, t, resize, $cells, $win = $(window), - $tbl = c.$table, - flag = false, - filterInputs = 'input, select'; + $tbl = c.$table; if (!c.isScrolling) { h = wo.scroller_height || 300; @@ -109,11 +107,7 @@ ts.addWidget({ $cells = $hdr .wrap('<div class="tablesorter-scroller-header" style="width:' + $tbl.width() + ';" />') - .find('.' + ts.css.header) - .bind('mousedown', function(){ - this.onselectstart = function(){ return false; }; - return false; - }); + .find('.' + ts.css.header); $tbl .wrap('<div class="tablesorter-scroller-table" style="height:' + h + 'px;width:' + $tbl.width() + ';overflow-y:scroll;" />') @@ -134,39 +128,12 @@ ts.addWidget({ }); // make scroller header sortable - c.$headers.find(c.selectorSort).add( c.$headers.filter(c.selectorSort) ).each(function(i){ - var t = $(this); - $cells.eq(i) - // clicking on new header will trigger a sort - .bind('mouseup', function(e){ - t.trigger(e, true); // external mouseup flag (click timer is ignored) - }) - // prevent header text selection - .bind('mousedown', function(){ - this.onselectstart = function(){ return false; }; - return false; - }); - }); + ts.bindEvents(table, $cells); // look for filter widget - $tbl.bind('filterEnd', function(){ - if (flag) { return; } - $cells.each(function(i){ - $(this).find(filterInputs).val( c.$filters.find(filterInputs).eq(i).val() ); - }); - }); - $hdr.find(filterInputs).bind('keyup search', function(e){ - // ignore arrow and meta keys; allow backspace - if ((e.which < 32 && e.which !== 8) || (e.which >= 37 && e.which <=40)) { return; } - flag = true; - var $f = $(this), col = $f.attr('data-column'); - c.$filters.find(filterInputs).eq(col) - .val( $f.val() ) - .trigger('search'); - setTimeout(function(){ - flag = false; - }, wo.filter_searchDelay); - }); + if ($tbl.hasClass('hasFilters')) { + ts.filter.bindSearch( $tbl, $cells.find('.' + ts.css.filter) ); + } resize = function(){ var d, @@ -234,8 +201,14 @@ ts.addWidget({ }, remove : function(table, c, wo){ - + var $table = c.$table; + $table.closest('.tablesorter-scroller').find('.tablesorter-scroller-header').remove(); + $table + .unwrap() + .find('.tablesorter-filter-row').removeClass('hideme').end() + .find('thead').show().css('visibility', 'visible'); + c.isScrolling = false; } - }); +}); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js new file mode 100644 index 0000000..3089ed7 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js @@ -0,0 +1,123 @@ +/* + * StaticRow widget for jQuery TableSorter 2.0 + * Version 1.1 - modified by Rob Garrison (4/22/2014 for tablesorter v2.16.1-beta) + * Requires: + * jQuery v1.4+ + * tablesorter plugin, v2.8+, available at http://mottie.github.com/tablesorter/docs/ + * + * Copyright (c) 2011 Nils Luxton + * Licensed under the MIT license: + * http://www.opensource.org/licenses/mit-license.php + * + */ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;(function($){ +"use strict"; +var ts = $.tablesorter, + +// events triggered on the table that update this widget +events = 'staticRowsRefresh updateComplete '.split(' ').join('.tsstaticrows '), + +// add/refresh row indexes +addIndexes = function(table){ + var $tr, wo, v, indx, rows, + c = table.config; + // "Index" the static rows, saving their current (starting) position in the + // table inside a data() param on the <tr> element itself for later use. + if (c) { + wo = c.widgetOptions; + c.$tbodies.each(function(){ + $tr = $(this).children(); + rows = $tr.length; + $tr.filter(wo.staticRow_class).each(function() { + $tr = $(this); + indx = $tr.data(wo.staticRow_index); + if (typeof indx !== "undefined") { + v = parseFloat(indx); + // percentage of total rows + indx = (/%/.test(indx)) ? Math.round(v/100 * rows) : v; + } else { + indx = $tr.index(); + } + // row indexing starts over within each tbody + $tr.data( wo.staticRow_data, indx ); + }); + }); + } +}; + +ts.addWidget({ + // Give the new Widget an ID to be used in the tablesorter() call, as follows: + // $('#myElement').tablesorter({ widgets: ['zebra', 'staticRow'] }); + id: 'staticRow', + + options: { + staticRow_class : '.static', + staticRow_data : 'static-index', + staticRow_index : 'row-index' + }, + + init: function(table, thisWidget, c, wo){ + addIndexes(table); + // refresh static rows after updates + c.$table + .unbind(events) + .bind(events, function(){ + addIndexes(table); + c.$table.trigger('applyWidgets'); + }); + }, + + format: function(table, c, wo) { + // Loop thru static rows, moving them to their original "indexed" position, + // & repeat until no more re-shuffling is needed + var targetIndex, $thisRow, indx, numRows, $tbody, hasShuffled, $rows, max; + c.$tbodies.each(function(){ + $tbody = $(this); + hasShuffled = true; + indx = 0; + $rows = $tbody.children(wo.staticRow_class); + numRows = $tbody.children('tr').length - 1; + max = $rows.length; + + // don't allow the while loop to cycle more times than the set number of static rows + while (hasShuffled && indx < max) { + hasShuffled = false; + + /*jshint loopfunc:true */ + $rows.each(function() { + targetIndex = $(this).data(wo.staticRow_data); + // allow setting target index >> num rows to always make a row last + targetIndex = targetIndex >= numRows ? numRows : targetIndex < 0 ? 0 : targetIndex; + if (targetIndex !== $(this).index()) { + hasShuffled = true; + $thisRow = $(this).detach(); + + if (targetIndex >= numRows) { + // Are we trying to be the last row? + $thisRow.appendTo( $tbody ); + } else if (targetIndex === 0) { + // Are we trying to be the first row? + $thisRow.prependTo( $tbody ); + } else { + // No, we want to be somewhere in the middle! + $thisRow.insertBefore( $tbody.find('tr:eq(' + targetIndex + ')') ); + } + } + }); + indx++; + + } + + }); + c.$table.trigger('staticRowsComplete', table); + }, + + remove : function(table, c, wo){ + c.$table.unbind(events); + } + +}); + +})(jQuery); \ No newline at end of file From 9663717db8bb1ecca36a3a2a5d3ebe9937465a02 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Mon, 28 Apr 2014 19:58:58 +0200 Subject: [PATCH 028/138] * updated tablesorter to latest version (2.16.2) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery-tablesorter/jquery.tablesorter.js | 45 ++++++++------- ...ry.tablesorter.widgets-filter-formatter.js | 30 +++++----- .../jquery.tablesorter.widgets.js | 57 +++++++++---------- .../parsers/parser-input-select.js | 12 +++- .../jquery-tablesorter/widgets/widget-math.js | 5 +- .../widgets/widget-scroller.js | 20 +------ 10 files changed, 90 insertions(+), 89 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf8c674..44c003a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.11.1 (2014-04-28) + +* Upgrade tablesorter to v2.16.2 + #### v1.11.0 (2014-04-24) * Upgrade tablesorter to v2.16.1 diff --git a/README.md b/README.md index 47a2124..fb86070 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.16.1 (4/24/2014), [documentation] +Current tablesorter version: 2.16.2 (4/27/2014), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 5a62f49..eff2c5f 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.11.0' + VERSION = '1.11.1' end diff --git a/tablesorter b/tablesorter index e658868..f4cded6 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit e658868573097c6ecb9eedfaf42ee62faf7a816b +Subproject commit f4cded6c6cfc872b9d7abe174c656d442b3d7857 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index c1796ae..826c0d7 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.16.1 - Client-side table sorting with ease! +* TableSorter 2.16.2 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.16.1"; + ts.version = "2.16.2"; ts.parsers = []; ts.widgets = []; @@ -489,34 +489,34 @@ } function setHeadersCss(table) { - var f, i, j, l, + var f, i, j, c = table.config, list = c.sortList, + len = list.length, none = ts.css.sortNone + ' ' + c.cssNone, css = [ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc], aria = ['ascending', 'descending'], // find the footer - $t = $(table).find('tfoot tr').children().removeClass(css.join(' ')); + $t = $(table).find('tfoot tr').children().add(c.$extraHeaders).removeClass(css.join(' ')); // remove all header information c.$headers .removeClass(css.join(' ')) .addClass(none).attr('aria-sort', 'none'); - l = list.length; - for (i = 0; i < l; i++) { + for (i = 0; i < len; i++) { // direction = 2 means reset! if (list[i][1] !== 2) { // multicolumn sorting updating - choose the :last in case there are nested columns - f = c.$headers.not('.sorter-false').filter('[data-column="' + list[i][0] + '"]' + (l === 1 ? ':last' : '') ); + f = c.$headers.not('.sorter-false').filter('[data-column="' + list[i][0] + '"]' + (len === 1 ? ':last' : '') ); if (f.length) { for (j = 0; j < f.length; j++) { if (!f[j].sortDisabled) { f.eq(j).removeClass(none).addClass(css[list[i][1]]).attr('aria-sort', aria[list[i][1]]); - // add sorted class to footer, if it exists - if ($t.length) { - $t.filter('[data-column="' + list[i][0] + '"]').eq(j).addClass(css[list[i][1]]); - } } } + // add sorted class to footer & extra headers, if they exist + if ($t.length) { + $t.filter('[data-column="' + list[i][0] + '"]').removeClass(none).addClass(css[list[i][1]]); + } } } } @@ -544,7 +544,7 @@ } } - function updateHeaderSortCount(table, list) { + function updateHeaderSortCount(table, list, triggered) { var s, t, o, c = table.config, sl = list || c.sortList; c.sortList = []; @@ -552,10 +552,11 @@ // ensure all sortList values are numeric - fixes #127 s = [ parseInt(v[0], 10), parseInt(v[1], 10) ]; // make sure header exists - o = c.$headers[s[0]]; + o = c.$headers.filter('[data-column="' + s[0] + '"]:last')[0]; if (o) { // prevents error if sorton array is wrong c.sortList.push(s); t = $.inArray(s[1], o.order); // fixes issue #167 + if (triggered) { o.count = o.count + 1; } o.count = t >= 0 ? t : s[1] % (c.sortReset ? 3 : 2); } }); @@ -625,7 +626,7 @@ // reverse the sorting direction for (col = 0; col < c.sortList.length; col++) { s = c.sortList[col]; - order = c.$headers[s[0]]; + order = c.$headers.filter('[data-column="' + s[0] + '"]:last')[0]; if (s[0] === indx) { // order.count seems to be incorrect when compared to cell.count s[1] = order.order[cell.count]; @@ -782,7 +783,7 @@ ts.refreshWidgets(table, true, true); ts.restoreHeaders(table); buildHeaders(table); - ts.bindEvents(table, c.$headers); + ts.bindEvents(table, c.$headers, true); bindMethods(table); commonUpdate(table, resort, callback); }) @@ -827,6 +828,7 @@ updateHeader(table); commonUpdate(table, resort, callback); } else { + $row = $($row); // make sure we're using a jQuery object var i, j, l, rowData, cells, rows = $row.filter('tr').length, tbdy = $table.find('tbody').index( $row.parents('tbody').filter(':first') ); @@ -868,7 +870,7 @@ e.stopPropagation(); $table.trigger("sortStart", this); // update header count index - updateHeaderSortCount(table, list); + updateHeaderSortCount(table, list, true); // set css for headers setHeadersCss(table); // fixes #346 @@ -961,7 +963,7 @@ return (version[0] > 1) || (version[0] === 1 && parseInt(version[1], 10) >= 4); })($.fn.jquery.split(".")); // digit sort text location; keeping max+/- for backwards compatibility - c.string = { 'max': 1, 'min': -1, 'max+': 1, 'max-': -1, 'zero': 0, 'none': 0, 'null': 0, 'top': true, 'bottom': false }; + c.string = { 'max': 1, 'min': -1, 'emptyMin': 1, 'emptyMax': -1, 'zero': 0, 'none': 0, 'null': 0, 'top': true, 'bottom': false }; // add table theme class only if there isn't already one there if (!/tablesorter\-/.test($table.attr('class'))) { k = (c.theme !== '' ? ' tablesorter-' + c.theme : ''); @@ -999,7 +1001,7 @@ // delayInit will delay building the cache until the user starts a sort if (!c.delayInit) { buildCache(table); } // bind all header events and methods - ts.bindEvents(table, c.$headers); + ts.bindEvents(table, c.$headers, true); bindMethods(table); // get sort list from jQuery data or metadata // in jQuery < 1.4, an error occurs when calling $table.data() @@ -1132,10 +1134,13 @@ $(table)[0].config.$tbodies.empty(); }; - ts.bindEvents = function(table, $headers){ + ts.bindEvents = function(table, $headers, core){ table = $(table)[0]; var downTime, - c = table.config; + c = table.config; + if (core !== true) { + c.$extraHeaders = c.$extraHeaders ? c.$extraHeaders.add($headers) : $headers; + } // apply event handling to headers and/or additional headers (stickyheaders, scroller, etc) $headers // http://stackoverflow.com/questions/5312849/jquery-find-self; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js index 9f3a3db..a3088ff 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js @@ -1,4 +1,4 @@ -/*! Filter widget formatter functions - updated 4/23/2014 (v2.16.0) +/*! Filter widget formatter functions - updated 4/27/2014 (v2.16.2) * requires: tableSorter 2.15+ and jQuery 1.4.3+ * * uiSpinner (jQuery UI spinner) @@ -517,7 +517,7 @@ tsff = ts.filterFormatter = { getdate = $date.datepicker('getDate') || '', compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', searchType = c.$table[0].hasInitialized ? o.delayed || '': true; - $date.datepicker('setDate', getdate === '' ? '' : getdate); + $date.datepicker('setDate', (getdate === '' ? '' : getdate) || null); if (getdate === '') { notrigger = false; } date = $date.datepicker('getDate'); query = date ? ( o.endOfDay && /<=/.test(compare) ? date.setHours(23, 59, 59) : date.getTime() ) || '' : ''; @@ -557,7 +557,7 @@ tsff = ts.filterFormatter = { if ($.isArray(o.compare)) { $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); } - $cell.add($shcell).find('.date').val(o.defaultDate).datepicker('setDate', o.defaultDate); + $cell.add($shcell).find('.date').val(o.defaultDate).datepicker('setDate', o.defaultDate || null); setTimeout(function(){ date1Compare(); }, 0); @@ -570,13 +570,13 @@ tsff = ts.filterFormatter = { // date range found; assume an exact match on one day $cell.find(compareSelect).val('='); num = v.split(/\s+-\s+/)[0]; - $date.datepicker( 'setDate', num ); + $date.datepicker( 'setDate', num || null ); } else { num = (tsff.updateCompare($cell, $input, o)[1]).toString() || ''; // differeniate 1388556000000 from 1/1/2014 using \d{5} regex num = num !== '' ? /\d{5}/g.test(num) ? Number(num) : num || '' : ''; } - $cell.add($shcell).find('.date').datepicker( 'setDate', num ); + $cell.add($shcell).find('.date').datepicker( 'setDate', num || null ); setTimeout(function(){ date1Compare(true); }, 0); @@ -681,16 +681,16 @@ tsff = ts.filterFormatter = { if (/<=/.test(range)) { $cell.add( $shcell ) - .find('.dateFrom').datepicker('option', 'maxDate', to ).end() - .find('.dateTo').datepicker('option', 'minDate', null).datepicker('setDate', to); + .find('.dateFrom').datepicker('option', 'maxDate', to || null ).end() + .find('.dateTo').datepicker('option', 'minDate', null).datepicker('setDate', to || null); } else if (/>=/.test(range)) { $cell.add( $shcell ) - .find('.dateFrom').datepicker('option', 'maxDate', null).datepicker('setDate', from).end() - .find('.dateTo').datepicker('option', 'minDate', from ); + .find('.dateFrom').datepicker('option', 'maxDate', null).datepicker('setDate', from || null).end() + .find('.dateTo').datepicker('option', 'minDate', from || null ); } else { $cell.add( $shcell ) - .find('.dateFrom').datepicker('option', 'maxDate', null).datepicker('setDate', from ).end() - .find('.dateTo').datepicker('option', 'minDate', null).datepicker('setDate', to); + .find('.dateFrom').datepicker('option', 'maxDate', null).datepicker('setDate', from || null ).end() + .find('.dateTo').datepicker('option', 'minDate', null).datepicker('setDate', to || null); } if (typeof o.oldonClose === 'function') { o.oldonClose(selectedDate, ui); } @@ -718,8 +718,8 @@ tsff = ts.filterFormatter = { // less than date (from date empty) to = val.replace(/<=/, '') || ''; } - $cell.add($shcell).find('.dateFrom').datepicker('setDate', from); - $cell.add($shcell).find('.dateTo').datepicker('setDate', to); + $cell.add($shcell).find('.dateFrom').datepicker('setDate', from || null); + $cell.add($shcell).find('.dateTo').datepicker('setDate', to || null); // give datepicker time to process setTimeout(function(){ closeDate(); @@ -742,8 +742,8 @@ tsff = ts.filterFormatter = { // on reset $cell.closest('table').bind('filterReset', function(){ - $cell.add($shcell).find('.dateFrom').val('').datepicker('setDate', o.from ); - $cell.add($shcell).find('.dateTo').val('').datepicker('setDate', o.to ); + $cell.add($shcell).find('.dateFrom').val('').datepicker('setDate', o.from || null ); + $cell.add($shcell).find('.dateTo').val('').datepicker('setDate', o.to || null ); setTimeout(function(){ closeDate(); }, 0); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index eefd396..effb3cb 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.16+ widgets - updated 4/23/2014 (v2.16.0) +/*! tableSorter 2.16+ widgets - updated 4/27/2014 (v2.16.2) * * Column Styles * Column Filters @@ -176,7 +176,7 @@ ts.addWidget({ id: "uitheme", priority: 10, format: function(table, c, wo) { - var time, classes, $header, $icon, $tfoot, + var i, time, classes, $header, $icon, $tfoot, themesAll = ts.themes, $table = c.$table, $headers = c.$headers, @@ -222,10 +222,10 @@ ts.addWidget({ $headers.find('.' + ts.css.filterRow).addClass(themes.filterRow); } } - $.each($headers, function() { - $header = $(this); + for (i = 0; i < c.columns; i++) { + $header = c.$headers.add(c.$extraHeaders).filter('[data-column="' + i + '"]'); $icon = (ts.css.icon) ? $header.find('.' + ts.css.icon) : $header; - if (this.sortDisabled) { + if (c.$headers.filter('[data-column="' + i + '"]:last')[0].sortDisabled) { // no sort arrows for disabled columns! $header.removeClass(remove); $icon.removeClass(remove + ' ' + themes.icons); @@ -237,7 +237,7 @@ ts.addWidget({ $header[classes === themes.sortNone ? 'removeClass' : 'addClass'](themes.active); $icon.removeClass(remove).addClass(classes); } - }); + } if (c.debug) { ts.benchmark("Applying " + theme + " theme", time); } @@ -856,7 +856,7 @@ ts.filter = { if (table.config.lastCombinedFilter === combinedFilters) { return; } var cached, len, $rows, cacheIndex, rowIndex, tbodyIndex, $tbody, $cells, columnIndex, childRow, childRowText, exact, iExact, iFilter, lastSearch, matches, result, - searchFiltered, filterMatched, showRow, time, + notFiltered, searchFiltered, filterMatched, showRow, time, anyMatch, iAnyMatch, rowArray, rowText, iRowText, rowCache, c = table.config, wo = c.widgetOptions, @@ -887,11 +887,23 @@ ts.filter = { searchFiltered = true; lastSearch = c.lastSearch || c.$table.data('lastSearch') || []; $.each(filters, function(indx, val) { - // check for changes from beginning of filter; but ignore if there is a logical "or" in the string - searchFiltered = (val || '').indexOf(lastSearch[indx]) === 0 && searchFiltered && !/(\s+or\s+|\|)/g.test(val || ''); + // search already filtered rows if... + searchFiltered = searchFiltered && + // there are changes from beginning of filter + (val || '').indexOf(lastSearch[indx]) === 0 && + // if there is not a logical "or" in the string + !/(\s+or\s+|\|)/g.test(val || '') && + // if we are not doing exact matches + !/[=\"]/.test(lastSearch[indx]) && + // if filtering using a select without a "filter-match" class (exact match) - fixes #593 + !( val !== '' && wo.filter_functions && wo.filter_functions[indx] === true && !c.$headers.filter('[data-column="' + indx + '"]:last').hasClass('filter-match') ); }); + notFiltered = $rows.not('.' + wo.filter_filteredRow).length; // can't search when all rows are hidden - this happens when looking for exact matches - if (searchFiltered && $rows.not('.' + wo.filter_filteredRow).length === 0) { searchFiltered = false; } + if (searchFiltered && notFiltered === 0) { searchFiltered = false; } + if (c.debug) { + ts.log( "Searching through " + ( searchFiltered && notFiltered < len ? notFiltered : "all" ) + " rows" ); + } if ((wo.filter_$anyMatch && wo.filter_$anyMatch.length) || filters[c.columns]) { anyMatch = wo.filter_$anyMatch && wo.filter_$anyMatch.val() || filters[c.columns] || ''; if (c.sortLocaleCompare) { @@ -1213,6 +1225,7 @@ ts.addWidget({ stickyHeaders : '', // extra class name added to the sticky header row stickyHeaders_attachTo : null, // jQuery selector or object to attach sticky header to stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element + stickyHeaders_filteredToTop: true, // scroll table top into view after filtering stickyHeaders_cloneId : '-sticky', // added to table ID, if it exists stickyHeaders_addResizeEvent : true, // trigger "resize" event on headers stickyHeaders_includeCaption : true, // if false and a caption exist, it won't be included in the sticky header @@ -1223,8 +1236,7 @@ ts.addWidget({ if ( c.$table.hasClass('hasStickyHeaders') || ($.inArray('filter', c.widgets) >= 0 && !c.$table.hasClass('hasFilters')) ) { return; } - var $cell, - $table = c.$table, + var $table = c.$table, $attach = $(wo.stickyHeaders_attachTo), $thead = $table.children('thead:first'), $win = $attach.length ? $attach : $(window), @@ -1296,19 +1308,6 @@ ts.addWidget({ // update sticky header class names to match real header after sorting $table .addClass('hasStickyHeaders') - .bind('sortEnd.tsSticky', function() { - $header.filter(':visible').each(function(indx) { - $cell = $stickyCells.filter(':visible').eq(indx) - .attr('class', $(this).attr('class')) - // remove processing icon - .removeClass(ts.css.processing + ' ' + c.cssProcessing); - if (c.cssIcon) { - $cell - .find('.' + ts.css.icon) - .attr('class', $(this).find('.' + ts.css.icon).attr('class')); - } - }); - }) .bind('pagerComplete.tsSticky', function() { resizeHeader(); }); @@ -1355,11 +1354,11 @@ ts.addWidget({ var $td = $(document.activeElement).closest('td'), column = $td.parent().children().index($td); // only scroll if sticky header is active - if ($stickyTable.hasClass(ts.css.stickyVis)) { + if ($stickyTable.hasClass(ts.css.stickyVis) && wo.stickyHeaders_filteredToTop) { // scroll to original table (not sticky clone) window.scrollTo(0, $table.position().top); - // give same input/select focus - if (column >= 0) { + // give same input/select focus; check if c.$filters exists; fixes #594 + if (column >= 0 && c.$filters) { c.$filters.eq(column).find('a, select, input').filter(':visible').focus(); } } @@ -1373,7 +1372,7 @@ ts.addWidget({ remove: function(table, c, wo) { c.$table .removeClass('hasStickyHeaders') - .unbind('sortEnd.tsSticky pagerComplete.tsSticky') + .unbind('pagerComplete.tsSticky') .find('.' + ts.css.sticky).remove(); if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table // don't unbind if any table on the page still has stickyheaders applied diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index 6d79c9a..8e073c3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,5 +1,5 @@ /*! input & select parsers for jQuery 1.7+ & tablesorter 2.7.11+ - * Updated 2/19/2014 (v2.15.0) + * Updated 4/27/2014 (v2.16.2) * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ /*jshint browser: true, jquery:true, unused:false */ @@ -75,7 +75,15 @@ $('table').find('tbody').on('change', 'select, input', function(e){ if (!alreadyUpdating) { var $tar = $(e.target), - $table = $tar.closest('table'); + $cell = $tar.closest('td'), + $table = $cell.closest('table'), + indx = $cell[0].cellIndex, + c = $table[0].config, + $hdr = c && c.$headers && c.$headers.eq(indx); + // don't use updateCell if column is set to "sorter-false" and "filter-false" + if ($hdr.length && $hdr.hasClass('sorter-false') && $hdr.hasClass('filter-false')){ + return false; + } alreadyUpdating = true; $table.trigger('updateCell', [ $tar.closest('td'), resort ]); updateServer(e, $table, $tar); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index 050ec68..615bccf 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -1,4 +1,4 @@ -/*! tablesorter math widget - beta testing +/*! tablesorter math widget - beta - updated 4/27/2014 (v2.16.2) * Requires tablesorter v2.16+ and jQuery 1.7+ * by Rob Garrison */ @@ -149,7 +149,8 @@ }, output : function($cell, wo, value, arry) { - var result = ts.formatMask( wo.math_mask, value ); + // get mask from cell data-attribute: data-math-mask="#,##0.00" + var result = ts.formatMask( $cell.attr('data-' + wo.math_data + '-mask') || wo.math_mask, value ); if ($.isFunction(wo.math_complete)) { result = wo.math_complete($cell, wo, result, value, arry); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index dc9fe36..3ecdfcb 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -10,7 +10,7 @@ Resizable scroller widget for the jQuery tablesorter plugin - Version 2.0 - modified by Rob Garrison (4/12/2013; updated 4/20/2014 for tablesorter v2.16.0) + Version 2.0 - modified by Rob Garrison (4/12/2013; updated 4/27/2014 for tablesorter v2.16.2) Requires jQuery v1.7+ Requires the tablesorter plugin, v2.8+, available at http://mottie.github.com/tablesorter/docs/ @@ -109,23 +109,7 @@ ts.addWidget({ .wrap('<div class="tablesorter-scroller-header" style="width:' + $tbl.width() + ';" />') .find('.' + ts.css.header); - $tbl - .wrap('<div class="tablesorter-scroller-table" style="height:' + h + 'px;width:' + $tbl.width() + ';overflow-y:scroll;" />') - .unbind('sortEnd.tsScroller') - .bind('sortEnd.tsScroller', function(){ - c.$headers.each(function(i){ - var t = $cells.eq(i); - t - .attr('class', $(this).attr('class')) - // remove processing icon - .removeClass(ts.css.processing + ' ' + c.cssProcessing); - if (ts.css.icon){ - t - .find('.' + ts.css.icon) - .attr('class', $(this).find('.' + ts.css.icon).attr('class')); - } - }); - }); + $tbl.wrap('<div class="tablesorter-scroller-table" style="height:' + h + 'px;width:' + $tbl.width() + ';overflow-y:scroll;" />') // make scroller header sortable ts.bindEvents(table, $cells); From e162a4f427094e150bb99bd2b631bf6a7fb14517 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Thu, 1 May 2014 09:46:34 +0200 Subject: [PATCH 029/138] * updated tablesorter to latest version (2.16.3) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery-tablesorter/jquery.tablesorter.js | 31 +++++--- ...sorter.widgets-filter-formatter-select2.js | 27 ++++--- .../jquery.tablesorter.widgets.js | 76 ++++++++++++++----- 7 files changed, 98 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44c003a..b4047e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.11.2 (2014-05-01) + +* Upgrade tablesorter to v2.16.3 + #### v1.11.1 (2014-04-28) * Upgrade tablesorter to v2.16.2 diff --git a/README.md b/README.md index fb86070..fc88a67 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.16.2 (4/27/2014), [documentation] +Current tablesorter version: 2.16.3 (4/30/2014), [documentation] Any issue associate with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index eff2c5f..43baced 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.11.1' + VERSION = '1.11.2' end diff --git a/tablesorter b/tablesorter index f4cded6..4f17bb7 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit f4cded6c6cfc872b9d7abe174c656d442b3d7857 +Subproject commit 4f17bb771cff880cbedbac95d6c7dfc4a115e04c diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 826c0d7..8a33251 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.16.2 - Client-side table sorting with ease! +* TableSorter 2.16.3 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.16.2"; + ts.version = "2.16.3"; ts.parsers = []; ts.widgets = []; @@ -219,7 +219,7 @@ var c = table.config, // update table bodies in case we start with an empty table tb = c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'), - rows, list, l, i, h, ch, p, time, + rows, list, l, i, h, ch, p, time, indx, j = 0, parsersDebug = "", len = tb.length; @@ -240,7 +240,9 @@ h = c.$headers.filter(':not([colspan])'); h = h.add( c.$headers.filter('[colspan="1"]') ) // ie8 fix .filter('[data-column="' + i + '"]:last'); - ch = c.headers[i]; + // get headers option corrected index + indx = c.$headers.index(h); + ch = c.headers[indx]; // get column parser p = ts.getParserById( ts.getData(h, ch, 'sorter') ); // empty cells behaviour - keeping emptyToBottom for backwards compatibility @@ -804,8 +806,8 @@ $cell = $(cell), // update cache - format: function(s, table, cell, cellIndex) // no closest in jQuery v1.2.6 - tbdy = $tb.index( $(cell).closest('tbody') ),$row = $(cell).closest('tr'); - tbdy = $tb.index( $cell.parents('tbody').filter(':first') ), - $row = $cell.parents('tr').filter(':first'); + tbdy = $tb.index( $.fn.closest ? $cell.closest('tbody') : $cell.parents('tbody').filter(':first') ), + $row = $.fn.closest ? $cell.closest('tr') : $cell.parents('tr').filter(':first'); cell = $cell[0]; // in case cell is a jQuery object // tbody may not exist if update is initialized while tbody is removed for processing if ($tb.length && tbdy >= 0) { @@ -1019,9 +1021,7 @@ setHeadersCss(table); if (c.initWidgets) { // apply widget format - setTimeout(function(){ - ts.applyWidget(table, false); - }, 0); + ts.applyWidget(table, false); } } @@ -1030,7 +1030,13 @@ $table .unbind('sortBegin' + c.namespace + ' sortEnd' + c.namespace) .bind('sortBegin' + c.namespace + ' sortEnd' + c.namespace, function(e) { - ts.isProcessing(table, e.type === 'sortBegin'); + clearTimeout(c.processTimer); + ts.isProcessing(table); + if (e.type === 'sortBegin') { + c.processTimer = setTimeout(function(){ + ts.isProcessing(table, true); + }, 500); + } }); } @@ -1044,7 +1050,6 @@ if (typeof c.initialized === 'function') { c.initialized(table); } }; - // computeTableHeaderCellIndexes from: // http://www.javascripttoolbox.com/lib/table/examples.php // http://www.javascripttoolbox.com/temp/table_cellindex.html @@ -1161,7 +1166,7 @@ } if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); } // jQuery v1.2.6 doesn't have closest() - cell = /TH|TD/.test(this.tagName) ? this : $(this).parents('th, td')[0]; + cell = $.fn.closest ? $(this).closest('th, td')[0] : /TH|TD/.test(this.tagName) ? this : $(this).parents('th, td')[0]; // reference original table headers and find the same cell cell = c.$headers[ $headers.index( cell ) ]; if (!cell.sortDisabled) { @@ -1238,6 +1243,8 @@ }; // Natural sort - https://github.com/overset/javascript-natural-sort (date sorting removed) + // this function will only accept strings, or you'll see "TypeError: undefined is not a function" + // I could add a = a.toString(); b = b.toString(); but it'll slow down the sort overall ts.sortNatural = function(a, b) { if (a === b) { return 0; } var xN, xD, yN, yD, xF, yF, i, mx, diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js index 771e8c3..89b75f6 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js @@ -1,4 +1,4 @@ -/*! Filter widget formatter functions - updated 4/22/2014 (v2.16.1-beta) +/*! Filter widget formatter functions - updated 4/30/2014 (v2.16.3) * requires: jQuery 1.7.2+, tableSorter 2.16+, filter widget 2.16+ and select2 v3.4.6+ plugin */ /*jshint browser:true, jquery:true, unused:false */ @@ -17,6 +17,7 @@ ts.filterFormatter.select2 = function($cell, indx, select2Def) { // select2 filter formatter options cellText : '', // Text (wrapped in a label element) match : true, // adds "filter-match" to header + value : '', // include ANY select2 options below multiple : true, width : '100%' @@ -31,21 +32,23 @@ ts.filterFormatter.select2 = function($cell, indx, select2Def) { // hidden filter update namespace trigger by filter widget .bind('change' + c.namespace + 'filter', function(){ var val = this.value; - val = val.replace(/[/()$]/g, '').split('|'); - updateSelect2(val); + val = val.replace(/[/()$^]/g, '').split('|'); + $cell.find('.select2').select2('val', val); + updateSelect2(); }), $header = c.$headers.filter('[data-column="' + indx + '"]:last'), onlyAvail = $header.hasClass(wo.filter_onlyAvail), $shcell = [], - match = o.match ? '' : '$', + matchPrefix = o.match ? '' : '^', + matchSuffix = o.match ? '' : '$', // this function updates the hidden input and adds the current values to the header cell text - updateSelect2 = function(v, notrigger) { - v = typeof v === "undefined" || v === '' ? $cell.find('.select2').select2('val') || o.value || '' : v || ''; + updateSelect2 = function() { + var v = $cell.find('.select2').select2('val') || o.value || ''; $input - // add equal to the beginning, so we filter exact numbers - .val( $.isArray(v) && v.length ? '/(' + (v || []).join(match + '|') + match + ')/' : '' ) - .trigger( notrigger ? '' : 'search' ).end() + // add regex, so we filter exact numbers + .val( $.isArray(v) && v.length && v.join('') !== '' ? '/(' + matchPrefix + (v || []).join(matchSuffix + '|' + matchPrefix) + matchSuffix + ')/' : '' ) + .trigger('search').end() .find('.select2').select2('val', v); // update sticky header cell if ($shcell.length) { @@ -94,11 +97,11 @@ ts.filterFormatter.select2 = function($cell, indx, select2Def) { // update select2 from filter hidden input, in case of saved filters c.$table.bind('filterFomatterUpdate', function(){ - // value = '/(x$|y$)/' => 'x,y' + // value = '/(^x$|^y$)/' => 'x,y' var val = c.$table.data('lastSearch')[indx] || ''; - val = val.replace(/[/()$]/g, '').split('|'); + val = val.replace(/[/()$^]/g, '').split('|'); $cell.find('.select2').select2('val', val); - updateSelect2(val, true); + updateSelect2(); }); // has sticky headers? diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index effb3cb..205ae63 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.16+ widgets - updated 4/27/2014 (v2.16.2) +/*! tableSorter 2.16+ widgets - updated 4/30/2014 (v2.16.3) * * Column Styles * Column Filters @@ -817,7 +817,7 @@ ts.filter = { }, hideFilters: function(table, c) { var $filterRow, $filterRow2, timer; - c.$table + $(table) .find('.' + ts.css.filterRow) .addClass('hideme') .bind('mouseenter mouseleave', function(e) { @@ -846,7 +846,7 @@ ts.filter = { var event = e; timer = setTimeout(function() { // don't hide row if any filter has a value - if (ts.getFilters(table).join('') === '') { + if (ts.getFilters(c.$table).join('') === '') { $filterRow2[ event.type === 'focus' ? 'removeClass' : 'addClass']('hideme'); } }, 200); @@ -854,7 +854,7 @@ ts.filter = { }, findRows: function(table, filters, combinedFilters) { if (table.config.lastCombinedFilter === combinedFilters) { return; } - var cached, len, $rows, cacheIndex, rowIndex, tbodyIndex, $tbody, $cells, columnIndex, + var cached, len, $rows, rowIndex, tbodyIndex, $tbody, $cells, columnIndex, childRow, childRowText, exact, iExact, iFilter, lastSearch, matches, result, notFiltered, searchFiltered, filterMatched, showRow, time, anyMatch, iAnyMatch, rowArray, rowText, iRowText, rowCache, @@ -866,9 +866,10 @@ ts.filter = { anyMatchNotAllowedTypes = [ 'range', 'notMatch', 'operators' ], // parse columns after formatter, in case the class is added at that point parsed = c.$headers.map(function(columnIndex) { - return c.parsers && c.parsers[columnIndex] && c.parsers[columnIndex].parsed || ( ts.getData ? - ts.getData(c.$headers.filter('[data-column="' + columnIndex + '"]:last'), c.headers[columnIndex], 'filter') === 'parsed' : - $(this).hasClass('filter-parsed') ); + return c.parsers && c.parsers[columnIndex] && c.parsers[columnIndex].parsed || + // getData won't return "parsed" if other "filter-" class names exist (e.g. <th class="filter-select filter-parsed">) + ts.getData && ts.getData(c.$headers.filter('[data-column="' + columnIndex + '"]:last'), c.headers[columnIndex], 'filter') === 'parsed' || + $(this).hasClass('filter-parsed'); }).get(); if (c.debug) { time = new Date(); } for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { @@ -913,7 +914,6 @@ ts.filter = { iAnyMatch = anyMatch.toLowerCase(); } // loop through the rows - cacheIndex = 0; for (rowIndex = 0; rowIndex < len; rowIndex++) { childRow = $rows[rowIndex].className; // skip child rows & already filtered rows @@ -932,7 +932,7 @@ ts.filter = { rowArray = $cells.map(function(i){ var txt; if (parsed[i]) { - txt = c.cache[tbodyIndex].normalized[cacheIndex][i]; + txt = c.cache[tbodyIndex].normalized[rowIndex][i]; } else { txt = wo.filter_ignoreCase ? $(this).text().toLowerCase() : $(this).text(); if (c.sortLocaleCompare) { @@ -943,7 +943,7 @@ ts.filter = { }).get(); rowText = rowArray.join(' '); iRowText = rowText.toLowerCase(); - rowCache = c.cache[tbodyIndex].normalized[cacheIndex].slice(0,-1).join(' '); + rowCache = c.cache[tbodyIndex].normalized[rowIndex].slice(0,-1).join(' '); filterMatched = null; $.each(ts.filter.types, function(type, typeFunction) { if ($.inArray(type, anyMatchNotAllowedTypes) < 0) { @@ -964,7 +964,7 @@ ts.filter = { for (columnIndex = 0; columnIndex < columns; columnIndex++) { // ignore if filter is empty or disabled if (filters[columnIndex]) { - cached = c.cache[tbodyIndex].normalized[cacheIndex][columnIndex]; + cached = c.cache[tbodyIndex].normalized[rowIndex][columnIndex]; // check if column data should be from the cell or from parsed data if (wo.filter_useParsedData || parsed[columnIndex]) { exact = cached; @@ -1020,7 +1020,6 @@ ts.filter = { if (childRow.length) { childRow.toggleClass(wo.filter_filteredRow, !showRow); } - cacheIndex++; } } ts.processTbody(table, $tbody, false); @@ -1040,8 +1039,10 @@ ts.filter = { }, 0); }, getOptionSource: function(table, column, onlyAvail) { - var c = table.config, + var cts, + c = table.config, wo = c.widgetOptions, + parsed = [], arry = false, source = wo.filter_selectSource; @@ -1064,7 +1065,43 @@ ts.filter = { arry = $.grep(arry, function(value, indx) { return $.inArray(value, arry) === indx; }); - return (ts.sortNatural) ? arry.sort(function(a, b) { return ts.sortNatural(a, b); }) : arry.sort(true); + + if (c.$headers.filter('[data-column="' + column + '"]:last').hasClass('filter-select-nosort')) { + // unsorted select options + return arry; + } else { + // parse select option values + $.each(arry, function(i, v){ + // parse array data using set column parser; this DOES NOT pass the original + // table cell to the parser format function + parsed.push({ t : v, p : c.parsers && c.parsers[column].format( v, table, [], column ) || v }); + }); + + // sort parsed select options + cts = c.textSorter || ''; + parsed.sort(function(a, b){ + // sortNatural breaks if you don't pass it strings + var x = a.p.toString(), y = b.p.toString(); + if ($.isFunction(cts)) { + // custom OVERALL text sorter + return cts(x, y, true, column, table); + } else if (typeof(cts) === 'object' && cts.hasOwnProperty(column)) { + // custom text sorter for a SPECIFIC COLUMN + return cts[column](x, y, true, column, table); + } else if (ts.sortNatural) { + // fall back to natural sort + return ts.sortNatural(x, y); + } + // using an older version! do a basic sort + return true; + }); + // rebuild arry from sorted parsed data + arry = []; + $.each(parsed, function(i, v){ + arry.push(v.t); + }); + return arry; + } }, getOptions: function(table, column, onlyAvail) { var rowIndex, tbodyIndex, len, row, cache, cell, @@ -1282,10 +1319,7 @@ ts.addWidget({ // some wibbly-wobbly... timey-wimey... stuff, to make columns line up in Firefox offset = nonwkie && $(this).attr('data-column') === ( '' + parseInt(c.columns/2, 10) ) ? 1 : 0; $(this) - .css({ - width: $cell.width() - spacing, - height: $cell.height() - }) + .css({ width: $cell.width() - spacing }) .find(innerHeader).width( $cell.find(innerHeader).width() - offset ); }); }; @@ -1364,6 +1398,10 @@ ts.addWidget({ } }); ts.filter.bindSearch( $table, $stickyCells.find('.' + ts.css.filter) ); + // support hideFilters + if (wo.filter_hideFilters) { + ts.filter.hideFilters($stickyTable, c); + } } $table.trigger('stickyHeadersInit'); @@ -1477,7 +1515,7 @@ ts.addWidget({ }) .find('.' + ts.css.resizer + ',.' + ts.css.grip) .bind('mousedown', function(event) { - // save header cell and mouse position; closest() not supported by jQuery v1.2.6 + // save header cell and mouse position $target = $(event.target).closest('th'); var $header = c.$headers.filter('[data-column="' + $target.attr('data-column') + '"]'); if ($header.length > 1) { $target = $target.add($header); } From 64c8de040107900da3929b38a3e2144ab4f4782e Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Thu, 22 May 2014 22:50:48 +0200 Subject: [PATCH 030/138] * updated tablesorter to latest version (2.17.0) --- CHANGELOG.md | 4 + README.md | 4 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../bootstrap-black-unsorted.png | Bin 0 -> 284 bytes .../bootstrap-white-unsorted.png | Bin 0 -> 223 bytes .../jquery-tablesorter/metro-black-asc.png | Bin 0 -> 213 bytes .../jquery-tablesorter/metro-black-desc.png | Bin 0 -> 232 bytes .../jquery-tablesorter/metro-loading.gif | Bin 0 -> 673 bytes .../jquery-tablesorter/metro-unsorted.png | Bin 0 -> 181 bytes .../jquery-tablesorter/metro-white-asc.png | Bin 0 -> 209 bytes .../jquery-tablesorter/metro-white-desc.png | Bin 0 -> 224 bytes .../addons/pager/jquery.tablesorter.pager.js | 11 +- .../jquery-tablesorter/jquery.tablesorter.js | 123 ++++++++--- .../jquery.tablesorter.widgets.js | 170 ++++++++++------ .../widgets/widget-columnSelector.js | 25 ++- .../widgets/widget-cssStickyHeaders.js | 17 +- .../jquery-tablesorter/widgets/widget-math.js | 89 +++++--- .../widgets/widget-output.js | 66 ++++-- .../widgets/widget-pager.js | 13 +- .../widgets/widget-print.js | 122 +++++++++++ .../widgets/widget-scroller.js | 6 +- .../jquery-tablesorter/theme.bootstrap.css | 12 +- .../jquery-tablesorter/theme.green.css | 20 +- .../jquery-tablesorter/theme.metro-dark.css | 192 ++++++++++++++++++ 25 files changed, 695 insertions(+), 183 deletions(-) create mode 100644 vendor/assets/images/jquery-tablesorter/bootstrap-black-unsorted.png create mode 100644 vendor/assets/images/jquery-tablesorter/bootstrap-white-unsorted.png create mode 100644 vendor/assets/images/jquery-tablesorter/metro-black-asc.png create mode 100644 vendor/assets/images/jquery-tablesorter/metro-black-desc.png create mode 100644 vendor/assets/images/jquery-tablesorter/metro-loading.gif create mode 100644 vendor/assets/images/jquery-tablesorter/metro-unsorted.png create mode 100644 vendor/assets/images/jquery-tablesorter/metro-white-asc.png create mode 100644 vendor/assets/images/jquery-tablesorter/metro-white-desc.png create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js create mode 100644 vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css diff --git a/CHANGELOG.md b/CHANGELOG.md index b4047e9..5653b49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.12.0 (2014-05-22) + +* Upgrade tablesorter to v2.17.0 + #### v1.11.2 (2014-05-01) * Upgrade tablesorter to v2.16.3 diff --git a/README.md b/README.md index fc88a67..c419b51 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.16.3 (4/30/2014), [documentation] +Current tablesorter version: 2.17.0 (5/22/2014), [documentation] -Any issue associate with the js/css files, please report to [Mottie's fork]. +Any issue associated with the js/css files, please report to [Mottie's fork]. ## Installation diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 43baced..deeb577 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.11.2' + VERSION = '1.12.0' end diff --git a/tablesorter b/tablesorter index 4f17bb7..5909671 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 4f17bb771cff880cbedbac95d6c7dfc4a115e04c +Subproject commit 5909671c7635c8e8f6a69f6e110ebcda214871a0 diff --git a/vendor/assets/images/jquery-tablesorter/bootstrap-black-unsorted.png b/vendor/assets/images/jquery-tablesorter/bootstrap-black-unsorted.png new file mode 100644 index 0000000000000000000000000000000000000000..4e161a65ec0d89596124ebd5eeb18014e900fc5f GIT binary patch literal 284 zcmeAS@N?(olHy`uVBq!ia0vp^+(697!3-qN?H5}Pq(TCGLR^8glao_qWTd2|<jIpK z6%-VJ3?TUP=Z~D6oUE*@6c~hrgh)$EU$}7L&Ye3xK0ZM~K|t>7*RL%sEWEtDfVy;b zbxll6jE#-e)zyOpeq8{XD^U{U7tC<&w4}=pSw^<Qr?j;;&a!d5dS$iO-y`2wddesP z)x>+cIEHXsFFo$b)S$q_a*%=RSZ%q`zyJQp@4l^^wPsR(OvfDM=HriKqBL$joSm{K zXVT90He-#d&nmaQUaJZJH4i^GW#_qP3J;D<NQhhXpz+AjJEnmlNtY*{U$^`6^Rj)b Xm07uOmpozzTE*b$>gTe~DWM4fZ4GGK literal 0 HcmV?d00001 diff --git a/vendor/assets/images/jquery-tablesorter/bootstrap-white-unsorted.png b/vendor/assets/images/jquery-tablesorter/bootstrap-white-unsorted.png new file mode 100644 index 0000000000000000000000000000000000000000..fb227a150ec672948d40c0989ab4654957d4074c GIT binary patch literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^+(697!VF^h|6T^9v;urWT!Hj|;y~N(Q~E&Fyd^<? z!3+;vPqAIm+AC|fS#s9YvlX|165*aMjv*Y^lM@nNWF;^#nME55Os)G~#l*~9{`T(f zt2{hw%3D*`8yOVD&N28X#w_(<=bZ~R?Tw8e*KK|4%Eor?#_n=uNr{M%_i>?&UREc$ gEEzP#QWe-4B7cis^t<g_0<?m`)78&qol`;+09y`Y9smFU literal 0 HcmV?d00001 diff --git a/vendor/assets/images/jquery-tablesorter/metro-black-asc.png b/vendor/assets/images/jquery-tablesorter/metro-black-asc.png new file mode 100644 index 0000000000000000000000000000000000000000..f7a781be17147df66dfad715f1a977b148fd1153 GIT binary patch literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eK!VDxY_&1vXDa8Pv5LY0LALL&2I0#h1RTAVE z%wWu|6?cDuf+*XOEM^0sXr!l$V+hA}-7~sE4h$l$4~-?Rm@@@l>{N*qQH@MuUvp6C zPs?_%_h)|`vVF@G*#BDZenOR(_Zq8cwyV-krlwQVQ$FtbV`gaCncnKA!t6Kk+IbhR dGl35-$=XTsix@`i@H_)@oTsaw%Q~loCIIowIb;9; literal 0 HcmV?d00001 diff --git a/vendor/assets/images/jquery-tablesorter/metro-black-desc.png b/vendor/assets/images/jquery-tablesorter/metro-black-desc.png new file mode 100644 index 0000000000000000000000000000000000000000..ef5f48e07e52c6608e913fda2aa724c3eda3cecd GIT binary patch literal 232 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eK!VDxY_&1vXDct~{5LX~gJUF;O!w#sLuO!GX zn88R#j+05)@57y~)3P!xeeTBsWnw&C978y+CnqE%cqcG0x(f=NT%{qxuw(`+D{n){ zq6G^in3=m31S||#8Z|=MoCQ?RFtGl<$9OQiO+#Sol*ZJ1yUX=uB_wq2?agNMGTK|c loFORWyUdzt-x-*h87{7oXg<B{)OnD5Jzf1=);T3K0RT~YH)H?+ literal 0 HcmV?d00001 diff --git a/vendor/assets/images/jquery-tablesorter/metro-loading.gif b/vendor/assets/images/jquery-tablesorter/metro-loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..ae274c6c12de1a8b5fb237336e5208d2d269f0ad GIT binary patch literal 673 zcmZ?wbhEHb6krfw_{6~Q|NsBc(9mnwt~E3?02$M!O<TQs^|o!>6#sMkxrPKgI|jHK z=@~FHGB7YG{*!VpN=+<DO;IS%EXhzv%u1}t$xlqt%gjs5XHfjf!pQ|xr31u3qXifk zn2kAh-T7y5$}?LynZt@hat^!Sj5JQyjfRJIiOf8>O)ufdp~4Hg(Gr$-m$6$)@UVMj zKlcFIf@zKk$Q-{OMFr^=p4}Usb-5He1%@OTN-8AyxJFB@sFB%PdjD;r1bc(Rb%{r9 z&XSu{RkVb#8Dz$>%Sc)Kl;epBXQqVdv$ZD6@O_c=`{?PEIAe8z%m=PBoA=5bTcOPq z`BwZ7WAkBtqm?OA^Y^o0GYaUpBvtKGMr{+{%n6Hc?Ow*&s>s7)=+d4jBe=?~X~RW% z=A@w5&$jXg7CKV>Czcdh`e5~&5y!3~b#1TbDIy;vKKnHCsFfW~h)D{W(%Q09E+f)O z^ZY3X0cN?EHu8tnz~%%^!fFiAISPj+Sc*R`dA{e?JOjz&G7l8>oVLw96~1PXArBk3 zNO9us4W{3xGBz4);OyKzKUdQWt2sdD-1+x`Q}E`T(AZgw?`{<qOik#xn>gpL$+T8S zx1$Ck6B1@`W8`Hy<RCEHu;I|Pa}#kJ1C06AsZLksXkM4&;5e+%cf62~#n52osSWD_ dBn|<iE9}-%CWX=@iKcncf)f}RtU(C}3;;TA)_MQ{ literal 0 HcmV?d00001 diff --git a/vendor/assets/images/jquery-tablesorter/metro-unsorted.png b/vendor/assets/images/jquery-tablesorter/metro-unsorted.png new file mode 100644 index 0000000000000000000000000000000000000000..653b083428357a3a81387e840c003c4d8a88c226 GIT binary patch literal 181 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eK!VDxY_&1vXDWL$L5LY1m4i$XLud@J(vXun+ z1v3~4v1==^v+ZyPiuicCIEHXsPfkck2ufgJbWSvQs==fb$;!&yFlEt#1p>^>n-v5k z3^p`qu(vq~sGeeA43P7%jc8xIK*3{w=Yj>s6O!06nI)M`Sh55ef+w))8BS!k1Dea= M>FVdQ&MBb@0IrQR`v3p{ literal 0 HcmV?d00001 diff --git a/vendor/assets/images/jquery-tablesorter/metro-white-asc.png b/vendor/assets/images/jquery-tablesorter/metro-white-asc.png new file mode 100644 index 0000000000000000000000000000000000000000..63327188b8fc240895c3e02d2e02e64e49e297c4 GIT binary patch literal 209 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eK!VDxY_&1vXDY*cj5LY1m4mYstac=>t;4BI9 z3uZ9p)Dn$*y+GkdYyN(qV5FyuV+hA}-7{<X92i8}9+ua+*(!IK&F)!N$i8Y-=Bi_+ z5@qkMS<Y6k$^YF@ap>#x{0(+XRRizpY<|VNa8}!`%^RxY>Xy%Fx>D?*J&AFu$MbY0 f)wDB(7a8Qwbqb|%)xLTMw1mOa)z4*}Q$iB}B+^)A literal 0 HcmV?d00001 diff --git a/vendor/assets/images/jquery-tablesorter/metro-white-desc.png b/vendor/assets/images/jquery-tablesorter/metro-white-desc.png new file mode 100644 index 0000000000000000000000000000000000000000..ca7c363e34009bdf9f61bfea526e410a9ecddc49 GIT binary patch literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eK!VDxY_&1vXDfIxK5LY1mjtC&A#&`*+l&2)f zFPOndM~;(8IMeUNw4Hf3%JYHZF`h1tAsp9}6A}`<6BroX1qDv7(hy--GJ}<sw;^QF zf&~)H%-sqC76vSh8lh~?0;*>iShLSD9Mo*f5ZF4kG4<R`V|7^xiJWtDEP1A+SQWc5 jE?M!MB|^8Fk(rsHyG_iYB>X`v&>jX)S3j3^P6<r__~la{ literal 0 HcmV?d00001 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index fa2c711..ef9c16d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 4/23/2014 (v2.16.0) + * updated 5/22/2014 (v2.17.0) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -289,7 +289,7 @@ exception === 'timeout' ? 'Time out error' : exception === 'abort' ? 'Ajax Request aborted' : 'Uncaught error: ' + xhr.statusText + ' [' + xhr.status + ']' ); - c.$tbodies.eq(0).empty(); + c.$tbodies.eq(0).detach(); p.totalRows = 0; } else { // process ajax object @@ -311,7 +311,7 @@ if (d instanceof jQuery) { if (p.processAjaxOnInit) { // append jQuery object - c.$tbodies.eq(0).empty().append(d); + c.$tbodies.eq(0).detach().append(d); } } else if (l) { // build table from array @@ -726,6 +726,11 @@ }) .bind('update updateRows updateAll addRows '.split(' ').join('.pager '), function(e){ e.stopPropagation(); + fixHeight(table, p); + var $rows = c.$tbodies.eq(0).children(); + p.totalRows = $rows.length - ( p.countChildRows ? 0 : $rows.filter('.' + c.cssChildRow).length ); + p.totalPages = Math.ceil( p.totalRows / p.size ); + updatePageDisplay(table, p); hideRows(table, p); }) .bind('pageSize.pager', function(e,v){ diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 8a33251..c58d110 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.16.3 - Client-side table sorting with ease! +* TableSorter 2.17.0 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.16.3"; + ts.version = "2.17.0"; ts.parsers = []; ts.widgets = []; @@ -167,7 +167,7 @@ function getElementText(table, node, cellIndex) { if (!node) { return ""; } - var c = table.config, + var te, c = table.config, t = c.textExtraction || '', text = ""; if (t === "basic") { @@ -176,8 +176,8 @@ } else { if (typeof(t) === "function") { text = t(node, table, cellIndex); - } else if (typeof(t) === "object" && t.hasOwnProperty(cellIndex)) { - text = t[cellIndex](node, table, cellIndex); + } else if (typeof (te = ts.getColumnData( table, t, cellIndex )) === 'function') { + text = te(node, table, cellIndex); } else { // previous "simple" method text = node.textContent || node.innerText || $(node).text() || ""; @@ -219,7 +219,7 @@ var c = table.config, // update table bodies in case we start with an empty table tb = c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'), - rows, list, l, i, h, ch, p, time, indx, + rows, list, l, i, h, ch, p, time, j = 0, parsersDebug = "", len = tb.length; @@ -233,16 +233,11 @@ while (j < len) { rows = tb[j].rows; if (rows[j]) { - l = rows[j].cells.length; + l = c.columns; // rows[j].cells.length; for (i = 0; i < l; i++) { - // tons of thanks to AnthonyM1229 for working out the following selector (issue #74) to make this work in IE8! - // More fixes to this selector to work properly in iOS and jQuery 1.8+ (issue #132 & #174) - h = c.$headers.filter(':not([colspan])'); - h = h.add( c.$headers.filter('[colspan="1"]') ) // ie8 fix - .filter('[data-column="' + i + '"]:last'); - // get headers option corrected index - indx = c.$headers.index(h); - ch = c.headers[indx]; + h = c.$headers.filter('[data-column="' + i + '"]:last'); + // get column indexed table cell + ch = ts.getColumnData( table, c.headers, i ); // get column parser p = ts.getParserById( ts.getData(h, ch, 'sorter') ); // empty cells behaviour - keeping emptyToBottom for backwards compatibility @@ -421,10 +416,12 @@ c.columns = ts.computeColumnIndex( c.$table.children('thead, tfoot').children('tr') ); // add icon if cssIcon option exists i = c.cssIcon ? '<i class="' + ( c.cssIcon === ts.css.icon ? ts.css.icon : c.cssIcon + ' ' + ts.css.icon ) + '"></i>' : ''; - c.$headers = $(table).find(c.selectorHeaders).each(function(index) { + c.$headers.each(function(index) { $t = $(this); - ch = c.headers[index]; - c.headerContent[index] = $(this).html(); // save original header content + // make sure to get header cell & not column indexed cell + ch = ts.getColumnData( table, c.headers, index, true ); + // save original header content + c.headerContent[index] = $(this).html(); // set up header template t = c.headerTemplate.replace(/\{content\}/g, $(this).html()).replace(/\{icon\}/g, i); if (c.onRenderTemplate) { @@ -473,10 +470,11 @@ } function updateHeader(table) { - var s, $th, c = table.config; + var s, $th, + c = table.config; c.$headers.each(function(index, th){ $th = $(th); - s = ts.getData( th, c.headers[index], 'sorter' ) === 'false'; + s = ts.getData( th, ts.getColumnData( table, c.headers, index, true ), 'sorter' ) === 'false'; th.sortDisabled = s; $th[ s ? 'addClass' : 'removeClass' ]('sorter-false').attr('aria-disabled', '' + s); // aria-controls - requires table ID @@ -546,19 +544,46 @@ } } - function updateHeaderSortCount(table, list, triggered) { - var s, t, o, c = table.config, + function updateHeaderSortCount(table, list) { + var s, t, o, col, primary, + c = table.config, sl = list || c.sortList; c.sortList = []; $.each(sl, function(i,v){ // ensure all sortList values are numeric - fixes #127 - s = [ parseInt(v[0], 10), parseInt(v[1], 10) ]; + col = parseInt(v[0], 10); // make sure header exists - o = c.$headers.filter('[data-column="' + s[0] + '"]:last')[0]; + o = c.$headers.filter('[data-column="' + col + '"]:last')[0]; if (o) { // prevents error if sorton array is wrong + // o.count = o.count + 1; + t = ('' + v[1]).match(/^(1|d|s|o|n)/); + t = t ? t[0] : ''; + // 0/(a)sc (default), 1/(d)esc, (s)ame, (o)pposite, (n)ext + switch(t) { + case '1': case 'd': // descending + t = 1; + break; + case 's': // same direction (as primary column) + // if primary sort is set to "s", make it ascending + t = primary || 0; + break; + case 'o': + s = o.order[(primary || 0) % (c.sortReset ? 3 : 2)]; + // opposite of primary column; but resets if primary resets + t = s === 0 ? 1 : s === 1 ? 0 : 2; + break; + case 'n': + o.count = o.count + 1; + t = o.order[(o.count) % (c.sortReset ? 3 : 2)]; + break; + default: // ascending + t = 0; + break; + } + primary = i === 0 ? t : primary; + s = [ col, parseInt(t, 10) || 0 ]; c.sortList.push(s); t = $.inArray(s[1], o.order); // fixes issue #167 - if (triggered) { o.count = o.count + 1; } o.count = t >= 0 ? t : s[1] % (c.sortReset ? 3 : 2); } }); @@ -872,7 +897,7 @@ e.stopPropagation(); $table.trigger("sortStart", this); // update header count index - updateHeaderSortCount(table, list, true); + updateHeaderSortCount(table, list); // set css for headers setHeadersCss(table); // fixes #346 @@ -921,6 +946,16 @@ .bind("destroy" + c.namespace, function(e, c, cb){ e.stopPropagation(); ts.destroy(table, c, cb); + }) + .bind("resetToLoadState" + c.namespace, function(e){ + // remove all widgets + ts.refreshWidgets(table, true, true); + // restore original settings; this clears out current settings, but does not clear + // values saved to storage. + c = $.extend(true, ts.defaults, c.originalSettings); + table.hasInitialized = false; + // setup the entire table again + ts.setup( table, c ); }); } @@ -930,6 +965,8 @@ var table = this, // merge & extend config options c = $.extend(true, {}, ts.defaults, settings); + // save initial settings + c.originalSettings = settings; // create a table from data (build table widget) if (!table.hasInitialized && ts.buildTable && this.tagName !== 'TABLE') { // return the table (in case the original target is the table's container) @@ -973,6 +1010,7 @@ c.$table = $table .addClass(ts.css.table + ' ' + c.tableClass + k) .attr({ role : 'grid'}); + c.$headers = $(table).find(c.selectorHeaders); // give the table a unique id, which will be used in namespace binding if (!c.namespace) { @@ -1050,6 +1088,31 @@ if (typeof c.initialized === 'function') { c.initialized(table); } }; + ts.getColumnData = function(table, obj, indx, getCell){ + if (typeof obj === 'undefined' || obj === null) { return; } + table = $(table)[0]; + var result, $h, k, + c = table.config; + if (obj[indx]) { + return getCell ? obj[indx] : obj[c.$headers.index( c.$headers.filter('[data-column="' + indx + '"]:last') )]; + } + for (k in obj) { + if (typeof k === 'string') { + if (getCell) { + // get header cell + $h = c.$headers.eq(indx).filter(k); + } else { + // get column indexed cell + $h = c.$headers.filter('[data-column="' + indx + '"]:last').filter(k); + } + if ($h.length) { + return obj[k]; + } + } + } + return result; + }; + // computeTableHeaderCellIndexes from: // http://www.javascripttoolbox.com/lib/table/examples.php // http://www.javascripttoolbox.com/temp/table_cellindex.html @@ -1136,7 +1199,7 @@ }; ts.clearTableBody = function(table) { - $(table)[0].config.$tbodies.empty(); + $(table)[0].config.$tbodies.detach(); }; ts.bindEvents = function(table, $headers, core){ @@ -1217,7 +1280,7 @@ // disable tablesorter $t .removeData('tablesorter') - .unbind('sortReset update updateAll updateRows updateCell addRows updateComplete sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress sortBegin sortEnd '.split(' ').join(c.namespace + ' ')); + .unbind('sortReset update updateAll updateRows updateCell addRows updateComplete sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress sortBegin sortEnd resetToLoadState '.split(' ').join(c.namespace + ' ')); c.$headers.add($f) .removeClass( [ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone].join(' ') ) .removeAttr('data-column') @@ -1403,6 +1466,8 @@ }; ts.getParserById = function(name) { + /*jshint eqeqeq:false */ + if (name == 'false') { return false; } var i, l = ts.parsers.length; for (i = 0; i < l; i++) { if (ts.parsers[i].id.toLowerCase() === (name.toString()).toLowerCase()) { @@ -1678,7 +1743,7 @@ if (s) { var c = table.config, ci = c.$headers.filter('[data-column=' + cellIndex + ']:last'), - format = ci.length && ci[0].dateFormat || ts.getData( ci, c.headers[cellIndex], 'dateFormat') || c.dateFormat; + format = ci.length && ci[0].dateFormat || ts.getData( ci, ts.getColumnData( table, c.headers, cellIndex ), 'dateFormat') || c.dateFormat; s = s.replace(/\s+/g," ").replace(/[\-.,]/g, "/"); // escaped - because JSHint in Firefox was showing it as an error if (format === "mmddyyyy") { s = s.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$3/$1/$2"); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 205ae63..8e0a32d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.16+ widgets - updated 4/30/2014 (v2.16.3) +/*! tableSorter 2.16+ widgets - updated 5/22/2014 (v2.17.0) * * Column Styles * Column Filters @@ -438,16 +438,18 @@ ts.filter = { query = ts.formatFloat( iFilter.replace(ts.filter.regex.operators, ''), table ), parser = c.parsers[index], savedSearch = query; - // parse filter value in case we're comparing numbers (dates) + // parse filter value in case we're comparing numbers (dates) if (parsed[index] || parser.type === 'numeric') { - cachedValue = parser.format( '' + iFilter.replace(ts.filter.regex.operators, ''), table, c.$headers.eq(index), index ); - query = ( typeof query === "number" && cachedValue !== '' && !isNaN(cachedValue) ) ? cachedValue : query; + result = parser.format( $.trim('' + iFilter.replace(ts.filter.regex.operators, '')), table, [], index ); + query = ( typeof result === "number" && result !== '' && !isNaN(result) ) ? result : query; } + // iExact may be numeric - see issue #149; // check if cached is defined, because sometimes j goes out of range? (numeric columns) - cachedValue = ( parsed[index] || parser.type === 'numeric' ) && !isNaN(query) && cached ? cached : + cachedValue = ( parsed[index] || parser.type === 'numeric' ) && !isNaN(query) && typeof cached !== 'undefined' ? cached : isNaN(iExact) ? ts.formatFloat( iExact.replace(ts.filter.regex.nondigit, ''), table) : ts.formatFloat( iExact, table ); + if ( />/.test(iFilter) ) { result = />=/.test(iFilter) ? cachedValue >= query : cachedValue > query; } if ( /</.test(iFilter) ) { result = /<=/.test(iFilter) ? cachedValue <= query : cachedValue < query; } // keep showing all rows if nothing follows the operator @@ -476,8 +478,8 @@ ts.filter = { }, // Look for an AND or && operator (logical and) and : function( filter, iFilter, exact, iExact ) { - if ( /\s+(AND|&&)\s+/g.test(filter) ) { - var query = iFilter.split( /(?:\s+(?:and|&&)\s+)/g ), + if ( ts.filter.regex.andTest.test(filter) ) { + var query = iFilter.split( ts.filter.regex.andSplit ), result = iExact.search( $.trim(query[0]) ) >= 0, indx = query.length - 1; while (result && indx) { @@ -490,10 +492,11 @@ ts.filter = { }, // Look for a range (using " to " or " - ") - see issue #166; thanks matzhu! range : function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed ) { - if ( /\s+(-|to)\s+/.test(iFilter) ) { + if ( ts.filter.regex.toTest.test(iFilter) ) { var result, tmp, c = table.config, - query = iFilter.split(/(?: - | to )/), // make sure the dash is for a range and not indicating a negative number + // make sure the dash is for a range and not indicating a negative number + query = iFilter.split( ts.filter.regex.toSplit ), range1 = ts.formatFloat(query[0].replace(ts.filter.regex.nondigit, ''), table), range2 = ts.formatFloat(query[1].replace(ts.filter.regex.nondigit, ''), table); // parse filter value in case we're comparing numbers (dates) @@ -513,9 +516,9 @@ ts.filter = { }, // Look for wild card: ? = single, * = multiple, or | = logical OR wild : function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed, rowArray ) { - if ( /[\?|\*]/.test(iFilter) || /\s+OR\s+/i.test(filter) ) { + if ( /[\?|\*]/.test(iFilter) || ts.filter.regex.orReplace.test(filter) ) { var c = table.config, - query = iFilter.replace(/\s+OR\s+/gi,"|"); + query = iFilter.replace(ts.filter.regex.orReplace, "|"); // look for an exact match with the "or" unless the "filter-match" class is found if (!c.$headers.filter('[data-column="' + index + '"]:last').hasClass('filter-match') && /\|/.test(query)) { query = $.isArray(rowArray) ? '(' + query + ')' : '^(' + query + ')$'; @@ -545,14 +548,30 @@ ts.filter = { } }, init: function(table, c, wo) { - var options, string, $header, column, filters, time; + // filter language options + ts.language = $.extend(true, {}, { + to : 'to', + or : 'or', + and : 'and' + }, ts.language); + + var options, string, $header, column, filters, time, fxn, + regex = ts.filter.regex; if (c.debug) { time = new Date(); } c.$table.addClass('hasFilters'); - ts.filter.regex.child = new RegExp(c.cssChildRow); - ts.filter.regex.filtered = new RegExp(wo.filter_filteredRow); + $.extend( regex, { + child : new RegExp(c.cssChildRow), + filtered : new RegExp(wo.filter_filteredRow), + alreadyFiltered : new RegExp('(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i'), + toTest : new RegExp('\\s+(-|' + ts.language.to + ')\\s+', 'i'), + toSplit : new RegExp('(?:\\s+(?:-|' + ts.language.to + ')\\s+)' ,'gi'), + andTest : new RegExp('\\s+(' + ts.language.and + '|&&)\\s+', 'i'), + andSplit : new RegExp('(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi'), + orReplace : new RegExp('\\s+(' + ts.language.or + ')\\s+', 'gi') + }); // don't build filter row if columnFilters is false or all columns are set to "filter-false" - issue #156 if (wo.filter_columnFilters !== false && c.$headers.filter('.filter-false').length !== c.$headers.length) { @@ -576,6 +595,7 @@ ts.filter = { if (/(update|add)/.test(event.type) && event.type !== "updateComplete") { // force a new search since content has changed c.lastCombinedFilter = null; + c.lastSearch = []; } // pass true (skipFirst) to prevent the tablesorter.setFilters function from skipping the first input // ensures all inputs are updated when a search is triggered on the table $('table').trigger('search', [...]); @@ -602,16 +622,16 @@ ts.filter = { } } if (wo.filter_functions) { - // column = column # (string) - for (column in wo.filter_functions) { - if (wo.filter_functions.hasOwnProperty(column) && typeof column === 'string') { + for (column = 0; column < c.columns; column++) { + fxn = ts.getColumnData( table, wo.filter_functions, column ); + if (fxn) { $header = c.$headers.filter('[data-column="' + column + '"]:last'); options = ''; - if (wo.filter_functions[column] === true && !$header.hasClass('filter-false')) { + if (fxn === true && !$header.hasClass('filter-false')) { ts.filter.buildSelect(table, column); - } else if (typeof column === 'string' && !$header.hasClass('filter-false')) { + } else if (typeof fxn === 'object' && !$header.hasClass('filter-false')) { // add custom drop down list - for (string in wo.filter_functions[column]) { + for (string in fxn) { if (typeof string === 'string') { options += options === '' ? '<option value="">' + ($header.data('placeholder') || $header.attr('data-placeholder') || wo.filter_placeholder.select || '') + '</option>' : ''; @@ -683,7 +703,7 @@ ts.filter = { return filters; }, buildRow: function(table, c, wo) { - var column, $header, buildSelect, disabled, name, + var column, $header, buildSelect, disabled, name, ffxn, // c.columns defined in computeThIndexes() columns = c.columns, buildFilter = '<tr class="' + ts.css.filterRow + '">'; @@ -696,22 +716,18 @@ ts.filter = { disabled = false; // assuming last cell of a column is the main column $header = c.$headers.filter('[data-column="' + column + '"]:last'); - buildSelect = (wo.filter_functions && wo.filter_functions[column] && typeof wo.filter_functions[column] !== 'function') || + ffxn = ts.getColumnData( table, wo.filter_functions, column ); + buildSelect = (wo.filter_functions && ffxn && typeof ffxn !== "function" ) || $header.hasClass('filter-select'); // get data from jQuery data, metadata, headers option or header class name - if (ts.getData) { - // get data from jQuery data, metadata, headers option or header class name - disabled = ts.getData($header[0], c.headers[column], 'filter') === 'false'; - } else { - // only class names and header options - keep this for compatibility with tablesorter v2.0.5 - disabled = (c.headers[column] && c.headers[column].hasOwnProperty('filter') && c.headers[column].filter === false) || - $header.hasClass('filter-false'); - } + disabled = ts.getData($header[0], ts.getColumnData( table, c.headers, column ), 'filter') === 'false'; + if (buildSelect) { buildFilter = $('<select>').appendTo( c.$filters.eq(column) ); } else { - if (wo.filter_formatter && $.isFunction(wo.filter_formatter[column])) { - buildFilter = wo.filter_formatter[column]( c.$filters.eq(column), column ); + ffxn = ts.getColumnData( table, wo.filter_formatter, column ); + if (ffxn) { + buildFilter = ffxn( c.$filters.eq(column), column ); // no element returned, so lets go find it if (buildFilter && buildFilter.length === 0) { buildFilter = c.$filters.eq(column).children('input'); @@ -761,7 +777,7 @@ ts.filter = { $el // use data attribute instead of jQuery data since the head is cloned without including the data/binding .attr('data-lastSearchTime', new Date().getTime()) - .unbind('keyup search change '.split(' ').join(c.namespace + 'filter ')) + .unbind('keypress keyup search change '.split(' ').join(c.namespace + 'filter ')) // include change for select - fixes #473 .bind('keyup search change '.split(' ').join(c.namespace + 'filter '), function(event) { $(this).attr('data-lastSearchTime', new Date().getTime()); @@ -774,8 +790,14 @@ ts.filter = { ( event.which >= 37 && event.which <= 40 ) || (event.which !== 13 && wo.filter_liveSearch === false) ) ) ) { return; } - // true flag tells getFilters to skip newest timed input - ts.filter.searching( table, true, true ); + // change event = no delay; last true flag tells getFilters to skip newest timed input + ts.filter.searching( table, event.type !== 'change', true ); + }) + .bind('keypress.' + c.namespace + 'filter', function(event){ + if (event.which === 13) { + event.preventDefault(); + $(this).blur(); + } }); c.$table.bind('filterReset', function(){ $el.val(''); @@ -787,6 +809,8 @@ ts.filter = { filterArray = $.isArray(filter), filters = (filterArray) ? filter : ts.getFilters(table, true), combinedFilters = (filters || []).join(''); // combined filter values + // prevent errors if delay init is set + if ($.isEmptyObject(c.cache)) { return; } // add filter array back into inputs if (filterArray) { ts.setFilters( table, filters, false, skipFirst !== true ); @@ -802,6 +826,7 @@ ts.filter = { } else if (filter === false) { // force filter refresh c.lastCombinedFilter = null; + c.lastSearch = []; } c.$table.trigger('filterStart', [filters]); if (c.showProcessing) { @@ -856,8 +881,9 @@ ts.filter = { if (table.config.lastCombinedFilter === combinedFilters) { return; } var cached, len, $rows, rowIndex, tbodyIndex, $tbody, $cells, columnIndex, childRow, childRowText, exact, iExact, iFilter, lastSearch, matches, result, - notFiltered, searchFiltered, filterMatched, showRow, time, - anyMatch, iAnyMatch, rowArray, rowText, iRowText, rowCache, + notFiltered, searchFiltered, filterMatched, showRow, time, val, indx, + anyMatch, iAnyMatch, rowArray, rowText, iRowText, rowCache, fxn, + regex = ts.filter.regex, c = table.config, wo = c.widgetOptions, columns = c.columns, @@ -868,7 +894,7 @@ ts.filter = { parsed = c.$headers.map(function(columnIndex) { return c.parsers && c.parsers[columnIndex] && c.parsers[columnIndex].parsed || // getData won't return "parsed" if other "filter-" class names exist (e.g. <th class="filter-select filter-parsed">) - ts.getData && ts.getData(c.$headers.filter('[data-column="' + columnIndex + '"]:last'), c.headers[columnIndex], 'filter') === 'parsed' || + ts.getData && ts.getData(c.$headers.filter('[data-column="' + columnIndex + '"]:last'), ts.getColumnData( table, c.headers, columnIndex ), 'filter') === 'parsed' || $(this).hasClass('filter-parsed'); }).get(); if (c.debug) { time = new Date(); } @@ -879,26 +905,34 @@ ts.filter = { // $rows = $tbody.children('tr').not(c.selectorRemove); columnIndex = c.columns; // convert stored rows into a jQuery object - $rows = true ? $( $.map(c.cache[tbodyIndex].normalized, function(el){ return el[columnIndex].$row.get(); }) ) : $tbody.children('tr').not(c.selectorRemove); - len = $rows.length; + $rows = $( $.map(c.cache[tbodyIndex].normalized, function(el){ return el[columnIndex].$row.get(); }) ); + if (combinedFilters === '' || wo.filter_serversideFiltering) { $rows.removeClass(wo.filter_filteredRow).not('.' + c.cssChildRow).show(); } else { + // filter out child rows + $rows = $rows.not('.' + c.cssChildRow); + len = $rows.length; // optimize searching only through already filtered rows - see #313 searchFiltered = true; lastSearch = c.lastSearch || c.$table.data('lastSearch') || []; - $.each(filters, function(indx, val) { + for (indx = 0; indx < columnIndex; indx++) { + val = filters[indx] || ''; + // break out of loop if we've already determined not to search filtered rows + if (!searchFiltered) { indx = columnIndex; } // search already filtered rows if... - searchFiltered = searchFiltered && - // there are changes from beginning of filter - (val || '').indexOf(lastSearch[indx]) === 0 && - // if there is not a logical "or" in the string - !/(\s+or\s+|\|)/g.test(val || '') && - // if we are not doing exact matches - !/[=\"]/.test(lastSearch[indx]) && + searchFiltered = searchFiltered && lastSearch.length && + // there are no changes from beginning of filter + val.indexOf(lastSearch[indx] || '') === 0 && + // if there is NOT a logical "or", or range ("to" or "-") in the string + !regex.alreadyFiltered.test(val) && + // if we are not doing exact matches, using "|" (logical or) or not "!" + !/[=\"\|!]/.test(val) && + // don't search only filtered if the value is negative ('> -10' => '> -100' will ignore hidden rows) + !(/(>=?\s*-\d)/.test(val) || /(<=?\s*\d)/.test(val)) && // if filtering using a select without a "filter-match" class (exact match) - fixes #593 - !( val !== '' && wo.filter_functions && wo.filter_functions[indx] === true && !c.$headers.filter('[data-column="' + indx + '"]:last').hasClass('filter-match') ); - }); + !( val !== '' && c.$filters && c.$filters.eq(indx).find('select').length && !c.$headers.filter('[data-column="' + indx + '"]:last').hasClass('filter-match') ); + } notFiltered = $rows.not('.' + wo.filter_filteredRow).length; // can't search when all rows are hidden - this happens when looking for exact matches if (searchFiltered && notFiltered === 0) { searchFiltered = false; } @@ -917,7 +951,7 @@ ts.filter = { for (rowIndex = 0; rowIndex < len; rowIndex++) { childRow = $rows[rowIndex].className; // skip child rows & already filtered rows - if ( ts.filter.regex.child.test(childRow) || (searchFiltered && ts.filter.regex.filtered.test(childRow)) ) { continue; } + if ( regex.child.test(childRow) || (searchFiltered && regex.filtered.test(childRow)) ) { continue; } showRow = true; // *** nextAll/nextUntil not supported by Zepto! *** childRow = $rows.eq(rowIndex).nextUntil('tr:not(.' + c.cssChildRow + ')'); @@ -973,24 +1007,25 @@ ts.filter = { exact = $.trim($cells.eq(columnIndex).text()); exact = c.sortLocaleCompare ? ts.replaceAccents(exact) : exact; // issue #405 } - iExact = !ts.filter.regex.type.test(typeof exact) && wo.filter_ignoreCase ? exact.toLocaleLowerCase() : exact; + iExact = !regex.type.test(typeof exact) && wo.filter_ignoreCase ? exact.toLocaleLowerCase() : exact; result = showRow; // if showRow is true, show that row // replace accents - see #357 filters[columnIndex] = c.sortLocaleCompare ? ts.replaceAccents(filters[columnIndex]) : filters[columnIndex]; // val = case insensitive, filters[columnIndex] = case sensitive iFilter = wo.filter_ignoreCase ? (filters[columnIndex] || '').toLocaleLowerCase() : filters[columnIndex]; - if (wo.filter_functions && wo.filter_functions[columnIndex]) { - if (wo.filter_functions[columnIndex] === true) { + fxn = ts.getColumnData( table, wo.filter_functions, columnIndex ); + if (fxn) { + if (fxn === true) { // default selector; no "filter-select" class result = (c.$headers.filter('[data-column="' + columnIndex + '"]:last').hasClass('filter-match')) ? iExact.search(iFilter) >= 0 : filters[columnIndex] === exact; - } else if (typeof wo.filter_functions[columnIndex] === 'function') { + } else if (typeof fxn === 'function') { // filter callback( exact cell content, parser normalized content, filter input value, column index, jQuery row object ) - result = wo.filter_functions[columnIndex](exact, cached, filters[columnIndex], columnIndex, $rows.eq(rowIndex)); - } else if (typeof wo.filter_functions[columnIndex][filters[columnIndex]] === 'function') { + result = fxn(exact, cached, filters[columnIndex], columnIndex, $rows.eq(rowIndex)); + } else if (typeof fxn[filters[columnIndex]] === 'function') { // selector option function - result = wo.filter_functions[columnIndex][filters[columnIndex]](exact, cached, filters[columnIndex], columnIndex, $rows.eq(rowIndex)); + result = fxn[filters[columnIndex]](exact, cached, filters[columnIndex], columnIndex, $rows.eq(rowIndex)); } } else { filterMatched = null; @@ -1044,15 +1079,16 @@ ts.filter = { wo = c.widgetOptions, parsed = [], arry = false, - source = wo.filter_selectSource; + source = wo.filter_selectSource, + fxn = $.isFunction(source) ? true : ts.getColumnData( table, source, column ); // filter select source option - if ($.isFunction(source)) { + if (fxn === true) { // OVERALL source arry = source(table, column, onlyAvail); - } else if ($.type(source) === 'object' && source.hasOwnProperty(column)) { + } else if ($.type(source) === 'object' && fxn) { // custom select source function for a SPECIFIC COLUMN - arry = source[column](table, column, onlyAvail); + arry = fxn(table, column, onlyAvail); } if (arry === false) { // fall back to original method @@ -1159,6 +1195,8 @@ ts.filter = { $filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters; } $filters.filter('select[data-column="' + column + '"]')[ updating ? 'html' : 'append' ](options); + if (!wo.filter_functions) { wo.filter_functions = {}; } + wo.filter_functions[column] = true; }, buildDefault: function(table, updating) { var columnIndex, $header, @@ -1169,10 +1207,7 @@ ts.filter = { for (columnIndex = 0; columnIndex < columns; columnIndex++) { $header = c.$headers.filter('[data-column="' + columnIndex + '"]:last'); // look for the filter-select class; build/update it if found - if (($header.hasClass('filter-select') || wo.filter_functions && wo.filter_functions[columnIndex] === true) && - !$header.hasClass('filter-false')) { - if (!wo.filter_functions) { wo.filter_functions = {}; } - wo.filter_functions[columnIndex] = true; // make sure this select gets processed by filter_functions + if (($header.hasClass('filter-select') || ts.getColumnData( table, wo.filter_functions, columnIndex ) === true) && !$header.hasClass('filter-false')) { ts.filter.buildSelect(table, columnIndex, updating, $header.hasClass(wo.filter_onlyAvail)); } } @@ -1244,6 +1279,7 @@ ts.setFilters = function(table, filter, apply, skipFirst) { if (c && apply) { // ensure new set filters are applied, even if the search is the same c.lastCombinedFilter = null; + c.lastSearch = []; ts.filter.searching(c.$table[0], filter, skipFirst); c.$table.trigger('filterFomatterUpdate'); } @@ -1475,7 +1511,7 @@ ts.addWidget({ var canResize, $column = $(this); column = $column.attr('data-column'); - canResize = ts.getData( $column, c.headers[column], 'resizable') === "false"; + canResize = ts.getData( $column, ts.getColumnData( table, c.headers, column ), 'resizable') === "false"; $rows.children().filter('[data-column="' + column + '"]')[canResize ? 'addClass' : 'removeClass']('resizable-false'); }); // add wrapper inside each cell to allow for positioning of the resizable target block diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index 89d6279..22d641b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Column Selector/Responsive table widget (beta) for TableSorter 3/31/2014 (v2.15.12) +/* Column Selector/Responsive table widget (beta) for TableSorter 5/22/2014 (v2.17.0) * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -12,7 +12,7 @@ namespace = '.tscolsel', tsColSel = ts.columnSelector = { queryAll : '@media only all { [columns] { display: none; } }', - queryBreak : '@media screen and (min-width: [size]) { [columns] { display: table-cell; } }', + queryBreak : '@media all and (min-width: [size]) { [columns] { display: table-cell; } }', init: function(table, c, wo) { var $t, colSel; @@ -47,6 +47,15 @@ tsColSel = ts.columnSelector = { tsColSel.updateCols(c, wo); } + c.$table + .off('refreshColumnSelector' + namespace) + .on('refreshColumnSelector' + namespace, function(){ + // make sure we're using current config settings + var c = this.config; + tsColSel.updateBreakpoints(c, c.widgetOptions); + tsColSel.updateCols(c, c.widgetOptions); + }); + }, setupSelector: function(table, c, wo) { @@ -114,10 +123,12 @@ tsColSel = ts.columnSelector = { colSel.lastIndex = -1; wo.columnSelector_breakpoints.sort(); tsColSel.updateBreakpoints(c, wo); - c.$table.off('updateAll' + namespace).on('updateAll' + namespace, function(){ - tsColSel.updateBreakpoints(c, wo); - tsColSel.updateCols(c, wo); - }); + c.$table + .off('updateAll' + namespace) + .on('updateAll' + namespace, function(){ + tsColSel.updateBreakpoints(c, wo); + tsColSel.updateCols(c, wo); + }); } if (colSel.$container.length) { @@ -295,7 +306,7 @@ ts.addWidget({ remove: function(table, c){ var csel = c.selector; csel.$container.empty(); - csel.$popup.empty(); + if (csel.$popup) { csel.$popup.empty(); } csel.$style.remove(); csel.$breakpoints.remove(); c.$table.off('updateAll' + namespace + ' update' + namespace); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js index b616670..27f5f51 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js @@ -1,4 +1,4 @@ -/*! tablesorter CSS Sticky Headers widget - updated 12/17/2013 (v2.15.0) +/*! tablesorter CSS Sticky Headers widget - updated 5/5/2014 (v2.16.4) * Requires a modern browser, tablesorter v2.8+ */ /*jshint jquery:true, unused:false */ @@ -9,10 +9,11 @@ id: "cssStickyHeaders", priority: 10, options: { - cssStickyHeaders_offset : 0, - cssStickyHeaders_addCaption : false, - cssStickyHeaders_attachTo : null, - cssStickyHeaders_zIndex : 10 + cssStickyHeaders_offset : 0, + cssStickyHeaders_addCaption : false, + cssStickyHeaders_attachTo : null, + cssStickyHeaders_filteredToTop : true, + cssStickyHeaders_zIndex : 10 }, init : function(table, thisWidget, c, wo) { var $attach = $(wo.cssStickyHeaders_attachTo), @@ -44,8 +45,10 @@ }); }); c.$table.bind('filterEnd', function() { - // scroll top of table into view - window.scrollTo(0, c.$table.position().top); + if (wo.cssStickyHeaders_filteredToTop) { + // scroll top of table into view + window.scrollTo(0, c.$table.position().top); + } }); }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index 615bccf..42c3398 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -1,4 +1,4 @@ -/*! tablesorter math widget - beta - updated 4/27/2014 (v2.16.2) +/*! tablesorter math widget - beta - updated 5/22/2014 (v2.17.0) * Requires tablesorter v2.16+ and jQuery 1.7+ * by Rob Garrison */ @@ -96,7 +96,7 @@ }, recalculate : function(table, c, wo, init){ - if (c && !wo.math_isUpdating) { + if (c && (!wo.math_isUpdating || init)) { // add data-column attributes to all table cells if (init) { @@ -150,7 +150,7 @@ output : function($cell, wo, value, arry) { // get mask from cell data-attribute: data-math-mask="#,##0.00" - var result = ts.formatMask( $cell.attr('data-' + wo.math_data + '-mask') || wo.math_mask, value ); + var result = ts.formatMask( $cell.attr('data-' + wo.math_data + '-mask') || wo.math_mask, value, wo.math_wrapPrefix, wo.math_wrapSuffix ); if ($.isFunction(wo.math_complete)) { result = wo.math_complete($cell, wo, result, value, arry); } @@ -169,20 +169,52 @@ * (c)2011 ecava * Dual licensed under the MIT or GPL Version 2 licenses. */ - ts.formatMask = function(m, v){ - var isNegative, result, decimal, group, pos_lead_zero, pos_trail_zero, pos_separator, part, szSep, - integer, str, offset, i, l; + ts.formatMask = function(m, v, tmpPrefix, tmpSuffix){ if ( !m || isNaN(+v) ) { return v; // return as it is. } + + var isNegative, result, decimal, group, posLeadZero, posTrailZero, posSeparator, part, szSep, + integer, str, offset, i, l, len, start, tmp, end, inv, + prefix = '', + suffix = ''; + + // find prefix/suffix + len = m.length; + start = m.search( /[0-9\-\+#]/ ); + tmp = start > 0 ? m.substring(0, start) : ''; + prefix = tmp; + if ( start > 0 && tmpPrefix ) { + if ( /\{content\}/.test(tmpPrefix || '') ) { + prefix = (tmpPrefix || '').replace(/\{content\}/g, tmp || ''); + } else { + prefix = (tmpPrefix || '') + tmp; + } + } + // reverse string: not an ideal method if there are surrogate pairs + inv = m.split('').reverse().join(''); + end = inv.search( /[0-9\-\+#]/ ); + i = len - end; + i += (m.substring( i, i + 1 ) === '.') ? 1 : 0; + tmp = end > 0 ? m.substring( i, len) : ''; + suffix = tmp; + if (tmp !== '' && tmpSuffix) { + if ( /\{content\}/.test(tmpSuffix || '') ) { + suffix = (tmpSuffix || '').replace(/\{content\}/g, tmp || ''); + } else { + suffix = tmp + (tmpSuffix || ''); + } + } + m = m.substring(start, i); + // convert any string to number according to formation sign. - v = m.charAt(0) == '-'? -v : +v; + v = m.charAt(0) == '-' ? -v : +v; isNegative = v < 0 ? v = -v : 0; // process only abs(), and turn on flag. // search for separator for grp & decimal, anything not digit, not +/- sign, not #. - result = m.match(/[^\d\-\+#]/g); - decimal = (result && result[result.length-1]) || '.'; // treat the right most symbol as decimal - group = (result && result[1] && result[0]) || ','; // treat the left most symbol as group separator + result = m.match( /[^\d\-\+#]/g ); + decimal = ( result && result[result.length-1] ) || '.'; // treat the right most symbol as decimal + group = ( result && result[1] && result[0] ) || ','; // treat the left most symbol as group separator // split the decimal for the format string if any. m = m.split( decimal ); @@ -191,18 +223,18 @@ v = +(v) + ''; // convert number to string to trim off *all* trailing decimal zero(es) // fill back any trailing zero according to format - pos_trail_zero = m[1] && m[1].lastIndexOf('0'); // look for last zero in format + posTrailZero = m[1] && m[1].lastIndexOf('0'); // look for last zero in format part = v.split('.'); // integer will get !part[1] - if ( !part[1] || part[1] && part[1].length <= pos_trail_zero ) { - v = (+v).toFixed( pos_trail_zero + 1 ); + if ( !part[1] || ( part[1] && part[1].length <= posTrailZero ) ) { + v = (+v).toFixed( posTrailZero + 1 ); } szSep = m[0].split( group ); // look for separator m[0] = szSep.join(''); // join back without separator for counting the pos of any leading 0. - pos_lead_zero = m[0] && m[0].indexOf('0'); - if ( pos_lead_zero > -1 ) { - while ( part[0].length < ( m[0].length - pos_lead_zero ) ) { + posLeadZero = m[0] && m[0].indexOf('0'); + if ( posLeadZero > -1 ) { + while ( part[0].length < ( m[0].length - posLeadZero ) ) { part[0] = '0' + part[0]; } } else if ( +part[0] === 0 ) { @@ -214,16 +246,17 @@ // process the first group separator from decimal (.) only, the rest ignore. // get the length of the last slice of split result. - pos_separator = ( szSep[1] && szSep[ szSep.length - 1 ].length ); - if (pos_separator) { + posSeparator = ( szSep[1] && szSep[ szSep.length - 1 ].length ); + if ( posSeparator ) { integer = v[0]; str = ''; - offset = integer.length % pos_separator; + offset = integer.length % posSeparator; l = integer.length; for ( i = 0; i < l; i++ ) { str += integer.charAt(i); // ie6 only support charAt for sz. - // -pos_separator so that won't trail separator on full length - if ( !(( i - offset + 1 ) % pos_separator) && i < l - pos_separator ) { + // -posSeparator so that won't trail separator on full length + /*jshint -W018 */ + if ( !( ( i - offset + 1 ) % posSeparator ) && i < l - posSeparator ) { str += group; } } @@ -231,7 +264,8 @@ } v[1] = ( m[1] && v[1] ) ? decimal + v[1] : ""; - return ( isNegative ? '-' : '' ) + v[0] + v[1]; // put back any negation and combine integer and fraction. + // put back any negation, combine integer and fraction, and add back prefix & suffix + return prefix + ( ( isNegative ? '-' : '' ) + v[0] + v[1] ) + suffix; }; ts.equations = { @@ -332,13 +366,18 @@ // complete executed after each fucntion math_complete : null, // function($cell, wo, result, value, arry){ return result; }, // order of calculation; "all" is last - math_priority : [ 'row', 'above', 'col' ] + math_priority : [ 'row', 'above', 'col' ], + // template for or just prepend the mask prefix & suffix with this HTML + // e.g. '<span class="red">{content}</span>' + math_prefix : '', + math_suffix : '' }, init : function(table, thisWidget, c, wo){ c.$table .bind('tablesorter-initialized update updateRows addRows updateCell filterReset filterEnd '.split(' ').join('.tsmath '), function(e){ - if (!wo.math_isUpdating) { - math.recalculate(table, c, wo, e.type === 'tablesorter-initialized'); + var init = e.type === 'tablesorter-initialized'; + if (!wo.math_isUpdating || init) { + math.recalculate( table, c, wo, init ); } }) .bind('updateComplete.tsmath', function(){ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index c1cdb21..3e2af5c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -1,4 +1,4 @@ -/* Output widget (beta) for TableSorter 4/14/2014 (v2.16.0) +/* Output widget (beta) for TableSorter 5/22/2014 (v2.17.0) * Requires tablesorter v2.8+ and jQuery 1.7+ * Modified from: * HTML Table to CSV: http://www.kunalbabre.com/projects/table2CSV.php (License unknown?) @@ -42,8 +42,8 @@ output = ts.output = { var $this, row, col, rowlen, collen, txt, wo = c.widgetOptions, tmpRow = [], + dupe = wo.output_duplicateSpans, addSpanIndex = isHeader && isJSON && wo.output_headerRows && $.isFunction(wo.output_callbackJSON), - rowIndex = 0, cellIndex = 0; $rows.each(function(rowIndex) { if (!tmpRow[rowIndex]) { tmpRow[rowIndex] = []; } @@ -56,7 +56,7 @@ output = ts.output = { txt = output.formatData( wo, $this.attr(wo.output_dataAttrib) || $this.html(), isHeader ); for (row = 1; row <= rowlen; row++) { if (!tmpRow[rowIndex + row]) { tmpRow[rowIndex + row] = []; } - tmpRow[rowIndex + row][cellIndex] = txt; + tmpRow[rowIndex + row][cellIndex] = isHeader ? txt : dupe ? txt : ''; } } // process colspans @@ -70,11 +70,11 @@ output = ts.output = { for (row = 0; row < rowlen; row++) { if (!tmpRow[rowIndex + row]) { tmpRow[rowIndex + row] = []; } tmpRow[rowIndex + row][cellIndex + col] = addSpanIndex ? - wo.output_callbackJSON($this, txt, cellIndex + col) || txt + '(' + (cellIndex + col) + ')' : txt; + wo.output_callbackJSON($this, txt, cellIndex + col) || txt + '(' + (cellIndex + col) + ')' : isHeader ? txt : dupe ? txt : ''; } } else { tmpRow[rowIndex][cellIndex + col] = addSpanIndex ? - wo.output_callbackJSON($this, txt, cellIndex + col) || txt + '(' + (cellIndex + col) + ')' : txt; + wo.output_callbackJSON($this, txt, cellIndex + col) || txt + '(' + (cellIndex + col) + ')' : isHeader ? txt : dupe ? txt : ''; } } } @@ -82,7 +82,7 @@ output = ts.output = { // don't include hidden columns if ( $this.css('display') !== 'none' ) { // skip column if already defined - while (tmpRow[rowIndex][cellIndex]) { cellIndex++; } + while (typeof tmpRow[rowIndex][cellIndex] !== 'undefined') { cellIndex++; } tmpRow[rowIndex][cellIndex] = tmpRow[rowIndex][cellIndex] || output.formatData( wo, $this.attr(wo.output_dataAttrib) || $this.html(), isHeader ); cellIndex++; @@ -92,6 +92,16 @@ output = ts.output = { return tmpRow; }, + ignoreColumns : function(wo, data) { + // ignore columns -> remove data from built array (because we've already processed any rowspan/colspan) + $.each( data, function(indx, val){ + data[indx] = $.grep(val, function(v, cellIndx){ + return $.inArray(cellIndx, wo.output_ignoreColumns) < 0; + }); + }); + return data; + }, + process : function(c, wo) { var mydata, $this, $rows, headers, csvData, len, hasStringify = window.JSON && JSON.hasOwnProperty('stringify'), @@ -118,23 +128,30 @@ output = ts.output = { csvData = output.processRow(c, $rows); len = headers.length; + if (wo.output_ignoreColumns.length) { + headers = output.ignoreColumns(wo, headers); + csvData = output.ignoreColumns(wo, csvData); + } + if (outputJSON) { tmpData = []; $.each( csvData, function(indx, val){ // multiple header rows & output_headerRows = true, pick the last row... tmpData.push( output.row2Hash( headers[ (len > 1 && wo.output_headerRows) ? indx % len : len - 1], val ) ); }); + // requires JSON stringify; if it doesn't exist, the output will show [object Object],... in the output window mydata = hasStringify ? JSON.stringify(tmpData) : tmpData; } else { tmpData = output.row2CSV(wo, wo.output_headerRows ? headers : [ headers[ (len > 1 && wo.output_headerRows) ? indx % len : len - 1] ], outputArray) .concat( output.row2CSV(wo, csvData, outputArray) ); + // stringify the array; if stringify doesn't exist the array will be flattened mydata = outputArray && hasStringify ? JSON.stringify(tmpData) : tmpData.join('\n'); } // callback; if true returned, continue processing - if (!wo.output_callback(mydata)) { return; } + if ($.isFunction(wo.output_callback) && !wo.output_callback(c, mydata)) { return; } if ( /p/.test( (wo.output_delivery || '').toLowerCase() ) ) { output.popup(mydata, wo.output_popupStyle, outputJSON || outputArray); @@ -206,7 +223,7 @@ output = ts.output = { // modified from https://github.com/PixelsCommander/Download-File-JS download : function (wo, data){ var e, link, - processedData = 'data:text/csv;charset=utf8,' + encodeURIComponent(data); + processedData = wo.output_encoding + encodeURIComponent(data); // iOS devices do not support downloading. We have to inform user about this. if (/(iP)/g.test(navigator.userAgent)) { @@ -242,23 +259,30 @@ output = ts.output = { ts.addWidget({ id: "output", options: { - output_separator : ',', // set to "json", "array" or any separator - output_dataAttrib : 'data-name', // header attrib containing modified header name - output_headerRows : false, // if true, include multiple header rows (JSON only) - output_delivery : 'popup', // popup, download - output_saveRows : 'filtered', // all, visible or filtered - output_replaceQuote : '\u201c;', // left double quote - output_includeHTML : false, - output_trimSpaces : true, - output_wrapQuotes : false, - output_popupStyle : 'width=500,height=300', - output_saveFileName : 'mytable.csv', + output_separator : ',', // set to "json", "array" or any separator + output_ignoreColumns : [], // columns to ignore [0, 1,... ] (zero-based index) + output_dataAttrib : 'data-name', // header attrib containing modified header name + output_headerRows : false, // if true, include multiple header rows (JSON only) + output_delivery : 'popup', // popup, download + output_saveRows : 'filtered', // all, visible or filtered + output_duplicateSpans: true, // duplicate output data in tbody colspan/rowspan + output_replaceQuote : '\u201c;', // left double quote + output_includeHTML : false, + output_trimSpaces : true, + output_wrapQuotes : false, + output_popupStyle : 'width=500,height=300', + output_saveFileName : 'mytable.csv', // callback executed when processing completes // return true to continue download/output // return false to stop delivery & do something else with the data - output_callback : function(data){ return true; }, + output_callback : function(config, data){ return true; }, // JSON callback executed when a colspan is encountered in the header - output_callbackJSON : function($cell, txt, cellIndex) { return txt + '(' + (cellIndex + col) + ')'; } + output_callbackJSON : function($cell, txt, cellIndex) { return txt + '(' + (cellIndex) + ')'; }, + // output data type (with BOM or Windows-1252 is needed for excel) + // NO BOM : 'data:text/csv;charset=utf8,' + // With BOM : 'data:text/csv;charset=utf8,%EF%BB%BF' + // WIN 1252 : 'data:text/csv;charset=windows-1252' + output_encoding : 'data:text/csv;charset=utf8,' }, init: function(table, thisWidget, c) { output.init(c); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 4cb4c3b..7fe8df8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/* Pager widget (beta) for TableSorter 4/23/2014 (v2.16.0) */ +/* Pager widget (beta) for TableSorter 5/22/2014 (v2.17.0) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ "use strict"; @@ -131,7 +131,6 @@ tsp = ts.pager = { filteredPages: 0, currentFilters: [], page: wo.pager_startPage, - size: wo.pager_size, startRow: 0, endRow: 0, ajaxCounter: 0, @@ -147,6 +146,7 @@ tsp = ts.pager = { ts.log('Pager initializing'); } + p.size = $.data(table, 'pagerLastSize') || wo.pager_size; // added in case the pager is reinitialized after being destroyed. p.$container = $(s.container).addClass(wo.pager_css.container).show(); // goto selector @@ -246,6 +246,11 @@ tsp = ts.pager = { }) .on('update updateRows updateAll addRows '.split(' ').join('.pager '), function(e){ e.stopPropagation(); + tsp.fixHeight(table, c); + var $rows = c.$tbodies.eq(0).children(); + p.totalRows = $rows.length - ( c.widgetOptions.pager_countChildRows ? 0 : $rows.filter('.' + c.cssChildRow).length ); + p.totalPages = Math.ceil( p.totalRows / p.size ); + tsp.updatePageDisplay(table, c); tsp.hideRows(table, c); // make sure widgets are applied - fixes #450 c.$table.trigger('applyWidgets'); @@ -486,7 +491,7 @@ tsp = ts.pager = { ts.log('Ajax Error', xhr, exception); } ts.showError(table, exception.message + ' (' + xhr.status + ')'); - c.$tbodies.eq(0).empty(); + c.$tbodies.eq(0).detach(); p.totalRows = 0; } else { // process ajax object @@ -508,7 +513,7 @@ tsp = ts.pager = { if (d instanceof jQuery) { if (wo.pager_processAjaxOnInit) { // append jQuery object - c.$tbodies.eq(0).empty().append(d); + c.$tbodies.eq(0).detach().append(d); } } else if (l) { // build table from array diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js new file mode 100644 index 0000000..54bda3d --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js @@ -0,0 +1,122 @@ +/* Print widget (beta) for TableSorter 5/22/2014 (v2.17.0) + * Requires tablesorter v2.8+ and jQuery 1.7+ + */ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;(function($){ +"use strict"; + +var ts = $.tablesorter, + +printTable = ts.printTable = { + + event : 'printTable', + basicStyle : 'table, tr, td, th { border : solid 1px black; border-collapse : collapse; } td, th { padding: 2px; }', + + init : function(c) { + c.$table + .off(printTable.event) + .on(printTable.event, function(){ + // explicitly use table.config.widgetOptions because we want + // the most up-to-date values; not the "wo" from initialization + printTable.process(c, c.widgetOptions); + }); + }, + + process : function(c, wo) { + var $this, + $table = $('<div/>').append(c.$table.clone()), + printStyle = printTable.basicStyle + 'table { width: 100% }' + + // hide filter row + '.tablesorter-filter-row { display: none }' + + // hide sort arrows + '.tablesorter-header { background-image: none !important; }'; + + // replace content with data-attribute content + $table.find('[' + wo.print_dataAttrib + ']').each(function(){ + $this = $(this); + $this.text( $this.attr(wo.print_dataAttrib) ); + }); + + // === rows === + // Assume "visible" means rows hidden by the pager (rows set to "display:none") + // or hidden by a class name which is added to the wo.print_extraCSS definition + if (/a/i.test(wo.print_rows)) { + // force show of all rows + printStyle += 'tbody tr { display: table-row !important; }'; + } else if (/f/i.test(wo.print_rows)) { + // add definition to show all non-filtered rows (cells hidden by the pager) + printStyle += 'tbody tr:not(.' + (wo.filter_filteredRow || 'filtered') + ') { display: table-row !important; }'; + } + + // === columns === + // columnSelector -> c.selector.$style + // Assume "visible" means hidden columns have a "display:none" style, or a class name + // add the definition to the wo.print_extraCSS option + if (/s/i.test(wo.print_columns) && c.selector && c.widgets.indexOf('columnSelector') >= 0) { + // show selected (visible) columns; make a copy of the columnSelector widget css (not media queries) + printStyle += c.selector.auto ? '' : c.selector.$style.text(); + } else if (/a/i.test(wo.print_columns)) { + // force show all cells + printStyle += 'td, th { display: table-cell !important; }'; + } + + printStyle += wo.print_extraCSS; + + // callback function + if ( $.isFunction(wo.print_callback) ) { + wo.print_callback( c, $table, printStyle ); + } else { + printTable.printOutput(c, $table.html(), printStyle); + } + + }, // end process + + printOutput : function(c, data, style) { + var wo = c.widgetOptions, + generator = window.open('', wo.print_title, 'width=500,height=300'), + t = wo.print_title || c.$table.find('caption').text() || c.$table[0].id || document.title || 'table'; + generator.document.write( + '<html><head><title>' + t + '</title>' + + ( wo.print_styleSheet ? '<link rel="stylesheet" href="' + wo.print_styleSheet + '">' : '' ) + + '<style>' + style + '</style>' + + '</head><body>' + data + '</body></html>' + ); + generator.print(); + generator.document.close(); + return true; + }, + + remove : function(c) { + c.$table.off(printTable.event); + } + +}; + +ts.addWidget({ + id: 'print', + options: { + print_title : '', // this option > caption > table id > "table" + print_dataAttrib : 'data-name', // header attrib containing modified header name + print_rows : 'filtered', // (a)ll, (v)isible or (f)iltered + print_columns : 'selected', // (a)ll or (s)elected (if columnSelector widget is added) + print_extraCSS : '', // add any extra css definitions for the popup window here + print_styleSheet : '', // add the url of your print stylesheet + // callback executed when processing completes + // to continue printing, use the following function: + // function( config, $table, printStyle ) { + // // do something to the table or printStyle string + // $.tablesorter.printTable.printOutput( config, $table.html(), printStyle ); + // } + print_callback : null + }, + init: function(table, thisWidget, c) { + printTable.init(c); + }, + remove: function(table, c){ + printTable.remove(c); + } + +}); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index 3ecdfcb..eee5de5 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -10,7 +10,7 @@ Resizable scroller widget for the jQuery tablesorter plugin - Version 2.0 - modified by Rob Garrison (4/12/2013; updated 4/27/2014 for tablesorter v2.16.2) + Version 2.0 - modified by Rob Garrison 4/12/2013; updated 5/22/2014 (v2.17.0) Requires jQuery v1.7+ Requires the tablesorter plugin, v2.8+, available at http://mottie.github.com/tablesorter/docs/ @@ -109,14 +109,14 @@ ts.addWidget({ .wrap('<div class="tablesorter-scroller-header" style="width:' + $tbl.width() + ';" />') .find('.' + ts.css.header); - $tbl.wrap('<div class="tablesorter-scroller-table" style="height:' + h + 'px;width:' + $tbl.width() + ';overflow-y:scroll;" />') + $tbl.wrap('<div class="tablesorter-scroller-table" style="height:' + h + 'px;width:' + $tbl.width() + ';overflow-y:scroll;" />'); // make scroller header sortable ts.bindEvents(table, $cells); // look for filter widget if ($tbl.hasClass('hasFilters')) { - ts.filter.bindSearch( $tbl, $cells.find('.' + ts.css.filter) ); + ts.filter.bindSearch( $tbl, $hdr.find('.' + ts.css.filter) ); } resize = function(){ diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index cab2b0d..8de3d71 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -37,10 +37,18 @@ line-height: 14px; display: inline-block; } + +/* black unsorted icon */ .tablesorter-bootstrap .bootstrap-icon-unsorted { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAYAAAD5YeaVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAWVJREFUeNqUUL9Lw2AUTGP8mqGlpBQkNeCSRcckEBcHq1jImMElToKuDvpHFMGhU0BQcHBwLji6CE1B4uB/INQsDi4d2jQ/fPeZxo764OV6915f7lLJ81xot9tCURXqdVEUr7IsO6ffH9Q5BlEUCaLwWxWqTcbYnaIoh0Dw4gAvcWlxq1qt9hqNxg6hUGAP+uIPUrGs0qXLer2+v/pTX6QpxLtkc2U2m53ACb8sSdIDXerSEms2m6+DweAICA4d89KGbduf9MpEVdXQ9/2LVqv1CASHjjn3iq/x1xKFfxQPqGnada1W86bT6SiO42OS3qk3KPStLMvbk8nkfjwen/LLuq6blFymMB0KdUPSGhAcOualjX6/f0bCiC7NaWGPQr0BwaFjzn0gYJqmLAiCA8/zni3LmhuGkQPBoWPOPwQeaPIqD4fDruu6L6Zp5kBw6IudchmdJAkLw3DXcZwnIPjy/FuAAQCiqqWWCAFKcwAAAABJRU5ErkJggg==); + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAMAAADOvxanAAAAVFBMVEUAAABCQkJZWVkZGRnJyckgICAZGRkZGRn8/PweHh4dHR0aGhoaGhpUVFQbGxvQ0NDc3NxMTExSUlIbGxvr6+s4ODhKSkogICAtLS00NDQzMzMnJydSEPrQAAAAGHRSTlMA1ssZRLgdAQbDyisqsZo8QdXUq0r9xPepSRwiAAAAX0lEQVQI13XHSQKAIAwEwQAKxn13Ev7/T2Pu9qmarJKPXIicI4PH4hxaKNrhm2S8bJK5h4YzKHrzJNtK6yYT/TdXzpS5zuYg4MSQYF6i4IHExdw1UVRi05HPrrvT53a+qyMFC9t04gcAAAAASUVORK5CYII=); } +/* white unsorted icon +.tablesorter-bootstrap .bootstrap-icon-unsorted { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOBAMAAAALT/umAAAAKlBMVEUAAAD///////////////////////////////////////////////////+Gu8ovAAAADXRSTlMA4EXKBtQqvR0+sxmalc142gAAAFdJREFUCNdjYGDoamAAAjZbMxCVfvd6AgMDd+3du9UMDKx3hWSvMjBwXZww8RYDGuC53NB8h4GB8a617UUGBs7Yu3cjGRhYVO9eVQFKOskKOQApFmUgBwBZ+xXRTttNdAAAAABJRU5ErkJggg==); +} + */ + /* since bootstrap (table-striped) uses nth-child(), we just use this to add a zebra stripe color */ .tablesorter-bootstrap > tbody > tr.odd > td, .tablesorter-bootstrap > tbody > tr.tablesorter-hasChildRow.odd:hover ~ tr.tablesorter-hasChildRow.odd ~ .tablesorter-childRow.odd > td { @@ -62,8 +70,6 @@ background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs='); background-position: center center !important; background-repeat: no-repeat !important; - position: absolute; - z-index: 1000; } /* caption */ diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css index c7dc6e2..0306745 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css @@ -17,7 +17,7 @@ } /* header */ -.tablesorter-green thead tr, +.tablesorter-green thead tr .tablesorter-header, .tablesorter-green tfoot tr { background: center center repeat-x; background-image: url(data:image/gif;base64,R0lGODlhAQBkAOYAAN/e39XU1fX19tTU1eXm5uTl5ePk5OLj4+Hi4vX29fT19PP08/Lz8vHy8fDx8O/w7+7v7uzt7Orr6ufo5/T08/Pz8ufn5uLi4eDg39/f3t3d3Nzc29HR0NDQz8/Pzuvq6urp6eno6Ojn5+fm5tfW1tbV1dTT09PS0tLR0dHQ0NDPz/f39/b29vX19fT09PPz8/Ly8vHx8e/v7+7u7u3t7ezs7Ovr6+rq6unp6ejo6Ofn5+bm5uXl5eTk5OPj4+Li4uHh4eDg4N/f397e3t3d3dzc3Nvb29ra2tnZ2djY2NfX19XV1dPT09LS0tHR0dDQ0M/Pz8rKysXFxf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAFMALAAAAAABAGQAAAdegCsrLC0tLi+ILi6FCSwsCS0KkhQVDA0OMjM0NTYfICEiIzw9P0AYGUQaG0ZHSEoDTU9Qs08pTk1MSyRJR0VDQT8+PTw7Ojg3NTMyMTAvi4WOhC0vMTI1OT9GTlFSgQA7); @@ -31,7 +31,7 @@ padding: 6px; } .tablesorter-green .header, -.tablesorter-green .tablesorter-header { +.tablesorter-green .tablesorter-header-inner { background: no-repeat 5px center; background-image: url(data:image/gif;base64,R0lGODlhEAAQAOYAAA5NDBBYDlWWUzRUM5DVjp7inJ/fnQ1ECiCsGhyYFxqKFRFdDhBXDQxCCiO8HSK2HCCqGh2aGByUFxuPFhqNFhmHFRZ2EhVvERRpEBBVDSS8HiGyHB+mGh6fGRuTFxiAFBd5Eww/Cgs5CRp7Fiu+JRx8GCy/JjHAKyynKCuhJzXCMDbCMDnDMyNuHz3EODy9N0LFPSl7JkvIRjycOFDKS1LKTVPLT1XLUFTCT17OWTBkLmbQYnDTbHXVcXnWdoXago/djGmUZ112XCJEIEdjRf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAEUALAAAAAAQABAAAAdlgEWCg4SFhoIvh4cVLECKhCMeJjwFj0UlEwgaMD4Gii0WFAkRHQ47BIY6IQAZDAwBCyAPOJa1toRBGBAwNTY3OT0/AoZCDQoOKi4yNDOKRCIfGycrKZYDBxIkKLZDFxy3RTHgloEAOw==); /* background-image: url(/assets/jquery-tablesorter/green-unsorted.gif); */ @@ -39,15 +39,15 @@ white-space: normal; cursor: pointer; } -.tablesorter-green thead .headerSortUp, -.tablesorter-green thead .tablesorter-headerSortUp, -.tablesorter-green thead .tablesorter-headerAsc { +.tablesorter-green thead .headerSortUp .tablesorter-header-inner, +.tablesorter-green thead .tablesorter-headerSortUp .tablesorter-header-inner, +.tablesorter-green thead .tablesorter-headerAsc .tablesorter-header-inner { background-image: url(data:image/gif;base64,R0lGODlhEAAQANUAAA5NDBBYDpDVjp7inJ/fnSCsGhyYFxFdDhBXDSO8HSK2HB2aGBuPFhqNFhmHFRZ2EhBVDSS8Hh6fGRuTFxd5Eww/Chp7Fhx8GCy/JjnDMyNuHzy9N0LFPVTCTzBkLmbQYnDTbHnWdo/djP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACMALAAAAAAQABAAAAY4wJFwSCwaj8ikcslMbpojR0bEtEwwoIHywihEOCECUvNoGBaSxEdg9FQAEAQicKAoOtC8fs8fBgEAOw==) /* background-image: url(/assets/jquery-tablesorter/green-asc.gif); */ } -.tablesorter-green thead .headerSortDown, -.tablesorter-green thead .tablesorter-headerSortDown, -.tablesorter-green thead .tablesorter-headerDesc { +.tablesorter-green thead .headerSortDown .tablesorter-header-inner, +.tablesorter-green thead .tablesorter-headerSortDown .tablesorter-header-inner, +.tablesorter-green thead .tablesorter-headerDesc .tablesorter-header-inner { background-image: url(data:image/gif;base64,R0lGODlhEAAQANUAAFWWUzRUMw1EChqKFQxCCiO8HSCqGhyUFxVvERRpECGyHB+mGhiAFAs5CSu+JTHAKyynKCuhJzXCMDbCMD3EOELFPSl7JkvIRjycOFDKS1LKTVPLT1XLUF7OWXXVcYXagmmUZ112XCJEIEdjRf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACQALAAAAAAQABAAAAY4QJJwSCwaj8ikcskkghKGimbD6Xg+AGOIMChIKJcMBjlqMBSPSUQZEBwcEKYIsWiSLPa8fs9HBgEAOw==) /* background-image: url(/assets/jquery-tablesorter/green-desc.gif); */ } @@ -55,10 +55,10 @@ .tablesorter-green td.tablesorter-header .tablesorter-header-inner { padding-left: 23px; } -.tablesorter-green thead .tablesorter-header.sorter-false { +.tablesorter-green thead .tablesorter-header.sorter-false .tablesorter-header-inner { background-image: none; cursor: default; - padding: 4px; + padding-left: 6px; } /* tfoot */ diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css new file mode 100644 index 0000000..a2c8ec1 --- /dev/null +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css @@ -0,0 +1,192 @@ +/************* +Metro Dark Theme +*************/ +/* overall */ +.tablesorter-metro-dark { + width: 100%; + font: 12px/18px 'Segoe UI Semilight', 'Open Sans', Verdana, Arial, Helvetica, sans-serif; + color: #000; + background-color: #333; + border-spacing: 0; + margin: 10px 0 15px; + text-align: left; +} + +.tablesorter-metro-dark tr.dark-row th, .tablesorter-metro-dark tr.dark-row td { + background-color: #222; + color: #fff; + padding: 2px; + text-align: left; + font-size: 14px; +} + +/* header/footer */ +.tablesorter-metro-dark caption, +.tablesorter-metro-dark th, +.tablesorter-metro-dark thead td, +.tablesorter-metro-dark tfoot th, +.tablesorter-metro-dark tfoot td { + font-weight: 300; + font-size: 15px; + color: #ddd; + background-color: #333; + padding: 4px; +} + +.tablesorter-metro-dark .header, +.tablesorter-metro-dark .tablesorter-header { + background-image: url(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAQBAMAAADQT4M0AAAAGFBMVEUAAADu7u7u7u7u7u7u7u7u7u7u7u7u7u5jNePWAAAACHRSTlMAMxIHKwEgMWD59H4AAABSSURBVAjXY2BgYFJgAAHzYhDJ6igSAKTYBAUTgJSioKAQAwNzoaCguAFDiCAQuDIkgigxBgiA8cJAVCpQt6AgSL+JoKAzA0gjUBsQqBcBCYhFAAE/CV4zeSzxAAAAAElFTkSuQmCC); + background-position: center right; + background-repeat: no-repeat; + cursor: pointer; + white-space: normal; +} +.tablesorter-metro-dark .tablesorter-header-inner { + padding: 0 18px 0 4px; +} +.tablesorter-metro-dark thead .headerSortUp, +.tablesorter-metro-dark thead .tablesorter-headerSortUp, +.tablesorter-metro-dark thead .tablesorter-headerAsc { + background-image: url(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAQBAMAAADQT4M0AAAAIVBMVEUAAADu7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u5meJAOAAAACnRSTlMAMwsqXt+gIBUGxGoDMAAAAFlJREFUCNctzC0SQAAUReEzGNQ3AlHRiSRZFCVZYgeswRL8hLdK7834wj3tAlGP6y7fYHpKS6w6WwbVG0I1NZVnZPG8/DYxOYlnhUYkA06R1s9ESsxR4NIdPhkPFDFYuEnMAAAAAElFTkSuQmCC); +} +.tablesorter-metro-dark thead .headerSortDown, +.tablesorter-metro-dark thead .tablesorter-headerSortDown, +.tablesorter-metro-dark thead .tablesorter-headerDesc { + background-image: url(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAQBAMAAADQT4M0AAAALVBMVEUAAADu7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7u7i0NViAAAADnRSTlMAMiweCQITTvDctZZqaTlM310AAABcSURBVAjXY2BgYEtgAAFHERDJqigUAKSYBQUNgFSioKAYAwOLIBA4MASBKFUGQxAlzAAF+94BwWuGKBC1lIFl3rt3Lx0YGCzevWsGSjK9e6cAUlT3HKyW9wADAwDRrBiDy6bKzwAAAABJRU5ErkJggg==); +} +.tablesorter-metro-dark thead .sorter-false { + background-image: none; + cursor: default; + padding: 4px; +} + +/* tbody */ +.tablesorter-metro-dark td { + background-color: #fff; + padding: 4px; + vertical-align: top; +} + +/* hovered row colors */ +.tablesorter-metro-dark tbody > tr:hover > td, +.tablesorter-metro-dark tbody > tr.even:hover > td, +.tablesorter-metro-dark tbody > tr.odd:hover > td { + background: #bbb; + color: #000; +} + +/* table processing indicator */ +.tablesorter-metro-dark .tablesorter-processing { + background-position: center center !important; + background-repeat: no-repeat !important; + /* background-image: url(../addons/pager/icons/loading.gif) !important; */ + background-image: url(data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=) !important; +} + +/* pager */ +.tablesorter-metro-dark .tablesorter-pager button { + background-color: #444; + color: #eee; + border: #555 1px solid; + cursor: pointer; +} +.tablesorter-metro-dark .tablesorter-pager button:hover { + background-color: #555; +} + +/* Zebra Widget - row alternating colors */ +.tablesorter-metro-dark tr.odd td { + background-color: #eee; +} +.tablesorter-metro-dark tr.even td { + background-color: #fff; +} + +/* Column Widget - column sort colors */ +.tablesorter-metro-dark tr.odd td.primary { + background-color: #bfbfbf; +} +.tablesorter-metro-dark td.primary, +.tablesorter-metro-dark tr.even td.primary { + background-color: #d9d9d9; +} +.tablesorter-metro-dark tr.odd td.secondary { + background-color: #d9d9d9; +} +.tablesorter-metro-dark td.secondary, +.tablesorter-metro-dark tr.even td.secondary { + background-color: #e6e6e6; +} +.tablesorter-metro-dark tr.odd td.tertiary { + background-color: #e6e6e6; +} +.tablesorter-metro-dark td.tertiary, +.tablesorter-metro-dark tr.even td.tertiary { + background-color: #f2f2f2; +} + +/* filter widget */ +.tablesorter-metro-dark .tablesorter-filter-row td { + background: #eee; + line-height: normal; + text-align: center; /* center the input */ + -webkit-transition: line-height 0.1s ease; + -moz-transition: line-height 0.1s ease; + -o-transition: line-height 0.1s ease; + transition: line-height 0.1s ease; +} +/* optional disabled input styling */ +.tablesorter-metro-dark .tablesorter-filter-row .disabled { + opacity: 0.5; + filter: alpha(opacity=50); + cursor: not-allowed; +} +/* hidden filter row */ +.tablesorter-metro-dark .tablesorter-filter-row.hideme td { + /*** *********************************************** ***/ + /*** change this padding to modify the thickness ***/ + /*** of the closed filter row (height = padding x 2) ***/ + padding: 2px; + /*** *********************************************** ***/ + margin: 0; + line-height: 0; + cursor: pointer; +} +.tablesorter-metro-dark .tablesorter-filter-row.hideme .tablesorter-filter { + height: 1px; + min-height: 0; + border: 0; + padding: 0; + margin: 0; + /* don't use visibility: hidden because it disables tabbing */ + opacity: 0; + filter: alpha(opacity=0); +} +/* filters */ +.tablesorter-metro-dark .tablesorter-filter { + width: 95%; + height: auto; + margin: 4px; + padding: 4px; + background-color: #fff; + border: 1px solid #bbb; + color: #333; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition: height 0.1s ease; + -moz-transition: height 0.1s ease; + -o-transition: height 0.1s ease; + transition: height 0.1s ease; +} +/* rows hidden by filtering (needed for child rows) */ +.tablesorter .filtered { + display: none; +} + +/* ajax error row */ +.tablesorter .tablesorter-errorRow td { + text-align: center; + cursor: pointer; + background-color: #e6bf99; +} From 63de0f08d2ce4de43492b39f7bafe8fa461bee69 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Thu, 29 May 2014 10:51:30 +0200 Subject: [PATCH 031/138] * updated tablesorter to latest version (2.17.1) --- CHANGELOG.md | 4 +++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 23 ++++++------ .../jquery-tablesorter/jquery.tablesorter.js | 35 ++++++++++++++----- .../jquery.tablesorter.widgets.js | 26 ++++++++------ .../parsers/parser-input-select.js | 15 ++++---- .../jquery-tablesorter/widgets/widget-math.js | 26 ++++++++++---- .../widgets/widget-pager.js | 20 ++++++----- 10 files changed, 103 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5653b49..f647e67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.12.1 (2014-05-29) + +* Upgrade tablesorter to v2.17.1 + #### v1.12.0 (2014-05-22) * Upgrade tablesorter to v2.17.0 diff --git a/README.md b/README.md index c419b51..96387e8 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.17.0 (5/22/2014), [documentation] +Current tablesorter version: 2.17.1 (5/28/2014), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index deeb577..64d8710 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.12.0' + VERSION = '1.12.1' end diff --git a/tablesorter b/tablesorter index 5909671..c09945c 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 5909671c7635c8e8f6a69f6e110ebcda214871a0 +Subproject commit c09945c60c2ff8d96163718031d39cedfb3f7754 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index ef9c16d..26fa83a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 5/22/2014 (v2.17.0) + * updated 5/28/2014 (v2.17.1) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -67,7 +67,7 @@ // Save pager page & size if the storage script is loaded (requires $.tablesorter.storage in jquery.tablesorter.widgets.js) savePages: true, - + // defines custom storage key storageKey: 'tablesorter-pager', @@ -271,7 +271,7 @@ c = table.config, $t = c.$table, tds = '', - result = p.ajaxProcessing(data, table) || [ 0, [] ], + result = p.ajaxProcessing(data, table, xhr) || [ 0, [] ], hl = $t.find('thead th').length; // Clean up any previous error. @@ -368,10 +368,13 @@ fixHeight(table, p); $t.trigger('updateCache', [function(){ if (p.initialized) { - // apply widgets after table has rendered - $t - .trigger('applyWidgets') - .trigger('pagerChange', p); + // apply widgets after table has rendered & after a delay to prevent + // multiple applyWidget blocking code from blocking this trigger + setTimeout(function(){ + $t + .trigger('applyWidgets') + .trigger('pagerChange', p); + }, 0); } }]); @@ -401,12 +404,12 @@ counter = ++p.ajaxCounter; p.ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl - p.ajaxObject.success = function(data) { + p.ajaxObject.success = function(data, status, jqxhr) { // Refuse to process old ajax commands that were overwritten by new ones - see #443 if (counter < p.ajaxCounter){ return; } - renderAjax(data, table, p); + renderAjax(data, table, p, jqxhr); $doc.unbind('ajaxError.pager'); if (typeof p.oldAjaxSuccess === 'function') { p.oldAjaxSuccess(data); @@ -484,7 +487,7 @@ // if filtered, start from zero index = f ? 0 : s; count = f ? 0 : s; - added = 0; + added = 0; while (added < e && index < rows.length) { if (!f || !/filtered/.test(rows[index][0].className)){ count++; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index c58d110..29f2914 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.17.0 - Client-side table sorting with ease! +* TableSorter 2.17.1 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.17.0"; + ts.version = "2.17.1"; ts.parsers = []; ts.widgets = []; @@ -219,7 +219,7 @@ var c = table.config, // update table bodies in case we start with an empty table tb = c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'), - rows, list, l, i, h, ch, p, time, + rows, list, l, i, h, ch, np, p, time, j = 0, parsersDebug = "", len = tb.length; @@ -240,17 +240,21 @@ ch = ts.getColumnData( table, c.headers, i ); // get column parser p = ts.getParserById( ts.getData(h, ch, 'sorter') ); + np = ts.getData(h, ch, 'parser') === 'false'; // empty cells behaviour - keeping emptyToBottom for backwards compatibility c.empties[i] = ts.getData(h, ch, 'empty') || c.emptyTo || (c.emptyToBottom ? 'bottom' : 'top' ); // text strings behaviour in numerical sorts c.strings[i] = ts.getData(h, ch, 'string') || c.stringTo || 'max'; + if (np) { + p = ts.getParserById('no-parser'); + } if (!p) { p = detectParserForColumn(table, rows, -1, i); } if (c.debug) { parsersDebug += "column:" + i + "; parser:" + p.id + "; string:" + c.strings[i] + '; empty: ' + c.empties[i] + "\n"; } - list.push(p); + list[i] = p; } } j += (list.length) ? len : 1; @@ -327,7 +331,7 @@ t = getElementText(table, $row[0].cells[j], j); // allow parsing if the string is empty, previously parsing would change it to zero, // in case the parser needs to extract data from the table cell attributes - v = parsers[j].format(t, table, $row[0].cells[j], j); + v = parsers[j].id === 'no-parser' ? '' : parsers[j].format(t, table, $row[0].cells[j], j); cols.push(v); if ((parsers[j].type || '').toLowerCase() === "numeric") { // determine column max value (ignore sign) @@ -839,7 +843,8 @@ row = $tb.eq(tbdy).find('tr').index( $row ); icell = $cell.index(); c.cache[tbdy].normalized[row][c.columns].$row = $row; - v = c.cache[tbdy].normalized[row][icell] = c.parsers[icell].format( getElementText(table, cell, icell), table, cell, icell ); + v = c.cache[tbdy].normalized[row][icell] = c.parsers[icell].id === 'no-parser' ? '' : + c.parsers[icell].format( getElementText(table, cell, icell), table, cell, icell ); if ((c.parsers[icell].type || '').toLowerCase() === "numeric") { // update column max value (ignore sign) c.cache[tbdy].colMax[icell] = Math.max(Math.abs(v) || 0, c.cache[tbdy].colMax[icell] || 0); @@ -874,7 +879,8 @@ }; // add each cell for (j = 0; j < l; j++) { - cells[j] = c.parsers[j].format( getElementText(table, $row[i].cells[j], j), table, $row[i].cells[j], j ); + cells[j] = c.parsers[j].id === 'no-parser' ? '' : + c.parsers[j].format( getElementText(table, $row[i].cells[j], j), table, $row[i].cells[j], j ); if ((c.parsers[j].type || '').toLowerCase() === "numeric") { // update column max value (ignore sign) c.cache[tbdy].colMax[j] = Math.max(Math.abs(cells[j]) || 0, c.cache[tbdy].colMax[j] || 0); @@ -947,7 +953,7 @@ e.stopPropagation(); ts.destroy(table, c, cb); }) - .bind("resetToLoadState" + c.namespace, function(e){ + .bind("resetToLoadState" + c.namespace, function(){ // remove all widgets ts.refreshWidgets(table, true, true); // restore original settings; this clears out current settings, but does not clear @@ -1225,7 +1231,7 @@ // set timer on mousedown if (type === 'mousedown') { downTime = new Date().getTime(); - return e.target.tagName === "INPUT" ? '' : !c.cancelSelection; + return /(input|select|button|textarea)/i.test(e.target.tagName) ? '' : !c.cancelSelection; } if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); } // jQuery v1.2.6 doesn't have closest() @@ -1630,6 +1636,17 @@ }); // add default parsers + ts.addParser({ + id: 'no-parser', + is: function() { + return false; + }, + format: function() { + return ''; + }, + type: 'text' + }); + ts.addParser({ id: "text", is: function() { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 8e0a32d..e232643 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.16+ widgets - updated 5/22/2014 (v2.17.0) +/*! tableSorter 2.16+ widgets - updated 5/28/2014 (v2.17.1) * * Column Styles * Column Filters @@ -458,6 +458,21 @@ ts.filter = { } return null; }, + // Look for a not match + notMatch: function( filter, iFilter, exact, iExact, cached, index, table, wo ) { + if ( /^\!/.test(iFilter) ) { + iFilter = iFilter.replace('!', ''); + if (ts.filter.regex.exact.test(iFilter)) { + // look for exact not matches - see #628 + iFilter = iFilter.replace(ts.filter.regex.exact, ''); + return iFilter === '' ? true : $.trim(iFilter) !== iExact; + } else { + var indx = iExact.search( $.trim(iFilter) ); + return iFilter === '' ? true : !(wo.filter_startsWith ? indx === 0 : indx >= 0); + } + } + return null; + }, // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric exact: function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed, rowArray ) { /*jshint eqeqeq:false */ @@ -467,15 +482,6 @@ ts.filter = { } return null; }, - // Look for a not match - notMatch: function( filter, iFilter, exact, iExact, cached, index, table, wo ) { - if ( /^\!/.test(iFilter) ) { - iFilter = iFilter.replace('!', ''); - var indx = iExact.search( $.trim(iFilter) ); - return iFilter === '' ? true : !(wo.filter_startsWith ? indx === 0 : indx >= 0); - } - return null; - }, // Look for an AND or && operator (logical and) and : function( filter, iFilter, exact, iExact ) { if ( ts.filter.regex.andTest.test(filter) ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index 8e073c3..255835b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,5 +1,5 @@ /*! input & select parsers for jQuery 1.7+ & tablesorter 2.7.11+ - * Updated 4/27/2014 (v2.16.2) + * Updated 5/28/2014 (v2.17.1) * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ /*jshint browser: true, jquery:true, unused:false */ @@ -71,17 +71,20 @@ $(window).load(function(){ // this flag prevents the updateCell event from being spammed // it happens when you modify input text and hit enter - var alreadyUpdating = false; - $('table').find('tbody').on('change', 'select, input', function(e){ + var alreadyUpdating = false, + t = $.tablesorter.css.table || 'tablesorter'; + // bind to .tablesorter (default class name) + $('.' + t).find('tbody').on('change', 'select, input', function(e){ if (!alreadyUpdating) { var $tar = $(e.target), $cell = $tar.closest('td'), $table = $cell.closest('table'), indx = $cell[0].cellIndex, - c = $table[0].config, + c = $table[0].config || false, $hdr = c && c.$headers && c.$headers.eq(indx); - // don't use updateCell if column is set to "sorter-false" and "filter-false" - if ($hdr.length && $hdr.hasClass('sorter-false') && $hdr.hasClass('filter-false')){ + // abort if not a tablesorter table, or + // don't use updateCell if column is set to "sorter-false" and "filter-false", or column is set to "parser-false" + if ( !c || ( $hdr && $hdr.length && ( $hdr.hasClass('parser-false') || ($hdr.hasClass('sorter-false') && $hdr.hasClass('filter-false')) ) ) ){ return false; } alreadyUpdating = true; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index 42c3398..bf15152 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -1,4 +1,4 @@ -/*! tablesorter math widget - beta - updated 5/22/2014 (v2.17.0) +/*! tablesorter math widget - beta - updated 5/28/2014 (v2.17.1) * Requires tablesorter v2.16+ and jQuery 1.7+ * by Rob Garrison */ @@ -12,7 +12,8 @@ // get all of the row numerical values in an arry getRow : function(table, wo, $el, dataAttrib) { - var txt, + var $t, txt, + c = table.config, arry = [], $row = $el.closest('tr'), $cells = $row.children(); @@ -21,7 +22,11 @@ $cells = $cells.not('[' + dataAttrib + '=ignore]').not('[data-column=' + wo.math_ignore.join('],[data-column=') + ']'); } arry = $cells.not($el).map(function(){ - txt = this.textContent || $(this).text(); + $t = $(this); + txt = $t.attr(c.textAttribute); + if (typeof txt === "undefined") { + txt = this.textContent || $t.text(); + } txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table); return isNaN(txt) ? 0 : txt; }).get(); @@ -52,7 +57,10 @@ if (mathAbove) { i = 0; } else if ($t.length) { - txt = $t[0].textContent || $t.text(); + txt = $t.attr(c.textAttribute); + if (typeof txt === "undefined") { + txt = $t[0].textContent || $t.text(); + } txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table); arry.push(isNaN(txt) ? 0 : txt); } @@ -63,7 +71,10 @@ $rows.each(function(){ $t = $(this).children().filter('[data-column=' + cIndex + ']'); if (!$(this).hasClass(filtered) && $t.not('[' + dataAttrib + '^=above],[' + dataAttrib + '^=col]').length && !$t.is($el)) { - txt = ($t[0] ? $t[0].textContent : '') || $t.text(); + txt = $t.attr(c.textAttribute); + if (typeof txt === "undefined") { + txt = ($t[0] ? $t[0].textContent : '') || $t.text(); + } txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table); arry.push(isNaN(txt) ? 0 : txt); } @@ -85,7 +96,10 @@ $t = $(this); col = parseInt( $t.attr('data-column'), 10); if (!$t.filter('[' + dataAttrib + ']').length && $.inArray(col, wo.math_ignore) < 0) { - txt = ($t[0] ? $t[0].textContent : '') || $t.text(); + txt = $t.attr(c.textAttribute); + if (typeof txt === "undefined") { + txt = ($t[0] ? $t[0].textContent : '') || $t.text(); + } txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table); arry.push(isNaN(txt) ? 0 : txt); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 7fe8df8..485743e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/* Pager widget (beta) for TableSorter 5/22/2014 (v2.17.0) */ +/* Pager widget (beta) for TableSorter 5/28/2014 (v2.17.1) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ "use strict"; @@ -480,7 +480,7 @@ tsp = ts.pager = { var i, j, t, hsh, $f, $sh, th, d, l, rr_count, $t = c.$table, tds = '', - result = wo.pager_ajaxProcessing(data, table) || [ 0, [] ], + result = wo.pager_ajaxProcessing(data, table, xhr) || [ 0, [] ], hl = $t.find('thead th').length; // Clean up any previous error. @@ -570,9 +570,13 @@ tsp = ts.pager = { tsp.fixHeight(table, c); $t.trigger('updateCache', [function(){ if (p.initialized) { - // apply widgets after table has rendered - $t.trigger('applyWidgets'); - $t.trigger('pagerChange', p); + // apply widgets after table has rendered & after a delay to prevent + // multiple applyWidget blocking code from blocking this trigger + setTimeout(function(){ + $t + .trigger('applyWidgets') + .trigger('pagerChange', p); + }, 0); } }]); } @@ -597,12 +601,12 @@ tsp = ts.pager = { }); counter = ++p.ajaxCounter; wo.pager_ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl - wo.pager_ajaxObject.success = function(data) { + wo.pager_ajaxObject.success = function(data, status, jqxhr) { // Refuse to process old ajax commands that were overwritten by new ones - see #443 if (counter < p.ajaxCounter){ return; } - tsp.renderAjax(data, table, c); + tsp.renderAjax(data, table, c, jqxhr); $doc.unbind('ajaxError.pager'); if (typeof p.oldAjaxSuccess === 'function') { p.oldAjaxSuccess(data); @@ -682,7 +686,7 @@ tsp = ts.pager = { // if filtered, start from zero index = f ? 0 : s; count = f ? 0 : s; - added = 0; + added = 0; while (added < e && index < rows.length) { if (!f || !/filtered/.test(rows[index][0].className)){ count++; From e7972ea1bf71b0068c7157ee3f326d2521d492c0 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Thu, 19 Jun 2014 15:14:30 +0200 Subject: [PATCH 032/138] * updated tablesorter to latest version (2.17.2) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 15 ++-- .../jquery-tablesorter/jquery.tablesorter.js | 6 +- ...ry.tablesorter.widgets-filter-formatter.js | 9 +- .../jquery.tablesorter.widgets.js | 20 +++-- .../widgets/widget-pager.js | 17 ++-- .../widgets/widget-print.js | 11 +-- .../widgets/widget-scroller.js | 87 +++++++++++-------- .../jquery-tablesorter/theme.bootstrap.css | 6 +- .../jquery-tablesorter/theme.bootstrap_2.css | 8 +- 13 files changed, 113 insertions(+), 76 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f647e67..1fed828 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.12.2 (2014-06-19) + +* Upgrade tablesorter to v2.17.2 + #### v1.12.1 (2014-05-29) * Upgrade tablesorter to v2.17.1 diff --git a/README.md b/README.md index 96387e8..2786d08 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.17.1 (5/28/2014), [documentation] +Current tablesorter version: 2.17.2 (6/18/2014), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 64d8710..26c9446 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.12.1' + VERSION = '1.12.2' end diff --git a/tablesorter b/tablesorter index c09945c..575263d 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit c09945c60c2ff8d96163718031d39cedfb3f7754 +Subproject commit 575263d83c6b42f051022823dbe827e7667ab0f2 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 26fa83a..9749745 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 5/28/2014 (v2.17.1) + * updated 6/18/2014 (v2.17.2) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -221,7 +221,7 @@ var i, lastIndex = 0, c = table.config, - rows = c.$tbodies.eq(0).children(), + rows = c.$tbodies.eq(0).children('tr'), l = rows.length, s = ( p.page * p.size ), e = s + p.size, @@ -289,7 +289,7 @@ exception === 'timeout' ? 'Time out error' : exception === 'abort' ? 'Ajax Request aborted' : 'Uncaught error: ' + xhr.statusText + ' [' + xhr.status + ']' ); - c.$tbodies.eq(0).detach(); + c.$tbodies.eq(0).children().detach(); p.totalRows = 0; } else { // process ajax object @@ -311,7 +311,8 @@ if (d instanceof jQuery) { if (p.processAjaxOnInit) { // append jQuery object - c.$tbodies.eq(0).detach().append(d); + c.$tbodies.eq(0).children().detach(); + c.$tbodies.eq(0).append(d); } } else if (l) { // build table from array @@ -332,7 +333,7 @@ // only add new header text if the length matches if ( th && th.length === hl ) { hsh = $t.hasClass('hasStickyHeaders'); - $sh = hsh ? c.widgetOptions.$sticky.children('thead:first').children().children() : ''; + $sh = hsh ? c.widgetOptions.$sticky.children('thead:first').children('tr').children() : ''; $f = $t.find('tfoot tr:first').children(); // don't change td headers (may contain pager) c.$headers.filter('th').each(function(j){ @@ -655,7 +656,7 @@ p = c.pager; if ( !p.ajax ) { c.rowsCopy = rows; - p.totalRows = p.countChildRows ? c.$tbodies.eq(0).children().length : rows.length; + p.totalRows = p.countChildRows ? c.$tbodies.eq(0).children('tr').length : rows.length; p.size = $.data(table, 'pagerLastSize') || p.size || 10; p.totalPages = Math.ceil( p.totalRows / p.size ); renderTable(table, rows, p); @@ -730,7 +731,7 @@ .bind('update updateRows updateAll addRows '.split(' ').join('.pager '), function(e){ e.stopPropagation(); fixHeight(table, p); - var $rows = c.$tbodies.eq(0).children(); + var $rows = c.$tbodies.eq(0).children('tr'); p.totalRows = $rows.length - ( p.countChildRows ? 0 : $rows.filter('.' + c.cssChildRow).length ); p.totalPages = Math.ceil( p.totalRows / p.size ); updatePageDisplay(table, p); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 29f2914..6ec0dc8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.17.1 - Client-side table sorting with ease! +* TableSorter 2.17.2 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.17.1"; + ts.version = "2.17.2"; ts.parsers = []; ts.widgets = []; @@ -1205,7 +1205,7 @@ }; ts.clearTableBody = function(table) { - $(table)[0].config.$tbodies.detach(); + $(table)[0].config.$tbodies.children().detach(); }; ts.bindEvents = function(table, $headers, core){ diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js index a3088ff..83b85eb 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js @@ -1,4 +1,4 @@ -/*! Filter widget formatter functions - updated 4/27/2014 (v2.16.2) +/*! Filter widget formatter functions - updated 6/18/2014 (v2.17.2) * requires: tableSorter 2.15+ and jQuery 1.4.3+ * * uiSpinner (jQuery UI spinner) @@ -574,7 +574,7 @@ tsff = ts.filterFormatter = { } else { num = (tsff.updateCompare($cell, $input, o)[1]).toString() || ''; // differeniate 1388556000000 from 1/1/2014 using \d{5} regex - num = num !== '' ? /\d{5}/g.test(num) ? Number(num) : num || '' : ''; + num = num !== '' ? /\d{5}/g.test(num) ? new Date(Number(num)) : num || '' : ''; } $cell.add($shcell).find('.date').datepicker( 'setDate', num || null ); setTimeout(function(){ @@ -718,6 +718,11 @@ tsff = ts.filterFormatter = { // less than date (from date empty) to = val.replace(/<=/, '') || ''; } + + // differeniate 1388556000000 from 1/1/2014 using \d{5} regex + from = from !== '' ? /\d{5}/g.test(from) ? new Date(Number(from)) : from || '' : ''; + to = to !== '' ? /\d{5}/g.test(to) ? new Date(Number(to)) : to || '' : ''; + $cell.add($shcell).find('.dateFrom').datepicker('setDate', from || null); $cell.add($shcell).find('.dateTo').datepicker('setDate', to || null); // give datepicker time to process diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index e232643..d9c366a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.16+ widgets - updated 5/28/2014 (v2.17.1) +/*! tableSorter 2.16+ widgets - updated 6/18/2014 (v2.17.2) * * Column Styles * Column Filters @@ -681,13 +681,16 @@ ts.filter = { filters = ts.filter.setDefaults(table, c, wo) || []; if (filters.length) { ts.setFilters(table, filters, true); + // ts.filter.checkFilters(table, filters); } c.$table.trigger('filterFomatterUpdate'); - ts.filter.checkFilters(table, filters); + if (!wo.filter_initialized) { + // filter widget initialized + wo.filter_initialized = true; + c.$table.trigger('filterInit'); + } }); - // filter widget initialized - wo.filter_initialized = true; - c.$table.trigger('filterInit'); + }, setDefaults: function(table, c, wo) { var isArray, saved, indx, @@ -820,6 +823,7 @@ ts.filter = { // add filter array back into inputs if (filterArray) { ts.setFilters( table, filters, false, skipFirst !== true ); + if (!wo.filter_initialized) { c.lastCombinedFilter = ''; } } if (wo.filter_hideFilters) { // show/hide filter row as needed @@ -834,7 +838,7 @@ ts.filter = { c.lastCombinedFilter = null; c.lastSearch = []; } - c.$table.trigger('filterStart', [filters]); + if (wo.filter_initialized) { c.$table.trigger('filterStart', [filters]); } if (c.showProcessing) { // give it time for the processing icon to kick in setTimeout(function() { @@ -1074,7 +1078,7 @@ ts.filter = { if (c.debug) { ts.benchmark("Completed filter widget search", time); } - c.$table.trigger('filterEnd'); + if (wo.filter_initialized) { c.$table.trigger('filterEnd'); } setTimeout(function(){ c.$table.trigger('applyWidgets'); // make sure zebra widget is applied }, 0); @@ -1116,7 +1120,7 @@ ts.filter = { $.each(arry, function(i, v){ // parse array data using set column parser; this DOES NOT pass the original // table cell to the parser format function - parsed.push({ t : v, p : c.parsers && c.parsers[column].format( v, table, [], column ) || v }); + parsed.push({ t : v, p : c.parsers && c.parsers[column].format( v, table, [], column ) }); }); // sort parsed select options diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 485743e..0ef51f3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/* Pager widget (beta) for TableSorter 5/28/2014 (v2.17.1) */ +/* Pager widget (beta) for TableSorter 6/18/2014 (v2.17.2) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ "use strict"; @@ -153,7 +153,7 @@ tsp = ts.pager = { p.$goto = p.$container.find(s.goto); // page size selector p.$size = p.$container.find(s.pageSize); - p.totalRows = c.$tbodies.eq(0).children().length; + p.totalRows = c.$tbodies.eq(0).children('tr').length; p.oldAjaxSuccess = p.oldAjaxSuccess || wo.pager_ajaxObject.success; c.appender = tsp.appender; if (ts.filter && $.inArray('filter', c.widgets) >= 0) { @@ -247,7 +247,7 @@ tsp = ts.pager = { .on('update updateRows updateAll addRows '.split(' ').join('.pager '), function(e){ e.stopPropagation(); tsp.fixHeight(table, c); - var $rows = c.$tbodies.eq(0).children(); + var $rows = c.$tbodies.eq(0).children('tr'); p.totalRows = $rows.length - ( c.widgetOptions.pager_countChildRows ? 0 : $rows.filter('.' + c.cssChildRow).length ); p.totalPages = Math.ceil( p.totalRows / p.size ); tsp.updatePageDisplay(table, c); @@ -428,7 +428,7 @@ tsp = ts.pager = { lastIndex = 0, p = c.pager, wo = c.widgetOptions, - rows = c.$tbodies.eq(0).children(), + rows = c.$tbodies.eq(0).children('tr'), l = rows.length, s = ( p.page * p.size ), e = s + p.size, @@ -491,7 +491,7 @@ tsp = ts.pager = { ts.log('Ajax Error', xhr, exception); } ts.showError(table, exception.message + ' (' + xhr.status + ')'); - c.$tbodies.eq(0).detach(); + c.$tbodies.eq(0).children().detach(); p.totalRows = 0; } else { // process ajax object @@ -513,7 +513,8 @@ tsp = ts.pager = { if (d instanceof jQuery) { if (wo.pager_processAjaxOnInit) { // append jQuery object - c.$tbodies.eq(0).detach().append(d); + c.$tbodies.eq(0).children().detach(); + c.$tbodies.eq(0).append(d); } } else if (l) { // build table from array @@ -534,7 +535,7 @@ tsp = ts.pager = { // only add new header text if the length matches if ( th && th.length === hl ) { hsh = $t.hasClass('hasStickyHeaders'); - $sh = hsh ? wo.$sticky.children('thead:first').children().children() : ''; + $sh = hsh ? wo.$sticky.children('thead:first').children('tr').children() : ''; $f = $t.find('tfoot tr:first').children(); // don't change td headers (may contain pager) c.$headers.filter('th').each(function(j){ @@ -862,7 +863,7 @@ tsp = ts.pager = { p = c.pager; if ( !p.ajax ) { c.rowsCopy = rows; - p.totalRows = c.widgetOptions.pager_countChildRows ? c.$tbodies.eq(0).children().length : rows.length; + p.totalRows = c.widgetOptions.pager_countChildRows ? c.$tbodies.eq(0).children('tr').length : rows.length; p.size = $.data(table, 'pagerLastSize') || p.size || wo.pager_size || 10; p.totalPages = Math.ceil( p.totalRows / p.size ); tsp.moveToPage(table, p, true); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js index 54bda3d..5b3d883 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js @@ -1,5 +1,5 @@ -/* Print widget (beta) for TableSorter 5/22/2014 (v2.17.0) - * Requires tablesorter v2.8+ and jQuery 1.7+ +/* Print widget (beta) for TableSorter 6/18/2014 (v2.17.2) + * Requires tablesorter v2.8+ and jQuery 1.2.6+ */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ @@ -15,8 +15,8 @@ printTable = ts.printTable = { init : function(c) { c.$table - .off(printTable.event) - .on(printTable.event, function(){ + .unbind(printTable.event) + .bind(printTable.event, function(){ // explicitly use table.config.widgetOptions because we want // the most up-to-date values; not the "wo" from initialization printTable.process(c, c.widgetOptions); @@ -82,8 +82,9 @@ printTable = ts.printTable = { '<style>' + style + '</style>' + '</head><body>' + data + '</body></html>' ); - generator.print(); generator.document.close(); + generator.print(); + generator.close(); return true; }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index eee5de5..8170ebc 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -10,7 +10,7 @@ Resizable scroller widget for the jQuery tablesorter plugin - Version 2.0 - modified by Rob Garrison 4/12/2013; updated 5/22/2014 (v2.17.0) + Version 2.0 - modified by Rob Garrison 4/12/2013; updated 6/18/2014 (v2.17.2) Requires jQuery v1.7+ Requires the tablesorter plugin, v2.8+, available at http://mottie.github.com/tablesorter/docs/ @@ -22,7 +22,7 @@ widgets: ['zebra', 'scroller'], widgetOptions : { scroller_height : 300, // height of scroll window - scroller_barWidth : 17, // scroll bar width + scroller_barWidth : 18, // scroll bar width scroller_jumpToHeader : true, // header snap to browser top when scrolling the tbody scroller_idPrefix : 's_' // cloned thead id prefix (random number added to end) } @@ -53,8 +53,12 @@ ts.window_resize = function(){ // Add extra scroller css $(function(){ var s = '<style>' + + '.tablesorter-scroller-reset { width: auto !important; } ' + + '.tablesorter-scroller { text-align: left; overflow: hidden; }' + + '.tablesorter-scroller-header { overflow: hidden; }' + '.tablesorter-scroller-header table.tablesorter { margin-bottom: 0; }' + - '.tablesorter-scroller-table table.tablesorter { margin-top: 0; } ' + + '.tablesorter-scroller-table { overflow-y: scroll; }' + + '.tablesorter-scroller-table table.tablesorter { margin-top: 0; overflow: scroll; } ' + '.tablesorter-scroller-table .tablesorter-filter-row, .tablesorter-scroller-table tfoot { display: none; }' + '.tablesorter-scroller-table table.tablesorter thead tr.tablesorter-headerRow * {' + 'line-height:0;height:0;border:none;background-image:none;padding-top:0;padding-bottom:0;margin-top:0;margin-bottom:0;overflow:hidden;' + @@ -67,21 +71,21 @@ ts.addWidget({ priority: 60, // run after the filter widget options: { scroller_height : 300, - scroller_barWidth : 17, + scroller_barWidth : 18, scroller_jumpToHeader: true, scroller_idPrefix : 's_' }, init: function(table, thisWidget, c, wo){ var $win = $(window); - //Setup window.resizeEnd event + // Setup window.resizeEnd event $win .bind('resize', ts.window_resize) .bind('resizeEnd', function() { // init is run before format, so scroller_resizeWidth // won't be defined within the "c" or "wo" parameters if (typeof table.config.widgetOptions.scroller_resizeWidth === 'function') { - //IE calls resize when you modify content, so we have to unbind the resize event - //so we don't end up with an infinite loop. we can rebind after we're done. + // IE calls resize when you modify content, so we have to unbind the resize event + // so we don't end up with an infinite loop. we can rebind after we're done. $win.unbind('resize', ts.window_resize); table.config.widgetOptions.scroller_resizeWidth(); $win.bind('resize', ts.window_resize); @@ -97,11 +101,11 @@ ts.addWidget({ h = wo.scroller_height || 300; t = $tbl.find('tbody').height(); if (t !== 0 && h > t) { h = t + 10; } // Table is less than h px - id = wo.scroller_id = wo.scroller_idPrefix + Math.floor(Math.random() * 101); + id = wo.scroller_id = wo.scroller_idPrefix + Math.floor(Math.random() * 1001); $hdr = $('<table class="' + $tbl.attr('class') + '" cellpadding=0 cellspacing=0><thead>' + $tbl.find('thead:first').html() + '</thead></table>'); $tbl - .wrap('<div id="' + id + '" class="tablesorter-scroller" style="text-align:left;" />') + .wrap('<div id="' + id + '" class="tablesorter-scroller" />') .before($hdr) .find('.tablesorter-filter-row').addClass('hideme'); @@ -109,7 +113,7 @@ ts.addWidget({ .wrap('<div class="tablesorter-scroller-header" style="width:' + $tbl.width() + ';" />') .find('.' + ts.css.header); - $tbl.wrap('<div class="tablesorter-scroller-table" style="height:' + h + 'px;width:' + $tbl.width() + ';overflow-y:scroll;" />'); + $tbl.wrap('<div class="tablesorter-scroller-table" style="height:' + h + 'px;width:' + $tbl.width() + ';" />'); // make scroller header sortable ts.bindEvents(table, $cells); @@ -120,46 +124,57 @@ ts.addWidget({ } resize = function(){ - var d, - //Hide other scrollers so we can resize + var d, b, $h, $th, w, + // Hide other scrollers so we can resize $div = $('div.scroller[id != "' + id + '"]').hide(); $tbl.find('thead').show(); - //Reset sizes so parent can resize. - $hdr - .width(0) - .parent().width(0) - .find('th,td').width(0); - + // Reset sizes so parent can resize. $tbl - .width(0) - .find('thead').find('th,td').width(0); + .addClass('tablesorter-scroller-reset') + .find('thead').find('.tablesorter-header-inner').addClass('tablesorter-scroller-reset'); d = $tbl.parent(); - d.width(0); + d.addClass('tablesorter-scroller-reset'); d.parent().trigger('resize'); + // Shrink a bit to accommodate scrollbar d.width( d.parent().innerWidth() - ( d.parent().hasScrollBar() ? wo.scroller_barWidth : 0 ) ); - - $tbl.width( d.innerWidth() - ( d.hasScrollBar() ? wo.scroller_barWidth : 0 ) ); - $tbl.find('thead').find('th,td').filter(':visible').each(function(i, c){ - var $th = $(c), - //Wrap in browser detect?? - w = parseInt( $th.css('min-width').replace('auto', '0').replace(/(px|em)/, ''), 10 ); - if ( $th.width() < w ) { - $th.width(w); - } else { - w = $th.width(); + w = d.innerWidth() - ( d.hasScrollBar() ? wo.scroller_barWidth : 0 ); + $tbl.width( w ); + $hdr.width( w ); + $hdr.parent().width( w ); + + $tbl.closest('.tablesorter-scroller').find('.tablesorter-scroller-reset').removeClass('tablesorter-scroller-reset'); + + // include left & right border widths + b = parseInt( $tbl.css('border-left-width'), 10 ) + parseInt( $tbl.css('border-right-width'), 10 ); + $h = $hdr.find('thead').children().children(); + + // adjust cloned header to match original table width - includes wrappers, headers, and header inner div + $tbl.find('thead').children().children().each(function(i, c){ + $th = $(c).find('.tablesorter-header-inner'); + if ($th.length) { + // I have no idea why this is in here anymore LOL + w = parseInt( $th.css('min-width').replace('auto', '0').replace(/(px|em)/, ''), 10 ); + if ( $th.width() < w ) { + $th.width(w); + } else { + w = $th.width(); + } + $h.eq(i) + .find('.tablesorter-header-inner').width(w - b) + // set inner width first + .parent() + .width( $th.parent().width() - b ); } - $hdr.find('th,td').eq(i).width(w); }); - $hdr.width($tbl.innerWidth()); $div.show(); }; - //Expose to external calls + // Expose to external calls wo.scroller_resizeWidth = resize; resize(); @@ -168,6 +183,7 @@ ts.addWidget({ c.isScrolling = true; t = $tbl.parent().parent().height(); + // The header will always jump into view if scrolling the table body $tbl.parent().bind('scroll', function(){ if (wo.scroller_jumpToHeader) { @@ -176,11 +192,12 @@ ts.addWidget({ $win.scrollTop( $hdr.offset().top ); } } + $hdr.parent().scrollLeft( $(this).scrollLeft() ); }); } - //Sorting, so scroll to top + // Sorting, so scroll to top $tbl.parent().animate({ scrollTop: 0 }, 'fast'); }, diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index 8de3d71..a8e717c 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -43,11 +43,10 @@ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAMAAADOvxanAAAAVFBMVEUAAABCQkJZWVkZGRnJyckgICAZGRkZGRn8/PweHh4dHR0aGhoaGhpUVFQbGxvQ0NDc3NxMTExSUlIbGxvr6+s4ODhKSkogICAtLS00NDQzMzMnJydSEPrQAAAAGHRSTlMA1ssZRLgdAQbDyisqsZo8QdXUq0r9xPepSRwiAAAAX0lEQVQI13XHSQKAIAwEwQAKxn13Ev7/T2Pu9qmarJKPXIicI4PH4hxaKNrhm2S8bJK5h4YzKHrzJNtK6yYT/TdXzpS5zuYg4MSQYF6i4IHExdw1UVRi05HPrrvT53a+qyMFC9t04gcAAAAASUVORK5CYII=); } -/* white unsorted icon -.tablesorter-bootstrap .bootstrap-icon-unsorted { +/* white unsorted icon */ +.tablesorter-bootstrap .icon-white.bootstrap-icon-unsorted { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOBAMAAAALT/umAAAAKlBMVEUAAAD///////////////////////////////////////////////////+Gu8ovAAAADXRSTlMA4EXKBtQqvR0+sxmalc142gAAAFdJREFUCNdjYGDoamAAAjZbMxCVfvd6AgMDd+3du9UMDKx3hWSvMjBwXZww8RYDGuC53NB8h4GB8a617UUGBs7Yu3cjGRhYVO9eVQFKOskKOQApFmUgBwBZ+xXRTttNdAAAAABJRU5ErkJggg==); } - */ /* since bootstrap (table-striped) uses nth-child(), we just use this to add a zebra stripe color */ .tablesorter-bootstrap > tbody > tr.odd > td, @@ -80,7 +79,6 @@ /* filter widget */ .tablesorter-bootstrap .tablesorter-filter-row .tablesorter-filter { width: 98%; - height: auto; margin: 0 auto; padding: 4px 6px; color: #333; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css index 1ecd8d1..bff0ee4 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css @@ -48,10 +48,17 @@ line-height: 14px; display: inline-block; } + +/* black unsorted icon */ .tablesorter-bootstrap .bootstrap-icon-unsorted { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAYAAAD5YeaVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAWVJREFUeNqUUL9Lw2AUTGP8mqGlpBQkNeCSRcckEBcHq1jImMElToKuDvpHFMGhU0BQcHBwLji6CE1B4uB/INQsDi4d2jQ/fPeZxo764OV6915f7lLJ81xot9tCURXqdVEUr7IsO6ffH9Q5BlEUCaLwWxWqTcbYnaIoh0Dw4gAvcWlxq1qt9hqNxg6hUGAP+uIPUrGs0qXLer2+v/pTX6QpxLtkc2U2m53ACb8sSdIDXerSEms2m6+DweAICA4d89KGbduf9MpEVdXQ9/2LVqv1CASHjjn3iq/x1xKFfxQPqGnada1W86bT6SiO42OS3qk3KPStLMvbk8nkfjwen/LLuq6blFymMB0KdUPSGhAcOualjX6/f0bCiC7NaWGPQr0BwaFjzn0gYJqmLAiCA8/zni3LmhuGkQPBoWPOPwQeaPIqD4fDruu6L6Zp5kBw6IudchmdJAkLw3DXcZwnIPjy/FuAAQCiqqWWCAFKcwAAAABJRU5ErkJggg==); } +/* white unsorted icon */ +.tablesorter-bootstrap .icon-white.bootstrap-icon-unsorted { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOBAMAAAALT/umAAAAKlBMVEUAAAD///////////////////////////////////////////////////+Gu8ovAAAADXRSTlMA4EXKBtQqvR0+sxmalc142gAAAFdJREFUCNdjYGDoamAAAjZbMxCVfvd6AgMDd+3du9UMDKx3hWSvMjBwXZww8RYDGuC53NB8h4GB8a617UUGBs7Yu3cjGRhYVO9eVQFKOskKOQApFmUgBwBZ+xXRTttNdAAAAABJRU5ErkJggg==); +} + /* since bootstrap (table-striped) uses nth-child(), we just use this to add a zebra stripe color */ .tablesorter-bootstrap tr.odd td { background-color: #f9f9f9; @@ -79,7 +86,6 @@ caption { /* filter widget */ .tablesorter-bootstrap .tablesorter-filter-row .tablesorter-filter { width: 98%; - height: auto; margin: 0 auto; padding: 4px 6px; background-color: #fff; From 7f8b82673cac6793ca54a27135a9c300ba677f0c Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Mon, 30 Jun 2014 19:38:29 +0200 Subject: [PATCH 033/138] * updated tablesorter to latest version (2.17.3) --- CHANGELOG.md | 4 + README.md | 4 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 26 ++-- .../jquery-tablesorter/jquery.tablesorter.js | 8 +- .../jquery.tablesorter.widgets.js | 61 +++++---- .../parsers/parser-roman.js | 117 ++++++++++++++++++ .../widgets/widget-pager.js | 30 ++--- .../widgets/widget-scroller.js | 7 +- .../widgets/widget-staticRow.js | 9 +- 11 files changed, 203 insertions(+), 67 deletions(-) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/parsers/parser-roman.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fed828..8bf2210 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.12.3 (2014-06-30) + +* Upgrade tablesorter to v2.17.3 + #### v1.12.2 (2014-06-19) * Upgrade tablesorter to v2.17.2 diff --git a/README.md b/README.md index 2786d08..32a417f 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.17.2 (6/18/2014), [documentation] +Current tablesorter version: 2.17.3 (6/28/2014), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. @@ -26,7 +26,7 @@ Or install it yourself as: Rails 3.1 and higher (tested up to 4.1) -Tested with ruby 1.9.3 - 2.1.1 +Tested with ruby 1.9.3 - 2.1.2 ## Usage diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 26c9446..c164710 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.12.2' + VERSION = '1.12.3' end diff --git a/tablesorter b/tablesorter index 575263d..197af4f 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 575263d83c6b42f051022823dbe827e7667ab0f2 +Subproject commit 197af4fc31b370d67bca33dcab6856db957932e9 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 9749745..2af0a60 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 6/18/2014 (v2.17.2) + * updated 6/28/2014 (v2.17.3) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -121,7 +121,7 @@ dis = !!disable, first = ( dis || p.page === 0 ), tp = Math.min( p.totalPages, p.filteredPages ), - last = ( dis || (p.page === tp - 1) || p.totalPages === 0 ); + last = ( dis || (p.page === tp - 1) || tp === 0 ); if ( p.updateArrows ) { p.$container.find(p.cssFirst + ',' + p.cssPrev)[ first ? a : r ](d).attr('aria-disabled', first); p.$container.find(p.cssNext + ',' + p.cssLast)[ last ? a : r ](d).attr('aria-disabled', last); @@ -131,21 +131,22 @@ updatePageDisplay = function(table, p, completed) { var i, pg, s, out, regex, c = table.config, - f = c.$table.hasClass('hasFilters') && !p.ajaxUrl, + f = c.$table.hasClass('hasFilters'), t = [], sz = p.size || 10; // don't allow dividing by zero - t = [ (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered'), c.selectorRemove ]; + t = [ (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered'), c.selectorRemove.replace(/^(\w+\.)/g,'') ]; if (p.countChildRows) { t.push(c.cssChildRow); } regex = new RegExp( '(' + t.join('|') + ')' ); p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method - p.filteredRows = (f) ? 0 : p.totalRows; - p.filteredPages = p.totalPages; - if (f) { + if (f && !p.ajaxUrl) { + p.filteredRows = 0; $.each(c.cache[0].normalized, function(i, el) { p.filteredRows += p.regexRows.test(el[c.columns].$row[0].className) ? 0 : 1; }); - p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; + } else if (!f) { + p.filteredRows = p.totalRows; } + p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) { t = (p.size * p.page > p.filteredRows); p.startRow = (t) ? 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1); @@ -202,7 +203,7 @@ if (h) { d = h - $b.height(); if ( d > 5 && $.data(table, 'pagerLastSize') === p.size && $b.children('tr:visible').length < p.size ) { - $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.replace(/(tr)?\./g,'') + '" style="height:' + d + 'px;"></tr>'); + $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.replace(/^(\w+\.)/g,'') + '" style="height:' + d + 'px;"></tr>'); } } } @@ -235,7 +236,7 @@ } else { rows[i].style.display = ( j >= s && j < e ) ? '' : 'none'; // don't count child rows - j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) && !p.countChildRows ? 0 : 1; + j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.replace(/^(\w+\.)/g,'')) && !p.countChildRows ? 0 : 1; if ( j === e && rows[i].style.display !== 'none' && rows[i].className.match(ts.css.cssHasChild) ) { lastIndex = i; } @@ -296,6 +297,7 @@ if (!$.isArray(result)) { p.ajaxData = result; p.totalRows = result.total; + p.filteredRows = typeof result.filteredRows !== 'undefined' ? result.filteredRows : result.total; th = result.headers; d = result.rows; } else { @@ -696,7 +698,7 @@ } // skipped rows - p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.substring(1) + '|' + c.cssChildRow + ')'); + p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.replace(/^(\w+\.)/g,'') + '|' + c.cssChildRow + ')'); $t .unbind('filterStart filterEnd sortEnd disable enable destroy update updateRows updateAll addRows pageSize '.split(' ').join('.pager ')) @@ -845,7 +847,7 @@ }) // add error row to thead instead of tbody, or clicking on the header will result in a parser error .appendTo( c.$table.find('thead:first') ) - .addClass( errorRow + ' ' + c.selectorRemove.replace(/^[.#]/, '') ) + .addClass( errorRow + ' ' + c.selectorRemove.replace(/^(\w+\.)/g,'') ) .attr({ role : 'alert', 'aria-live' : 'assertive' diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 6ec0dc8..18e1f91 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.17.2 - Client-side table sorting with ease! +* TableSorter 2.17.3 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.17.2"; + ts.version = "2.17.3"; ts.parsers = []; ts.widgets = []; @@ -1181,9 +1181,9 @@ return this.sortDisabled ? false : ts.isValueInArray( parseFloat($(this).attr('data-column')), c.sortList) >= 0; }); } - $h.addClass(ts.css.processing + ' ' + c.cssProcessing); + table.add($h).addClass(ts.css.processing + ' ' + c.cssProcessing); } else { - $h.removeClass(ts.css.processing + ' ' + c.cssProcessing); + table.add($h).removeClass(ts.css.processing + ' ' + c.cssProcessing); } }; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index d9c366a..eb484eb 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.16+ widgets - updated 6/18/2014 (v2.17.2) +/*! tableSorter 2.16+ widgets - updated 6/28/2014 (v2.17.3) * * Column Styles * Column Filters @@ -568,6 +568,9 @@ ts.filter = { } c.$table.addClass('hasFilters'); + // define searchTimer so using clearTimeout won't cause an undefined error + wo.searchTimer = null; + $.extend( regex, { child : new RegExp(c.cssChildRow), filtered : new RegExp(wo.filter_filteredRow), @@ -592,6 +595,7 @@ ts.filter = { ts.filter.buildDefault(table, true); } if (event.type === 'filterReset') { + c.$table.find('.' + ts.css.filter).add(wo.filter_$externalFilters).val(''); ts.filter.searching(table, []); } else if (event.type === 'filterEnd') { ts.filter.buildDefault(table, true); @@ -788,29 +792,45 @@ ts.filter = { .attr('data-lastSearchTime', new Date().getTime()) .unbind('keypress keyup search change '.split(' ').join(c.namespace + 'filter ')) // include change for select - fixes #473 - .bind('keyup search change '.split(' ').join(c.namespace + 'filter '), function(event) { + .bind('keyup' + c.namespace + 'filter', function(event) { $(this).attr('data-lastSearchTime', new Date().getTime()); // emulate what webkit does.... escape clears the filter if (event.which === 27) { this.value = ''; - // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace - } else if ( (typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch && this.value !== '') || - ( event.type === 'keyup' && ( (event.which < 32 && event.which !== 8 && wo.filter_liveSearch === true && event.which !== 13) || - ( event.which >= 37 && event.which <= 40 ) || (event.which !== 13 && wo.filter_liveSearch === false) ) ) ) { - return; + // live search + } else if ( wo.filter_liveSearch === false ) { + return; + // don't return if the search value is empty (all rows need to be revealed) + } else if ( this.value !== '' && ( + // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace + ( typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch ) || + // let return & backspace continue on, but ignore arrows & non-valid characters + ( event.which !== 13 && event.which !== 8 && ( event.which < 32 || (event.which >= 37 && event.which <= 40) ) ) ) ) { + return; } // change event = no delay; last true flag tells getFilters to skip newest timed input - ts.filter.searching( table, event.type !== 'change', true ); + ts.filter.searching( table, true, true ); }) - .bind('keypress.' + c.namespace + 'filter', function(event){ - if (event.which === 13) { + .bind('search change keypress '.split(' ').join(c.namespace + 'filter '), function(event){ + if (event.which === 13 || event.type === 'search' || event.type === 'change') { event.preventDefault(); - $(this).blur(); + // init search with no delay + ts.filter.searching( table, false, true ); } }); - c.$table.bind('filterReset', function(){ - $el.val(''); - }); + }, + searching: function(table, filter, skipFirst) { + var wo = table.config.widgetOptions; + clearTimeout(wo.searchTimer); + if (typeof filter === 'undefined' || filter === true) { + // delay filtering + wo.searchTimer = setTimeout(function() { + ts.filter.checkFilters(table, filter, skipFirst ); + }, wo.filter_liveSearch ? wo.filter_searchDelay : 10); + } else { + // skip delay + ts.filter.checkFilters(table, filter, skipFirst); + } }, checkFilters: function(table, filter, skipFirst) { var c = table.config, @@ -1221,19 +1241,6 @@ ts.filter = { ts.filter.buildSelect(table, columnIndex, updating, $header.hasClass(wo.filter_onlyAvail)); } } - }, - searching: function(table, filter, skipFirst) { - if (typeof filter === 'undefined' || filter === true) { - var wo = table.config.widgetOptions; - // delay filtering - clearTimeout(wo.searchTimer); - wo.searchTimer = setTimeout(function() { - ts.filter.checkFilters(table, filter, skipFirst ); - }, wo.filter_liveSearch ? wo.filter_searchDelay : 10); - } else { - // skip delay - ts.filter.checkFilters(table, filter, skipFirst); - } } }; diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-roman.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-roman.js new file mode 100644 index 0000000..ca03c75 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-roman.js @@ -0,0 +1,117 @@ +/*! Roman numeral parsers + * code modified from both: + * Steven Levithan @ http://blog.stevenlevithan.com/archives/javascript-roman-numeral-converter + * Jonathan Snook comment @ http://blog.stevenlevithan.com/archives/javascript-roman-numeral-converter#comment-16140 + */ +/*jshint jquery:true, unused:false */ +;(function($){ +"use strict"; + + // allow lower case roman numerals, since lists use i, ii, iii, etc. + var validator = /^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/i, + matcher = /\b([MCDLXVI]+\b)/gi, + lookup = { I:1, V:5, X:10, L:50, C:100, D:500, M:1000 }; + + $.tablesorter.addParser({ + id: 'roman', + is: function(){ + return false; + }, + format: function(s) { + var val, + roman = s.toUpperCase().split(''), + num = 0; + + // roman numerals not found! + if ( !(s && validator.test(s)) ) { + return s; + } + + while (roman.length) { + val = lookup[roman.shift()]; + num += val * (val < lookup[roman[0]] ? -1 : 1); + } + + return num; + }, + type: "numeric" + }); + + $.tablesorter.addParser({ + id: 'roman-ignore', + is: function(){ + return false; + }, + format: function(s, table, cell, column) { + var val, orig, + c = table.config, + ignore = $.isArray(c.roman_ignore) ? c.roman_ignore[column] : 0, + // find roman numerals + roman = ( isNaN(ignore) ? + // ignore can be a regex or string + $.trim( s.replace(ignore, '') ) : + // or a number to ignore the last x letters... + $.trim( s.substring(0, s.length - ignore) ) + ).match(matcher), + v = validator.test(roman), + num = 0; + + // roman numerals not found! + if ( !(v) ) { + return s; + } + + // save roman numeral for replacement + orig = roman[0]; + roman = orig.toUpperCase().split(''); + + while (roman.length) { + val = lookup[roman.shift()]; + // ignore non-roman numerals + if (val) { + num += val * (val < lookup[roman[0]] ? -1 : 1); + } + } + + return num ? s.replace(orig, num) : s; + }, + type: "text" + }); + + $.tablesorter.addParser({ + id: 'roman-extract', + is: function(){ + return false; + }, + format: function(s) { + var val, + // find roman numerals + roman = $.grep(s.split(/\b/), function(v, i){ + return validator.test(v) ? v : ''; + }).join('').match(matcher), + + v = roman ? validator.test(roman) : 0, + num = 0; + + // roman numerals not found! + if ( !(v) ) { + return s; + } + + // save roman numeral for replacement + roman = roman[0].toUpperCase().split(''); + + while (roman.length) { + val = lookup[roman.shift()]; + // ignore non-roman numerals + if (val) { + num += val * (val < lookup[roman[0]] ? -1 : 1); + } + } + + return num ? num : s; + }, + type: "numeric" + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 0ef51f3..d8fe8d0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/* Pager widget (beta) for TableSorter 6/18/2014 (v2.17.2) */ +/* Pager widget (beta) for TableSorter 6/28/2014 (v2.17.3) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ "use strict"; @@ -93,7 +93,7 @@ ts.addWidget({ prev : '.prev', // previous page arrow next : '.next', // next page arrow last : '.last', // go to last page arrow - goto : '.gotoPage', // go to page selector - select dropdown that sets the current page + gotoPage : '.gotoPage', // go to page selector - select dropdown that sets the current page pageDisplay : '.pagedisplay', // location of where the "output" is displayed pageSize : '.pagesize' // page size selector - select dropdown that sets the "size" option } @@ -150,7 +150,7 @@ tsp = ts.pager = { // added in case the pager is reinitialized after being destroyed. p.$container = $(s.container).addClass(wo.pager_css.container).show(); // goto selector - p.$goto = p.$container.find(s.goto); + p.$goto = p.$container.find(s.gotoPage); // goto is a reserved word #657 // page size selector p.$size = p.$container.find(s.pageSize); p.totalRows = c.$tbodies.eq(0).children('tr').length; @@ -170,7 +170,7 @@ tsp = ts.pager = { } // skipped rows - p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.substring(1) + '|' + c.cssChildRow + ')'); + p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.replace(/^(\w+\.)/g,'') + '|' + c.cssChildRow + ')'); // clear initialized flag p.initialized = false; @@ -322,7 +322,7 @@ tsp = ts.pager = { dis = !!disable, first = dis || p.page === 0, tp = Math.min( p.totalPages, p.filteredPages ), - last = dis || p.page === tp - 1 || p.totalPages === 0, + last = dis || p.page === tp - 1 || tp === 0, wo = c.widgetOptions, s = wo.pager_selectors; if ( wo.pager_updateArrows ) { @@ -335,22 +335,23 @@ tsp = ts.pager = { var i, pg, s, out, regex, wo = c.widgetOptions, p = c.pager, - f = c.$table.hasClass('hasFilters') && !wo.pager_ajaxUrl, + f = c.$table.hasClass('hasFilters'), t = [], sz = p.size || 10; // don't allow dividing by zero - t = [ wo && wo.filter_filteredRow || 'filtered', c.selectorRemove ]; + t = [ wo && wo.filter_filteredRow || 'filtered', c.selectorRemove.replace(/^(\w+\.)/g,'') ]; if (wo.pager_countChildRows) { t.push(c.cssChildRow); } regex = new RegExp( '(' + t.join('|') + ')' ); p.$size.add(p.$goto).removeClass(wo.pager_css.disabled).removeAttr('disabled').attr('aria-disabled', 'false'); p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method - p.filteredRows = (f) ? 0 : p.totalRows; - p.filteredPages = p.totalPages; - if (f) { + if (f && !wo.pager_ajaxUrl) { + p.filteredRows = 0; $.each(c.cache[0].normalized, function(i, el) { p.filteredRows += p.regexRows.test(el[c.columns].$row[0].className) ? 0 : 1; }); - p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; + } else if (!f) { + p.filteredRows = p.totalRows; } + p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) { t = (p.size * p.page > p.filteredRows); p.startRow = (t) ? 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1); @@ -408,7 +409,7 @@ tsp = ts.pager = { if (h) { d = h - $b.height(); if ( d > 5 && $.data(table, 'pagerLastSize') === p.size && $b.children('tr:visible').length < p.size ) { - $b.append('<tr class="pagerSavedHeightSpacer ' + wo.pager_selectors.remove.replace(/(tr)?\./g,'') + '" style="height:' + d + 'px;"></tr>'); + $b.append('<tr class="pagerSavedHeightSpacer ' + wo.pager_selectors.remove.replace(/^(\w+\.)/g,'') + '" style="height:' + d + 'px;"></tr>'); } } } @@ -442,7 +443,7 @@ tsp = ts.pager = { } else { rows[i].style.display = ( j >= s && j < e ) ? '' : 'none'; // don't count child rows - j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) && !wo.pager_countChildRows ? 0 : 1; + j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.replace(/^(\w+\.)/g,'')) && !wo.pager_countChildRows ? 0 : 1; if ( j === e && rows[i].style.display !== 'none' && rows[i].className.match(ts.css.cssHasChild) ) { lastIndex = i; } @@ -498,6 +499,7 @@ tsp = ts.pager = { if (!$.isArray(result)) { p.ajaxData = result; p.totalRows = result.total; + p.filteredRows = typeof result.filteredRows !== 'undefined' ? result.filteredRows : result.total; th = result.headers; d = result.rows; } else { @@ -892,7 +894,7 @@ ts.showError = function(table, message){ }) // add error row to thead instead of tbody, or clicking on the header will result in a parser error .appendTo( c.$table.find('thead:first') ) - .addClass( errorRow + ' ' + c.selectorRemove.replace(/^[.#]/, '') ) + .addClass( errorRow + ' ' + c.selectorRemove.replace(/^(\w+\.)/g,'') ) .attr({ role : 'alert', 'aria-live' : 'assertive' diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index 8170ebc..be785dd 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -10,7 +10,7 @@ Resizable scroller widget for the jQuery tablesorter plugin - Version 2.0 - modified by Rob Garrison 4/12/2013; updated 6/18/2014 (v2.17.2) + Version 2.0 - modified by Rob Garrison 4/12/2013; updated 6/28/2014 (v2.17.3) Requires jQuery v1.7+ Requires the tablesorter plugin, v2.8+, available at http://mottie.github.com/tablesorter/docs/ @@ -73,6 +73,7 @@ ts.addWidget({ scroller_height : 300, scroller_barWidth : 18, scroller_jumpToHeader: true, + scroller_upAfterSort: true, scroller_idPrefix : 's_' }, init: function(table, thisWidget, c, wo){ @@ -198,7 +199,9 @@ ts.addWidget({ } // Sorting, so scroll to top - $tbl.parent().animate({ scrollTop: 0 }, 'fast'); + if (wo.scroller_upAfterSort) { + $tbl.parent().animate({ scrollTop: 0 }, 'fast'); + } }, remove : function(table, c, wo){ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js index 3089ed7..70817a9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js @@ -1,6 +1,6 @@ /* * StaticRow widget for jQuery TableSorter 2.0 - * Version 1.1 - modified by Rob Garrison (4/22/2014 for tablesorter v2.16.1-beta) + * Version 1.2 - modified by Rob Garrison (6/28/2014 for tablesorter v2.16.1-beta+) * Requires: * jQuery v1.4+ * tablesorter plugin, v2.8+, available at http://mottie.github.com/tablesorter/docs/ @@ -73,8 +73,9 @@ ts.addWidget({ // Loop thru static rows, moving them to their original "indexed" position, // & repeat until no more re-shuffling is needed var targetIndex, $thisRow, indx, numRows, $tbody, hasShuffled, $rows, max; + c.$tbodies.each(function(){ - $tbody = $(this); + $tbody = $.tablesorter.processTbody(table, $(this), true); // remove tbody hasShuffled = true; indx = 0; $rows = $tbody.children(wo.staticRow_class); @@ -84,7 +85,6 @@ ts.addWidget({ // don't allow the while loop to cycle more times than the set number of static rows while (hasShuffled && indx < max) { hasShuffled = false; - /*jshint loopfunc:true */ $rows.each(function() { targetIndex = $(this).data(wo.staticRow_data); @@ -107,10 +107,11 @@ ts.addWidget({ } }); indx++; - } + $.tablesorter.processTbody(table, $tbody, false); // restore tbody }); + c.$table.trigger('staticRowsComplete', table); }, From 94d9c7cc58df653fbf883feaa9a338c2ff2512fa Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Sun, 6 Jul 2014 21:57:42 +0200 Subject: [PATCH 034/138] * updated tablesorter to latest version (2.17.4) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 12 +- .../jquery-tablesorter/jquery.tablesorter.js | 9 +- .../jquery.tablesorter.widgets.js | 150 +++++++++++------- .../widgets/widget-pager.js | 14 +- 8 files changed, 122 insertions(+), 73 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bf2210..2a6cb74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.12.4 (2014-07-06) + +* Upgrade tablesorter to v2.17.4 + #### v1.12.3 (2014-06-30) * Upgrade tablesorter to v2.17.3 diff --git a/README.md b/README.md index 32a417f..7b9bccf 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.17.3 (6/28/2014), [documentation] +Current tablesorter version: 2.17.4 (7/4/2014), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index c164710..726b6ea 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.12.3' + VERSION = '1.12.4' end diff --git a/tablesorter b/tablesorter index 197af4f..6de1009 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 197af4fc31b370d67bca33dcab6856db957932e9 +Subproject commit 6de1009af887e6023f414a99a4b97189e3640275 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 2af0a60..817a74a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 6/28/2014 (v2.17.3) + * updated 7/4/2014 (v2.17.4) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -146,6 +146,8 @@ } else if (!f) { p.filteredRows = p.totalRows; } + c.totalRows = p.totalRows; + c.filteredRows = p.filteredRows; p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) { t = (p.size * p.page > p.filteredRows); @@ -296,8 +298,8 @@ // process ajax object if (!$.isArray(result)) { p.ajaxData = result; - p.totalRows = result.total; - p.filteredRows = typeof result.filteredRows !== 'undefined' ? result.filteredRows : result.total; + c.totalRows = p.totalRows = result.total; + c.filteredRows = p.filteredRows = typeof result.filteredRows !== 'undefined' ? result.filteredRows : result.total; th = result.headers; d = result.rows; } else { @@ -306,10 +308,12 @@ // ensure a zero returned row count doesn't fail the logical || rr_count = result[t ? 1 : 0]; p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count; + // can't set filtered rows when returning an array + c.totalRows = c.filteredRows = p.filteredRows = p.totalRows; d = p.totalRows === 0 ? [""] : result[t ? 0 : 1] || []; // row data th = result[2]; // headers } - l = d.length; + l = d && d.length; if (d instanceof jQuery) { if (p.processAjaxOnInit) { // append jQuery object diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 18e1f91..f1e44a9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.17.3 - Client-side table sorting with ease! +* TableSorter 2.17.4 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.17.3"; + ts.version = "2.17.4"; ts.parsers = []; ts.widgets = []; @@ -1487,6 +1487,11 @@ ts.widgets.push(widget); }; + ts.hasWidget = function(table, name){ + table = $(table); + return table.length && table[0].config && table[0].config.widgetInit[name] || false; + }; + ts.getWidgetById = function(name) { var i, w, l = ts.widgets.length; for (i = 0; i < l; i++) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index eb484eb..6ee24c0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.16+ widgets - updated 6/28/2014 (v2.17.3) +/*! tableSorter 2.16+ widgets - updated 7/4/2014 (v2.17.4) * * Column Styles * Column Filters @@ -176,7 +176,7 @@ ts.addWidget({ id: "uitheme", priority: 10, format: function(table, c, wo) { - var i, time, classes, $header, $icon, $tfoot, + var i, time, classes, $header, $icon, $tfoot, $h, themesAll = ts.themes, $table = c.$table, $headers = c.$headers, @@ -225,17 +225,20 @@ ts.addWidget({ for (i = 0; i < c.columns; i++) { $header = c.$headers.add(c.$extraHeaders).filter('[data-column="' + i + '"]'); $icon = (ts.css.icon) ? $header.find('.' + ts.css.icon) : $header; - if (c.$headers.filter('[data-column="' + i + '"]:last')[0].sortDisabled) { - // no sort arrows for disabled columns! - $header.removeClass(remove); - $icon.removeClass(remove + ' ' + themes.icons); - } else { - classes = ($header.hasClass(ts.css.sortAsc)) ? - themes.sortAsc : - ($header.hasClass(ts.css.sortDesc)) ? themes.sortDesc : - $header.hasClass(ts.css.header) ? themes.sortNone : ''; - $header[classes === themes.sortNone ? 'removeClass' : 'addClass'](themes.active); - $icon.removeClass(remove).addClass(classes); + $h = c.$headers.filter('[data-column="' + i + '"]:last'); + if ($h.length) { + if ($h[0].sortDisabled) { + // no sort arrows for disabled columns! + $header.removeClass(remove); + $icon.removeClass(remove + ' ' + themes.icons); + } else { + classes = ($header.hasClass(ts.css.sortAsc)) ? + themes.sortAsc : + ($header.hasClass(ts.css.sortDesc)) ? themes.sortDesc : + $header.hasClass(ts.css.header) ? themes.sortNone : ''; + $header[classes === themes.sortNone ? 'removeClass' : 'addClass'](themes.active); + $icon.removeClass(remove).addClass(classes); + } } } if (c.debug) { @@ -364,6 +367,7 @@ ts.addWidget({ filter_reset : null, // jQuery selector string of an element used to reset the filters filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters filter_searchDelay : 300, // typing delay in milliseconds before starting a search + filter_searchFiltered: true, // allow searching through already filtered rows in special circumstances; will speed up searching in large tables if true filter_selectSource : null, // include a function to return an array of values to be added to the column filter select filter_startsWith : false, // if true, filter start from the beginning of the cell contents filter_useParsedData : false, // filter all data using parsed content @@ -681,7 +685,9 @@ ts.filter = { ts.benchmark("Applying Filter widget", time); } // add default values - c.$table.bind('tablesorter-initialized pagerInitialized', function() { + c.$table.bind('tablesorter-initialized pagerInitialized', function(e) { + // redefine "wo" as it does not update properly inside this callback + var wo = this.config.widgetOptions; filters = ts.filter.setDefaults(table, c, wo) || []; if (filters.length) { ts.setFilters(table, filters, true); @@ -691,9 +697,16 @@ ts.filter = { if (!wo.filter_initialized) { // filter widget initialized wo.filter_initialized = true; - c.$table.trigger('filterInit'); + c.$table.trigger('filterInit', c); } }); + // if filter widget is added after pager has initialized; then set filter init flag + if (c.pager && c.pager.initialized && !wo.filter_initialized) { + wo.filter_initialized = true; + c.$table + .trigger('filterFomatterUpdate') + .trigger('filterInit', c); + } }, setDefaults: function(table, c, wo) { @@ -815,6 +828,7 @@ ts.filter = { if (event.which === 13 || event.type === 'search' || event.type === 'change') { event.preventDefault(); // init search with no delay + $(this).attr('data-lastSearchTime', new Date().getTime()); ts.filter.searching( table, false, true ); } }); @@ -928,6 +942,9 @@ ts.filter = { $(this).hasClass('filter-parsed'); }).get(); if (c.debug) { time = new Date(); } + // filtered rows count + c.filteredRows = 0; + c.totalRows = 0; for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { if ($tbodies.eq(tbodyIndex).hasClass(c.cssInfoBlock || ts.css.info)) { continue; } // ignore info blocks, issue #264 $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); @@ -944,24 +961,27 @@ ts.filter = { $rows = $rows.not('.' + c.cssChildRow); len = $rows.length; // optimize searching only through already filtered rows - see #313 - searchFiltered = true; + searchFiltered = wo.filter_searchFiltered; lastSearch = c.lastSearch || c.$table.data('lastSearch') || []; - for (indx = 0; indx < columnIndex; indx++) { - val = filters[indx] || ''; - // break out of loop if we've already determined not to search filtered rows - if (!searchFiltered) { indx = columnIndex; } - // search already filtered rows if... - searchFiltered = searchFiltered && lastSearch.length && - // there are no changes from beginning of filter - val.indexOf(lastSearch[indx] || '') === 0 && - // if there is NOT a logical "or", or range ("to" or "-") in the string - !regex.alreadyFiltered.test(val) && - // if we are not doing exact matches, using "|" (logical or) or not "!" - !/[=\"\|!]/.test(val) && - // don't search only filtered if the value is negative ('> -10' => '> -100' will ignore hidden rows) - !(/(>=?\s*-\d)/.test(val) || /(<=?\s*\d)/.test(val)) && - // if filtering using a select without a "filter-match" class (exact match) - fixes #593 - !( val !== '' && c.$filters && c.$filters.eq(indx).find('select').length && !c.$headers.filter('[data-column="' + indx + '"]:last').hasClass('filter-match') ); + if (searchFiltered) { + // cycle through all filters; include last (columnIndex + 1 = match any column). Fixes #669 + for (indx = 0; indx < columnIndex + 1; indx++) { + val = filters[indx] || ''; + // break out of loop if we've already determined not to search filtered rows + if (!searchFiltered) { indx = columnIndex; } + // search already filtered rows if... + searchFiltered = searchFiltered && lastSearch.length && + // there are no changes from beginning of filter + val.indexOf(lastSearch[indx] || '') === 0 && + // if there is NOT a logical "or", or range ("to" or "-") in the string + !regex.alreadyFiltered.test(val) && + // if we are not doing exact matches, using "|" (logical or) or not "!" + !/[=\"\|!]/.test(val) && + // don't search only filtered if the value is negative ('> -10' => '> -100' will ignore hidden rows) + !(/(>=?\s*-\d)/.test(val) || /(<=?\s*\d)/.test(val)) && + // if filtering using a select without a "filter-match" class (exact match) - fixes #593 + !( val !== '' && c.$filters && c.$filters.eq(indx).find('select').length && !c.$headers.filter('[data-column="' + indx + '"]:last').hasClass('filter-match') ); + } } notFiltered = $rows.not('.' + wo.filter_filteredRow).length; // can't search when all rows are hidden - this happens when looking for exact matches @@ -1087,6 +1107,8 @@ ts.filter = { } } } + c.filteredRows += $rows.not('.' + wo.filter_filteredRow).length; + c.totalRows += $rows.length; ts.processTbody(table, $tbody, false); } c.lastCombinedFilter = combinedFilters; // save last search @@ -1098,7 +1120,7 @@ ts.filter = { if (c.debug) { ts.benchmark("Completed filter widget search", time); } - if (wo.filter_initialized) { c.$table.trigger('filterEnd'); } + if (wo.filter_initialized) { c.$table.trigger('filterEnd', c ); } setTimeout(function(){ c.$table.trigger('applyWidgets'); // make sure zebra widget is applied }, 0); @@ -1484,19 +1506,31 @@ ts.addWidget({ options: { resizable : true, resizable_addLastColumn : false, - resizable_widths : [] + resizable_widths : [], + resizable_throttle : false // set to true (5ms) or any number 0-10 range }, format: function(table, c, wo) { if (c.$table.hasClass('hasResizable')) { return; } c.$table.addClass('hasResizable'); ts.resizableReset(table, true); // set default widths - var $rows, $columns, $column, column, + var $rows, $columns, $column, column, timer, storedSizes = {}, $table = c.$table, mouseXPosition = 0, $target = null, $next = null, fullWidth = Math.abs($table.parent().width() - $table.width()) < 20, + mouseMove = function(event){ + if (mouseXPosition === 0 || !$target) { return; } + // resize columns + var leftEdge = event.pageX - mouseXPosition, + targetWidth = $target.width(); + $target.width( targetWidth + leftEdge ); + if ($target.width() !== targetWidth && fullWidth) { + $next.width( $next.width() - leftEdge ); + } + mouseXPosition = event.pageX; + }, stopResize = function() { if (ts.storage && $target && $next) { storedSizes = {}; @@ -1551,21 +1585,6 @@ ts.addWidget({ .append('<div class="' + ts.css.resizer + '" style="cursor:w-resize;position:absolute;z-index:1;right:-' + padding + 'px;top:0;height:100%;width:20px;"></div>'); }) - .bind('mousemove.tsresize', function(event) { - // ignore mousemove if no mousedown - if (mouseXPosition === 0 || !$target) { return; } - // resize columns - var leftEdge = event.pageX - mouseXPosition, - targetWidth = $target.width(); - $target.width( targetWidth + leftEdge ); - if ($target.width() !== targetWidth && fullWidth) { - $next.width( $next.width() - leftEdge ); - } - mouseXPosition = event.pageX; - }) - .bind('mouseup.tsresize', function() { - stopResize(); - }) .find('.' + ts.css.resizer + ',.' + ts.css.grip) .bind('mousedown', function(event) { // save header cell and mouse position @@ -1576,17 +1595,30 @@ ts.addWidget({ $next = event.shiftKey ? $target.parent().find('th').not('.resizable-false').filter(':last') : $target.nextAll(':not(.resizable-false)').eq(0); mouseXPosition = event.pageX; }); - $table.find('thead:first') - .bind('mouseup.tsresize mouseleave.tsresize', function() { - stopResize(); + $(document) + .bind('mousemove.tsresize', function(event) { + // ignore mousemove if no mousedown + if (mouseXPosition === 0 || !$target) { return; } + if (wo.resizable_throttle) { + clearTimeout(timer); + timer = setTimeout(function(){ + mouseMove(event); + }, isNaN(wo.resizable_throttle) ? 5 : wo.resizable_throttle ); + } else { + mouseMove(event); + } }) + .bind('mouseup.tsresize', function() { + stopResize(); + }); + // right click to reset columns to default widths - .bind('contextmenu.tsresize', function() { - ts.resizableReset(table); - // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset - var allowClick = $.isEmptyObject ? $.isEmptyObject(storedSizes) : true; - storedSizes = {}; - return allowClick; + $table.find('thead:first').bind('contextmenu.tsresize', function() { + ts.resizableReset(table); + // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset + var allowClick = $.isEmptyObject ? $.isEmptyObject(storedSizes) : true; + storedSizes = {}; + return allowClick; }); }, remove: function(table, c) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index d8fe8d0..694bde8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/* Pager widget (beta) for TableSorter 6/28/2014 (v2.17.3) */ +/* Pager widget for TableSorter 7/4/2014 (v2.17.4) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ "use strict"; @@ -205,7 +205,7 @@ tsp = ts.pager = { p.isInitializing = false; tsp.setPageSize(table, 0, c); // page size 0 is ignored c.$table.trigger('pagerInitialized', c); - + tsp.updatePageDisplay(table, c); }, bindEvents: function(table, c){ @@ -351,6 +351,8 @@ tsp = ts.pager = { } else if (!f) { p.filteredRows = p.totalRows; } + c.totalRows = p.totalRows; + c.filteredRows = p.filteredRows; p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) { t = (p.size * p.page > p.filteredRows); @@ -498,8 +500,8 @@ tsp = ts.pager = { // process ajax object if (!$.isArray(result)) { p.ajaxData = result; - p.totalRows = result.total; - p.filteredRows = typeof result.filteredRows !== 'undefined' ? result.filteredRows : result.total; + c.totalRows = p.totalRows = result.total; + c.filteredRows = p.filteredRows = typeof result.filteredRows !== 'undefined' ? result.filteredRows : result.total; th = result.headers; d = result.rows; } else { @@ -508,10 +510,12 @@ tsp = ts.pager = { // ensure a zero returned row count doesn't fail the logical || rr_count = result[t ? 1 : 0]; p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count; + // can't set filtered rows when returning an array + c.totalRows = c.filteredRows = p.filteredRows = p.totalRows; d = p.totalRows === 0 ? [""] : result[t ? 0 : 1] || []; // row data th = result[2]; // headers } - l = d.length; + l = d && d.length; if (d instanceof jQuery) { if (wo.pager_processAjaxOnInit) { // append jQuery object From 55f3378841f404d4d1a2f6254f48db96a3c160da Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Sun, 20 Jul 2014 00:08:56 +0200 Subject: [PATCH 035/138] * updated tablesorter to latest version (2.17.5) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 57 ++++++-- .../jquery-tablesorter/jquery.tablesorter.js | 13 +- ...sorter.widgets-filter-formatter-select2.js | 3 +- ...ry.tablesorter.widgets-filter-formatter.js | 10 +- .../jquery.tablesorter.widgets.js | 125 +++++++++++++----- .../parsers/parser-image.js | 20 +++ .../parsers/parser-input-select.js | 64 +++++---- .../widgets/widget-output.js | 68 ++++++---- .../widgets/widget-pager.js | 79 ++++++++--- 13 files changed, 328 insertions(+), 121 deletions(-) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/parsers/parser-image.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a6cb74..a29d1fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.12.5 (2014-07-20) + +* Upgrade tablesorter to v2.17.5 + #### v1.12.4 (2014-07-06) * Upgrade tablesorter to v2.17.4 diff --git a/README.md b/README.md index 7b9bccf..158e5d2 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.17.4 (7/4/2014), [documentation] +Current tablesorter version: 2.17.5 (7/17/2014), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 726b6ea..3bc6be7 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.12.4' + VERSION = '1.12.5' end diff --git a/tablesorter b/tablesorter index 6de1009..e861f6c 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 6de1009af887e6023f414a99a4b97189e3640275 +Subproject commit e861f6c3fcc28c79f385071be11c357245222d53 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 817a74a..8bcc113 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 7/4/2014 (v2.17.4) + * updated 7/17/2014 (v2.17.5) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -137,15 +137,20 @@ t = [ (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered'), c.selectorRemove.replace(/^(\w+\.)/g,'') ]; if (p.countChildRows) { t.push(c.cssChildRow); } regex = new RegExp( '(' + t.join('|') + ')' ); - p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method if (f && !p.ajaxUrl) { - p.filteredRows = 0; - $.each(c.cache[0].normalized, function(i, el) { - p.filteredRows += p.regexRows.test(el[c.columns].$row[0].className) ? 0 : 1; - }); + if ($.isEmptyObject(c.cache)) { + // delayInit: true so nothing is in the cache + p.filteredRows = p.totalRows = c.$tbodies.eq(0).children('tr').not( p.countChildRows ? '' : '.' + c.cssChildRow ).length; + } else { + p.filteredRows = 0; + $.each(c.cache[0].normalized, function(i, el) { + p.filteredRows += p.regexRows.test(el[c.columns].$row[0].className) ? 0 : 1; + }); + } } else if (!f) { p.filteredRows = p.totalRows; } + p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method c.totalRows = p.totalRows; c.filteredRows = p.filteredRows; p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; @@ -292,7 +297,7 @@ exception === 'timeout' ? 'Time out error' : exception === 'abort' ? 'Ajax Request aborted' : 'Uncaught error: ' + xhr.statusText + ' [' + xhr.status + ']' ); - c.$tbodies.eq(0).children().detach(); + c.$tbodies.eq(0).children('tr').detach(); p.totalRows = 0; } else { // process ajax object @@ -317,7 +322,7 @@ if (d instanceof jQuery) { if (p.processAjaxOnInit) { // append jQuery object - c.$tbodies.eq(0).children().detach(); + c.$tbodies.eq(0).children('tr').detach(); c.$tbodies.eq(0).append(d); } } else if (l) { @@ -477,7 +482,13 @@ l = rows && rows.length || 0, // rows may be undefined s = ( p.page * p.size ), e = p.size; - if ( l < 1 ) { return; } // empty table, abort! + if ( l < 1 ) { + if (c.debug) { + ts.log('Pager: no rows for pager to render'); + } + // empty table, abort! + return; + } if ( p.page >= p.totalPages ) { // lets not render the table more than once moveToLastPage(table, p); @@ -540,12 +551,32 @@ }); }, + // updateCache if delayInit: true + updateCache = function(table) { + var c = table.config, + p = c.pager; + c.$table.trigger('updateCache', [ function(){ + var i, + rows = [], + n = table.config.cache[0].normalized; + p.totalRows = n.length; + for (i = 0; i < p.totalRows; i++) { + rows.push(n[i][c.columns].$row); + } + c.rowsCopy = rows; + moveToPage(table, p, true); + } ]); + }, + moveToPage = function(table, p, pageMoved) { if ( p.isDisabled ) { return; } var c = table.config, $t = $(table), l = p.last, pg = Math.min( p.totalPages, p.filteredPages ); + if ( pageMoved !== false && p.initialized && $.isEmptyObject(table.config.cache)) { + return updateCache(table); + } if ( p.page < 0 ) { p.page = 0; } if ( p.page > ( pg - 1 ) && pg !== 0 ) { p.page = pg - 1; } // fixes issue where one currentFilter is [] and the other is ['','',''], @@ -716,6 +747,10 @@ // update pager after filter widget completes .bind('filterEnd.pager sortEnd.pager', function() { if (p.initialized) { + if (c.delayInit && c.rowsCopy && c.rowsCopy.length === 0) { + // make sure we have a copy of all table rows once the cache has been built + updateCache(table); + } // update page display first, so we update p.filteredPages updatePageDisplay(table, p, false); moveToPage(table, p, false); @@ -754,7 +789,7 @@ e.stopPropagation(); p.page = (parseInt(v, 10) || 1) - 1; if (p.$goto.length) { p.$goto.val(p.size); } // twice? - moveToPage(table, p); + moveToPage(table, p, true); updatePageDisplay(table, p, false); }); @@ -784,7 +819,7 @@ .unbind('change') .bind('change', function(){ p.page = $(this).val() - 1; - moveToPage(table, p); + moveToPage(table, p, true); updatePageDisplay(table, p, false); }); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index f1e44a9..5138c44 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.17.4 - Client-side table sorting with ease! +* TableSorter 2.17.5 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.17.4"; + ts.version = "2.17.5"; ts.parsers = []; ts.widgets = []; @@ -274,6 +274,7 @@ $tb = c.$table.children('tbody'), parsers = c.parsers; c.cache = {}; + c.totalRows = 0; // if no parsers found, return - it's an empty table. if (!parsers) { return c.debug ? log('Warning: *Empty table!* Not building a cache') : ''; @@ -343,6 +344,8 @@ cc.normalized.push(cols); } cc.colMax = colMax; + // total up rows, not including child rows + c.totalRows += cc.normalized.length; } } if (c.showProcessing) { @@ -1013,6 +1016,7 @@ if (!/tablesorter\-/.test($table.attr('class'))) { k = (c.theme !== '' ? ' tablesorter-' + c.theme : ''); } + c.table = table; c.$table = $table .addClass(ts.css.table + ' ' + c.tableClass + k) .attr({ role : 'grid'}); @@ -1043,6 +1047,8 @@ fixColumnWidth(table); // try to auto detect column type, and store in tables config buildParserCache(table); + // start total row count at zero + c.totalRows = 0; // build the cache for the tbody cells // delayInit will delay building the cache until the user starts a sort if (!c.delayInit) { buildCache(table); } @@ -1534,13 +1540,14 @@ $.each(widgets, function(i,w){ if (w) { if (init || !(c.widgetInit[w.id])) { + // set init flag first to prevent calling init more than once (e.g. pager) + c.widgetInit[w.id] = true; if (w.hasOwnProperty('options')) { wo = table.config.widgetOptions = $.extend( true, {}, w.options, wo ); } if (w.hasOwnProperty('init')) { w.init(table, w, c, wo); } - c.widgetInit[w.id] = true; } if (!init && w.hasOwnProperty('format')) { w.format(table, c, wo, false); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js index 89b75f6..d835d93 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js @@ -1,4 +1,4 @@ -/*! Filter widget formatter functions - updated 4/30/2014 (v2.16.3) +/*! Filter widget select2 formatter function - updated 7/17/2014 (v2.17.5) * requires: jQuery 1.7.2+, tableSorter 2.16+, filter widget 2.16+ and select2 v3.4.6+ plugin */ /*jshint browser:true, jquery:true, unused:false */ @@ -102,6 +102,7 @@ ts.filterFormatter.select2 = function($cell, indx, select2Def) { val = val.replace(/[/()$^]/g, '').split('|'); $cell.find('.select2').select2('val', val); updateSelect2(); + ts.filter.formatterUpdated($cell, indx); }); // has sticky headers? diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js index 83b85eb..81d006c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js @@ -1,4 +1,4 @@ -/*! Filter widget formatter functions - updated 6/18/2014 (v2.17.2) +/*! Filter widget formatter functions - updated 7/17/2014 (v2.17.5) * requires: tableSorter 2.15+ and jQuery 1.4.3+ * * uiSpinner (jQuery UI spinner) @@ -145,6 +145,7 @@ tsff = ts.filterFormatter = { var val = tsff.updateCompare($cell, $input, o)[0]; $cell.find('.spinner').val( val ); updateSpinner({ value: val }, true); + ts.filter.formatterUpdated($cell, indx); }); if (o.compare) { @@ -303,6 +304,7 @@ tsff = ts.filterFormatter = { var val = tsff.updateCompare($cell, $input, o)[0]; $cell.find('.slider').slider('value', val ); updateSlider({ value: val }, false); + ts.filter.formatterUpdated($cell, indx); }); if (o.compare) { @@ -447,6 +449,7 @@ tsff = ts.filterFormatter = { // update slider from hidden input, in case of saved filters c.$table.bind('filterFomatterUpdate', function(){ getRange(); + ts.filter.formatterUpdated($cell, indx); }); // on reset @@ -579,6 +582,7 @@ tsff = ts.filterFormatter = { $cell.add($shcell).find('.date').datepicker( 'setDate', num || null ); setTimeout(function(){ date1Compare(true); + ts.filter.formatterUpdated($cell, indx); }, 0); }); @@ -728,6 +732,7 @@ tsff = ts.filterFormatter = { // give datepicker time to process setTimeout(function(){ closeDate(); + ts.filter.formatterUpdated($cell, indx); }, 0); }); @@ -848,6 +853,7 @@ tsff = ts.filterFormatter = { var val = tsff.updateCompare($cell, $input, o)[0] || o.value; $cell.find('.number').val( ((val || '') + '').replace(/[><=]/g,'') ); updateNumber(false, true); + ts.filter.formatterUpdated($cell, indx); }); if (o.compare) { @@ -968,6 +974,7 @@ tsff = ts.filterFormatter = { var val = tsff.updateCompare($cell, $input, o)[0]; $cell.find('.range').val( val ); updateRange(val, false, true); + ts.filter.formatterUpdated($cell, indx); }); if (o.compare) { @@ -1100,6 +1107,7 @@ tsff = ts.filterFormatter = { // update slider from hidden input, in case of saved filters c.$table.bind('filterFomatterUpdate', function(){ updateColor( $input.val(), true ); + ts.filter.formatterUpdated($cell, indx); }); // on reset diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 6ee24c0..fe16343 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.16+ widgets - updated 7/4/2014 (v2.17.4) +/*! tableSorter 2.16+ widgets - updated 7/17/2014 (v2.17.5) * * Column Styles * Column Filters @@ -54,7 +54,6 @@ $.extend(ts.css, { filter : 'tablesorter-filter', wrapper : 'tablesorter-wrapper', // ui theme & resizable resizer : 'tablesorter-resizer', // resizable - grip : 'tablesorter-resizer-grip', sticky : 'tablesorter-stickyHeader', // stickyHeader stickyVis : 'tablesorter-sticky-visible' }); @@ -444,7 +443,7 @@ ts.filter = { savedSearch = query; // parse filter value in case we're comparing numbers (dates) if (parsed[index] || parser.type === 'numeric') { - result = parser.format( $.trim('' + iFilter.replace(ts.filter.regex.operators, '')), table, [], index ); + result = ts.filter.parseFilter(table, $.trim('' + iFilter.replace(ts.filter.regex.operators, '')), index, parsed[index], true); query = ( typeof result === "number" && result !== '' && !isNaN(result) ) ? result : query; } @@ -463,9 +462,9 @@ ts.filter = { return null; }, // Look for a not match - notMatch: function( filter, iFilter, exact, iExact, cached, index, table, wo ) { + notMatch: function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed ) { if ( /^\!/.test(iFilter) ) { - iFilter = iFilter.replace('!', ''); + iFilter = ts.filter.parseFilter(table, iFilter.replace('!', ''), index, parsed[index]); if (ts.filter.regex.exact.test(iFilter)) { // look for exact not matches - see #628 iFilter = iFilter.replace(ts.filter.regex.exact, ''); @@ -481,19 +480,19 @@ ts.filter = { exact: function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed, rowArray ) { /*jshint eqeqeq:false */ if (ts.filter.regex.exact.test(iFilter)) { - var fltr = iFilter.replace(ts.filter.regex.exact, ''); + var fltr = ts.filter.parseFilter(table, iFilter.replace(ts.filter.regex.exact, ''), index, parsed[index]); return rowArray ? $.inArray(fltr, rowArray) >= 0 : fltr == iExact; } return null; }, // Look for an AND or && operator (logical and) - and : function( filter, iFilter, exact, iExact ) { + and : function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed ) { if ( ts.filter.regex.andTest.test(filter) ) { var query = iFilter.split( ts.filter.regex.andSplit ), - result = iExact.search( $.trim(query[0]) ) >= 0, + result = iExact.search( $.trim(ts.filter.parseFilter(table, query[0], index, parsed[index])) ) >= 0, indx = query.length - 1; while (result && indx) { - result = result && iExact.search( $.trim(query[indx]) ) >= 0; + result = result && iExact.search( $.trim(ts.filter.parseFilter(table, query[indx], index, parsed[index])) ) >= 0; indx--; } return result; @@ -507,8 +506,8 @@ ts.filter = { c = table.config, // make sure the dash is for a range and not indicating a negative number query = iFilter.split( ts.filter.regex.toSplit ), - range1 = ts.formatFloat(query[0].replace(ts.filter.regex.nondigit, ''), table), - range2 = ts.formatFloat(query[1].replace(ts.filter.regex.nondigit, ''), table); + range1 = ts.formatFloat( ts.filter.parseFilter(table, query[0].replace(ts.filter.regex.nondigit, ''), index, parsed[index]), table ), + range2 = ts.formatFloat( ts.filter.parseFilter(table, query[1].replace(ts.filter.regex.nondigit, ''), index, parsed[index]), table ); // parse filter value in case we're comparing numbers (dates) if (parsed[index] || c.parsers[index].type === 'numeric') { result = c.parsers[index].format('' + query[0], table, c.$headers.eq(index), index); @@ -528,22 +527,23 @@ ts.filter = { wild : function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed, rowArray ) { if ( /[\?|\*]/.test(iFilter) || ts.filter.regex.orReplace.test(filter) ) { var c = table.config, - query = iFilter.replace(ts.filter.regex.orReplace, "|"); + query = ts.filter.parseFilter(table, iFilter.replace(ts.filter.regex.orReplace, "|"), index, parsed[index]); // look for an exact match with the "or" unless the "filter-match" class is found if (!c.$headers.filter('[data-column="' + index + '"]:last').hasClass('filter-match') && /\|/.test(query)) { query = $.isArray(rowArray) ? '(' + query + ')' : '^(' + query + ')$'; } + // parsing the filter may not work properly when using wildcards =/ return new RegExp( query.replace(/\?/g, '\\S{1}').replace(/\*/g, '\\S*') ).test(iExact); } return null; }, // fuzzy text search; modified from https://github.com/mattyork/fuzzy (MIT license) - fuzzy: function( filter, iFilter, exact, iExact ) { + fuzzy: function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed ) { if ( /^~/.test(iFilter) ) { var indx, patternIndx = 0, len = iExact.length, - pattern = iFilter.slice(1); + pattern = ts.filter.parseFilter(table, iFilter.slice(1), index, parsed[index]); for (indx = 0; indx < len; indx++) { if (iExact[indx] === pattern[patternIndx]) { patternIndx += 1; @@ -572,8 +572,11 @@ ts.filter = { } c.$table.addClass('hasFilters'); - // define searchTimer so using clearTimeout won't cause an undefined error + // define timers so using clearTimeout won't cause an undefined error wo.searchTimer = null; + wo.filter_initTimer = null; + wo.filter_formatterCount = 0; + wo.filter_formatterInit = []; $.extend( regex, { child : new RegExp(c.cssChildRow), @@ -593,7 +596,7 @@ ts.filter = { } c.$table.bind('addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter '), function(event, filter) { - c.$table.find('.' + ts.css.filterRow).toggle( !(wo.filter_hideEmpty && $.isEmptyObject(c.cache)) ); // fixes #450 + c.$table.find('.' + ts.css.filterRow).toggle( !(wo.filter_hideEmpty && $.isEmptyObject(c.cache) && !(c.delayInit && event.type === 'appendCache')) ); // fixes #450 if ( !/(search|filter)/.test(event.type) ) { event.stopPropagation(); ts.filter.buildDefault(table, true); @@ -681,6 +684,9 @@ ts.filter = { }); } + // set filtered rows count (intially unfiltered) + c.filteredRows = c.totalRows; + if (c.debug) { ts.benchmark("Applying Filter widget", time); } @@ -690,24 +696,58 @@ ts.filter = { var wo = this.config.widgetOptions; filters = ts.filter.setDefaults(table, c, wo) || []; if (filters.length) { - ts.setFilters(table, filters, true); - // ts.filter.checkFilters(table, filters); + // prevent delayInit from triggering a cache build if filters are empty + if ( !(c.delayInit && filters.join('') === '') ) { + ts.setFilters(table, filters, true); + } } c.$table.trigger('filterFomatterUpdate'); - if (!wo.filter_initialized) { - // filter widget initialized - wo.filter_initialized = true; - c.$table.trigger('filterInit', c); - } + // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers + setTimeout(function(){ + if (!wo.filter_initialized) { + ts.filter.filterInitComplete(c); + } + }, 100); }); // if filter widget is added after pager has initialized; then set filter init flag if (c.pager && c.pager.initialized && !wo.filter_initialized) { + c.$table.trigger('filterFomatterUpdate'); + setTimeout(function(){ + ts.filter.filterInitComplete(c); + }, 100); + } + }, + // $cell parameter, but not the config, is passed to the + // filter_formatters, so we have to work with it instead + formatterUpdated: function($cell, column) { + var wo = $cell.closest('table')[0].config.widgetOptions; + if (!wo.filter_initialized) { + // add updates by column since this function + // may be called numerous times before initialization + wo.filter_formatterInit[column] = 1; + } + }, + filterInitComplete: function(c){ + var wo = c.widgetOptions, + count = 0; + $.each( wo.filter_formatterInit, function(i, val) { + if (val === 1) { + count++; + } + }); + clearTimeout(wo.filter_initTimer); + if (!wo.filter_initialized && count === wo.filter_formatterCount) { + // filter widget initialized wo.filter_initialized = true; - c.$table - .trigger('filterFomatterUpdate') - .trigger('filterInit', c); + c.$table.trigger('filterInit', c); + } else if (!wo.filter_initialized) { + // fall back in case a filter_formatter doesn't call + // $.tablesorter.filter.formatterUpdated($cell, column), and the count is off + wo.filter_initTimer = setTimeout(function(){ + wo.filter_initialized = true; + c.$table.trigger('filterInit', c); + }, 500); } - }, setDefaults: function(table, c, wo) { var isArray, saved, indx, @@ -728,6 +768,12 @@ ts.filter = { c.$table.data('lastSearch', filters); return filters; }, + parseFilter: function(table, filter, column, parsed, forceParse){ + var c = table.config; + return forceParse || parsed ? + c.parsers[column].format( filter, table, [], column ) : + filter; + }, buildRow: function(table, c, wo) { var column, $header, buildSelect, disabled, name, ffxn, // c.columns defined in computeThIndexes() @@ -753,6 +799,7 @@ ts.filter = { } else { ffxn = ts.getColumnData( table, wo.filter_formatter, column ); if (ffxn) { + wo.filter_formatterCount++; buildFilter = ffxn( c.$filters.eq(column), column ); // no element returned, so lets go find it if (buildFilter && buildFilter.length === 0) { @@ -825,7 +872,9 @@ ts.filter = { ts.filter.searching( table, true, true ); }) .bind('search change keypress '.split(' ').join(c.namespace + 'filter '), function(event){ - if (event.which === 13 || event.type === 'search' || event.type === 'change') { + var column = $(this).data('column'); + // don't allow "change" event to process if the input value is the same - fixes #685 + if (event.which === 13 || event.type === 'search' || event.type === 'change' && this.value !== c.lastSearch[column]) { event.preventDefault(); // init search with no delay $(this).attr('data-lastSearchTime', new Date().getTime()); @@ -853,7 +902,15 @@ ts.filter = { filters = (filterArray) ? filter : ts.getFilters(table, true), combinedFilters = (filters || []).join(''); // combined filter values // prevent errors if delay init is set - if ($.isEmptyObject(c.cache)) { return; } + if ($.isEmptyObject(c.cache)) { + // update cache if delayInit set & pager has initialized (after user initiates a search) + if (c.delayInit && c.pager && c.pager.initialized) { + c.$table.trigger('updateCache', [function(){ + ts.filter.checkFilters(table, false, skipFirst); + }] ); + } + return; + } // add filter array back into inputs if (filterArray) { ts.setFilters( table, filters, false, skipFirst !== true ); @@ -1092,7 +1149,7 @@ ts.filter = { result = filterMatched; // Look for match, and add child row data for matching } else { - exact = (iExact + childRowText).indexOf(iFilter); + exact = (iExact + childRowText).indexOf( ts.filter.parseFilter(table, iFilter, columnIndex, parsed[columnIndex]) ); result = ( (!wo.filter_startsWith && exact >= 0) || (wo.filter_startsWith && exact === 0) ); } } @@ -1208,7 +1265,7 @@ ts.filter = { // check if has class filtered if (onlyAvail && row.className.match(wo.filter_filteredRow)) { continue; } // get non-normalized cell content - if (wo.filter_useParsedData) { + if (wo.filter_useParsedData || c.parsers[column].parsed || c.$headers.filter('[data-column="' + column + '"]:last').hasClass('filter-parsed')) { arry.push( '' + cache.normalized[rowIndex][column] ); } else { cell = row.cells[column]; @@ -1579,13 +1636,13 @@ ts.addWidget({ $columns .each(function() { var $column = $(this), - padding = parseInt($column.css('padding-right'), 10) + 10; // 10 is 1/2 of the 20px wide resizer grip + padding = parseInt($column.css('padding-right'), 10) + 10; // 10 is 1/2 of the 20px wide resizer $column .find('.' + ts.css.wrapper) .append('<div class="' + ts.css.resizer + '" style="cursor:w-resize;position:absolute;z-index:1;right:-' + padding + 'px;top:0;height:100%;width:20px;"></div>'); }) - .find('.' + ts.css.resizer + ',.' + ts.css.grip) + .find('.' + ts.css.resizer) .bind('mousedown', function(event) { // save header cell and mouse position $target = $(event.target).closest('th'); @@ -1629,7 +1686,7 @@ ts.addWidget({ .children('tr').children() .unbind('mousemove.tsresize mouseup.tsresize') // don't remove "tablesorter-wrapper" as uitheme uses it too - .find('.' + ts.css.resizer + ',.' + ts.css.grip).remove(); + .find('.' + ts.css.resizer).remove(); ts.resizableReset(table); } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-image.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-image.js new file mode 100644 index 0000000..0774403 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-image.js @@ -0,0 +1,20 @@ +/*! image alt attribute parser for jQuery 1.7+ & tablesorter 2.7.11+ + * New 7/17/2014 (v2.17.5) + */ +/*jshint jquery:true, unused:false */ +;(function($){ +"use strict"; + + $.tablesorter.addParser({ + id: "image", + is: function(){ + return false; + }, + format: function(s, table, cell) { + return $(cell).find('img').attr(table.config.imgAttr || 'alt') || s; + }, + parsed : true, // filter widget flag + type: "text" + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index 255835b..97e37a8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,5 +1,5 @@ /*! input & select parsers for jQuery 1.7+ & tablesorter 2.7.11+ - * Updated 5/28/2014 (v2.17.1) + * Updated 7/17/2014 (v2.17.5) * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ /*jshint browser: true, jquery:true, unused:false */ @@ -64,34 +64,50 @@ type: "text" }); + // Select parser to get the selected text + $.tablesorter.addParser({ + id: "select-text", + is: function(){ + return false; + }, + format: function(s, table, cell) { + var $s = $(cell).find('select'); + return $s.length ? $s.find('option:selected').text() || '' : s; + }, + parsed : true, // filter widget flag + type: "text" + }); + // update select and all input types in the tablesorter cache when the change event fires. // This method only works with jQuery 1.7+ // you can change it to use delegate (v1.4.3+) or live (v1.3+) as desired // if this code interferes somehow, target the specific table $('#mytable'), instead of $('table') - $(window).load(function(){ - // this flag prevents the updateCell event from being spammed - // it happens when you modify input text and hit enter - var alreadyUpdating = false, - t = $.tablesorter.css.table || 'tablesorter'; - // bind to .tablesorter (default class name) - $('.' + t).find('tbody').on('change', 'select, input', function(e){ - if (!alreadyUpdating) { - var $tar = $(e.target), - $cell = $tar.closest('td'), - $table = $cell.closest('table'), - indx = $cell[0].cellIndex, - c = $table[0].config || false, - $hdr = c && c.$headers && c.$headers.eq(indx); - // abort if not a tablesorter table, or - // don't use updateCell if column is set to "sorter-false" and "filter-false", or column is set to "parser-false" - if ( !c || ( $hdr && $hdr.length && ( $hdr.hasClass('parser-false') || ($hdr.hasClass('sorter-false') && $hdr.hasClass('filter-false')) ) ) ){ - return false; + $(function(){ + $('table').on('tablesorter-initialized', function(){ + // this flag prevents the updateCell event from being spammed + // it happens when you modify input text and hit enter + var alreadyUpdating = false; + // bind to .tablesorter (default class name) + $(this).children('tbody').on('change', 'select, input', function(e){ + if (!alreadyUpdating) { + var $tar = $(e.target), + $cell = $tar.closest('td'), + $table = $cell.closest('table'), + indx = $cell[0].cellIndex, + c = $table[0].config || false, + $hdr = c && c.$headers && c.$headers.eq(indx); + // abort if not a tablesorter table, or + // don't use updateCell if column is set to "sorter-false" and "filter-false", or column is set to "parser-false" + if ( !c || ( $hdr && $hdr.length && ( $hdr.hasClass('parser-false') || ($hdr.hasClass('sorter-false') && $hdr.hasClass('filter-false')) ) ) ){ + return false; + } + alreadyUpdating = true; + $table.trigger('updateCell', [ $tar.closest('td'), resort, function(){ + updateServer(e, $table, $tar); + setTimeout(function(){ alreadyUpdating = false; }, 10); + } ]); } - alreadyUpdating = true; - $table.trigger('updateCell', [ $tar.closest('td'), resort ]); - updateServer(e, $table, $tar); - setTimeout(function(){ alreadyUpdating = false; }, 10); - } + }); }); }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index 3e2af5c..e7ee4a3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -1,4 +1,4 @@ -/* Output widget (beta) for TableSorter 5/22/2014 (v2.17.0) +/* Output widget (beta) for TableSorter 7/17/2014 (v2.17.5) * Requires tablesorter v2.8+ and jQuery 1.7+ * Modified from: * HTML Table to CSV: http://www.kunalbabre.com/projects/table2CSV.php (License unknown?) @@ -221,33 +221,55 @@ output = ts.output = { }, // modified from https://github.com/PixelsCommander/Download-File-JS + // & http://html5-demos.appspot.com/static/a.download.html download : function (wo, data){ - var e, link, - processedData = wo.output_encoding + encodeURIComponent(data); + + var e, blob, gotBlob, + nav = window.navigator, + link = document.createElement('a'); // iOS devices do not support downloading. We have to inform user about this. - if (/(iP)/g.test(navigator.userAgent)) { + if (/(iP)/g.test(nav.userAgent)) { alert(output.message); return false; } - // If in Chrome or Safari - download via virtual link click - if ( /(chrome|safari)/.test(navigator.userAgent.toLowerCase()) ) { - // Creating new link node. - link = document.createElement('a'); - link.href = processedData; - link.download = wo.output_saveFileName; - // Dispatching click event. - if (document.createEvent) { - e = document.createEvent('MouseEvents'); - e.initEvent('click', true, true); - link.dispatchEvent(e); - return true; + + // test for blob support + try { + gotBlob = !!new Blob(); + } catch (err) { + gotBlob = false; + } + + // Use HTML5 Blob if browser supports it + if ( gotBlob ) { + + window.URL = window.webkitURL || window.URL; + blob = new Blob([data], {type: wo.output_encoding}); + + if (nav.msSaveBlob) { + // IE 10+ + nav.msSaveBlob(blob, wo.output_saveFileName); + } else { + // all other browsers + link.href = window.URL.createObjectURL(blob); + link.download = wo.output_saveFileName; + // Dispatching click event; using $(link).trigger() won't work + if (document.createEvent) { + e = document.createEvent('MouseEvents'); + // event.initMouseEvent(type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget); + e.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); + link.dispatchEvent(e); + } } + return false; } - // Force file download (whether supported by server). - processedData += '?download'; - window.open(processedData, '_self'); + + // fallback to force file download (whether supported by server). + // not sure if this actually works in IE9 and older... + window.open( wo.output_encoding + encodeURIComponent(data) + '?download' , '_self'); return true; + }, remove : function(c) { @@ -278,11 +300,9 @@ ts.addWidget({ output_callback : function(config, data){ return true; }, // JSON callback executed when a colspan is encountered in the header output_callbackJSON : function($cell, txt, cellIndex) { return txt + '(' + (cellIndex) + ')'; }, - // output data type (with BOM or Windows-1252 is needed for excel) - // NO BOM : 'data:text/csv;charset=utf8,' - // With BOM : 'data:text/csv;charset=utf8,%EF%BB%BF' - // WIN 1252 : 'data:text/csv;charset=windows-1252' - output_encoding : 'data:text/csv;charset=utf8,' + // the need to modify this for Excel no longer exists + output_encoding : 'data:application/octet-stream;charset=utf8,' + }, init: function(table, thisWidget, c) { output.init(c); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 694bde8..81626f4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/* Pager widget for TableSorter 7/4/2014 (v2.17.4) */ +/* Pager widget for TableSorter 7/17/2014 (v2.17.5) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ "use strict"; @@ -153,7 +153,7 @@ tsp = ts.pager = { p.$goto = p.$container.find(s.gotoPage); // goto is a reserved word #657 // page size selector p.$size = p.$container.find(s.pageSize); - p.totalRows = c.$tbodies.eq(0).children('tr').length; + p.totalRows = c.$tbodies.eq(0).children('tr').not( c.widgetOptions.pager_countChildRows ? '' : '.' + c.cssChildRow ).length; p.oldAjaxSuccess = p.oldAjaxSuccess || wo.pager_ajaxObject.success; c.appender = tsp.appender; if (ts.filter && $.inArray('filter', c.widgets) >= 0) { @@ -199,11 +199,11 @@ tsp = ts.pager = { var p = c.pager; tsp.changeHeight(table, c); tsp.bindEvents(table, c); + tsp.setPageSize(table, 0, c); // page size 0 is ignored // pager initialized p.initialized = true; p.isInitializing = false; - tsp.setPageSize(table, 0, c); // page size 0 is ignored c.$table.trigger('pagerInitialized', c); tsp.updatePageDisplay(table, c); }, @@ -226,6 +226,10 @@ tsp = ts.pager = { // update pager after filter widget completes .bind('filterEnd.pager sortEnd.pager', function() { if (p.initialized) { + if (c.delayInit && c.rowsCopy && c.rowsCopy.length === 0) { + // make sure we have a copy of all table rows once the cache has been built + tsp.updateCache(table); + } // update page display first, so we update p.filteredPages tsp.updatePageDisplay(table, c, false); tsp.moveToPage(table, p, false); @@ -266,7 +270,7 @@ tsp = ts.pager = { e.stopPropagation(); p.page = (parseInt(v, 10) || 1) - 1; if (p.$goto.length) { p.$goto.val(c.size); } // twice? - tsp.moveToPage(table, p); + tsp.moveToPage(table, p, true); tsp.updatePageDisplay(table, c, false); }); @@ -296,7 +300,7 @@ tsp = ts.pager = { .unbind('change') .bind('change', function(){ p.page = $(this).val() - 1; - tsp.moveToPage(table, p); + tsp.moveToPage(table, p, true); tsp.updatePageDisplay(table, c, false); }); } @@ -342,15 +346,20 @@ tsp = ts.pager = { if (wo.pager_countChildRows) { t.push(c.cssChildRow); } regex = new RegExp( '(' + t.join('|') + ')' ); p.$size.add(p.$goto).removeClass(wo.pager_css.disabled).removeAttr('disabled').attr('aria-disabled', 'false'); - p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method if (f && !wo.pager_ajaxUrl) { - p.filteredRows = 0; - $.each(c.cache[0].normalized, function(i, el) { - p.filteredRows += p.regexRows.test(el[c.columns].$row[0].className) ? 0 : 1; - }); + if ($.isEmptyObject(c.cache)) { + // delayInit: true so nothing is in the cache + p.filteredRows = p.totalRows = c.$tbodies.eq(0).children('tr').not( c.widgetOptions.pager_countChildRows ? '' : '.' + c.cssChildRow ).length; + } else { + p.filteredRows = 0; + $.each(c.cache[0].normalized, function(i, el) { + p.filteredRows += p.regexRows.test(el[c.columns].$row[0].className) ? 0 : 1; + }); + } } else if (!f) { p.filteredRows = p.totalRows; } + p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method c.totalRows = p.totalRows; c.filteredRows = p.filteredRows; p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; @@ -494,7 +503,7 @@ tsp = ts.pager = { ts.log('Ajax Error', xhr, exception); } ts.showError(table, exception.message + ' (' + xhr.status + ')'); - c.$tbodies.eq(0).children().detach(); + c.$tbodies.eq(0).children('tr').detach(); p.totalRows = 0; } else { // process ajax object @@ -519,7 +528,7 @@ tsp = ts.pager = { if (d instanceof jQuery) { if (wo.pager_processAjaxOnInit) { // append jQuery object - c.$tbodies.eq(0).children().detach(); + c.$tbodies.eq(0).children('tr').detach(); c.$tbodies.eq(0).append(d); } } else if (l) { @@ -676,14 +685,19 @@ tsp = ts.pager = { l = rows && rows.length || 0, // rows may be undefined s = ( p.page * p.size ), e = p.size; - if ( l < 1 ) { return; } // empty table, abort! + if ( l < 1 ) { + if (c.debug) { + ts.log('Pager: no rows for pager to render'); + } + // empty table, abort! + return; + } if ( p.page >= p.totalPages ) { // lets not render the table more than once return tsp.moveToLastPage(table, p); } p.isDisabled = false; // needed because sorting will change the page and re-enable the pager if (p.initialized) { c.$table.trigger('pagerChange', c); } - if ( !wo.pager_removeRows ) { tsp.hideRows(table, c); } else { @@ -746,8 +760,33 @@ tsp = ts.pager = { }); }, + // updateCache if delayInit: true + // this is normally done by "appendToTable" function in the tablesorter core AFTER a sort + updateCache: function(table) { + var c = table.config, + p = c.pager; + c.$table.trigger('updateCache', [ function(){ + if ( !$.isEmptyObject(table.config.cache) ) { + var i, + rows = [], + n = table.config.cache[0].normalized; + p.totalRows = n.length; + for (i = 0; i < p.totalRows; i++) { + rows.push(n[i][c.columns].$row); + } + c.rowsCopy = rows; + tsp.moveToPage(table, p, true); + // clear out last search to force an update + p.last.currentFilters = [' ']; + } + } ]); + }, + moveToPage: function(table, p, pageMoved) { if ( p.isDisabled ) { return; } + if ( pageMoved !== false && p.initialized && $.isEmptyObject(table.config.cache)) { + return tsp.updateCache(table); + } var c = table.config, l = p.last, pg = Math.min( p.totalPages, p.filteredPages ); @@ -796,17 +835,17 @@ tsp = ts.pager = { $.data(table, 'pagerLastSize', p.size); p.totalPages = Math.ceil( p.totalRows / p.size ); p.filteredPages = Math.ceil( p.filteredRows / p.size ); - tsp.moveToPage(table, p); + tsp.moveToPage(table, p, true); }, moveToFirstPage: function(table, p) { p.page = 0; - tsp.moveToPage(table, p); + tsp.moveToPage(table, p, true); }, moveToLastPage: function(table, p) { p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 ); - tsp.moveToPage(table, p); + tsp.moveToPage(table, p, true); }, moveToNextPage: function(table, p) { @@ -814,7 +853,7 @@ tsp = ts.pager = { if ( p.page >= ( Math.min( p.totalPages, p.filteredPages ) - 1 ) ) { p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 ); } - tsp.moveToPage(table, p); + tsp.moveToPage(table, p, true); }, moveToPrevPage: function(table, p) { @@ -822,7 +861,7 @@ tsp = ts.pager = { if ( p.page <= 0 ) { p.page = 0; } - tsp.moveToPage(table, p); + tsp.moveToPage(table, p, true); }, destroyPager: function(table, c){ @@ -872,7 +911,7 @@ tsp = ts.pager = { p.totalRows = c.widgetOptions.pager_countChildRows ? c.$tbodies.eq(0).children('tr').length : rows.length; p.size = $.data(table, 'pagerLastSize') || p.size || wo.pager_size || 10; p.totalPages = Math.ceil( p.totalRows / p.size ); - tsp.moveToPage(table, p, true); + tsp.moveToPage(table, p); // update display here in case all rows are removed tsp.updatePageDisplay(table, c, false); } else { From 3d773fcd1bb35fe77932cecf8c63ca997827c8ec Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Sun, 3 Aug 2014 23:41:50 +0200 Subject: [PATCH 036/138] * updated tablesorter to latest version (2.17.6) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 33 +++-- .../jquery-tablesorter/jquery.tablesorter.js | 90 +++++++++---- .../jquery.tablesorter.widgets.js | 126 +++++++++++++----- .../parsers/parser-date-extract.js | 1 - .../parsers/parser-input-select.js | 65 +++++++-- .../widgets/widget-editable.js | 84 +++++++----- .../widgets/widget-pager.js | 26 +++- 11 files changed, 318 insertions(+), 117 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a29d1fb..22e9430 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.12.6 (2014-08-03) + +* Upgrade tablesorter to v2.17.6 + #### v1.12.5 (2014-07-20) * Upgrade tablesorter to v2.17.5 diff --git a/README.md b/README.md index 158e5d2..e4ba391 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.17.5 (7/17/2014), [documentation] +Current tablesorter version: 2.17.6 (8/1/2014), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 3bc6be7..c1497f0 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.12.5' + VERSION = '1.12.6' end diff --git a/tablesorter b/tablesorter index e861f6c..232cfe5 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit e861f6c3fcc28c79f385071be11c357245222d53 +Subproject commit 232cfe55a004630659dca038e6c8e4b9be90a074 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 8bcc113..a0239a4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 7/17/2014 (v2.17.5) + * updated 8/1/2014 (v2.17.6) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -129,7 +129,7 @@ }, updatePageDisplay = function(table, p, completed) { - var i, pg, s, out, regex, + var i, pg, s, $out, regex, c = table.config, f = c.$table.hasClass('hasFilters'), t = [], @@ -159,7 +159,7 @@ p.startRow = (t) ? 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1); p.page = (t) ? 0 : p.page; p.endRow = Math.min( p.filteredRows, p.totalRows, p.size * ( p.page + 1 ) ); - out = p.$container.find(p.cssPageDisplay); + $out = p.$container.find(p.cssPageDisplay); // form the output string (can now get a new output string from the server) s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || p.output : p.output ) // {page} = one-based index; {page+#} = zero based index +/- value @@ -168,15 +168,21 @@ }) // {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object) .replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m){ - var str = m.replace(/[{}\s]/g,''), + var len, indx, + str = m.replace(/[{}\s]/g,''), extra = str.split(':'), data = p.ajaxData, // return zero for default page/row numbers deflt = /(rows?|pages?)$/i.test(str) ? 0 : ''; + if (/(startRow|page)/.test(extra[0]) && extra[1] === 'input') { + len = ('' + (extra[0] === 'page' ? p.totalPages : p.totalRows)).length; + indx = extra[0] === 'page' ? p.page + 1 : p.startRow; + return '<input type="text" class="ts-' + extra[0] + '" style="max-width:' + len + 'em" value="' + indx + '"/>'; + } return extra.length > 1 && data && data[extra[0]] ? data[extra[0]][extra[1]] : p[str] || (data ? data[str] : deflt) || deflt; }); - if (out.length) { - out[ (out[0].tagName === 'INPUT') ? 'val' : 'html' ](s); + if ($out.length) { + $out[ ($out[0].tagName === 'INPUT') ? 'val' : 'html' ](s); if ( p.$goto.length ) { t = ''; pg = Math.min( p.totalPages, p.filteredPages ); @@ -185,6 +191,12 @@ } p.$goto.html(t).val( p.page + 1 ); } + // rebind startRow/page inputs + $out.find('.ts-startRow, .ts-page').unbind('change').bind('change', function(){ + var v = $(this).val(), + pg = $(this).hasClass('ts-startRow') ? Math.floor( v/p.size ) + 1 : v; + c.$table.trigger('pageSet.pager', [ pg ]); + }); } } pagerArrows(p); @@ -546,7 +558,7 @@ } } // disable size selector - p.$size.add(p.$goto).each(function(){ + p.$size.add(p.$goto).add(p.$container.find('.ts-startRow, .ts-page')).each(function(){ $(this).attr('aria-disabled', 'true').addClass(p.cssDisabled)[0].disabled = true; }); }, @@ -665,7 +677,10 @@ enablePager = function(table, p, triggered){ var info, c = table.config; - p.$size.add(p.$goto).removeClass(p.cssDisabled).removeAttr('disabled').attr('aria-disabled', 'false'); + p.$size.add(p.$goto).add(p.$container.find('.ts-startRow, .ts-page')) + .removeClass(p.cssDisabled) + .removeAttr('disabled') + .attr('aria-disabled', 'false'); p.isDisabled = false; p.page = $.data(table, 'pagerLastPage') || p.page || 0; p.size = $.data(table, 'pagerLastSize') || parseInt(p.$size.find('option[selected]').val(), 10) || p.size || 10; @@ -827,6 +842,8 @@ // page size selector p.$size = pager.find(p.cssPageSize); if ( p.$size.length ) { + // setting an option as selected appears to cause issues with initial page size + p.$size.find('option').removeAttr('selected'); p.$size.unbind('change.pager').bind('change.pager', function() { p.$size.val( $(this).val() ); // in case there are more than one pagers if ( !$(this).hasClass(p.cssDisabled) ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 5138c44..ed48241 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.17.5 - Client-side table sorting with ease! +* TableSorter 2.17.6 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.17.5"; + ts.version = "2.17.6"; ts.parsers = []; ts.widgets = []; @@ -219,7 +219,7 @@ var c = table.config, // update table bodies in case we start with an empty table tb = c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'), - rows, list, l, i, h, ch, np, p, time, + rows, list, l, i, h, ch, np, p, e, time, j = 0, parsersDebug = "", len = tb.length; @@ -229,7 +229,10 @@ time = new Date(); log('Detecting parsers for each column'); } - list = []; + list = { + extractors: [], + parsers: [] + }; while (j < len) { rows = tb[j].rows; if (rows[j]) { @@ -238,7 +241,8 @@ h = c.$headers.filter('[data-column="' + i + '"]:last'); // get column indexed table cell ch = ts.getColumnData( table, c.headers, i ); - // get column parser + // get column parser/extractor + e = ts.getParserById( ts.getData(h, ch, 'extractor') ); p = ts.getParserById( ts.getData(h, ch, 'sorter') ); np = ts.getData(h, ch, 'parser') === 'false'; // empty cells behaviour - keeping emptyToBottom for backwards compatibility @@ -248,31 +252,38 @@ if (np) { p = ts.getParserById('no-parser'); } + if (!e) { + // For now, maybe detect someday + e = false; + } if (!p) { p = detectParserForColumn(table, rows, -1, i); } if (c.debug) { - parsersDebug += "column:" + i + "; parser:" + p.id + "; string:" + c.strings[i] + '; empty: ' + c.empties[i] + "\n"; + parsersDebug += "column:" + i + "; extractor:" + e.id + "; parser:" + p.id + "; string:" + c.strings[i] + '; empty: ' + c.empties[i] + "\n"; } - list[i] = p; + list.parsers[i] = p; + list.extractors[i] = e; } } - j += (list.length) ? len : 1; + j += (list.parsers.length) ? len : 1; } if (c.debug) { log(parsersDebug ? parsersDebug : "No parsers detected"); benchmark("Completed detecting parsers", time); } - c.parsers = list; + c.parsers = list.parsers; + c.extractors = list.extractors; } /* utils */ function buildCache(table) { - var cc, t, v, i, j, k, $row, rows, cols, cacheTime, + var cc, t, tx, v, i, j, k, $row, rows, cols, cacheTime, totalRows, rowData, colMax, c = table.config, $tb = c.$table.children('tbody'), - parsers = c.parsers; + extractors = c.extractors, + parsers = c.parsers; c.cache = {}; c.totalRows = 0; // if no parsers found, return - it's an empty table. @@ -330,10 +341,16 @@ continue; } t = getElementText(table, $row[0].cells[j], j); + // do extract before parsing if there is one + if (typeof extractors[j].id === 'undefined') { + tx = t; + } else { + tx = extractors[j].format(t, table, $row[0].cells[j], j); + } // allow parsing if the string is empty, previously parsing would change it to zero, // in case the parser needs to extract data from the table cell attributes - v = parsers[j].id === 'no-parser' ? '' : parsers[j].format(t, table, $row[0].cells[j], j); - cols.push(v); + v = parsers[j].id === 'no-parser' ? '' : parsers[j].format(tx, table, $row[0].cells[j], j); + cols.push( c.ignoreCase && typeof v === 'string' ? v.toLowerCase() : v ); if ((parsers[j].type || '').toLowerCase() === "numeric") { // determine column max value (ignore sign) colMax[j] = Math.max(Math.abs(v) || 0, colMax[j] || 0); @@ -379,6 +396,7 @@ if ($bk.length && !$bk.hasClass(c.cssInfoBlock)) { // get tbody $tb = ts.processTbody(table, $bk, true); + $tb.children().detach(); // remove rows n = cc[k].normalized; totalRows = n.length; for (i = 0; i < totalRows; i++) { @@ -423,7 +441,8 @@ c.columns = ts.computeColumnIndex( c.$table.children('thead, tfoot').children('tr') ); // add icon if cssIcon option exists i = c.cssIcon ? '<i class="' + ( c.cssIcon === ts.css.icon ? ts.css.icon : c.cssIcon + ' ' + ts.css.icon ) + '"></i>' : ''; - c.$headers.each(function(index) { + // redefine c.$headers here in case of an updateAll that replaces or adds an entire header cell - see #683 + c.$headers = $(table).find(c.selectorHeaders).each(function(index) { $t = $(this); // make sure to get header cell & not column indexed cell ch = ts.getColumnData( table, c.headers, index, true ); @@ -477,11 +496,13 @@ } function updateHeader(table) { - var s, $th, + var s, $th, col, c = table.config; c.$headers.each(function(index, th){ $th = $(th); - s = ts.getData( th, ts.getColumnData( table, c.headers, index, true ), 'sorter' ) === 'false'; + col = ts.getColumnData( table, c.headers, index, true ); + // add "sorter-false" class if "parser-false" is set + s = ts.getData( th, col, 'sorter' ) === 'false' || ts.getData( th, col, 'parser' ) === 'false'; th.sortDisabled = s; $th[ s ? 'addClass' : 'removeClass' ]('sorter-false').attr('aria-disabled', '' + s); // aria-controls - requires table ID @@ -544,7 +565,7 @@ var colgroup = $('<colgroup>'), overallWidth = $(table).width(); // only add col for visible columns - fixes #371 - $(table.tBodies[0]).find("tr:first").children("td:visible").each(function() { + $(table.tBodies[0]).find("tr:first").children(":visible").each(function() { colgroup.append($('<col>').css('width', parseInt(($(this).width()/overallWidth)*1000, 10)/10 + '%')); }); $(table).prepend(colgroup); @@ -601,6 +622,10 @@ } function initSort(table, cell, event){ + if (table.isUpdating) { + // let any updates complete before initializing a sort + return setTimeout(function(){ initSort(table, cell, event); }, 50); + } var arry, indx, col, order, s, c = table.config, key = !event[c.sortMultiSortKey], @@ -833,7 +858,7 @@ table.isUpdating = true; $table.find(c.selectorRemove).remove(); // get position from the dom - var v, row, icell, + var v, t, row, icell, $tb = $table.find('tbody'), $cell = $(cell), // update cache - format: function(s, table, cell, cellIndex) @@ -846,8 +871,14 @@ row = $tb.eq(tbdy).find('tr').index( $row ); icell = $cell.index(); c.cache[tbdy].normalized[row][c.columns].$row = $row; - v = c.cache[tbdy].normalized[row][icell] = c.parsers[icell].id === 'no-parser' ? '' : - c.parsers[icell].format( getElementText(table, cell, icell), table, cell, icell ); + if (typeof c.extractors[icell].id === 'undefined') { + t = getElementText(table, cell, icell); + } else { + t = c.extractors[icell].format( getElementText(table, cell, icell), table, cell, icell ); + } + v = c.parsers[icell].id === 'no-parser' ? '' : + c.parsers[icell].format( t, table, cell, icell ); + c.cache[tbdy].normalized[row][icell] = c.ignoreCase && typeof v === 'string' ? v.toLowerCase() : v; if ((c.parsers[icell].type || '').toLowerCase() === "numeric") { // update column max value (ignore sign) c.cache[tbdy].colMax[icell] = Math.max(Math.abs(v) || 0, c.cache[tbdy].colMax[icell] || 0); @@ -863,8 +894,8 @@ updateHeader(table); commonUpdate(table, resort, callback); } else { - $row = $($row); // make sure we're using a jQuery object - var i, j, l, rowData, cells, + $row = $($row).attr('role', 'row'); // make sure we're using a jQuery object + var i, j, l, t, v, rowData, cells, rows = $row.filter('tr').length, tbdy = $table.find('tbody').index( $row.parents('tbody').filter(':first') ); // fixes adding rows to an empty table - see issue #179 @@ -882,8 +913,14 @@ }; // add each cell for (j = 0; j < l; j++) { - cells[j] = c.parsers[j].id === 'no-parser' ? '' : - c.parsers[j].format( getElementText(table, $row[i].cells[j], j), table, $row[i].cells[j], j ); + if (typeof c.extractors[j].id === 'undefined') { + t = getElementText(table, $row[i].cells[j], j); + } else { + t = c.extractors[j].format( getElementText(table, $row[i].cells[j], j), table, $row[i].cells[j], j ); + } + v = c.parsers[j].id === 'no-parser' ? '' : + c.parsers[j].format( t, table, $row[i].cells[j], j ); + cells[j] = c.ignoreCase && typeof v === 'string' ? v.toLowerCase() : v; if ((c.parsers[j].type || '').toLowerCase() === "numeric") { // update column max value (ignore sign) c.cache[tbdy].colMax[j] = Math.max(Math.abs(cells[j]) || 0, c.cache[tbdy].colMax[j] || 0); @@ -1019,8 +1056,8 @@ c.table = table; c.$table = $table .addClass(ts.css.table + ' ' + c.tableClass + k) - .attr({ role : 'grid'}); - c.$headers = $(table).find(c.selectorHeaders); + .attr('role', 'grid'); + c.$headers = $table.find(c.selectorHeaders); // give the table a unique id, which will be used in namespace binding if (!c.namespace) { @@ -1030,6 +1067,7 @@ c.namespace = '.' + c.namespace.replace(/\W/g,''); } + c.$table.children().children('tr').attr('role', 'row'); c.$tbodies = $table.children('tbody:not(.' + c.cssInfoBlock + ')').attr({ 'aria-live' : 'polite', 'aria-relevant' : 'all' diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index fe16343..e5ab03e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.16+ widgets - updated 7/17/2014 (v2.17.5) +/*! tableSorter 2.16+ widgets - updated 8/1/2014 (v2.17.6) * * Column Styles * Column Filters @@ -131,6 +131,7 @@ ts.storage = function(table, key, value, options) { // Add a resize event to table headers // ************************** ts.addHeaderResizeEvent = function(table, disable, settings) { + table = $(table)[0]; // make sure we're usig a dom element var headers, defaults = { timer : 250 @@ -371,7 +372,8 @@ ts.addWidget({ filter_startsWith : false, // if true, filter start from the beginning of the cell contents filter_useParsedData : false, // filter all data using parsed content filter_serversideFiltering : false, // if true, server-side filtering should be performed because client-side filtering will be disabled, but the ui and events will still be used. - filter_defaultAttrib : 'data-value' // data attribute in the header cell that contains the default filter value + filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value + filter_selectSourceSeparator : '|' // filter_selectSource array text left of the separator is added to the option value, right into the option text }, format: function(table, c, wo) { if (!c.$table.hasClass('hasFilters')) { @@ -565,7 +567,7 @@ ts.filter = { and : 'and' }, ts.language); - var options, string, $header, column, filters, time, fxn, + var options, string, txt, $header, column, filters, val, time, fxn, noSelect, regex = ts.filter.regex; if (c.debug) { time = new Date(); @@ -590,7 +592,7 @@ ts.filter = { }); // don't build filter row if columnFilters is false or all columns are set to "filter-false" - issue #156 - if (wo.filter_columnFilters !== false && c.$headers.filter('.filter-false').length !== c.$headers.length) { + if (wo.filter_columnFilters !== false && c.$headers.filter('.filter-false, .parser-false').length !== c.$headers.length) { // build filter row ts.filter.buildRow(table, c, wo); } @@ -642,17 +644,27 @@ ts.filter = { for (column = 0; column < c.columns; column++) { fxn = ts.getColumnData( table, wo.filter_functions, column ); if (fxn) { - $header = c.$headers.filter('[data-column="' + column + '"]:last'); + // remove "filter-select" from header otherwise the options added here are replaced with all options + $header = c.$headers.filter('[data-column="' + column + '"]:last').removeClass('filter-select'); + // don't build select if "filter-false" or "parser-false" set + noSelect = !($header.hasClass('filter-false') || $header.hasClass('parser-false')); options = ''; - if (fxn === true && !$header.hasClass('filter-false')) { + if ( fxn === true && noSelect ) { ts.filter.buildSelect(table, column); - } else if (typeof fxn === 'object' && !$header.hasClass('filter-false')) { + } else if ( typeof fxn === 'object' && noSelect ) { // add custom drop down list for (string in fxn) { if (typeof string === 'string') { options += options === '' ? '<option value="">' + ($header.data('placeholder') || $header.attr('data-placeholder') || wo.filter_placeholder.select || '') + '</option>' : ''; - options += '<option value="' + string + '">' + string + '</option>'; + val = string; + txt = string; + if (string.indexOf(wo.filter_selectSourceSeparator) >= 0) { + val = string.split(wo.filter_selectSourceSeparator); + txt = val[1]; + val = val[0]; + } + options += '<option ' + (txt === val ? '' : 'data-function-name="' + string + '" ') + 'value="' + val + '">' + txt + '</option>'; } } c.$table.find('thead').find('select.' + ts.css.filter + '[data-column="' + column + '"]').append(options); @@ -691,7 +703,7 @@ ts.filter = { ts.benchmark("Applying Filter widget", time); } // add default values - c.$table.bind('tablesorter-initialized pagerInitialized', function(e) { + c.$table.bind('tablesorter-initialized pagerInitialized', function() { // redefine "wo" as it does not update properly inside this callback var wo = this.config.widgetOptions; filters = ts.filter.setDefaults(table, c, wo) || []; @@ -775,10 +787,10 @@ ts.filter = { filter; }, buildRow: function(table, c, wo) { - var column, $header, buildSelect, disabled, name, ffxn, + var col, column, $header, buildSelect, disabled, name, ffxn, // c.columns defined in computeThIndexes() columns = c.columns, - buildFilter = '<tr class="' + ts.css.filterRow + '">'; + buildFilter = '<tr role="row" class="' + ts.css.filterRow + '">'; for (column = 0; column < columns; column++) { buildFilter += '<td></td>'; } @@ -792,7 +804,8 @@ ts.filter = { buildSelect = (wo.filter_functions && ffxn && typeof ffxn !== "function" ) || $header.hasClass('filter-select'); // get data from jQuery data, metadata, headers option or header class name - disabled = ts.getData($header[0], ts.getColumnData( table, c.headers, column ), 'filter') === 'false'; + col = ts.getColumnData( table, c.headers, column ); + disabled = ts.getData($header[0], col, 'filter') === 'false' || ts.getData($header[0], col, 'parser') === 'false'; if (buildSelect) { buildFilter = $('<select>').appendTo( c.$filters.eq(column) ); @@ -983,7 +996,7 @@ ts.filter = { var cached, len, $rows, rowIndex, tbodyIndex, $tbody, $cells, columnIndex, childRow, childRowText, exact, iExact, iFilter, lastSearch, matches, result, notFiltered, searchFiltered, filterMatched, showRow, time, val, indx, - anyMatch, iAnyMatch, rowArray, rowText, iRowText, rowCache, fxn, + anyMatch, iAnyMatch, rowArray, rowText, iRowText, rowCache, fxn, ffxn, regex = ts.filter.regex, c = table.config, wo = c.widgetOptions, @@ -1098,7 +1111,16 @@ ts.filter = { if (filterMatched !== null) { showRow = filterMatched; } else { - showRow = (iRowText + childRowText).indexOf(iAnyMatch) >= 0; + if (wo.filter_startsWith) { + showRow = false; + columnIndex = columns; + while (!showRow && columnIndex > 0) { + columnIndex--; + showRow = showRow || rowArray[columnIndex].indexOf(iAnyMatch) === 0; + } + } else { + showRow = (iRowText + childRowText).indexOf(iAnyMatch) >= 0; + } } } @@ -1117,6 +1139,9 @@ ts.filter = { iExact = !regex.type.test(typeof exact) && wo.filter_ignoreCase ? exact.toLocaleLowerCase() : exact; result = showRow; // if showRow is true, show that row + // in case select filter option has a different value vs text "a - z|A through Z" + ffxn = c.$filters.eq(columnIndex).find('select option:selected').attr('data-function-name') || ''; + // replace accents - see #357 filters[columnIndex] = c.sortLocaleCompare ? ts.replaceAccents(filters[columnIndex]) : filters[columnIndex]; // val = case insensitive, filters[columnIndex] = case sensitive @@ -1130,9 +1155,9 @@ ts.filter = { } else if (typeof fxn === 'function') { // filter callback( exact cell content, parser normalized content, filter input value, column index, jQuery row object ) result = fxn(exact, cached, filters[columnIndex], columnIndex, $rows.eq(rowIndex)); - } else if (typeof fxn[filters[columnIndex]] === 'function') { + } else if (typeof fxn[ffxn || filters[columnIndex]] === 'function') { // selector option function - result = fxn[filters[columnIndex]](exact, cached, filters[columnIndex], columnIndex, $rows.eq(rowIndex)); + result = fxn[ffxn || filters[columnIndex]](exact, cached, filters[columnIndex], columnIndex, $rows.eq(rowIndex)); } } else { filterMatched = null; @@ -1189,12 +1214,22 @@ ts.filter = { parsed = [], arry = false, source = wo.filter_selectSource, + last = c.$table.data('lastSearch') || [], fxn = $.isFunction(source) ? true : ts.getColumnData( table, source, column ); + if (onlyAvail && last[column] !== '') { + onlyAvail = false; + } + // filter select source option if (fxn === true) { // OVERALL source arry = source(table, column, onlyAvail); + } else if ( fxn instanceof $ || ($.type(fxn) === 'string' && fxn.indexOf('</option>') >= 0) ) { + // selectSource is a jQuery object or string of options + return fxn; + } else if ($.isArray(fxn)) { + arry = fxn; } else if ($.type(source) === 'object' && fxn) { // custom select source function for a SPECIFIC COLUMN arry = fxn(table, column, onlyAvail); @@ -1278,46 +1313,73 @@ ts.filter = { } return arry; }, - buildSelect: function(table, column, updating, onlyAvail) { - if (!table.config.cache || $.isEmptyObject(table.config.cache)) { return; } + buildSelect: function(table, column, arry, updating, onlyAvail) { + table = $(table)[0]; column = parseInt(column, 10); - var indx, txt, $filters, + if (!table.config.cache || $.isEmptyObject(table.config.cache)) { return; } + var indx, val, txt, t, $filters, $filter, c = table.config, wo = c.widgetOptions, node = c.$headers.filter('[data-column="' + column + '"]:last'), // t.data('placeholder') won't work in jQuery older than 1.4.3 options = '<option value="">' + ( node.data('placeholder') || node.attr('data-placeholder') || wo.filter_placeholder.select || '' ) + '</option>', - arry = ts.filter.getOptionSource(table, column, onlyAvail), // Get curent filter value currentValue = c.$table.find('thead').find('select.' + ts.css.filter + '[data-column="' + column + '"]').val(); + // nothing included in arry (external source), so get the options from filter_selectSource or column data + if (typeof arry === 'undefined' || arry === '') { + arry = ts.filter.getOptionSource(table, column, onlyAvail); + } - // build option list - for (indx = 0; indx < arry.length; indx++) { - txt = arry[indx].replace(/\"/g, """); - // replace quotes - fixes #242 & ignore empty strings - see http://stackoverflow.com/q/14990971/145346 - options += arry[indx] !== '' ? '<option value="' + txt + '"' + (currentValue === txt ? ' selected="selected"' : '') + - '>' + arry[indx] + '</option>' : ''; + if ($.isArray(arry)) { + // build option list + for (indx = 0; indx < arry.length; indx++) { + txt = arry[indx] = ('' + arry[indx]).replace(/\"/g, """); + val = txt; + // allow including a symbol in the selectSource array + // "a-z|A through Z" so that "a-z" becomes the option value + // and "A through Z" becomes the option text + if (txt.indexOf(wo.filter_selectSourceSeparator) >= 0) { + t = txt.split(wo.filter_selectSourceSeparator); + val = t[0]; + txt = t[1]; + } + // replace quotes - fixes #242 & ignore empty strings - see http://stackoverflow.com/q/14990971/145346 + options += arry[indx] !== '' ? '<option ' + (val === txt ? '' : 'data-function-name="' + arry[indx] + '" ') + 'value="' + val + '">' + txt + '</option>' : ''; + } + // clear arry so it doesn't get appended twice + arry = []; } + // update all selects in the same column (clone thead in sticky headers & any external selects) - fixes 473 $filters = ( c.$filters ? c.$filters : c.$table.children('thead') ).find('.' + ts.css.filter); if (wo.filter_$externalFilters) { $filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters; } - $filters.filter('select[data-column="' + column + '"]')[ updating ? 'html' : 'append' ](options); - if (!wo.filter_functions) { wo.filter_functions = {}; } - wo.filter_functions[column] = true; + $filter = $filters.filter('select[data-column="' + column + '"]'); + + // make sure there is a select there! + if ($filter.length) { + $filter[ updating ? 'html' : 'append' ](options); + if (!$.isArray(arry)) { + // append options if arry is provided externally as a string or jQuery object + // options (default value) was already added + $filter.append(arry).val(currentValue); + } + $filter.val(currentValue); + } }, buildDefault: function(table, updating) { - var columnIndex, $header, + var columnIndex, $header, noSelect, c = table.config, wo = c.widgetOptions, columns = c.columns; // build default select dropdown for (columnIndex = 0; columnIndex < columns; columnIndex++) { $header = c.$headers.filter('[data-column="' + columnIndex + '"]:last'); + noSelect = !($header.hasClass('filter-false') || $header.hasClass('parser-false')); // look for the filter-select class; build/update it if found - if (($header.hasClass('filter-select') || ts.getColumnData( table, wo.filter_functions, columnIndex ) === true) && !$header.hasClass('filter-false')) { - ts.filter.buildSelect(table, columnIndex, updating, $header.hasClass(wo.filter_onlyAvail)); + if (($header.hasClass('filter-select') || ts.getColumnData( table, wo.filter_functions, columnIndex ) === true) && noSelect) { + ts.filter.buildSelect(table, columnIndex, '', updating, $header.hasClass(wo.filter_onlyAvail)); } } } diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js index 05c8bcb..3a46110 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js @@ -71,7 +71,6 @@ var date = s.replace(/\s+/g," ").replace(/[\-.,]/g, "/").match(/(\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2}(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?)/i); if (date) { date = date[0].replace(/(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/, "$2/$3/$1"); - console.log(date); return $.tablesorter.formatFloat((new Date(date).getTime() || ''), table) || s; } return s; diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index 97e37a8..5e57556 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,5 +1,5 @@ /*! input & select parsers for jQuery 1.7+ & tablesorter 2.7.11+ - * Updated 7/17/2014 (v2.17.5) + * Updated 8/1/2014 (v2.17.6) * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ /*jshint browser: true, jquery:true, unused:false */ @@ -78,6 +78,20 @@ type: "text" }); + // Custom parser for parsing textarea values + // updated dynamically using the "change" function below + $.tablesorter.addParser({ + id: "textarea", + is: function(){ + return false; + }, + format: function(s, table, cell) { + return $(cell).find('textarea').val() || s; + }, + parsed : true, // filter widget flag + type: "text" + }); + // update select and all input types in the tablesorter cache when the change event fires. // This method only works with jQuery 1.7+ // you can change it to use delegate (v1.4.3+) or live (v1.3+) as desired @@ -86,10 +100,36 @@ $('table').on('tablesorter-initialized', function(){ // this flag prevents the updateCell event from being spammed // it happens when you modify input text and hit enter - var alreadyUpdating = false; + var focused = false, + restoreValue = function(){ + // focused = false; // uncomment this line to prevent auto-accepting changes + // make sure we restore original values + $(':focus').blur(); + return; + }; // bind to .tablesorter (default class name) - $(this).children('tbody').on('change', 'select, input', function(e){ - if (!alreadyUpdating) { + $(this).children('tbody') + .on('mouseleave', function(){ + restoreValue(); + }) + .on('focus', 'select, input, textarea', function(){ + focused = true; + $(this).data('ts-original-value', this.value); + }) + .on('blur', 'input, textarea', function(){ + // restore input value; + // "change" is triggered before "blur" so this doesn't replace the new update with the original + this.value = $(this).data('ts-original-value'); + }) + .on('change keyup', 'select, input, textarea', function(e){ + if ( e.which === 27 ) { + // escape: restore original value + this.value = $(this).data('ts-original-value'); + return; + } + // Update cell cache using... select: change, input: enter or textarea: alt + enter + if ( ( e.type === 'change' && focused ) || + ( e.type === 'keyup' && e.which === 13 && ( e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' && e.altKey ) ) ) { var $tar = $(e.target), $cell = $tar.closest('td'), $table = $cell.closest('table'), @@ -98,14 +138,17 @@ $hdr = c && c.$headers && c.$headers.eq(indx); // abort if not a tablesorter table, or // don't use updateCell if column is set to "sorter-false" and "filter-false", or column is set to "parser-false" - if ( !c || ( $hdr && $hdr.length && ( $hdr.hasClass('parser-false') || ($hdr.hasClass('sorter-false') && $hdr.hasClass('filter-false')) ) ) ){ - return false; + if ( !c || ( $hdr && $hdr.length && ( $hdr.hasClass('parser-false') || ( $hdr.hasClass('sorter-false') && $hdr.hasClass('filter-false') ) ) ) ) { + return restoreValue(); + } + // ignore change event if nothing changed + if ($tar.val() !== $tar.data('ts-original-value')) { + $tar.data('ts-original-value', $tar.val()); + $table.trigger('updateCell', [ $tar.closest('td'), resort, function(){ + updateServer(e, $table, $tar); + setTimeout(function(){ focused = false; }, 10); + } ]); } - alreadyUpdating = true; - $table.trigger('updateCell', [ $tar.closest('td'), resort, function(){ - updateServer(e, $table, $tar); - setTimeout(function(){ alreadyUpdating = false; }, 10); - } ]); } }); }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js index 44b95ef..d63ff86 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js @@ -1,4 +1,4 @@ -/*! tablesorter Editable Content widget - updated 1/24/2014 (core v2.15.0) +/*! tablesorter Editable Content widget - updated 8/1/2014 (core v2.17.6) * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -12,76 +12,100 @@ options : { editable_columns : [], editable_enterToAccept : true, + editable_autoAccept : true, editable_autoResort : false, + editable_validate : null, // function(text, originalText){ return text; } editable_noEdit : 'no-edit', editable_editComplete : 'editComplete' }, init: function(table, thisWidget, c, wo){ - if (!wo.editable_columns.length) { return; } - var indx, tmp, $t, cols = []; - if (wo.editable_columns.indexOf('-') >= 0) { + if ( !wo.editable_columns.length ) { return; } + var indx, tmp, $t, + cols = []; + if ( $.type(wo.editable_columns) === "string" && wo.editable_columns.indexOf('-') >= 0 ) { // editable_columns can contain a range string (i.e. "2-4" ) tmp = wo.editable_columns.split('-'); indx = parseInt(tmp[0],10) || 0; tmp = parseInt(tmp[1],10) || (c.columns - 1); - if (tmp > c.columns) { tmp = c.columns - 1; } - for (; indx <= tmp; indx++) { + if ( tmp > c.columns ) { tmp = c.columns - 1; } + for ( ; indx <= tmp; indx++ ) { cols.push('td:nth-child(' + (indx + 1) + ')'); } - } else if ($.isArray(wo.editable_columns)) { + } else if ( $.isArray(wo.editable_columns) ) { $.each(wo.editable_columns, function(i, col){ - cols.push('td:nth-child(' + (col + 1) + ')'); + if ( col < c.columns ) { + cols.push('td:nth-child(' + (col + 1) + ')'); + } }); } // IE does not allow making TR/TH/TD cells directly editable (issue #404) // so add a div or span inside ( it's faster than using wrapInner() ) - c.$tbodies.find( cols.join(',') ).not('.' + wo.editable_noEdit).each(function(){ + c.$tbodies.find( cols.join(',') ).not( '.' + wo.editable_noEdit ).each(function(){ // test for children, if they exist, then make the children editable $t = $(this); - ( $t.children().length ? $t.children() : $t ).prop('contenteditable', true); + ( $t.children().length ? $t.children() : $t ).prop( 'contenteditable', true ); }); c.$tbodies .on('mouseleave.tseditable', function(){ - if (c.$table.data('contentFocused')) { + if ( c.$table.data('contentFocused') ) { + // change to "true" instead of element to allow focusout to process + c.$table.data( 'contentFocused', true ); $(':focus').trigger('blur'); } }) - .on('focus.tseditable', '[contenteditable]', function(){ - c.$table.data('contentFocused', true); - var $this = $(this), v = $this.html(); + .on('focus.tseditable', '[contenteditable]', function(e){ + c.$table.data( 'contentFocused', e.target ); + var $this = $(this), + v = $this.html(); if (wo.editable_enterToAccept) { // prevent enter from adding into the content $this.on('keydown.tseditable', function(e){ - if (e.which === 13) { + if ( e.which === 13 ) { e.preventDefault(); } }); } $this.data({ before : v, original: v }); }) - .on('blur focusout keyup '.split(' ').join('.tseditable '), '[contenteditable]', function(e){ - if (!c.$table.data('contentFocused')) { return; } - var $this = $(e.target), t; - if (e.which === 27) { + .on('blur focusout keydown '.split(' ').join('.tseditable '), '[contenteditable]', function(e){ + if ( !c.$table.data('contentFocused') ) { return; } + var t, + valid = false, + $this = $(e.target); + if ( e.which === 27 ) { // user cancelled $this.html( $this.data('original') ).trigger('blur.tseditable'); - c.$table.data('contentFocused', false); + c.$table.data( 'contentFocused', false ); return false; } - t = e.type !== 'keyup' || (wo.editable_enterToAccept && e.which === 13); + t = e.which === 13 && ( wo.editable_enterToAccept || e.altKey ) || wo.editable_autoAccept && e.type !== 'keydown'; // change if new or user hits enter (if option set) - if ($this.data('before') !== $this.html() || t) { - $this.data('before', $this.html()).trigger('change'); - if (t) { - c.$table - .data('contentFocused', false) - .trigger('updateCell', [ $this.closest('td'), wo.editable_autoResort, function(table){ - $this.trigger( wo.editable_editComplete ); + if ( t && $this.data('before') !== $this.html() ) { + valid = $.isFunction(wo.editable_validate) ? wo.editable_validate( $this.html(), $this.data('original') ) : $this.html(); + if ( t && valid !== false ) { + $this + .html( valid ) + .data('before', valid) + .trigger('change'); + c.$table.trigger('updateCell', [ $this.closest('td'), wo.editable_autoResort, function(table){ + $this.trigger( wo.editable_editComplete, [c] ); + $this.data( 'original', $this.html() ); + if ( wo.editable_autoResort && c.sortList.length ) { c.$table.trigger('applyWidgets'); - } ]); - $this.trigger('blur.tseditable'); + } + // restore focus last cell after updating + setTimeout(function(){ + var t = c.$table.data('contentFocused'); + if ( t instanceof HTMLElement ) { t.focus(); } + }, 50); + } ]); + return false; } } + if ( !valid && e.type !== 'keydown' ) { + // restore original content on blur + $this.html( $this.data('original') ); + } }); } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 81626f4..ce609ac 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/* Pager widget for TableSorter 7/17/2014 (v2.17.5) */ +/* Pager widget for TableSorter 8/1/2014 (v2.17.6) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ "use strict"; @@ -306,6 +306,8 @@ tsp = ts.pager = { } if ( p.$size.length ) { + // setting an option as selected appears to cause issues with initial page size + p.$size.find('option').removeAttr('selected'); p.$size .unbind('change.pager') .bind('change.pager', function() { @@ -336,7 +338,7 @@ tsp = ts.pager = { }, updatePageDisplay: function(table, c, completed) { - var i, pg, s, out, regex, + var i, pg, s, $out, regex, wo = c.widgetOptions, p = c.pager, f = c.$table.hasClass('hasFilters'), @@ -368,7 +370,7 @@ tsp = ts.pager = { p.startRow = (t) ? 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1); p.page = (t) ? 0 : p.page; p.endRow = Math.min( p.filteredRows, p.totalRows, p.size * ( p.page + 1 ) ); - out = p.$container.find(wo.pager_selectors.pageDisplay); + $out = p.$container.find(wo.pager_selectors.pageDisplay); // form the output string (can now get a new output string from the server) s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || wo.pager_output : wo.pager_output ) // {page} = one-based index; {page+#} = zero based index +/- value @@ -377,15 +379,21 @@ tsp = ts.pager = { }) // {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object) .replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m){ - var str = m.replace(/[{}\s]/g,''), + var len, indx, + str = m.replace(/[{}\s]/g,''), extra = str.split(':'), data = p.ajaxData, // return zero for default page/row numbers deflt = /(rows?|pages?)$/i.test(str) ? 0 : ''; + if (/(startRow|page)/.test(extra[0]) && extra[1] === 'input') { + len = ('' + (extra[0] === 'page' ? p.totalPages : p.totalRows)).length; + indx = extra[0] === 'page' ? p.page + 1 : p.startRow; + return '<input type="text" class="ts-' + extra[0] + '" style="max-width:' + len + 'em" value="' + indx + '"/>'; + } return extra.length > 1 && data && data[extra[0]] ? data[extra[0]][extra[1]] : p[str] || (data ? data[str] : deflt) || deflt; }); - if (out.length) { - out[ (out[0].tagName === 'INPUT') ? 'val' : 'html' ](s); + if ($out.length) { + $out[ ($out[0].tagName === 'INPUT') ? 'val' : 'html' ](s); if ( p.$goto.length ) { t = ''; pg = Math.min( p.totalPages, p.filteredPages ); @@ -394,6 +402,12 @@ tsp = ts.pager = { } p.$goto.html(t).val( p.page + 1 ); } + // rebind startRow/page inputs + $out.find('.ts-startRow, .ts-page').unbind('change').bind('change', function(){ + var v = $(this).val(), + pg = $(this).hasClass('ts-startRow') ? Math.floor( v/p.size ) + 1 : v; + c.$table.trigger('pageSet.pager', [ pg ]); + }); } } tsp.pagerArrows(c); From df33fb4b41729acd7492c739a9a44cb792a11ced Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Sun, 10 Aug 2014 21:32:00 +0200 Subject: [PATCH 037/138] * updated tablesorter to latest version (2.17.7) --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../javascripts/jquery-tablesorter/jquery.tablesorter.js | 5 ++--- .../jquery-tablesorter/jquery.tablesorter.widgets.js | 5 +++-- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22e9430..a81a30b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.12.7 (2014-08-10) + +* Upgrade tablesorter to v2.17.7 + #### v1.12.6 (2014-08-03) * Upgrade tablesorter to v2.17.6 diff --git a/README.md b/README.md index e4ba391..b45b055 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.17.6 (8/1/2014), [documentation] +Current tablesorter version: 2.17.7 (8/9/2014), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index c1497f0..124cedd 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.12.6' + VERSION = '1.12.7' end diff --git a/tablesorter b/tablesorter index 232cfe5..c1ce076 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 232cfe55a004630659dca038e6c8e4b9be90a074 +Subproject commit c1ce0768d76d3631bb5a5b838e1ee8d4103f5f73 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index ed48241..dceaeb7 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.17.6 - Client-side table sorting with ease! +* TableSorter 2.17.7 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.17.6"; + ts.version = "2.17.7"; ts.parsers = []; ts.widgets = []; @@ -396,7 +396,6 @@ if ($bk.length && !$bk.hasClass(c.cssInfoBlock)) { // get tbody $tb = ts.processTbody(table, $bk, true); - $tb.children().detach(); // remove rows n = cc[k].normalized; totalRows = n.length; for (i = 0; i < totalRows; i++) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index e5ab03e..f96c7d0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.16+ widgets - updated 8/1/2014 (v2.17.6) +/*! tableSorter 2.16+ widgets - updated 8/9/2014 (v2.17.7) * * Column Styles * Column Filters @@ -1140,7 +1140,8 @@ ts.filter = { result = showRow; // if showRow is true, show that row // in case select filter option has a different value vs text "a - z|A through Z" - ffxn = c.$filters.eq(columnIndex).find('select option:selected').attr('data-function-name') || ''; + ffxn = wo.filter_columnFilters ? + c.$filters.add(c.$externalFilters).filter('[data-column="'+ columnIndex + '"]').find('select option:selected').attr('data-function-name') || '' : ''; // replace accents - see #357 filters[columnIndex] = c.sortLocaleCompare ? ts.replaceAccents(filters[columnIndex]) : filters[columnIndex]; From 060050e0c5776c22a549df569943ed0f803f5f43 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Wed, 17 Sep 2014 19:20:02 +0200 Subject: [PATCH 038/138] * updated tablesorter to latest version (2.17.8) --- CHANGELOG.md | 5 + README.md | 2 +- jquery-tablesorter.gemspec | 6 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 61 +++- .../jquery-tablesorter/jquery.tablesorter.js | 71 ++-- .../jquery.tablesorter.widgets.js | 334 +++++++++++------- .../parsers/parser-duration.js | 40 +++ .../parsers/parser-ignore-articles.js | 32 +- .../parsers/parser-input-select.js | 14 +- .../widgets/widget-editable.js | 139 ++++++-- .../widgets/widget-pager.js | 31 +- 13 files changed, 509 insertions(+), 230 deletions(-) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/parsers/parser-duration.js diff --git a/CHANGELOG.md b/CHANGELOG.md index a81a30b..3d80b9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ Changelog === +#### v1.12.8 (2014-09-17) + +* Upgrade tablesorter to v2.17.8 +* Set required Ruby version to >= 1.9.3 + #### v1.12.7 (2014-08-10) * Upgrade tablesorter to v2.17.7 diff --git a/README.md b/README.md index b45b055..80723a7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.17.7 (8/9/2014), [documentation] +Current tablesorter version: 2.17.8 (9/15/2014), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/jquery-tablesorter.gemspec b/jquery-tablesorter.gemspec index b1d9955..579195e 100644 --- a/jquery-tablesorter.gemspec +++ b/jquery-tablesorter.gemspec @@ -10,11 +10,13 @@ Gem::Specification.new do |s| s.authors = ['Jun Lin', 'Erik-B. Ernst'] s.email = ['github@black-milk.de'] s.homepage = 'https://github.com/themilkman/jquery-tablesorter-rails' - s.summary = 'Simple integration of jquery-tablesorter into the asset pipeline.' - s.description = 'Simple integration of jquery-tablesorter into the asset pipeline.' + s.summary = "Simple integration of jquery-tablesorter (Mottie's fork) into the Rails asset pipeline." + s.description = "Simple integration of jquery-tablesorter (Mottie's fork) into the Rails asset pipeline." s.license = 'MIT' s.files = Dir['{vendor,lib}/**/*'] + %w( MIT-LICENSE Rakefile README.md ) + s.required_ruby_version = '>= 1.9.3' + s.add_dependency 'railties', '>= 3.1', '< 5' end diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 124cedd..3739176 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.12.7' + VERSION = '1.12.8' end diff --git a/tablesorter b/tablesorter index c1ce076..db8878a 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit c1ce0768d76d3631bb5a5b838e1ee8d4103f5f73 +Subproject commit db8878ac83d1fbbcc22ef24587a17e41b0e7ff39 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index a0239a4..08047ff 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 8/1/2014 (v2.17.6) + * updated 9/15/2014 (v2.17.8) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -58,13 +58,16 @@ // starting page of the pager (zero based index) page: 0, - // reset pager after filtering; set to desired page # - // set to false to not change page at filter start + // reset pager after filtering; set to desired page # + // set to false to not change page at filter start pageReset: 0, // Number of visible rows size: 10, + // Number of options to include in the pager number selector + maxOptionSize: 20, + // Save pager page & size if the storage script is loaded (requires $.tablesorter.storage in jquery.tablesorter.widgets.js) savePages: true, @@ -186,10 +189,37 @@ if ( p.$goto.length ) { t = ''; pg = Math.min( p.totalPages, p.filteredPages ); - for ( i = 1; i <= pg; i++ ) { - t += '<option>' + i + '</option>'; + // Filter the options page number link array if it's larger than 'maxOptionSize' + // as large page set links will slow the browser on large dom inserts + var skip_set_size = Math.floor(pg / p.maxOptionSize), + large_collection = pg > p.maxOptionSize, + current_page = p.page + 1, + start_page = 1, + end_page = pg, + option_pages = []; + //construct default options pages array + var option_pages_start_page = (large_collection && current_page == 1) ? skip_set_size : 1; + for (i = option_pages_start_page; i <= pg;) { + option_pages.push(i); + i = large_collection ? i + skip_set_size : i++; + } + if (large_collection) { + var central_focus_size = Math.floor(p.maxOptionSize / 2) - 1, + lower_focus_window = Math.abs(Math.floor(current_page - central_focus_size/2)), + focus_option_pages = []; + start_page = Math.min(current_page, lower_focus_window); + end_page = start_page + central_focus_size; + //construct an array to get a focus set around the current page + for (i = start_page; i <= end_page ; i++) focus_option_pages.push(i); + var insert_index = Math.floor(option_pages.length / 2) - Math.floor(focus_option_pages.length / 2); + Array.prototype.splice.apply(option_pages, [ insert_index, focus_option_pages.length ].concat(focus_option_pages)); + option_pages.sort(function sortNumber(a,b) { return a - b; }); + } + for ( i = 0; i < option_pages.length; i++) { + t += '<option>' + option_pages[i] + '</option>'; } - p.$goto.html(t).val( p.page + 1 ); + p.$goto[0].innerHTML = t; + p.$goto[0].value = current_page; } // rebind startRow/page inputs $out.find('.ts-startRow, .ts-page').unbind('change').bind('change', function(){ @@ -532,9 +562,8 @@ } updatePageDisplay(table, p); if ( !p.isDisabled ) { fixHeight(table, p); } - $t.trigger('applyWidgets'); if (table.isUpdating) { - $t.trigger("updateComplete", table); + $t.trigger('updateComplete', [ table, true ]); } }, @@ -553,6 +582,7 @@ .removeAttr('aria-describedby') .find('tr.pagerSavedHeightSpacer').remove(); renderTable(table, table.config.rowsCopy, p); + $(table).trigger('applyWidgets'); if (table.config.debug) { ts.log('pager disabled'); } @@ -621,7 +651,7 @@ .trigger('pageMoved', p) .trigger('applyWidgets'); if (table.isUpdating) { - $t.trigger('updateComplete'); + $t.trigger('updateComplete', [ table, true ]); } } }, @@ -751,7 +781,7 @@ p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.replace(/^(\w+\.)/g,'') + '|' + c.cssChildRow + ')'); $t - .unbind('filterStart filterEnd sortEnd disable enable destroy update updateRows updateAll addRows pageSize '.split(' ').join('.pager ')) + .unbind('filterStart filterEnd sortEnd disable enable destroy updateComplete pageSize '.split(' ').join('.pager ')) .bind('filterStart.pager', function(e, filters) { p.currentFilters = filters; // don't change page is filters are the same (pager updating, etc) @@ -769,6 +799,7 @@ // update page display first, so we update p.filteredPages updatePageDisplay(table, p, false); moveToPage(table, p, false); + c.$table.trigger('applyWidgets'); fixHeight(table, p); } }) @@ -784,12 +815,18 @@ e.stopPropagation(); destroyPager(table, p); }) - .bind('update updateRows updateAll addRows '.split(' ').join('.pager '), function(e){ + .bind('updateComplete.pager', function(e, table, triggered){ e.stopPropagation(); + // table can be unintentionally undefined in tablesorter v2.17.7 and earlier + if ( !table || triggered ) { return; } fixHeight(table, p); - var $rows = c.$tbodies.eq(0).children('tr'); + var $rows = c.$tbodies.eq(0).children('tr').not(c.selectorRemove); p.totalRows = $rows.length - ( p.countChildRows ? 0 : $rows.filter('.' + c.cssChildRow).length ); p.totalPages = Math.ceil( p.totalRows / p.size ); + if ($rows.length && c.rowsCopy && c.rowsCopy.length === 0) { + // make a copy of all table rows once the cache has been built + updateCache(table); + } updatePageDisplay(table, p); hideRows(table, p); }) diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index dceaeb7..896315b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.17.7 - Client-side table sorting with ease! +* TableSorter 2.17.8 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.17.7"; + ts.version = "2.17.8"; ts.parsers = []; ts.widgets = []; @@ -246,9 +246,9 @@ p = ts.getParserById( ts.getData(h, ch, 'sorter') ); np = ts.getData(h, ch, 'parser') === 'false'; // empty cells behaviour - keeping emptyToBottom for backwards compatibility - c.empties[i] = ts.getData(h, ch, 'empty') || c.emptyTo || (c.emptyToBottom ? 'bottom' : 'top' ); + c.empties[i] = ( ts.getData(h, ch, 'empty') || c.emptyTo || (c.emptyToBottom ? 'bottom' : 'top' ) ).toLowerCase(); // text strings behaviour in numerical sorts - c.strings[i] = ts.getData(h, ch, 'string') || c.stringTo || 'max'; + c.strings[i] = ( ts.getData(h, ch, 'string') || c.stringTo || 'max' ).toLowerCase(); if (np) { p = ts.getParserById('no-parser'); } @@ -447,14 +447,16 @@ ch = ts.getColumnData( table, c.headers, index, true ); // save original header content c.headerContent[index] = $(this).html(); - // set up header template - t = c.headerTemplate.replace(/\{content\}/g, $(this).html()).replace(/\{icon\}/g, i); - if (c.onRenderTemplate) { - h = c.onRenderTemplate.apply($t, [index, t]); - if (h && typeof h === 'string') { t = h; } // only change t if something is returned + // if headerTemplate is empty, don't reformat the header cell + if ( c.headerTemplate !== '' ) { + // set up header template + t = c.headerTemplate.replace(/\{content\}/g, $(this).html()).replace(/\{icon\}/g, i); + if (c.onRenderTemplate) { + h = c.onRenderTemplate.apply($t, [index, t]); + if (h && typeof h === 'string') { t = h; } // only change t if something is returned + } + $(this).html('<div class="' + ts.css.headerIn + '">' + t + '</div>'); // faster than wrapInner } - $(this).html('<div class="' + ts.css.headerIn + '">' + t + '</div>'); // faster than wrapInner - if (c.onRenderHeader) { c.onRenderHeader.apply($t, [index]); } this.column = parseInt( $(this).attr('data-column'), 10); this.order = formatSortingOrder( ts.getData($t, ch, 'sortInitialOrder') || c.sortInitialOrder ) ? [1,0,2] : [0,1,2]; @@ -560,14 +562,16 @@ // automatically add col group, and column sizes if set function fixColumnWidth(table) { - if (table.config.widthFixed && $(table).find('colgroup').length === 0) { - var colgroup = $('<colgroup>'), - overallWidth = $(table).width(); + var colgroup, overallWidth, + c = table.config; + if (c.widthFixed && c.$table.find('colgroup').length === 0) { + colgroup = $('<colgroup>'); + overallWidth = $(table).width(); // only add col for visible columns - fixes #371 - $(table.tBodies[0]).find("tr:first").children(":visible").each(function() { + $(table.tBodies).not('.' + c.cssInfoBlock).find("tr:first").children(":visible").each(function() { colgroup.append($('<col>').css('width', parseInt(($(this).width()/overallWidth)*1000, 10)/10 + '%')); }); - $(table).prepend(colgroup); + c.$table.prepend(colgroup); } } @@ -798,7 +802,7 @@ function resortComplete($table, callback){ var table = $table[0]; if (table.isUpdating) { - $table.trigger('updateComplete'); + $table.trigger('updateComplete', table); } if ($.isFunction(callback)) { callback($table[0]); @@ -1047,7 +1051,10 @@ return (version[0] > 1) || (version[0] === 1 && parseInt(version[1], 10) >= 4); })($.fn.jquery.split(".")); // digit sort text location; keeping max+/- for backwards compatibility - c.string = { 'max': 1, 'min': -1, 'emptyMin': 1, 'emptyMax': -1, 'zero': 0, 'none': 0, 'null': 0, 'top': true, 'bottom': false }; + c.string = { 'max': 1, 'min': -1, 'emptymin': 1, 'emptymax': -1, 'zero': 0, 'none': 0, 'null': 0, 'top': true, 'bottom': false }; + // ensure case insensitivity + c.emptyTo = c.emptyTo.toLowerCase(); + c.stringTo = c.stringTo.toLowerCase(); // add table theme class only if there isn't already one there if (!/tablesorter\-/.test($table.attr('class'))) { k = (c.theme !== '' ? ' tablesorter-' + c.theme : ''); @@ -1761,6 +1768,7 @@ format: function(s) { return s ? $.trim(s.replace(/(https?|ftp|file):\/\//, '')) : s; }, + parsed : true, // filter widget flag type: "text" }); @@ -1853,7 +1861,7 @@ id: "zebra", priority: 90, format: function(table, c, wo) { - var $tb, $tv, $tr, row, even, time, k, l, + var $tb, $tv, $tr, row, even, time, k, child = new RegExp(c.cssChildRow, 'i'), b = c.$tbodies; if (c.debug) { @@ -1861,21 +1869,18 @@ } for (k = 0; k < b.length; k++ ) { // loop through the visible rows + row = 0; $tb = b.eq(k); - l = $tb.children('tr').length; - if (l > 1) { - row = 0; - $tv = $tb.children('tr:visible').not(c.selectorRemove); - // revered back to using jQuery each - strangely it's the fastest method - /*jshint loopfunc:true */ - $tv.each(function(){ - $tr = $(this); - // style children rows the same way the parent row was styled - if (!child.test(this.className)) { row++; } - even = (row % 2 === 0); - $tr.removeClass(wo.zebra[even ? 1 : 0]).addClass(wo.zebra[even ? 0 : 1]); - }); - } + $tv = $tb.children('tr:visible').not(c.selectorRemove); + // revered back to using jQuery each - strangely it's the fastest method + /*jshint loopfunc:true */ + $tv.each(function(){ + $tr = $(this); + // style child rows the same way the parent row was styled + if (!child.test(this.className)) { row++; } + even = (row % 2 === 0); + $tr.removeClass(wo.zebra[even ? 1 : 0]).addClass(wo.zebra[even ? 0 : 1]); + }); } if (c.debug) { ts.benchmark("Applying Zebra widget", time); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index f96c7d0..f28e676 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.16+ widgets - updated 8/9/2014 (v2.17.7) +/*! tableSorter 2.16+ widgets - updated 9/15/2014 (v2.17.8) * * Column Styles * Column Filters @@ -245,7 +245,7 @@ ts.addWidget({ ts.benchmark("Applying " + theme + " theme", time); } }, - remove: function(table, c, wo) { + remove: function(table, c) { var $table = c.$table, theme = c.theme || 'jui', themes = ts.themes[ theme ] || ts.themes.jui, @@ -354,6 +354,8 @@ ts.addWidget({ filter_childRows : false, // if true, filter includes child row content in the search filter_columnFilters : true, // if true, a filter will be added to the top of each table column filter_cssFilter : '', // css class name added to the filter row & each input in the row (tablesorter-filter is ALWAYS added) + filter_defaultFilter : {}, // add a default column filter type "~{query}" to make fuzzy searches default; "{q1} AND {q2}" to make all searches use a logical AND. + filter_excludeFilter : {}, // filters to exclude, per column filter_external : '', // jQuery selector string (or jQuery object) of external filters filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin filter_formatter : null, // add custom filter elements to the filter row @@ -410,24 +412,25 @@ ts.filter = { type : /undefined|number/, // check type exact : /(^[\"|\'|=]+)|([\"|\'|=]+$)/g, // exact match (allow '==') nondigit : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser) - operators : /[<>=]/g // replace operators + operators : /[<>=]/g, // replace operators + query : '(q|query)' // replace filter queries }, - // function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed ) - // filter = array of filter input values; iFilter = same array, except lowercase - // exact = table cell text (or parsed data if column parser enabled) - // iExact = same as exact, except lowercase - // cached = table cell text from cache, so it has been parsed - // index = column index; table = table element (DOM) - // wo = widget options (table.config.widgetOptions) - // parsed = array (by column) of boolean values (from filter_useParsedData or "filter-parsed" class) + // function( c, data ) { } + // c = table.config + // data.filter = array of filter input values; data.iFilter = same array, except lowercase + // data.exact = table cell text (or parsed data if column parser enabled) + // data.iExact = same as data.exact, except lowercase + // data.cache = table cell text from cache, so it has been parsed + // data.index = column index; table = table element (DOM) + // data.parsed = array (by column) of boolean values (from filter_useParsedData or "filter-parsed" class) types: { // Look for regex - regex: function( filter, iFilter, exact, iExact ) { - if ( ts.filter.regex.regex.test(iFilter) ) { + regex: function( c, data ) { + if ( ts.filter.regex.regex.test(data.iFilter) ) { var matches, - regex = ts.filter.regex.regex.exec(iFilter); + regex = ts.filter.regex.regex.exec(data.iFilter); try { - matches = new RegExp(regex[1], regex[2]).test( iExact ); + matches = new RegExp(regex[1], regex[2]).test( data.iExact ); } catch (error) { matches = false; } @@ -436,27 +439,29 @@ ts.filter = { return null; }, // Look for operators >, >=, < or <= - operators: function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed ) { - if ( /^[<>]=?/.test(iFilter) ) { + operators: function( c, data ) { + if ( /^[<>]=?/.test(data.iFilter) ) { var cachedValue, result, - c = table.config, - query = ts.formatFloat( iFilter.replace(ts.filter.regex.operators, ''), table ), + table = c.table, + index = data.index, + parsed = data.parsed[index], + query = ts.formatFloat( data.iFilter.replace(ts.filter.regex.operators, ''), table ), parser = c.parsers[index], savedSearch = query; // parse filter value in case we're comparing numbers (dates) - if (parsed[index] || parser.type === 'numeric') { - result = ts.filter.parseFilter(table, $.trim('' + iFilter.replace(ts.filter.regex.operators, '')), index, parsed[index], true); + if (parsed || parser.type === 'numeric') { + result = ts.filter.parseFilter(c, $.trim('' + data.iFilter.replace(ts.filter.regex.operators, '')), index, parsed, true); query = ( typeof result === "number" && result !== '' && !isNaN(result) ) ? result : query; } // iExact may be numeric - see issue #149; // check if cached is defined, because sometimes j goes out of range? (numeric columns) - cachedValue = ( parsed[index] || parser.type === 'numeric' ) && !isNaN(query) && typeof cached !== 'undefined' ? cached : - isNaN(iExact) ? ts.formatFloat( iExact.replace(ts.filter.regex.nondigit, ''), table) : - ts.formatFloat( iExact, table ); + cachedValue = ( parsed || parser.type === 'numeric' ) && !isNaN(query) && typeof data.cache !== 'undefined' ? data.cache : + isNaN(data.iExact) ? ts.formatFloat( data.iExact.replace(ts.filter.regex.nondigit, ''), table) : + ts.formatFloat( data.iExact, table ); - if ( />/.test(iFilter) ) { result = />=/.test(iFilter) ? cachedValue >= query : cachedValue > query; } - if ( /</.test(iFilter) ) { result = /<=/.test(iFilter) ? cachedValue <= query : cachedValue < query; } + if ( />/.test(data.iFilter) ) { result = />=/.test(data.iFilter) ? cachedValue >= query : cachedValue > query; } + if ( /</.test(data.iFilter) ) { result = /<=/.test(data.iFilter) ? cachedValue <= query : cachedValue < query; } // keep showing all rows if nothing follows the operator if ( !result && savedSearch === '' ) { result = true; } return result; @@ -464,37 +469,40 @@ ts.filter = { return null; }, // Look for a not match - notMatch: function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed ) { - if ( /^\!/.test(iFilter) ) { - iFilter = ts.filter.parseFilter(table, iFilter.replace('!', ''), index, parsed[index]); - if (ts.filter.regex.exact.test(iFilter)) { + notMatch: function( c, data ) { + if ( /^\!/.test(data.iFilter) ) { + var indx, + filter = ts.filter.parseFilter(c, data.iFilter.replace('!', ''), data.index, data.parsed[data.index]); + if (ts.filter.regex.exact.test(filter)) { // look for exact not matches - see #628 - iFilter = iFilter.replace(ts.filter.regex.exact, ''); - return iFilter === '' ? true : $.trim(iFilter) !== iExact; + filter = filter.replace(ts.filter.regex.exact, ''); + return filter === '' ? true : $.trim(filter) !== data.iExact; } else { - var indx = iExact.search( $.trim(iFilter) ); - return iFilter === '' ? true : !(wo.filter_startsWith ? indx === 0 : indx >= 0); + indx = data.iExact.search( $.trim(filter) ); + return filter === '' ? true : !(c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0); } } return null; }, // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric - exact: function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed, rowArray ) { + exact: function( c, data ) { /*jshint eqeqeq:false */ - if (ts.filter.regex.exact.test(iFilter)) { - var fltr = ts.filter.parseFilter(table, iFilter.replace(ts.filter.regex.exact, ''), index, parsed[index]); - return rowArray ? $.inArray(fltr, rowArray) >= 0 : fltr == iExact; + if (ts.filter.regex.exact.test(data.iFilter)) { + var filter = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.exact, ''), data.index, data.parsed[data.index]); + return data.anyMatch ? $.inArray(filter, data.rowArray) >= 0 : filter == data.iExact; } return null; }, // Look for an AND or && operator (logical and) - and : function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed ) { - if ( ts.filter.regex.andTest.test(filter) ) { - var query = iFilter.split( ts.filter.regex.andSplit ), - result = iExact.search( $.trim(ts.filter.parseFilter(table, query[0], index, parsed[index])) ) >= 0, + and : function( c, data ) { + if ( ts.filter.regex.andTest.test(data.filter) ) { + var index = data.index, + parsed = data.parsed[index], + query = data.iFilter.split( ts.filter.regex.andSplit ), + result = data.iExact.search( $.trim( ts.filter.parseFilter(c, query[0], index, parsed) ) ) >= 0, indx = query.length - 1; while (result && indx) { - result = result && iExact.search( $.trim(ts.filter.parseFilter(table, query[indx], index, parsed[index])) ) >= 0; + result = result && data.iExact.search( $.trim( ts.filter.parseFilter(c, query[indx], index, parsed) ) ) >= 0; indx--; } return result; @@ -502,52 +510,55 @@ ts.filter = { return null; }, // Look for a range (using " to " or " - ") - see issue #166; thanks matzhu! - range : function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed ) { - if ( ts.filter.regex.toTest.test(iFilter) ) { + range : function( c, data ) { + if ( ts.filter.regex.toTest.test(data.iFilter) ) { var result, tmp, - c = table.config, + table = c.table, + index = data.index, + parsed = data.parsed[index], // make sure the dash is for a range and not indicating a negative number - query = iFilter.split( ts.filter.regex.toSplit ), - range1 = ts.formatFloat( ts.filter.parseFilter(table, query[0].replace(ts.filter.regex.nondigit, ''), index, parsed[index]), table ), - range2 = ts.formatFloat( ts.filter.parseFilter(table, query[1].replace(ts.filter.regex.nondigit, ''), index, parsed[index]), table ); + query = data.iFilter.split( ts.filter.regex.toSplit ), + range1 = ts.formatFloat( ts.filter.parseFilter(c, query[0].replace(ts.filter.regex.nondigit, ''), index, parsed), table ), + range2 = ts.formatFloat( ts.filter.parseFilter(c, query[1].replace(ts.filter.regex.nondigit, ''), index, parsed), table ); // parse filter value in case we're comparing numbers (dates) - if (parsed[index] || c.parsers[index].type === 'numeric') { + if (parsed || c.parsers[index].type === 'numeric') { result = c.parsers[index].format('' + query[0], table, c.$headers.eq(index), index); range1 = (result !== '' && !isNaN(result)) ? result : range1; result = c.parsers[index].format('' + query[1], table, c.$headers.eq(index), index); range2 = (result !== '' && !isNaN(result)) ? result : range2; } - result = ( parsed[index] || c.parsers[index].type === 'numeric' ) && !isNaN(range1) && !isNaN(range2) ? cached : - isNaN(iExact) ? ts.formatFloat( iExact.replace(ts.filter.regex.nondigit, ''), table) : - ts.formatFloat( iExact, table ); + result = ( parsed || c.parsers[index].type === 'numeric' ) && !isNaN(range1) && !isNaN(range2) ? data.cache : + isNaN(data.iExact) ? ts.formatFloat( data.iExact.replace(ts.filter.regex.nondigit, ''), table) : + ts.formatFloat( data.iExact, table ); if (range1 > range2) { tmp = range1; range1 = range2; range2 = tmp; } // swap return (result >= range1 && result <= range2) || (range1 === '' || range2 === ''); } return null; }, // Look for wild card: ? = single, * = multiple, or | = logical OR - wild : function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed, rowArray ) { - if ( /[\?|\*]/.test(iFilter) || ts.filter.regex.orReplace.test(filter) ) { - var c = table.config, - query = ts.filter.parseFilter(table, iFilter.replace(ts.filter.regex.orReplace, "|"), index, parsed[index]); + wild : function( c, data ) { + if ( /[\?|\*]/.test(data.iFilter) || ts.filter.regex.orReplace.test(data.filter) ) { + var index = data.index, + parsed = data.parsed[index], + query = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.orReplace, "|"), index, parsed); // look for an exact match with the "or" unless the "filter-match" class is found if (!c.$headers.filter('[data-column="' + index + '"]:last').hasClass('filter-match') && /\|/.test(query)) { - query = $.isArray(rowArray) ? '(' + query + ')' : '^(' + query + ')$'; + query = data.anyMatch && $.isArray(data.rowArray) ? '(' + query + ')' : '^(' + query + ')$'; } // parsing the filter may not work properly when using wildcards =/ - return new RegExp( query.replace(/\?/g, '\\S{1}').replace(/\*/g, '\\S*') ).test(iExact); + return new RegExp( query.replace(/\?/g, '\\S{1}').replace(/\*/g, '\\S*') ).test(data.iExact); } return null; }, // fuzzy text search; modified from https://github.com/mattyork/fuzzy (MIT license) - fuzzy: function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed ) { - if ( /^~/.test(iFilter) ) { + fuzzy: function( c, data ) { + if ( /^~/.test(data.iFilter) ) { var indx, patternIndx = 0, - len = iExact.length, - pattern = ts.filter.parseFilter(table, iFilter.slice(1), index, parsed[index]); + len = data.iExact.length, + pattern = ts.filter.parseFilter(c, data.iFilter.slice(1), data.index, data.parsed[data.index]); for (indx = 0; indx < len; indx++) { - if (iExact[indx] === pattern[patternIndx]) { + if (data.iExact[indx] === pattern[patternIndx]) { patternIndx += 1; } } @@ -579,7 +590,9 @@ ts.filter = { wo.filter_initTimer = null; wo.filter_formatterCount = 0; wo.filter_formatterInit = []; + wo.filter_initializing = true; + txt = '\\{' + ts.filter.regex.query + '\\}'; $.extend( regex, { child : new RegExp(c.cssChildRow), filtered : new RegExp(wo.filter_filteredRow), @@ -588,7 +601,9 @@ ts.filter = { toSplit : new RegExp('(?:\\s+(?:-|' + ts.language.to + ')\\s+)' ,'gi'), andTest : new RegExp('\\s+(' + ts.language.and + '|&&)\\s+', 'i'), andSplit : new RegExp('(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi'), - orReplace : new RegExp('\\s+(' + ts.language.or + ')\\s+', 'gi') + orReplace : new RegExp('\\s+(' + ts.language.or + ')\\s+', 'gi'), + iQuery : new RegExp(txt, 'i'), + igQuery : new RegExp(txt, 'ig') }); // don't build filter row if columnFilters is false or all columns are set to "filter-false" - issue #156 @@ -741,7 +756,13 @@ ts.filter = { }, filterInitComplete: function(c){ var wo = c.widgetOptions, - count = 0; + count = 0, + completed = function(){ + wo.filter_initialized = true; + wo.filter_initializing = false; + ts.filter.findRows(c.table, c.$table.data('lastSearch'), null); + c.$table.trigger('filterInit', c); + }; $.each( wo.filter_formatterInit, function(i, val) { if (val === 1) { count++; @@ -750,17 +771,16 @@ ts.filter = { clearTimeout(wo.filter_initTimer); if (!wo.filter_initialized && count === wo.filter_formatterCount) { // filter widget initialized - wo.filter_initialized = true; - c.$table.trigger('filterInit', c); + completed(); } else if (!wo.filter_initialized) { // fall back in case a filter_formatter doesn't call // $.tablesorter.filter.formatterUpdated($cell, column), and the count is off wo.filter_initTimer = setTimeout(function(){ - wo.filter_initialized = true; - c.$table.trigger('filterInit', c); + completed(); }, 500); } }, + setDefaults: function(table, c, wo) { var isArray, saved, indx, // get current (default) filters @@ -780,10 +800,9 @@ ts.filter = { c.$table.data('lastSearch', filters); return filters; }, - parseFilter: function(table, filter, column, parsed, forceParse){ - var c = table.config; + parseFilter: function(c, filter, column, parsed, forceParse){ return forceParse || parsed ? - c.parsers[column].format( filter, table, [], column ) : + c.parsers[column].format( filter, c.table, [], column ) : filter; }, buildRow: function(table, c, wo) { @@ -991,26 +1010,51 @@ ts.filter = { }, 200); }); }, + defaultFilter: function(filter, mask){ + if (filter === '') { return filter; } + var regex = ts.filter.regex.iQuery, + maskLen = mask.match( ts.filter.regex.igQuery ).length, + query = maskLen > 1 ? $.trim(filter).split(/\s/) : [ $.trim(filter) ], + len = query.length - 1, + indx = 0, + val = mask; + if ( len < 1 && maskLen > 1 ) { + // only one "word" in query but mask has >1 slots + query[1] = query[0]; + } + // replace all {query} with query words... + // if query = "Bob", then convert mask from "!{query}" to "!Bob" + // if query = "Bob Joe Frank", then convert mask "{q} OR {q}" to "Bob OR Joe OR Frank" + while (regex.test(val)) { + val = val.replace(regex, query[indx++] || ''); + if (regex.test(val) && indx < len && (query[indx] || '') !== '') { + val = mask.replace(regex, val); + } + } + return val; + }, findRows: function(table, filters, combinedFilters) { - if (table.config.lastCombinedFilter === combinedFilters) { return; } - var cached, len, $rows, rowIndex, tbodyIndex, $tbody, $cells, columnIndex, - childRow, childRowText, exact, iExact, iFilter, lastSearch, matches, result, - notFiltered, searchFiltered, filterMatched, showRow, time, val, indx, - anyMatch, iAnyMatch, rowArray, rowText, iRowText, rowCache, fxn, ffxn, + if (table.config.lastCombinedFilter === combinedFilters || table.config.widgetOptions.filter_initializing) { return; } + var len, $rows, rowIndex, tbodyIndex, $tbody, $cells, $cell, columnIndex, + childRow, lastSearch, hasSelect, matches, result, showRow, time, val, indx, + notFiltered, searchFiltered, filterMatched, excludeMatch, fxn, ffxn, regex = ts.filter.regex, c = table.config, wo = c.widgetOptions, - columns = c.columns, $tbodies = c.$table.children('tbody'), // target all tbodies #568 + // data object passed to filters; anyMatch is a flag for the filters + data = { anyMatch: false }, // anyMatch really screws up with these types of filters - anyMatchNotAllowedTypes = [ 'range', 'notMatch', 'operators' ], - // parse columns after formatter, in case the class is added at that point - parsed = c.$headers.map(function(columnIndex) { - return c.parsers && c.parsers[columnIndex] && c.parsers[columnIndex].parsed || - // getData won't return "parsed" if other "filter-" class names exist (e.g. <th class="filter-select filter-parsed">) - ts.getData && ts.getData(c.$headers.filter('[data-column="' + columnIndex + '"]:last'), ts.getColumnData( table, c.headers, columnIndex ), 'filter') === 'parsed' || - $(this).hasClass('filter-parsed'); - }).get(); + noAnyMatch = [ 'range', 'notMatch', 'operators' ]; + + // parse columns after formatter, in case the class is added at that point + data.parsed = c.$headers.map(function(columnIndex) { + return c.parsers && c.parsers[columnIndex] && c.parsers[columnIndex].parsed || + // getData won't return "parsed" if other "filter-" class names exist (e.g. <th class="filter-select filter-parsed">) + ts.getData && ts.getData(c.$headers.filter('[data-column="' + columnIndex + '"]:last'), ts.getColumnData( table, c.headers, columnIndex ), 'filter') === 'parsed' || + $(this).hasClass('filter-parsed'); + }).get(); + if (c.debug) { time = new Date(); } // filtered rows count c.filteredRows = 0; @@ -1060,15 +1104,25 @@ ts.filter = { ts.log( "Searching through " + ( searchFiltered && notFiltered < len ? notFiltered : "all" ) + " rows" ); } if ((wo.filter_$anyMatch && wo.filter_$anyMatch.length) || filters[c.columns]) { - anyMatch = wo.filter_$anyMatch && wo.filter_$anyMatch.val() || filters[c.columns] || ''; + data.anyMatchFlag = true; + data.anyMatchFilter = wo.filter_$anyMatch && wo.filter_$anyMatch.val() || filters[c.columns] || ''; if (c.sortLocaleCompare) { // replace accents - anyMatch = ts.replaceAccents(anyMatch); + data.anyMatchFilter = ts.replaceAccents(data.anyMatchFilter); } - iAnyMatch = anyMatch.toLowerCase(); + if (wo.filter_defaultFilter && regex.iQuery.test( ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '')) { + data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) ); + // clear search filtered flag because default filters are not saved to the last search + searchFiltered = false; + } + data.iAnyMatchFilter = data.anyMatchFilter; } + // loop through the rows for (rowIndex = 0; rowIndex < len; rowIndex++) { + + data.cacheArray = c.cache[tbodyIndex].normalized[rowIndex]; + childRow = $rows[rowIndex].className; // skip child rows & already filtered rows if ( regex.child.test(childRow) || (searchFiltered && regex.filtered.test(childRow)) ) { continue; } @@ -1078,15 +1132,16 @@ ts.filter = { // so, if "table.config.widgetOptions.filter_childRows" is true and there is // a match anywhere in the child row, then it will make the row visible // checked here so the option can be changed dynamically - childRowText = (childRow.length && wo.filter_childRows) ? childRow.text() : ''; - childRowText = wo.filter_ignoreCase ? childRowText.toLocaleLowerCase() : childRowText; + data.childRowText = (childRow.length && wo.filter_childRows) ? childRow.text() : ''; + data.childRowText = wo.filter_ignoreCase ? data.childRowText.toLocaleLowerCase() : data.childRowText; $cells = $rows.eq(rowIndex).children(); - if (anyMatch) { - rowArray = $cells.map(function(i){ + if (data.anyMatchFlag) { + data.anyMatch = true; + data.rowArray = $cells.map(function(i){ var txt; - if (parsed[i]) { - txt = c.cache[tbodyIndex].normalized[rowIndex][i]; + if (data.parsed[i]) { + txt = data.cacheArray[i]; } else { txt = wo.filter_ignoreCase ? $(this).text().toLowerCase() : $(this).text(); if (c.sortLocaleCompare) { @@ -1095,13 +1150,15 @@ ts.filter = { } return txt; }).get(); - rowText = rowArray.join(' '); - iRowText = rowText.toLowerCase(); - rowCache = c.cache[tbodyIndex].normalized[rowIndex].slice(0,-1).join(' '); + data.filter = data.anyMatchFilter; + data.iFilter = data.iAnyMatchFilter; + data.exact = data.rowArray.join(' '); + data.iExact = data.exact.toLowerCase(); + data.cache = data.cacheArray.slice(0,-1).join(' '); filterMatched = null; $.each(ts.filter.types, function(type, typeFunction) { - if ($.inArray(type, anyMatchNotAllowedTypes) < 0) { - matches = typeFunction( anyMatch, iAnyMatch, rowText, iRowText, rowCache, columns, table, wo, parsed, rowArray ); + if ($.inArray(type, noAnyMatch) < 0) { + matches = typeFunction( c, data ); if (matches !== null) { filterMatched = matches; return false; @@ -1113,30 +1170,37 @@ ts.filter = { } else { if (wo.filter_startsWith) { showRow = false; - columnIndex = columns; + columnIndex = c.columns; while (!showRow && columnIndex > 0) { columnIndex--; - showRow = showRow || rowArray[columnIndex].indexOf(iAnyMatch) === 0; + showRow = showRow || data.rowArray[columnIndex].indexOf(data.iFilter) === 0; } } else { - showRow = (iRowText + childRowText).indexOf(iAnyMatch) >= 0; + showRow = (data.iExact + data.childRowText).indexOf(data.iFilter) >= 0; } } + data.anyMatch = false; } - for (columnIndex = 0; columnIndex < columns; columnIndex++) { + for (columnIndex = 0; columnIndex < c.columns; columnIndex++) { + data.filter = filters[columnIndex]; + data.index = columnIndex; + + // filter types to exclude, per column + excludeMatch = ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split(/\s+/); + // ignore if filter is empty or disabled - if (filters[columnIndex]) { - cached = c.cache[tbodyIndex].normalized[rowIndex][columnIndex]; + if (data.filter) { + data.cache = data.cacheArray[columnIndex]; // check if column data should be from the cell or from parsed data - if (wo.filter_useParsedData || parsed[columnIndex]) { - exact = cached; + if (wo.filter_useParsedData || data.parsed[columnIndex]) { + data.exact = data.cache; } else { // using older or original tablesorter - exact = $.trim($cells.eq(columnIndex).text()); - exact = c.sortLocaleCompare ? ts.replaceAccents(exact) : exact; // issue #405 + data.exact = $.trim( $cells.eq(columnIndex).text() ); + data.exact = c.sortLocaleCompare ? ts.replaceAccents(data.exact) : data.exact; // issue #405 } - iExact = !regex.type.test(typeof exact) && wo.filter_ignoreCase ? exact.toLocaleLowerCase() : exact; + data.iExact = !regex.type.test(typeof data.exact) && wo.filter_ignoreCase ? data.exact.toLocaleLowerCase() : data.exact; result = showRow; // if showRow is true, show that row // in case select filter option has a different value vs text "a - z|A through Z" @@ -1144,39 +1208,49 @@ ts.filter = { c.$filters.add(c.$externalFilters).filter('[data-column="'+ columnIndex + '"]').find('select option:selected').attr('data-function-name') || '' : ''; // replace accents - see #357 - filters[columnIndex] = c.sortLocaleCompare ? ts.replaceAccents(filters[columnIndex]) : filters[columnIndex]; - // val = case insensitive, filters[columnIndex] = case sensitive - iFilter = wo.filter_ignoreCase ? (filters[columnIndex] || '').toLocaleLowerCase() : filters[columnIndex]; + data.filter = c.sortLocaleCompare ? ts.replaceAccents(data.filter) : data.filter; + + val = true; + if (wo.filter_defaultFilter && regex.iQuery.test( ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || '')) { + data.filter = ts.filter.defaultFilter( data.filter, ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) ); + // val is used to indicate that a filter select is using a default filter; so we override the exact & partial matches + val = false; + } + // data.iFilter = case insensitive, data.filter = case sensitive + data.iFilter = wo.filter_ignoreCase ? (data.filter || '').toLocaleLowerCase() : data.filter; fxn = ts.getColumnData( table, wo.filter_functions, columnIndex ); - if (fxn) { - if (fxn === true) { - // default selector; no "filter-select" class - result = (c.$headers.filter('[data-column="' + columnIndex + '"]:last').hasClass('filter-match')) ? - iExact.search(iFilter) >= 0 : filters[columnIndex] === exact; + $cell = c.$headers.filter('[data-column="' + columnIndex + '"]:last'); + hasSelect = $cell.hasClass('filter-select'); + if ( fxn || ( hasSelect && val ) ) { + if (fxn === true || hasSelect) { + // default selector uses exact match unless "filter-match" class is found + result = ($cell.hasClass('filter-match')) ? data.iExact.search(data.iFilter) >= 0 : data.filter === data.exact; } else if (typeof fxn === 'function') { // filter callback( exact cell content, parser normalized content, filter input value, column index, jQuery row object ) - result = fxn(exact, cached, filters[columnIndex], columnIndex, $rows.eq(rowIndex)); - } else if (typeof fxn[ffxn || filters[columnIndex]] === 'function') { + result = fxn(data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex)); + } else if (typeof fxn[ffxn || data.filter] === 'function') { // selector option function - result = fxn[ffxn || filters[columnIndex]](exact, cached, filters[columnIndex], columnIndex, $rows.eq(rowIndex)); + result = fxn[ffxn || data.filter](data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex)); } } else { filterMatched = null; // cycle through the different filters // filters return a boolean or null if nothing matches $.each(ts.filter.types, function(type, typeFunction) { - matches = typeFunction( filters[columnIndex], iFilter, exact, iExact, cached, columnIndex, table, wo, parsed ); - if (matches !== null) { - filterMatched = matches; - return false; + if ($.inArray(type, excludeMatch) < 0) { + matches = typeFunction( c, data ); + if (matches !== null) { + filterMatched = matches; + return false; + } } }); if (filterMatched !== null) { result = filterMatched; // Look for match, and add child row data for matching } else { - exact = (iExact + childRowText).indexOf( ts.filter.parseFilter(table, iFilter, columnIndex, parsed[columnIndex]) ); - result = ( (!wo.filter_startsWith && exact >= 0) || (wo.filter_startsWith && exact === 0) ); + data.exact = (data.iExact + data.childRowText).indexOf( ts.filter.parseFilter(c, data.iFilter, columnIndex, data.parsed[columnIndex]) ); + result = ( (!wo.filter_startsWith && data.exact >= 0) || (wo.filter_startsWith && data.exact === 0) ); } } showRow = (result) ? showRow : false; @@ -1469,7 +1543,7 @@ ts.addWidget({ return; } var $table = c.$table, - $attach = $(wo.stickyHeaders_attachTo), + $attach = $(wo.stickyHeaders_attachTo || 'window'), $thead = $table.children('thead:first'), $win = $attach.length ? $attach : $(window), $header = $thead.children('tr').not('.sticky-false').children(), diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-duration.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-duration.js new file mode 100644 index 0000000..408797d --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-duration.js @@ -0,0 +1,40 @@ +/*! Duration parser + */ +/*jshint jquery:true, unused:false */ +;(function($){ +"use strict"; + + // If any number > 9999, then set table.config.durationLength = 5 + // The below regex matches this duration example: 1y 23d 12h 44m 9s + $.tablesorter.addParser({ + id: "duration", + is: function() { + return false; + }, + format: function(s, table) { + var i, time, + c = table.config, + t = '', + duration = '', + len = c.durationLength || 4, + str = new Array(len + 1).join('0'), + labels = (c.durationLabels || '(?:years|year|y),(?:days|day|d),(?:hours|hour|h),(?:minutes|minute|min|m),(?:seconds|second|sec|s)').split(/\s*,\s*/), + llen = labels.length; + // build regex + if (!c.durationRegex) { + for (i = 0; i < llen; i++) { + t += '(?:(\\d+)\\s*' + labels[i] + '\\s*)?'; + } + c.durationRegex = new RegExp(t, 'i'); + } + // remove commas from value + time = ( c.usNumberFormat ? s.replace(/,/g, '') : s.replace( /(\d)(?:\.|\s*)(\d)/g, '$1$2') ).match(c.durationRegex); + for (i = 1; i < llen + 1; i++) { + duration += ( str + ( time[i] || 0 ) ).slice(-len); + } + return duration; + }, + type: "text" + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js index d842980..2d8b47b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js @@ -1,14 +1,16 @@ -/*! Title parser +/*! Title parser - updated 9/15/2014 (v2.17.8) * This parser will remove "The", "A" and "An" from the beginning of a book * or movie title, so it sorts by the second word or number * Demo: http://jsfiddle.net/Mottie/abkNM/5/ */ -/*global jQuery: false */ +/*jshint browser: true, jquery:true, unused:false */ ;(function($){ "use strict"; +var ts = $.tablesorter; + // basic list from http://en.wikipedia.org/wiki/Article_%28grammar%29 - $.tablesorter.ignoreArticles = { + ts.ignoreArticles = { "en" : "the, a, an", "de" : "der, die, das, des, dem, den, ein, eine, einer, eines, einem, einen", "nl" : "de, het, de, een", @@ -24,22 +26,34 @@ // and then set the language id 'xx' in the headers option // ignoreArticles : 'xx' - $.tablesorter.addParser({ + ts.addParser({ id: 'ignoreArticles', is: function() { return false; }, format: function(s, table, cell, cellIndex) { - var c = table.config, art, lang; + var art, ignore, lang, + c = table.config, + str = s || ''; if ( !(c.headers && c.headers[cellIndex] && c.headers[cellIndex].ignoreArticlesRegex) ) { - // initialize - save regex in c.headers[cellIndex].ignoreArticles + // initialize - save regex in c.headers[cellIndex].ignoreArticlesRegex if (!c.headers) { c.headers = {}; } if (!c.headers[cellIndex]) { c.headers[cellIndex] = {}; } - lang = $.tablesorter.getData(c.$headers.eq(cellIndex), c.headers[cellIndex], 'ignoreArticles'); - art = ($.tablesorter.ignoreArticles[lang] || "the, a, an" ) + ""; + lang = ts.getData( c.$headers.eq(cellIndex), ts.getColumnData( table, c.headers, cellIndex ), 'ignoreArticles' ); + art = (ts.ignoreArticles[lang] || "the, a, an" ) + ""; c.headers[cellIndex].ignoreArticlesRegex = new RegExp('^(' + $.trim( art.split(/\s*\,\s*/).join('\\s|') + "\\s" ).replace("_\\s","") + ')', 'i'); + // exception regex stored in c.headers[cellIndex].ignoreArticlesRegex2 + ignore = ts.getData( c.$headers.eq(cellIndex), ts.getColumnData( table, c.headers, cellIndex ), 'ignoreArticlesExcept' ); + c.headers[cellIndex].ignoreArticlesRegex2 = ignore !== '' ? new RegExp('^(' + ignore.replace(/\s/g, "\\s") + ')', 'i') : ''; + } + art = c.headers[cellIndex].ignoreArticlesRegex; + if (art.test(str)) { + ignore = c.headers[cellIndex].ignoreArticlesRegex2; + if ( !(ignore && ignore.test(str)) ) { + return str.replace(art, ''); + } } - return (s || '').replace(c.headers[cellIndex].ignoreArticlesRegex, ''); + return str; }, type: 'text' }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index 5e57556..e166ef9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,5 +1,5 @@ /*! input & select parsers for jQuery 1.7+ & tablesorter 2.7.11+ - * Updated 8/1/2014 (v2.17.6) + * Updated 9/15/2014 (v2.17.8) * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ /*jshint browser: true, jquery:true, unused:false */ @@ -101,16 +101,20 @@ // this flag prevents the updateCell event from being spammed // it happens when you modify input text and hit enter var focused = false, - restoreValue = function(){ + restoreValue = function(isTbody){ // focused = false; // uncomment this line to prevent auto-accepting changes // make sure we restore original values - $(':focus').blur(); + // isTbody is needed to prevent the select from closing in IE + // see https://connect.microsoft.com/IE/feedbackdetail/view/962618/ + if (isTbody) { + $(':focus').blur(); + } return; }; // bind to .tablesorter (default class name) $(this).children('tbody') - .on('mouseleave', function(){ - restoreValue(); + .on('mouseleave', function(e){ + restoreValue(e.target.tagName === 'TBODY'); }) .on('focus', 'select, input, textarea', function(){ focused = true; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js index d63ff86..2a3a19a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js @@ -1,4 +1,4 @@ -/*! tablesorter Editable Content widget - updated 8/1/2014 (core v2.17.6) +/*! tablesorter Editable Content widget - updated 9/15/2014 (core v2.17.8) * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -14,14 +14,42 @@ editable_enterToAccept : true, editable_autoAccept : true, editable_autoResort : false, - editable_validate : null, // function(text, originalText){ return text; } + editable_wrapContent : '<div>', // wrap the cell content... makes this widget work in IE, and with autocomplete + editable_trimContent : true, // trim content inside of contenteditable (remove tabs & carriage returns) + editable_validate : null, // function(text, originalText){ return text; } + editable_focused : null, // function(text, columnIndex, $element) {} + editable_blur : null, // function(text, columnIndex, $element) { } + editable_selectAll : false, // true/false or function(text, columnIndex, $element) { return true; } editable_noEdit : 'no-edit', editable_editComplete : 'editComplete' }, init: function(table, thisWidget, c, wo){ if ( !wo.editable_columns.length ) { return; } var indx, tmp, $t, - cols = []; + cols = [], + editComplete = function($cell, refocus){ + $cell + .removeClass('tseditable-last-edited-cell') + .trigger( wo.editable_editComplete, [c] ); + // restore focus last cell after updating + if (refocus) { + setTimeout(function(){ + $cell.focus(); + }, 50); + } + }, + selectAll = function(cell){ + setTimeout(function(){ + // select all text in contenteditable + // see http://stackoverflow.com/a/6150060/145346 + var range = document.createRange(); + range.selectNodeContents(cell); + var sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + }, 100); + }; + if ( $.type(wo.editable_columns) === "string" && wo.editable_columns.indexOf('-') >= 0 ) { // editable_columns can contain a range string (i.e. "2-4" ) tmp = wo.editable_columns.split('-'); @@ -38,25 +66,51 @@ } }); } + tmp = $('<div>').wrapInner(wo.editable_wrapContent).children().length || $.isFunction(wo.editable_wrapContent); // IE does not allow making TR/TH/TD cells directly editable (issue #404) // so add a div or span inside ( it's faster than using wrapInner() ) c.$tbodies.find( cols.join(',') ).not( '.' + wo.editable_noEdit ).each(function(){ // test for children, if they exist, then make the children editable $t = $(this); - ( $t.children().length ? $t.children() : $t ).prop( 'contenteditable', true ); + + if (tmp && $t.children().length === 0) { + $t.wrapInner( wo.editable_wrapContent ); + } + if ($t.children().length) { + // make all children content editable + $t.children().not('.' + wo.editable_noEdit).each(function(){ + var $this = $(this); + if (wo.editable_trimContent) { + $this.text(function(i, txt){ + return $.trim(txt); + }); + } + $this.prop( 'contenteditable', true ); + }); + } else { + if (wo.editable_trimContent) { + $t.text(function(i, txt){ + return $.trim(txt); + }); + } + $t.prop( 'contenteditable', true ); + } }); c.$tbodies .on('mouseleave.tseditable', function(){ if ( c.$table.data('contentFocused') ) { // change to "true" instead of element to allow focusout to process c.$table.data( 'contentFocused', true ); - $(':focus').trigger('blur'); + $(':focus').trigger('focusout'); } }) .on('focus.tseditable', '[contenteditable]', function(e){ + clearTimeout( $(this).data('timer') ); c.$table.data( 'contentFocused', e.target ); var $this = $(this), - v = $this.html(); + selAll = wo.editable_selectAll, + column = $this.closest('td').index(), + txt = $.trim( $this.text() ); if (wo.editable_enterToAccept) { // prevent enter from adding into the content $this.on('keydown.tseditable', function(e){ @@ -65,46 +119,79 @@ } }); } - $this.data({ before : v, original: v }); + $this.data({ before : txt, original: txt }); + + if (typeof wo.editable_focused === 'function') { + wo.editable_focused( txt, column, $this ); + } + + if (selAll) { + if (typeof selAll === 'function') { + if ( selAll( txt, column, $this ) ) { + selectAll($this[0]); + } + } else { + selectAll($this[0]); + } + } }) .on('blur focusout keydown '.split(' ').join('.tseditable '), '[contenteditable]', function(e){ if ( !c.$table.data('contentFocused') ) { return; } - var t, + var t, validate, valid = false, - $this = $(e.target); + $this = $(e.target), + txt = $.trim( $this.text() ), + column = $this.closest('td').index(); if ( e.which === 27 ) { // user cancelled - $this.html( $this.data('original') ).trigger('blur.tseditable'); + $this.html( $.trim( $this.data('original') ) ).trigger('blur.tseditable'); c.$table.data( 'contentFocused', false ); return false; } + // accept on enter (if set), alt-enter (always) or if autoAccept is set and element is blurred or unfocused t = e.which === 13 && ( wo.editable_enterToAccept || e.altKey ) || wo.editable_autoAccept && e.type !== 'keydown'; // change if new or user hits enter (if option set) - if ( t && $this.data('before') !== $this.html() ) { - valid = $.isFunction(wo.editable_validate) ? wo.editable_validate( $this.html(), $this.data('original') ) : $this.html(); + if ( t && $this.data('before') !== txt ) { + + validate = wo.editable_validate; + valid = txt; + + if (typeof(validate) === "function") { + valid = validate( txt, $this.data('original'), column, $this ); + } else if (typeof (validate = $.tablesorter.getColumnData( table, validate, column )) === 'function') { + valid = validate( txt, $this.data('original'), column, $this ); + } + if ( t && valid !== false ) { + c.$table.find('.tseditable-last-edited-cell').removeClass('tseditable-last-edited-cell'); $this - .html( valid ) + .addClass('tseditable-last-edited-cell') + .html( $.trim( valid ) ) .data('before', valid) + .data('original', valid) .trigger('change'); - c.$table.trigger('updateCell', [ $this.closest('td'), wo.editable_autoResort, function(table){ - $this.trigger( wo.editable_editComplete, [c] ); - $this.data( 'original', $this.html() ); - if ( wo.editable_autoResort && c.sortList.length ) { - c.$table.trigger('applyWidgets'); + c.$table.trigger('updateCell', [ $this.closest('td'), false, function(){ + if (wo.editable_autoResort) { + setTimeout(function(){ + c.$table.trigger("sorton", [ c.sortList, function(){ + editComplete(c.$table.find('.tseditable-last-edited-cell'), true); + }, true ]); + }, 10); + } else { + editComplete(c.$table.find('.tseditable-last-edited-cell')); } - // restore focus last cell after updating - setTimeout(function(){ - var t = c.$table.data('contentFocused'); - if ( t instanceof HTMLElement ) { t.focus(); } - }, 50); } ]); return false; } - } - if ( !valid && e.type !== 'keydown' ) { + } else if ( !valid && e.type !== 'keydown' ) { + clearTimeout( $this.data('timer') ); + $this.data('timer', setTimeout(function(){ + if ($.isFunction(wo.editable_blur)) { + wo.editable_blur( $.trim( $this.text() ), column, $this ); + } + }, 100)); // restore original content on blur - $this.html( $this.data('original') ); + $this.html( $.trim( $this.data('original') ) ); } }); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index ce609ac..7f31034 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/* Pager widget for TableSorter 8/1/2014 (v2.17.6) */ +/* Pager widget for TableSorter 9/15/2014 (v2.17.8) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ "use strict"; @@ -215,7 +215,7 @@ tsp = ts.pager = { s = wo.pager_selectors; c.$table - .unbind('filterStart filterEnd sortEnd disable enable destroy update updateRows updateAll addRows pageSize '.split(' ').join('.pager ')) + .unbind('filterStart filterEnd sortEnd disable enable destroy updateComplete pageSize '.split(' ').join('.pager ')) .bind('filterStart.pager', function(e, filters) { p.currentFilters = filters; // don't change page is filters are the same (pager updating, etc) @@ -232,7 +232,9 @@ tsp = ts.pager = { } // update page display first, so we update p.filteredPages tsp.updatePageDisplay(table, c, false); - tsp.moveToPage(table, p, false); + // tsp.moveToPage(table, p, false); <-- called when applyWidgets is triggered + c.pager.last.page = -1; + c.$table.trigger('applyWidgets'); tsp.fixHeight(table, c); } }) @@ -248,12 +250,18 @@ tsp = ts.pager = { e.stopPropagation(); tsp.destroyPager(table, c); }) - .on('update updateRows updateAll addRows '.split(' ').join('.pager '), function(e){ + .on('updateComplete.pager', function(e, table, triggered){ e.stopPropagation(); + // table can be unintentionally undefined in tablesorter v2.17.7 and earlier + if (!table || triggered) { return; } tsp.fixHeight(table, c); - var $rows = c.$tbodies.eq(0).children('tr'); + var $rows = c.$tbodies.eq(0).children('tr').not(c.selectorRemove); p.totalRows = $rows.length - ( c.widgetOptions.pager_countChildRows ? 0 : $rows.filter('.' + c.cssChildRow).length ); p.totalPages = Math.ceil( p.totalRows / p.size ); + if ($rows.length && c.rowsCopy && c.rowsCopy.length === 0) { + // make a copy of all table rows once the cache has been built + tsp.updateCache(table); + } tsp.updatePageDisplay(table, c); tsp.hideRows(table, c); // make sure widgets are applied - fixes #450 @@ -400,7 +408,8 @@ tsp = ts.pager = { for ( i = 1; i <= pg; i++ ) { t += '<option>' + i + '</option>'; } - p.$goto.html(t).val( p.page + 1 ); + p.$goto[0].innerHTML = t; + p.$goto[0].value = p.page + 1; } // rebind startRow/page inputs $out.find('.ts-startRow, .ts-page').unbind('change').bind('change', function(){ @@ -740,9 +749,8 @@ tsp = ts.pager = { wo.pager_startPage = p.page; wo.pager_size = p.size; - c.$table.trigger('applyWidgets'); if (table.isUpdating) { - c.$table.trigger('updateComplete'); + c.$table.trigger('updateComplete', [ table, true ]); } }, @@ -764,6 +772,7 @@ tsp = ts.pager = { .removeAttr('aria-describedby') .find('tr.pagerSavedHeightSpacer').remove(); tsp.renderTable(table, c.rowsCopy); + c.$table.trigger('applyWidgets'); if (c.debug) { ts.log('pager disabled'); } @@ -834,9 +843,11 @@ tsp = ts.pager = { } $.data(table, 'pagerLastPage', p.page); if (p.initialized && pageMoved !== false) { - c.$table.trigger('pageMoved', c); + c.$table + .trigger('pageMoved', c) + .trigger('applyWidgets'); if (!p.ajax && table.isUpdating) { - c.$table.trigger('updateComplete'); + c.$table.trigger('updateComplete', [ table, true ]); } } }, From 10d494bfcb664cc631fb8a6ee6e8a3ad89064e45 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Mon, 27 Oct 2014 18:12:48 +0100 Subject: [PATCH 039/138] * updated tablesorter to latest version (2.18.0) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 182 +++++---- .../jquery-tablesorter/jquery.tablesorter.js | 102 +++-- .../jquery.tablesorter.widgets.js | 369 +++++++++++------ .../parsers/parser-date-extract.js | 72 ++-- .../parsers/parser-date-iso8601.js | 9 +- .../parsers/parser-date-month.js | 26 +- .../parsers/parser-date-two-digit-year.js | 45 ++- .../parsers/parser-date-weekday.js | 26 +- .../jquery-tablesorter/parsers/parser-date.js | 11 +- .../parsers/parser-named-numbers.js | 121 ++++++ .../{parser-ipv6.js => parser-network.js} | 83 +++- .../widgets/widget-columnSelector.js | 17 +- .../widgets/widget-cssStickyHeaders.js | 97 +++-- .../widgets/widget-editable.js | 371 ++++++++++-------- .../widgets/widget-grouping.js | 9 +- .../widgets/widget-pager.js | 207 +++++++--- .../widgets/widget-repeatheaders.js | 2 +- .../widgets/widget-scroller.js | 30 +- .../jquery-tablesorter/theme.black-ice.css | 4 +- .../jquery-tablesorter/theme.blue.css | 4 +- .../jquery-tablesorter/theme.bootstrap_2.css | 4 +- .../jquery-tablesorter/theme.dark.css | 4 +- .../jquery-tablesorter/theme.default.css | 4 +- .../jquery-tablesorter/theme.dropbox.css | 4 +- .../jquery-tablesorter/theme.green.css | 4 +- .../jquery-tablesorter/theme.grey.css | 4 +- .../jquery-tablesorter/theme.ice.css | 4 +- .../jquery-tablesorter/theme.jui.css | 5 +- .../jquery-tablesorter/theme.metro-dark.css | 8 +- 33 files changed, 1200 insertions(+), 638 deletions(-) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js rename vendor/assets/javascripts/jquery-tablesorter/parsers/{parser-ipv6.js => parser-network.js} (62%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d80b9f..b70a064 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.13.0 (2014-10-27) + +* Upgrade tablesorter to v2.18.0 + #### v1.12.8 (2014-09-17) * Upgrade tablesorter to v2.17.8 diff --git a/README.md b/README.md index 80723a7..186374d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.17.8 (9/15/2014), [documentation] +Current tablesorter version: 2.18.0 (10/26/2014), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 3739176..ed5e7fa 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.12.8' + VERSION = '1.13.0' end diff --git a/tablesorter b/tablesorter index db8878a..f04b51b 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit db8878ac83d1fbbcc22ef24587a17e41b0e7ff39 +Subproject commit f04b51b336e7830ba1bc2d0cea9000dfc99d3735 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 08047ff..ad9f79d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 9/15/2014 (v2.17.8) + * updated 10/26/2014 (v2.18.0) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -131,16 +131,10 @@ } }, - updatePageDisplay = function(table, p, completed) { - var i, pg, s, $out, regex, - c = table.config, - f = c.$table.hasClass('hasFilters'), - t = [], - sz = p.size || 10; // don't allow dividing by zero - t = [ (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered'), c.selectorRemove.replace(/^(\w+\.)/g,'') ]; - if (p.countChildRows) { t.push(c.cssChildRow); } - regex = new RegExp( '(' + t.join('|') + ')' ); - if (f && !p.ajaxUrl) { + calcFilters = function(table, p) { + var c = table.config, + hasFilters = c.$table.hasClass('hasFilters'); + if (hasFilters && !p.ajaxUrl) { if ($.isEmptyObject(c.cache)) { // delayInit: true so nothing is in the cache p.filteredRows = p.totalRows = c.$tbodies.eq(0).children('tr').not( p.countChildRows ? '' : '.' + c.cssChildRow ).length; @@ -150,11 +144,20 @@ p.filteredRows += p.regexRows.test(el[c.columns].$row[0].className) ? 0 : 1; }); } - } else if (!f) { + } else if (!hasFilters) { p.filteredRows = p.totalRows; } + }, + + updatePageDisplay = function(table, p, completed) { + if ( !p.initialized ) { return; } + var s, t, $out, + c = table.config, + sz = p.size || 10; // don't allow dividing by zero + if (p.countChildRows) { t.push(c.cssChildRow); } p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method c.totalRows = p.totalRows; + calcFilters(table, p); c.filteredRows = p.filteredRows; p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) { @@ -188,38 +191,11 @@ $out[ ($out[0].tagName === 'INPUT') ? 'val' : 'html' ](s); if ( p.$goto.length ) { t = ''; - pg = Math.min( p.totalPages, p.filteredPages ); - // Filter the options page number link array if it's larger than 'maxOptionSize' - // as large page set links will slow the browser on large dom inserts - var skip_set_size = Math.floor(pg / p.maxOptionSize), - large_collection = pg > p.maxOptionSize, - current_page = p.page + 1, - start_page = 1, - end_page = pg, - option_pages = []; - //construct default options pages array - var option_pages_start_page = (large_collection && current_page == 1) ? skip_set_size : 1; - for (i = option_pages_start_page; i <= pg;) { - option_pages.push(i); - i = large_collection ? i + skip_set_size : i++; - } - if (large_collection) { - var central_focus_size = Math.floor(p.maxOptionSize / 2) - 1, - lower_focus_window = Math.abs(Math.floor(current_page - central_focus_size/2)), - focus_option_pages = []; - start_page = Math.min(current_page, lower_focus_window); - end_page = start_page + central_focus_size; - //construct an array to get a focus set around the current page - for (i = start_page; i <= end_page ; i++) focus_option_pages.push(i); - var insert_index = Math.floor(option_pages.length / 2) - Math.floor(focus_option_pages.length / 2); - Array.prototype.splice.apply(option_pages, [ insert_index, focus_option_pages.length ].concat(focus_option_pages)); - option_pages.sort(function sortNumber(a,b) { return a - b; }); - } - for ( i = 0; i < option_pages.length; i++) { - t += '<option>' + option_pages[i] + '</option>'; - } - p.$goto[0].innerHTML = t; - p.$goto[0].value = current_page; + $.each(buildPageSelect(p), function(i, opt){ + t += '<option value="' + opt + '">' + opt + '</option>'; + }); + // innerHTML doesn't work in IE9 - http://support2.microsoft.com/kb/276228 + p.$goto.html(t).val( p.page + 1 ); } // rebind startRow/page inputs $out.find('.ts-startRow, .ts-page').unbind('change').bind('change', function(){ @@ -230,6 +206,7 @@ } } pagerArrows(p); + fixHeight(table, p); if (p.initialized && completed !== false) { c.$table.trigger('pagerComplete', p); // save pager info to storage @@ -242,26 +219,92 @@ } }, + buildPageSelect = function(p) { + // Filter the options page number link array if it's larger than 'maxOptionSize' + // as large page set links will slow the browser on large dom inserts + var i, central_focus_size, focus_option_pages, insert_index, option_length, focus_length, + pg = Math.min( p.totalPages, p.filteredPages ) || 1, + // make skip set size multiples of 5 + skip_set_size = Math.ceil( ( pg / p.maxOptionSize ) / 5 ) * 5, + large_collection = pg > p.maxOptionSize, + current_page = p.page + 1, + start_page = skip_set_size, + end_page = pg - skip_set_size, + option_pages = [1], + // construct default options pages array + option_pages_start_page = (large_collection) ? skip_set_size : 1; + + for ( i = option_pages_start_page; i <= pg; ) { + option_pages.push(i); + i = i + ( large_collection ? skip_set_size : 1 ); + } + option_pages.push(pg); + if (large_collection) { + focus_option_pages = []; + // don't allow central focus size to be > 5 on either side of current page + central_focus_size = Math.max( Math.floor( p.maxOptionSize / skip_set_size ) - 1, 5 ); + + start_page = current_page - central_focus_size; + if (start_page < 1) { start_page = 1; } + end_page = current_page + central_focus_size; + if (end_page > pg) { end_page = pg; } + // construct an array to get a focus set around the current page + for (i = start_page; i <= end_page ; i++) { + focus_option_pages.push(i); + } + + // keep unique values + option_pages = $.grep(option_pages, function(value, indx) { + return $.inArray(value, option_pages) === indx; + }); + + option_length = option_pages.length; + focus_length = focus_option_pages.length; + + // make sure at all option_pages aren't replaced + if (option_length - focus_length > skip_set_size / 2 && option_length + focus_length > p.maxOptionSize ) { + insert_index = Math.floor(option_length / 2) - Math.floor(focus_length / 2); + Array.prototype.splice.apply(option_pages, [ insert_index, focus_length ]); + } + option_pages = option_pages.concat(focus_option_pages); + + } + + // keep unique values again + option_pages = $.grep(option_pages, function(value, indx) { + return $.inArray(value, option_pages) === indx; + }) + .sort(function(a,b) { return a - b; }); + + return option_pages; + }, + fixHeight = function(table, p) { var d, h, c = table.config, $b = c.$tbodies.eq(0); - if (p.fixedHeight) { - $b.find('tr.pagerSavedHeightSpacer').remove(); + $b.find('tr.pagerSavedHeightSpacer').remove(); + if (p.fixedHeight && !p.isDisabled) { h = $.data(table, 'pagerSavedHeight'); if (h) { d = h - $b.height(); if ( d > 5 && $.data(table, 'pagerLastSize') === p.size && $b.children('tr:visible').length < p.size ) { - $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.replace(/^(\w+\.)/g,'') + '" style="height:' + d + 'px;"></tr>'); + $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice(1) + '" style="height:' + d + 'px;"></tr>'); } } } }, changeHeight = function(table, p) { - var $b = table.config.$tbodies.eq(0); + var h, + c = table.config, + $b = c.$tbodies.eq(0); $b.find('tr.pagerSavedHeightSpacer').remove(); - $.data(table, 'pagerSavedHeight', $b.height()); + if (!$b.children('tr:visible').length) { + $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice(1) + '"><td> </td></tr>'); + } + h = $b.children('tr').eq(0).height() * p.size; + $.data(table, 'pagerSavedHeight', h); fixHeight(table, p); $.data(table, 'pagerLastSize', p.size); }, @@ -418,8 +461,7 @@ p.last.totalRows = p.totalRows; p.last.currentFilters = p.currentFilters; p.last.sortList = (c.sortList || []).join(','); - updatePageDisplay(table, p); - fixHeight(table, p); + updatePageDisplay(table, p, true); $t.trigger('updateCache', [function(){ if (p.initialized) { // apply widgets after table has rendered & after a delay to prevent @@ -560,8 +602,7 @@ } ts.processTbody(table, $tb, false); } - updatePageDisplay(table, p); - if ( !p.isDisabled ) { fixHeight(table, p); } + updatePageDisplay(table, p, true); if (table.isUpdating) { $t.trigger('updateComplete', [ table, true ]); } @@ -612,13 +653,14 @@ moveToPage = function(table, p, pageMoved) { if ( p.isDisabled ) { return; } - var c = table.config, + var pg, c = table.config, $t = $(table), - l = p.last, - pg = Math.min( p.totalPages, p.filteredPages ); - if ( pageMoved !== false && p.initialized && $.isEmptyObject(table.config.cache)) { + l = p.last; + if ( pageMoved !== false && p.initialized && $.isEmptyObject(c.cache)) { return updateCache(table); } + calcFilters(table, p); + pg = Math.min( p.totalPages, p.filteredPages ); if ( p.page < 0 ) { p.page = 0; } if ( p.page > ( pg - 1 ) && pg !== 0 ) { p.page = pg - 1; } // fixes issue where one currentFilter is [] and the other is ['','',''], @@ -628,6 +670,10 @@ // don't allow rendering multiple times on the same page/size/totalRows/filters/sorts if ( l.page === p.page && l.size === p.size && l.totalRows === p.totalRows && (l.currentFilters || []).join(',') === (p.currentFilters || []).join(',') && + // check for ajax url changes see #730 + (l.ajaxUrl || '') === (p.ajaxObject.url || '') && + // & ajax url option changes (dynamically add/remove/rename sort & filter parameters) + (l.optAjaxUrl || '') === (p.ajaxUrl || '') && l.sortList === (c.sortList || []).join(',') ) { return; } if (c.debug) { ts.log('Pager changing to page ' + p.page); @@ -638,7 +684,9 @@ // fixes #408; modify sortList otherwise it auto-updates sortList : (c.sortList || []).join(','), totalRows : p.totalRows, - currentFilters : p.currentFilters || [] + currentFilters : p.currentFilters || [], + ajaxUrl : p.ajaxObject.url || '', + optAjaxUrl : p.ajaxUrl || '' }; if (p.ajax) { getAjax(table, p); @@ -722,11 +770,11 @@ p.$container.find(p.cssPageDisplay).attr('id', info); c.$table.attr('aria-describedby', info); } + changeHeight(table, p); if ( triggered ) { c.$table.trigger('updateRows'); setPageSize(table, p.size, p); hideRowsSetup(table, p); - fixHeight(table, p); if (c.debug) { ts.log('pager enabled'); } @@ -781,7 +829,7 @@ p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.replace(/^(\w+\.)/g,'') + '|' + c.cssChildRow + ')'); $t - .unbind('filterStart filterEnd sortEnd disable enable destroy updateComplete pageSize '.split(' ').join('.pager ')) + .unbind('filterStart filterEnd sortEnd disable enable destroy updateComplete pageSize pageSet '.split(' ').join('.pager ')) .bind('filterStart.pager', function(e, filters) { p.currentFilters = filters; // don't change page is filters are the same (pager updating, etc) @@ -796,11 +844,9 @@ // make sure we have a copy of all table rows once the cache has been built updateCache(table); } - // update page display first, so we update p.filteredPages - updatePageDisplay(table, p, false); moveToPage(table, p, false); c.$table.trigger('applyWidgets'); - fixHeight(table, p); + updatePageDisplay(table, p, false); } }) .bind('disable.pager', function(e){ @@ -819,7 +865,6 @@ e.stopPropagation(); // table can be unintentionally undefined in tablesorter v2.17.7 and earlier if ( !table || triggered ) { return; } - fixHeight(table, p); var $rows = c.$tbodies.eq(0).children('tr').not(c.selectorRemove); p.totalRows = $rows.length - ( p.countChildRows ? 0 : $rows.filter('.' + c.cssChildRow).length ); p.totalPages = Math.ceil( p.totalRows / p.size ); @@ -827,8 +872,12 @@ // make a copy of all table rows once the cache has been built updateCache(table); } - updatePageDisplay(table, p); + if ( p.page >= p.totalPages ) { + moveToLastPage(table, p); + } hideRows(table, p); + changeHeight(table, p); + updatePageDisplay(table, p, true); }) .bind('pageSize.pager', function(e,v){ e.stopPropagation(); @@ -912,11 +961,10 @@ hideRowsSetup(table, p); } - changeHeight(table, p); - // pager initialized if (!p.ajax) { p.initialized = true; + updatePageDisplay(table, p, true); $(table).trigger('pagerInitialized', p); } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 896315b..1fdc8ee 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.17.8 - Client-side table sorting with ease! +* TableSorter 2.18.0 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.17.8"; + ts.version = "2.18.0"; ts.parsers = []; ts.widgets = []; @@ -75,6 +75,7 @@ zebra : [ 'even', 'odd' ] // zebra widget alternating row class names }, initWidgets : true, // apply widgets on tablesorter initialization + widgetClass : 'widget-{name}', // table class name template to match to include a widget // *** callbacks initialized : null, // function(table){}, @@ -187,7 +188,7 @@ } function detectParserForColumn(table, rows, rowIndex, cellIndex) { - var cur, + var cur, $node, i = ts.parsers.length, node = false, nodeValue = '', @@ -197,6 +198,7 @@ if (rows[rowIndex]) { node = rows[rowIndex].cells[cellIndex]; nodeValue = getElementText(table, node, cellIndex); + $node = $(node); if (table.config.debug) { log('Checking if value was empty on row ' + rowIndex + ', column: ' + cellIndex + ': "' + nodeValue + '"'); } @@ -207,7 +209,7 @@ while (--i >= 0) { cur = ts.parsers[i]; // ignore the default text parser because it will always be true - if (cur && cur.id !== 'text' && cur.is && cur.is(nodeValue, table, node)) { + if (cur && cur.id !== 'text' && cur.is && cur.is(nodeValue, table, node, $node)) { return cur; } } @@ -457,7 +459,8 @@ } $(this).html('<div class="' + ts.css.headerIn + '">' + t + '</div>'); // faster than wrapInner } - if (c.onRenderHeader) { c.onRenderHeader.apply($t, [index]); } + if (c.onRenderHeader) { c.onRenderHeader.apply($t, [index, c, c.$table]); } + // *** remove this.column value if no conflicts found this.column = parseInt( $(this).attr('data-column'), 10); this.order = formatSortingOrder( ts.getData($t, ch, 'sortInitialOrder') || c.sortInitialOrder ) ? [1,0,2] : [0,1,2]; this.count = -1; // set to -1 because clicking on the header automatically adds one @@ -648,7 +651,7 @@ }); } // get current column index - indx = cell.column; + indx = parseInt( $(cell).attr('data-column'), 10 ); // user only wants to sort on one column if (key) { // flush the sort list @@ -1078,8 +1081,10 @@ 'aria-live' : 'polite', 'aria-relevant' : 'all' }); - if (c.$table.find('caption').length) { - c.$table.attr('aria-labelledby', 'theCaption'); + if (c.$table.children('caption').length) { + k = c.$table.children('caption')[0]; + if (!k.id) { k.id = c.namespace.slice(1) + 'caption'; } + c.$table.attr('aria-labelledby', k.id); } c.widgetInit = {}; // keep a list of initialized widgets // change textExtraction via data-attribute @@ -1556,11 +1561,24 @@ table = $(table)[0]; // in case this is called externally var c = table.config, wo = c.widgetOptions, + tableClass = ' ' + c.table.className + ' ', widgets = [], - time, w, wd; + time, time2, w, wd; // prevent numerous consecutive widget applications if (init !== false && table.hasInitialized && (table.isApplyingWidgets || table.isUpdating)) { return; } if (c.debug) { time = new Date(); } + // look for widgets to apply from in table class + // stop using \b otherwise this matches "ui-widget-content" & adds "content" widget + wd = new RegExp( '\\s' + c.widgetClass.replace( /\{name\}/i, '([\\w-]+)' )+ '\\s', 'g' ); + if ( tableClass.match( wd ) ) { + // extract out the widget id from the table class (widget id's can include dashes) + w = tableClass.match( wd ); + if ( w ) { + $.each( w, function( i,n ){ + c.widgets.push( n.replace( wd, '$1' ) ); + }); + } + } if (c.widgets.length) { table.isApplyingWidgets = true; // ensure unique widget ids @@ -1590,17 +1608,22 @@ wo = table.config.widgetOptions = $.extend( true, {}, w.options, wo ); } if (w.hasOwnProperty('init')) { + if (c.debug) { time2 = new Date(); } w.init(table, w, c, wo); + if (c.debug) { ts.benchmark('Initializing ' + w.id + ' widget', time2); } } } if (!init && w.hasOwnProperty('format')) { + if (c.debug) { time2 = new Date(); } w.format(table, c, wo, false); + if (c.debug) { ts.benchmark( ( init ? 'Initializing ' : 'Applying ' ) + w.id + ' widget', time2); } } } }); } setTimeout(function(){ table.isApplyingWidgets = false; + $.data(table, 'lastWidgetApplication', new Date()); }, 0); if (c.debug) { w = c.widgets.length; @@ -1743,23 +1766,6 @@ type: "numeric" }); - ts.addParser({ - id: "ipAddress", - is: function(s) { - return (/^\d{1,3}[\.]\d{1,3}[\.]\d{1,3}[\.]\d{1,3}$/).test(s); - }, - format: function(s, table) { - var i, a = s ? s.split(".") : '', - r = "", - l = a.length; - for (i = 0; i < l; i++) { - r += ("00" + a[i]).slice(-3); - } - return s ? ts.formatFloat(r, table) : s; - }, - type: "numeric" - }); - ts.addParser({ id: "url", is: function(s) { @@ -1778,7 +1784,8 @@ return (/^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}/).test(s); }, format: function(s, table) { - return s ? ts.formatFloat((s !== "") ? (new Date(s.replace(/-/g, "/")).getTime() || s) : "", table) : s; + var date = s ? new Date( s.replace(/-/g, "/") ) : s; + return date instanceof Date && isFinite(date) ? date.getTime() : s; }, type: "numeric" }); @@ -1794,6 +1801,19 @@ type: "numeric" }); + // added image parser to core v2.17.9 + ts.addParser({ + id: "image", + is: function(s, table, node, $node){ + return $node.find('img').length > 0; + }, + format: function(s, table, cell) { + return $(cell).find('img').attr(table.config.imgAttr || 'alt') || s; + }, + parsed : true, // filter widget flag + type: "text" + }); + ts.addParser({ id: "usLongDate", is: function(s) { @@ -1802,7 +1822,8 @@ return (/^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i).test(s) || (/^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i).test(s); }, format: function(s, table) { - return s ? ts.formatFloat( (new Date(s.replace(/(\S)([AP]M)$/i, "$1 $2")).getTime() || s), table) : s; + var date = s ? new Date( s.replace(/(\S)([AP]M)$/i, "$1 $2") ) : s; + return date instanceof Date && isFinite(date) ? date.getTime() : s; }, type: "numeric" }); @@ -1815,19 +1836,22 @@ }, format: function(s, table, cell, cellIndex) { if (s) { - var c = table.config, + var date, d, + c = table.config, ci = c.$headers.filter('[data-column=' + cellIndex + ']:last'), format = ci.length && ci[0].dateFormat || ts.getData( ci, ts.getColumnData( table, c.headers, cellIndex ), 'dateFormat') || c.dateFormat; - s = s.replace(/\s+/g," ").replace(/[\-.,]/g, "/"); // escaped - because JSHint in Firefox was showing it as an error + d = s.replace(/\s+/g," ").replace(/[\-.,]/g, "/"); // escaped - because JSHint in Firefox was showing it as an error if (format === "mmddyyyy") { - s = s.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$3/$1/$2"); + d = d.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$3/$1/$2"); } else if (format === "ddmmyyyy") { - s = s.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$3/$2/$1"); + d = d.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$3/$2/$1"); } else if (format === "yyyymmdd") { - s = s.replace(/(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/, "$1/$2/$3"); + d = d.replace(/(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/, "$1/$2/$3"); } + date = new Date(d); + return date instanceof Date && isFinite(date) ? date.getTime() : s; } - return s ? ts.formatFloat( (new Date(s).getTime() || s), table) : s; + return s; }, type: "numeric" }); @@ -1838,7 +1862,8 @@ return (/^(([0-2]?\d:[0-5]\d)|([0-1]?\d:[0-5]\d\s?([AP]M)))$/i).test(s); }, format: function(s, table) { - return s ? ts.formatFloat( (new Date("2000/01/01 " + s.replace(/(\S)([AP]M)$/i, "$1 $2")).getTime() || s), table) : s; + var date = s ? new Date( "2000/01/01 " + s.replace(/(\S)([AP]M)$/i, "$1 $2") ) : s; + return date instanceof Date && isFinite(date) ? date.getTime() : s; }, type: "numeric" }); @@ -1882,18 +1907,15 @@ $tr.removeClass(wo.zebra[even ? 1 : 0]).addClass(wo.zebra[even ? 0 : 1]); }); } - if (c.debug) { - ts.benchmark("Applying Zebra widget", time); - } }, remove: function(table, c, wo){ var k, $tb, b = c.$tbodies, rmv = (wo.zebra || [ "even", "odd" ]).join(' '); for (k = 0; k < b.length; k++ ){ - $tb = $.tablesorter.processTbody(table, b.eq(k), true); // remove tbody + $tb = ts.processTbody(table, b.eq(k), true); // remove tbody $tb.children().removeClass(rmv); - $.tablesorter.processTbody(table, $tb, false); // restore tbody + ts.processTbody(table, $tb, false); // restore tbody } } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index f28e676..cca5c07 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.16+ widgets - updated 9/15/2014 (v2.17.8) +/*! tableSorter 2.16+ widgets - updated 10/26/2014 (v2.18.0) * * Column Styles * Column Filters @@ -9,8 +9,8 @@ * [ "columns", "filter", "resizable", "stickyHeaders", "uitheme", "saveSort" ] */ /*jshint browser:true, jquery:true, unused:false, loopfunc:true */ -/*global jQuery: false, localStorage: false, navigator: false */ -;(function($) { +/*global jQuery: false, localStorage: false */ +;(function ($, window) { "use strict"; var ts = $.tablesorter = $.tablesorter || {}; @@ -33,7 +33,7 @@ ts.themes = { }, "jui" : { table : 'ui-widget ui-widget-content ui-corner-all', // table classes - caption : 'ui-widget-content ui-corner-all', + caption : 'ui-widget-content', header : 'ui-widget-header ui-corner-all ui-state-default', // header classes footerRow : '', footerCells: '', @@ -55,7 +55,8 @@ $.extend(ts.css, { wrapper : 'tablesorter-wrapper', // ui theme & resizable resizer : 'tablesorter-resizer', // resizable sticky : 'tablesorter-stickyHeader', // stickyHeader - stickyVis : 'tablesorter-sticky-visible' + stickyVis : 'tablesorter-sticky-visible', + stickyWrap: 'tablesorter-sticky-wrapper' }); // *** Store data in local storage, with a cookie fallback *** @@ -176,34 +177,42 @@ ts.addWidget({ id: "uitheme", priority: 10, format: function(table, c, wo) { - var i, time, classes, $header, $icon, $tfoot, $h, + var i, time, classes, $header, $icon, $tfoot, $h, oldtheme, oldremove, themesAll = ts.themes, $table = c.$table, $headers = c.$headers, theme = c.theme || 'jui', themes = themesAll[theme] || themesAll.jui, - remove = themes.sortNone + ' ' + themes.sortDesc + ' ' + themes.sortAsc; + remove = [ themes.sortNone, themes.sortDesc, themes.sortAsc, themes.active ].join( ' ' ); if (c.debug) { time = new Date(); } // initialization code - run once - if (!$table.hasClass('tablesorter-' + theme) || c.theme === theme || !table.hasInitialized) { + if (!$table.hasClass('tablesorter-' + theme) || c.theme !== c.appliedTheme || !table.hasInitialized) { + oldtheme = themes[c.appliedTheme] || {}; + oldremove = oldtheme ? [ oldtheme.sortNone, oldtheme.sortDesc, oldtheme.sortAsc, oldtheme.active ].join( ' ' ) : ''; + if (oldtheme) { + wo.zebra[0] = wo.zebra[0].replace(' ' + oldtheme.even, ''); + wo.zebra[1] = wo.zebra[1].replace(' ' + oldtheme.odd, ''); + } // update zebra stripes if (themes.even !== '') { wo.zebra[0] += ' ' + themes.even; } if (themes.odd !== '') { wo.zebra[1] += ' ' + themes.odd; } // add caption style - $table.find('caption').addClass(themes.caption); + $table.children('caption').removeClass(oldtheme.caption).addClass(themes.caption); // add table/footer class names $tfoot = $table // remove other selected themes - .removeClass( c.theme === '' ? '' : 'tablesorter-' + c.theme ) + .removeClass( c.appliedTheme ? 'tablesorter-' + ( c.appliedTheme || '' ) : '' ) .addClass('tablesorter-' + theme + ' ' + themes.table) // add theme widget class name - .find('tfoot'); + .children('tfoot'); if ($tfoot.length) { $tfoot - .find('tr').addClass(themes.footerRow) - .children('th, td').addClass(themes.footerCells); + .children('tr').removeClass(oldtheme.footerRow).addClass(themes.footerRow) + .children('th, td').removeClass(oldtheme.footerCells).addClass(themes.footerCells); } // update header classes $headers + .add(c.$extraHeaders) + .removeClass(oldtheme.header + ' ' + oldtheme.hover + ' ' + oldremove) .addClass(themes.header) .not('.sorter-false') .bind('mouseenter.tsuitheme mouseleave.tsuitheme', function(event) { @@ -216,16 +225,17 @@ ts.addWidget({ } if (c.cssIcon) { // if c.cssIcon is '', then no <i> is added to the header - $headers.find('.' + ts.css.icon).addClass(themes.icons); + $headers.find('.' + ts.css.icon).removeClass(oldtheme.icons + ' ' + oldremove).addClass(themes.icons); } if ($table.hasClass('hasFilters')) { - $headers.find('.' + ts.css.filterRow).addClass(themes.filterRow); + $table.children('thead').children('.' + ts.css.filterRow).removeClass(oldtheme.filterRow).addClass(themes.filterRow); } + c.appliedTheme = c.theme; } for (i = 0; i < c.columns; i++) { - $header = c.$headers.add(c.$extraHeaders).filter('[data-column="' + i + '"]'); + $header = c.$headers.add(c.$extraHeaders).not('.sorter-false').filter('[data-column="' + i + '"]'); $icon = (ts.css.icon) ? $header.find('.' + ts.css.icon) : $header; - $h = c.$headers.filter('[data-column="' + i + '"]:last'); + $h = $headers.not('.sorter-false').filter('[data-column="' + i + '"]:last'); if ($h.length) { if ($h[0].sortDisabled) { // no sort arrows for disabled columns! @@ -274,7 +284,7 @@ ts.addWidget({ columns : [ "primary", "secondary", "tertiary" ] }, format: function(table, c, wo) { - var time, $tbody, tbodyIndex, $rows, rows, $row, $cells, remove, indx, + var $tbody, tbodyIndex, $rows, rows, $row, $cells, remove, indx, $table = c.$table, $tbodies = c.$tbodies, sortList = c.sortList, @@ -283,9 +293,6 @@ ts.addWidget({ css = wo && wo.columns || [ "primary", "secondary", "tertiary" ], last = css.length - 1; remove = css.join(' '); - if (c.debug) { - time = new Date(); - } // check if there is a sort (on initialization there may not be one) for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // detach tbody @@ -325,9 +332,6 @@ ts.addWidget({ } } } - if (c.debug) { - ts.benchmark("Applying Columns widget", time); - } }, remove: function(table, c, wo) { var tbodyIndex, $tbody, @@ -353,6 +357,7 @@ ts.addWidget({ options : { filter_childRows : false, // if true, filter includes child row content in the search filter_columnFilters : true, // if true, a filter will be added to the top of each table column + filter_cellFilter : '', // css class name added to the filter cell (string or array) filter_cssFilter : '', // css class name added to the filter row & each input in the row (tablesorter-filter is ALWAYS added) filter_defaultFilter : {}, // add a default column filter type "~{query}" to make fuzzy searches default; "{q1} AND {q2}" to make all searches use a logical AND. filter_excludeFilter : {}, // filters to exclude, per column @@ -410,17 +415,18 @@ ts.filter = { child : /tablesorter-childRow/, // child row class name; this gets updated in the script filtered : /filtered/, // filtered (hidden) row class name; updated in the script type : /undefined|number/, // check type - exact : /(^[\"|\'|=]+)|([\"|\'|=]+$)/g, // exact match (allow '==') + exact : /(^[\"\'=]+)|([\"\'=]+$)/g, // exact match (allow '==') nondigit : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser) operators : /[<>=]/g, // replace operators query : '(q|query)' // replace filter queries }, // function( c, data ) { } // c = table.config - // data.filter = array of filter input values; data.iFilter = same array, except lowercase + // data.filter = array of filter input values; + // data.iFilter = same array, except lowercase (if wo.filter_ignoreCase is true) // data.exact = table cell text (or parsed data if column parser enabled) - // data.iExact = same as data.exact, except lowercase - // data.cache = table cell text from cache, so it has been parsed + // data.iExact = same as data.exact, except lowercase (if wo.filter_ignoreCase is true) + // data.cache = table cell text from cache, so it has been parsed (& in all lower case if config.ignoreCase is true) // data.index = column index; table = table element (DOM) // data.parsed = array (by column) of boolean values (from filter_useParsedData or "filter-parsed" class) types: { @@ -537,12 +543,14 @@ ts.filter = { }, // Look for wild card: ? = single, * = multiple, or | = logical OR wild : function( c, data ) { - if ( /[\?|\*]/.test(data.iFilter) || ts.filter.regex.orReplace.test(data.filter) ) { + if ( /[\?\*\|]/.test(data.iFilter) || ts.filter.regex.orReplace.test(data.filter) ) { var index = data.index, parsed = data.parsed[index], query = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.orReplace, "|"), index, parsed); // look for an exact match with the "or" unless the "filter-match" class is found if (!c.$headers.filter('[data-column="' + index + '"]:last').hasClass('filter-match') && /\|/.test(query)) { + // show all results while using filter match. Fixes #727 + if (query[ query.length - 1 ] === '|') { query += '*'; } query = data.anyMatch && $.isArray(data.rowArray) ? '(' + query + ')' : '^(' + query + ')$'; } // parsing the filter may not work properly when using wildcards =/ @@ -578,11 +586,8 @@ ts.filter = { and : 'and' }, ts.language); - var options, string, txt, $header, column, filters, val, time, fxn, noSelect, + var options, string, txt, $header, column, filters, val, fxn, noSelect, regex = ts.filter.regex; - if (c.debug) { - time = new Date(); - } c.$table.addClass('hasFilters'); // define timers so using clearTimeout won't cause an undefined error @@ -591,6 +596,8 @@ ts.filter = { wo.filter_formatterCount = 0; wo.filter_formatterInit = []; wo.filter_initializing = true; + wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; + wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; txt = '\\{' + ts.filter.regex.query + '\\}'; $.extend( regex, { @@ -714,9 +721,6 @@ ts.filter = { // set filtered rows count (intially unfiltered) c.filteredRows = c.totalRows; - if (c.debug) { - ts.benchmark("Applying Filter widget", time); - } // add default values c.$table.bind('tablesorter-initialized pagerInitialized', function() { // redefine "wo" as it does not update properly inside this callback @@ -758,9 +762,10 @@ ts.filter = { var wo = c.widgetOptions, count = 0, completed = function(){ - wo.filter_initialized = true; + // set initializing false first so findRows will process wo.filter_initializing = false; ts.filter.findRows(c.table, c.$table.data('lastSearch'), null); + wo.filter_initialized = true; c.$table.trigger('filterInit', c); }; $.each( wo.filter_formatterInit, function(i, val) { @@ -780,7 +785,7 @@ ts.filter = { }, 500); } }, - + setDefaults: function(table, c, wo) { var isArray, saved, indx, // get current (default) filters @@ -809,9 +814,14 @@ ts.filter = { var col, column, $header, buildSelect, disabled, name, ffxn, // c.columns defined in computeThIndexes() columns = c.columns, + arry = $.isArray(wo.filter_cellFilter), buildFilter = '<tr role="row" class="' + ts.css.filterRow + '">'; for (column = 0; column < columns; column++) { - buildFilter += '<td></td>'; + if (arry) { + buildFilter += '<td' + ( wo.filter_cellFilter[column] ? ' class="' + wo.filter_cellFilter[column] + '"' : '' ) + '></td>'; + } else { + buildFilter += '<td' + ( wo.filter_cellFilter !== '' ? ' class="' + wo.filter_cellFilter + '"' : '' ) + '></td>'; + } } c.$filters = $(buildFilter += '</tr>').appendTo( c.$table.children('thead').eq(0) ).find('td'); // build each filter input @@ -870,7 +880,7 @@ ts.filter = { $ext = wo.filter_$externalFilters; if (internal !== true) { // save anyMatch element - wo.filter_$anyMatch = $el.filter('[data-column="all"]'); + wo.filter_$anyMatch = $el.filter(wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector); if ($ext && $ext.length) { wo.filter_$externalFilters = wo.filter_$externalFilters.add( $el ); } else { @@ -1033,6 +1043,57 @@ ts.filter = { } return val; }, + getLatestSearch: function( $input ) { + return $input.sort(function(a, b) { + return $(b).attr('data-lastSearchTime') - $(a).attr('data-lastSearchTime'); + }); + }, + multipleColumns: function( c, $input ) { + // look for multiple columns "1-3,4-6,8" in data-column + var ranges, singles, indx, + wo = c.widgetOptions, + // only target "all" column inputs on initialization + // & don't target "all" column inputs if they don't exist + targets = wo.filter_initialized || !$input.filter(wo.filter_anyColumnSelector).length, + columns = [], + val = $.trim( ts.filter.getLatestSearch( $input ).attr('data-column') ); + // process column range + if ( targets && /-/.test( val ) ) { + ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); + $.each(ranges, function(i,v){ + var t, + range = v.split( /\s*-\s*/ ), + start = parseInt( range[0], 10 ) || 0, + end = parseInt( range[1], 10 ) || ( c.columns - 1 ); + if ( start > end ) { t = start; start = end; end = t; } // swap + if ( end >= c.columns ) { end = c.columns - 1; } + for ( ; start <= end; start++ ) { + columns.push(start); + } + // remove processed range from val + val = val.replace( v, '' ); + }); + } + // process single columns + if ( targets && /,/.test( val ) ) { + singles = val.split( /\s*,\s*/ ); + $.each( singles, function(i,v) { + if (v !== '') { + indx = parseInt( v, 10 ); + if ( indx < c.columns ) { + columns.push( indx ); + } + } + }); + } + // return all columns + if (!columns.length) { + for ( indx = 0; indx < c.columns; indx++ ) { + columns.push( indx ); + } + } + return columns; + }, findRows: function(table, filters, combinedFilters) { if (table.config.lastCombinedFilter === combinedFilters || table.config.widgetOptions.filter_initializing) { return; } var len, $rows, rowIndex, tbodyIndex, $tbody, $cells, $cell, columnIndex, @@ -1092,7 +1153,7 @@ ts.filter = { // if we are not doing exact matches, using "|" (logical or) or not "!" !/[=\"\|!]/.test(val) && // don't search only filtered if the value is negative ('> -10' => '> -100' will ignore hidden rows) - !(/(>=?\s*-\d)/.test(val) || /(<=?\s*\d)/.test(val)) && + !(/(>=?\s*-\d)/.test(val) || /(<=?\s*\d)/.test(val)) && // if filtering using a select without a "filter-match" class (exact match) - fixes #593 !( val !== '' && c.$filters && c.$filters.eq(indx).find('select').length && !c.$headers.filter('[data-column="' + indx + '"]:last').hasClass('filter-match') ); } @@ -1105,7 +1166,7 @@ ts.filter = { } if ((wo.filter_$anyMatch && wo.filter_$anyMatch.length) || filters[c.columns]) { data.anyMatchFlag = true; - data.anyMatchFilter = wo.filter_$anyMatch && wo.filter_$anyMatch.val() || filters[c.columns] || ''; + data.anyMatchFilter = wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || filters[c.columns] || ''; if (c.sortLocaleCompare) { // replace accents data.anyMatchFilter = ts.replaceAccents(data.anyMatchFilter); @@ -1115,7 +1176,9 @@ ts.filter = { // clear search filtered flag because default filters are not saved to the last search searchFiltered = false; } - data.iAnyMatchFilter = data.anyMatchFilter; + // make iAnyMatchFilter lowercase unless both filter widget & core ignoreCase options are true + // when c.ignoreCase is true, the cache contains all lower case data + data.iAnyMatchFilter = !(wo.filter_ignoreCase && c.ignoreCase) ? data.anyMatchFilter : data.anyMatchFilter.toLocaleLowerCase(); } // loop through the rows @@ -1135,25 +1198,28 @@ ts.filter = { data.childRowText = (childRow.length && wo.filter_childRows) ? childRow.text() : ''; data.childRowText = wo.filter_ignoreCase ? data.childRowText.toLocaleLowerCase() : data.childRowText; $cells = $rows.eq(rowIndex).children(); - if (data.anyMatchFlag) { + // look for multiple columns "1-3,4-6,8" + columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); data.anyMatch = true; data.rowArray = $cells.map(function(i){ - var txt; - if (data.parsed[i]) { - txt = data.cacheArray[i]; - } else { - txt = wo.filter_ignoreCase ? $(this).text().toLowerCase() : $(this).text(); - if (c.sortLocaleCompare) { - txt = ts.replaceAccents(txt); + if ( $.inArray(i, columnIndex) > -1 ) { + var txt; + if (data.parsed[i]) { + txt = data.cacheArray[i]; + } else { + txt = wo.filter_ignoreCase ? $(this).text().toLowerCase() : $(this).text(); + if (c.sortLocaleCompare) { + txt = ts.replaceAccents(txt); + } } + return txt; } - return txt; }).get(); data.filter = data.anyMatchFilter; data.iFilter = data.iAnyMatchFilter; data.exact = data.rowArray.join(' '); - data.iExact = data.exact.toLowerCase(); + data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; data.cache = data.cacheArray.slice(0,-1).join(' '); filterMatched = null; $.each(ts.filter.types, function(type, typeFunction) { @@ -1204,7 +1270,7 @@ ts.filter = { result = showRow; // if showRow is true, show that row // in case select filter option has a different value vs text "a - z|A through Z" - ffxn = wo.filter_columnFilters ? + ffxn = wo.filter_columnFilters ? c.$filters.add(c.$externalFilters).filter('[data-column="'+ columnIndex + '"]').find('select option:selected').attr('data-function-name') || '' : ''; // replace accents - see #357 @@ -1216,7 +1282,7 @@ ts.filter = { // val is used to indicate that a filter select is using a default filter; so we override the exact & partial matches val = false; } - // data.iFilter = case insensitive, data.filter = case sensitive + // data.iFilter = case insensitive (if wo.filter_ignoreCase is true), data.filter = case sensitive data.iFilter = wo.filter_ignoreCase ? (data.filter || '').toLocaleLowerCase() : data.filter; fxn = ts.getColumnData( table, wo.filter_functions, columnIndex ); $cell = c.$headers.filter('[data-column="' + columnIndex + '"]:last'); @@ -1461,7 +1527,7 @@ ts.filter = { }; ts.getFilters = function(table, getRaw, setFilters, skipFirst) { - var i, $filters, $column, + var i, f, $filters, $column, cols, filters = false, c = table ? $(table)[0].config : '', wo = c ? c.widgetOptions : ''; @@ -1478,19 +1544,34 @@ ts.getFilters = function(table, getRaw, setFilters, skipFirst) { if ($filters && $filters.length) { filters = setFilters || []; for (i = 0; i < c.columns + 1; i++) { - $column = $filters.filter('[data-column="' + (i === c.columns ? 'all' : i) + '"]'); + cols = ( i === c.columns ? + // "all" columns can now include a range or set of columms (data-column="0-2,4,6-7") + wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector : + '[data-column="' + i + '"]' ); + $column = $filters.filter(cols); if ($column.length) { // move the latest search to the first slot in the array - $column = $column.sort(function(a, b){ - return $(b).attr('data-lastSearchTime') - $(a).attr('data-lastSearchTime'); - }); + $column = ts.filter.getLatestSearch( $column ); if ($.isArray(setFilters)) { // skip first (latest input) to maintain cursor position while typing - (skipFirst ? $column.slice(1) : $column).val( setFilters[i] ).trigger('change.tsfilter'); + if (skipFirst) { $column.slice(1); } + if (i === c.columns) { + // prevent data-column="all" from filling data-column="0,1" (etc) + cols = $column.filter(wo.filter_anyColumnSelector); + $column = cols.length ? cols : $column; + } + $column + .val( setFilters[i] ) + .trigger('change.tsfilter'); } else { filters[i] = $column.val() || ''; // don't change the first... it will move the cursor - $column.slice(1).val( filters[i] ); + if (i === c.columns) { + // don't update range columns from "all" setting + $column.slice(1).filter('[data-column*="' + $column.attr('data-column') + '"]').val( filters[i] ); + } else { + $column.slice(1).val( filters[i] ); + } } // save any match input dynamically if (i === c.columns && $column.length) { @@ -1530,6 +1611,8 @@ ts.addWidget({ options: { stickyHeaders : '', // extra class name added to the sticky header row stickyHeaders_attachTo : null, // jQuery selector or object to attach sticky header to + stickyHeaders_xScroll : null, // jQuery selector or object to monitor horizontal scroll position (defaults: xScroll > attachTo > window) + stickyHeaders_yScroll : null, // jQuery selector or object to monitor vertical scroll position (defaults: yScroll > attachTo > window) stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element stickyHeaders_filteredToTop: true, // scroll table top into view after filtering stickyHeaders_cloneId : '-sticky', // added to table ID, if it exists @@ -1543,54 +1626,74 @@ ts.addWidget({ return; } var $table = c.$table, - $attach = $(wo.stickyHeaders_attachTo || 'window'), + $attach = $(wo.stickyHeaders_attachTo), + namespace = c.namespace + 'stickyheaders ', + // element to watch for the scroll event + $yScroll = $(wo.stickyHeaders_yScroll || wo.stickyHeaders_attachTo || window), + $xScroll = $(wo.stickyHeaders_xScroll || wo.stickyHeaders_attachTo || window), $thead = $table.children('thead:first'), - $win = $attach.length ? $attach : $(window), $header = $thead.children('tr').not('.sticky-false').children(), - innerHeader = '.' + ts.css.headerIn, - $tfoot = $table.find('tfoot'), + $tfoot = $table.children('tfoot'), $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '', stickyOffset = $attach.length ? 0 : $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, + // is this table nested? If so, find parent sticky header wrapper (div, not table) + $nestedSticky = $table.parent().closest('.' + ts.css.table).hasClass('hasStickyHeaders') ? + $table.parent().closest('table.tablesorter')[0].config.widgetOptions.$sticky.parent() : [], + nestedStickyTop = $nestedSticky.length ? $nestedSticky.height() : 0, + // clone table, then wrap to make sticky header $stickyTable = wo.$sticky = $table.clone() - .addClass('containsStickyHeaders') - .css({ - position : $attach.length ? 'absolute' : 'fixed', - margin : 0, - top : stickyOffset, - left : 0, - visibility : 'hidden', - zIndex : wo.stickyHeaders_zIndex ? wo.stickyHeaders_zIndex : 2 - }), - $stickyThead = $stickyTable.children('thead:first').addClass(ts.css.sticky + ' ' + wo.stickyHeaders), + .addClass('containsStickyHeaders ' + ts.css.sticky + ' ' + wo.stickyHeaders) + .wrap('<div class="' + ts.css.stickyWrap + '">'), + $stickyWrap = $stickyTable.parent().css({ + position : $attach.length ? 'absolute' : 'fixed', + margin : 0, + top : stickyOffset + nestedStickyTop, + left : 0, + visibility : 'hidden', + zIndex : wo.stickyHeaders_zIndex || 2 + }), + $stickyThead = $stickyTable.children('thead:first'), $stickyCells, laststate = '', spacing = 0, - nonwkie = $table.css('border-collapse') !== 'collapse' && !/(webkit|msie)/i.test(navigator.userAgent), + setWidth = function($orig, $clone){ + $orig.filter(':visible').each(function(i) { + var width, border, + $cell = $clone.filter(':visible').eq(i), + $this = $(this); + // code from https://github.com/jmosbech/StickyTableHeaders + if ($this.css('box-sizing') === 'border-box') { + width = $this.outerWidth(); + } else { + if ($cell.css('border-collapse') === 'collapse') { + if (window.getComputedStyle) { + width = parseFloat( window.getComputedStyle(this, null).width ); + } else { + // ie8 only + border = parseFloat( $this.css('border-width') ); + width = $this.outerWidth() - parseFloat( $this.css('padding-left') ) - parseFloat( $this.css('padding-right') ) - border; + } + } else { + width = $this.width(); + } + } + $cell.css({ + 'min-width': width, + 'max-width': width + }); + }); + }, resizeHeader = function() { stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0; spacing = 0; - // yes, I dislike browser sniffing, but it really is needed here :( - // webkit automatically compensates for border spacing - if (nonwkie) { - // Firefox & Opera use the border-spacing - // update border-spacing here because of demos that switch themes - spacing = parseInt($header.eq(0).css('border-left-width'), 10) * 2; - } - $stickyTable.css({ - left : $attach.length ? (parseInt($attach.css('padding-left'), 10) || 0) + parseInt(c.$table.css('padding-left'), 10) + - parseInt(c.$table.css('margin-left'), 10) + parseInt($table.css('border-left-width'), 10) : - $thead.offset().left - $win.scrollLeft() - spacing, - width: $table.width() - }); - $stickyCells.filter(':visible').each(function(i) { - var $cell = $header.filter(':visible').eq(i), - // some wibbly-wobbly... timey-wimey... stuff, to make columns line up in Firefox - offset = nonwkie && $(this).attr('data-column') === ( '' + parseInt(c.columns/2, 10) ) ? 1 : 0; - $(this) - .css({ width: $cell.width() - spacing }) - .find(innerHeader).width( $cell.find(innerHeader).width() - offset ); + $stickyWrap.css({ + left : $attach.length ? parseInt($attach.css('padding-left'), 10) || 0 : + $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing, + width: $table.outerWidth() }); + setWidth( $table, $stickyTable ); + setWidth( $header, $stickyCells ); }; // fix clone ID, if it exists - fixes #271 if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; } @@ -1600,42 +1703,60 @@ ts.addWidget({ $stickyTable.find('tbody, tfoot').remove(); if (!wo.stickyHeaders_includeCaption) { $stickyTable.find('caption').remove(); - } else { - $stickyTable.find('caption').css( 'margin-left', '-1px' ); } // issue #172 - find td/th in sticky header $stickyCells = $stickyThead.children().children(); - $stickyTable.css({ height:0, width:0, padding:0, margin:0, border:0 }); + $stickyTable.css({ height:0, width:0, margin: 0 }); // remove resizable block $stickyCells.find('.' + ts.css.resizer).remove(); // update sticky header class names to match real header after sorting $table .addClass('hasStickyHeaders') - .bind('pagerComplete.tsSticky', function() { + .bind('pagerComplete' + namespace, function() { resizeHeader(); }); ts.bindEvents(table, $stickyThead.children().children('.tablesorter-header')); // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. - $table.after( $stickyTable ); + $table.after( $stickyWrap ); + + // onRenderHeader is defined, we need to do something about it (fixes #641) + if (c.onRenderHeader) { + $stickyThead.children('tr').children().each(function(index){ + // send second parameter + c.onRenderHeader.apply( $(this), [ index, c, $stickyTable ] ); + }); + } + // make it sticky! - $win.bind('scroll.tsSticky resize.tsSticky', function(event) { + $xScroll.add($yScroll) + .unbind('scroll resize '.split(' ').join( namespace ) ) + .bind('scroll resize '.split(' ').join( namespace ), function(event) { if (!$table.is(':visible')) { return; } // fixes #278 + // Detect nested tables - fixes #724 + nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; var prefix = 'tablesorter-sticky-', offset = $table.offset(), - captionHeight = (wo.stickyHeaders_includeCaption ? 0 : $table.find('caption').outerHeight(true)), - scrollTop = ($attach.length ? $attach.offset().top : $win.scrollTop()) + stickyOffset - captionHeight, - tableHeight = $table.height() - ($stickyTable.height() + ($tfoot.height() || 0)), - isVisible = (scrollTop > offset.top) && (scrollTop < offset.top + tableHeight) ? 'visible' : 'hidden', + yWindow = $.isWindow( $yScroll[0] ), + xWindow = $.isWindow( $xScroll[0] ), + // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, + scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, + tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)), + isVisible = ( scrollTop > offset.top) && (scrollTop < offset.top + tableHeight) ? 'visible' : 'hidden', cssSettings = { visibility : isVisible }; + if ($attach.length) { - cssSettings.top = $attach.scrollTop(); - } else { + cssSettings.top = yWindow ? scrollTop : $attach.scrollTop(); + } + if (xWindow) { // adjust when scrolling horizontally - fixes issue #143 - cssSettings.left = $thead.offset().left - $win.scrollLeft() - spacing; + cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing; } - $stickyTable + if ($nestedSticky.length) { + cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; + } + $stickyWrap .removeClass(prefix + 'visible ' + prefix + 'hidden') .addClass(prefix + isVisible) .css(cssSettings); @@ -1650,14 +1771,14 @@ ts.addWidget({ } // look for filter widget - if ($table.hasClass('hasFilters')) { + if ($table.hasClass('hasFilters') && wo.filter_columnFilters) { // scroll table into view after filtering, if sticky header is active - #482 - $table.bind('filterEnd', function() { + $table.bind('filterEnd' + namespace, function() { // $(':focus') needs jQuery 1.6+ var $td = $(document.activeElement).closest('td'), column = $td.parent().children().index($td); // only scroll if sticky header is active - if ($stickyTable.hasClass(ts.css.stickyVis) && wo.stickyHeaders_filteredToTop) { + if ($stickyWrap.hasClass(ts.css.stickyVis) && wo.stickyHeaders_filteredToTop) { // scroll to original table (not sticky clone) window.scrollTo(0, $table.position().top); // give same input/select focus; check if c.$filters exists; fixes #594 @@ -1677,14 +1798,16 @@ ts.addWidget({ }, remove: function(table, c, wo) { + var namespace = c.namespace + 'stickyheaders '; c.$table .removeClass('hasStickyHeaders') - .unbind('pagerComplete.tsSticky') - .find('.' + ts.css.sticky).remove(); + .unbind( 'pagerComplete filterEnd '.split(' ').join(namespace) ) + .next('.' + ts.css.stickyWrap).remove(); if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table // don't unbind if any table on the page still has stickyheaders applied if (!$('.hasStickyHeaders').length) { - $(window).unbind('scroll.tsSticky resize.tsSticky'); + $(window).add(wo.stickyHeaders_xScroll).add(wo.stickyHeaders_yScroll).add(wo.stickyHeaders_attachTo) + .unbind( 'scroll resize '.split(' ').join(namespace) ); } ts.addHeaderResizeEvent(table, false); } @@ -1710,6 +1833,8 @@ ts.addWidget({ var $rows, $columns, $column, column, timer, storedSizes = {}, $table = c.$table, + $wrap = $table.parent(), + overflow = $table.parent().css('overflow') === 'auto', mouseXPosition = 0, $target = null, $next = null, @@ -1722,6 +1847,14 @@ ts.addWidget({ $target.width( targetWidth + leftEdge ); if ($target.width() !== targetWidth && fullWidth) { $next.width( $next.width() - leftEdge ); + } else if (overflow) { + $table.width(function(i, w){ + return w + leftEdge; + }); + if (!$next.length) { + // if expanding right-most column, scroll the wrapper + $wrap[0].scrollLeft = $table.width(); + } } mouseXPosition = event.pageX; }, @@ -1909,4 +2042,4 @@ ts.addWidget({ } }); -})(jQuery); +})(jQuery, window); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js index 3a46110..95b8bf1 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js @@ -1,57 +1,80 @@ /*! * Extract out date parsers + * 10/26/2014 (v2.18.0) */ /*jshint jquery:true */ ;(function($){ "use strict"; + var regex = { + usLong : /[A-Z]{3,10}\.?\s+\d{1,2},?\s+(?:\d{4})(?:\s+\d{1,2}:\d{2}(?::\d{2})?(?:\s+[AP]M)?)?/i, + mdy : /(\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4}(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?)/i, + + dmy : /(\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4}(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?)/i, + dmyreplace : /(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, + + ymd : /(\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2}(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?)/i, + ymdreplace : /(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/ + }; + /*! extract US Long Date (ignore any other text) * e.g. "Sue's Birthday! Jun 26, 2004 7:22 AM (8# 2oz)" - * demo: http://jsfiddle.net/abkNM/2293/ + * demo: http://jsfiddle.net/Mottie/abkNM/4165/ */ $.tablesorter.addParser({ id: "extractUSLongDate", - is: function (s) { + is: function () { // don't auto detect this parser return false; }, - format: function (s, table) { - var date = s.match(/[A-Z]{3,10}\.?\s+\d{1,2},?\s+(?:\d{4})(?:\s+\d{1,2}:\d{2}(?::\d{2})?(?:\s+[AP]M)?)?/i); - return date ? $.tablesorter.formatFloat((new Date(date[0]).getTime() || ''), table) || s : s; + format: function (s) { + var date, + str = s ? s.match(regex.usLong) : s; + if (str) { + date = new Date( str[0] ); + return date instanceof Date && isFinite(date) ? date.getTime() : s; + } + return s; }, type: "numeric" }); /*! extract MMDDYYYY (ignore any other text) - * demo: http://jsfiddle.net/Mottie/abkNM/2418/ + * demo: http://jsfiddle.net/Mottie/abkNM/4166/ */ $.tablesorter.addParser({ id: "extractMMDDYYYY", - is: function (s) { + is: function () { // don't auto detect this parser return false; }, - format: function (s, table) { - var date = s.replace(/\s+/g," ").replace(/[\-.,]/g, "/").match(/(\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4}(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?)/i); - return date ? $.tablesorter.formatFloat((new Date(date[0]).getTime() || ''), table) || s : s; + format: function (s) { + var date, + str = s ? s.replace(/\s+/g," ").replace(/[\-.,]/g, "/").match(regex.mdy) : s; + if (str) { + date = new Date( str[0] ); + return date instanceof Date && isFinite(date) ? date.getTime() : s; + } + return s; }, type: "numeric" }); /*! extract DDMMYYYY (ignore any other text) - * demo: http://jsfiddle.net/Mottie/abkNM/2419/ + * demo: http://jsfiddle.net/Mottie/abkNM/4167/ */ $.tablesorter.addParser({ id: "extractDDMMYYYY", - is: function (s) { + is: function () { // don't auto detect this parser return false; }, - format: function (s, table) { - var date = s.replace(/\s+/g," ").replace(/[\-.,]/g, "/").match(/(\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4}(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?)/i); - if (date) { - date = date[0].replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$2/$1/$3"); - return $.tablesorter.formatFloat((new Date(date).getTime() || ''), table) || s; + format: function (s) { + var date, + str = s ? s.replace(/\s+/g," ").replace(/[\-.,]/g, "/").match(regex.dmy) : s; + if (str) { + date = new Date( str[0].replace(regex.dmyreplace, "$2/$1/$3") ); + return date instanceof Date && isFinite(date) ? date.getTime() : s; } return s; }, @@ -59,19 +82,20 @@ }); /*! extract YYYYMMDD (ignore any other text) - * demo: http://jsfiddle.net/Mottie/abkNM/2420/ + * demo: http://jsfiddle.net/Mottie/abkNM/4168/ */ $.tablesorter.addParser({ id: "extractYYYYMMDD", - is: function (s) { + is: function () { // don't auto detect this parser return false; }, - format: function (s, table) { - var date = s.replace(/\s+/g," ").replace(/[\-.,]/g, "/").match(/(\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2}(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?)/i); - if (date) { - date = date[0].replace(/(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/, "$2/$3/$1"); - return $.tablesorter.formatFloat((new Date(date).getTime() || ''), table) || s; + format: function (s) { + var date, + str = s ? s.replace(/\s+/g," ").replace(/[\-.,]/g, "/").match(regex.ymd) : s; + if (str) { + date = new Date( str[0].replace(regex.ymdreplace, "$2/$3/$1") ); + return date instanceof Date && isFinite(date) ? date.getTime() : s; } return s; }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js index 0a133c3..fc21203 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js @@ -1,4 +1,4 @@ -/*! ISO-8601 date parser +/*! ISO-8601 date parser - 10/26/2014 (v2.18.0) * This parser will work with dates in ISO8601 format * 2013-02-18T18:18:44+00:00 * Written by Sean Ellingham :https://github.com/seanellingham @@ -9,13 +9,14 @@ "use strict"; var iso8601date = /^([0-9]{4})(-([0-9]{2})(-([0-9]{2})(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?$/; + $.tablesorter.addParser({ id : 'iso8601date', is : function(s) { - return s.match(iso8601date); + return s ? s.match(iso8601date) : false; }, format : function(s) { - var result = s.match(iso8601date); + var result = s ? s.match(iso8601date) : s; if (result) { var date = new Date(result[1], 0, 1); if (result[3]) { date.setMonth(result[3] - 1); } @@ -24,7 +25,7 @@ if (result[8]) { date.setMinutes(result[8]); } if (result[10]) { date.setSeconds(result[10]); } if (result[12]) { date.setMilliseconds(Number('0.' + result[12]) * 1000); } - return date; + return date.getTime(); } return s; }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js index 4c725d6..04eb679 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js @@ -1,5 +1,5 @@ -/*! Month parser - * Demo: http://jsfiddle.net/Mottie/abkNM/477/ +/*! Month parser - 10/26/2014 (v2.18.0) + * Demo: http://jsfiddle.net/Mottie/abkNM/4169/ */ /*jshint jquery:true */ ;(function($){ @@ -18,14 +18,20 @@ return false; }, format: function(s, table) { - var j = -1, c = table.config, - n = c.ignoreCase ? s.toLocaleLowerCase() : s; - $.each(ts.dates[ 'month' + (c.ignoreCase ? 'Lower' : 'Cased') ], function(i,v){ - if (j < 0 && n.match(v)) { j = i; } - }); - // return s (original string) if there isn't a match - // (non-weekdays will sort separately and empty cells will sort as expected) - return j < 0 ? s : j; + if (s) { + var j = -1, c = table.config, + n = c.ignoreCase ? s.toLocaleLowerCase() : s; + $.each(ts.dates[ 'month' + (c.ignoreCase ? 'Lower' : 'Cased') ], function(i,v){ + if (j < 0 && n.match(v)) { + j = i; + return false; + } + }); + // return s (original string) if there isn't a match + // (non-weekdays will sort separately and empty cells will sort as expected) + return j < 0 ? s : j; + } + return s; }, type: "numeric" }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js index 8d322bc..0b43016 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js @@ -1,16 +1,18 @@ -/*! Two digit year parser - * Demo: http://jsfiddle.net/Mottie/abkNM/427/ +/*! Two digit year parser - 10/26/2014 (v2.18.0) + * Demo: http://mottie.github.io/tablesorter/docs/example-parsers-dates.html */ /*jshint jquery:true */ ;(function($){ "use strict"; - var ts = $.tablesorter, - // Make the date be within +/- range of the 2 digit year // so if the current year is 2020, and the 2 digit year is 80 (2080 - 2020 > 50), it becomes 1980 // if the 2 digit year is 50 (2050 - 2020 < 50), then it becomes 2050. - range = 50; + var range = 50, + + // no need to change any of the code below + ts = $.tablesorter, + now = new Date().getFullYear(); ts.dates = $.extend({}, ts.dates, { regxxxxyy: /(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{2})/, @@ -18,21 +20,26 @@ }); ts.formatDate = function(s, regex, format, table){ - var n = s - // replace separators - .replace(/\s+/g," ").replace(/[-.,]/g, "/") - // reformat xx/xx/xx to mm/dd/19yy; - .replace(regex, format), - d = new Date(n), - y = d.getFullYear(), - rng = table && table.config.dateRange || range, - now = new Date().getFullYear(); - // if date > 50 years old (set range), add 100 years - // this will work when people start using "50" and mean "2050" - while (now - y > rng) { - y += 100; + if (s) { + var y, rng, + n = s + // replace separators + .replace(/\s+/g," ").replace(/[-.,]/g, "/") + // reformat xx/xx/xx to mm/dd/19yy; + .replace(regex, format), + d = new Date(n); + if ( d instanceof Date && isFinite(d) ) { + y = d.getFullYear(); + rng = table && table.config.dateRange || range; + // if date > 50 years old (set range), add 100 years + // this will work when people start using "50" and mean "2050" + while (now - y > rng) { + y += 100; + } + return d.setFullYear(y); + } } - return d.setFullYear(y) || s; + return s; }; $.tablesorter.addParser({ diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js index 62f4504..41499de 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js @@ -1,5 +1,5 @@ -/*! Weekday parser - * Demo: http://jsfiddle.net/Mottie/abkNM/477/ +/*! Weekday parser - 10/26/2014 (v2.18.0) + * Demo: http://jsfiddle.net/Mottie/abkNM/4169/ */ /*jshint jquery:true */ ;(function($){ @@ -18,14 +18,20 @@ return false; }, format: function(s, table) { - var j = -1, c = table.config; - s = c.ignoreCase ? s.toLocaleLowerCase() : s; - $.each(ts.dates[ 'weekday' + (c.ignoreCase ? 'Lower' : 'Cased') ], function(i,v){ - if (j < 0 && s.match(v)) { j = i; } - }); - // return s (original string) if there isn't a match - // (non-weekdays will sort separately and empty cells will sort as expected) - return j < 0 ? s : j; + if (s) { + var j = -1, c = table.config; + s = c.ignoreCase ? s.toLocaleLowerCase() : s; + $.each(ts.dates[ 'weekday' + (c.ignoreCase ? 'Lower' : 'Cased') ], function(i,v){ + if (j < 0 && s.match(v)) { + j = i; + return false; + } + }); + // return s (original string) if there isn't a match + // (non-weekdays will sort separately and empty cells will sort as expected) + return j < 0 ? s : j; + } + return s; }, type: "numeric" }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js index 52ece6c..9a5e3d8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js @@ -1,12 +1,13 @@ /*! * Extract dates using popular natural language date parsers + * 10/26/2014 (v2.18.0) */ /*jshint jquery:true */ ;(function($){ "use strict"; /*! Sugar (http://sugarjs.com/dates#comparing_dates) - * demo: http://jsfiddle.net/Mottie/abkNM/551/ + * demo: http://jsfiddle.net/Mottie/abkNM/4163/ */ $.tablesorter.addParser({ id: "sugar", @@ -14,13 +15,14 @@ return false; }, format: function(s) { - return Date.create ? Date.create(s).getTime() || s : new Date(s).getTime() || s; + var date = Date.create ? Date.create(s) : s ? new Date(s) : s; + return date instanceof Date && isFinite(date) ? date.getTime() : s; }, type: "numeric" }); /*! Datejs (http://www.datejs.com/) - * demo: http://jsfiddle.net/Mottie/abkNM/550/ + * demo: http://jsfiddle.net/Mottie/abkNM/4164/ */ $.tablesorter.addParser({ id: "datejs", @@ -28,7 +30,8 @@ return false; }, format: function(s) { - return Date.parse && Date.parse(s) || s; + var date = Date.parse ? Date.parse(s) : s ? new Date(s) : s; + return date instanceof Date && isFinite(date) ? date.getTime() : s; }, type: "numeric" }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js new file mode 100644 index 0000000..2e72c46 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js @@ -0,0 +1,121 @@ +/*! Named Numbers Parser - 10/26/2014 (v2.18.0) + * code modified from http://stackoverflow.com/a/12014376/145346 + */ +/*jshint jquery:true */ +;(function($){ +"use strict"; + + // Change language of the named numbers as needed + var named = { + negative: [ 'negative', 'minus' ], + numbers : { + 'zero' : 0, + 'one' : 1, + 'two' : 2, + 'three' : 3, + 'four' : 4, + 'five' : 5, + 'six' : 6, + 'seven' : 7, + 'eight' : 8, + 'nine' : 9, + 'ten' : 10, + 'eleven' : 11, + 'twelve' : 12, + 'thirteen' : 13, + 'fourteen' : 14, + 'fifteen' : 15, + 'sixteen' : 16, + 'seventeen' : 17, + 'eighteen' : 18, + 'nineteen' : 19, + 'twenty' : 20, + 'thirty' : 30, + 'forty' : 40, + 'fourty' : 40, // common misspelling + 'fifty' : 50, + 'sixty' : 60, + 'seventy' : 70, + 'eighty' : 80, + 'ninety' : 90 + }, + // special case + hundred : 'hundred', + // multiples + powers : { + 'thousand' : 1e3, + 'million' : 1e6, + 'billion' : 1e9, + 'trillion' : 1e12, + 'quadrillion' : 1e15, + 'quintillion' : 1e18, + 'sextillion' : 1e21, + 'septillion' : 1e24, + 'octillion' : 1e27, + 'nonillion' : 1e30, + 'decillion' : 1e33, + 'undecillion' : 1e36, + 'duodecillion' : 1e39, + 'tredecillion' : 1e42, + 'quattuordecillion' : 1e45, + 'quindecillion' : 1e48, + 'sexdecillion' : 1e51, + 'septendecillion' : 1e54, + 'octodecillion' : 1e57, + 'novemdecillion' : 1e60, + 'vigintillion' : 1e63, + 'unvigintillion' : 1e66, + 'duovigintillion' : 1e69, + 'trevigintillion' : 1e72, + 'quattuorvigintillion' : 1e75, + 'quinvigintillion' : 1e78, + 'sexvigintillion' : 1e81, + 'septenvigintillion' : 1e84, + 'octovigintillion' : 1e87, + 'novemvigintillion' : 1e90, + 'trigintillion' : 1e93, + 'untrigintillion' : 1e96, + 'duotrigintillion' : 1e99, + 'googl' : 1e100 + } + }, + result, group, + negativeRegex = new RegExp('(' + named.negative.join('|') + ')'), + calc = function ( word, table ) { + var num = named.numbers.hasOwnProperty( word ) ? named.numbers[ word ] : null, + power = named.powers.hasOwnProperty( word ) ? named.powers[ word ] : null; + if ( !num && !isNaN( word ) ) { + num = $.tablesorter.formatFloat( word || '', table ); + } + if ( num !== null ) { + group += num; + } else if ( word === named.hundred ) { + group *= 100; + } else if ( power !== null ) { + result += group * power; + group = 0; + } + }; + + $.tablesorter.addParser({ + id: "namedNumbers", + is: function () { + return false; + }, + format: function ( str, table ) { + result = 0; + group = 0; + var indx, + arry = ( str || '' ).split( /[\s-]+/ ), + len = arry.length; + for ( indx = 0; indx < len; indx++ ) { + calc( arry[ indx ].toLowerCase(), table ); + } + result = ( result + group ) * ( str.match( negativeRegex ) ? -1 : 1 ); + // make sure to let zero get parsed, so check hasOwnProperty + return result || named.numbers.hasOwnProperty( str ) ? result : $.tablesorter.formatFloat( str || '', table ); + }, + type: "numeric" + }); + +})( jQuery ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ipv6.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js similarity index 62% rename from vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ipv6.js rename to vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js index 121bc37..04166e9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ipv6.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js @@ -1,15 +1,18 @@ -/*! IPv6 Address parser (WIP) -* IPv6 Address (ffff:0000:0000:0000:0000:0000:0000:0000) -* needs to support short versions like "::8" or "1:2::7:8" -* and "::00:192.168.10.184" (embedded IPv4 address) -* see http://www.intermapper.com/support/tools/IPV6-Validator.aspx -*/ +/*! Network parsers - IPv4, IPv6 and MAC Addresses - 10/26/2014 (v2.18.0) */ /*global jQuery: false */ ;(function($){ "use strict"; - var ts = $.tablesorter; + var ts = $.tablesorter, + ipv4Format, + ipv4Is; + /*! IPv6 Address parser (WIP) + * IPv6 Address (ffff:0000:0000:0000:0000:0000:0000:0000) + * needs to support short versions like "::8" or "1:2::7:8" + * and "::00:192.168.10.184" (embedded IPv4 address) + * see http://www.intermapper.com/support/tools/IPV6-Validator.aspx + */ $.extend( ts.regex, {}, { ipv4Validate : /((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})/, ipv4Extract : /([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/, @@ -20,14 +23,14 @@ }); ts.addParser({ - id: "ipv6Address", + id: 'ipv6Address', is: function(s) { return ts.regex.ipv6Validate.test(s); }, format: function(address, table) { // code modified from http://forrst.com/posts/JS_Expand_Abbreviated_IPv6_Addresses-1OR var i, t, sides, groups, groupsPresent, - hex = table ? (typeof table === "boolean" ? table : table && table.config.ipv6HexFormat || false) : false, + hex = table ? (typeof table === 'boolean' ? table : table && table.config.ipv6HexFormat || false) : false, fullAddress = '', expandedAddress = '', validGroupCount = 8, @@ -44,23 +47,23 @@ address = address.replace( ts.regex.ipv4Extract, t ); } - if (address.indexOf("::") == -1) { + if (address.indexOf('::') == -1) { // All eight groups are present fullAddress = address; } else { - // Consecutive groups of zeroes have been collapsed with "::". - sides = address.split("::"); + // Consecutive groups of zeroes have been collapsed with '::'. + sides = address.split('::'); groupsPresent = 0; for (i = 0; i < sides.length; i++) { - groupsPresent += sides[i].split(":").length; + groupsPresent += sides[i].split(':').length; } - fullAddress += sides[0] + ":"; + fullAddress += sides[0] + ':'; for (i = 0; i < validGroupCount - groupsPresent; i++) { - fullAddress += "0000:"; + fullAddress += '0000:'; } fullAddress += sides[1]; } - groups = fullAddress.split(":"); + groups = fullAddress.split(':'); for (i = 0; i < validGroupCount; i++) { // it's fastest & easiest for tablesorter to sort decimal values (vs hex) groups[i] = hex ? ('0000' + groups[i]).slice(-4) : @@ -70,7 +73,53 @@ return hex ? expandedAddress : expandedAddress.replace(/:/g, ''); }, // uses natural sort hex compare - type: "numeric" + type: 'numeric' + }); + + // ipv4 address + // moved here from jquery.tablesorter.js (core file) + ipv4Is = function(s) { + return (/^\d{1,3}[\.]\d{1,3}[\.]\d{1,3}[\.]\d{1,3}$/).test(s); + }; + ipv4Format = function(s, table) { + var i, a = s ? s.split('.') : '', + r = '', + l = a.length; + for (i = 0; i < l; i++) { + r += ('000' + a[i]).slice(-3); + } + return s ? ts.formatFloat(r, table) : s; + }; + + // duplicate "ipAddress" as "ipv4Address" (to maintain backwards compatility) + ts.addParser({ + id: 'ipAddress', + is: ipv4Is, + format: ipv4Format, + type: 'numeric' + }); + ts.addParser({ + id: 'ipv4Address', + is: ipv4Is, + format: ipv4Format, + type: 'numeric' + }); + + ts.addParser({ + id: 'MAC', + is: function(s) { + return ts.regex.ipv6Validate.test(s); + }, + format: function(s) { + var t = '', + val = s.replace(/[:.-]/g, '').match(/\w{2}/g); + $.each(val, function(i, v){ + t += ( '000' + parseInt(v, 16) ).slice(-3); + }); + return t; + }, + // uses natural sort hex compare + type: 'numeric' }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index 22d641b..bf0ac42 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Column Selector/Responsive table widget (beta) for TableSorter 5/22/2014 (v2.17.0) +/* Column Selector/Responsive table widget (beta) for TableSorter - 10/26/2014 (v2.18.0) * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -11,8 +11,8 @@ var ts = $.tablesorter, namespace = '.tscolsel', tsColSel = ts.columnSelector = { - queryAll : '@media only all { [columns] { display: none; } }', - queryBreak : '@media all and (min-width: [size]) { [columns] { display: table-cell; } }', + queryAll : '@media only all { [columns] { display: none; } } ', + queryBreak : '@media all and (min-width: [size]) { [columns] { display: table-cell; } } ', init: function(table, c, wo) { var $t, colSel; @@ -81,7 +81,6 @@ tsColSel = ts.columnSelector = { colId = $this.attr('data-column'), state = ts.getData(this, c.headers[colId], 'columnSelector'); - // if this column not hidable at all // include getData check (includes "columnSelector-false" class, data attribute, etc) if ( isNaN(priority) && priority.length > 0 || state === 'disable' || @@ -191,6 +190,7 @@ tsColSel = ts.columnSelector = { breaks = []; c.$headers.filter('[' + wo.columnSelector_priority + '=' + (priority + 1) + ']').each(function(){ column = parseInt($(this).attr('data-column'), 10) + 1; + breaks.push(prefix + ' col:nth-child(' + column + ')'); breaks.push(prefix + ' tr th:nth-child(' + column + ')'); breaks.push(prefix + ' tr td:nth-child(' + column + ')'); }); @@ -204,9 +204,11 @@ tsColSel = ts.columnSelector = { if (colSel.$style) { colSel.$style.prop('disabled', true); } - colSel.$breakpoints - .prop('disabled', false) - .html( tsColSel.queryAll.replace(/\[columns\]/g, mediaAll.join(',')) + breakpts ); + if (mediaAll.length) { + colSel.$breakpoints + .prop('disabled', false) + .html( tsColSel.queryAll.replace(/\[columns\]/g, mediaAll.join(',')) + breakpts ); + } }, updateCols: function(c, wo) { @@ -220,6 +222,7 @@ tsColSel = ts.columnSelector = { colSel.$container.find('input[data-column]').filter('[data-column!="auto"]').each(function(){ if (!this.checked) { column = parseInt( $(this).attr('data-column'), 10 ) + 1; + styles.push(prefix + ' col:nth-child(' + column + ')'); styles.push(prefix + ' tr th:nth-child(' + column + ')'); styles.push(prefix + ' tr td:nth-child(' + column + ')'); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js index 27f5f51..edf5a3e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js @@ -1,68 +1,95 @@ -/*! tablesorter CSS Sticky Headers widget - updated 5/5/2014 (v2.16.4) +/*! tablesorter CSS Sticky Headers widget - updated 10/26/2014 (v2.18.0) * Requires a modern browser, tablesorter v2.8+ */ /*jshint jquery:true, unused:false */ ;(function($){ - "use strict"; + 'use strict'; - $.tablesorter.addWidget({ - id: "cssStickyHeaders", + var ts = $.tablesorter; + + ts.addWidget({ + id: 'cssStickyHeaders', priority: 10, options: { cssStickyHeaders_offset : 0, cssStickyHeaders_addCaption : false, + // jQuery selector or object to attach sticky header to cssStickyHeaders_attachTo : null, - cssStickyHeaders_filteredToTop : true, - cssStickyHeaders_zIndex : 10 + cssStickyHeaders_filteredToTop : true }, init : function(table, thisWidget, c, wo) { - var $attach = $(wo.cssStickyHeaders_attachTo), - namespace = '.cssstickyheader', - $thead = c.$table.children('thead'), - $caption = c.$table.find('caption'), - $win = $attach.length ? $attach : $(window); - $win.bind('scroll resize '.split(' ').join(namespace + ' '), function() { + var isIE = 'ActiveXObject' in window, // target all versions of IE + $table = c.$table, + $attach = $(wo.cssStickyHeaders_attachTo), + namespace = c.namespace + 'cssstickyheader ', + $thead = $table.children('thead'), + $caption = $table.children('caption'), + $win = $attach.length ? $attach : $(window), + $parent = $table.parent().closest('table.' + ts.css.table), + $parentThead = $parent.length && ts.hasWidget($parent[0], 'cssStickyHeaders') ? $parent.children('thead') : []; + + $win + .unbind('scroll resize '.split(' ').join(namespace)) + .bind('scroll resize '.split(' ').join(namespace), function() { var top = $attach.length ? $attach.offset().top : $win.scrollTop(), // add caption height; include table padding top & border-spacing or text may be above the fold (jQuery UI themes) // border-spacing needed in Firefox, but not webkit... not sure if I should account for that - captionTop = wo.cssStickyHeaders_addCaption ? $caption.outerHeight(true) + - (parseInt(c.$table.css('padding-top'), 10) || 0) + (parseInt(c.$table.css('border-spacing'), 10) || 0) : 0, - bottom = c.$table.height() - $thead.height() - (c.$table.find('tfoot').height() || 0) - captionTop, - deltaY = top - $thead.offset().top + (parseInt(c.$table.css('border-top-width'), 10) || 0) + - (wo.cssStickyHeaders_offset || 0) + captionTop, - finalY = (deltaY > 0 && deltaY <= bottom ? deltaY : 0), - // IE can only transform header cells - fixes #447 thanks to @gakreol! - $cells = $thead.children().children(); + captionHeight = wo.cssStickyHeaders_addCaption ? ( $caption.outerHeight(true) || 0 ) + + ( parseInt( $table.css('padding-top'), 10 ) || 0 ) + ( parseInt( $table.css('border-spacing'), 10 ) || 0 ) : 0, + + bottom = $table.height() - $thead.height() - ( $table.children('tfoot').height() || 0 ) - captionHeight, + // get bottom of nested sticky headers + nestedStickyTop = $parentThead.length ? ( isIE ? $parent.data('cssStickyHeaderTop') : $parentThead.offset().top ) + + $parentThead.height() - $win.scrollTop() : 0, + + // Detect nested tables - fixes #724 + deltaY = top - $table.offset().top + nestedStickyTop + ( parseInt( $table.css('border-top-width'), 10 ) || 0 ) + + // Again, I dislike browser sniffing... but I have no idea why I need to include a captionHeight + // for Firefox here and not for Chrome. Even IE behaves, sorta! + ( wo.cssStickyHeaders_offset || 0 ) + ( navigator.userAgent.toLowerCase().indexOf('firefox') > -1 ? captionHeight : 0 ), + + finalY = deltaY > 0 && deltaY <= bottom ? deltaY : 0, + + // All IE (even IE11) can only transform header cells - fixes #447 thanks to @gakreol! + $cells = isIE ? $thead.children().children() : $thead; + + // more crazy IE stuff.. somehow the second nested table is completely ignored + if (isIE) { + c.$table.data('cssStickyHeaderTop', finalY - ( $parentThead.length ? $parentThead.height() : 0 )); + if ($parentThead.length) { + top = $parent.data('cssStickyHeaderTop') - $parentThead.height(); + finalY = top > 0 && top <= bottom ? top : 0; + } + } + if (wo.cssStickyHeaders_addCaption) { $cells = $cells.add($caption); } + $cells.css({ - "position" : "relative", - "z-index" : wo.cssStickyHeaders_zIndex, - "transform" : finalY === 0 ? "" : "translate(0px," + finalY + "px)", - "-ms-transform" : finalY === 0 ? "" : "translate(0px," + finalY + "px)", - "-webkit-transform" : finalY === 0 ? "" : "translate(0px," + finalY + "px)" + 'transform' : finalY === 0 ? '' : 'translate(0px,' + finalY + 'px)', + '-ms-transform' : finalY === 0 ? '' : 'translate(0px,' + finalY + 'px)', + '-webkit-transform' : finalY === 0 ? '' : 'translate(0px,' + finalY + 'px)' }); }); - c.$table.bind('filterEnd', function() { + $table.unbind('filterEnd' + namespace).bind('filterEnd' + namespace, function() { if (wo.cssStickyHeaders_filteredToTop) { // scroll top of table into view - window.scrollTo(0, c.$table.position().top); + window.scrollTo(0, $table.position().top); } }); }, remove: function(table, c, wo){ - var namespace = '.cssstickyheader'; - $(window).unbind('scroll resize '.split(' ').join(namespace + ' ')); + var namespace = c.namespace + 'cssstickyheader '; + $(window).unbind('scroll resize '.split(' ').join(namespace)); c.$table - .unbind('update updateAll '.split(' ').join(namespace + ' ')) + .unbind('filterEnd scroll resize '.split(' ').join(namespace)) + .add( c.$table.children('thead').children().children() ) .children('thead, caption').css({ - "position" : "", - "z-index" : "", - "transform" : "", - "-ms-transform" : "", - "-webkit-transform" : "" + 'transform' : '', + '-ms-transform' : '', + '-webkit-transform' : '' }); } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js index 2a3a19a..d02d8f2 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js @@ -1,200 +1,223 @@ -/*! tablesorter Editable Content widget - updated 9/15/2014 (core v2.17.8) +/*! tablesorter Editable Content widget - updated 10/26/2014 (v2.18.0) * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ -;(function($){ - "use strict"; +;( function( $ ){ + 'use strict'; - $.tablesorter.addWidget({ - id: 'editable', - options : { - editable_columns : [], - editable_enterToAccept : true, - editable_autoAccept : true, - editable_autoResort : false, - editable_wrapContent : '<div>', // wrap the cell content... makes this widget work in IE, and with autocomplete - editable_trimContent : true, // trim content inside of contenteditable (remove tabs & carriage returns) - editable_validate : null, // function(text, originalText){ return text; } - editable_focused : null, // function(text, columnIndex, $element) {} - editable_blur : null, // function(text, columnIndex, $element) { } - editable_selectAll : false, // true/false or function(text, columnIndex, $element) { return true; } - editable_noEdit : 'no-edit', - editable_editComplete : 'editComplete' - }, - init: function(table, thisWidget, c, wo){ - if ( !wo.editable_columns.length ) { return; } - var indx, tmp, $t, - cols = [], - editComplete = function($cell, refocus){ - $cell - .removeClass('tseditable-last-edited-cell') - .trigger( wo.editable_editComplete, [c] ); - // restore focus last cell after updating - if (refocus) { - setTimeout(function(){ - $cell.focus(); - }, 50); - } - }, - selectAll = function(cell){ - setTimeout(function(){ - // select all text in contenteditable - // see http://stackoverflow.com/a/6150060/145346 - var range = document.createRange(); - range.selectNodeContents(cell); - var sel = window.getSelection(); - sel.removeAllRanges(); - sel.addRange(range); - }, 100); - }; - - if ( $.type(wo.editable_columns) === "string" && wo.editable_columns.indexOf('-') >= 0 ) { - // editable_columns can contain a range string (i.e. "2-4" ) - tmp = wo.editable_columns.split('-'); - indx = parseInt(tmp[0],10) || 0; - tmp = parseInt(tmp[1],10) || (c.columns - 1); - if ( tmp > c.columns ) { tmp = c.columns - 1; } - for ( ; indx <= tmp; indx++ ) { - cols.push('td:nth-child(' + (indx + 1) + ')'); +var tse = $.tablesorter.editable = { + + editComplete: function( c, wo, $cell, refocus ) { + $cell + .removeClass( 'tseditable-last-edited-cell' ) + .trigger( wo.editable_editComplete, [ c ] ); + // restore focus last cell after updating + if ( refocus ) { + setTimeout( function() { + $cell.focus(); + }, 50 ); + } + }, + + selectAll: function( cell ) { + setTimeout( function() { + // select all text in contenteditable + // see http://stackoverflow.com/a/6150060/145346 + var sel, range = document.createRange(); + range.selectNodeContents( cell ); + sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange( range ); + }, 100 ); + }, + + update: function( c, wo ) { + var indx, tmp, $t, + cols = []; + + if ( $.type( wo.editable_columns ) === 'string' && wo.editable_columns.indexOf( '-' ) >= 0 ) { + // editable_columns can contain a range string ( i.e. '2-4' ) + tmp = wo.editable_columns.split( /\s*-\s*/ ); + indx = parseInt( tmp[ 0 ], 10 ) || 0; + tmp = parseInt( tmp[ 1 ], 10 ) || ( c.columns - 1 ); + if ( tmp > c.columns ) { + tmp = c.columns - 1; + } + for ( ; indx <= tmp; indx++ ) { + cols.push( 'td:nth-child(' + ( indx + 1 ) + ')' ); + } + } else if ( $.isArray( wo.editable_columns ) ) { + $.each( wo.editable_columns, function( i, col ) { + if ( col < c.columns ) { + cols.push( 'td:nth-child(' + ( col + 1 ) + ')' ); } - } else if ( $.isArray(wo.editable_columns) ) { - $.each(wo.editable_columns, function(i, col){ - if ( col < c.columns ) { - cols.push('td:nth-child(' + (col + 1) + ')'); + }); + } + tmp = $( '<div>' ).wrapInner( wo.editable_wrapContent ).children().length || $.isFunction( wo.editable_wrapContent ); + // IE does not allow making TR/TH/TD cells directly editable ( issue #404 ) + // so add a div or span inside ( it's faster than using wrapInner() ) + c.$tbodies.find( cols.join( ',' ) ).not( '.' + wo.editable_noEdit ).each( function() { + // test for children, if they exist, then make the children editable + $t = $( this ); + + if ( tmp && $t.children().length === 0 ) { + $t.wrapInner( wo.editable_wrapContent ); + } + if ( $t.children().length ) { + // make all children content editable + $t.children().not( '.' + wo.editable_noEdit ).each( function() { + var $this = $( this ); + if ( wo.editable_trimContent ) { + $this.text( function( i, txt ) { + return $.trim( txt ); + }); } + $this.prop( 'contenteditable', true ); }); + } else { + if ( wo.editable_trimContent ) { + $t.text( function( i, txt ) { + return $.trim( txt ); + }); + } + $t.prop( 'contenteditable', true ); } - tmp = $('<div>').wrapInner(wo.editable_wrapContent).children().length || $.isFunction(wo.editable_wrapContent); - // IE does not allow making TR/TH/TD cells directly editable (issue #404) - // so add a div or span inside ( it's faster than using wrapInner() ) - c.$tbodies.find( cols.join(',') ).not( '.' + wo.editable_noEdit ).each(function(){ - // test for children, if they exist, then make the children editable - $t = $(this); - - if (tmp && $t.children().length === 0) { - $t.wrapInner( wo.editable_wrapContent ); + }); + }, + + bindEvents: function( c, wo ) { + c.$table + .off( 'updateComplete pagerComplete '.split( ' ' ).join( '.tseditable' ) ) + .on( 'updateComplete pagerComplete '.split( ' ' ).join( '.tseditable' ), function() { + tse.update( c, wo ); + }); + + c.$tbodies + .off( 'mouseleave focus blur focusout keydown '.split( ' ' ).join( '.tseditable ' ) ) + .on( 'mouseleave.tseditable', function() { + if ( c.$table.data( 'contentFocused' ) ) { + // change to 'true' instead of element to allow focusout to process + c.$table.data( 'contentFocused', true ); + $( ':focus' ).trigger( 'focusout' ); } - if ($t.children().length) { - // make all children content editable - $t.children().not('.' + wo.editable_noEdit).each(function(){ - var $this = $(this); - if (wo.editable_trimContent) { - $this.text(function(i, txt){ - return $.trim(txt); - }); + }) + .on( 'focus.tseditable', '[contenteditable]', function( e ) { + clearTimeout( $( this ).data( 'timer' ) ); + c.$table.data( 'contentFocused', e.target ); + var $this = $( this ), + selAll = wo.editable_selectAll, + column = $this.closest( 'td' ).index(), + txt = $.trim( $this.text() ); + if ( wo.editable_enterToAccept ) { + // prevent enter from adding into the content + $this.on( 'keydown.tseditable', function( e ){ + if ( e.which === 13 ) { + e.preventDefault(); } - $this.prop( 'contenteditable', true ); }); - } else { - if (wo.editable_trimContent) { - $t.text(function(i, txt){ - return $.trim(txt); - }); - } - $t.prop( 'contenteditable', true ); } - }); - c.$tbodies - .on('mouseleave.tseditable', function(){ - if ( c.$table.data('contentFocused') ) { - // change to "true" instead of element to allow focusout to process - c.$table.data( 'contentFocused', true ); - $(':focus').trigger('focusout'); - } - }) - .on('focus.tseditable', '[contenteditable]', function(e){ - clearTimeout( $(this).data('timer') ); - c.$table.data( 'contentFocused', e.target ); - var $this = $(this), - selAll = wo.editable_selectAll, - column = $this.closest('td').index(), - txt = $.trim( $this.text() ); - if (wo.editable_enterToAccept) { - // prevent enter from adding into the content - $this.on('keydown.tseditable', function(e){ - if ( e.which === 13 ) { - e.preventDefault(); - } - }); + $this.data({ before : txt, original: txt }); + + if ( typeof wo.editable_focused === 'function' ) { + wo.editable_focused( txt, column, $this ); + } + + if ( selAll ) { + if ( typeof selAll === 'function' ) { + if ( selAll( txt, column, $this ) ) { + tse.selectAll( $this[0] ); + } + } else { + tse.selectAll( $this[0] ); } - $this.data({ before : txt, original: txt }); + } + }) + .on( 'blur focusout keydown '.split( ' ' ).join( '.tseditable ' ), '[contenteditable]', function( e ) { + if ( !c.$table.data( 'contentFocused' ) ) { return; } + var t, validate, + valid = false, + $this = $( e.target ), + txt = $.trim( $this.text() ), + column = $this.closest( 'td' ).index(); + if ( e.which === 27 ) { + // user cancelled + $this.html( $.trim( $this.data( 'original' ) ) ).trigger( 'blur.tseditable' ); + c.$table.data( 'contentFocused', false ); + return false; + } + // accept on enter ( if set ), alt-enter ( always ) or if autoAccept is set and element is blurred or unfocused + t = e.which === 13 && ( wo.editable_enterToAccept || e.altKey ) || wo.editable_autoAccept && e.type !== 'keydown'; + // change if new or user hits enter ( if option set ) + if ( t && $this.data( 'before' ) !== txt ) { - if (typeof wo.editable_focused === 'function') { - wo.editable_focused( txt, column, $this ); + validate = wo.editable_validate; + valid = txt; + + if ( typeof( validate ) === 'function' ) { + valid = validate( txt, $this.data( 'original' ), column, $this ); + } else if ( typeof ( validate = $.tablesorter.getColumnData( c.table, validate, column ) ) === 'function' ) { + valid = validate( txt, $this.data( 'original' ), column, $this ); } - if (selAll) { - if (typeof selAll === 'function') { - if ( selAll( txt, column, $this ) ) { - selectAll($this[0]); + if ( t && valid !== false ) { + c.$table.find( '.tseditable-last-edited-cell' ).removeClass( 'tseditable-last-edited-cell' ); + $this + .addClass( 'tseditable-last-edited-cell' ) + .html( $.trim( valid ) ) + .data( 'before', valid ) + .data( 'original', valid ) + .trigger( 'change' ); + c.$table.trigger( 'updateCell', [ $this.closest( 'td' ), false, function() { + if ( wo.editable_autoResort ) { + setTimeout( function() { + c.$table.trigger( 'sorton', [ c.sortList, function() { + tse.editComplete( c, wo, c.$table.find( '.tseditable-last-edited-cell' ), true ); + }, true ] ); + }, 10 ); + } else { + tse.editComplete( c, wo, c.$table.find( '.tseditable-last-edited-cell' ) ); } - } else { - selectAll($this[0]); - } - } - }) - .on('blur focusout keydown '.split(' ').join('.tseditable '), '[contenteditable]', function(e){ - if ( !c.$table.data('contentFocused') ) { return; } - var t, validate, - valid = false, - $this = $(e.target), - txt = $.trim( $this.text() ), - column = $this.closest('td').index(); - if ( e.which === 27 ) { - // user cancelled - $this.html( $.trim( $this.data('original') ) ).trigger('blur.tseditable'); - c.$table.data( 'contentFocused', false ); + } ] ); return false; } - // accept on enter (if set), alt-enter (always) or if autoAccept is set and element is blurred or unfocused - t = e.which === 13 && ( wo.editable_enterToAccept || e.altKey ) || wo.editable_autoAccept && e.type !== 'keydown'; - // change if new or user hits enter (if option set) - if ( t && $this.data('before') !== txt ) { - - validate = wo.editable_validate; - valid = txt; - - if (typeof(validate) === "function") { - valid = validate( txt, $this.data('original'), column, $this ); - } else if (typeof (validate = $.tablesorter.getColumnData( table, validate, column )) === 'function') { - valid = validate( txt, $this.data('original'), column, $this ); + } else if ( !valid && e.type !== 'keydown' ) { + clearTimeout( $this.data( 'timer' ) ); + $this.data( 'timer', setTimeout( function() { + if ( $.isFunction( wo.editable_blur ) ) { + wo.editable_blur( $.trim( $this.text() ), column, $this ); } + }, 100 ) ); + // restore original content on blur + $this.html( $.trim( $this.data( 'original' ) ) ); + } + }); + } - if ( t && valid !== false ) { - c.$table.find('.tseditable-last-edited-cell').removeClass('tseditable-last-edited-cell'); - $this - .addClass('tseditable-last-edited-cell') - .html( $.trim( valid ) ) - .data('before', valid) - .data('original', valid) - .trigger('change'); - c.$table.trigger('updateCell', [ $this.closest('td'), false, function(){ - if (wo.editable_autoResort) { - setTimeout(function(){ - c.$table.trigger("sorton", [ c.sortList, function(){ - editComplete(c.$table.find('.tseditable-last-edited-cell'), true); - }, true ]); - }, 10); - } else { - editComplete(c.$table.find('.tseditable-last-edited-cell')); - } - } ]); - return false; - } - } else if ( !valid && e.type !== 'keydown' ) { - clearTimeout( $this.data('timer') ); - $this.data('timer', setTimeout(function(){ - if ($.isFunction(wo.editable_blur)) { - wo.editable_blur( $.trim( $this.text() ), column, $this ); - } - }, 100)); - // restore original content on blur - $this.html( $.trim( $this.data('original') ) ); - } - }); +}; + + $.tablesorter.addWidget({ + id: 'editable', + options : { + editable_columns : [], + editable_enterToAccept : true, + editable_autoAccept : true, + editable_autoResort : false, + editable_wrapContent : '<div>', // wrap the cell content... makes this widget work in IE, and with autocomplete + editable_trimContent : true, // trim content inside of contenteditable ( remove tabs & carriage returns ) + editable_validate : null, // function( text, originalText ){ return text; } + editable_focused : null, // function( text, columnIndex, $element ) {} + editable_blur : null, // function( text, columnIndex, $element ) { } + editable_selectAll : false, // true/false or function( text, columnIndex, $element ) { return true; } + editable_noEdit : 'no-edit', + editable_editComplete : 'editComplete' + }, + init: function( table, thisWidget, c, wo ){ + if ( !wo.editable_columns.length ) { return; } + tse.update( c, wo ); + tse.bindEvents( c, wo ); } }); -})(jQuery); +})( jQuery ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index 7813b36..bdfa28e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -1,4 +1,4 @@ -/*! tablesorter Grouping widget - updated 3/7/2014 (core v2.15.6) +/*! tablesorter Grouping widget - updated 10/26/2014 (v2.18.0) * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -43,6 +43,7 @@ ts.grouping = { hours = time.getHours(); return part === 'year' ? time.getFullYear() : part === 'month' ? wo.group_months[time.getMonth()] : + part === 'monthyear' ? wo.group_months[time.getMonth()] + ' ' + time.getFullYear() : part === 'day' ? wo.group_months[time.getMonth()] + ' ' + time.getDate() : part === 'week' ? wo.group_week[time.getDay()] : part === 'time' ? ('00' + (hours > 12 ? hours - 12 : hours === 0 ? hours + 12 : hours)).slice(-2) + ':' + @@ -53,7 +54,7 @@ ts.grouping = { update : function(table, c, wo){ if ($.isEmptyObject(c.cache)) { return; } - var rowIndex, tbodyIndex, currentGroup, $rows, groupClass, grouping, time, cache, saveName, direction, + var rowIndex, tbodyIndex, currentGroup, $rows, groupClass, grouping, cache, saveName, direction, lang = wo.grouping_language, group = '', savedGroup = false, @@ -66,7 +67,6 @@ ts.grouping = { c.$table.data('pagerSavedHeight', 0); } if (column >= 0 && !c.$headers.filter('[data-column="' + column + '"]:last').hasClass('group-false')) { - if (c.debug){ time = new Date(); } wo.group_currentGroup = ''; // save current groups wo.group_currentGroups = {}; @@ -150,9 +150,6 @@ ts.grouping = { } }); c.$table.trigger(wo.group_complete); - if (c.debug) { - $.tablesorter.benchmark("Applying groups widget: ", time); - } } }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 7f31034..3e9c739 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/* Pager widget for TableSorter 9/15/2014 (v2.17.8) */ +/* Pager widget for TableSorter 10/26/2014 (v2.18.0) - requires jQuery 1.7+ */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ "use strict"; @@ -26,6 +26,9 @@ ts.addWidget({ // Number of visible rows pager_size: 10, + // Number of options to include in the pager number selector + pager_maxOptionSize: 20, + // Save pager page & size if the storage script is loaded (requires $.tablesorter.storage in jquery.tablesorter.widgets.js) pager_savePages: true, @@ -153,12 +156,12 @@ tsp = ts.pager = { p.$goto = p.$container.find(s.gotoPage); // goto is a reserved word #657 // page size selector p.$size = p.$container.find(s.pageSize); - p.totalRows = c.$tbodies.eq(0).children('tr').not( c.widgetOptions.pager_countChildRows ? '' : '.' + c.cssChildRow ).length; + p.totalRows = c.$tbodies.eq(0).children('tr').not( wo.pager_countChildRows ? '' : '.' + c.cssChildRow ).length; p.oldAjaxSuccess = p.oldAjaxSuccess || wo.pager_ajaxObject.success; c.appender = tsp.appender; if (ts.filter && $.inArray('filter', c.widgets) >= 0) { // get any default filter settings (data-value attribute) fixes #388 - p.currentFilters = c.$table.data('lastSearch') || ts.filter.setDefaults(table, c, wo) || []; + p.currentFilters = c.$table.data('lastSearch') || []; // set, but don't apply current filters ts.setFilters(table, p.currentFilters, false); } @@ -179,6 +182,10 @@ tsp = ts.pager = { tsp.enablePager(table, c, false); + // p must have ajaxObject + p.ajaxObject = wo.pager_ajaxObject; // $.extend({}, wo.pager_ajaxObject ); + p.ajaxObject.url = wo.pager_ajaxUrl; + if ( typeof(wo.pager_ajaxUrl) === 'string' ) { // ajax pager; interact with database p.ajax = true; @@ -197,7 +204,6 @@ tsp = ts.pager = { initComplete: function(table, c){ var p = c.pager; - tsp.changeHeight(table, c); tsp.bindEvents(table, c); tsp.setPageSize(table, 0, c); // page size 0 is ignored @@ -215,8 +221,8 @@ tsp = ts.pager = { s = wo.pager_selectors; c.$table - .unbind('filterStart filterEnd sortEnd disable enable destroy updateComplete pageSize '.split(' ').join('.pager ')) - .bind('filterStart.pager', function(e, filters) { + .off('filterStart filterEnd sortEnd disable enable destroy updateComplete pageSize pageSet '.split(' ').join('.pager ')) + .on('filterStart.pager', function(e, filters) { p.currentFilters = filters; // don't change page is filters are the same (pager updating, etc) if (wo.pager_pageReset !== false && (c.lastCombinedFilter || '') !== (filters || []).join('')) { @@ -224,21 +230,19 @@ tsp = ts.pager = { } }) // update pager after filter widget completes - .bind('filterEnd.pager sortEnd.pager', function() { + .on('filterEnd.pager sortEnd.pager', function() { if (p.initialized) { if (c.delayInit && c.rowsCopy && c.rowsCopy.length === 0) { // make sure we have a copy of all table rows once the cache has been built tsp.updateCache(table); } - // update page display first, so we update p.filteredPages - tsp.updatePageDisplay(table, c, false); // tsp.moveToPage(table, p, false); <-- called when applyWidgets is triggered c.pager.last.page = -1; c.$table.trigger('applyWidgets'); - tsp.fixHeight(table, c); + tsp.updatePageDisplay(table, c, false); } }) - .bind('disable.pager', function(e){ + .on('disable.pager', function(e){ e.stopPropagation(); tsp.showAllRows(table, c); }) @@ -254,16 +258,19 @@ tsp = ts.pager = { e.stopPropagation(); // table can be unintentionally undefined in tablesorter v2.17.7 and earlier if (!table || triggered) { return; } - tsp.fixHeight(table, c); var $rows = c.$tbodies.eq(0).children('tr').not(c.selectorRemove); - p.totalRows = $rows.length - ( c.widgetOptions.pager_countChildRows ? 0 : $rows.filter('.' + c.cssChildRow).length ); + p.totalRows = $rows.length - ( wo.pager_countChildRows ? 0 : $rows.filter('.' + c.cssChildRow).length ); p.totalPages = Math.ceil( p.totalRows / p.size ); if ($rows.length && c.rowsCopy && c.rowsCopy.length === 0) { // make a copy of all table rows once the cache has been built tsp.updateCache(table); } - tsp.updatePageDisplay(table, c); + if ( p.page >= p.totalPages ) { + tsp.moveToLastPage(table, p); + } tsp.hideRows(table, c); + tsp.changeHeight(table, c); + tsp.updatePageDisplay(table, c); // make sure widgets are applied - fixes #450 c.$table.trigger('applyWidgets'); }) @@ -287,8 +294,8 @@ tsp = ts.pager = { fxn = [ 'moveToFirstPage', 'moveToPrevPage', 'moveToNextPage', 'moveToLastPage' ]; p.$container.find(ctrls.join(',')) .attr("tabindex", 0) - .unbind('click.pager') - .bind('click.pager', function(e){ + .off('click.pager') + .on('click.pager', function(e){ e.stopPropagation(); var i, $c = $(this), @@ -305,8 +312,8 @@ tsp = ts.pager = { if ( p.$goto.length ) { p.$goto - .unbind('change') - .bind('change', function(){ + .off('change') + .on('change', function(){ p.page = $(this).val() - 1; tsp.moveToPage(table, p, true); tsp.updatePageDisplay(table, c, false); @@ -317,8 +324,8 @@ tsp = ts.pager = { // setting an option as selected appears to cause issues with initial page size p.$size.find('option').removeAttr('selected'); p.$size - .unbind('change.pager') - .bind('change.pager', function() { + .off('change.pager') + .on('change.pager', function() { p.$size.val( $(this).val() ); // in case there are more than one pagers if ( !$(this).hasClass(wo.pager_css.disabled) ) { tsp.setPageSize(table, parseInt( $(this).val(), 10 ), c); @@ -345,32 +352,35 @@ tsp = ts.pager = { } }, - updatePageDisplay: function(table, c, completed) { - var i, pg, s, $out, regex, - wo = c.widgetOptions, + calcFilters: function(table, c) { + var wo = c.widgetOptions, p = c.pager, - f = c.$table.hasClass('hasFilters'), - t = [], - sz = p.size || 10; // don't allow dividing by zero - t = [ wo && wo.filter_filteredRow || 'filtered', c.selectorRemove.replace(/^(\w+\.)/g,'') ]; - if (wo.pager_countChildRows) { t.push(c.cssChildRow); } - regex = new RegExp( '(' + t.join('|') + ')' ); - p.$size.add(p.$goto).removeClass(wo.pager_css.disabled).removeAttr('disabled').attr('aria-disabled', 'false'); - if (f && !wo.pager_ajaxUrl) { + hasFilters = c.$table.hasClass('hasFilters'); + if (hasFilters && !wo.pager_ajaxUrl) { if ($.isEmptyObject(c.cache)) { // delayInit: true so nothing is in the cache - p.filteredRows = p.totalRows = c.$tbodies.eq(0).children('tr').not( c.widgetOptions.pager_countChildRows ? '' : '.' + c.cssChildRow ).length; + p.filteredRows = p.totalRows = c.$tbodies.eq(0).children('tr').not( wo.pager_countChildRows ? '' : '.' + c.cssChildRow ).length; } else { p.filteredRows = 0; $.each(c.cache[0].normalized, function(i, el) { p.filteredRows += p.regexRows.test(el[c.columns].$row[0].className) ? 0 : 1; }); } - } else if (!f) { + } else if (!hasFilters) { p.filteredRows = p.totalRows; } + }, + + updatePageDisplay: function(table, c, completed) { + var s, t, $out, + wo = c.widgetOptions, + p = c.pager, + sz = p.size || 10; // don't allow dividing by zero + if (wo.pager_countChildRows) { t.push(c.cssChildRow); } + p.$size.add(p.$goto).removeClass(wo.pager_css.disabled).removeAttr('disabled').attr('aria-disabled', 'false'); p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method c.totalRows = p.totalRows; + tsp.calcFilters(table, c); c.filteredRows = p.filteredRows; p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) { @@ -400,19 +410,19 @@ tsp = ts.pager = { } return extra.length > 1 && data && data[extra[0]] ? data[extra[0]][extra[1]] : p[str] || (data ? data[str] : deflt) || deflt; }); + if ($out.length) { $out[ ($out[0].tagName === 'INPUT') ? 'val' : 'html' ](s); if ( p.$goto.length ) { t = ''; - pg = Math.min( p.totalPages, p.filteredPages ); - for ( i = 1; i <= pg; i++ ) { - t += '<option>' + i + '</option>'; - } - p.$goto[0].innerHTML = t; - p.$goto[0].value = p.page + 1; + $.each(tsp.buildPageSelect(p, c), function(i, opt){ + t += '<option value="' + opt + '">' + opt + '</option>'; + }); + // innerHTML doesn't work in IE9 - http://support2.microsoft.com/kb/276228 + p.$goto.html(t).val( p.page + 1 ); } // rebind startRow/page inputs - $out.find('.ts-startRow, .ts-page').unbind('change').bind('change', function(){ + $out.find('.ts-startRow, .ts-page').off('change').on('change', function(){ var v = $(this).val(), pg = $(this).hasClass('ts-startRow') ? Math.floor( v/p.size ) + 1 : v; c.$table.trigger('pageSet.pager', [ pg ]); @@ -420,6 +430,7 @@ tsp = ts.pager = { } } tsp.pagerArrows(c); + tsp.fixHeight(table, c); if (p.initialized && completed !== false) { c.$table.trigger('pagerComplete', c); // save pager info to storage @@ -432,27 +443,93 @@ tsp = ts.pager = { } }, + buildPageSelect: function(p, c) { + // Filter the options page number link array if it's larger than 'pager_maxOptionSize' + // as large page set links will slow the browser on large dom inserts + var i, central_focus_size, focus_option_pages, insert_index, option_length, focus_length, + wo = c.widgetOptions, + pg = Math.min( p.totalPages, p.filteredPages ) || 1, + // make skip set size multiples of 5 + skip_set_size = Math.ceil( ( pg / wo.pager_maxOptionSize ) / 5 ) * 5, + large_collection = pg > wo.pager_maxOptionSize, + current_page = p.page + 1, + start_page = skip_set_size, + end_page = pg - skip_set_size, + option_pages = [1], + // construct default options pages array + option_pages_start_page = (large_collection) ? skip_set_size : 1; + + for ( i = option_pages_start_page; i <= pg; ) { + option_pages.push(i); + i = i + ( large_collection ? skip_set_size : 1 ); + } + option_pages.push(pg); + + if (large_collection) { + focus_option_pages = []; + // don't allow central focus size to be > 5 on either side of current page + central_focus_size = Math.max( Math.floor( wo.pager_maxOptionSize / skip_set_size ) - 1, 5 ); + + start_page = current_page - central_focus_size; + if (start_page < 1) { start_page = 1; } + end_page = current_page + central_focus_size; + if (end_page > pg) { end_page = pg; } + // construct an array to get a focus set around the current page + for (i = start_page; i <= end_page ; i++) { + focus_option_pages.push(i); + } + + // keep unique values + option_pages = $.grep(option_pages, function(value, indx) { + return $.inArray(value, option_pages) === indx; + }); + + option_length = option_pages.length; + focus_length = focus_option_pages.length; + + // make sure at all option_pages aren't replaced + if (option_length - focus_length > skip_set_size / 2 && option_length + focus_length > wo.pager_maxOptionSize ) { + insert_index = Math.floor(option_length / 2) - Math.floor(focus_length / 2); + Array.prototype.splice.apply(option_pages, [ insert_index, focus_length ]); + } + option_pages = option_pages.concat(focus_option_pages); + + } + + // keep unique values again + option_pages = $.grep(option_pages, function(value, indx) { + return $.inArray(value, option_pages) === indx; + }) + .sort(function(a,b) { return a - b; }); + + return option_pages; + }, + fixHeight: function(table, c) { var d, h, p = c.pager, wo = c.widgetOptions, $b = c.$tbodies.eq(0); - if (wo.pager_fixedHeight) { - $b.find('tr.pagerSavedHeightSpacer').remove(); + $b.find('tr.pagerSavedHeightSpacer').remove(); + if (wo.pager_fixedHeight && !p.isDisabled) { h = $.data(table, 'pagerSavedHeight'); if (h) { d = h - $b.height(); if ( d > 5 && $.data(table, 'pagerLastSize') === p.size && $b.children('tr:visible').length < p.size ) { - $b.append('<tr class="pagerSavedHeightSpacer ' + wo.pager_selectors.remove.replace(/^(\w+\.)/g,'') + '" style="height:' + d + 'px;"></tr>'); + $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice(1) + '" style="height:' + d + 'px;"></tr>'); } } } }, changeHeight: function(table, c) { - var $b = c.$tbodies.eq(0); + var h, $b = c.$tbodies.eq(0); $b.find('tr.pagerSavedHeightSpacer').remove(); - $.data(table, 'pagerSavedHeight', $b.height()); + if (!$b.children('tr:visible').length) { + $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice(1) + '"><td> </td></tr>'); + } + h = $b.children('tr').eq(0).height() * c.pager.size; + $.data(table, 'pagerSavedHeight', h); tsp.fixHeight(table, c); $.data(table, 'pagerLastSize', c.pager.size); }, @@ -606,7 +683,6 @@ tsp = ts.pager = { p.last.currentFilters = p.currentFilters; p.last.sortList = (c.sortList || []).join(','); tsp.updatePageDisplay(table, c); - tsp.fixHeight(table, c); $t.trigger('updateCache', [function(){ if (p.initialized) { // apply widgets after table has rendered & after a delay to prevent @@ -628,7 +704,6 @@ tsp = ts.pager = { var counter, url = tsp.getAjaxUrl(table, c), $doc = $(document), - wo = c.widgetOptions, p = c.pager; if ( url !== '' ) { if (c.showProcessing) { @@ -636,25 +711,25 @@ tsp = ts.pager = { } $doc.on('ajaxError.pager', function(e, xhr, settings, exception) { tsp.renderAjax(null, table, c, xhr, exception); - $doc.unbind('ajaxError.pager'); + $doc.off('ajaxError.pager'); }); counter = ++p.ajaxCounter; - wo.pager_ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl - wo.pager_ajaxObject.success = function(data, status, jqxhr) { + p.ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl + p.ajaxObject.success = function(data, status, jqxhr) { // Refuse to process old ajax commands that were overwritten by new ones - see #443 if (counter < p.ajaxCounter){ return; } tsp.renderAjax(data, table, c, jqxhr); - $doc.unbind('ajaxError.pager'); + $doc.off('ajaxError.pager'); if (typeof p.oldAjaxSuccess === 'function') { p.oldAjaxSuccess(data); } }; if (c.debug) { - ts.log('ajax initialized', wo.pager_ajaxObject); + ts.log('ajax initialized', p.ajaxObject); } - $.ajax(wo.pager_ajaxObject); + $.ajax(p.ajaxObject); } }, @@ -745,7 +820,6 @@ tsp = ts.pager = { } tsp.updatePageDisplay(table, c); - if ( !p.isDisabled ) { tsp.fixHeight(table, c); } wo.pager_startPage = p.page; wo.pager_size = p.size; @@ -810,9 +884,11 @@ tsp = ts.pager = { if ( pageMoved !== false && p.initialized && $.isEmptyObject(table.config.cache)) { return tsp.updateCache(table); } - var c = table.config, - l = p.last, - pg = Math.min( p.totalPages, p.filteredPages ); + var pg, c = table.config, + wo = c.widgetOptions, + l = p.last; + tsp.calcFilters(table, c); + pg = Math.min( p.totalPages, p.filteredPages ); if ( p.page < 0 ) { p.page = 0; } if ( p.page > ( pg - 1 ) && pg !== 0 ) { p.page = pg - 1; } // fixes issue where one current filter is [] and the other is ['','',''], @@ -822,6 +898,10 @@ tsp = ts.pager = { // don't allow rendering multiple times on the same page/size/totalRows/filters/sorts if ( l.page === p.page && l.size === p.size && l.totalRows === p.totalRows && (l.currentFilters || []).join(',') === (p.currentFilters || []).join(',') && + // check for ajax url changes see #730 + (l.ajaxUrl || '') === (p.ajaxObject.url || '') && + // & ajax url option changes (dynamically add/remove/rename sort & filter parameters) + (l.optAjaxUrl || '') === (wo.pager_ajaxUrl || '') && l.sortList === (c.sortList || []).join(',') ) { return; } @@ -834,7 +914,9 @@ tsp = ts.pager = { // fixes #408; modify sortList otherwise it auto-updates sortList : (c.sortList || []).join(','), totalRows : p.totalRows, - currentFilters : p.currentFilters || [] + currentFilters : p.currentFilters || [], + ajaxUrl : p.ajaxObject.url || '', + optAjaxUrl : wo.pager_ajaxUrl }; if (p.ajax) { tsp.getAjax(table, c); @@ -896,7 +978,7 @@ tsp = ts.pager = { c.appender = null; // remove pager appender function p.initialized = false; delete table.config.rowsCopy; - c.$table.unbind('destroy.pager sortEnd.pager filterEnd.pager enable.pager disable.pager'); + c.$table.off('destroy.pager sortEnd.pager filterEnd.pager enable.pager disable.pager'); if (ts.storage) { ts.storage(table, c.widgetOptions.pager_storageKey, ''); } @@ -916,11 +998,11 @@ tsp = ts.pager = { p.$container.find(c.widgetOptions.pager_selectors.pageDisplay).attr('id', info); c.$table.attr('aria-describedby', info); } + tsp.changeHeight(table, c); if ( triggered ) { c.$table.trigger('updateRows'); tsp.setPageSize(table, p.size, c); tsp.hideRowsSetup(table, c); - tsp.fixHeight(table, c); if (c.debug) { ts.log('pager enabled'); } @@ -933,7 +1015,7 @@ tsp = ts.pager = { p = c.pager; if ( !p.ajax ) { c.rowsCopy = rows; - p.totalRows = c.widgetOptions.pager_countChildRows ? c.$tbodies.eq(0).children('tr').length : rows.length; + p.totalRows = wo.pager_countChildRows ? c.$tbodies.eq(0).children('tr').length : rows.length; p.size = $.data(table, 'pagerLastSize') || p.size || wo.pager_size || 10; p.totalPages = Math.ceil( p.totalRows / p.size ); tsp.moveToPage(table, p); @@ -951,7 +1033,8 @@ ts.showError = function(table, message){ $(table).each(function(){ var $row, c = this.config, - errorRow = c.pager && c.pager.cssErrorRow || c.widgetOptions.pager_css && c.widgetOptions.pager_css.errorRow || 'tablesorter-errorRow'; + wo = c.widgetOptions, + errorRow = c.pager && c.pager.cssErrorRow || wo.pager_css && wo.pager_css.errorRow || 'tablesorter-errorRow'; if (c) { if (typeof message === 'undefined') { c.$table.find('thead').find(c.selectorRemove).remove(); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js index c551333..3f8afd3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js @@ -1,4 +1,4 @@ -/*! tablesorter repeatHeaders widget - updated 4/12/2013 +/*! tablesorter repeatHeaders widget - updated 10/26/2014 (v2.18.0) * Requires tablesorter v2.8+ and jQuery 1.7+ * Original by Christian Bach from the example-widgets.html demo */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index be785dd..382eb01 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -10,7 +10,7 @@ Resizable scroller widget for the jQuery tablesorter plugin - Version 2.0 - modified by Rob Garrison 4/12/2013; updated 6/28/2014 (v2.17.3) + Version 2.0 - modified by Rob Garrison 4/12/2013; updated 10/26/2014 (v2.18.0) Requires jQuery v1.7+ Requires the tablesorter plugin, v2.8+, available at http://mottie.github.com/tablesorter/docs/ @@ -24,7 +24,6 @@ scroller_height : 300, // height of scroll window scroller_barWidth : 18, // scroll bar width scroller_jumpToHeader : true, // header snap to browser top when scrolling the tbody - scroller_idPrefix : 's_' // cloned thead id prefix (random number added to end) } }); @@ -43,7 +42,7 @@ var ts = $.tablesorter; ts.window_resize = function(){ if (this.resize_timer) { - clearTimeout(this.resize_timer); + clearTimeout(this.resize_timer); } this.resize_timer = setTimeout(function(){ $(this).trigger('resizeEnd'); @@ -73,28 +72,30 @@ ts.addWidget({ scroller_height : 300, scroller_barWidth : 18, scroller_jumpToHeader: true, - scroller_upAfterSort: true, - scroller_idPrefix : 's_' + scroller_upAfterSort: true }, init: function(table, thisWidget, c, wo){ - var $win = $(window); + var $win = $(window), + namespace = c.namespace + 'tsscroller'; // Setup window.resizeEnd event $win - .bind('resize', ts.window_resize) - .bind('resizeEnd', function() { + .bind('resize' + namespace, ts.window_resize) + .bind('resizeEnd' + namespace, function() { // init is run before format, so scroller_resizeWidth // won't be defined within the "c" or "wo" parameters if (typeof table.config.widgetOptions.scroller_resizeWidth === 'function') { // IE calls resize when you modify content, so we have to unbind the resize event // so we don't end up with an infinite loop. we can rebind after we're done. - $win.unbind('resize', ts.window_resize); + $win.unbind('resize' + namespace, ts.window_resize); table.config.widgetOptions.scroller_resizeWidth(); - $win.bind('resize', ts.window_resize); + $win.bind('resize' + namespace, ts.window_resize); } }); }, format: function(table, c, wo) { - var h, $hdr, id, t, resize, $cells, + var h, $hdr, t, resize, $cells, + // c.namespace contains a unique tablesorter ID, per table + id = c.namespace.slice(1) + 'tsscroller', $win = $(window), $tbl = c.$table; @@ -102,7 +103,6 @@ ts.addWidget({ h = wo.scroller_height || 300; t = $tbl.find('tbody').height(); if (t !== 0 && h > t) { h = t + 10; } // Table is less than h px - id = wo.scroller_id = wo.scroller_idPrefix + Math.floor(Math.random() * 1001); $hdr = $('<table class="' + $tbl.attr('class') + '" cellpadding=0 cellspacing=0><thead>' + $tbl.find('thead:first').html() + '</thead></table>'); $tbl @@ -204,13 +204,15 @@ ts.addWidget({ } }, - remove : function(table, c, wo){ - var $table = c.$table; + remove : function(table, c){ + var $table = c.$table, + namespace = c.namespace + 'tsscroller'; $table.closest('.tablesorter-scroller').find('.tablesorter-scroller-header').remove(); $table .unwrap() .find('.tablesorter-filter-row').removeClass('hideme').end() .find('thead').show().css('visibility', 'visible'); + $(window).unbind('resize' + namespace + ' resizeEnd' + namespace); c.isScrolling = false; } }); diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css index a5911aa..64853c6 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css @@ -84,10 +84,10 @@ } /* Zebra Widget - row alternating colors */ -.tablesorter-blackice tr.odd td { +.tablesorter-blackice tr.odd > td { background-color: #333; } -.tablesorter-blackice tr.even td { +.tablesorter-blackice tr.even > td { background-color: #393939; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css index fdd0a6b..abccf2a 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css @@ -119,10 +119,10 @@ } /* Zebra Widget - row alternating colors */ -.tablesorter-blue tbody tr.odd td { +.tablesorter-blue tbody tr.odd > td { background-color: #ebf2fa; } -.tablesorter-blue tbody tr.even td { +.tablesorter-blue tbody tr.even > td { background-color: #fff; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css index bff0ee4..d9665c9 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css @@ -60,14 +60,14 @@ } /* since bootstrap (table-striped) uses nth-child(), we just use this to add a zebra stripe color */ -.tablesorter-bootstrap tr.odd td { +.tablesorter-bootstrap tr.odd > td { background-color: #f9f9f9; } .tablesorter-bootstrap tbody > .odd:hover > td, .tablesorter-bootstrap tbody > .even:hover > td { background-color: #f5f5f5; } -.tablesorter-bootstrap tr.even td { +.tablesorter-bootstrap tr.even > td { background-color: #fff; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css index 091d7f7..7cf876a 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css @@ -83,10 +83,10 @@ } /* Zebra Widget - row alternating colors */ -.tablesorter-dark tr.odd td { +.tablesorter-dark tr.odd > td { background-color: #202020; } -.tablesorter-dark tr.even td { +.tablesorter-dark tr.even > td { background-color: #101010; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css index 468df0a..7475753 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css @@ -86,10 +86,10 @@ Default Theme } /* Zebra Widget - row alternating colors */ -.tablesorter-default tr.odd td { +.tablesorter-default tr.odd > td { background-color: #dfdfdf; } -.tablesorter-default tr.even td { +.tablesorter-default tr.even > td { background-color: #efefef; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css index e145c6d..ad74572 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css @@ -116,9 +116,9 @@ } /* Zebra Widget - row alternating colors */ -.tablesorter-dropbox tr.odd td { +.tablesorter-dropbox tr.odd > td { } -.tablesorter-dropbox tr.even td { +.tablesorter-dropbox tr.even > td { } /* Column Widget - column sort colors */ diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css index 0306745..77d1b54 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css @@ -101,10 +101,10 @@ } /* Zebra Widget - row alternating colors */ -.tablesorter-green tr.odd td { +.tablesorter-green tr.odd > td { background-color: #ebfaeb; } -.tablesorter-green tr.even td { +.tablesorter-green tr.even > td { background-color: #fff; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css index ae62ee7..06453d2 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css @@ -137,10 +137,10 @@ } /* Zebra Widget - row alternating colors */ -.tablesorter-grey tbody tr.odd td { +.tablesorter-grey tbody tr.odd > td { background-color: #5e5e5e; } -.tablesorter-grey tbody tr.even td { +.tablesorter-grey tbody tr.even > td { background-color: #6d6d6d; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css index 9150a92..74e88bf 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css @@ -93,10 +93,10 @@ } /* Zebra Widget - row alternating colors */ -.tablesorter-ice tr.odd td { +.tablesorter-ice tr.odd > td { background-color: #dfdfdf; } -.tablesorter-ice tr.even td { +.tablesorter-ice tr.even > td { background-color: #efefef; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css index ea33277..d06d9bc 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css @@ -5,7 +5,7 @@ .tablesorter-jui { width: 100%; border-collapse: separate; - border-spacing: 2px; /* adjust spacing between table cells */ + border-spacing: 2px; /* adjust spacing between table cells */ margin: 10px 0 15px; padding: 5px; font-size: 0.8em; @@ -43,6 +43,9 @@ .tablesorter-jui thead .sorter-false { cursor: default; } +.tablesorter-jui thead tr .sorter-false .ui-icon { + display: none; +} /* tfoot */ .tablesorter-jui tfoot th, diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css index a2c8ec1..e119b28 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css @@ -12,7 +12,7 @@ Metro Dark Theme text-align: left; } -.tablesorter-metro-dark tr.dark-row th, .tablesorter-metro-dark tr.dark-row td { +.tablesorter-metro-dark tr.dark-row th, .tablesorter-metro-dark tr.dark-row td, .tablesorter-metro-dark caption.dark-row { background-color: #222; color: #fff; padding: 2px; @@ -36,7 +36,7 @@ Metro Dark Theme .tablesorter-metro-dark .header, .tablesorter-metro-dark .tablesorter-header { background-image: url(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAQBAMAAADQT4M0AAAAGFBMVEUAAADu7u7u7u7u7u7u7u7u7u7u7u7u7u5jNePWAAAACHRSTlMAMxIHKwEgMWD59H4AAABSSURBVAjXY2BgYFJgAAHzYhDJ6igSAKTYBAUTgJSioKAQAwNzoaCguAFDiCAQuDIkgigxBgiA8cJAVCpQt6AgSL+JoKAzA0gjUBsQqBcBCYhFAAE/CV4zeSzxAAAAAElFTkSuQmCC); - background-position: center right; + background-position: right 5px center; background-repeat: no-repeat; cursor: pointer; white-space: normal; @@ -95,10 +95,10 @@ Metro Dark Theme } /* Zebra Widget - row alternating colors */ -.tablesorter-metro-dark tr.odd td { +.tablesorter-metro-dark tr.odd > td { background-color: #eee; } -.tablesorter-metro-dark tr.even td { +.tablesorter-metro-dark tr.even > td { background-color: #fff; } From 46978bcbc094bee90981639b685d51d3b2eea719 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 3 Nov 2014 18:40:57 +0100 Subject: [PATCH 040/138] * updated tablesorter to latest version (2.18.1) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 49 +++++++++-------- .../jquery-tablesorter/jquery.tablesorter.js | 21 ++++---- .../jquery.tablesorter.widgets.js | 18 +++---- .../widgets/widget-columnSelector.js | 3 +- .../widgets/widget-cssStickyHeaders.js | 52 +++++++++++++------ .../widgets/widget-pager.js | 45 +++++++++------- 10 files changed, 117 insertions(+), 81 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b70a064..1606b18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.13.1 (2014-11-03) + +* Upgrade tablesorter to v2.18.1 + #### v1.13.0 (2014-10-27) * Upgrade tablesorter to v2.18.0 diff --git a/README.md b/README.md index 186374d..d795abb 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.18.0 (10/26/2014), [documentation] +Current tablesorter version: 2.18.1 (11/3/2014), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index ed5e7fa..ced7f62 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.13.0' + VERSION = '1.13.1' end diff --git a/tablesorter b/tablesorter index f04b51b..363f083 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit f04b51b336e7830ba1bc2d0cea9000dfc99d3735 +Subproject commit 363f083682cab970e5ef84090bdf631782d55024 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index ad9f79d..ee91e97 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter pager plugin - * updated 10/26/2014 (v2.18.0) + * updated 11/3/2014 (v2.18.1) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -150,7 +150,7 @@ }, updatePageDisplay = function(table, p, completed) { - if ( !p.initialized ) { return; } + if ( p.initializing ) { return; } var s, t, $out, c = table.config, sz = p.size || 10; // don't allow dividing by zero @@ -328,7 +328,7 @@ } else { rows[i].style.display = ( j >= s && j < e ) ? '' : 'none'; // don't count child rows - j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.replace(/^(\w+\.)/g,'')) && !p.countChildRows ? 0 : 1; + j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) && !p.countChildRows ? 0 : 1; if ( j === e && rows[i].style.display !== 'none' && rows[i].className.match(ts.css.cssHasChild) ) { lastIndex = i; } @@ -477,9 +477,11 @@ } if (!p.initialized) { p.initialized = true; + p.initializing = false; $(table) .trigger('applyWidgets') .trigger('pagerInitialized', p); + updatePageDisplay(table, p); } }, @@ -499,6 +501,7 @@ counter = ++p.ajaxCounter; + p.last.ajaxUrl = url; // remember processed url p.ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl p.ajaxObject.success = function(data, status, jqxhr) { // Refuse to process old ajax commands that were overwritten by new ones - see #443 @@ -602,7 +605,7 @@ } ts.processTbody(table, $tb, false); } - updatePageDisplay(table, p, true); + updatePageDisplay(table, p); if (table.isUpdating) { $t.trigger('updateComplete', [ table, true ]); } @@ -659,10 +662,13 @@ if ( pageMoved !== false && p.initialized && $.isEmptyObject(c.cache)) { return updateCache(table); } + // abort page move if the table has filters and has not been initialized + if (p.ajax && ts.hasWidget(table, 'filter') && !c.widgetOptions.filter_initialized) { return; } calcFilters(table, p); pg = Math.min( p.totalPages, p.filteredPages ); if ( p.page < 0 ) { p.page = 0; } if ( p.page > ( pg - 1 ) && pg !== 0 ) { p.page = pg - 1; } + // fixes issue where one currentFilter is [] and the other is ['','',''], // making the next if comparison think the filters are different (joined by commas). Fixes #202. l.currentFilters = (l.currentFilters || []).join('') === '' ? [] : l.currentFilters; @@ -746,7 +752,7 @@ table.config.appender = null; // remove pager appender function p.initialized = false; delete table.config.rowsCopy; - $(table).unbind('destroy.pager sortEnd.pager filterEnd.pager enable.pager disable.pager'); + $(table).unbind('filterInit filterStart filterEnd sortEnd disable enable destroy updateComplete pageSize pageSet '.split(' ').join('.pager ')); if (ts.storage) { ts.storage(table, p.storageKey, ''); } @@ -812,12 +818,7 @@ } p.oldAjaxSuccess = p.oldAjaxSuccess || p.ajaxObject.success; c.appender = $this.appender; - if (ts.filter && $.inArray('filter', c.widgets) >= 0) { - // get any default filter settings (data-value attribute) fixes #388 - p.currentFilters = c.$table.data('lastSearch') || ts.filter.setDefaults(table, c, c.widgetOptions) || []; - // set, but don't apply current filters - ts.setFilters(table, p.currentFilters, false); - } + p.initializing = true; if (p.savePages && ts.storage) { t = ts.storage(table, p.storageKey) || {}; // fixes #387 p.page = isNaN(t.page) ? p.page : t.page; @@ -826,20 +827,21 @@ } // skipped rows - p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.replace(/^(\w+\.)/g,'') + '|' + c.cssChildRow + ')'); + p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.slice(1) + '|' + c.cssChildRow + ')'); $t - .unbind('filterStart filterEnd sortEnd disable enable destroy updateComplete pageSize pageSet '.split(' ').join('.pager ')) - .bind('filterStart.pager', function(e, filters) { - p.currentFilters = filters; - // don't change page is filters are the same (pager updating, etc) - if (p.pageReset !== false && (c.lastCombinedFilter || '') !== (filters || []).join('')) { + .unbind('filterInit filterStart filterEnd sortEnd disable enable destroy updateComplete pageSize pageSet '.split(' ').join('.pager ')) + .bind('filterInit.pager filterStart.pager', function() { + p.currentFilters = c.$table.data('lastSearch'); + // don't change page if filters are the same (pager updating, etc) + if (p.pageReset !== false && (c.lastCombinedFilter || '') !== (p.currentFilters || []).join('')) { p.page = p.pageReset; // fixes #456 & #565 } }) // update pager after filter widget completes .bind('filterEnd.pager sortEnd.pager', function() { - if (p.initialized) { + p.currentFilters = c.$table.data('lastSearch'); + if (p.initialized || p.initializing) { if (c.delayInit && c.rowsCopy && c.rowsCopy.length === 0) { // make sure we have a copy of all table rows once the cache has been built updateCache(table); @@ -946,7 +948,6 @@ $t.trigger('pagerBeforeInitialized', p); enablePager(table, p, false); - if ( typeof(p.ajaxUrl) === 'string' ) { // ajax pager; interact with database p.ajax = true; @@ -962,10 +963,14 @@ } // pager initialized - if (!p.ajax) { + if (!p.ajax && !p.initialized) { + p.initializing = false; p.initialized = true; - updatePageDisplay(table, p, true); + moveToPage(table, p); $(table).trigger('pagerInitialized', p); + if ( !( c.widgetOptions.filter_initialized && ts.hasWidget(table, 'filter') ) ) { + updatePageDisplay(table, c, false); + } } }); }; @@ -988,7 +993,7 @@ }) // add error row to thead instead of tbody, or clicking on the header will result in a parser error .appendTo( c.$table.find('thead:first') ) - .addClass( errorRow + ' ' + c.selectorRemove.replace(/^(\w+\.)/g,'') ) + .addClass( errorRow + ' ' + c.selectorRemove.slice(1) ) .attr({ role : 'alert', 'aria-live' : 'assertive' diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 1fdc8ee..f8892b4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.18.0 - Client-side table sorting with ease! +* TableSorter 2.18.1 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.18.0"; + ts.version = "2.18.1"; ts.parsers = []; ts.widgets = []; @@ -92,6 +92,7 @@ cssChildRow : 'tablesorter-childRow', // class name indiciating that a row is to be attached to the its parent cssIcon : 'tablesorter-icon', // if this class exists, a <i> will be added to the header automatically cssInfoBlock : 'tablesorter-infoOnly', // don't sort tbody with this class name (only one class name allowed here!) + cssAllowClicks : 'tablesorter-allowClicks', // class name added to table header which allows clicks to bubble up // *** selectors selectorHeaders : '> thead th, > thead td', @@ -1159,13 +1160,11 @@ } for (k in obj) { if (typeof k === 'string') { - if (getCell) { - // get header cell - $h = c.$headers.eq(indx).filter(k); - } else { - // get column indexed cell - $h = c.$headers.filter('[data-column="' + indx + '"]:last').filter(k); - } + $h = c.$headers.filter('[data-column="' + indx + '"]:last') + // header cell with class/id + .filter(k) + // find elements within the header cell with cell/id + .add( c.$headers.filter('[data-column="' + indx + '"]:last').find(k) ); if ($h.length) { return obj[k]; } @@ -1286,7 +1285,9 @@ // set timer on mousedown if (type === 'mousedown') { downTime = new Date().getTime(); - return /(input|select|button|textarea)/i.test(e.target.tagName) ? '' : !c.cancelSelection; + return /(input|select|button|textarea)/i.test(e.target.tagName) || + // allow clicks to contents of selected cells + $(e.target).closest('td,th').hasClass(c.cssAllowClicks) ? '' : !c.cancelSelection; } if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); } // jQuery v1.2.6 doesn't have closest() diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index cca5c07..c1eba9d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.16+ widgets - updated 10/26/2014 (v2.18.0) +/*! tableSorter 2.16+ widgets - updated 11/3/2014 (v2.18.1) * * Column Styles * Column Filters @@ -206,8 +206,9 @@ ts.addWidget({ .children('tfoot'); if ($tfoot.length) { $tfoot - .children('tr').removeClass(oldtheme.footerRow).addClass(themes.footerRow) - .children('th, td').removeClass(oldtheme.footerCells).addClass(themes.footerCells); + // if oldtheme.footerRow or oldtheme.footerCells are undefined, all class names are removed + .children('tr').removeClass(oldtheme.footerRow || '').addClass(themes.footerRow) + .children('th, td').removeClass(oldtheme.footerCells || '').addClass(themes.footerCells); } // update header classes $headers @@ -595,7 +596,6 @@ ts.filter = { wo.filter_initTimer = null; wo.filter_formatterCount = 0; wo.filter_formatterInit = []; - wo.filter_initializing = true; wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; @@ -722,7 +722,7 @@ ts.filter = { c.filteredRows = c.totalRows; // add default values - c.$table.bind('tablesorter-initialized pagerInitialized', function() { + c.$table.bind('tablesorter-initialized pagerBeforeInitialized', function() { // redefine "wo" as it does not update properly inside this callback var wo = this.config.widgetOptions; filters = ts.filter.setDefaults(table, c, wo) || []; @@ -762,11 +762,9 @@ ts.filter = { var wo = c.widgetOptions, count = 0, completed = function(){ - // set initializing false first so findRows will process - wo.filter_initializing = false; - ts.filter.findRows(c.table, c.$table.data('lastSearch'), null); wo.filter_initialized = true; c.$table.trigger('filterInit', c); + ts.filter.findRows(c.table, c.$table.data('lastSearch'), null); }; $.each( wo.filter_formatterInit, function(i, val) { if (val === 1) { @@ -1095,7 +1093,7 @@ ts.filter = { return columns; }, findRows: function(table, filters, combinedFilters) { - if (table.config.lastCombinedFilter === combinedFilters || table.config.widgetOptions.filter_initializing) { return; } + if (table.config.lastCombinedFilter === combinedFilters || !table.config.widgetOptions.filter_initialized) { return; } var len, $rows, rowIndex, tbodyIndex, $tbody, $cells, $cell, columnIndex, childRow, lastSearch, hasSelect, matches, result, showRow, time, val, indx, notFiltered, searchFiltered, filterMatched, excludeMatch, fxn, ffxn, @@ -1527,7 +1525,7 @@ ts.filter = { }; ts.getFilters = function(table, getRaw, setFilters, skipFirst) { - var i, f, $filters, $column, cols, + var i, $filters, $column, cols, filters = false, c = table ? $(table)[0].config : '', wo = c ? c.widgetOptions : ''; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index bf0ac42..5455e0a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Column Selector/Responsive table widget (beta) for TableSorter - 10/26/2014 (v2.18.0) +/* Column Selector/Responsive table widget (beta) for TableSorter - 11/3/2014 (v2.18.1) * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -120,7 +120,6 @@ tsColSel = ts.columnSelector = { if (wo.columnSelector_mediaquery) { // used by window resize function colSel.lastIndex = -1; - wo.columnSelector_breakpoints.sort(); tsColSel.updateBreakpoints(c, wo); c.$table .off('updateAll' + namespace) diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js index edf5a3e..2e45193 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js @@ -1,4 +1,4 @@ -/*! tablesorter CSS Sticky Headers widget - updated 10/26/2014 (v2.18.0) +/*! tablesorter CSS Sticky Headers widget - updated 11/3/2014 (v2.18.1) * Requires a modern browser, tablesorter v2.8+ */ /*jshint jquery:true, unused:false */ @@ -19,6 +19,7 @@ }, init : function(table, thisWidget, c, wo) { var isIE = 'ActiveXObject' in window, // target all versions of IE + isFF = navigator.userAgent.toLowerCase().indexOf('firefox') > -1, $table = c.$table, $attach = $(wo.cssStickyHeaders_attachTo), namespace = c.namespace + 'cssstickyheader ', @@ -26,45 +27,66 @@ $caption = $table.children('caption'), $win = $attach.length ? $attach : $(window), $parent = $table.parent().closest('table.' + ts.css.table), - $parentThead = $parent.length && ts.hasWidget($parent[0], 'cssStickyHeaders') ? $parent.children('thead') : []; + $parentThead = $parent.length && ts.hasWidget($parent[0], 'cssStickyHeaders') ? $parent.children('thead') : [], + lastCaptionSetting = wo.cssStickyHeaders_addCaption; $win .unbind('scroll resize '.split(' ').join(namespace)) .bind('scroll resize '.split(' ').join(namespace), function() { + // make sure "wo" is current otherwise changes to widgetOptions + // are not dynamic (like the add caption button in the demo) + wo = c.widgetOptions; var top = $attach.length ? $attach.offset().top : $win.scrollTop(), // add caption height; include table padding top & border-spacing or text may be above the fold (jQuery UI themes) // border-spacing needed in Firefox, but not webkit... not sure if I should account for that - captionHeight = wo.cssStickyHeaders_addCaption ? ( $caption.outerHeight(true) || 0 ) + - ( parseInt( $table.css('padding-top'), 10 ) || 0 ) + ( parseInt( $table.css('border-spacing'), 10 ) || 0 ) : 0, + captionHeight = ( $caption.outerHeight(true) || 0 ) + + ( parseInt( $table.css('padding-top'), 10 ) || 0 ) + + ( parseInt( $table.css('border-spacing'), 10 ) || 0 ), + + bottom = $table.height() - $thead.height() - ( $table.children('tfoot').height() || 0 ) - ( wo.cssStickyHeaders_addCaption ? captionHeight : 0 ), + + parentTheadHeight = $parentThead.length ? $parentThead.height() : 0, - bottom = $table.height() - $thead.height() - ( $table.children('tfoot').height() || 0 ) - captionHeight, // get bottom of nested sticky headers - nestedStickyTop = $parentThead.length ? ( isIE ? $parent.data('cssStickyHeaderTop') : $parentThead.offset().top ) + - $parentThead.height() - $win.scrollTop() : 0, + nestedStickyBottom = $parentThead.length ? ( + isIE ? $parent.data('cssStickyHeaderBottom') + parentTheadHeight : + $parentThead.offset().top + parentTheadHeight - $win.scrollTop() + ) : 0, // Detect nested tables - fixes #724 - deltaY = top - $table.offset().top + nestedStickyTop + ( parseInt( $table.css('border-top-width'), 10 ) || 0 ) + + deltaY = top - $table.offset().top + nestedStickyBottom + + ( parseInt( $table.css('border-top-width'), 10 ) || 0 ) + + ( wo.cssStickyHeaders_offset || 0 ) + // Again, I dislike browser sniffing... but I have no idea why I need to include a captionHeight // for Firefox here and not for Chrome. Even IE behaves, sorta! - ( wo.cssStickyHeaders_offset || 0 ) + ( navigator.userAgent.toLowerCase().indexOf('firefox') > -1 ? captionHeight : 0 ), + ( wo.cssStickyHeaders_addCaption ? ( isFF ? captionHeight : 0 ) : -captionHeight ), finalY = deltaY > 0 && deltaY <= bottom ? deltaY : 0, // All IE (even IE11) can only transform header cells - fixes #447 thanks to @gakreol! $cells = isIE ? $thead.children().children() : $thead; - // more crazy IE stuff.. somehow the second nested table is completely ignored + // more crazy IE stuff... if (isIE) { - c.$table.data('cssStickyHeaderTop', finalY - ( $parentThead.length ? $parentThead.height() : 0 )); - if ($parentThead.length) { - top = $parent.data('cssStickyHeaderTop') - $parentThead.height(); - finalY = top > 0 && top <= bottom ? top : 0; - } + // I didn't bother testing 3 nested tables deep in IE, because I hate it + c.$table.data( 'cssStickyHeaderBottom', ( $parentThead.length ? parentTheadHeight : 0 ) - + ( wo.cssStickyHeaders_addCaption ? captionHeight : 0 ) ); } if (wo.cssStickyHeaders_addCaption) { $cells = $cells.add($caption); } + if (lastCaptionSetting !== wo.cssStickyHeaders_addCaption) { + lastCaptionSetting = wo.cssStickyHeaders_addCaption; + // reset caption position if addCaption option is dynamically changed to false + if (!lastCaptionSetting) { + $caption.css({ + 'transform' : '', + '-ms-transform' : '', + '-webkit-transform' : '' + }); + } + } $cells.css({ 'transform' : finalY === 0 ? '' : 'translate(0px,' + finalY + 'px)', diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 3e9c739..e6ede34 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/* Pager widget for TableSorter 10/26/2014 (v2.18.0) - requires jQuery 1.7+ */ +/* Pager widget for TableSorter 11/3/2014 (v2.18.1) - requires jQuery 1.7+ */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ "use strict"; @@ -159,12 +159,7 @@ tsp = ts.pager = { p.totalRows = c.$tbodies.eq(0).children('tr').not( wo.pager_countChildRows ? '' : '.' + c.cssChildRow ).length; p.oldAjaxSuccess = p.oldAjaxSuccess || wo.pager_ajaxObject.success; c.appender = tsp.appender; - if (ts.filter && $.inArray('filter', c.widgets) >= 0) { - // get any default filter settings (data-value attribute) fixes #388 - p.currentFilters = c.$table.data('lastSearch') || []; - // set, but don't apply current filters - ts.setFilters(table, p.currentFilters, false); - } + p.initializing = true; if (wo.pager_savePages && ts.storage) { t = ts.storage(table, wo.pager_storageKey) || {}; // fixes #387 p.page = isNaN(t.page) ? p.page : t.page; @@ -173,7 +168,7 @@ tsp = ts.pager = { } // skipped rows - p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.replace(/^(\w+\.)/g,'') + '|' + c.cssChildRow + ')'); + p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.slice(1) + '|' + c.cssChildRow + ')'); // clear initialized flag p.initialized = false; @@ -209,9 +204,13 @@ tsp = ts.pager = { // pager initialized p.initialized = true; + p.initializing = false; p.isInitializing = false; c.$table.trigger('pagerInitialized', c); - tsp.updatePageDisplay(table, c); + // filter widget not initialized; it will update the output display & fire off the pagerComplete event + if ( !( c.widgetOptions.filter_initialized && ts.hasWidget(table, 'filter') ) ) { + tsp.updatePageDisplay(table, c, false); + } }, bindEvents: function(table, c){ @@ -221,23 +220,23 @@ tsp = ts.pager = { s = wo.pager_selectors; c.$table - .off('filterStart filterEnd sortEnd disable enable destroy updateComplete pageSize pageSet '.split(' ').join('.pager ')) - .on('filterStart.pager', function(e, filters) { - p.currentFilters = filters; - // don't change page is filters are the same (pager updating, etc) - if (wo.pager_pageReset !== false && (c.lastCombinedFilter || '') !== (filters || []).join('')) { + .off('filterInit filterStart filterEnd sortEnd disable enable destroy updateComplete pageSize pageSet '.split(' ').join('.pager ')) + .on('filterInit.pager filterStart.pager', function() { + p.currentFilters = c.$table.data('lastSearch'); + // don't change page if filters are the same (pager updating, etc) + if (wo.pager_pageReset !== false && (c.lastCombinedFilter || '') !== (p.currentFilters || []).join('')) { p.page = wo.pager_pageReset; // fixes #456 & #565 } }) // update pager after filter widget completes .on('filterEnd.pager sortEnd.pager', function() { - if (p.initialized) { + p.currentFilters = c.$table.data('lastSearch'); + if (p.initialized || p.initializing) { if (c.delayInit && c.rowsCopy && c.rowsCopy.length === 0) { // make sure we have a copy of all table rows once the cache has been built tsp.updateCache(table); } // tsp.moveToPage(table, p, false); <-- called when applyWidgets is triggered - c.pager.last.page = -1; c.$table.trigger('applyWidgets'); tsp.updatePageDisplay(table, c, false); } @@ -372,6 +371,7 @@ tsp = ts.pager = { }, updatePageDisplay: function(table, c, completed) { + if ( c.pager.initializing ) { return; } var s, t, $out, wo = c.widgetOptions, p = c.pager, @@ -554,7 +554,7 @@ tsp = ts.pager = { } else { rows[i].style.display = ( j >= s && j < e ) ? '' : 'none'; // don't count child rows - j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.replace(/^(\w+\.)/g,'')) && !wo.pager_countChildRows ? 0 : 1; + j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) && !wo.pager_countChildRows ? 0 : 1; if ( j === e && rows[i].style.display !== 'none' && rows[i].className.match(ts.css.cssHasChild) ) { lastIndex = i; } @@ -682,6 +682,7 @@ tsp = ts.pager = { p.last.totalRows = p.totalRows; p.last.currentFilters = p.currentFilters; p.last.sortList = (c.sortList || []).join(','); + p.initializing = false; tsp.updatePageDisplay(table, c); $t.trigger('updateCache', [function(){ if (p.initialized) { @@ -714,6 +715,7 @@ tsp = ts.pager = { $doc.off('ajaxError.pager'); }); counter = ++p.ajaxCounter; + p.last.ajaxUrl = url; // remember processed url p.ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl p.ajaxObject.success = function(data, status, jqxhr) { // Refuse to process old ajax commands that were overwritten by new ones - see #443 @@ -887,10 +889,15 @@ tsp = ts.pager = { var pg, c = table.config, wo = c.widgetOptions, l = p.last; + + // abort page move if the table has filters and has not been initialized + if (p.ajax && !wo.filter_initialized && ts.hasWidget(table, 'filter')) { return; } + tsp.calcFilters(table, c); pg = Math.min( p.totalPages, p.filteredPages ); if ( p.page < 0 ) { p.page = 0; } if ( p.page > ( pg - 1 ) && pg !== 0 ) { p.page = pg - 1; } + // fixes issue where one current filter is [] and the other is ['','',''], // making the next if comparison think the filters as different. Fixes #202. l.currentFilters = (l.currentFilters || []).join('') === '' ? [] : l.currentFilters; @@ -978,7 +985,7 @@ tsp = ts.pager = { c.appender = null; // remove pager appender function p.initialized = false; delete table.config.rowsCopy; - c.$table.off('destroy.pager sortEnd.pager filterEnd.pager enable.pager disable.pager'); + c.$table.off('filterInit filterStart filterEnd sortEnd disable enable destroy updateComplete pageSize pageSet '.split(' ').join('.pager ')); if (ts.storage) { ts.storage(table, c.widgetOptions.pager_storageKey, ''); } @@ -1045,7 +1052,7 @@ ts.showError = function(table, message){ }) // add error row to thead instead of tbody, or clicking on the header will result in a parser error .appendTo( c.$table.find('thead:first') ) - .addClass( errorRow + ' ' + c.selectorRemove.replace(/^(\w+\.)/g,'') ) + .addClass( errorRow + ' ' + c.selectorRemove.slice(1) ) .attr({ role : 'alert', 'aria-live' : 'assertive' From f6bd099692445e9811b2e61a79c56dc891095159 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 3 Nov 2014 19:03:20 +0100 Subject: [PATCH 041/138] * added metro-dark theme to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d795abb..6cda9ab 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ Avaliable theme names: * theme.grey * theme.ice * theme.jui +* theme.metro-dark pager theme: From 8d50a37f7024dc4927ba319383a4fd7a0ae6b01a Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Tue, 4 Nov 2014 02:23:00 +0100 Subject: [PATCH 042/138] * updated tablesorter to latest version (2.18.2) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 12 ++--- .../jquery-tablesorter/jquery.tablesorter.js | 6 +-- ...sorter.widgets-filter-formatter-select2.js | 2 +- ...ry.tablesorter.widgets-filter-formatter.js | 2 +- .../jquery.tablesorter.widgets.js | 44 ++++++++++++------- .../widgets/widget-pager.js | 15 ++++--- 10 files changed, 54 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1606b18..00084dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.13.2 (2014-11-04) + +* Upgrade tablesorter to v2.18.2 + #### v1.13.1 (2014-11-03) * Upgrade tablesorter to v2.18.1 diff --git a/README.md b/README.md index 6cda9ab..7446e01 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.18.1 (11/3/2014), [documentation] +Current tablesorter version: 2.18.2 (11/3/2014), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index ced7f62..e02bc31 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.13.1' + VERSION = '1.13.2' end diff --git a/tablesorter b/tablesorter index 363f083..e07ce81 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 363f083682cab970e5ef84090bdf631782d55024 +Subproject commit e07ce8146f80f0fda9701bd8254069a63f024b1e diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index ee91e97..1340b20 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! - * tablesorter pager plugin - * updated 11/3/2014 (v2.18.1) + * tablesorter (FORK) pager plugin + * updated 11/3/2014 (v2.18.2) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -161,7 +161,7 @@ c.filteredRows = p.filteredRows; p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) { - t = (p.size * p.page > p.filteredRows); + t = (p.size * p.page > p.filteredRows) && completed; p.startRow = (t) ? 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1); p.page = (t) ? 0 : p.page; p.endRow = Math.min( p.filteredRows, p.totalRows, p.size * ( p.page + 1 ) ); @@ -831,10 +831,10 @@ $t .unbind('filterInit filterStart filterEnd sortEnd disable enable destroy updateComplete pageSize pageSet '.split(' ').join('.pager ')) - .bind('filterInit.pager filterStart.pager', function() { + .bind('filterInit.pager filterStart.pager', function(e) { p.currentFilters = c.$table.data('lastSearch'); // don't change page if filters are the same (pager updating, etc) - if (p.pageReset !== false && (c.lastCombinedFilter || '') !== (p.currentFilters || []).join('')) { + if (e.type === 'filterStart' && p.pageReset !== false && (c.lastCombinedFilter || '') !== (p.currentFilters || []).join('')) { p.page = p.pageReset; // fixes #456 & #565 } }) @@ -969,7 +969,7 @@ moveToPage(table, p); $(table).trigger('pagerInitialized', p); if ( !( c.widgetOptions.filter_initialized && ts.hasWidget(table, 'filter') ) ) { - updatePageDisplay(table, c, false); + updatePageDisplay(table, p, false); } } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index f8892b4..e9ebd41 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter 2.18.1 - Client-side table sorting with ease! +* TableSorter (FORK) 2.18.2 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -9,7 +9,7 @@ * http://www.gnu.org/licenses/gpl.html * * @type jQuery -* @name tablesorter +* @name tablesorter (FORK) * @cat Plugins/Tablesorter * @author Christian Bach/christian.bach@polyester.se * @contributor Rob Garrison/https://github.com/Mottie/tablesorter @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.18.1"; + ts.version = "2.18.2"; ts.parsers = []; ts.widgets = []; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js index d835d93..c065573 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js @@ -1,5 +1,5 @@ /*! Filter widget select2 formatter function - updated 7/17/2014 (v2.17.5) - * requires: jQuery 1.7.2+, tableSorter 2.16+, filter widget 2.16+ and select2 v3.4.6+ plugin + * requires: jQuery 1.7.2+, tableSorter (FORK) 2.16+, filter widget 2.16+ and select2 v3.4.6+ plugin */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js index 81d006c..77050cd 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js @@ -1,5 +1,5 @@ /*! Filter widget formatter functions - updated 7/17/2014 (v2.17.5) - * requires: tableSorter 2.15+ and jQuery 1.4.3+ + * requires: tableSorter (FORK) 2.15+ and jQuery 1.4.3+ * * uiSpinner (jQuery UI spinner) * uiSlider (jQuery UI slider) diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index c1eba9d..f1c7128 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter 2.16+ widgets - updated 11/3/2014 (v2.18.1) +/*! tableSorter (FORK) 2.16+ widgets - updated 11/3/2014 (v2.18.2) * * Column Styles * Column Filters @@ -657,7 +657,7 @@ ts.filter = { $(document) .undelegate(wo.filter_reset, 'click.tsfilter') .delegate(wo.filter_reset, 'click.tsfilter', function() { - // trigger a reset event, so other functions (filterFormatter) know when to reset + // trigger a reset event, so other functions (filter_formatter) know when to reset c.$table.trigger('filterReset'); }); } @@ -764,23 +764,27 @@ ts.filter = { completed = function(){ wo.filter_initialized = true; c.$table.trigger('filterInit', c); - ts.filter.findRows(c.table, c.$table.data('lastSearch'), null); + ts.filter.findRows(c.table, c.$table.data('lastSearch') || []); }; - $.each( wo.filter_formatterInit, function(i, val) { - if (val === 1) { - count++; - } - }); - clearTimeout(wo.filter_initTimer); - if (!wo.filter_initialized && count === wo.filter_formatterCount) { - // filter widget initialized + if ( $.isEmptyObject( wo.filter_formatter ) ) { completed(); - } else if (!wo.filter_initialized) { - // fall back in case a filter_formatter doesn't call - // $.tablesorter.filter.formatterUpdated($cell, column), and the count is off - wo.filter_initTimer = setTimeout(function(){ + } else { + $.each( wo.filter_formatterInit, function(i, val) { + if (val === 1) { + count++; + } + }); + clearTimeout(wo.filter_initTimer); + if (!wo.filter_initialized && count === wo.filter_formatterCount) { + // filter widget initialized completed(); - }, 500); + } else if (!wo.filter_initialized) { + // fall back in case a filter_formatter doesn't call + // $.tablesorter.filter.formatterUpdated($cell, column), and the count is off + wo.filter_initTimer = setTimeout(function(){ + completed(); + }, 500); + } } }, @@ -1114,10 +1118,16 @@ ts.filter = { $(this).hasClass('filter-parsed'); }).get(); - if (c.debug) { time = new Date(); } + if (c.debug) { + ts.log('Starting filter widget search', filters); + time = new Date(); + } // filtered rows count c.filteredRows = 0; c.totalRows = 0; + // combindedFilters are undefined on init + combinedFilters = (filters || []).join(''); + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { if ($tbodies.eq(tbodyIndex).hasClass(c.cssInfoBlock || ts.css.info)) { continue; } // ignore info blocks, issue #264 $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index e6ede34..981af15 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -192,7 +192,6 @@ tsp = ts.pager = { p.ajax = false; // Regular pager; all rows stored in memory c.$table.trigger("appendCache", [{}, true]); - tsp.hideRowsSetup(table, c); } }, @@ -202,6 +201,10 @@ tsp = ts.pager = { tsp.bindEvents(table, c); tsp.setPageSize(table, 0, c); // page size 0 is ignored + if (!p.ajax) { + tsp.hideRowsSetup(table, c); + } + // pager initialized p.initialized = true; p.initializing = false; @@ -209,7 +212,8 @@ tsp = ts.pager = { c.$table.trigger('pagerInitialized', c); // filter widget not initialized; it will update the output display & fire off the pagerComplete event if ( !( c.widgetOptions.filter_initialized && ts.hasWidget(table, 'filter') ) ) { - tsp.updatePageDisplay(table, c, false); + // if ajax, then don't fire off pagerComplete + tsp.updatePageDisplay(table, c, !p.ajax); } }, @@ -221,10 +225,10 @@ tsp = ts.pager = { c.$table .off('filterInit filterStart filterEnd sortEnd disable enable destroy updateComplete pageSize pageSet '.split(' ').join('.pager ')) - .on('filterInit.pager filterStart.pager', function() { + .on('filterInit.pager filterStart.pager', function(e) { p.currentFilters = c.$table.data('lastSearch'); // don't change page if filters are the same (pager updating, etc) - if (wo.pager_pageReset !== false && (c.lastCombinedFilter || '') !== (p.currentFilters || []).join('')) { + if (e.type === 'filterStart' && wo.pager_pageReset !== false && (c.lastCombinedFilter || '') !== (p.currentFilters || []).join('')) { p.page = wo.pager_pageReset; // fixes #456 & #565 } }) @@ -384,7 +388,7 @@ tsp = ts.pager = { c.filteredRows = p.filteredRows; p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) { - t = (p.size * p.page > p.filteredRows); + t = (p.size * p.page > p.filteredRows) && completed; p.startRow = (t) ? 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1); p.page = (t) ? 0 : p.page; p.endRow = Math.min( p.filteredRows, p.totalRows, p.size * ( p.page + 1 ) ); @@ -820,7 +824,6 @@ tsp = ts.pager = { } ts.processTbody(table, $tb, false); } - tsp.updatePageDisplay(table, c); wo.pager_startPage = p.page; From ee3e9fa76662127ee708708f26e0926a4c5c508e Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Mon, 10 Nov 2014 17:54:22 +0100 Subject: [PATCH 043/138] * updated tablesorter to latest version (2.18.3) --- CHANGELOG.md | 4 + README.md | 4 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery-tablesorter/jquery.tablesorter.js | 25 +++++-- .../jquery.tablesorter.widgets.js | 6 +- .../widgets/widget-cssStickyHeaders.js | 74 +++++++++++++------ .../jquery-tablesorter/theme.black-ice.css | 2 +- .../jquery-tablesorter/theme.blue.css | 2 +- .../jquery-tablesorter/theme.bootstrap.css | 2 +- .../jquery-tablesorter/theme.bootstrap_2.css | 2 +- .../jquery-tablesorter/theme.dark.css | 2 +- .../jquery-tablesorter/theme.default.css | 2 +- .../jquery-tablesorter/theme.dropbox.css | 2 +- .../jquery-tablesorter/theme.green.css | 2 +- .../jquery-tablesorter/theme.grey.css | 4 +- .../jquery-tablesorter/theme.ice.css | 2 +- .../jquery-tablesorter/theme.jui.css | 2 +- .../jquery-tablesorter/theme.metro-dark.css | 2 +- 19 files changed, 96 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00084dc..4ecb1a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.13.3 (2014-11-10) + +* Upgrade tablesorter to v2.18.3 + #### v1.13.2 (2014-11-04) * Upgrade tablesorter to v2.18.2 diff --git a/README.md b/README.md index 7446e01..4f06ca9 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.18.2 (11/3/2014), [documentation] +Current tablesorter version: 2.18.3 (11/7/2014), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. @@ -26,7 +26,7 @@ Or install it yourself as: Rails 3.1 and higher (tested up to 4.1) -Tested with ruby 1.9.3 - 2.1.2 +Tested with ruby 1.9.3 - 2.1.4 ## Usage diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index e02bc31..7764573 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.13.2' + VERSION = '1.13.3' end diff --git a/tablesorter b/tablesorter index e07ce81..5ef1781 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit e07ce8146f80f0fda9701bd8254069a63f024b1e +Subproject commit 5ef1781aba08b83a7dc3c23b848c072deb60af93 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index e9ebd41..62d49f9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter (FORK) 2.18.2 - Client-side table sorting with ease! +* TableSorter (FORK) 2.18.3 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.18.2"; + ts.version = "2.18.3"; ts.parsers = []; ts.widgets = []; @@ -89,8 +89,11 @@ cssHeaderRow : '', cssProcessing : '', // processing icon applied to header during sort/filter - cssChildRow : 'tablesorter-childRow', // class name indiciating that a row is to be attached to the its parent + cssChildRow : 'tablesorter-childRow', // class name indiciating that a row is to be attached to the its parent cssIcon : 'tablesorter-icon', // if this class exists, a <i> will be added to the header automatically + cssIconNone : '', // class name added to the icon when there is no column sort + cssIconAsc : '', // class name added to the icon when the column has an ascending sort + cssIconDesc : '', // class name added to the icon when the column has a descending sort cssInfoBlock : 'tablesorter-infoOnly', // don't sort tbody with this class name (only one class name allowed here!) cssAllowClicks : 'tablesorter-allowClicks', // class name added to table header which allows clicks to bubble up @@ -528,13 +531,17 @@ len = list.length, none = ts.css.sortNone + ' ' + c.cssNone, css = [ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc], + cssIcon = [ c.cssIconAsc, c.cssIconDesc, c.cssIconNone ], aria = ['ascending', 'descending'], // find the footer $t = $(table).find('tfoot tr').children().add(c.$extraHeaders).removeClass(css.join(' ')); // remove all header information c.$headers .removeClass(css.join(' ')) - .addClass(none).attr('aria-sort', 'none'); + .addClass(none).attr('aria-sort', 'none') + .find('.' + c.cssIcon) + .removeClass(cssIcon.join(' ')) + .addClass(cssIcon[2]); for (i = 0; i < len; i++) { // direction = 2 means reset! if (list[i][1] !== 2) { @@ -543,7 +550,13 @@ if (f.length) { for (j = 0; j < f.length; j++) { if (!f[j].sortDisabled) { - f.eq(j).removeClass(none).addClass(css[list[i][1]]).attr('aria-sort', aria[list[i][1]]); + f.eq(j) + .removeClass(none) + .addClass(css[list[i][1]]) + .attr('aria-sort', aria[list[i][1]]) + .find('.' + c.cssIcon) + .removeClass(cssIcon[2]) + .addClass(cssIcon[list[i][1]]); } } // add sorted class to footer & extra headers, if they exist @@ -568,7 +581,7 @@ function fixColumnWidth(table) { var colgroup, overallWidth, c = table.config; - if (c.widthFixed && c.$table.find('colgroup').length === 0) { + if (c.widthFixed && c.$table.children('colgroup').length === 0) { colgroup = $('<colgroup>'); overallWidth = $(table).width(); // only add col for visible columns - fixes #371 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index f1c7128..a17136d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter (FORK) 2.16+ widgets - updated 11/3/2014 (v2.18.2) +/*! tableSorter (FORK) 2.16+ widgets - updated 11/7/2014 (v2.18.3) * * Column Styles * Column Filters @@ -821,7 +821,7 @@ ts.filter = { for (column = 0; column < columns; column++) { if (arry) { buildFilter += '<td' + ( wo.filter_cellFilter[column] ? ' class="' + wo.filter_cellFilter[column] + '"' : '' ) + '></td>'; - } else { + } else { buildFilter += '<td' + ( wo.filter_cellFilter !== '' ? ' class="' + wo.filter_cellFilter + '"' : '' ) + '></td>'; } } @@ -1732,7 +1732,7 @@ ts.addWidget({ // onRenderHeader is defined, we need to do something about it (fixes #641) if (c.onRenderHeader) { $stickyThead.children('tr').children().each(function(index){ - // send second parameter + // send second parameter c.onRenderHeader.apply( $(this), [ index, c, $stickyTable ] ); }); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js index 2e45193..8363c72 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js @@ -1,4 +1,4 @@ -/*! tablesorter CSS Sticky Headers widget - updated 11/3/2014 (v2.18.1) +/*! tablesorter CSS Sticky Headers widget - updated 11/7/2014 (v2.18.3) * Requires a modern browser, tablesorter v2.8+ */ /*jshint jquery:true, unused:false */ @@ -18,17 +18,45 @@ cssStickyHeaders_filteredToTop : true }, init : function(table, thisWidget, c, wo) { - var isIE = 'ActiveXObject' in window, // target all versions of IE - isFF = navigator.userAgent.toLowerCase().indexOf('firefox') > -1, + var ht, offst, adjustY, $table = c.$table, $attach = $(wo.cssStickyHeaders_attachTo), + isIE = 'ActiveXObject' in window, // target all versions of IE namespace = c.namespace + 'cssstickyheader ', $thead = $table.children('thead'), $caption = $table.children('caption'), $win = $attach.length ? $attach : $(window), $parent = $table.parent().closest('table.' + ts.css.table), $parentThead = $parent.length && ts.hasWidget($parent[0], 'cssStickyHeaders') ? $parent.children('thead') : [], - lastCaptionSetting = wo.cssStickyHeaders_addCaption; + borderTopWidth = ( parseInt( $table.css('border-top-width'), 10 ) || 0 ), + lastCaptionSetting = wo.cssStickyHeaders_addCaption, + // table offset top changes while scrolling in FF + adjustOffsetTop = false, + addCaptionHeight = false, + setTransform = function( $elms, y ) { + var translate = y === 0 ? '' : 'translate(0px,' + y + 'px)'; + $elms.css({ + 'transform' : translate, + '-ms-transform' : translate, + '-webkit-transform' : translate + }); + }; + + // Firefox fixes + if ($caption.length) { + // Firefox does not include the caption height when getting the table height + // see https://bugzilla.mozilla.org/show_bug.cgi?id=820891, so lets detect it instead of browser sniff + ht = $table.height(); + $caption.hide(); + addCaptionHeight = $table.height() === ht; + $caption.show(); + + // Firefox changes the offset().top when translating the table caption + offst = $table.offset().top; + setTransform( $caption, 20 ); + adjustOffsetTop = $table.offset().top !== offst; + setTransform( $caption, 0 ); + } $win .unbind('scroll resize '.split(' ').join(namespace)) @@ -36,6 +64,13 @@ // make sure "wo" is current otherwise changes to widgetOptions // are not dynamic (like the add caption button in the demo) wo = c.widgetOptions; + + if ( adjustOffsetTop ) { + // remove transform from caption to get the correct offset().top value + setTransform( $caption, 0 ); + adjustY = $table.offset().top; + } + var top = $attach.length ? $attach.offset().top : $win.scrollTop(), // add caption height; include table padding top & border-spacing or text may be above the fold (jQuery UI themes) // border-spacing needed in Firefox, but not webkit... not sure if I should account for that @@ -43,7 +78,9 @@ ( parseInt( $table.css('padding-top'), 10 ) || 0 ) + ( parseInt( $table.css('border-spacing'), 10 ) || 0 ), - bottom = $table.height() - $thead.height() - ( $table.children('tfoot').height() || 0 ) - ( wo.cssStickyHeaders_addCaption ? captionHeight : 0 ), + bottom = $table.height() + ( addCaptionHeight && wo.cssStickyHeaders_addCaption ? captionHeight : 0 ) - + $thead.height() - ( $table.children('tfoot').height() || 0 ) - + ( wo.cssStickyHeaders_addCaption ? captionHeight : ( addCaptionHeight ? 0 : captionHeight ) ), parentTheadHeight = $parentThead.length ? $parentThead.height() : 0, @@ -53,13 +90,15 @@ $parentThead.offset().top + parentTheadHeight - $win.scrollTop() ) : 0, + // in this case FF's offsetTop changes while scrolling, so we get a saved offsetTop before scrolling occurs + // but when the table is inside a wrapper ($attach) we need to continually update the offset top + tableOffsetTop = adjustOffsetTop ? adjustY : $table.offset().top, + + offsetTop = addCaptionHeight ? tableOffsetTop - ( wo.cssStickyHeaders_addCaption ? captionHeight : 0 ) : tableOffsetTop, + // Detect nested tables - fixes #724 - deltaY = top - $table.offset().top + nestedStickyBottom + - ( parseInt( $table.css('border-top-width'), 10 ) || 0 ) + - ( wo.cssStickyHeaders_offset || 0 ) + - // Again, I dislike browser sniffing... but I have no idea why I need to include a captionHeight - // for Firefox here and not for Chrome. Even IE behaves, sorta! - ( wo.cssStickyHeaders_addCaption ? ( isFF ? captionHeight : 0 ) : -captionHeight ), + deltaY = top - offsetTop + nestedStickyBottom + borderTopWidth + ( wo.cssStickyHeaders_offset || 0 ) - + ( wo.cssStickyHeaders_addCaption ? ( addCaptionHeight ? captionHeight : 0 ) : captionHeight ), finalY = deltaY > 0 && deltaY <= bottom ? deltaY : 0, @@ -80,19 +119,12 @@ lastCaptionSetting = wo.cssStickyHeaders_addCaption; // reset caption position if addCaption option is dynamically changed to false if (!lastCaptionSetting) { - $caption.css({ - 'transform' : '', - '-ms-transform' : '', - '-webkit-transform' : '' - }); + setTransform( $caption, 0 ); } } - $cells.css({ - 'transform' : finalY === 0 ? '' : 'translate(0px,' + finalY + 'px)', - '-ms-transform' : finalY === 0 ? '' : 'translate(0px,' + finalY + 'px)', - '-webkit-transform' : finalY === 0 ? '' : 'translate(0px,' + finalY + 'px)' - }); + setTransform( $cells, finalY ); + }); $table.unbind('filterEnd' + namespace).bind('filterEnd' + namespace, function() { if (wo.cssStickyHeaders_filteredToTop) { diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css index 64853c6..2104004 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css @@ -146,7 +146,7 @@ caption { line-height: 0; cursor: pointer; } -.tablesorter-blackice .tablesorter-filter-row.hideme .tablesorter-filter { +.tablesorter-blackice .tablesorter-filter-row.hideme * { height: 1px; min-height: 0; border: 0; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css index abccf2a..06a4cf2 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css @@ -181,7 +181,7 @@ caption { line-height: 0; cursor: pointer; } -.tablesorter-blue .tablesorter-filter-row.hideme .tablesorter-filter { +.tablesorter-blue .tablesorter-filter-row.hideme * { height: 1px; min-height: 0; border: 0; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index a8e717c..9610398 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -117,7 +117,7 @@ margin: 0; line-height: 0; } -.tablesorter-bootstrap .tablesorter-filter-row.hideme .tablesorter-filter { +.tablesorter-bootstrap .tablesorter-filter-row.hideme * { height: 1px; min-height: 0; border: 0; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css index d9665c9..84c7c77 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css @@ -119,7 +119,7 @@ caption { margin: 0; line-height: 0; } -.tablesorter-bootstrap .tablesorter-filter-row.hideme .tablesorter-filter { +.tablesorter-bootstrap .tablesorter-filter-row.hideme * { height: 1px; min-height: 0; border: 0; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css index 7cf876a..322e4cc 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css @@ -146,7 +146,7 @@ caption { line-height: 0; cursor: pointer; } -.tablesorter-dark .tablesorter-filter-row.hideme .tablesorter-filter { +.tablesorter-dark .tablesorter-filter-row.hideme * { height: 1px; min-height: 0; border: 0; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css index 7475753..757b357 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css @@ -149,7 +149,7 @@ caption { line-height: 0; cursor: pointer; } -.tablesorter-default .tablesorter-filter-row.hideme .tablesorter-filter { +.tablesorter-default .tablesorter-filter-row.hideme * { height: 1px; min-height: 0; border: 0; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css index ad74572..d949774 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css @@ -171,7 +171,7 @@ caption { line-height: 0; cursor: pointer; } -.tablesorter-dropbox .tablesorter-filter-row.hideme .tablesorter-filter { +.tablesorter-dropbox .tablesorter-filter-row.hideme * { height: 1px; min-height: 0; border: 0; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css index 77d1b54..493f685 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css @@ -163,7 +163,7 @@ caption { line-height: 0; cursor: pointer; } -.tablesorter-green .tablesorter-filter-row.hideme .tablesorter-filter { +.tablesorter-green .tablesorter-filter-row.hideme * { height: 1px; min-height: 0; border: 0; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css index 06453d2..24e1726 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css @@ -21,7 +21,7 @@ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#555), to(#3c3c3c)); background-image: -webkit-linear-gradient(top, #555, #3c3c3c); background-image: -o-linear-gradient(top, #555, #3c3c3c); - background-image: linear-gradient(to bottom, #555,#3c3c3c); + background-image: linear-gradient(to bottom, #555,#3c3c3c); filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#555555', endColorstr='#3c3c3c',GradientType=0 ); background-repeat: repeat-x; border-right: #555 1px solid; @@ -205,7 +205,7 @@ caption { line-height: 0; cursor: pointer; } -.tablesorter-grey .tablesorter-filter-row.hideme .tablesorter-filter { +.tablesorter-grey .tablesorter-filter-row.hideme * { height: 1px; min-height: 0; border: 0; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css index 74e88bf..a09039e 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css @@ -161,7 +161,7 @@ caption { line-height: 0; cursor: pointer; } -.tablesorter-ice .tablesorter-filter-row.hideme .tablesorter-filter { +.tablesorter-ice .tablesorter-filter-row.hideme * { height: 1px; min-height: 0; border: 0; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css index d06d9bc..ae29860 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css @@ -119,7 +119,7 @@ line-height: 0; cursor: pointer; } -.tablesorter-jui .tablesorter-filter-row.hideme .tablesorter-filter { +.tablesorter-jui .tablesorter-filter-row.hideme * { height: 1px; min-height: 0; border: 0; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css index e119b28..eba471e 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css @@ -152,7 +152,7 @@ Metro Dark Theme line-height: 0; cursor: pointer; } -.tablesorter-metro-dark .tablesorter-filter-row.hideme .tablesorter-filter { +.tablesorter-metro-dark .tablesorter-filter-row.hideme * { height: 1px; min-height: 0; border: 0; From c9bf72b836ef0ffedbdd53eb2f7ec6df152fb0be Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Sun, 21 Dec 2014 22:48:58 +0100 Subject: [PATCH 044/138] * updated Readme: ice theme needs to be required with file extension * updated Readme: a few version bumps --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4f06ca9..c9fe1cf 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,9 @@ Or install it yourself as: ## Requirements -Rails 3.1 and higher (tested up to 4.1) +Rails 3.1 and higher (tested up to 4.2) -Tested with ruby 1.9.3 - 2.1.4 +Tested with ruby 1.9.3 - 2.1.5 ## Usage @@ -73,11 +73,11 @@ Avaliable theme names: * theme.dropbox * theme.green * theme.grey -* theme.ice +* theme.ice.css (file extension required due to sprockets, see [issue #3](https://github.com/themilkman/jquery-tablesorter-rails/issues/3)) * theme.jui * theme.metro-dark -pager theme: +Pager theme: ```css /* From 66cccee2fa9072b0c4daf82ccb1bfa9297c4f594 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Tue, 23 Dec 2014 05:06:06 +0100 Subject: [PATCH 045/138] * updated tablesorter to latest version (2.18.4) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 6 +-- .../jquery-tablesorter/jquery.tablesorter.js | 48 +++++++++++-------- .../jquery.tablesorter.widgets.js | 12 ++--- .../widgets/widget-columnSelector.js | 18 +++++-- .../widgets/widget-pager.js | 4 +- .../jquery-tablesorter/theme.black-ice.css | 3 +- .../jquery-tablesorter/theme.blue.css | 3 +- .../jquery-tablesorter/theme.bootstrap.css | 5 +- .../jquery-tablesorter/theme.bootstrap_2.css | 5 +- .../jquery-tablesorter/theme.dark.css | 3 +- .../jquery-tablesorter/theme.default.css | 3 +- .../jquery-tablesorter/theme.dropbox.css | 25 +++++----- .../jquery-tablesorter/theme.green.css | 3 +- .../jquery-tablesorter/theme.grey.css | 19 ++++---- .../jquery-tablesorter/theme.ice.css | 3 +- .../jquery-tablesorter/theme.jui.css | 3 +- .../jquery-tablesorter/theme.metro-dark.css | 3 +- 21 files changed, 102 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ecb1a5..f58578c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.13.4 (2014-12-23) + +* Upgrade tablesorter to v2.18.4 + #### v1.13.3 (2014-11-10) * Upgrade tablesorter to v2.18.3 diff --git a/README.md b/README.md index c9fe1cf..54fcabe 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.18.3 (11/7/2014), [documentation] +Current tablesorter version: 2.18.4 (12/22/2014), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 7764573..ac1ca62 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.13.3' + VERSION = '1.13.4' end diff --git a/tablesorter b/tablesorter index 5ef1781..cdb766f 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 5ef1781aba08b83a7dc3c23b848c072deb60af93 +Subproject commit cdb766f8d901c6a2f2519a872d108429eab4ce18 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 1340b20..b11b6e4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin - * updated 11/3/2014 (v2.18.2) + * updated 12/22/2014 (v2.18.4) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -284,7 +284,7 @@ c = table.config, $b = c.$tbodies.eq(0); $b.find('tr.pagerSavedHeightSpacer').remove(); - if (p.fixedHeight && !p.isDisabled) { + if (p.fixedHeight && !p.isDisabled) { h = $.data(table, 'pagerSavedHeight'); if (h) { d = h - $b.height(); @@ -846,9 +846,9 @@ // make sure we have a copy of all table rows once the cache has been built updateCache(table); } + updatePageDisplay(table, p, false); moveToPage(table, p, false); c.$table.trigger('applyWidgets'); - updatePageDisplay(table, p, false); } }) .bind('disable.pager', function(e){ diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 62d49f9..22bd435 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter (FORK) 2.18.3 - Client-side table sorting with ease! +* TableSorter (FORK) 2.18.4 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -24,7 +24,7 @@ var ts = this; - ts.version = "2.18.3"; + ts.version = "2.18.4"; ts.parsers = []; ts.widgets = []; @@ -96,6 +96,7 @@ cssIconDesc : '', // class name added to the icon when the column has a descending sort cssInfoBlock : 'tablesorter-infoOnly', // don't sort tbody with this class name (only one class name allowed here!) cssAllowClicks : 'tablesorter-allowClicks', // class name added to table header which allows clicks to bubble up + cssIgnoreRow : 'tablesorter-ignoreRow', // header row to ignore; cells within this row will not be added to c.$headers // *** selectors selectorHeaders : '> thead th, > thead td', @@ -447,40 +448,44 @@ // add icon if cssIcon option exists i = c.cssIcon ? '<i class="' + ( c.cssIcon === ts.css.icon ? ts.css.icon : c.cssIcon + ' ' + ts.css.icon ) + '"></i>' : ''; // redefine c.$headers here in case of an updateAll that replaces or adds an entire header cell - see #683 - c.$headers = $(table).find(c.selectorHeaders).each(function(index) { - $t = $(this); + c.$headers = $( $.map( $(table).find(c.selectorHeaders), function(elem, index) { + $t = $(elem); + // ignore cell (don't add it to c.$headers) if row has ignoreRow class + if ($t.parent().hasClass(c.cssIgnoreRow)) { return; } // make sure to get header cell & not column indexed cell ch = ts.getColumnData( table, c.headers, index, true ); // save original header content - c.headerContent[index] = $(this).html(); + c.headerContent[index] = $t.html(); // if headerTemplate is empty, don't reformat the header cell if ( c.headerTemplate !== '' ) { // set up header template - t = c.headerTemplate.replace(/\{content\}/g, $(this).html()).replace(/\{icon\}/g, i); + t = c.headerTemplate.replace(/\{content\}/g, $t.html()).replace(/\{icon\}/g, i); if (c.onRenderTemplate) { h = c.onRenderTemplate.apply($t, [index, t]); if (h && typeof h === 'string') { t = h; } // only change t if something is returned } - $(this).html('<div class="' + ts.css.headerIn + '">' + t + '</div>'); // faster than wrapInner + $t.html('<div class="' + ts.css.headerIn + '">' + t + '</div>'); // faster than wrapInner } if (c.onRenderHeader) { c.onRenderHeader.apply($t, [index, c, c.$table]); } // *** remove this.column value if no conflicts found - this.column = parseInt( $(this).attr('data-column'), 10); - this.order = formatSortingOrder( ts.getData($t, ch, 'sortInitialOrder') || c.sortInitialOrder ) ? [1,0,2] : [0,1,2]; - this.count = -1; // set to -1 because clicking on the header automatically adds one - this.lockedOrder = false; + elem.column = parseInt( $t.attr('data-column'), 10); + elem.order = formatSortingOrder( ts.getData($t, ch, 'sortInitialOrder') || c.sortInitialOrder ) ? [1,0,2] : [0,1,2]; + elem.count = -1; // set to -1 because clicking on the header automatically adds one + elem.lockedOrder = false; lock = ts.getData($t, ch, 'lockedOrder') || false; if (typeof lock !== 'undefined' && lock !== false) { - this.order = this.lockedOrder = formatSortingOrder(lock) ? [1,1,1] : [0,0,0]; + elem.order = elem.lockedOrder = formatSortingOrder(lock) ? [1,1,1] : [0,0,0]; } $t.addClass(ts.css.header + ' ' + c.cssHeader); // add cell to headerList - c.headerList[index] = this; + c.headerList[index] = elem; // add to parent in case there are multiple rows $t.parent().addClass(ts.css.headerRow + ' ' + c.cssHeaderRow).attr('role', 'row'); // allow keyboard cursor to focus on element if (c.tabIndex) { $t.attr("tabindex", 0); } - }).attr({ + return elem; + })); + $(table).find(c.selectorHeaders).attr({ scope: 'col', role : 'columnheader' }); @@ -1163,27 +1168,28 @@ if (typeof c.initialized === 'function') { c.initialized(table); } }; - ts.getColumnData = function(table, obj, indx, getCell){ + ts.getColumnData = function(table, obj, indx, getCell, $headers){ if (typeof obj === 'undefined' || obj === null) { return; } table = $(table)[0]; - var result, $h, k, - c = table.config; + var $h, k, + c = table.config, + $cell = ( $headers || c.$headers ); if (obj[indx]) { - return getCell ? obj[indx] : obj[c.$headers.index( c.$headers.filter('[data-column="' + indx + '"]:last') )]; + return getCell ? obj[indx] : obj[$cell.index( $cell.filter('[data-column="' + indx + '"]:last') )]; } for (k in obj) { if (typeof k === 'string') { - $h = c.$headers.filter('[data-column="' + indx + '"]:last') + $h = $cell.filter('[data-column="' + indx + '"]:last') // header cell with class/id .filter(k) // find elements within the header cell with cell/id - .add( c.$headers.filter('[data-column="' + indx + '"]:last').find(k) ); + .add( $cell.filter('[data-column="' + indx + '"]:last').find(k) ); if ($h.length) { return obj[k]; } } } - return result; + return; }; // computeTableHeaderCellIndexes from: diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index a17136d..47b3939 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter (FORK) 2.16+ widgets - updated 11/7/2014 (v2.18.3) +/*! tableSorter (FORK) 2.16+ widgets - updated 12/22/2014 (v2.18.4) * * Column Styles * Column Filters @@ -1655,7 +1655,7 @@ ts.addWidget({ .wrap('<div class="' + ts.css.stickyWrap + '">'), $stickyWrap = $stickyTable.parent().css({ position : $attach.length ? 'absolute' : 'fixed', - margin : 0, + padding : parseInt( $stickyTable.parent().parent().css('padding-left'), 10 ), top : stickyOffset + nestedStickyTop, left : 0, visibility : 'hidden', @@ -1709,9 +1709,7 @@ ts.addWidget({ // include caption & filter row (fixes #126 & #249) - don't remove cells to get correct cell indexing $stickyTable.find('thead:gt(0), tr.sticky-false').hide(); $stickyTable.find('tbody, tfoot').remove(); - if (!wo.stickyHeaders_includeCaption) { - $stickyTable.find('caption').remove(); - } + $stickyTable.find('caption').toggle(wo.stickyHeaders_includeCaption); // issue #172 - find td/th in sticky header $stickyCells = $stickyThead.children().children(); $stickyTable.css({ height:0, width:0, margin: 0 }); @@ -1746,12 +1744,12 @@ ts.addWidget({ nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; var prefix = 'tablesorter-sticky-', offset = $table.offset(), - yWindow = $.isWindow( $yScroll[0] ), + yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 xWindow = $.isWindow( $xScroll[0] ), // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)), - isVisible = ( scrollTop > offset.top) && (scrollTop < offset.top + tableHeight) ? 'visible' : 'hidden', + isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', cssSettings = { visibility : isVisible }; if ($attach.length) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index 5455e0a..4432c67 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Column Selector/Responsive table widget (beta) for TableSorter - 11/3/2014 (v2.18.1) +/* Column Selector/Responsive table widget (beta) for TableSorter - 12/22/2014 (v2.18.4) * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -103,6 +103,7 @@ tsColSel = ts.columnSelector = { // input may not be wrapped within the layout template .find('input').add( colSel.$wrapper[colId].filter('input') ) .attr('data-column', colId) + .toggleClass( wo.columnSelector_cssChecked, colSel.states[colId] ) .prop('checked', colSel.states[colId]) .on('change', function(){ colSel.states[colId] = this.checked; @@ -138,6 +139,7 @@ tsColSel = ts.columnSelector = { .find('input').add( colSel.$auto.filter('input') ) .attr('data-column', 'auto') .prop('checked', colSel.auto) + .toggleClass( wo.columnSelector_cssChecked, colSel.auto ) .on('change', function(){ colSel.auto = this.checked; $.each( colSel.$checkbox, function(i, $cb){ @@ -225,6 +227,7 @@ tsColSel = ts.columnSelector = { styles.push(prefix + ' tr th:nth-child(' + column + ')'); styles.push(prefix + ' tr td:nth-child(' + column + ')'); } + $(this).toggleClass( wo.columnSelector_cssChecked, this.checked ); }); if (wo.columnSelector_mediaquery){ colSel.$breakpoints.prop('disabled', true); @@ -235,6 +238,7 @@ tsColSel = ts.columnSelector = { if (wo.columnSelector_saveColumns && ts.storage) { ts.storage( c.$table[0], 'tablesorter-columnSelector', colSel.states ); } + c.$table.trigger('columnUpdate'); }, attachTo : function(table, elm) { @@ -252,12 +256,13 @@ tsColSel = ts.columnSelector = { $popup.find('.tablesorter-column-selector') .html( colSel.$container.html() ) .find('input').each(function(){ - var indx = $(this).attr('data-column'); - $(this).prop( 'checked', indx === 'auto' ? colSel.auto : colSel.states[indx] ); + var indx = $(this).toggleClass( wo.columnSelector_cssChecked, isChecked ).attr('data-column'), + isChecked = indx === 'auto' ? colSel.auto : colSel.states[indx]; + $(this).prop( 'checked', isChecked ); }); colSel.$popup = $popup.on('change', 'input', function(){ // data input - indx = $(this).attr('data-column'); + indx = $(this).toggleClass( wo.columnSelector_cssChecked, this.checked ).attr('data-column'); // update original popup colSel.$container.find('input[data-column="' + indx + '"]') .prop('checked', this.checked) @@ -299,7 +304,10 @@ ts.addWidget({ // data attribute containing column priority // duplicates how jQuery mobile uses priorities: // http://view.jquerymobile.com/1.3.2/dist/demos/widgets/table-column-toggle/ - columnSelector_priority : 'data-priority' + columnSelector_priority : 'data-priority', + // class name added to checked checkboxes - this fixes an issue with Chrome not updating FontAwesome + // applied icons; use this class name (input.checked) instead of input:checked + columnSelector_cssChecked : 'checked' }, init: function(table, thisWidget, c, wo) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 981af15..76172fa 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/* Pager widget for TableSorter 11/3/2014 (v2.18.1) - requires jQuery 1.7+ */ +/* Pager widget for TableSorter 12/22/2014 (v2.18.4) - requires jQuery 1.7+ */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ "use strict"; @@ -240,9 +240,9 @@ tsp = ts.pager = { // make sure we have a copy of all table rows once the cache has been built tsp.updateCache(table); } + tsp.updatePageDisplay(table, c, false); // tsp.moveToPage(table, p, false); <-- called when applyWidgets is triggered c.$table.trigger('applyWidgets'); - tsp.updatePageDisplay(table, c, false); } }) .on('disable.pager', function(e){ diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css index 2104004..72cfda6 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css @@ -157,7 +157,8 @@ caption { filter: alpha(opacity=0); } /* filters */ -.tablesorter-blackice .tablesorter-filter { +.tablesorter-blackice input.tablesorter-filter, +.tablesorter-blackice select.tablesorter-filter { width: 98%; height: auto; margin: 0; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css index 06a4cf2..191efdd 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css @@ -192,7 +192,8 @@ caption { filter: alpha(opacity=0); } /* filters */ -.tablesorter-blue .tablesorter-filter { +.tablesorter-blue input.tablesorter-filter, +.tablesorter-blue select.tablesorter-filter { width: 98%; height: auto; margin: 0; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index 9610398..dd34d6a 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -25,7 +25,7 @@ } /* bootstrap uses <i> for icons */ -.tablesorter-bootstrap .tablesorter-header i { +.tablesorter-bootstrap .tablesorter-header i.tablesorter-icon { font-size: 11px; position: absolute; right: 2px; @@ -77,7 +77,8 @@ } /* filter widget */ -.tablesorter-bootstrap .tablesorter-filter-row .tablesorter-filter { +.tablesorter-bootstrap .tablesorter-filter-row input.tablesorter-filter, +.tablesorter-bootstrap .tablesorter-filter-row select.tablesorter-filter { width: 98%; margin: 0 auto; padding: 4px 6px; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css index 84c7c77..d572c2d 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css @@ -37,7 +37,7 @@ } /* bootstrap uses <i> for icons */ -.tablesorter-bootstrap .tablesorter-header i { +.tablesorter-bootstrap .tablesorter-header i.tablesorter-icon { position: absolute; right: 2px; top: 50%; @@ -84,7 +84,8 @@ caption { } /* filter widget */ -.tablesorter-bootstrap .tablesorter-filter-row .tablesorter-filter { +.tablesorter-bootstrap .tablesorter-filter-row input.tablesorter-filter, +.tablesorter-bootstrap .tablesorter-filter-row select.tablesorter-filter { width: 98%; margin: 0 auto; padding: 4px 6px; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css index 322e4cc..329e508 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css @@ -158,7 +158,8 @@ caption { } /* filters */ -.tablesorter-dark .tablesorter-filter { +.tablesorter-dark input.tablesorter-filter, +.tablesorter-dark select.tablesorter-filter { width: 98%; height: auto; margin: 4px; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css index 757b357..d8fec8e 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css @@ -160,7 +160,8 @@ caption { filter: alpha(opacity=0); } /* filters */ -.tablesorter-default .tablesorter-filter { +.tablesorter-default input.tablesorter-filter, +.tablesorter-default select.tablesorter-filter { width: 95%; height: auto; margin: 4px; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css index d949774..adc388c 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css @@ -46,7 +46,7 @@ .tablesorter-dropbox .tablesorter-header { cursor: pointer; } -.tablesorter-dropbox .tablesorter-header i { +.tablesorter-dropbox .tablesorter-header i.tablesorter-icon { width: 9px; height: 9px; padding: 0 10px 0 4px; @@ -55,23 +55,23 @@ background-repeat: no-repeat; content: ""; } -.tablesorter-dropbox .tablesorter-headerSortUp i, -.tablesorter-dropbox .tablesorter-headerAsc i { +.tablesorter-dropbox .tablesorter-headerSortUp i.tablesorter-icon, +.tablesorter-dropbox .tablesorter-headerAsc i.tablesorter-icon { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAALhJREFUeNpi/P//PwMhwILMiexYx8bIxNTy/9+/muUVQb9g4kzIitg4edI4+YRLQTSyOCPMupjerUI8whK3OXgEhH58+fDuy9sXqkuKvd+hmMTOxdvCxS8sxMUvxACiQXwU6+Im7DDg5BNKY+fiY2BmYWMA0SA+SByuiJ2bbzIHrwAzMxsb0AGMDCAaxAeJg+SZ7wtaqfAISfQAdTIwMUM8ywhUyMTEzPD/71+5FXvPLWUkJpwAAgwAZqYvvHStbD4AAAAASUVORK5CYII='); /* background-image: url(/assets/jquery-tablesorter/dropbox-asc.png); */ } -.tablesorter-dropbox .tablesorter-headerSortUp:hover i, -.tablesorter-dropbox .tablesorter-headerAsc:hover i { +.tablesorter-dropbox .tablesorter-headerSortUp:hover i.tablesorter-icon, +.tablesorter-dropbox .tablesorter-headerAsc:hover i.tablesorter-icon { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAALVJREFUeNpi/P//PwMhwILMCc+qZGNkYmr5/+9fzcpp7b9g4kzIitjYOdM4uXlLQTSyOCPMuqi8OiEefsHbHFzcQj++fX335eN71WWTmt6hmMTOwdXCycMnBDSJAUSD+CjWxRQ0GHBw86Sxc3AyMDOzMIBoEB8kDlfEzsk1mYOLByjPCnQAIwOIBvFB4iB55rsfmVS4+QV7QNYwMTNDHApUyMTExPDv/z+5Feu3L2UkJpwAAgwA244u+I9CleAAAAAASUVORK5CYII='); /* background-image: url(/assets/jquery-tablesorter/dropbox-asc-hovered.png); */ } -.tablesorter-dropbox .tablesorter-headerSortDown i, -.tablesorter-dropbox .tablesorter-headerDesc i { +.tablesorter-dropbox .tablesorter-headerSortDown i.tablesorter-icon, +.tablesorter-dropbox .tablesorter-headerDesc i.tablesorter-icon { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAALdJREFUeNpi/P//PwMhwBLdtVGFhZ3zNhMzC4bkv79/GP78/K7KCDIpZ9mVw+xcfDaMTExwBf///WP4+e3TkSlROrZg7UxMLLns3HxnmFnZmGGK/v7+9ff3j2+5YHkQMSlC48Kv719m/f//D2IKkAbxQeJwRSDw4/OHmr+/fr0DqmAA0SA+TA6uaEq0zjugG+r//vkFcks9iA/3HbJvvn18O+vf379yP758mMXAoAAXZyQmnAACDADX316BiTFbMQAAAABJRU5ErkJggg=='); /* background-image: url(/assets/jquery-tablesorter/dropbox-desc.png); */ } -.tablesorter-dropbox .tablesorter-headerSortDown:hover i, -.tablesorter-dropbox .tablesorter-headerDesc:hover i { +.tablesorter-dropbox .tablesorter-headerSortDown:hover i.tablesorter-icon, +.tablesorter-dropbox .tablesorter-headerDesc:hover i.tablesorter-icon { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAALNJREFUeNpi/P//PwMhwBJf3uP879e3PUzMzBiS//7+ZWBi43JhBJmU2z7nIzMzEx8jIyNcAUj8799/nyZXpvCzgARYuXjTWBkZVjCzIEz7++cvw+//DGkgNiPMTWVT1l5hZvynDTINbMp/pqtdOcE6IDkmmM5fv3/5//v37z9QBQOIBvFhcnBFEwoj7/5jZFnz9+8fBhAN4sN9h+ybH9++JrGxscr/+vE1CVmckZhwAggwANvlUyq5Dd1wAAAAAElFTkSuQmCC'); /* background-image: url(/assets/jquery-tablesorter/dropbox-desc-hovered.png); */ } @@ -79,8 +79,8 @@ cursor: default; } -.tablesorter-dropbox thead .sorter-false i, -.tablesorter-dropbox thead .sorter-false:hover i { +.tablesorter-dropbox thead .sorter-false i.tablesorter-icon, +.tablesorter-dropbox thead .sorter-false:hover i.tablesorter-icon { background-image: none; padding: 4px; } @@ -183,7 +183,8 @@ caption { } /* filters */ -.tablesorter-dropbox .tablesorter-filter { +.tablesorter-dropbox input.tablesorter-filter, +.tablesorter-dropbox select.tablesorter-filter { width: 98%; height: auto; margin: 4px; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css index 493f685..3d8c5aa 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css @@ -174,7 +174,8 @@ caption { filter: alpha(opacity=0); } /* filters */ -.tablesorter-green .tablesorter-filter { +.tablesorter-green input.tablesorter-filter, +.tablesorter-green select.tablesorter-filter { width: 98%; height: auto; margin: 4px; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css index 24e1726..b31498a 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css @@ -41,7 +41,7 @@ cursor: pointer; } .tablesorter-grey .header i, -.tablesorter-grey .tablesorter-header i { +.tablesorter-grey .tablesorter-header i.tablesorter-icon { width: 18px; height: 10px; position: absolute; @@ -69,22 +69,22 @@ background-image: linear-gradient(to bottom, #195c93, #0e4776); filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#195c93', endColorstr='#0e4776',GradientType=0 ); } -.tablesorter-grey .headerSortUp i, -.tablesorter-grey .tablesorter-headerSortUp i, -.tablesorter-grey .tablesorter-headerAsc i { +.tablesorter-grey .headerSortUp i.tablesorter-icon, +.tablesorter-grey .tablesorter-headerSortUp i.tablesorter-icon, +.tablesorter-grey .tablesorter-headerAsc i.tablesorter-icon { /* white asc arrow */ background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7); } -.tablesorter-grey .headerSortDown i, -.tablesorter-grey .tablesorter-headerSortDown i, -.tablesorter-grey .tablesorter-headerDesc i { +.tablesorter-grey .headerSortDown i.tablesorter-icon, +.tablesorter-grey .tablesorter-headerSortDown i.tablesorter-icon, +.tablesorter-grey .tablesorter-headerDesc i.tablesorter-icon { /* white desc arrow */ background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7); } .tablesorter-grey thead .sorter-false { cursor: default; } -.tablesorter-grey thead .sorter-false i { +.tablesorter-grey thead .sorter-false i.tablesorter-icon { background-image: none; padding: 4px; } @@ -216,7 +216,8 @@ caption { filter: alpha(opacity=0); } /* filters */ -.tablesorter-grey .tablesorter-filter { +.tablesorter-grey input.tablesorter-filter, +.tablesorter-grey select.tablesorter-filter { width: 98%; height: auto; margin: 0; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css index a09039e..32952c4 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css @@ -172,7 +172,8 @@ caption { filter: alpha(opacity=0); } /* filters */ -.tablesorter-ice .tablesorter-filter { +.tablesorter-ice input.tablesorter-filter, +.tablesorter-ice select.tablesorter-filter { width: 98%; height: auto; margin: 4px; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css index ae29860..a6f0f92 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css @@ -130,7 +130,8 @@ filter: alpha(opacity=0); } /* filters */ -.tablesorter-jui .tablesorter-filter { +.tablesorter-jui input.tablesorter-filter, +.tablesorter-jui select.tablesorter-filter { width: 98%; height: auto; margin: 0; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css index eba471e..4898d49 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css @@ -163,7 +163,8 @@ Metro Dark Theme filter: alpha(opacity=0); } /* filters */ -.tablesorter-metro-dark .tablesorter-filter { +.tablesorter-metro-dark input.tablesorter-filter, +.tablesorter-metro-dark select.tablesorter-filter { width: 95%; height: auto; margin: 4px; From d2f355b14053da7c794464f8ca0b791c50f53b8b Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Sat, 7 Feb 2015 11:53:25 +0100 Subject: [PATCH 046/138] * updated tablesorter to latest version (2.19.0) --- CHANGELOG.md | 4 + MIT-LICENSE | 2 +- README.md | 4 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery-tablesorter/dragtable-handle.png | Bin 0 -> 103 bytes .../jquery-tablesorter/dragtable-handle.svg | 7 + .../addons/pager/jquery.tablesorter.pager.js | 90 ++- .../extras/jquery.dragtable.mod.js | 605 ++++++++++++++++++ .../jquery-tablesorter/jquery.tablesorter.js | 242 ++++--- ...sorter.widgets-filter-formatter-select2.js | 71 +- .../jquery.tablesorter.widgets.js | 247 ++++--- .../parsers/parser-duration.js | 37 +- .../parsers/parser-input-select.js | 47 +- .../widgets/widget-alignChar.js | 9 +- .../widgets/widget-build-table.js | 4 +- .../widgets/widget-chart.js | 276 ++++++++ .../widgets/widget-columnSelector.js | 96 ++- .../widgets/widget-cssStickyHeaders.js | 5 +- .../widgets/widget-editable.js | 16 +- .../widgets/widget-formatter.js | 70 ++ .../jquery-tablesorter/widgets/widget-math.js | 19 +- .../widgets/widget-output.js | 10 +- .../widgets/widget-pager.js | 111 ++-- .../widgets/widget-print.js | 4 +- .../widgets/widget-reflow.js | 7 +- .../widgets/widget-repeatheaders.js | 26 +- .../widgets/widget-scroller.js | 94 ++- .../jquery-tablesorter/dragtable.mod.css | 64 ++ .../jquery-tablesorter/theme.black-ice.css | 3 + .../jquery-tablesorter/theme.blue.css | 3 + .../jquery-tablesorter/theme.bootstrap.css | 3 + .../jquery-tablesorter/theme.bootstrap_2.css | 3 + .../jquery-tablesorter/theme.dark.css | 3 + .../jquery-tablesorter/theme.default.css | 3 + .../jquery-tablesorter/theme.dropbox.css | 3 + .../jquery-tablesorter/theme.green.css | 3 + .../jquery-tablesorter/theme.grey.css | 3 + .../jquery-tablesorter/theme.ice.css | 3 + .../jquery-tablesorter/theme.jui.css | 3 + .../jquery-tablesorter/theme.metro-dark.css | 3 + 41 files changed, 1794 insertions(+), 413 deletions(-) create mode 100644 vendor/assets/images/jquery-tablesorter/dragtable-handle.png create mode 100644 vendor/assets/images/jquery-tablesorter/dragtable-handle.svg create mode 100644 vendor/assets/javascripts/jquery-tablesorter/extras/jquery.dragtable.mod.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js create mode 100644 vendor/assets/stylesheets/jquery-tablesorter/dragtable.mod.css diff --git a/CHANGELOG.md b/CHANGELOG.md index f58578c..c2b2cc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.14.0 (2015-02-07) + +* Upgrade tablesorter to v2.19.0 + #### v1.13.4 (2014-12-23) * Upgrade tablesorter to v2.18.4 diff --git a/MIT-LICENSE b/MIT-LICENSE index 8016b6d..1c11a4f 100644 --- a/MIT-LICENSE +++ b/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright 2014 Jun Lin, Erik-B. Ernst +Copyright 2015 Jun Lin, Erik-B. Ernst Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.md b/README.md index 54fcabe..7a360c3 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.18.4 (12/22/2014), [documentation] +Current tablesorter version: 2.19.0 (2/7/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. @@ -26,7 +26,7 @@ Or install it yourself as: Rails 3.1 and higher (tested up to 4.2) -Tested with ruby 1.9.3 - 2.1.5 +Tested with ruby 1.9.3 - 2.2.0 ## Usage diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index ac1ca62..a46c0f3 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.13.4' + VERSION = '1.14.0' end diff --git a/tablesorter b/tablesorter index cdb766f..9b32f0c 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit cdb766f8d901c6a2f2519a872d108429eab4ce18 +Subproject commit 9b32f0c5f6a09084143214f460323de4cfa1a90f diff --git a/vendor/assets/images/jquery-tablesorter/dragtable-handle.png b/vendor/assets/images/jquery-tablesorter/dragtable-handle.png new file mode 100644 index 0000000000000000000000000000000000000000..ff64a7d572cf53cf31ff98cd261c1d21ebe19e97 GIT binary patch literal 103 zcmeAS@N?(olHy`uVBq!ia0vp^OdvKRGmzY=r@IMAu?6^qxB_WoW8)JapD6%YOeH~n w!3<}fI65x|^7uSm978y+Co?dz>&#$e5Qt)Q)VVuxK2V0i)78&qol`;+0PtZJpa1{> literal 0 HcmV?d00001 diff --git a/vendor/assets/images/jquery-tablesorter/dragtable-handle.svg b/vendor/assets/images/jquery-tablesorter/dragtable-handle.svg new file mode 100644 index 0000000..041ec1d --- /dev/null +++ b/vendor/assets/images/jquery-tablesorter/dragtable-handle.svg @@ -0,0 +1,7 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="2" height="13"> + <rect style="fill:#333;fill-opacity:.8;" width="1" height="1" x="1" y="2"/> + <rect style="fill:#333;fill-opacity:.8;" width="1" height="1" x="1" y="4"/> + <rect style="fill:#333;fill-opacity:.8;" width="1" height="1" x="1" y="6"/> + <rect style="fill:#333;fill-opacity:.8;" width="1" height="1" x="1" y="8"/> + <rect style="fill:#333;fill-opacity:.8;" width="1" height="1" x="1" y="10"/> +</svg> \ No newline at end of file diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index b11b6e4..9090b81 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin - * updated 12/22/2014 (v2.18.4) + * updated 2/7/2015 (v2.19.0) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -114,7 +114,10 @@ }; - var $this = this, + var pagerEvents = 'filterInit filterStart filterEnd sortEnd disable enable destroy updateComplete ' + + 'pageSize pageSet pageAndSize pagerUpdate ', + + $this = this, // hide arrows at extremes pagerArrows = function(p, disable) { @@ -132,15 +135,18 @@ }, calcFilters = function(table, p) { - var c = table.config, + var tbodyIndex, + c = table.config, hasFilters = c.$table.hasClass('hasFilters'); if (hasFilters && !p.ajaxUrl) { if ($.isEmptyObject(c.cache)) { // delayInit: true so nothing is in the cache p.filteredRows = p.totalRows = c.$tbodies.eq(0).children('tr').not( p.countChildRows ? '' : '.' + c.cssChildRow ).length; } else { + // just in case the pager tbody isn't the first tbody + tbodyIndex = c.$table.children('tbody').index( c.$tbodies.eq(0) ); p.filteredRows = 0; - $.each(c.cache[0].normalized, function(i, el) { + $.each(c.cache[tbodyIndex].normalized, function(i, el) { p.filteredRows += p.regexRows.test(el[c.columns].$row[0].className) ? 0 : 1; }); } @@ -153,7 +159,7 @@ if ( p.initializing ) { return; } var s, t, $out, c = table.config, - sz = p.size || 10; // don't allow dividing by zero + sz = p.size || p.settings.size || 10; // don't allow dividing by zero if (p.countChildRows) { t.push(c.cssChildRow); } p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method c.totalRows = p.totalRows; @@ -162,8 +168,8 @@ p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) { t = (p.size * p.page > p.filteredRows) && completed; - p.startRow = (t) ? 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1); - p.page = (t) ? 0 : p.page; + p.page = (t) ? p.pageReset || 0 : p.page; + p.startRow = (t) ? p.size * p.page + 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1); p.endRow = Math.min( p.filteredRows, p.totalRows, p.size * ( p.page + 1 ) ); $out = p.$container.find(p.cssPageDisplay); // form the output string (can now get a new output string from the server) @@ -187,16 +193,16 @@ } return extra.length > 1 && data && data[extra[0]] ? data[extra[0]][extra[1]] : p[str] || (data ? data[str] : deflt) || deflt; }); + if ( p.$goto.length ) { + t = ''; + $.each(buildPageSelect(p), function(i, opt){ + t += '<option value="' + opt + '">' + opt + '</option>'; + }); + // innerHTML doesn't work in IE9 - http://support2.microsoft.com/kb/276228 + p.$goto.html(t).val( p.page + 1 ); + } if ($out.length) { $out[ ($out[0].tagName === 'INPUT') ? 'val' : 'html' ](s); - if ( p.$goto.length ) { - t = ''; - $.each(buildPageSelect(p), function(i, opt){ - t += '<option value="' + opt + '">' + opt + '</option>'; - }); - // innerHTML doesn't work in IE9 - http://support2.microsoft.com/kb/276228 - p.$goto.html(t).val( p.page + 1 ); - } // rebind startRow/page inputs $out.find('.ts-startRow, .ts-page').unbind('change').bind('change', function(){ var v = $(this).val(), @@ -319,7 +325,9 @@ s = ( p.page * p.size ), e = s + p.size, f = c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered', + last = 0, // for cache indexing j = 0; // size counter + p.cacheIndex = []; for ( i = 0; i < l; i++ ){ if ( !rows[i].className.match(f) ) { if (j === s && rows[i].className.match(c.cssChildRow)) { @@ -327,6 +335,10 @@ rows[i].style.display = 'none'; } else { rows[i].style.display = ( j >= s && j < e ) ? '' : 'none'; + if (last !== j && j >= s && j < e) { + p.cacheIndex.push(i); + last = j; + } // don't count child rows j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) && !p.countChildRows ? 0 : 1; if ( j === e && rows[i].style.display !== 'none' && rows[i].className.match(ts.css.cssHasChild) ) { @@ -345,7 +357,7 @@ }, hideRowsSetup = function(table, p){ - p.size = parseInt( p.$size.val(), 10 ) || p.size; + p.size = parseInt( p.$size.val(), 10 ) || p.size || p.settings.size || 10; $.data(table, 'pagerLastSize', p.size); pagerArrows(p); if ( !p.removeRows ) { @@ -457,11 +469,11 @@ } // make sure last pager settings are saved, prevents multiple server side calls with // the same parameters - p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) ); + p.totalPages = Math.ceil( p.totalRows / ( p.size || p.settings.size || 10 ) ); p.last.totalRows = p.totalRows; p.last.currentFilters = p.currentFilters; p.last.sortList = (c.sortList || []).join(','); - updatePageDisplay(table, p, true); + updatePageDisplay(table, p, false); $t.trigger('updateCache', [function(){ if (p.initialized) { // apply widgets after table has rendered & after a delay to prevent @@ -470,6 +482,7 @@ $t .trigger('applyWidgets') .trigger('pagerChange', p); + updatePageDisplay(table, p, true); }, 0); } }]); @@ -580,6 +593,7 @@ // lets not render the table more than once moveToLastPage(table, p); } + p.cacheIndex = []; p.isDisabled = false; // needed because sorting will change the page and re-enable the pager if (p.initialized) { $t.trigger('pagerChange', p); } @@ -598,6 +612,7 @@ count++; if (count > s && added <= e) { added++; + p.cacheIndex.push(index); $tb.append(rows[index]); } } @@ -644,7 +659,8 @@ c.$table.trigger('updateCache', [ function(){ var i, rows = [], - n = table.config.cache[0].normalized; + tbodyIndex = c.$table.children('tbody').index( c.$tbodies.eq(0) ), + n = table.config.cache[tbodyIndex].normalized; p.totalRows = n.length; for (i = 0; i < p.totalRows; i++) { rows.push(n[i][c.columns].$row); @@ -711,7 +727,7 @@ }, setPageSize = function(table, size, p) { - p.size = size || p.size || 10; + p.size = size || p.size || p.settings.size || 10; p.$size.val(p.size); $.data(table, 'pagerLastPage', p.page); $.data(table, 'pagerLastSize', p.size); @@ -752,7 +768,7 @@ table.config.appender = null; // remove pager appender function p.initialized = false; delete table.config.rowsCopy; - $(table).unbind('filterInit filterStart filterEnd sortEnd disable enable destroy updateComplete pageSize pageSet '.split(' ').join('.pager ')); + $(table).unbind(pagerEvents.split(' ').join('.pager ')); if (ts.storage) { ts.storage(table, p.storageKey, ''); } @@ -767,7 +783,7 @@ .attr('aria-disabled', 'false'); p.isDisabled = false; p.page = $.data(table, 'pagerLastPage') || p.page || 0; - p.size = $.data(table, 'pagerLastSize') || parseInt(p.$size.find('option[selected]').val(), 10) || p.size || 10; + p.size = $.data(table, 'pagerLastSize') || parseInt(p.$size.find('option[selected]').val(), 10) || p.size || p.settings.size || 10; p.$size.val(p.size); // set page size p.totalPages = Math.ceil( Math.min( p.totalRows, p.filteredRows ) / p.size ); // if table id exists, include page display with aria info @@ -793,7 +809,7 @@ if ( !p.ajax ) { c.rowsCopy = rows; p.totalRows = p.countChildRows ? c.$tbodies.eq(0).children('tr').length : rows.length; - p.size = $.data(table, 'pagerLastSize') || p.size || 10; + p.size = $.data(table, 'pagerLastSize') || p.size || p.settings.size || 10; p.totalPages = Math.ceil( p.totalRows / p.size ); renderTable(table, rows, p); // update display here in case all rows are removed @@ -813,6 +829,8 @@ $t = c.$table, // added in case the pager is reinitialized after being destroyed. pager = p.$container = $(p.container).addClass('tablesorter-pager').show(); + // save a copy of the original settings + p.settings = $.extend( true, {}, $.tablesorterPager.defaults, settings ); if (c.debug) { ts.log('Pager initializing'); } @@ -822,7 +840,7 @@ if (p.savePages && ts.storage) { t = ts.storage(table, p.storageKey) || {}; // fixes #387 p.page = isNaN(t.page) ? p.page : t.page; - p.size = ( isNaN(t.size) ? p.size : t.size ) || 10; + p.size = ( isNaN(t.size) ? p.size : t.size ) || p.settings.size || 10; $.data(table, 'pagerLastSize', p.size); } @@ -830,9 +848,9 @@ p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.slice(1) + '|' + c.cssChildRow + ')'); $t - .unbind('filterInit filterStart filterEnd sortEnd disable enable destroy updateComplete pageSize pageSet '.split(' ').join('.pager ')) - .bind('filterInit.pager filterStart.pager', function(e) { - p.currentFilters = c.$table.data('lastSearch'); + .unbind(pagerEvents.split(' ').join('.pager ')) + .bind('filterInit.pager filterStart.pager', function(e, filters) { + p.currentFilters = $.isArray(filters) ? filters : c.$table.data('lastSearch'); // don't change page if filters are the same (pager updating, etc) if (e.type === 'filterStart' && p.pageReset !== false && (c.lastCombinedFilter || '') !== (p.currentFilters || []).join('')) { p.page = p.pageReset; // fixes #456 & #565 @@ -881,18 +899,26 @@ changeHeight(table, p); updatePageDisplay(table, p, true); }) - .bind('pageSize.pager', function(e,v){ + .bind('pageSize.pager refreshComplete.pager', function(e,v){ e.stopPropagation(); - setPageSize(table, parseInt(v, 10) || 10, p); + setPageSize(table, parseInt(v, 10) || p.settings.size || 10, p); hideRows(table, p); updatePageDisplay(table, p, false); - if (p.$size.length) { p.$size.val(p.size); } // twice? }) - .bind('pageSet.pager', function(e,v){ + .bind('pageSet.pager pagerUpdate.pager', function(e,v){ e.stopPropagation(); p.page = (parseInt(v, 10) || 1) - 1; - if (p.$goto.length) { p.$goto.val(p.size); } // twice? + // force pager refresh + if (e.type === 'pagerUpdate') { p.last.page = true; } + moveToPage(table, p, true); + updatePageDisplay(table, p, false); + }) + .bind('pageAndSize.pager', function(e, page, size){ + e.stopPropagation(); + p.page = (parseInt(page, 10) || 1) - 1; + setPageSize(table, parseInt(size, 10) || p.settings.size || 10, p); moveToPage(table, p, true); + hideRows(table, p); updatePageDisplay(table, p, false); }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.dragtable.mod.js b/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.dragtable.mod.js new file mode 100644 index 0000000..c3309f6 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.dragtable.mod.js @@ -0,0 +1,605 @@ +/*! Dragtable Mod for TableSorter - 2/7/2015 (v2.19.0) *//* + * Requires + * tablesorter v2.8+ + * jQuery 1.7+ + * jQuery UI (Core, Widget, Mouse & Sortable) + * Dragtable by Akottr (https://github.com/akottr) modified by Rob Garrison + */ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;(function( $ ) { +'use strict'; + var undef, + ts = $.tablesorter; + + ts.dragtable = { + create : function( _this ) { + var hasAccept, + $table = _this.originalTable.el, + handle = _this.options.dragHandle.replace('.', ''); + $table.children('thead').children().children('th,td').each(function(){ + var $this = $(this); + if ( !$this.find( _this.options.dragHandle + ',.' + handle + '-disabled' ).length ) { + hasAccept = _this.options.dragaccept ? $this.hasClass( _this.options.dragaccept.replace('.', '') ) : true; + $this + // sortClass includes a "." to match the tablesorter selectorSort option - for consistency + .wrapInner('<div class="' + _this.options.sortClass.replace('.', '') + '"/>') + // add handle class + "-disabled" to drag-disabled columns + .prepend('<div class="' + handle + ( hasAccept ? '' : '-disabled' ) + '"></div>'); + } + }); + }, + start : function( table ) { + table = $( table )[0]; + if ( table && table.config ) { + table.config.widgetOptions.dragtableLast = { + search : $( table ).data( 'lastSearch' ), + order : ts.dragtable.getOrder( table ) + }; + } + }, + update : function( _this ) { + var t, list, val, + dragTable = _this.originalTable, + table = dragTable.el[ 0 ], + $table = $( table ), + c = table.config, + wo = c && c.widgetOptions, + startIndex = dragTable.startIndex - 1, + endIndex = dragTable.endIndex - 1, + columnOrder = ts.dragtable.getOrder( table ) || [], + hasFilters = ts.hasWidget( $table, 'filter' ) || false, + last = wo && wo.dragtableLast || {}, + // update moved filters + filters = []; + + // only trigger updateAll if column order changed + if ( ( last.order || [] ).join( '' ) !== columnOrder.join( '' ) ) { + + if ( c.sortList.length ) { + // must deep extend (nested arrays) to prevent list from changing with c.sortList + list = $.extend( true, [], c.sortList ); + $.each( columnOrder, function( indx, value ) { + val = ts.isValueInArray( parseInt( value, 10 ), list ); + if ( value !== last.order[ indx ] && val >= 0 ) { + c.sortList[ val ][ 0 ] = indx; + } + }); + } + + // update filter widget + if ( hasFilters ) { + $.each( last.search || [], function( indx ) { + filters[ indx ] = last.search[ columnOrder[ indx ] ]; + }); + } + + // update preset editable widget columns + t = ( ts.hasWidget( c.$table, 'editable' ) || false ) ? wo.editable_columnsArray : false; + if ( t ) { + c.widgetOptions.editable_columnsArray = ts.dragtable.reindexArrayItem( t, startIndex, endIndex ); + } + // update ignore math columns + t = ( ts.hasWidget( c.$table, 'math' ) || false ) ? wo.math_ignore : false; + if ( t ) { + c.widgetOptions.math_ignore = ts.dragtable.reindexArrayItem( t, startIndex, endIndex ); + } + // update preset resizable widget widths + t = ( ts.hasWidget( c.$table, 'resizable' ) || false ) ? wo.resizable_widths : false; + if ( t ) { + // use zero-based indexes in the array + wo.resizable_widths = ts.dragtable.moveArrayItem( t, startIndex, endIndex ); + } + /* + // chart widget WIP - there are other options that need to be rearranged! + t = ( ts.hasWidget( c.$table, 'chart' ) || false ) ? wo.chart_ignoreColumns : false; + if ( t ) { + // use zero-based indexes in the array + wo.chart_ignoreColumns = ts.dragtable.moveArrayItem( t, startIndex, endIndex ); + } + */ + + $table.trigger('updateAll', [ false, function() { + if ( hasFilters ) { + setTimeout( function() { + // just update the filter values + c.lastCombinedFilter = null; + c.$table.data('lastSearch', filters); + ts.setFilters( $table, filters ); + if ($.isFunction(_this.options.tablesorterComplete)) { + _this.options.tablesorterComplete( c.table ); + } + }, 10 ); + } + } ]); + } + }, + getOrder : function( table ) { + return $( table ).children( 'thead' ).children( '.' + ts.css.headerRow ).children().map( function() { + return $( this ).attr( 'data-column' ); + }).get() || []; + }, + // bubble the moved col left or right + startColumnMove : function( dragTable ) { + var $cols, + c = dragTable.el[ 0 ].config, + startIndex = dragTable.startIndex - 1, + endIndex = dragTable.endIndex - 1, + cols = c.columns - 1, + pos = endIndex === cols ? false : endIndex <= startIndex, + $rows = c.$table.children().children( 'tr' ); + if ( c.debug ) { + ts.log( 'Inserting column ' + startIndex + ( pos ? ' before' : ' after' ) + ' column ' + endIndex ); + } + $rows.each( function() { + $cols = $( this ).children(); + $cols.eq( startIndex )[ pos ? 'insertBefore' : 'insertAfter' ]( $cols.eq( endIndex ) ); + }); + // rearrange col in colgroup + $cols = c.$table.children( 'colgroup' ).children(); + $cols.eq( startIndex )[ pos ? 'insertBefore' : 'insertAfter' ]( $cols.eq( endIndex ) ); + }, + swapNodes : function( a, b ) { + var indx, aparent, asibling, + len = a.length; + for ( indx = 0; indx < len; indx++ ) { + aparent = a[ indx ].parentNode; + asibling = a[ indx ].nextSibling === b[ indx ] ? a[ indx ] : a[ indx ].nextSibling; + b[ indx ].parentNode.insertBefore( a[ indx ], b[ indx ] ); + aparent.insertBefore( b[ indx ], asibling ); + } + }, + // http://stackoverflow.com/a/5306832/145346 + moveArrayItem : function( array, oldIndex, newIndex ) { + var indx, len = array.length; + if ( newIndex >= len ) { + indx = newIndex - len; + while ( ( indx-- ) + 1 ) { + array.push( undef ); + } + } + array.splice( newIndex, 0, array.splice( oldIndex, 1 )[ 0 ] ); + return array; + }, + reindexArrayItem : function( array, oldIndex, newIndex ) { + var nIndx = $.inArray( newIndex, array ), + oIndx = $.inArray( oldIndex, array ), + max = Math.max.apply( Math, array ), + arry = []; + // columns in the array were swapped so return original array + if ( nIndx >= 0 && oIndx >= 0 ) { + return array; + } + // columns not in the array were moved + $.each( array, function( indx, value ) { + // column (not in array) inserted between indexes + if ( newIndex < oldIndex ) { + // ( [ 0,1,2,3 ], 5, 1 ) -> column inserted between 0 & 1 => [ 0,2,3,4 ] + if ( value >= newIndex ) { + // 5 -> 1 [ 0, 2, 3 ] then 1 -> 0 [ 1, 2, 3 ] + arry.push( value + ( value < oldIndex ? 1 : 0 ) ); + } else { + arry.push( value ); + } + } else if ( newIndex > oldIndex ) { + // ( [ 0,1,2,3 ], 1, 5 ) -> column in array moved outside => [ 0,1,2,5 ] + if ( value === oldIndex ) { + arry.push( newIndex ); + } else if ( value < newIndex && value >= oldIndex ) { + arry.push( value - 1 ); + } else if ( value <= newIndex ) { + arry.push( value ); + } else if ( value > oldIndex ) { + arry.push( value + ( value < newIndex ? 0 : 1 ) ); + } + } + }); + return arry.sort(); + } + }; + +/*! + * dragtable + * _____ _ + * | |___ _| | + * | | | | . | . | + * |_|_|_|___|___| + * + * @Version 2.0.14 MOD + * + * Copyright (c) 2010-2013, Andres akottr@gmail.com + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * Inspired by the the dragtable from Dan Vanderkam (danvk.org/dragtable/) + * Thanks to the jquery and jqueryui comitters + * + * Any comment, bug report, feature-request is welcome + * Feel free to contact me. + */ + +/* TOKNOW: + * For IE7 you need this css rule: + * table { + * border-collapse: collapse; + * } + * Or take a clean reset.css (see http://meyerweb.com/eric/tools/css/reset/) + */ + +/* TODO: investigate + * Does not work properly with css rule: + * html { + * overflow: -moz-scrollbars-vertical; + * } + * Workaround: + * Fixing Firefox issues by scrolling down the page + * http://stackoverflow.com/questions/2451528/jquery-ui-sortable-scroll-helper-element-offset-firefox-issue + * + * var start = $.noop; + * var beforeStop = $.noop; + * if($.browser.mozilla) { + * var start = function (event, ui) { + * if( ui.helper !== undefined ) + * ui.helper.css('position','absolute').css('margin-top', $(window).scrollTop() ); + * } + * var beforeStop = function (event, ui) { + * if( ui.offset !== undefined ) + * ui.helper.css('margin-top', 0); + * } + * } + * + * and pass this as start and stop function to the sortable initialisation + * start: start, + * beforeStop: beforeStop + */ +/* + * Special thx to all pull requests comitters + */ + + $.widget("akottr.dragtable", { + options: { + revert: false, // smooth revert + dragHandle: '.table-handle', // handle for moving cols, if not exists the whole 'th' is the handle + maxMovingRows: 40, // 1 -> only header. 40 row should be enough, the rest is usually not in the viewport + excludeFooter: false, // excludes the footer row(s) while moving other columns. Make sense if there is a footer with a colspan. */ + onlyHeaderThreshold: 100, // TODO: not implemented yet, switch automatically between entire col moving / only header moving + dragaccept: null, // draggable cols -> default all + persistState: null, // url or function -> plug in your custom persistState function right here. function call is persistState(originalTable) + restoreState: null, // JSON-Object or function: some kind of experimental aka Quick-Hack TODO: do it better + exact: true, // removes pixels, so that the overlay table width fits exactly the original table width + clickDelay: 10, // ms to wait before rendering sortable list and delegating click event + containment: null, // @see http://api.jqueryui.com/sortable/#option-containment, use it if you want to move in 2 dimesnions (together with axis: null) + cursor: 'move', // @see http://api.jqueryui.com/sortable/#option-cursor + cursorAt: false, // @see http://api.jqueryui.com/sortable/#option-cursorAt + distance: 0, // @see http://api.jqueryui.com/sortable/#option-distance, for immediate feedback use "0" + tolerance: 'pointer', // @see http://api.jqueryui.com/sortable/#option-tolerance + axis: 'x', // @see http://api.jqueryui.com/sortable/#option-axis, Only vertical moving is allowed. Use 'x' or null. Use this in conjunction with the 'containment' setting + beforeStart: $.noop, // returning FALSE will stop the execution chain. + beforeMoving: $.noop, + beforeReorganize: $.noop, + beforeStop: $.noop, + // new options + tablesorterComplete: null, + sortClass : '.sorter' + }, + originalTable: { + el: null, + selectedHandle: null, + sortOrder: null, + startIndex: 0, + endIndex: 0 + }, + sortableTable: { + el: $(), + selectedHandle: $(), + movingRow: $() + }, + persistState: function() { + var _this = this; + this.originalTable.el.find('th').each(function(i) { + if (this.id !== '') { + _this.originalTable.sortOrder[this.id] = i; + } + }); + $.ajax({ + url: this.options.persistState, + data: this.originalTable.sortOrder + }); + }, + /* + * persistObj looks like + * {'id1':'2','id3':'3','id2':'1'} + * table looks like + * | id2 | id1 | id3 | + */ + _restoreState: function(persistObj) { + for (var n in persistObj) { + if (n in persistObj) { + this.originalTable.startIndex = $('#' + n).closest('th').prevAll().length + 1; + this.originalTable.endIndex = parseInt(persistObj[n], 10) + 1; + this._bubbleCols(); + } + } + }, + // bubble the moved col left or right + _bubbleCols: function() { + ts.dragtable.startColumnMove(this.originalTable); + }, + _rearrangeTableBackroundProcessing: function() { + var _this = this; + return function() { + _this._bubbleCols(); + _this.options.beforeStop(_this.originalTable); + _this.sortableTable.el.remove(); + restoreTextSelection(); + ts.dragtable.update(_this); + // persist state if necessary + if ($.isFunction(_this.options.persistState)) { + _this.options.persistState(_this.originalTable); + } else { + _this.persistState(); + } + + }; + }, + _rearrangeTable: function() { + var _this = this; + return function() { + // remove handler-class -> handler is now finished + _this.originalTable.selectedHandle.removeClass('dragtable-handle-selected'); + // add disabled class -> reorgorganisation starts soon + _this.sortableTable.el.sortable("disable"); + _this.sortableTable.el.addClass('dragtable-disabled'); + _this.options.beforeReorganize(_this.originalTable, _this.sortableTable); + // do reorganisation asynchronous + // for chrome a little bit more than 1 ms because we want to force a rerender + _this.originalTable.endIndex = _this.sortableTable.movingRow.prevAll().length + 1; + setTimeout(_this._rearrangeTableBackroundProcessing(), 50); + }; + }, + /* + * Disrupts the table. The original table stays the same. + * But on a layer above the original table we are constructing a list (ul > li) + * each li with a separate table representig a single col of the original table. + */ + _generateSortable: function(e) { + if (e.cancelBubble) { + e.cancelBubble = true; + } else { + e.stopPropagation(); + } + var _this = this; + // table attributes + var attrs = this.originalTable.el[0].attributes; + var tableAttrsString = ''; + for (var i = 0; i < attrs.length; i++) { + if ( (attrs[i].value || attrs[i].nodeValue) && attrs[i].nodeName != 'id' && attrs[i].nodeName != 'width') { + tableAttrsString += attrs[i].nodeName + '="' + ( attrs[i].value || attrs[i].nodeValue ) + '" '; + } + } + // row attributes + var rowAttrsArr = []; + //compute height, special handling for ie needed :-( + var heightArr = []; + + // don't save tfoot attributes because it messes up indexing + _this.originalTable.el.children('thead, tbody').children('tr:visible').slice(0, _this.options.maxMovingRow).each(function() { + // row attributes + var attrs = this.attributes; + var attrsString = ''; + for (var j = 0; j < attrs.length; j++) { + if ( (attrs[j].value || attrs[j].nodeValue ) && attrs[j].nodeName != 'id') { + attrsString += ' ' + attrs[j].nodeName + '="' + ( attrs[j].value || attrs[j].nodeValue ) + '"'; + } + } + rowAttrsArr.push(attrsString); + heightArr.push($(this).height()); + }); + + // compute width, no special handling for ie needed :-) + var widthArr = []; + // compute total width, needed for not wrapping around after the screen ends (floating) + var totalWidth = 0; + /* Find children thead and tbody. + * Only to process the immediate tr-children. Bugfix for inner tables + */ + var thtb = _this.originalTable.el.children(); + var headerRows = thtb.filter('thead').children('tr:visible'); + var visibleRows = thtb.filter('tbody').children('tr:visible'); + + headerRows.eq(0).children('th, td').filter(':visible').each(function() { + var w = $(this).outerWidth(); + widthArr.push(w); + totalWidth += w; + }); + if(_this.options.exact) { + var difference = totalWidth - _this.originalTable.el.outerWidth(); + widthArr[0] -= difference; + } + // one extra px on right and left side + totalWidth += 2; + + var captionHeight = 0; + thtb.filter('caption').each(function(){ + captionHeight += $(this).outerHeight(); + }); + + var sortableHtml = '<ul class="dragtable-sortable" style="position:absolute; width:' + totalWidth + 'px;">'; + var sortableColumn = []; + // assemble the needed html + // build list + var rowIndex, + columns = headerRows.eq(0).children('th, td').length; + /*jshint loopfunc:true */ + for (i = 0; i < columns; i++) { + var row = headerRows.children(':nth-child(' + (i + 1) + ')'); + if (row.is(':visible')) { + rowIndex = 0; + sortableColumn[i] = '<li style="width:' + row.outerWidth() + 'px;">' + + '<table ' + tableAttrsString + '>' + + ( captionHeight ? '<caption style="height:' + captionHeight + 'px;"></caption>' : '' ) + + '<thead>'; + // thead + headerRows.each(function(j){ + sortableColumn[i] += '<tr ' + rowAttrsArr[rowIndex++] + + ( heightArr[j] ? ' style="height:' + heightArr[j] + 'px;"' : '' ) + '>' + + row[j].outerHTML + '</tr>'; + }); + sortableColumn[i] += '</thead><tbody>'; + // tbody + row = visibleRows.children(':nth-child(' + (i + 1) + ')'); + if (_this.options.maxMovingRows > 1) { + row = row.add(visibleRows.children(':nth-child(' + (i + 1) + ')').slice(0, _this.options.maxMovingRows - 1)); + } + row.each(function(j) { + sortableColumn[i] += '<tr ' + rowAttrsArr[rowIndex++] + + ( heightArr[j] ? ' style="height:' + heightArr[j] + 'px;"' : '' ) + '>' + + this.outerHTML + '</tr>'; + }); + sortableColumn[i] += '</tbody>'; + + // add footer to end of max Rows + if (!_this.options.excludeFooter) { + sortableColumn[i] += '<tfoot><tr ' + rowAttrsArr[rowIndex++] + '>' + + thtb.filter('tfoot').children('tr:visible').children()[i].outerHTML + '</tr></tfoot>'; + } + sortableColumn[i] += '</table></li>'; + } + } + sortableHtml += sortableColumn.join('') + '</ul>'; + this.sortableTable.el = this.originalTable.el.before(sortableHtml).prev(); + // set width if necessary + this.sortableTable.el.find('> li > table').each(function(i) { + $(this).css('width', widthArr[i] + 'px'); + }); + + // assign this.sortableTable.selectedHandle + this.sortableTable.selectedHandle = this.sortableTable.el.find('th .dragtable-handle-selected'); + + var items = !this.options.dragaccept ? 'li' : 'li:has(' + this.options.dragaccept + ')'; + this.sortableTable.el.sortable({ + items: items, + stop: this._rearrangeTable(), + // pass thru options for sortable widget + revert: this.options.revert, + tolerance: this.options.tolerance, + containment: this.options.containment, + cursor: this.options.cursor, + cursorAt: this.options.cursorAt, + distance: this.options.distance, + axis: this.options.axis + }); + + // assign start index + this.originalTable.startIndex = $(e.target).closest('th,td').prevAll().length + 1; + this.options.beforeMoving(this.originalTable, this.sortableTable); + // Start moving by delegating the original event to the new sortable table + this.sortableTable.movingRow = this.sortableTable.el.children('li:nth-child(' + this.originalTable.startIndex + ')'); + + // prevent the user from drag selecting "highlighting" surrounding page elements + disableTextSelection(); + // clone the initial event and trigger the sort with it + this.sortableTable.movingRow.trigger($.extend($.Event(e.type), { + which: 1, + clientX: e.clientX, + clientY: e.clientY, + pageX: e.pageX, + pageY: e.pageY, + screenX: e.screenX, + screenY: e.screenY + })); + + // Some inner divs to deliver the posibillity to style the placeholder more sophisticated + var placeholder = this.sortableTable.el.find('.ui-sortable-placeholder'); + if(placeholder.height() > 0) { + placeholder.css('height', this.sortableTable.el.find('.ui-sortable-helper').height()); + } + + placeholder.html('<div class="outer" style="height:100%;"><div class="inner" style="height:100%;"></div></div>'); + }, + bindTo: {}, + _create: function() { + var _this = this; + _this.originalTable = { + el: _this.element, + selectedHandle: $(), + sortOrder: {}, + startIndex: 0, + endIndex: 0 + }; + ts.dragtable.create( _this ); + // filter only the cols that are accepted + _this.bindTo = '> thead > tr > ' + ( _this.options.dragaccept || 'th, td' ); + // bind draggable to handle if exists + if (_this.element.find(_this.bindTo).find(_this.options.dragHandle).length) { + _this.bindTo += ' ' + _this.options.dragHandle; + } + // restore state if necessary + if ($.isFunction(_this.options.restoreState)) { + _this.options.restoreState(_this.originalTable); + } else { + _this._restoreState(_this.options.restoreState); + } + _this.originalTable.el.on( 'mousedown.dragtable', _this.bindTo, function(evt) { + // listen only to left mouse click + if (evt.which!==1) return; + ts.dragtable.start( _this.originalTable.el ); + if (_this.options.beforeStart(_this.originalTable) === false) { + return; + } + clearTimeout(_this.downTimer); + _this.downTimer = setTimeout(function() { + _this.originalTable.selectedHandle = $(_this); + _this.originalTable.selectedHandle.addClass('dragtable-handle-selected'); + _this._generateSortable(evt); + }, _this.options.clickDelay); + }).on( 'mouseup.dragtable', _this.options.dragHandle,function() { + clearTimeout(_this.downTimer); + }); + }, + redraw: function(){ + this.destroy(); + this._create(); + }, + destroy: function() { + this.originalTable.el.off('mousedown.dragtable mouseup.dragtable', this.bindTo); + $.Widget.prototype.destroy.apply(this, arguments); // default destroy + // now do other stuff particular to this widget + } + }); + + /** closure-scoped "private" functions **/ + var body_onselectstart_save = $(document.body).attr('onselectstart'), + body_unselectable_save = $(document.body).attr('unselectable'); + + // css properties to disable user-select on the body tag by appending a <style> tag to the <head> + // remove any current document selections + function disableTextSelection() { + // jQuery doesn't support the element.text attribute in MSIE 8 + // http://stackoverflow.com/questions/2692770/style-style-textcss-appendtohead-does-not-work-in-ie + var $style = $('<style id="__dragtable_disable_text_selection__" type="text/css">body { -ms-user-select:none;-moz-user-select:-moz-none;-khtml-user-select:none;-webkit-user-select:none;user-select:none; }</style>'); + $(document.head).append($style); + $(document.body).attr('onselectstart', 'return false;').attr('unselectable', 'on'); + if (window.getSelection) { + window.getSelection().removeAllRanges(); + } else { + document.selection.empty(); // MSIE http://msdn.microsoft.com/en-us/library/ms535869%28v=VS.85%29.aspx + } + } + + // remove the <style> tag, and restore the original <body> onselectstart attribute + function restoreTextSelection() { + $('#__dragtable_disable_text_selection__').remove(); + if (body_onselectstart_save) { + $(document.body).attr('onselectstart', body_onselectstart_save); + } else { + $(document.body).removeAttr('onselectstart'); + } + if (body_unselectable_save) { + $(document.body).attr('unselectable', body_unselectable_save); + } else { + $(document.body).removeAttr('unselectable'); + } + } + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 22bd435..ef584c3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter (FORK) 2.18.4 - Client-side table sorting with ease! +* TableSorter (FORK) 2.19.0 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -15,8 +15,16 @@ * @contributor Rob Garrison/https://github.com/Mottie/tablesorter */ /*jshint browser:true, jquery:true, unused:false, expr: true */ -/*global console:false, alert:false */ -!(function($) { +/*global console:false, alert:false, require:false, define:false, module:false */ +(function(factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + module.exports = factory(require('jquery')); + } else { + factory(jQuery); + } +}(function($) { "use strict"; $.extend({ /*jshint supernew:true */ @@ -24,7 +32,7 @@ var ts = this; - ts.version = "2.18.4"; + ts.version = "2.19.0"; ts.parsers = []; ts.widgets = []; @@ -48,6 +56,7 @@ usNumberFormat : true, // false for German "1.234.567,89" or French "1 234 567,89" delayInit : false, // if false, the parsed table contents will not update until the first sort serverSideSorting: false, // if true, server-side sorting should be performed because client-side sorting will be disabled, but the ui and events will still be used. + resort : true, // default setting to trigger a resort after an "update", "addRows", "updateCell", etc has completed // *** sort options headers : {}, // set sorter, string, empty, locked order, sortInitialOrder, filter, etc. @@ -62,10 +71,10 @@ sortReset : false, // third click on the header will reset column to default - unsorted sortRestart : false, // restart sort to "sortInitialOrder" when clicking on previously unsorted columns - emptyTo : 'bottom', // sort empty cell to bottom, top, none, zero + emptyTo : 'bottom', // sort empty cell to bottom, top, none, zero, emptyMax, emptyMin stringTo : 'max', // sort strings in numerical column as max, min, top, bottom, zero textExtraction : 'basic', // text extraction method/function - function(node, table, cellIndex){} - textAttribute : 'data-text',// data-attribute that contains alternate cell text (used in textExtraction function) + textAttribute : 'data-text',// data-attribute that contains alternate cell text (used in default textExtraction function) textSorter : null, // choose overall or specific column sorter function(a, b, direction, table, columnIndex) [alt: ts.sortText] numberSorter : null, // choose overall numeric sorter function(a, b, direction, maxColumnValue) @@ -90,7 +99,7 @@ cssProcessing : '', // processing icon applied to header during sort/filter cssChildRow : 'tablesorter-childRow', // class name indiciating that a row is to be attached to the its parent - cssIcon : 'tablesorter-icon', // if this class exists, a <i> will be added to the header automatically + cssIcon : 'tablesorter-icon', // if this class does not exist, the {icon} will not be added from the headerTemplate cssIconNone : '', // class name added to the icon when there is no column sort cssIconAsc : '', // class name added to the icon when the column has an ascending sort cssIconDesc : '', // class name added to the icon when the column has a descending sort @@ -112,8 +121,7 @@ strings: {}, parsers: [] - // deprecated; but retained for backwards compatibility - // widgetZebra: { css: ["even", "odd"] } + // removed: widgetZebra: { css: ["even", "odd"] } }; @@ -123,6 +131,7 @@ table : 'tablesorter', cssHasChild: 'tablesorter-hasChildRow', childRow : 'tablesorter-childRow', + colgroup : 'tablesorter-colgroup', header : 'tablesorter-header', headerRow : 'tablesorter-headerRow', headerIn : 'tablesorter-header-inner', @@ -172,24 +181,23 @@ } function getElementText(table, node, cellIndex) { - if (!node) { return ""; } - var te, c = table.config, - t = c.textExtraction || '', - text = ""; - if (t === "basic") { - // check data-attribute first - text = $(node).attr(c.textAttribute) || node.textContent || node.innerText || $(node).text() || ""; + if (!node) { return ''; } + var te, + $node = $(node), + c = table.config, + t = c.textExtraction || ''; + if (typeof(t) === 'string') { + // check data-attribute first when set to "basic"; don't use node.innerText - it's really slow! + return $.trim( (t === 'basic' ? $node.attr(c.textAttribute) || node.textContent : node.textContent ) || $node.text() || '' ); } else { - if (typeof(t) === "function") { - text = t(node, table, cellIndex); + if (typeof(t) === 'function') { + return $.trim( t(node, table, cellIndex) ); } else if (typeof (te = ts.getColumnData( table, t, cellIndex )) === 'function') { - text = te(node, table, cellIndex); - } else { - // previous "simple" method - text = node.textContent || node.innerText || $(node).text() || ""; + return $.trim( te(node, table, cellIndex) ); } } - return $.trim(text); + // fallback + return $.trim( node.textContent || $node.text() || '' ); } function detectParserForColumn(table, rows, rowIndex, cellIndex) { @@ -242,7 +250,7 @@ }; while (j < len) { rows = tb[j].rows; - if (rows[j]) { + if (rows.length) { l = c.columns; // rows[j].cells.length; for (i = 0; i < l; i++) { h = c.$headers.filter('[data-column="' + i + '"]:last'); @@ -457,9 +465,9 @@ // save original header content c.headerContent[index] = $t.html(); // if headerTemplate is empty, don't reformat the header cell - if ( c.headerTemplate !== '' ) { + if ( c.headerTemplate !== '' && !$t.find('.' + ts.css.headerIn).length ) { // set up header template - t = c.headerTemplate.replace(/\{content\}/g, $t.html()).replace(/\{icon\}/g, i); + t = c.headerTemplate.replace(/\{content\}/g, $t.html()).replace(/\{icon\}/g, $t.find('.' + ts.css.icon).length ? '' : i); if (c.onRenderTemplate) { h = c.onRenderTemplate.apply($t, [index, t]); if (h && typeof h === 'string') { t = h; } // only change t if something is returned @@ -505,7 +513,7 @@ buildParserCache(table); // rebuild the cache map buildCache(table); - checkResort(c.$table, resort, callback); + checkResort(c, resort, callback); } function updateHeader(table) { @@ -575,28 +583,13 @@ c.$headers.not('.sorter-false').each(function(){ var $this = $(this), nextSort = this.order[(this.count + 1) % (c.sortReset ? 3 : 2)], - txt = $this.text() + ': ' + + txt = $.trim( $this.text() ) + ': ' + ts.language[ $this.hasClass(ts.css.sortAsc) ? 'sortAsc' : $this.hasClass(ts.css.sortDesc) ? 'sortDesc' : 'sortNone' ] + ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ]; $this.attr('aria-label', txt ); }); } - // automatically add col group, and column sizes if set - function fixColumnWidth(table) { - var colgroup, overallWidth, - c = table.config; - if (c.widthFixed && c.$table.children('colgroup').length === 0) { - colgroup = $('<colgroup>'); - overallWidth = $(table).width(); - // only add col for visible columns - fixes #371 - $(table.tBodies).not('.' + c.cssInfoBlock).find("tr:first").children(":visible").each(function() { - colgroup.append($('<col>').css('width', parseInt(($(this).width()/overallWidth)*1000, 10)/10 + '%')); - }); - c.$table.prepend(colgroup); - } - } - function updateHeaderSortCount(table, list) { var s, t, o, col, primary, c = table.config, @@ -821,27 +814,35 @@ if (c.debug) { benchmark("Sorting on " + sortList.toString() + " and dir " + order + " time", sortTime); } } - function resortComplete($table, callback){ - var table = $table[0]; - if (table.isUpdating) { - $table.trigger('updateComplete', table); + function resortComplete(c, callback){ + if (c.table.isUpdating) { + c.$table.trigger('updateComplete', c.table); } if ($.isFunction(callback)) { - callback($table[0]); + callback(c.table); } } - function checkResort($table, flag, callback) { - var sl = $table[0].config.sortList; + function checkResort(c, resort, callback) { + var sl = $.isArray(resort) ? resort : c.sortList, + // if no resort parameter is passed, fallback to config.resort (true by default) + resrt = typeof resort === 'undefined' ? c.resort : resort; // don't try to resort if the table is still processing // this will catch spamming of the updateCell method - if (flag !== false && !$table[0].isProcessing && sl.length) { - $table.trigger("sorton", [sl, function(){ - resortComplete($table, callback); - }, true]); + if (resrt !== false && !c.serverSideSorting && !c.table.isProcessing) { + if (sl.length) { + c.$table.trigger('sorton', [sl, function(){ + resortComplete(c, callback); + }, true]); + } else { + c.$table.trigger('sortReset', [function(){ + resortComplete(c, callback); + ts.applyWidget(c.table, false); + }]); + } } else { - resortComplete($table, callback); - ts.applyWidget($table[0], false); + resortComplete(c, callback); + ts.applyWidget(c.table, false); } } @@ -865,7 +866,6 @@ e.stopPropagation(); table.isUpdating = true; ts.refreshWidgets(table, true, true); - ts.restoreHeaders(table); buildHeaders(table); ts.bindEvents(table, c.$headers, true); bindMethods(table); @@ -908,7 +908,18 @@ // update column max value (ignore sign) c.cache[tbdy].colMax[icell] = Math.max(Math.abs(v) || 0, c.cache[tbdy].colMax[icell] || 0); } - checkResort($table, resort, callback); + v = resort !== 'undefined' ? resort : c.resort; + if (v !== false) { + // widgets will be reapplied + checkResort(c, v, callback); + } else { + // don't reapply widgets is resort is false, just in case it causes + // problems with element focus + if ($.isFunction(callback)) { + callback(table); + } + c.$table.trigger('updateComplete', c.table); + } } }) .bind("addRows" + c.namespace, function(e, $row, resort, callback) { @@ -957,7 +968,7 @@ c.cache[tbdy].normalized.push(cells); } // resort using current settings - checkResort($table, resort, callback); + checkResort(c, resort, callback); } }) .bind("updateComplete" + c.namespace, function(){ @@ -1020,7 +1031,7 @@ }) .bind("resetToLoadState" + c.namespace, function(){ // remove all widgets - ts.refreshWidgets(table, true, true); + ts.removeWidget(table, true, false); // restore original settings; this clears out current settings, but does not clear // values saved to storage. c = $.extend(true, ts.defaults, c.originalSettings); @@ -1112,7 +1123,7 @@ buildHeaders(table); // fixate columns if the users supplies the fixedWidth option // do this after theme has been applied - fixColumnWidth(table); + ts.fixColumnWidth(table); // try to auto detect column type, and store in tables config buildParserCache(table); // start total row count at zero @@ -1168,6 +1179,28 @@ if (typeof c.initialized === 'function') { c.initialized(table); } }; + // automatically add a colgroup with col elements set to a percentage width + ts.fixColumnWidth = function(table) { + table = $(table)[0]; + var overallWidth, percent, + c = table.config, + colgroup = c.$table.children('colgroup'); + // remove plugin-added colgroup, in case we need to refresh the widths + if (colgroup.length && colgroup.hasClass(ts.css.colgroup)) { + colgroup.remove(); + } + if (c.widthFixed && c.$table.children('colgroup').length === 0) { + colgroup = $('<colgroup class="' + ts.css.colgroup + '">'); + overallWidth = c.$table.width(); + // only add col for visible columns - fixes #371 + $(table.tBodies).not('.' + c.cssInfoBlock).find('tr:first').children(':visible').each(function() { + percent = parseInt( ( $(this).width() / overallWidth ) * 1000, 10 ) / 10 + '%'; + colgroup.append( $('<col>').css('width', percent) ); + }); + c.$table.prepend(colgroup); + } + }; + ts.getColumnData = function(table, obj, indx, getCell, $headers){ if (typeof obj === 'undefined' || obj === null) { return; } table = $(table)[0]; @@ -1331,13 +1364,15 @@ // restore headers ts.restoreHeaders = function(table){ - var c = $(table)[0].config; + var $cell, + c = $(table)[0].config; // don't use c.$headers here in case header cells were swapped c.$table.find(c.selectorHeaders).each(function(i){ + $cell = $(this); // only restore header cells if it is wrapped // because this is also used by the updateAll method - if ($(this).find('.' + ts.css.headerIn).length){ - $(this).html( c.headerContent[i] ); + if ($cell.find('.' + ts.css.headerIn).length){ + $cell.html( c.headerContent[i] ); } }); }; @@ -1346,7 +1381,7 @@ table = $(table)[0]; if (!table.hasInitialized) { return; } // remove all widgets - ts.refreshWidgets(table, true, true); + ts.removeWidget(table, true, false); var $t = $(table), c = table.config, $h = $t.find('thead:first'), $r = $h.find('tr.' + ts.css.headerRow).removeClass(ts.css.headerRow + ' ' + c.cssHeaderRow), @@ -1577,7 +1612,7 @@ } }; - ts.applyWidget = function(table, init) { + ts.applyWidget = function(table, init, callback) { table = $(table)[0]; // in case this is called externally var c = table.config, wo = c.widgetOptions, @@ -1640,6 +1675,10 @@ } } }); + // callback executed on init only + if (!init && typeof callback === 'function') { + callback(table); + } } setTimeout(function(){ table.isApplyingWidgets = false; @@ -1651,24 +1690,63 @@ } }; + ts.removeWidget = function(table, name, refreshing){ + table = $(table)[0]; + // if name === true, add all widgets from $.tablesorter.widgets + if (name === true) { + name = []; + $.each( ts.widgets, function(i, w){ + if (w && w.id) { + name.push( w.id ); + } + }); + } else { + // name can be either an array of widgets names, + // or a space/comma separated list of widget names + name = ( $.isArray(name) ? name.join(',') : name || '' ).toLowerCase().split( /[\s,]+/ ); + } + var i, widget, indx, + c = table.config, + len = name.length; + for (i = 0; i < len; i++) { + widget = ts.getWidgetById(name[i]); + indx = $.inArray( name[i], c.widgets ); + if ( widget && 'remove' in widget ) { + if (c.debug && indx >= 0) { log( 'Removing "' + name[i] + '" widget' ); } + widget.remove(table, c, c.widgetOptions, refreshing); + c.widgetInit[name[i]] = false; + } + // don't remove the widget from config.widget if refreshing + if (indx >= 0 && refreshing !== true) { + c.widgets.splice( indx, 1 ); + } + } + }; + ts.refreshWidgets = function(table, doAll, dontapply) { table = $(table)[0]; // see issue #243 - var i, c = table.config, + var c = table.config, cw = c.widgets, - w = ts.widgets, l = w.length; - // remove previous widgets - for (i = 0; i < l; i++){ - if ( w[i] && w[i].id && (doAll || $.inArray( w[i].id, cw ) < 0) ) { - if (c.debug) { log( 'Refeshing widgets: Removing "' + w[i].id + '"' ); } - // only remove widgets that have been initialized - fixes #442 - if (w[i].hasOwnProperty('remove') && c.widgetInit[w[i].id]) { - w[i].remove(table, c, c.widgetOptions); - c.widgetInit[w[i].id] = false; - } + list = [], + callback = function(table){ + $(table).trigger('refreshComplete'); + }; + // remove widgets not defined in config.widgets, unless doAll is true + $.each( ts.widgets, function(i, w){ + if (w && w.id && (doAll || $.inArray( w.id, cw ) < 0)) { + list.push( w.id ); } - } + }); + ts.removeWidget( table, list.join(','), true ); if (dontapply !== true) { - ts.applyWidget(table, doAll); + // call widget init if + ts.applyWidget(table, doAll || false, callback ); + if (doAll) { + // apply widget format + ts.applyWidget(table, false, callback); + } + } else { + callback(table); } }; @@ -1928,7 +2006,8 @@ }); } }, - remove: function(table, c, wo){ + remove: function(table, c, wo, refreshing){ + if (refreshing) { return; } var k, $tb, b = c.$tbodies, rmv = (wo.zebra || [ "even", "odd" ]).join(' '); @@ -1940,4 +2019,5 @@ } }); -})(jQuery); + return ts; +})); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js index c065573..9b1ad25 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js @@ -1,4 +1,4 @@ -/*! Filter widget select2 formatter function - updated 7/17/2014 (v2.17.5) +/*! Filter widget select2 formatter function - updated 2/7/2015 (v2.19.0) * requires: jQuery 1.7.2+, tableSorter (FORK) 2.16+, filter widget 2.16+ and select2 v3.4.6+ plugin */ /*jshint browser:true, jquery:true, unused:false */ @@ -28,14 +28,14 @@ ts.filterFormatter.select2 = function($cell, indx, select2Def) { wo = c.widgetOptions, // Add a hidden input to hold the range values $input = $('<input class="filter" type="hidden">') - .appendTo($cell) - // hidden filter update namespace trigger by filter widget - .bind('change' + c.namespace + 'filter', function(){ - var val = this.value; - val = val.replace(/[/()$^]/g, '').split('|'); - $cell.find('.select2').select2('val', val); - updateSelect2(); - }), + .appendTo($cell) + // hidden filter update namespace trigger by filter widget + .bind('change' + c.namespace + 'filter', function(){ + var val = this.value; + val = val.replace(/[/()$^]/g, '').split('|'); + $cell.find('.select2').select2('val', val); + updateSelect2(); + }), $header = c.$headers.filter('[data-column="' + indx + '"]:last'), onlyAvail = $header.hasClass(wo.filter_onlyAvail), $shcell = [], @@ -44,16 +44,27 @@ ts.filterFormatter.select2 = function($cell, indx, select2Def) { // this function updates the hidden input and adds the current values to the header cell text updateSelect2 = function() { - var v = $cell.find('.select2').select2('val') || o.value || ''; + var arry = false, + v = $cell.find('.select2').select2('val') || o.value || ''; + // convert array to string + if ($.isArray(v)) { + arry = true; + v = v.join('\u0000'); + } + // escape special regex characters (http://stackoverflow.com/a/9310752/145346) + v = v.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); + // convert string back into an array + if (arry) { + v = v.split('\u0000'); + } $input - // add regex, so we filter exact numbers - .val( $.isArray(v) && v.length && v.join('') !== '' ? '/(' + matchPrefix + (v || []).join(matchSuffix + '|' + matchPrefix) + matchSuffix + ')/' : '' ) - .trigger('search').end() - .find('.select2').select2('val', v); + // add regex, so we filter exact numbers + .val( $.isArray(v) && v.length && v.join('') !== '' ? '/(' + matchPrefix + (v || []).join(matchSuffix + '|' + matchPrefix) + matchSuffix + ')/' : '' ) + .trigger('search').end() + .find('.select2').select2('val', v); // update sticky header cell if ($shcell.length) { - $shcell - .find('.select2').select2('val', v); + $shcell.find('.select2').select2('val', v); } }, @@ -88,18 +99,18 @@ ts.filterFormatter.select2 = function($cell, indx, select2Def) { // add a select2 hidden input! $('<input class="select2 select2-' + indx + '" type="hidden" />') - .val(o.value) - .appendTo($cell) - .select2(o) - .bind('change', function(){ - updateSelect2(); - }); + .val(o.value) + .appendTo($cell) + .select2(o) + .bind('change', function(){ + updateSelect2(); + }); // update select2 from filter hidden input, in case of saved filters c.$table.bind('filterFomatterUpdate', function(){ // value = '/(^x$|^y$)/' => 'x,y' var val = c.$table.data('lastSearch')[indx] || ''; - val = val.replace(/[/()$^]/g, '').split('|'); + val = val.replace(/^\/\(\^?/,'').replace(/\$\|\^/g, '|').replace(/\$?\)\/$/g,'').split('|'); $cell.find('.select2').select2('val', val); updateSelect2(); ts.filter.formatterUpdated($cell, indx); @@ -110,13 +121,13 @@ ts.filterFormatter.select2 = function($cell, indx, select2Def) { $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); // add a select2! $('<input class="select2 select2-' + indx + '" type="hidden">') - .val(o.value) - .appendTo($shcell) - .select2(o) - .bind('change', function(){ - $cell.find('.select2').select2('val', $shcell.find('.select2').select2('val') ); - updateSelect2(); - }); + .val(o.value) + .appendTo($shcell) + .select2(o) + .bind('change', function(){ + $cell.find('.select2').select2('val', $shcell.find('.select2').select2('val') ); + updateSelect2(); + }); if (o.cellText) { $shcell.prepend('<label>' + o.cellText + '</label>'); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 47b3939..1a24a97 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter (FORK) 2.16+ widgets - updated 12/22/2014 (v2.18.4) +/*! tableSorter (FORK) 2.16+ widgets - updated 2/7/2015 (v2.19.0) * * Column Styles * Column Filters @@ -11,41 +11,51 @@ /*jshint browser:true, jquery:true, unused:false, loopfunc:true */ /*global jQuery: false, localStorage: false */ ;(function ($, window) { -"use strict"; +'use strict'; var ts = $.tablesorter = $.tablesorter || {}; ts.themes = { - "bootstrap" : { - table : 'table table-bordered table-striped', - caption : 'caption', - header : 'bootstrap-header', // give the header a gradient background - footerRow : '', - footerCells: '', - icons : '', // add "icon-white" to make them white; this icon class is added to the <i> in the header - sortNone : 'bootstrap-icon-unsorted', - sortAsc : 'icon-chevron-up glyphicon glyphicon-chevron-up', - sortDesc : 'icon-chevron-down glyphicon glyphicon-chevron-down', - active : '', // applied when column is sorted - hover : '', // use custom css here - bootstrap class may not override it - filterRow : '', // filter row class - even : '', // even row zebra striping - odd : '' // odd row zebra striping + 'bootstrap' : { + table : 'table table-bordered table-striped', + caption : 'caption', + // header class names + header : 'bootstrap-header', // give the header a gradient background (theme.bootstrap_2.css) + sortNone : '', + sortAsc : '', + sortDesc : '', + active : '', // applied when column is sorted + hover : '', // custom css required - a defined bootstrap style may not override other classes + // icon class names + icons : '', // add "icon-white" to make them white; this icon class is added to the <i> in the header + iconSortNone : 'bootstrap-icon-unsorted', // class name added to icon when column is not sorted + iconSortAsc : 'icon-chevron-up glyphicon glyphicon-chevron-up', // class name added to icon when column has ascending sort + iconSortDesc : 'icon-chevron-down glyphicon glyphicon-chevron-down', // class name added to icon when column has descending sort + filterRow : '', // filter row class + footerRow : '', + footerCells : '', + even : '', // even row zebra striping + odd : '' // odd row zebra striping }, - "jui" : { - table : 'ui-widget ui-widget-content ui-corner-all', // table classes - caption : 'ui-widget-content', - header : 'ui-widget-header ui-corner-all ui-state-default', // header classes - footerRow : '', - footerCells: '', - icons : 'ui-icon', // icon class added to the <i> in the header - sortNone : 'ui-icon-carat-2-n-s', - sortAsc : 'ui-icon-carat-1-n', - sortDesc : 'ui-icon-carat-1-s', - active : 'ui-state-active', // applied when column is sorted - hover : 'ui-state-hover', // hover class - filterRow : '', - even : 'ui-widget-content', // even row zebra striping - odd : 'ui-state-default' // odd row zebra striping + 'jui' : { + table : 'ui-widget ui-widget-content ui-corner-all', // table classes + caption : 'ui-widget-content', + // header class names + header : 'ui-widget-header ui-corner-all ui-state-default', // header classes + sortNone : '', + sortAsc : '', + sortDesc : '', + active : 'ui-state-active', // applied when column is sorted + hover : 'ui-state-hover', // hover class + // icon class names + icons : 'ui-icon', // icon class added to the <i> in the header + iconSortNone : 'ui-icon-carat-2-n-s', // class name added to icon when column is not sorted + iconSortAsc : 'ui-icon-carat-1-n', // class name added to icon when column has ascending sort + iconSortDesc : 'ui-icon-carat-1-s', // class name added to icon when column has descending sort + filterRow : '', + footerRow : '', + footerCells : '', + even : 'ui-widget-content', // even row zebra striping + odd : 'ui-state-default' // odd row zebra striping } }; @@ -100,13 +110,13 @@ ts.storage = function(table, key, value, options) { // *** get value *** if ($.parseJSON) { if (hasLocalStorage) { - values = $.parseJSON(localStorage[key] || '{}'); + values = $.parseJSON(localStorage[key] || 'null') || {}; } else { // old browser, using cookies cookies = document.cookie.split(/[;\s|=]/); // add one to get from the key to the value cookieIndex = $.inArray(key, cookies) + 1; - values = (cookieIndex !== 0) ? $.parseJSON(cookies[cookieIndex] || '{}') : {}; + values = (cookieIndex !== 0) ? $.parseJSON(cookies[cookieIndex] || 'null') || {} : {}; } } // allow value to be an empty string too @@ -132,7 +142,7 @@ ts.storage = function(table, key, value, options) { // Add a resize event to table headers // ************************** ts.addHeaderResizeEvent = function(table, disable, settings) { - table = $(table)[0]; // make sure we're usig a dom element + table = $(table)[0]; // make sure we're using a dom element var headers, defaults = { timer : 250 @@ -177,33 +187,42 @@ ts.addWidget({ id: "uitheme", priority: 10, format: function(table, c, wo) { - var i, time, classes, $header, $icon, $tfoot, $h, oldtheme, oldremove, + var i, hdr, icon, time, $header, $icon, $tfoot, $h, oldtheme, oldremove, oldIconRmv, hasOldTheme, themesAll = ts.themes, - $table = c.$table, - $headers = c.$headers, + $table = c.$table.add( c.$extraTables ), + $headers = c.$headers.add( c.$extraHeaders ), theme = c.theme || 'jui', - themes = themesAll[theme] || themesAll.jui, - remove = [ themes.sortNone, themes.sortDesc, themes.sortAsc, themes.active ].join( ' ' ); + themes = themesAll[theme] || {}, + remove = $.trim( [ themes.sortNone, themes.sortDesc, themes.sortAsc, themes.active ].join( ' ' ) ), + iconRmv = $.trim( [ themes.iconSortNone, themes.iconSortDesc, themes.iconSortAsc ].join( ' ' ) ); if (c.debug) { time = new Date(); } // initialization code - run once - if (!$table.hasClass('tablesorter-' + theme) || c.theme !== c.appliedTheme || !table.hasInitialized) { - oldtheme = themes[c.appliedTheme] || {}; - oldremove = oldtheme ? [ oldtheme.sortNone, oldtheme.sortDesc, oldtheme.sortAsc, oldtheme.active ].join( ' ' ) : ''; - if (oldtheme) { - wo.zebra[0] = wo.zebra[0].replace(' ' + oldtheme.even, ''); - wo.zebra[1] = wo.zebra[1].replace(' ' + oldtheme.odd, ''); + if (!$table.hasClass('tablesorter-' + theme) || c.theme !== c.appliedTheme || !wo.uitheme_applied) { + wo.uitheme_applied = true; + oldtheme = themesAll[c.appliedTheme] || {}; + hasOldTheme = !$.isEmptyObject(oldtheme); + oldremove = hasOldTheme ? [ oldtheme.sortNone, oldtheme.sortDesc, oldtheme.sortAsc, oldtheme.active ].join( ' ' ) : '', + oldIconRmv = hasOldTheme ? [ oldtheme.iconSortNone, oldtheme.iconSortDesc, oldtheme.iconSortAsc ].join( ' ' ) : ''; + if (hasOldTheme) { + wo.zebra[0] = $.trim( ' ' + wo.zebra[0].replace(' ' + oldtheme.even, '') ); + wo.zebra[1] = $.trim( ' ' + wo.zebra[1].replace(' ' + oldtheme.odd, '') ); + c.$tbodies.children().removeClass( [oldtheme.even, oldtheme.odd].join(' ') ); } // update zebra stripes - if (themes.even !== '') { wo.zebra[0] += ' ' + themes.even; } - if (themes.odd !== '') { wo.zebra[1] += ' ' + themes.odd; } + if (themes.even) { wo.zebra[0] += ' ' + themes.even; } + if (themes.odd) { wo.zebra[1] += ' ' + themes.odd; } // add caption style - $table.children('caption').removeClass(oldtheme.caption).addClass(themes.caption); + $table.children('caption') + .removeClass(oldtheme.caption || '') + .addClass(themes.caption); // add table/footer class names $tfoot = $table // remove other selected themes - .removeClass( c.appliedTheme ? 'tablesorter-' + ( c.appliedTheme || '' ) : '' ) - .addClass('tablesorter-' + theme + ' ' + themes.table) // add theme widget class name + .removeClass( (c.appliedTheme ? 'tablesorter-' + (c.appliedTheme || '') : '') + ' ' + (oldtheme.table || '') ) + .addClass('tablesorter-' + theme + ' ' + (themes.table || '')) // add theme widget class name .children('tfoot'); + c.appliedTheme = c.theme; + if ($tfoot.length) { $tfoot // if oldtheme.footerRow or oldtheme.footerCells are undefined, all class names are removed @@ -212,43 +231,59 @@ ts.addWidget({ } // update header classes $headers - .add(c.$extraHeaders) - .removeClass(oldtheme.header + ' ' + oldtheme.hover + ' ' + oldremove) + .removeClass( (hasOldTheme ? [oldtheme.header, oldtheme.hover, oldremove].join(' ') : '') || '' ) .addClass(themes.header) .not('.sorter-false') + .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') .bind('mouseenter.tsuitheme mouseleave.tsuitheme', function(event) { // toggleClass with switch added in jQuery 1.3 - $(this)[ event.type === 'mouseenter' ? 'addClass' : 'removeClass' ](themes.hover); + $(this)[ event.type === 'mouseenter' ? 'addClass' : 'removeClass' ](themes.hover || ''); }); - if (!$headers.find('.' + ts.css.wrapper).length) { - // Firefox needs this inner div to position the resizer correctly - $headers.wrapInner('<div class="' + ts.css.wrapper + '" style="position:relative;height:100%;width:100%"></div>'); - } + + $headers.each(function(){ + var $this = $(this); + if (!$this.find('.' + ts.css.wrapper).length) { + // Firefox needs this inner div to position the icon & resizer correctly + $this.wrapInner('<div class="' + ts.css.wrapper + '" style="position:relative;height:100%;width:100%"></div>'); + } + }); if (c.cssIcon) { // if c.cssIcon is '', then no <i> is added to the header - $headers.find('.' + ts.css.icon).removeClass(oldtheme.icons + ' ' + oldremove).addClass(themes.icons); + $headers + .find('.' + ts.css.icon) + .removeClass(hasOldTheme ? [oldtheme.icons, oldIconRmv].join(' ') : '') + .addClass(themes.icons || ''); } if ($table.hasClass('hasFilters')) { - $table.children('thead').children('.' + ts.css.filterRow).removeClass(oldtheme.filterRow).addClass(themes.filterRow); + $table.children('thead').children('.' + ts.css.filterRow) + .removeClass(hasOldTheme ? oldtheme.filterRow || '' : '') + .addClass(themes.filterRow || ''); } - c.appliedTheme = c.theme; } for (i = 0; i < c.columns; i++) { $header = c.$headers.add(c.$extraHeaders).not('.sorter-false').filter('[data-column="' + i + '"]'); - $icon = (ts.css.icon) ? $header.find('.' + ts.css.icon) : $header; + $icon = (ts.css.icon) ? $header.find('.' + ts.css.icon) : $(); $h = $headers.not('.sorter-false').filter('[data-column="' + i + '"]:last'); if ($h.length) { + $header.removeClass(remove); + $icon.removeClass(iconRmv); if ($h[0].sortDisabled) { // no sort arrows for disabled columns! - $header.removeClass(remove); - $icon.removeClass(remove + ' ' + themes.icons); + $icon.removeClass(themes.icons || ''); } else { - classes = ($header.hasClass(ts.css.sortAsc)) ? - themes.sortAsc : - ($header.hasClass(ts.css.sortDesc)) ? themes.sortDesc : - $header.hasClass(ts.css.header) ? themes.sortNone : ''; - $header[classes === themes.sortNone ? 'removeClass' : 'addClass'](themes.active); - $icon.removeClass(remove).addClass(classes); + hdr = themes.sortNone; + icon = themes.iconSortNone; + if ($h.hasClass(ts.css.sortAsc)) { + hdr = [themes.sortAsc, themes.active].join(' '); + icon = themes.iconSortAsc; + } else if ($h.hasClass(ts.css.sortDesc)) { + hdr = [themes.sortDesc, themes.active].join(' '); + icon = themes.iconSortDesc; + } + $h + .addClass(hdr) + .find('.' + ts.css.icon) + .addClass(icon || ''); } } } @@ -256,21 +291,24 @@ ts.addWidget({ ts.benchmark("Applying " + theme + " theme", time); } }, - remove: function(table, c) { + remove: function(table, c, wo, refreshing) { + if (!wo.uitheme_applied) { return; } var $table = c.$table, - theme = c.theme || 'jui', + theme = c.appliedTheme || 'jui', themes = ts.themes[ theme ] || ts.themes.jui, $headers = $table.children('thead').children(), - remove = themes.sortNone + ' ' + themes.sortDesc + ' ' + themes.sortAsc; - $table - .removeClass('tablesorter-' + theme + ' ' + themes.table) - .find(ts.css.header).removeClass(themes.header); + remove = themes.sortNone + ' ' + themes.sortDesc + ' ' + themes.sortAsc, + iconRmv = themes.iconSortNone + ' ' + themes.iconSortDesc + ' ' + themes.iconSortAsc; + $table.removeClass('tablesorter-' + theme + ' ' + themes.table); + wo.uitheme_applied = false; + if (refreshing) { return; } + $table.find(ts.css.header).removeClass(themes.header); $headers .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') // remove hover .removeClass(themes.hover + ' ' + remove + ' ' + themes.active) - .find('.' + ts.css.filterRow) + .filter('.' + ts.css.filterRow) .removeClass(themes.filterRow); - $headers.find('.' + ts.css.icon).removeClass(themes.icons); + $headers.find('.' + ts.css.icon).removeClass(themes.icons + ' ' + iconRmv); } }); @@ -388,7 +426,7 @@ ts.addWidget({ ts.filter.init(table, c, wo); } }, - remove: function(table, c, wo) { + remove: function(table, c, wo, refreshing) { var tbodyIndex, $tbody, $table = c.$table, $tbodies = c.$tbodies; @@ -396,7 +434,9 @@ ts.addWidget({ .removeClass('hasFilters') // add .tsfilter namespace to all BUT search .unbind('addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter ')) + // remove the filter row even if refreshing, because the column might have been moved .find('.' + ts.css.filterRow).remove(); + if (refreshing) { return; } for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody $tbody.children().removeClass(wo.filter_filteredRow).show(); @@ -619,8 +659,11 @@ ts.filter = { ts.filter.buildRow(table, c, wo); } - c.$table.bind('addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter '), function(event, filter) { - c.$table.find('.' + ts.css.filterRow).toggle( !(wo.filter_hideEmpty && $.isEmptyObject(c.cache) && !(c.delayInit && event.type === 'appendCache')) ); // fixes #450 + txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter '); + c.$table.bind(txt, function(event, filter) { + val = (wo.filter_hideEmpty && $.isEmptyObject(c.cache) && !(c.delayInit && event.type === 'appendCache')); + // hide filter row using the "filtered" class name + c.$table.find('.' + ts.css.filterRow).toggleClass(wo.filter_filteredRow, val ); // fixes #450 if ( !/(search|filter)/.test(event.type) ) { event.stopPropagation(); ts.filter.buildDefault(table, true); @@ -1216,7 +1259,8 @@ ts.filter = { if (data.parsed[i]) { txt = data.cacheArray[i]; } else { - txt = wo.filter_ignoreCase ? $(this).text().toLowerCase() : $(this).text(); + txt = this.getAttribute( c.textAttribute ) || this.textContent || $(this).text(); + txt = $.trim( wo.filter_ignoreCase ? txt.toLowerCase() : txt ); if (c.sortLocaleCompare) { txt = ts.replaceAccents(txt); } @@ -1270,9 +1314,9 @@ ts.filter = { if (wo.filter_useParsedData || data.parsed[columnIndex]) { data.exact = data.cache; } else { - // using older or original tablesorter - data.exact = $.trim( $cells.eq(columnIndex).text() ); - data.exact = c.sortLocaleCompare ? ts.replaceAccents(data.exact) : data.exact; // issue #405 + val = $cells[columnIndex]; + result = $.trim( val.getAttribute( c.textAttribute ) || val.textContent || $cells.eq(columnIndex).text() ); + data.exact = c.sortLocaleCompare ? ts.replaceAccents(result) : result; // issue #405 } data.iExact = !regex.type.test(typeof data.exact) && wo.filter_ignoreCase ? data.exact.toLocaleLowerCase() : data.exact; result = showRow; // if showRow is true, show that row @@ -1280,9 +1324,10 @@ ts.filter = { // in case select filter option has a different value vs text "a - z|A through Z" ffxn = wo.filter_columnFilters ? c.$filters.add(c.$externalFilters).filter('[data-column="'+ columnIndex + '"]').find('select option:selected').attr('data-function-name') || '' : ''; - // replace accents - see #357 - data.filter = c.sortLocaleCompare ? ts.replaceAccents(data.filter) : data.filter; + if (c.sortLocaleCompare) { + data.filter = ts.replaceAccents(data.filter); + } val = true; if (wo.filter_defaultFilter && regex.iQuery.test( ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || '')) { @@ -1357,6 +1402,7 @@ ts.filter = { }, 0); }, getOptionSource: function(table, column, onlyAvail) { + table = $(table)[0]; var cts, c = table.config, wo = c.widgetOptions, @@ -1433,6 +1479,7 @@ ts.filter = { } }, getOptions: function(table, column, onlyAvail) { + table = $(table)[0]; var rowIndex, tbodyIndex, len, row, cache, cell, c = table.config, wo = c.widgetOptions, @@ -1454,7 +1501,7 @@ ts.filter = { } else { cell = row.cells[column]; if (cell) { - arry.push( $.trim( cell.textContent || cell.innerText || $(cell).text() ) ); + arry.push( $.trim( cell.getAttribute( c.textAttribute ) || cell.textContent || $(cell).text() ) ); } } } @@ -1602,7 +1649,7 @@ ts.setFilters = function(table, filter, apply, skipFirst) { // ensure new set filters are applied, even if the search is the same c.lastCombinedFilter = null; c.lastSearch = []; - ts.filter.searching(c.$table[0], filter, skipFirst); + ts.filter.searching(c.table, filter, skipFirst); c.$table.trigger('filterFomatterUpdate'); } return !!valid; @@ -1703,6 +1750,13 @@ ts.addWidget({ setWidth( $table, $stickyTable ); setWidth( $header, $stickyCells ); }; + // save stickyTable element to config + // it is also saved to wo.$sticky + if (c.$extraTables && c.$extraTables.length) { + c.$extraTables.add($stickyTable); + } else { + c.$extraTables = $stickyTable; + } // fix clone ID, if it exists - fixes #271 if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; } // clear out cloned table, except for sticky header @@ -1810,11 +1864,11 @@ ts.addWidget({ .unbind( 'pagerComplete filterEnd '.split(' ').join(namespace) ) .next('.' + ts.css.stickyWrap).remove(); if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table - // don't unbind if any table on the page still has stickyheaders applied - if (!$('.hasStickyHeaders').length) { - $(window).add(wo.stickyHeaders_xScroll).add(wo.stickyHeaders_yScroll).add(wo.stickyHeaders_attachTo) - .unbind( 'scroll resize '.split(' ').join(namespace) ); - } + $(window) + .add(wo.stickyHeaders_xScroll) + .add(wo.stickyHeaders_yScroll) + .add(wo.stickyHeaders_attachTo) + .unbind( 'scroll resize '.split(' ').join(namespace) ); ts.addHeaderResizeEvent(table, false); } }); @@ -1974,7 +2028,7 @@ ts.resizableReset = function(table, nosave) { if (table && c) { c.$headers.each(function(i){ $t = $(this); - if (wo.resizable_widths[i]) { + if (wo.resizable_widths && wo.resizable_widths[i]) { $t.css('width', wo.resizable_widths[i]); } else if (!$t.hasClass('resizable-false')) { // don't clear the width of any column that is not resizable @@ -2042,7 +2096,8 @@ ts.addWidget({ } } }, - remove: function(table) { + remove: function(table, c) { + c.$table.removeClass('hasSaveSort'); // clear storage if (ts.storage) { ts.storage( table, 'tablesorter-savesort', '' ); } } diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-duration.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-duration.js index 408797d..1f04e67 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-duration.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-duration.js @@ -1,13 +1,12 @@ -/*! Duration parser - */ +/*! Duration parser */ /*jshint jquery:true, unused:false */ ;(function($){ -"use strict"; +'use strict'; // If any number > 9999, then set table.config.durationLength = 5 // The below regex matches this duration example: 1y 23d 12h 44m 9s $.tablesorter.addParser({ - id: "duration", + id: 'duration', is: function() { return false; }, @@ -34,7 +33,35 @@ } return duration; }, - type: "text" + type: 'text' + }); + + /*! Countdown parser ( hh:mm:ss ) */ + /* Added 2/7/2015 (v2.19.0) - see http://stackoverflow.com/a/27023733/145346 */ + $.tablesorter.addParser({ + id: 'countdown', + is: function () { + return false; + }, + format: function ( text, table ) { + // change maxDigits to 4, if values go > 999 + // or to 5 for values > 9999, etc. + var maxDigits = table.config.durationLength || 4, + // prefix contains leading zeros that are tacked + prefix = new Array( maxDigits + 1 ).join( '0' ), + // split time into blocks + blocks = text.split( /\s*:\s*/ ), + len = blocks.length, + result = []; + // add values in reverse, so if there is only one block + // ( e.g. '10' ), then it would be the time in seconds + while ( len ) { + result.push( ( prefix + ( blocks[ --len ] || 0 ) ).slice( -maxDigits ) ); + } + // reverse the results and join them + return result.length ? result.reverse().join( '' ) : text; + }, + type: 'text' }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index e166ef9..131c882 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,18 +1,17 @@ /*! input & select parsers for jQuery 1.7+ & tablesorter 2.7.11+ - * Updated 9/15/2014 (v2.17.8) + * Updated 2/7/2015 (v2.19.0) * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ /*jshint browser: true, jquery:true, unused:false */ ;(function($){ "use strict"; - var resort = true, // resort table after update - updateServer = function(event, $table, $input){ - // do something here to update your server, if needed - // event = change event object - // $table = jQuery object of the table that was just updated - // $input = jQuery object of the input or select that was modified - }; + var updateServer = function(event, $table, $input){ + // do something here to update your server, if needed + // event = change event object + // $table = jQuery object of the table that was just updated + // $input = jQuery object of the input or select that was modified + }; // Custom parser for parsing input values // updated dynamically using the "change" function below @@ -98,26 +97,21 @@ // if this code interferes somehow, target the specific table $('#mytable'), instead of $('table') $(function(){ $('table').on('tablesorter-initialized', function(){ - // this flag prevents the updateCell event from being spammed - // it happens when you modify input text and hit enter - var focused = false, - restoreValue = function(isTbody){ - // focused = false; // uncomment this line to prevent auto-accepting changes - // make sure we restore original values - // isTbody is needed to prevent the select from closing in IE - // see https://connect.microsoft.com/IE/feedbackdetail/view/962618/ - if (isTbody) { - $(':focus').blur(); - } - return; - }; + var restoreValue = function(isTbody){ + // make sure we restore original values (trigger blur) + // isTbody is needed to prevent the select from closing in IE + // see https://connect.microsoft.com/IE/feedbackdetail/view/962618/ + if (isTbody) { + $(':focus').blur(); + } + return; + }; // bind to .tablesorter (default class name) $(this).children('tbody') .on('mouseleave', function(e){ restoreValue(e.target.tagName === 'TBODY'); }) .on('focus', 'select, input, textarea', function(){ - focused = true; $(this).data('ts-original-value', this.value); }) .on('blur', 'input, textarea', function(){ @@ -132,9 +126,10 @@ return; } // Update cell cache using... select: change, input: enter or textarea: alt + enter - if ( ( e.type === 'change' && focused ) || + if ( ( e.type === 'change' ) || ( e.type === 'keyup' && e.which === 13 && ( e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' && e.altKey ) ) ) { - var $tar = $(e.target), + var undef, + $tar = $(e.target), $cell = $tar.closest('td'), $table = $cell.closest('table'), indx = $cell[0].cellIndex, @@ -148,9 +143,9 @@ // ignore change event if nothing changed if ($tar.val() !== $tar.data('ts-original-value')) { $tar.data('ts-original-value', $tar.val()); - $table.trigger('updateCell', [ $tar.closest('td'), resort, function(){ + // pass undefined resort value so it falls back to config.resort setting + $table.trigger('updateCell', [ $tar.closest('td'), undef, function(){ updateServer(e, $table, $tar); - setTimeout(function(){ focused = false; }, 10); } ]); } } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js index 982db1b..b385971 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js @@ -1,4 +1,4 @@ -/*! tablesorter Align Character widget - updated 3/12/2014 (core v2.15.8) +/*! tablesorter Align Character widget - updated 2/7/2015 (v2.19.0) * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -83,10 +83,10 @@ ts.alignChar = { for (rowIndex = 0; rowIndex < len; rowIndex++) { alignChar = $(wo.alignChar_wrap).length ? $(wo.alignChar_wrap).html(v.align)[0].outerHTML : v.align; $row = rows.row ? rows.row[rowIndex] : rows.normalized[rowIndex][c.columns].$row; + last = right[rowIndex].slice(v.align.length); $row.find('td').eq(v.column).html( '<span class="ts-align-wrap"><span class="ts-align-left" style="' + wLeft + '">' + left[rowIndex] + '</span>' + - '<span class="ts-align-right" style="' + wRight + '">' + alignChar + - right[rowIndex].slice(v.align.length) + '</span></span>' + '<span class="ts-align-right" style="' + wRight + '">' + ( last.length ? alignChar + last : '' ) + '</span></span>' ); } } @@ -134,7 +134,8 @@ ts.addWidget({ c.$table.trigger('refreshAlign'); } }, - remove : function(table, c, wo){ + remove : function(table, c, wo, refreshing){ + if (refreshing) { return; } c.$headers.filter('[' + wo.alignChar_charAttrib + ']').each(function(){ ts.alignChar.remove(table, c, this.column); }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js index 695514e..046a52f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js @@ -1,4 +1,4 @@ -/*! Build Table widget for tableSorter v2.16.0 (4/23/2014) +/*! Build Table widget for tableSorter v2.16.0; updated 2/7/2015 (v2.19.0) * by Rob Garrison */ /*jshint browser:true, jquery:true, unused:false */ @@ -34,7 +34,7 @@ var ts = $.tablesorter = $.tablesorter || {}, return bt.html( table, d, wo ); } try { - d = $.parseJSON(d); + d = $.parseJSON(d || 'null'); if (d) { // valid JSON! return bt.object( table, d, wo ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js new file mode 100644 index 0000000..10a57b2 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js @@ -0,0 +1,276 @@ +/* Chart widget (beta) for TableSorter 2/7/2015 (v2.19.0) + * Requires tablesorter v2.8+ and jQuery 1.7+ + */ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;(function($){ + 'use strict'; + + var ts = $.tablesorter, + + // temp variables + chart_cols = [], + chart_headers = [], + // google charts + chart_rows = [], + chart_data = [], + // highcharts + chart_categories = [], + chart_series = [], + // fusioncharts + chart_category = [], + chart_dataset = [], + + chart = ts.chart = { + + // regex used to strip out non-digit values before sending + // the string to the $.tablesorter.formatFloat function + nonDigit : /[^\d,.\-()]/g, + + init: function(c, wo) { + c.$table + .off(wo.chart_event) + .on(wo.chart_event, function() { + if (this.hasInitialized) { + // refresh "c" variable in case options are updated dynamically + var c = this.config; + chart.getCols(c, c.widgetOptions); + chart.getData(c, c.widgetOptions); + } + }); + }, + + getCols: function(c, wo) { + var i; + chart_cols = []; + chart_series = []; + chart_dataset = []; + + for ( i = 0; i < c.columns; i++ ) { + if ( wo.chart_useSelector && ts.hasWidget( c.table, 'columnSelector' ) && !c.selector.auto ) { + if ( ( c.selector.states[i] && $.inArray(i, wo.chart_ignoreColumns) < 0 ) || + i === wo.chart_labelCol || i === wo.chart_sort[0][0] ) { + chart_cols.push(i); + } + } else { + if ( $.inArray(i, wo.chart_ignoreColumns) < 0 || i === wo.chart_labelCol || i === wo.chart_sort[0][0] ) { + chart_cols.push(i); + } + } + } + }, + + getData: function(c, wo) { + chart.getHeaders(c, wo); + chart.getRows(c, wo); + + /* == Google data == + array of arrays (Google charts) + [ + [ "Year", "Sales", "Expenses" ], + [ "2004", 1000, 400 ], + [ "2005", 1170, 460 ], + [ "2006", 660, 1120 ], + [ "2007", 1030, 540 ] + ] + + == Highcharts == + categories -> [ '2004', '2005', '2006', '2007' ] + series -> [{ + name: 'Sales', + data: [ 1000, 1170, 660, 1030 ] + }, { + name: 'Expenses', + data: [ 400, 460, 1120, 540 ] + }] + + == Fusioncharts + "categories": [{ + "category": [ + {"label": "2004"}, + {"label": "2005"}, + {"label": "2006"}, + {"label": "2007"} + ] + }], + "dataset": [ + { + "seriesname": "Sales", + "data": [ + {"value": "1000"}, + {"value": "1170"}, + {"value": "660"}, + {"value": "1030"} + ] + },{ + "seriesname": "Expenses", + "data": [ + {"value": "400"}, + {"value": "600"}, + {"value": "1120"}, + {"value": "540"} + ] + } + ] + */ + + chart_data = [ chart_headers ]; + $.each(chart_rows, function(k, row) { + chart_data.push(row); + }); + + c.chart = { + // google + data: chart_data, + // highcharts + categories: chart_categories, + series: chart_series, + // FusionCharts + category: chart_category, + dataset: chart_dataset + }; + }, + + getHeaders: function(c, wo) { + var text; + chart_headers = []; + chart_series = []; + chart_dataset = []; + chart_headers.push( c.headerContent[wo.chart_labelCol] ); + $.each(chart_cols, function(k, col) { + if (col === wo.chart_labelCol) { + return true; + } + text = c.headerContent[col]; + chart_headers.push( text ); + chart_series.push( { name: text, data: [] } ); + chart_dataset.push( { seriesname: text, data: [] } ); + }); + }, + + getRows: function(c, wo) { + // the cache may not have a zero index if there are any "info-only" tbodies above the main tbody + var cache = c.cache[0].normalized, + rows = []; + chart_rows = []; + chart_categories = []; + chart_category = []; + + $.each(cache, function(indx, rowVal) { + var i, txt, + $tr = rowVal[c.columns].$row, + $cells = $tr.children('th,td'), + row = []; + if ( + (/v/i.test(wo.chart_incRows) && $tr.is(':visible')) || + (/f/i.test(wo.chart_incRows) && !$tr.hasClass(wo.filter_filteredRow || 'filtered')) || + (!/(v|f)/i.test(wo.chart_incRows)) + ) { + // Add all cols (don't mess up indx for sorting) + for (i = 0; i < c.columns; i++) { + if ( $.inArray(indx, wo.chart_parsed) >= 0 ) { + row.push( rowVal[i] ); + } else { + txt = $cells[i].getAttribute( c.textAttribute ) || $cells[i].textContent || $cells.eq(i).text(); + row.push( $.trim( txt ) ); + } + } + rows.push(row); + } + }); + + // sort based on chart_sort + rows.sort(function(a, b) { + if ( wo.chart_sort[0][1] === 1 ) { + return ts.sortNatural( b[wo.chart_sort[0][0]], a[wo.chart_sort[0][0]] ); + } + return ts.sortNatural( a[wo.chart_sort[0][0]], b[wo.chart_sort[0][0]] ); + }); + + $.each(rows, function(i, rowVal) { + var value, + objIndex = 0, + row = [], + label = rowVal[wo.chart_labelCol]; + + row.push( '' + label ); + + $.each(rowVal, function(indx, cellValue) { + var tempVal; + if (indx === wo.chart_labelCol) { + chart_categories.push( cellValue ); + chart_category.push({ 'label': cellValue }); + return true; + } + value = false; + if ( wo.chart_useSelector && ts.hasWidget( c.table, 'columnSelector' ) && !c.selector.auto ) { + if ( c.selector.states[indx] && $.inArray(indx, wo.chart_ignoreColumns) < 0 ) { + value = '' + cellValue; + } + } else { + if ($.inArray(indx, wo.chart_ignoreColumns) < 0) { + value = '' + cellValue; + } + } + + if (value !== false) { + if ( /s/i.test( '' + wo.chart_layout[row.length] ) ) { + row.push( value ); + chart_series[objIndex].data.push( value ); + chart_dataset[objIndex].data.push( value ); + } else { + // using format float, after stripping out all non-digit values + tempVal = ts.formatFloat( value.replace( chart.nonDigit, '' ), c.table ); + tempVal = isNaN(tempVal) ? value : tempVal; + // if tempVal ends up being an empty string, fall back to value + row.push( tempVal ); + chart_series[objIndex].data.push( tempVal ); + chart_dataset[objIndex].data.push( { value : tempVal } ); + } + objIndex++; + } + }); + chart_rows.push(row); + }); + }, + + remove: function(c) { + c.$table.off(chart.event); + } + + }; + + ts.addWidget({ + id: 'chart', + options: { + // (a)ll, (v)isible or (f)iltered - only the first letter is needed + chart_incRows: 'filtered', + // prefer columnSelector for ignoreColumns + chart_useSelector: false, + // columns to ignore [0, 1,... ] (zero-based index) + chart_ignoreColumns: [], + // Use parsed data instead of cell.text() + chart_parsed: [], + // data output layout, float is default + chart_layout: { + // first element is a string, all others will be float + 0: 'string' + }, + // Set the label column + chart_labelCol: 0, + // data sort, should always be first row, might want [[0,1]] + chart_sort: [[0,0]], + // event to trigger get updated data + chart_event: 'chartData' + }, + + init: function(table, thisWidget, c, wo) { + chart.init(c, wo); + }, + + remove: function(table, c, wo) { + chart.remove(c); + } + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index 4432c67..dfa8018 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Column Selector/Responsive table widget (beta) for TableSorter - 12/22/2014 (v2.18.4) +/* Column Selector/Responsive table widget for TableSorter - 2/7/2015 (v2.19.0) * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -49,11 +49,31 @@ tsColSel = ts.columnSelector = { c.$table .off('refreshColumnSelector' + namespace) - .on('refreshColumnSelector' + namespace, function(){ + .on('refreshColumnSelector' + namespace, function(e, opt){ // make sure we're using current config settings - var c = this.config; - tsColSel.updateBreakpoints(c, c.widgetOptions); - tsColSel.updateCols(c, c.widgetOptions); + var i, + isArry = $.isArray(opt), + c = this.config, + wo = c.widgetOptions; + // see #798 + if (opt && c.selector.$container.length) { + if (isArry) { + // make sure array contains numbers + $.each(opt, function(i,v){ + opt[i] = parseInt(v, 10); + }); + for (i = 0; i < c.columns; i++) { + c.selector.$container + .find('input[data-column=' + i + ']') + .prop('checked', $.inArray( i, opt ) >= 0 ); + } + } + // if passing an array, set auto to false to allow manual column selection & update columns + tsColSel.updateAuto( c, wo, colSel.$container.find('input[data-column="auto"]').prop('checked', !isArry) ); + } else { + tsColSel.updateBreakpoints(c, wo); + tsColSel.updateCols(c, wo); + } }); }, @@ -91,7 +111,7 @@ tsColSel = ts.columnSelector = { // set default state; storage takes priority colSel.states[colId] = saved && typeof(saved[colId]) !== 'undefined' ? saved[colId] : typeof(wo.columnSelector_columns[colId]) !== 'undefined' ? - wo.columnSelector_columns[colId] : (state === 'true' || !(state === 'false')); + wo.columnSelector_columns[colId] : (state === 'true' || state !== 'false'); colSel.$column[colId] = $(this); // set default col title @@ -141,29 +161,7 @@ tsColSel = ts.columnSelector = { .prop('checked', colSel.auto) .toggleClass( wo.columnSelector_cssChecked, colSel.auto ) .on('change', function(){ - colSel.auto = this.checked; - $.each( colSel.$checkbox, function(i, $cb){ - if ($cb) { - $cb[0].disabled = colSel.auto; - colSel.$wrapper[i].toggleClass('disabled', colSel.auto); - } - }); - if (wo.columnSelector_mediaquery) { - tsColSel.updateBreakpoints(c, wo); - } - tsColSel.updateCols(c, wo); - // copy the column selector to a popup/tooltip - if (c.selector.$popup) { - c.selector.$popup.find('.tablesorter-column-selector') - .html( colSel.$container.html() ) - .find('input').each(function(){ - var indx = $(this).attr('data-column'); - $(this).prop( 'checked', indx === 'auto' ? colSel.auto : colSel.states[indx] ); - }); - } - if (wo.columnSelector_saveColumns && ts.storage) { - ts.storage( c.$table[0], 'tablesorter-columnSelector-auto', { auto : colSel.auto } ); - } + tsColSel.updateAuto(c, wo, $(this)); }).change(); } // Add a bind on update to re-run col setup @@ -173,6 +171,37 @@ tsColSel = ts.columnSelector = { } }, + updateAuto: function(c, wo, $el) { + var colSel = c.selector; + colSel.auto = $el.prop('checked') || false; + $.each( colSel.$checkbox, function(i, $cb){ + if ($cb) { + $cb[0].disabled = colSel.auto; + colSel.$wrapper[i].toggleClass('disabled', colSel.auto); + } + }); + if (wo.columnSelector_mediaquery) { + tsColSel.updateBreakpoints(c, wo); + } + tsColSel.updateCols(c, wo); + // copy the column selector to a popup/tooltip + if (c.selector.$popup) { + c.selector.$popup.find('.tablesorter-column-selector') + .html( colSel.$container.html() ) + .find('input').each(function(){ + var indx = $(this).attr('data-column'); + $(this).prop( 'checked', indx === 'auto' ? colSel.auto : colSel.states[indx] ); + }); + } + if (wo.columnSelector_saveColumns && ts.storage) { + ts.storage( c.$table[0], 'tablesorter-columnSelector-auto', { auto : colSel.auto } ); + } + // trigger columnUpdate if auto is true (it gets skipped in updateCols() + if (colSel.auto) { + c.$table.trigger('columnUpdate'); + } + }, + updateBreakpoints: function(c, wo) { var priority, column, breaks, colSel = c.selector, @@ -256,9 +285,11 @@ tsColSel = ts.columnSelector = { $popup.find('.tablesorter-column-selector') .html( colSel.$container.html() ) .find('input').each(function(){ - var indx = $(this).toggleClass( wo.columnSelector_cssChecked, isChecked ).attr('data-column'), + var indx = $(this).attr('data-column'), isChecked = indx === 'auto' ? colSel.auto : colSel.states[indx]; - $(this).prop( 'checked', isChecked ); + $(this) + .toggleClass( wo.columnSelector_cssChecked, isChecked ) + .prop( 'checked', isChecked ); }); colSel.$popup = $popup.on('change', 'input', function(){ // data input @@ -313,7 +344,8 @@ ts.addWidget({ init: function(table, thisWidget, c, wo) { tsColSel.init(table, c, wo); }, - remove: function(table, c){ + remove: function(table, c, wo, refreshing) { + if (refreshing) { return; } var csel = c.selector; csel.$container.empty(); if (csel.$popup) { csel.$popup.empty(); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js index 8363c72..d5419fd 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js @@ -1,4 +1,4 @@ -/*! tablesorter CSS Sticky Headers widget - updated 11/7/2014 (v2.18.3) +/*! tablesorter CSS Sticky Headers widget - updated 2/7/2015 (v2.19.0) * Requires a modern browser, tablesorter v2.8+ */ /*jshint jquery:true, unused:false */ @@ -134,7 +134,8 @@ }); }, - remove: function(table, c, wo){ + remove: function(table, c, wo, refreshing) { + if (refreshing) { return; } var namespace = c.namespace + 'cssstickyheader '; $(window).unbind('scroll resize '.split(' ').join(namespace)); c.$table diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js index d02d8f2..b1f17f1 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js @@ -1,4 +1,4 @@ -/*! tablesorter Editable Content widget - updated 10/26/2014 (v2.18.0) +/*! tablesorter Editable Content widget - updated 2/7/2015 (v2.19.0) * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -35,9 +35,9 @@ var tse = $.tablesorter.editable = { update: function( c, wo ) { var indx, tmp, $t, + colIndex = [], cols = []; - - if ( $.type( wo.editable_columns ) === 'string' && wo.editable_columns.indexOf( '-' ) >= 0 ) { + if ( !wo.editable_columnsArray && $.type( wo.editable_columns ) === 'string' && wo.editable_columns.indexOf( '-' ) >= 0 ) { // editable_columns can contain a range string ( i.e. '2-4' ) tmp = wo.editable_columns.split( /\s*-\s*/ ); indx = parseInt( tmp[ 0 ], 10 ) || 0; @@ -46,15 +46,21 @@ var tse = $.tablesorter.editable = { tmp = c.columns - 1; } for ( ; indx <= tmp; indx++ ) { + colIndex.push( indx ); cols.push( 'td:nth-child(' + ( indx + 1 ) + ')' ); } } else if ( $.isArray( wo.editable_columns ) ) { - $.each( wo.editable_columns, function( i, col ) { + $.each( wo.editable_columnsArray || wo.editable_columns, function( i, col ) { if ( col < c.columns ) { + colIndex.push( col ); cols.push( 'td:nth-child(' + ( col + 1 ) + ')' ); } }); } + if ( !wo.editable_columnsArray ) { + wo.editable_columnsArray = colIndex; + wo.editable_columnsArray.sort(function(a,b){ return a - b; }); + } tmp = $( '<div>' ).wrapInner( wo.editable_wrapContent ).children().length || $.isFunction( wo.editable_wrapContent ); // IE does not allow making TR/TH/TD cells directly editable ( issue #404 ) // so add a div or span inside ( it's faster than using wrapInner() ) @@ -91,7 +97,7 @@ var tse = $.tablesorter.editable = { c.$table .off( 'updateComplete pagerComplete '.split( ' ' ).join( '.tseditable' ) ) .on( 'updateComplete pagerComplete '.split( ' ' ).join( '.tseditable' ), function() { - tse.update( c, wo ); + tse.update( c, c.widgetOptions ); }); c.$tbodies diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js new file mode 100644 index 0000000..2e7e688 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js @@ -0,0 +1,70 @@ +/*! tablesorter Formatter widget - 2/7/2015 (v2.19.0) + * Requires tablesorter v2.8+ and jQuery 1.7+ + * by Rob Garrison + */ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;(function($){ +'use strict'; + var ts = $.tablesorter; + + ts.formatter = { + init : function( c ) { + var events = $.trim( c.widgetOptions.formatter_event ) + ' pagerComplete updateComplete ' + .split(' ').join('.tsformatter '); + c.$table.on( events, function() { + ts.formatter.setup( c ); + }); + ts.formatter.setup( c ); + }, + setup : function( c ) { + // do nothing for empty tables + if ( $.isEmptyObject( c.cache ) ) { return; } + var $tbody, tbodyIndex, rowIndex, rows, cell, len, column, + wo = c.widgetOptions, + data = { config: c, wo: wo }, + formatter = [], + $headers = []; + // set up variables + for ( column = 0; column < c.columns; column++ ) { + $headers[ column ] = c.$headers.filter('[data-column="' + column + '"]:last'); + formatter[ column ] = ts.getColumnData( c.table, wo.formatter_column, column ) || false; + } + // main loop + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ){ + $tbody = ts.processTbody( c.table, c.$tbodies.eq( tbodyIndex ), true ); // detach tbody + rows = c.cache[ tbodyIndex ]; + len = rows.normalized.length; + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { + data.$row = rows.normalized[ rowIndex ][ c.columns ].$row; + data.$cells = data.$row.children( 'th, td' ); + for ( column = 0; column < c.columns; column++ ) { + if ( formatter[ column ] ) { + data.columnIndex = column; + data.$header = $headers[ column ]; + data.$cell = data.$cells.eq( column ); + cell = data.$cell[0]; + // get text from attribute first, just in case we're updating + data.text = cell.getAttribute( c.textAttribute ) || cell.textContent || data.$cell.text(); + cell.innerHTML = formatter[ column ]( data.text, data ); + } + } + } + ts.processTbody( c.table, $tbody, false); // restore tbody + } + } + }; + + ts.addWidget({ + id: 'formatter', + priority: 100, + options: { + formatter_column : {}, + formatter_event : 'applyFormatter' + }, + init: function( table ) { + ts.formatter.init( table.config ); + } + }); + +})( jQuery ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index bf15152..5d0ddcb 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -1,4 +1,4 @@ -/*! tablesorter math widget - beta - updated 5/28/2014 (v2.17.1) +/*! tablesorter math widget - updated 2/7/2015 (v2.19.0) * Requires tablesorter v2.16+ and jQuery 1.7+ * by Rob Garrison */ @@ -8,6 +8,8 @@ "use strict"; var ts = $.tablesorter, + events = ( 'tablesorter-initialized update updateAll updateRows addRows updateCell ' + + 'filterReset filterEnd recalculate ' ).split(' ').join('.tsmath '), math = { // get all of the row numerical values in an arry @@ -183,7 +185,7 @@ * (c)2011 ecava * Dual licensed under the MIT or GPL Version 2 licenses. */ - ts.formatMask = function(m, v, tmpPrefix, tmpSuffix){ + ts.formatMask = function(m, v, tmpPrefix, tmpSuffix) { if ( !m || isNaN(+v) ) { return v; // return as it is. } @@ -388,9 +390,13 @@ }, init : function(table, thisWidget, c, wo){ c.$table - .bind('tablesorter-initialized update updateRows addRows updateCell filterReset filterEnd '.split(' ').join('.tsmath '), function(e){ + .unbind(events + ' updateComplete.tsmath') + .bind(events, function(e){ var init = e.type === 'tablesorter-initialized'; - if (!wo.math_isUpdating || init) { + if (e.type === 'updateAll') { + // redo data-column indexes in case columns were rearranged + ts.computeColumnIndex( c.$table.children('tbody').children() ); + } else if (!wo.math_isUpdating || init) { math.recalculate( table, c, wo, init ); } }) @@ -403,9 +409,10 @@ }, // this remove function is called when using the refreshWidgets method or when destroying the tablesorter plugin // this function only applies to tablesorter v2.4+ - remove: function(table, c, wo){ + remove: function(table, c, wo, refreshing){ + if (refreshing) { return; } $(table) - .unbind('tablesorter-initialized update updateRows addRows updateCell filterReset filterEnd '.split(' ').join('.tsmath ')) + .unbind(events + ' updateComplete.tsmath') .find('[data-' + wo.math_data + ']').empty(); } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index e7ee4a3..6d7b94a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -1,4 +1,4 @@ -/* Output widget (beta) for TableSorter 7/17/2014 (v2.17.5) +/* Output widget for TableSorter 2/7/2015 (v2.19.0) * Requires tablesorter v2.8+ and jQuery 1.7+ * Modified from: * HTML Table to CSV: http://www.kunalbabre.com/projects/table2CSV.php (License unknown?) @@ -20,7 +20,7 @@ output = ts.output = { regexBR : /(<br([\s\/])?>|\n)/g, // replace regexIMG : /<img[^>]+alt\s*=\s*['"]([^'"]+)['"][^>]*>/i, // match regexHTML : /<[^<]+>/g, // replace - + replaceCR : '\\n', replaceTab : '\\t', @@ -192,7 +192,11 @@ output = ts.output = { // replace " with “ if undefined result = input.replace(/\"/g, wo.output_replaceQuote || '\u201c'); // replace line breaks with \\n & tabs with \\t - result = result.replace(output.regexBR, output.replaceCR).replace(/\t/g, output.replaceTab); + if (!wo.output_trimSpaces) { + result = result.replace(output.regexBR, output.replaceCR).replace(/\t/g, output.replaceTab); + } else { + result = result.replace(output.regexBR, ''); + } // extract img alt text txt = result.match(output.regexIMG); if (!wo.output_includeHTML && txt !== null) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 76172fa..3d00632 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/* Pager widget for TableSorter 12/22/2014 (v2.18.4) - requires jQuery 1.7+ */ +/* Pager widget for TableSorter 2/7/2015 (v2.19.0) - requires jQuery 1.7+ */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ "use strict"; @@ -111,8 +111,8 @@ ts.addWidget({ } tsp.moveToPage(table, c.pager, false); }, - remove: function(table, c){ - tsp.destroyPager(table, c); + remove: function(table, c, wo, refreshing){ + tsp.destroyPager(table, c, refreshing); } }); @@ -138,7 +138,12 @@ tsp = ts.pager = { endRow: 0, ajaxCounter: 0, $size: null, - last: {} + last: {}, + // save original pager size + setSize: wo.pager_size, + setPage: wo.pager_startPage, + events: 'filterInit filterStart filterEnd sortEnd disable enable destroy updateComplete ' + + 'pageSize pageSet pageAndSize pagerUpdate ' }, c.pager); // pager initializes multiple times before table has completed initialization @@ -162,8 +167,8 @@ tsp = ts.pager = { p.initializing = true; if (wo.pager_savePages && ts.storage) { t = ts.storage(table, wo.pager_storageKey) || {}; // fixes #387 - p.page = isNaN(t.page) ? p.page : t.page; - p.size = ( isNaN(t.size) ? p.size : t.size ) || 10; + p.page = ( isNaN(t.page) ? p.page : t.page ) || p.setPage || 1; + p.size = ( isNaN(t.size) ? p.size : t.size ) || p.setSize || 10; $.data(table, 'pagerLastSize', p.size); } @@ -224,9 +229,9 @@ tsp = ts.pager = { s = wo.pager_selectors; c.$table - .off('filterInit filterStart filterEnd sortEnd disable enable destroy updateComplete pageSize pageSet '.split(' ').join('.pager ')) - .on('filterInit.pager filterStart.pager', function(e) { - p.currentFilters = c.$table.data('lastSearch'); + .off(p.events.split(' ').join('.pager ')) + .on('filterInit.pager filterStart.pager', function(e, filters) { + p.currentFilters = $.isArray(filters) ? filters : c.$table.data('lastSearch'); // don't change page if filters are the same (pager updating, etc) if (e.type === 'filterStart' && wo.pager_pageReset !== false && (c.lastCombinedFilter || '') !== (p.currentFilters || []).join('')) { p.page = wo.pager_pageReset; // fixes #456 & #565 @@ -253,9 +258,9 @@ tsp = ts.pager = { e.stopPropagation(); tsp.enablePager(table, c, true); }) - .on('destroy.pager', function(e){ + .on('destroy.pager', function(e, refreshing){ e.stopPropagation(); - tsp.destroyPager(table, c); + tsp.destroyPager(table, c, refreshing); }) .on('updateComplete.pager', function(e, table, triggered){ e.stopPropagation(); @@ -273,22 +278,32 @@ tsp = ts.pager = { } tsp.hideRows(table, c); tsp.changeHeight(table, c); - tsp.updatePageDisplay(table, c); + // update without triggering pagerComplete + tsp.updatePageDisplay(table, c, false); // make sure widgets are applied - fixes #450 c.$table.trigger('applyWidgets'); + tsp.updatePageDisplay(table, c); }) - .on('pageSize.pager', function(e,v){ + .on('pageSize.pager refreshComplete.pager', function(e,v){ e.stopPropagation(); - tsp.setPageSize(table, parseInt(v, 10) || 10, c); + tsp.setPageSize(table, parseInt(v, 10) || p.setSize || 10, c); tsp.hideRows(table, c); tsp.updatePageDisplay(table, c, false); - if (p.$size.length) { p.$size.val(p.size); } // twice? }) - .on('pageSet.pager', function(e,v){ + .on('pageSet.pager pagerUpdate.pager', function(e,v){ e.stopPropagation(); p.page = (parseInt(v, 10) || 1) - 1; - if (p.$goto.length) { p.$goto.val(c.size); } // twice? + // force pager refresh + if (e.type === 'pagerUpdate') { p.last.page = true; } + tsp.moveToPage(table, p, true); + tsp.updatePageDisplay(table, c, false); + }) + .on('pageAndSize.pager', function(e, page, size){ + e.stopPropagation(); + p.page = (parseInt(page, 10) || 1) - 1; + tsp.setPageSize(table, parseInt(size, 10) || p.setSize || 10, c); tsp.moveToPage(table, p, true); + tsp.hideRows(table, c); tsp.updatePageDisplay(table, c, false); }); @@ -356,7 +371,8 @@ tsp = ts.pager = { }, calcFilters: function(table, c) { - var wo = c.widgetOptions, + var tbodyIndex, + wo = c.widgetOptions, p = c.pager, hasFilters = c.$table.hasClass('hasFilters'); if (hasFilters && !wo.pager_ajaxUrl) { @@ -364,8 +380,10 @@ tsp = ts.pager = { // delayInit: true so nothing is in the cache p.filteredRows = p.totalRows = c.$tbodies.eq(0).children('tr').not( wo.pager_countChildRows ? '' : '.' + c.cssChildRow ).length; } else { + // just in case the pager tbody isn't the first tbody + tbodyIndex = c.$table.children('tbody').index( c.$tbodies.eq(0) ); p.filteredRows = 0; - $.each(c.cache[0].normalized, function(i, el) { + $.each(c.cache[tbodyIndex].normalized, function(i, el) { p.filteredRows += p.regexRows.test(el[c.columns].$row[0].className) ? 0 : 1; }); } @@ -379,7 +397,7 @@ tsp = ts.pager = { var s, t, $out, wo = c.widgetOptions, p = c.pager, - sz = p.size || 10; // don't allow dividing by zero + sz = p.size || p.setSize || 10; // don't allow dividing by zero if (wo.pager_countChildRows) { t.push(c.cssChildRow); } p.$size.add(p.$goto).removeClass(wo.pager_css.disabled).removeAttr('disabled').attr('aria-disabled', 'false'); p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method @@ -389,8 +407,8 @@ tsp = ts.pager = { p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) { t = (p.size * p.page > p.filteredRows) && completed; - p.startRow = (t) ? 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1); - p.page = (t) ? 0 : p.page; + p.page = (t) ? wo.pager_pageReset || 0 : p.page; + p.startRow = (t) ? p.size * p.page + 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1); p.endRow = Math.min( p.filteredRows, p.totalRows, p.size * ( p.page + 1 ) ); $out = p.$container.find(wo.pager_selectors.pageDisplay); // form the output string (can now get a new output string from the server) @@ -414,17 +432,16 @@ tsp = ts.pager = { } return extra.length > 1 && data && data[extra[0]] ? data[extra[0]][extra[1]] : p[str] || (data ? data[str] : deflt) || deflt; }); - + if ( p.$goto.length ) { + t = ''; + $.each(tsp.buildPageSelect(p, c), function(i, opt){ + t += '<option value="' + opt + '">' + opt + '</option>'; + }); + // innerHTML doesn't work in IE9 - http://support2.microsoft.com/kb/276228 + p.$goto.html(t).val( p.page + 1 ); + } if ($out.length) { $out[ ($out[0].tagName === 'INPUT') ? 'val' : 'html' ](s); - if ( p.$goto.length ) { - t = ''; - $.each(tsp.buildPageSelect(p, c), function(i, opt){ - t += '<option value="' + opt + '">' + opt + '</option>'; - }); - // innerHTML doesn't work in IE9 - http://support2.microsoft.com/kb/276228 - p.$goto.html(t).val( p.page + 1 ); - } // rebind startRow/page inputs $out.find('.ts-startRow, .ts-page').off('change').on('change', function(){ var v = $(this).val(), @@ -549,7 +566,9 @@ tsp = ts.pager = { s = ( p.page * p.size ), e = s + p.size, f = wo && wo.filter_filteredRow || 'filtered', + last = 0, // for cache indexing j = 0; // size counter + p.cacheIndex = []; for ( i = 0; i < l; i++ ){ if ( !rows[i].className.match(f) ) { if (j === s && rows[i].className.match(c.cssChildRow)) { @@ -557,6 +576,10 @@ tsp = ts.pager = { rows[i].style.display = 'none'; } else { rows[i].style.display = ( j >= s && j < e ) ? '' : 'none'; + if ( last !== j && j >= s && j < e ) { + p.cacheIndex.push(i); + last = j; + } // don't count child rows j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) && !wo.pager_countChildRows ? 0 : 1; if ( j === e && rows[i].style.display !== 'none' && rows[i].className.match(ts.css.cssHasChild) ) { @@ -576,7 +599,7 @@ tsp = ts.pager = { hideRowsSetup: function(table, c){ var p = c.pager; - p.size = parseInt( p.$size.val(), 10 ) || p.size; + p.size = parseInt( p.$size.val(), 10 ) || p.size || p.setSize || 10; $.data(table, 'pagerLastSize', p.size); tsp.pagerArrows(c); if ( !c.widgetOptions.pager_removeRows ) { @@ -682,12 +705,13 @@ tsp = ts.pager = { } // make sure last pager settings are saved, prevents multiple server side calls with // the same parameters - p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) ); + p.totalPages = Math.ceil( p.totalRows / ( p.size || p.setSize || 10 ) ); p.last.totalRows = p.totalRows; p.last.currentFilters = p.currentFilters; p.last.sortList = (c.sortList || []).join(','); p.initializing = false; - tsp.updatePageDisplay(table, c); + // update display without triggering pager complete... before updating cache + tsp.updatePageDisplay(table, c, false); $t.trigger('updateCache', [function(){ if (p.initialized) { // apply widgets after table has rendered & after a delay to prevent @@ -696,6 +720,7 @@ tsp = ts.pager = { $t .trigger('applyWidgets') .trigger('pagerChange', p); + tsp.updatePageDisplay(table, c); }, 0); } }]); @@ -800,6 +825,7 @@ tsp = ts.pager = { // lets not render the table more than once return tsp.moveToLastPage(table, p); } + p.cacheIndex = []; p.isDisabled = false; // needed because sorting will change the page and re-enable the pager if (p.initialized) { c.$table.trigger('pagerChange', c); } if ( !wo.pager_removeRows ) { @@ -817,6 +843,7 @@ tsp = ts.pager = { count++; if (count > s && added <= e) { added++; + p.cacheIndex.push(index); $tb.append(rows[index]); } } @@ -871,7 +898,8 @@ tsp = ts.pager = { if ( !$.isEmptyObject(table.config.cache) ) { var i, rows = [], - n = table.config.cache[0].normalized; + tbodyIndex = c.$table.children('tbody').index( c.$tbodies.eq(0) ), + n = table.config.cache[tbodyIndex].normalized; p.totalRows = n.length; for (i = 0; i < p.totalRows; i++) { rows.push(n[i][c.columns].$row); @@ -946,7 +974,7 @@ tsp = ts.pager = { setPageSize: function(table, size, c) { var p = c.pager; - p.size = size || p.size || 10; + p.size = size || p.size || p.setSize || 10; p.$size.val(p.size); $.data(table, 'pagerLastPage', p.page); $.data(table, 'pagerLastSize', p.size); @@ -981,14 +1009,15 @@ tsp = ts.pager = { tsp.moveToPage(table, p, true); }, - destroyPager: function(table, c){ + destroyPager: function(table, c, refreshing){ var p = c.pager; + p.initialized = false; + c.$table.off(p.events.split(' ').join('.pager ')); + if (refreshing) { return; } tsp.showAllRows(table, c); p.$container.hide(); // hide pager c.appender = null; // remove pager appender function - p.initialized = false; delete table.config.rowsCopy; - c.$table.off('filterInit filterStart filterEnd sortEnd disable enable destroy updateComplete pageSize pageSet '.split(' ').join('.pager ')); if (ts.storage) { ts.storage(table, c.widgetOptions.pager_storageKey, ''); } @@ -998,7 +1027,7 @@ tsp = ts.pager = { var info, p = c.pager; p.isDisabled = false; p.page = $.data(table, 'pagerLastPage') || p.page || 0; - p.size = $.data(table, 'pagerLastSize') || parseInt(p.$size.find('option[selected]').val(), 10) || p.size || 10; + p.size = $.data(table, 'pagerLastSize') || parseInt(p.$size.find('option[selected]').val(), 10) || p.size || p.setSize || 10; p.$size.val(p.size); // set page size p.totalPages = Math.ceil( Math.min( p.totalRows, p.filteredRows ) / p.size ); c.$table.removeClass('pagerDisabled'); @@ -1026,7 +1055,7 @@ tsp = ts.pager = { if ( !p.ajax ) { c.rowsCopy = rows; p.totalRows = wo.pager_countChildRows ? c.$tbodies.eq(0).children('tr').length : rows.length; - p.size = $.data(table, 'pagerLastSize') || p.size || wo.pager_size || 10; + p.size = $.data(table, 'pagerLastSize') || p.size || wo.pager_size || p.setSize || 10; p.totalPages = Math.ceil( p.totalRows / p.size ); tsp.moveToPage(table, p); // update display here in case all rows are removed diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js index 5b3d883..c7ce509 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js @@ -1,4 +1,4 @@ -/* Print widget (beta) for TableSorter 6/18/2014 (v2.17.2) +/* Print widget for TableSorter 2/7/2015 (v2.19.0) * Requires tablesorter v2.8+ and jQuery 1.2.6+ */ /*jshint browser:true, jquery:true, unused:false */ @@ -55,7 +55,7 @@ printTable = ts.printTable = { // add the definition to the wo.print_extraCSS option if (/s/i.test(wo.print_columns) && c.selector && c.widgets.indexOf('columnSelector') >= 0) { // show selected (visible) columns; make a copy of the columnSelector widget css (not media queries) - printStyle += c.selector.auto ? '' : c.selector.$style.text(); + printStyle += wo.columnSelector_mediaquery && c.selector.auto ? '' : c.selector.$style.text(); } else if (/a/i.test(wo.print_columns)) { // force show all cells printStyle += 'td, th { display: table-cell !important; }'; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-reflow.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-reflow.js index 5e163c9..07bec5b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-reflow.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-reflow.js @@ -1,4 +1,4 @@ -/* table reflow widget (beta) for TableSorter 3/22/2014 (v2.16.0) +/* table reflow widget for TableSorter 2/7/2015 (v2.19.0) * Requires tablesorter v2.8+ and jQuery 1.7+ * Also, this widget requires the following default css (modify as desired) @@ -73,7 +73,7 @@ tablereflow = { }); c.$headers.each(function(){ $this = $(this); - headers.push( $this.attr(header) || $this.text() ); + headers.push( $.trim( $this.attr(header) || $this.text() ) ); }); c.$tbodies.children().each(function(){ $(this).children().each(function(i){ @@ -100,6 +100,7 @@ tablereflow = { $hdr = c.$headers.filter('[data-column="' + i + '"]'); if ($hdr.length > 1) { txt = []; + /*jshint loopfunc:true */ $hdr.each(function(){ $this = $(this); if (!$this.hasClass(wo.reflow2_classIgnore)) { @@ -117,7 +118,7 @@ tablereflow = { $tbody = ts.processTbody(table, $(this), true); $tbody.children().each(function(j){ $this = $(this); - len = headers[j].length + len = headers[j].length; i = len - 1; while (i >= 0) { $this.prepend(txt + (i === 0 && len > 1 ? ' ' + wo.reflow2_labelTop : '') + '">' + headers[j][i] + '</b>'); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js index 3f8afd3..edc684e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js @@ -1,13 +1,13 @@ -/*! tablesorter repeatHeaders widget - updated 10/26/2014 (v2.18.0) +/*! tablesorter repeatHeaders widget - updated 2/7/2015 (v2.19.0) * Requires tablesorter v2.8+ and jQuery 1.7+ * Original by Christian Bach from the example-widgets.html demo */ /*global jQuery: false */ ;(function($){ - "use strict"; + 'use strict'; $.tablesorter.addWidget({ - id: "repeatHeaders", + id: 'repeatHeaders', priority: 10, options: { rowsToSkip : 4 @@ -18,11 +18,12 @@ // cache and collect all TH headers if (!wo.repeatHeaders) { h = '<tr class="repeated-header remove-me">'; - $.each(c.headerContent, function(i,t) { - h += '<th>' + t + '</th>'; - }); - // "remove-me" class was added in case the table needs to be updated, the "remove-me" rows will be - // removed prior to the update to prevent including the rows in the update - see "selectorRemove" option + for (i = 0; i < c.columns; i++) { + // only get the headerContent text + h += '<th>' + $.trim( c.$headers.eq(i).text() ) + '</th>'; + } + // 'remove-me' class was added in case the table needs to be updated, the 'remove-me' rows will be + // removed prior to the update to prevent including the rows in the update - see 'selectorRemove' option wo.repeatHeaders = h + '</tr>'; } @@ -30,10 +31,10 @@ skip = wo && wo.rowsToSkip || 4; // remove appended headers by classname - c.$table.find("tr.repeated-header").remove(); + c.$table.find('tr.repeated-header').remove(); $tr = c.$tbodies.find('tr'); l = $tr.length; - // loop all tr elements and insert a copy of the "headers" + // loop all tr elements and insert a copy of the 'headers' for (i = skip; i < l; i += skip) { // insert a copy of the table head every X rows $tr.eq(i).before(wo.repeatHeaders); @@ -41,8 +42,9 @@ }, // this remove function is called when using the refreshWidgets method or when destroying the tablesorter plugin // this function only applies to tablesorter v2.4+ - remove: function(table, c){ - c.$table.find("tr.repeated-header").remove(); + remove: function(table, c, wo){ + wo.repeatHeaders = ''; + c.$table.find('tr.repeated-header').remove(); } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index 382eb01..5a11d14 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -10,7 +10,7 @@ Resizable scroller widget for the jQuery tablesorter plugin - Version 2.0 - modified by Rob Garrison 4/12/2013; updated 10/26/2014 (v2.18.0) + Version 2.0 - modified by Rob Garrison 4/12/2013; updated 2/7/2015 (v2.19.0) Requires jQuery v1.7+ Requires the tablesorter plugin, v2.8+, available at http://mottie.github.com/tablesorter/docs/ @@ -32,7 +32,7 @@ Website: www.tconnell.com */ /*jshint browser:true, jquery:true, unused:false */ -;(function($){ +;(function($, window){ "use strict"; $.fn.hasScrollBar = function(){ @@ -52,6 +52,7 @@ ts.window_resize = function(){ // Add extra scroller css $(function(){ var s = '<style>' + + '.tablesorter-scrollbar-measure { width: 100px; height: 100px; overflow: scroll; position: absolute; top: -9999px; } ' + '.tablesorter-scroller-reset { width: auto !important; } ' + '.tablesorter-scroller { text-align: left; overflow: hidden; }' + '.tablesorter-scroller-header { overflow: hidden; }' + @@ -70,9 +71,10 @@ ts.addWidget({ priority: 60, // run after the filter widget options: { scroller_height : 300, - scroller_barWidth : 18, scroller_jumpToHeader: true, - scroller_upAfterSort: true + scroller_upAfterSort: true, + // bar width is now calculated; set a value to override + scroller_barWidth : null }, init: function(table, thisWidget, c, wo){ var $win = $(window), @@ -83,7 +85,7 @@ ts.addWidget({ .bind('resizeEnd' + namespace, function() { // init is run before format, so scroller_resizeWidth // won't be defined within the "c" or "wo" parameters - if (typeof table.config.widgetOptions.scroller_resizeWidth === 'function') { + if ($.isFunction(table.config.widgetOptions.scroller_resizeWidth)) { // IE calls resize when you modify content, so we have to unbind the resize event // so we don't end up with an infinite loop. we can rebind after we're done. $win.unbind('resize' + namespace, ts.window_resize); @@ -93,28 +95,37 @@ ts.addWidget({ }); }, format: function(table, c, wo) { - var h, $hdr, t, resize, $cells, + var maxHt, tbHt, $hdr, resize, getBarWidth, $cells, // c.namespace contains a unique tablesorter ID, per table id = c.namespace.slice(1) + 'tsscroller', $win = $(window), $tbl = c.$table; if (!c.isScrolling) { - h = wo.scroller_height || 300; - t = $tbl.find('tbody').height(); - if (t !== 0 && h > t) { h = t + 10; } // Table is less than h px - - $hdr = $('<table class="' + $tbl.attr('class') + '" cellpadding=0 cellspacing=0><thead>' + $tbl.find('thead:first').html() + '</thead></table>'); + maxHt = wo.scroller_height || 300; + tbHt = $tbl.children('tbody').height(); + if (tbHt !== 0 && maxHt > tbHt) { maxHt = tbHt + 10; } // Table is less than h px + + $hdr = $('<table class="' + $tbl.attr('class') + '" cellpadding=0 cellspacing=0>' + + '<thead>' + $tbl.find('thead:first').html() + '</thead>' + + '</table>'); + if (c.$extraTables && c.$extraTables.length) { + c.$extraTables = c.$extraTables.add($hdr); + } else { + c.$extraTables = $hdr; + } $tbl .wrap('<div id="' + id + '" class="tablesorter-scroller" />') .before($hdr) + // shrink filter row but don't completely hide it because the inputs/selectors may distort the columns .find('.tablesorter-filter-row').addClass('hideme'); $cells = $hdr .wrap('<div class="tablesorter-scroller-header" style="width:' + $tbl.width() + ';" />') .find('.' + ts.css.header); - $tbl.wrap('<div class="tablesorter-scroller-table" style="height:' + h + 'px;width:' + $tbl.width() + ';" />'); + // use max-height, so the height resizes dynamically while filtering + $tbl.wrap('<div class="tablesorter-scroller-table" style="max-height:' + maxHt + 'px;width:' + $tbl.width() + ';" />'); // make scroller header sortable ts.bindEvents(table, $cells); @@ -124,37 +135,59 @@ ts.addWidget({ ts.filter.bindSearch( $tbl, $hdr.find('.' + ts.css.filter) ); } + // modified from http://davidwalsh.name/detect-scrollbar-width + getBarWidth = function(){ + var $scrollDiv = $('<div class="tablesorter-scrollbar-measure">').appendTo('body'), + div = $scrollDiv[0], + barWidth = div.offsetWidth - div.clientWidth; + $scrollDiv.remove(); + return barWidth; + }; + resize = function(){ var d, b, $h, $th, w, // Hide other scrollers so we can resize - $div = $('div.scroller[id != "' + id + '"]').hide(); + $div = $('div.tablesorter-scroller[id != "' + id + '"]').hide(); - $tbl.find('thead').show(); + $tbl.children('thead').show(); + // only remove colgroup if it was added by the plugin + // the $.tablesorter.fixColumnWidth() function already does this (v2.19.0) + // but we need to get "accurate" resized measurements here - see issue #680 + $tbl.children('colgroup.tablesorter-colgroup').remove(); + $hdr.children('colgroup').remove(); // Reset sizes so parent can resize. $tbl .addClass('tablesorter-scroller-reset') - .find('thead').find('.tablesorter-header-inner').addClass('tablesorter-scroller-reset'); + .children('thead') + .find('.tablesorter-header-inner').addClass('tablesorter-scroller-reset').end() + .find('.tablesorter-filter-row').show(); d = $tbl.parent(); d.addClass('tablesorter-scroller-reset'); d.parent().trigger('resize'); + // include left & right border widths + b = parseInt( $tbl.css('border-left-width'), 10 ) + parseInt( $tbl.css('border-right-width'), 10 ); + // Shrink a bit to accommodate scrollbar - d.width( d.parent().innerWidth() - ( d.parent().hasScrollBar() ? wo.scroller_barWidth : 0 ) ); - w = d.innerWidth() - ( d.hasScrollBar() ? wo.scroller_barWidth : 0 ); + w = ( wo.scroller_barWidth || getBarWidth() ) + b; + + d.width( d.parent().innerWidth() - ( d.parent().hasScrollBar() ? w : 0 ) ); + w = d.innerWidth() - ( d.hasScrollBar() ? w : 0 ); $tbl.width( w ); $hdr.width( w ); $hdr.parent().width( w ); - $tbl.closest('.tablesorter-scroller').find('.tablesorter-scroller-reset').removeClass('tablesorter-scroller-reset'); + $tbl + .closest('.tablesorter-scroller') + .find('.tablesorter-scroller-reset') + .removeClass('tablesorter-scroller-reset'); - // include left & right border widths - b = parseInt( $tbl.css('border-left-width'), 10 ) + parseInt( $tbl.css('border-right-width'), 10 ); $h = $hdr.find('thead').children().children(); // adjust cloned header to match original table width - includes wrappers, headers, and header inner div - $tbl.find('thead').children().children().each(function(i, c){ + $tbl.children('thead').children().children().each(function(i, c){ $th = $(c).find('.tablesorter-header-inner'); if ($th.length) { // I have no idea why this is in here anymore LOL @@ -164,14 +197,23 @@ ts.addWidget({ } else { w = $th.width(); } + $h.eq(i) - .find('.tablesorter-header-inner').width(w - b) - // set inner width first .parent() .width( $th.parent().width() - b ); } }); + // refresh colgroup & copy to cloned header + $.tablesorter.fixColumnWidth( table ); + $h = $tbl.children('colgroup').clone(); + if ($h.length) { + $hdr.prepend($h); + } + + // hide filter row because filterEnd event fires + $tbl.children('thead').find('.tablesorter-filter-row').hide(); + $div.show(); }; @@ -183,13 +225,13 @@ ts.addWidget({ $tbl.find('thead').css('visibility', 'hidden'); c.isScrolling = true; - t = $tbl.parent().parent().height(); + tbHt = $tbl.parent().parent().height(); // The header will always jump into view if scrolling the table body $tbl.parent().bind('scroll', function(){ if (wo.scroller_jumpToHeader) { var pos = $win.scrollTop() - $hdr.offset().top; - if ($(this).scrollTop() !== 0 && pos < t && pos > 0) { + if ($(this).scrollTop() !== 0 && pos < tbHt && pos > 0) { $win.scrollTop( $hdr.offset().top ); } } @@ -217,4 +259,4 @@ ts.addWidget({ } }); -})(jQuery); +})(jQuery, window); diff --git a/vendor/assets/stylesheets/jquery-tablesorter/dragtable.mod.css b/vendor/assets/stylesheets/jquery-tablesorter/dragtable.mod.css new file mode 100644 index 0000000..6cb7d02 --- /dev/null +++ b/vendor/assets/stylesheets/jquery-tablesorter/dragtable.mod.css @@ -0,0 +1,64 @@ +/* + * dragtable + * @Version 2.0.14 MOD + * default css + */ +.dragtable-sortable { + list-style-type: none; + margin: 0; + padding: 0; + -moz-user-select: none; + z-index: 10; +} +.dragtable-sortable li { + margin: 0; + padding: 0; + float: left; + font-size: 1em; +} +.dragtable-sortable table { + margin-top: 0; +} +.dragtable-sortable th, .dragtable-sortable td { + border-left: 0px; +} +.dragtable-sortable li:first-child th, .dragtable-sortable li:first-child td { + border-left: 1px solid #CCC; +} +.dragtable-handle-selected { + /* table-handle class while actively dragging a column */ +} +.ui-sortable-helper { + opacity: 0.7; + filter: alpha(opacity=70); +} +.ui-sortable-placeholder { + -moz-box-shadow: 4px 5px 4px rgba(0,0,0,0.2) inset; + -webkit-box-shadow: 4px 5px 4px rgba(0,0,0,0.2) inset; + box-shadow: 4px 5px 4px rgba(0,0,0,0.2) inset; + border-bottom: 1px solid rgba(0,0,0,0.2); + border-top: 1px solid rgba(0,0,0,0.2); + visibility: visible !important; + /* change the background color here to match the tablesorter theme */ + background: #EFEFEF; +} +.ui-sortable-placeholder * { + opacity: 0.0; + visibility: hidden; +} +.table-handle, .table-handle-disabled { + /* background-image: url(/assets/jquery-tablesorter/dragtable-handle.png); */ + /* background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAANAQMAAAC5Li2yAAAABlBMVEUAAAAzMzPI8eYgAAAAAnRSTlMAzORBQ6MAAAAOSURBVAjXYwABByyYAQAQWgFBLN2RnwAAAABJRU5ErkJggg=='); */ + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyIiBoZWlnaHQ9IjEzIj48cmVjdCBzdHlsZT0iZmlsbDojMzMzO2ZpbGwtb3BhY2l0eTouODsiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHg9IjEiIHk9IjIiLz4JPHJlY3Qgc3R5bGU9ImZpbGw6IzMzMztmaWxsLW9wYWNpdHk6Ljg7IiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4PSIxIiB5PSI0Ii8+CTxyZWN0IHN0eWxlPSJmaWxsOiMzMzM7ZmlsbC1vcGFjaXR5Oi44OyIgd2lkdGg9IjEiIGhlaWdodD0iMSIgeD0iMSIgeT0iNiIvPjxyZWN0IHN0eWxlPSJmaWxsOiMzMzM7ZmlsbC1vcGFjaXR5Oi44OyIgd2lkdGg9IjEiIGhlaWdodD0iMSIgeD0iMSIgeT0iOCIvPjxyZWN0IHN0eWxlPSJmaWxsOiMzMzM7ZmlsbC1vcGFjaXR5Oi44OyIgd2lkdGg9IjEiIGhlaWdodD0iMSIgeD0iMSIgeT0iMTAiLz48L3N2Zz4=); + background-repeat: repeat-x; + height: 13px; + margin: 0 1px; + cursor: move; +} +.table-handle-disabled { + opacity: 0; + cursor: not-allowed; +} +.dragtable-sortable table { + margin-bottom: 0; +} diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css index 72cfda6..1b38c52 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css @@ -120,6 +120,9 @@ caption { } /* filter widget */ +.tablesorter-blackice .tablesorter-filter-row { + background: #222; +} .tablesorter-blackice .tablesorter-filter-row td { background: #222; line-height: normal; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css index 191efdd..509c2f8 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css @@ -155,6 +155,9 @@ caption { } /* filter widget */ +.tablesorter-blue .tablesorter-filter-row { + background: #eee; +} .tablesorter-blue .tablesorter-filter-row td { background: #eee; line-height: normal; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index dd34d6a..a66b581 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -101,6 +101,9 @@ box-sizing: border-box; transition: height 0.1s ease; } +.tablesorter-bootstrap .tablesorter-filter-row { + background: #efefef; +} .tablesorter-bootstrap .tablesorter-filter-row td { background: #efefef; line-height: normal; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css index d572c2d..d036082 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css @@ -103,6 +103,9 @@ caption { background: #eee; cursor: not-allowed; } +.tablesorter-bootstrap .tablesorter-filter-row { + background: #eee; +} .tablesorter-bootstrap .tablesorter-filter-row td { background: #eee; line-height: normal; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css index 329e508..adca809 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css @@ -119,6 +119,9 @@ caption { } /* filter widget */ +.tablesorter-dark .tablesorter-filter-row { + background: #202020; +} .tablesorter-dark .tablesorter-filter-row td { background: #202020; line-height: normal; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css index d8fec8e..20fc153 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css @@ -122,6 +122,9 @@ caption { } /* filter widget */ +.tablesorter-default .tablesorter-filter-row { + background: #eee; +} .tablesorter-default .tablesorter-filter-row td { background: #eee; border-bottom: #ccc 1px solid; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css index adc388c..dffddf5 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css @@ -144,6 +144,9 @@ caption { } /* Filter Widget */ +.tablesorter-dropbox .tablesorter-filter-row { + background: #fff; +} .tablesorter-dropbox .tablesorter-filter-row td { background: #fff; line-height: normal; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css index 3d8c5aa..af002e5 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css @@ -137,6 +137,9 @@ caption { } /* filter widget */ +.tablesorter-green .tablesorter-filter-row { + background: #eee; +} .tablesorter-green .tablesorter-filter-row td { background: #eee; line-height: normal; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css index b31498a..540c35d 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css @@ -179,6 +179,9 @@ caption { } /* filter widget */ +.tablesorter-grey .tablesorter-filter-row { + background: #3c3c3c; +} .tablesorter-grey .tablesorter-filter-row td { background: #3c3c3c; line-height: normal; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css index 32952c4..afd2ad0 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css @@ -135,6 +135,9 @@ caption { } /* filter widget */ +.tablesorter-ice .tablesorter-filter-row { + background: #eee; +} .tablesorter-ice .tablesorter-filter-row td { background: #eee; line-height: normal; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css index a6f0f92..17e53c3 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css @@ -93,6 +93,9 @@ } /* filter widget */ +.tablesorter-jui .tablesorter-filter-row { + background: transparent; +} .tablesorter-jui .tablesorter-filter-row td { background: transparent; line-height: normal; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css index 4898d49..7649bb8 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css @@ -126,6 +126,9 @@ Metro Dark Theme } /* filter widget */ +.tablesorter-metro-dark .tablesorter-filter-row { + background: #eee; +} .tablesorter-metro-dark .tablesorter-filter-row td { background: #eee; line-height: normal; From c73bb4a53915a1db05642cee8662a22e1c7b7ea8 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Tue, 10 Feb 2015 16:28:20 +0100 Subject: [PATCH 047/138] * updated tablesorter to latest version (2.19.1) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 12 +- .../jquery-tablesorter/jquery.tablesorter.js | 276 +++++++++--------- .../jquery.tablesorter.widgets.js | 35 ++- .../widgets/widget-chart.js | 6 +- .../widgets/widget-cssStickyHeaders.js | 12 +- .../widgets/widget-editable.js | 10 +- .../widgets/widget-formatter.js | 14 +- .../jquery-tablesorter/widgets/widget-math.js | 21 +- .../widgets/widget-output.js | 2 +- .../widgets/widget-pager.js | 12 +- .../widgets/widget-staticRow.js | 17 +- 15 files changed, 224 insertions(+), 203 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2b2cc2..e8130f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.14.1 (2015-02-10) + +* Upgrade tablesorter to v2.19.1 + #### v1.14.0 (2015-02-07) * Upgrade tablesorter to v2.19.0 diff --git a/README.md b/README.md index 7a360c3..8c6557e 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.19.0 (2/7/2015), [documentation] +Current tablesorter version: 2.19.1 (2/9/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index a46c0f3..19d5335 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.14.0' + VERSION = '1.14.1' end diff --git a/tablesorter b/tablesorter index 9b32f0c..c4a84f3 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 9b32f0c5f6a09084143214f460323de4cfa1a90f +Subproject commit c4a84f3dd513063856c9b976afc4396e672a886d diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 9090b81..a921d01 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin - * updated 2/7/2015 (v2.19.0) + * updated 2/9/2015 (v2.19.1) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -204,7 +204,7 @@ if ($out.length) { $out[ ($out[0].tagName === 'INPUT') ? 'val' : 'html' ](s); // rebind startRow/page inputs - $out.find('.ts-startRow, .ts-page').unbind('change').bind('change', function(){ + $out.find('.ts-startRow, .ts-page').unbind('change.pager').bind('change.pager', function(){ var v = $(this).val(), pg = $(this).hasClass('ts-startRow') ? Math.floor( v/p.size ) + 1 : v; c.$table.trigger('pageSet.pager', [ pg ]); @@ -768,7 +768,7 @@ table.config.appender = null; // remove pager appender function p.initialized = false; delete table.config.rowsCopy; - $(table).unbind(pagerEvents.split(' ').join('.pager ')); + $(table).unbind( $.trim(pagerEvents.split(' ').join('.pager ')) ); if (ts.storage) { ts.storage(table, p.storageKey, ''); } @@ -848,7 +848,7 @@ p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.slice(1) + '|' + c.cssChildRow + ')'); $t - .unbind(pagerEvents.split(' ').join('.pager ')) + .unbind( $.trim(pagerEvents.split(' ').join('.pager ')) ) .bind('filterInit.pager filterStart.pager', function(e, filters) { p.currentFilters = $.isArray(filters) ? filters : c.$table.data('lastSearch'); // don't change page if filters are the same (pager updating, etc) @@ -945,8 +945,8 @@ p.$goto = pager.find(p.cssGoto); if ( p.$goto.length ) { p.$goto - .unbind('change') - .bind('change', function(){ + .unbind('change.pager') + .bind('change.pager', function(){ p.page = $(this).val() - 1; moveToPage(table, p, true); updatePageDisplay(table, p, false); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index ef584c3..43158fd 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,5 +1,5 @@ /**! -* TableSorter (FORK) 2.19.0 - Client-side table sorting with ease! +* TableSorter (FORK) 2.19.1 - Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach @@ -25,14 +25,14 @@ factory(jQuery); } }(function($) { - "use strict"; + 'use strict'; $.extend({ /*jshint supernew:true */ tablesorter: new function() { var ts = this; - ts.version = "2.19.0"; + ts.version = '2.19.1'; ts.parsers = []; ts.widgets = []; @@ -50,13 +50,13 @@ // *** functionality cancelSelection : true, // prevent text selection in the header tabIndex : true, // add tabindex to header for keyboard accessibility - dateFormat : 'mmddyyyy', // other options: "ddmmyyy" or "yyyymmdd" + dateFormat : 'mmddyyyy', // other options: 'ddmmyyy' or 'yyyymmdd' sortMultiSortKey : 'shiftKey', // key used to select additional columns sortResetKey : 'ctrlKey', // key used to remove sorting on a column - usNumberFormat : true, // false for German "1.234.567,89" or French "1 234 567,89" + usNumberFormat : true, // false for German '1.234.567,89' or French '1 234 567,89' delayInit : false, // if false, the parsed table contents will not update until the first sort serverSideSorting: false, // if true, server-side sorting should be performed because client-side sorting will be disabled, but the ui and events will still be used. - resort : true, // default setting to trigger a resort after an "update", "addRows", "updateCell", etc has completed + resort : true, // default setting to trigger a resort after an 'update', 'addRows', 'updateCell', etc has completed // *** sort options headers : {}, // set sorter, string, empty, locked order, sortInitialOrder, filter, etc. @@ -69,7 +69,7 @@ sortInitialOrder : 'asc', // sort direction on first click sortLocaleCompare: false, // replace equivalent character (accented characters) sortReset : false, // third click on the header will reset column to default - unsorted - sortRestart : false, // restart sort to "sortInitialOrder" when clicking on previously unsorted columns + sortRestart : false, // restart sort to 'sortInitialOrder' when clicking on previously unsorted columns emptyTo : 'bottom', // sort empty cell to bottom, top, none, zero, emptyMax, emptyMin stringTo : 'max', // sort strings in numerical column as max, min, top, bottom, zero @@ -121,7 +121,7 @@ strings: {}, parsers: [] - // removed: widgetZebra: { css: ["even", "odd"] } + // removed: widgetZebra: { css: ['even', 'odd'] } }; @@ -157,7 +157,7 @@ function log() { var a = arguments[0], s = arguments.length > 1 ? Array.prototype.slice.call(arguments) : a; - if (typeof console !== "undefined" && typeof console.log !== "undefined") { + if (typeof console !== 'undefined' && typeof console.log !== 'undefined') { console[ /error/i.test(a) ? 'error' : /warn/i.test(a) ? 'warn' : 'log' ](s); } else { alert(s); @@ -165,7 +165,7 @@ } function benchmark(s, d) { - log(s + " (" + (new Date().getTime() - d.getTime()) + "ms)"); + log(s + ' (' + (new Date().getTime() - d.getTime()) + 'ms)'); } ts.log = log; @@ -187,7 +187,7 @@ c = table.config, t = c.textExtraction || ''; if (typeof(t) === 'string') { - // check data-attribute first when set to "basic"; don't use node.innerText - it's really slow! + // check data-attribute first when set to 'basic'; don't use node.innerText - it's really slow! return $.trim( (t === 'basic' ? $node.attr(c.textAttribute) || node.textContent : node.textContent ) || $node.text() || '' ); } else { if (typeof(t) === 'function') { @@ -236,7 +236,7 @@ tb = c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'), rows, list, l, i, h, ch, np, p, e, time, j = 0, - parsersDebug = "", + parsersDebug = '', len = tb.length; if ( len === 0) { return c.debug ? log('Warning: *Empty table!* Not building a parser cache') : ''; @@ -275,7 +275,7 @@ p = detectParserForColumn(table, rows, -1, i); } if (c.debug) { - parsersDebug += "column:" + i + "; extractor:" + e.id + "; parser:" + p.id + "; string:" + c.strings[i] + '; empty: ' + c.empties[i] + "\n"; + parsersDebug += 'column:' + i + '; extractor:' + e.id + '; parser:' + p.id + '; string:' + c.strings[i] + '; empty: ' + c.empties[i] + '\n'; } list.parsers[i] = p; list.extractors[i] = e; @@ -284,8 +284,8 @@ j += (list.parsers.length) ? len : 1; } if (c.debug) { - log(parsersDebug ? parsersDebug : "No parsers detected"); - benchmark("Completed detecting parsers", time); + log(parsersDebug ? parsersDebug : 'No parsers detected'); + benchmark('Completed detecting parsers', time); } c.parsers = list.parsers; c.extractors = list.extractors; @@ -315,7 +315,7 @@ for (k = 0; k < $tb.length; k++) { colMax = []; // column max value per tbody cc = c.cache[k] = { - normalized: [] // array of normalized row data; last entry contains "rowData" above + normalized: [] // array of normalized row data; last entry contains 'rowData' above // colMax: # // added at the end }; @@ -326,7 +326,8 @@ rowData = { // order: original row order # // $row : jQuery Object[] - child: [] // child row text (filter widget) + child: [], // child row text (filter widget) + raw: [] // original row text }; /** Add the table data to main data array */ $row = $($tb[k].rows[i]); @@ -337,12 +338,12 @@ if ($row.hasClass(c.cssChildRow) && i !== 0) { t = cc.normalized.length - 1; cc.normalized[t][c.columns].$row = cc.normalized[t][c.columns].$row.add($row); - // add "hasChild" class name to parent row + // add 'hasChild' class name to parent row if (!$row.prev().hasClass(c.cssChildRow)) { $row.prev().addClass(ts.css.cssHasChild); } // save child row content (un-parsed!) - rowData.child[t] = $.trim( $row[0].textContent || $row[0].innerText || $row.text() || "" ); + rowData.child[t] = $.trim( $row[0].textContent || $row.text() || '' ); // go to the next for loop continue; } @@ -356,6 +357,7 @@ continue; } t = getElementText(table, $row[0].cells[j], j); + rowData.raw.push(t); // save original row text // do extract before parsing if there is one if (typeof extractors[j].id === 'undefined') { tx = t; @@ -366,7 +368,7 @@ // in case the parser needs to extract data from the table cell attributes v = parsers[j].id === 'no-parser' ? '' : parsers[j].format(tx, table, $row[0].cells[j], j); cols.push( c.ignoreCase && typeof v === 'string' ? v.toLowerCase() : v ); - if ((parsers[j].type || '').toLowerCase() === "numeric") { + if ((parsers[j].type || '').toLowerCase() === 'numeric') { // determine column max value (ignore sign) colMax[j] = Math.max(Math.abs(v) || 0, colMax[j] || 0); } @@ -384,7 +386,7 @@ ts.isProcessing(table); // remove processing icon } if (c.debug) { - benchmark("Building cache for " + totalRows + " rows", cacheTime); + benchmark('Building cache for ' + totalRows + ' rows', cacheTime); } } @@ -401,7 +403,7 @@ if (isEmptyObject(cc)) { // run pager appender in case the table was just emptied return c.appender ? c.appender(table, rows) : - table.isUpdating ? c.$table.trigger("updateComplete", table) : ''; // Fixes #532 + table.isUpdating ? c.$table.trigger('updateComplete', table) : ''; // Fixes #532 } if (c.debug) { appendTime = new Date(); @@ -428,17 +430,17 @@ c.appender(table, rows); } if (c.debug) { - benchmark("Rebuilt table", appendTime); + benchmark('Rebuilt table', appendTime); } // apply table widgets; but not before ajax completes if (!init && !c.appender) { ts.applyWidget(table); } if (table.isUpdating) { - c.$table.trigger("updateComplete", table); + c.$table.trigger('updateComplete', table); } } function formatSortingOrder(v) { - // look for "d" in "desc" order; return true + // look for 'd' in 'desc' order; return true return (/^d/i.test(v) || v === 1); } @@ -490,7 +492,7 @@ // add to parent in case there are multiple rows $t.parent().addClass(ts.css.headerRow + ' ' + c.cssHeaderRow).attr('role', 'row'); // allow keyboard cursor to focus on element - if (c.tabIndex) { $t.attr("tabindex", 0); } + if (c.tabIndex) { $t.attr('tabindex', 0); } return elem; })); $(table).find(c.selectorHeaders).attr({ @@ -500,7 +502,7 @@ // enable/disable sorting updateHeader(table); if (c.debug) { - benchmark("Built headers:", time); + benchmark('Built headers:', time); log(c.$headers); } } @@ -522,7 +524,7 @@ c.$headers.each(function(index, th){ $th = $(th); col = ts.getColumnData( table, c.headers, index, true ); - // add "sorter-false" class if "parser-false" is set + // add 'sorter-false' class if 'parser-false' is set s = ts.getData( th, col, 'sorter' ) === 'false' || ts.getData( th, col, 'parser' ) === 'false'; th.sortDisabled = s; $th[ s ? 'addClass' : 'removeClass' ]('sorter-false').attr('aria-disabled', '' + s); @@ -610,7 +612,7 @@ t = 1; break; case 's': // same direction (as primary column) - // if primary sort is set to "s", make it ascending + // if primary sort is set to 's', make it ascending t = primary || 0; break; case 'o': @@ -649,7 +651,7 @@ key = !event[c.sortMultiSortKey], $table = c.$table; // Only call sortStart if sorting is enabled - $table.trigger("sortStart", table); + $table.trigger('sortStart', table); // get current column sort order cell.count = event[c.sortResetKey] ? 2 : (cell.count + 1) % (c.sortReset ? 3 : 2); // reset all sorts on non-current column - issue #30 @@ -736,14 +738,14 @@ } } // sortBegin event triggered immediately before the sort - $table.trigger("sortBegin", table); + $table.trigger('sortBegin', table); // setTimeout needed so the processing icon shows up setTimeout(function(){ // set css for headers setHeadersCss(table); multisort(table); appendToTable(table); - $table.trigger("sortEnd", table); + $table.trigger('sortEnd', table); }, 1); } @@ -787,7 +789,7 @@ num = (c.strings[col]) ? c.string[c.strings[col]] || 0 : 0; } // fall back to built-in numeric sort - // var sort = $.tablesorter["sort" + s](table, a[c], b[c], c, colMax[c], dir); + // var sort = $.tablesorter['sort' + s](table, a[c], b[c], c, colMax[c], dir); sort = c.numberSorter ? c.numberSorter(a[col], b[col], dir, colMax[col], table) : ts[ 'sortNumeric' + (dir ? 'Asc' : 'Desc') ](a[col], b[col], num, colMax[col], col, table); } else { @@ -811,7 +813,7 @@ return a[c.columns].order - b[c.columns].order; }); } - if (c.debug) { benchmark("Sorting on " + sortList.toString() + " and dir " + order + " time", sortTime); } + if (c.debug) { benchmark('Sorting on ' + sortList.toString() + ' and dir ' + order + ' time', sortTime); } } function resortComplete(c, callback){ @@ -848,11 +850,14 @@ function bindMethods(table){ var c = table.config, - $table = c.$table; + $table = c.$table, + events = 'sortReset update updateRows updateCell updateAll addRows updateComplete sorton appendCache ' + + 'updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave '.split(' ') + .join(c.namespace + ' '); // apply easy methods that trigger bound events $table - .unbind('sortReset update updateRows updateCell updateAll addRows updateComplete sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave '.split(' ').join(c.namespace + ' ')) - .bind("sortReset" + c.namespace, function(e, callback){ + .unbind( $.trim(events) ) + .bind('sortReset' + c.namespace, function(e, callback){ e.stopPropagation(); c.sortList = []; setHeadersCss(table); @@ -862,7 +867,7 @@ callback(table); } }) - .bind("updateAll" + c.namespace, function(e, resort, callback){ + .bind('updateAll' + c.namespace, function(e, resort, callback){ e.stopPropagation(); table.isUpdating = true; ts.refreshWidgets(table, true, true); @@ -871,14 +876,14 @@ bindMethods(table); commonUpdate(table, resort, callback); }) - .bind("update" + c.namespace + " updateRows" + c.namespace, function(e, resort, callback) { + .bind('update' + c.namespace + ' updateRows' + c.namespace, function(e, resort, callback) { e.stopPropagation(); table.isUpdating = true; // update sorting (if enabled/disabled) updateHeader(table); commonUpdate(table, resort, callback); }) - .bind("updateCell" + c.namespace, function(e, cell, resort, callback) { + .bind('updateCell' + c.namespace, function(e, cell, resort, callback) { e.stopPropagation(); table.isUpdating = true; $table.find(c.selectorRemove).remove(); @@ -904,7 +909,7 @@ v = c.parsers[icell].id === 'no-parser' ? '' : c.parsers[icell].format( t, table, cell, icell ); c.cache[tbdy].normalized[row][icell] = c.ignoreCase && typeof v === 'string' ? v.toLowerCase() : v; - if ((c.parsers[icell].type || '').toLowerCase() === "numeric") { + if ((c.parsers[icell].type || '').toLowerCase() === 'numeric') { // update column max value (ignore sign) c.cache[tbdy].colMax[icell] = Math.max(Math.abs(v) || 0, c.cache[tbdy].colMax[icell] || 0); } @@ -922,7 +927,7 @@ } } }) - .bind("addRows" + c.namespace, function(e, $row, resort, callback) { + .bind('addRows' + c.namespace, function(e, $row, resort, callback) { e.stopPropagation(); table.isUpdating = true; if (isEmptyObject(c.cache)) { @@ -957,7 +962,7 @@ v = c.parsers[j].id === 'no-parser' ? '' : c.parsers[j].format( t, table, $row[i].cells[j], j ); cells[j] = c.ignoreCase && typeof v === 'string' ? v.toLowerCase() : v; - if ((c.parsers[j].type || '').toLowerCase() === "numeric") { + if ((c.parsers[j].type || '').toLowerCase() === 'numeric') { // update column max value (ignore sign) c.cache[tbdy].colMax[j] = Math.max(Math.abs(cells[j]) || 0, c.cache[tbdy].colMax[j] || 0); } @@ -971,37 +976,37 @@ checkResort(c, resort, callback); } }) - .bind("updateComplete" + c.namespace, function(){ + .bind('updateComplete' + c.namespace, function(){ table.isUpdating = false; }) - .bind("sorton" + c.namespace, function(e, list, callback, init) { + .bind('sorton' + c.namespace, function(e, list, callback, init) { var c = table.config; e.stopPropagation(); - $table.trigger("sortStart", this); + $table.trigger('sortStart', this); // update header count index updateHeaderSortCount(table, list); // set css for headers setHeadersCss(table); // fixes #346 if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); } - $table.trigger("sortBegin", this); + $table.trigger('sortBegin', this); // sort the table and append it to the dom multisort(table); appendToTable(table, init); - $table.trigger("sortEnd", this); + $table.trigger('sortEnd', this); ts.applyWidget(table); if ($.isFunction(callback)) { callback(table); } }) - .bind("appendCache" + c.namespace, function(e, callback, init) { + .bind('appendCache' + c.namespace, function(e, callback, init) { e.stopPropagation(); appendToTable(table, init); if ($.isFunction(callback)) { callback(table); } }) - .bind("updateCache" + c.namespace, function(e, callback){ + .bind('updateCache' + c.namespace, function(e, callback){ // rebuild parsers if (!(c.parsers && c.parsers.length)) { buildParserCache(table); @@ -1012,24 +1017,24 @@ callback(table); } }) - .bind("applyWidgetId" + c.namespace, function(e, id) { + .bind('applyWidgetId' + c.namespace, function(e, id) { e.stopPropagation(); ts.getWidgetById(id).format(table, c, c.widgetOptions); }) - .bind("applyWidgets" + c.namespace, function(e, init) { + .bind('applyWidgets' + c.namespace, function(e, init) { e.stopPropagation(); // apply widgets ts.applyWidget(table, init); }) - .bind("refreshWidgets" + c.namespace, function(e, all, dontapply){ + .bind('refreshWidgets' + c.namespace, function(e, all, dontapply){ e.stopPropagation(); ts.refreshWidgets(table, all, dontapply); }) - .bind("destroy" + c.namespace, function(e, c, cb){ + .bind('destroy' + c.namespace, function(e, c, cb){ e.stopPropagation(); ts.destroy(table, c, cb); }) - .bind("resetToLoadState" + c.namespace, function(){ + .bind('resetToLoadState' + c.namespace, function(){ // remove all widgets ts.removeWidget(table, true, false); // restore original settings; this clears out current settings, but does not clear @@ -1075,14 +1080,14 @@ // make sure to store the config object table.config = c; // save the settings where they read - $.data(table, "tablesorter", c); + $.data(table, 'tablesorter', c); if (c.debug) { $.data( table, 'startoveralltimer', new Date()); } // removing this in version 3 (only supports jQuery 1.7+) c.supportsDataObject = (function(version) { version[0] = parseInt(version[0], 10); return (version[0] > 1) || (version[0] === 1 && parseInt(version[1], 10) >= 4); - })($.fn.jquery.split(".")); + })($.fn.jquery.split('.')); // digit sort text location; keeping max+/- for backwards compatibility c.string = { 'max': 1, 'min': -1, 'emptymin': 1, 'emptymax': -1, 'zero': 0, 'none': 0, 'null': 0, 'top': true, 'bottom': false }; // ensure case insensitivity @@ -1145,7 +1150,7 @@ ts.applyWidget(table, true); // if user has supplied a sort list to constructor if (c.sortList.length > 0) { - $table.trigger("sorton", [c.sortList, {}, !c.initWidgets, true]); + $table.trigger('sorton', [c.sortList, {}, !c.initWidgets, true]); } else { setHeadersCss(table); if (c.initWidgets) { @@ -1173,7 +1178,7 @@ table.hasInitialized = true; table.isProcessing = false; if (c.debug) { - ts.benchmark("Overall initialization time", $.data( table, 'startoveralltimer')); + ts.benchmark('Overall initialization time', $.data( table, 'startoveralltimer')); } $table.trigger('tablesorter-initialized', table); if (typeof c.initialized === 'function') { c.initialized(table); } @@ -1239,15 +1244,15 @@ cell = cells[j]; $cell = $(cell); rowIndex = cell.parentNode.rowIndex; - cellId = rowIndex + "-" + $cell.index(); + cellId = rowIndex + '-' + $cell.index(); rowSpan = cell.rowSpan || 1; colSpan = cell.colSpan || 1; - if (typeof(matrix[rowIndex]) === "undefined") { + if (typeof(matrix[rowIndex]) === 'undefined') { matrix[rowIndex] = []; } // Find first available column in the first row for (k = 0; k < matrix[rowIndex].length + 1; k++) { - if (typeof(matrix[rowIndex][k]) === "undefined") { + if (typeof(matrix[rowIndex][k]) === 'undefined') { firstAvailCol = k; break; } @@ -1257,12 +1262,12 @@ // add data-column $cell.attr({ 'data-column' : firstAvailCol }); // 'data-row' : rowIndex for (k = rowIndex; k < rowIndex + rowSpan; k++) { - if (typeof(matrix[k]) === "undefined") { + if (typeof(matrix[k]) === 'undefined') { matrix[k] = []; } matrixrow = matrix[k]; for (l = firstAvailCol; l < firstAvailCol + colSpan; l++) { - matrixrow[l] = "x"; + matrixrow[l] = 'x'; } } } @@ -1325,8 +1330,8 @@ $headers // http://stackoverflow.com/questions/5312849/jquery-find-self; .find(c.selectorSort).add( $headers.filter(c.selectorSort) ) - .unbind('mousedown mouseup sort keyup '.split(' ').join(c.namespace + ' ')) - .bind('mousedown mouseup sort keyup '.split(' ').join(c.namespace + ' '), function(e, external) { + .unbind( $.trim('mousedown mouseup sort keyup '.split(' ').join(c.namespace + ' ')) ) + .bind( $.trim('mousedown mouseup sort keyup '.split(' ').join(c.namespace + ' ')), function(e, external) { var cell, type = e.type; // only recognize left clicks or enter if ( ((e.which || e.button) !== 1 && !/sort|keyup/.test(type)) || (type === 'keyup' && e.which !== 13) ) { @@ -1382,10 +1387,12 @@ if (!table.hasInitialized) { return; } // remove all widgets ts.removeWidget(table, true, false); - var $t = $(table), c = table.config, - $h = $t.find('thead:first'), - $r = $h.find('tr.' + ts.css.headerRow).removeClass(ts.css.headerRow + ' ' + c.cssHeaderRow), - $f = $t.find('tfoot:first > tr').children('th, td'); + var events, + $t = $(table), + c = table.config, + $h = $t.find('thead:first'), + $r = $h.find('tr.' + ts.css.headerRow).removeClass(ts.css.headerRow + ' ' + c.cssHeaderRow), + $f = $t.find('tfoot:first > tr').children('th, td'); if (removeClasses === false && $.inArray('uitheme', c.widgets) >= 0) { // reapply uitheme classes, in case we want to maintain appearance $t.trigger('applyWidgetId', ['uitheme']); @@ -1394,15 +1401,18 @@ // remove widget added rows, just in case $h.find('tr').not($r).remove(); // disable tablesorter + events = 'sortReset update updateAll updateRows updateCell addRows updateComplete sorton appendCache updateCache ' + + 'applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress sortBegin sortEnd resetToLoadState '.split(' ') + .join(c.namespace + ' '); $t .removeData('tablesorter') - .unbind('sortReset update updateAll updateRows updateCell addRows updateComplete sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress sortBegin sortEnd resetToLoadState '.split(' ').join(c.namespace + ' ')); + .unbind( $.trim(events) ); c.$headers.add($f) .removeClass( [ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone].join(' ') ) .removeAttr('data-column') .removeAttr('aria-label') .attr('aria-disabled', 'true'); - $r.find(c.selectorSort).unbind('mousedown mouseup keypress '.split(' ').join(c.namespace + ' ')); + $r.find(c.selectorSort).unbind( $.trim('mousedown mouseup keypress '.split(' ').join(c.namespace + ' ')) ); ts.restoreHeaders(table); $t.toggleClass(ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme, removeClasses === false); // clear flag in case the plugin is initialized again @@ -1422,7 +1432,7 @@ }; // Natural sort - https://github.com/overset/javascript-natural-sort (date sorting removed) - // this function will only accept strings, or you'll see "TypeError: undefined is not a function" + // this function will only accept strings, or you'll see 'TypeError: undefined is not a function' // I could add a = a.toString(); b = b.toString(); but it'll slow down the sort overall ts.sortNatural = function(a, b) { if (a === b) { return 0; } @@ -1521,20 +1531,20 @@ // used when replacing accented characters during sorting ts.characterEquivalents = { - "a" : "\u00e1\u00e0\u00e2\u00e3\u00e4\u0105\u00e5", // áàâãäąå - "A" : "\u00c1\u00c0\u00c2\u00c3\u00c4\u0104\u00c5", // ÁÀÂÃÄĄÅ - "c" : "\u00e7\u0107\u010d", // çćč - "C" : "\u00c7\u0106\u010c", // ÇĆČ - "e" : "\u00e9\u00e8\u00ea\u00eb\u011b\u0119", // éèêëěę - "E" : "\u00c9\u00c8\u00ca\u00cb\u011a\u0118", // ÉÈÊËĚĘ - "i" : "\u00ed\u00ec\u0130\u00ee\u00ef\u0131", // íìİîïı - "I" : "\u00cd\u00cc\u0130\u00ce\u00cf", // ÍÌİÎÏ - "o" : "\u00f3\u00f2\u00f4\u00f5\u00f6", // óòôõö - "O" : "\u00d3\u00d2\u00d4\u00d5\u00d6", // ÓÒÔÕÖ - "ss": "\u00df", // ß (s sharp) - "SS": "\u1e9e", // ẞ (Capital sharp s) - "u" : "\u00fa\u00f9\u00fb\u00fc\u016f", // úùûüů - "U" : "\u00da\u00d9\u00db\u00dc\u016e" // ÚÙÛÜŮ + 'a' : '\u00e1\u00e0\u00e2\u00e3\u00e4\u0105\u00e5', // áàâãäąå + 'A' : '\u00c1\u00c0\u00c2\u00c3\u00c4\u0104\u00c5', // ÁÀÂÃÄĄÅ + 'c' : '\u00e7\u0107\u010d', // çćč + 'C' : '\u00c7\u0106\u010c', // ÇĆČ + 'e' : '\u00e9\u00e8\u00ea\u00eb\u011b\u0119', // éèêëěę + 'E' : '\u00c9\u00c8\u00ca\u00cb\u011a\u0118', // ÉÈÊËĚĘ + 'i' : '\u00ed\u00ec\u0130\u00ee\u00ef\u0131', // íìİîïı + 'I' : '\u00cd\u00cc\u0130\u00ce\u00cf', // ÍÌİÎÏ + 'o' : '\u00f3\u00f2\u00f4\u00f5\u00f6', // óòôõö + 'O' : '\u00d3\u00d2\u00d4\u00d5\u00d6', // ÓÒÔÕÖ + 'ss': '\u00df', // ß (s sharp) + 'SS': '\u1e9e', // ẞ (Capital sharp s) + 'u' : '\u00fa\u00f9\u00fb\u00fc\u016f', // úùûüů + 'U' : '\u00da\u00d9\u00db\u00dc\u016e' // ÚÙÛÜŮ }; ts.replaceAccents = function(s) { var a, acc = '[', eq = ts.characterEquivalents; @@ -1623,7 +1633,7 @@ if (init !== false && table.hasInitialized && (table.isApplyingWidgets || table.isUpdating)) { return; } if (c.debug) { time = new Date(); } // look for widgets to apply from in table class - // stop using \b otherwise this matches "ui-widget-content" & adds "content" widget + // stop using \b otherwise this matches 'ui-widget-content' & adds 'content' widget wd = new RegExp( '\\s' + c.widgetClass.replace( /\{name\}/i, '([\\w-]+)' )+ '\\s', 'g' ); if ( tableClass.match( wd ) ) { // extract out the widget id from the table class (widget id's can include dashes) @@ -1686,7 +1696,7 @@ }, 0); if (c.debug) { w = c.widgets.length; - benchmark("Completed " + (init === true ? "initializing " : "applying ") + w + " widget" + (w !== 1 ? "s" : ""), time); + benchmark('Completed ' + (init === true ? 'initializing ' : 'applying ') + w + ' widget' + (w !== 1 ? 's' : ''), time); } }; @@ -1751,7 +1761,7 @@ }; // get sorter, string, empty, etc options for each column from - // jQuery data, metadata, header option or header class name ("sorter-false") + // jQuery data, metadata, header option or header class name ('sorter-false') // priority = jQuery data > meta > headers option > header class name ts.getData = function(h, ch, key) { var val = '', $h = $(h), m, cl; @@ -1759,15 +1769,15 @@ m = $.metadata ? $h.metadata() : false; cl = ' ' + ($h.attr('class') || ''); if (typeof $h.data(key) !== 'undefined' || typeof $h.data(key.toLowerCase()) !== 'undefined'){ - // "data-lockedOrder" is assigned to "lockedorder"; but "data-locked-order" is assigned to "lockedOrder" - // "data-sort-initial-order" is assigned to "sortInitialOrder" + // 'data-lockedOrder' is assigned to 'lockedorder'; but 'data-locked-order' is assigned to 'lockedOrder' + // 'data-sort-initial-order' is assigned to 'sortInitialOrder' val += $h.data(key) || $h.data(key.toLowerCase()); } else if (m && typeof m[key] !== 'undefined') { val += m[key]; } else if (ch && typeof ch[key] !== 'undefined') { val += ch[key]; } else if (cl !== ' ' && cl.match(' ' + key + '-')) { - // include sorter class name "sorter-text", etc; now works with "sorter-my-custom-parser" + // include sorter class name 'sorter-text', etc; now works with 'sorter-my-custom-parser' val = cl.match( new RegExp('\\s' + key + '-([\\w-]+)') )[1] || ''; } return $.trim(val); @@ -1778,7 +1788,7 @@ // allow using formatFloat without a table; defaults to US number format var i, t = table && table.config ? table.config.usNumberFormat !== false : - typeof table !== "undefined" ? table : true; + typeof table !== 'undefined' ? table : true; if (t) { // US Format - 1,234,567.89 -> 1234567.89 s = s.replace(/,/g,''); @@ -1825,7 +1835,7 @@ }); ts.addParser({ - id: "text", + id: 'text', is: function() { return true; }, @@ -1837,35 +1847,35 @@ } return s; }, - type: "text" + type: 'text' }); ts.addParser({ - id: "digit", + id: 'digit', is: function(s) { return ts.isDigit(s); }, format: function(s, table) { - var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ""), table); + var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ''), table); return s && typeof n === 'number' ? n : s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; }, - type: "numeric" + type: 'numeric' }); ts.addParser({ - id: "currency", + id: 'currency', is: function(s) { return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test((s || '').replace(/[+\-,. ]/g,'')); // £$€¤¥¢ }, format: function(s, table) { - var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ""), table); + var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ''), table); return s && typeof n === 'number' ? n : s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; }, - type: "numeric" + type: 'numeric' }); ts.addParser({ - id: "url", + id: 'url', is: function(s) { return (/^(https?|ftp|file):\/\//).test(s); }, @@ -1873,35 +1883,35 @@ return s ? $.trim(s.replace(/(https?|ftp|file):\/\//, '')) : s; }, parsed : true, // filter widget flag - type: "text" + type: 'text' }); ts.addParser({ - id: "isoDate", + id: 'isoDate', is: function(s) { return (/^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}/).test(s); }, format: function(s, table) { - var date = s ? new Date( s.replace(/-/g, "/") ) : s; + var date = s ? new Date( s.replace(/-/g, '/') ) : s; return date instanceof Date && isFinite(date) ? date.getTime() : s; }, - type: "numeric" + type: 'numeric' }); ts.addParser({ - id: "percent", + id: 'percent', is: function(s) { return (/(\d\s*?%|%\s*?\d)/).test(s) && s.length < 15; }, format: function(s, table) { - return s ? ts.formatFloat(s.replace(/%/g, ""), table) : s; + return s ? ts.formatFloat(s.replace(/%/g, ''), table) : s; }, - type: "numeric" + type: 'numeric' }); // added image parser to core v2.17.9 ts.addParser({ - id: "image", + id: 'image', is: function(s, table, node, $node){ return $node.find('img').length > 0; }, @@ -1909,65 +1919,65 @@ return $(cell).find('img').attr(table.config.imgAttr || 'alt') || s; }, parsed : true, // filter widget flag - type: "text" + type: 'text' }); ts.addParser({ - id: "usLongDate", + id: 'usLongDate', is: function(s) { // two digit years are not allowed cross-browser // Jan 01, 2013 12:34:56 PM or 01 Jan 2013 return (/^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i).test(s) || (/^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i).test(s); }, format: function(s, table) { - var date = s ? new Date( s.replace(/(\S)([AP]M)$/i, "$1 $2") ) : s; + var date = s ? new Date( s.replace(/(\S)([AP]M)$/i, '$1 $2') ) : s; return date instanceof Date && isFinite(date) ? date.getTime() : s; }, - type: "numeric" + type: 'numeric' }); ts.addParser({ - id: "shortDate", // "mmddyyyy", "ddmmyyyy" or "yyyymmdd" + id: 'shortDate', // 'mmddyyyy', 'ddmmyyyy' or 'yyyymmdd' is: function(s) { // testing for ##-##-#### or ####-##-##, so it's not perfect; time can be included - return (/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/).test((s || '').replace(/\s+/g," ").replace(/[\-.,]/g, "/")); + return (/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/).test((s || '').replace(/\s+/g,' ').replace(/[\-.,]/g, '/')); }, format: function(s, table, cell, cellIndex) { if (s) { var date, d, c = table.config, - ci = c.$headers.filter('[data-column=' + cellIndex + ']:last'), + ci = c.$headers.filter('[data-column="' + cellIndex + '"]:last'), format = ci.length && ci[0].dateFormat || ts.getData( ci, ts.getColumnData( table, c.headers, cellIndex ), 'dateFormat') || c.dateFormat; - d = s.replace(/\s+/g," ").replace(/[\-.,]/g, "/"); // escaped - because JSHint in Firefox was showing it as an error - if (format === "mmddyyyy") { - d = d.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$3/$1/$2"); - } else if (format === "ddmmyyyy") { - d = d.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$3/$2/$1"); - } else if (format === "yyyymmdd") { - d = d.replace(/(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/, "$1/$2/$3"); + d = s.replace(/\s+/g, ' ').replace(/[\-.,]/g, '/'); // escaped - because JSHint in Firefox was showing it as an error + if (format === 'mmddyyyy') { + d = d.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, '$3/$1/$2'); + } else if (format === 'ddmmyyyy') { + d = d.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, '$3/$2/$1'); + } else if (format === 'yyyymmdd') { + d = d.replace(/(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/, '$1/$2/$3'); } date = new Date(d); return date instanceof Date && isFinite(date) ? date.getTime() : s; } return s; }, - type: "numeric" + type: 'numeric' }); ts.addParser({ - id: "time", + id: 'time', is: function(s) { return (/^(([0-2]?\d:[0-5]\d)|([0-1]?\d:[0-5]\d\s?([AP]M)))$/i).test(s); }, format: function(s, table) { - var date = s ? new Date( "2000/01/01 " + s.replace(/(\S)([AP]M)$/i, "$1 $2") ) : s; + var date = s ? new Date( '2000/01/01 ' + s.replace(/(\S)([AP]M)$/i, '$1 $2') ) : s; return date instanceof Date && isFinite(date) ? date.getTime() : s; }, - type: "numeric" + type: 'numeric' }); ts.addParser({ - id: "metadata", + id: 'metadata', is: function() { return false; }, @@ -1976,12 +1986,12 @@ p = (!c.parserMetadataName) ? 'sortValue' : c.parserMetadataName; return $(cell).metadata()[p]; }, - type: "numeric" + type: 'numeric' }); // add default widgets ts.addWidget({ - id: "zebra", + id: 'zebra', priority: 90, format: function(table, c, wo) { var $tb, $tv, $tr, row, even, time, k, @@ -2010,7 +2020,7 @@ if (refreshing) { return; } var k, $tb, b = c.$tbodies, - rmv = (wo.zebra || [ "even", "odd" ]).join(' '); + rmv = (wo.zebra || [ 'even', 'odd' ]).join(' '); for (k = 0; k < b.length; k++ ){ $tb = ts.processTbody(table, b.eq(k), true); // remove tbody $tb.children().removeClass(rmv); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 1a24a97..d32e830 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,4 +1,4 @@ -/*! tableSorter (FORK) 2.16+ widgets - updated 2/7/2015 (v2.19.0) +/*! tableSorter (FORK) 2.16+ widgets - updated 2/9/2015 (v2.19.1) * * Column Styles * Column Filters @@ -201,7 +201,7 @@ ts.addWidget({ wo.uitheme_applied = true; oldtheme = themesAll[c.appliedTheme] || {}; hasOldTheme = !$.isEmptyObject(oldtheme); - oldremove = hasOldTheme ? [ oldtheme.sortNone, oldtheme.sortDesc, oldtheme.sortAsc, oldtheme.active ].join( ' ' ) : '', + oldremove = hasOldTheme ? [ oldtheme.sortNone, oldtheme.sortDesc, oldtheme.sortAsc, oldtheme.active ].join( ' ' ) : ''; oldIconRmv = hasOldTheme ? [ oldtheme.iconSortNone, oldtheme.iconSortDesc, oldtheme.iconSortAsc ].join( ' ' ) : ''; if (hasOldTheme) { wo.zebra[0] = $.trim( ' ' + wo.zebra[0].replace(' ' + oldtheme.even, '') ); @@ -429,11 +429,12 @@ ts.addWidget({ remove: function(table, c, wo, refreshing) { var tbodyIndex, $tbody, $table = c.$table, - $tbodies = c.$tbodies; + $tbodies = c.$tbodies, + events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter '); $table .removeClass('hasFilters') // add .tsfilter namespace to all BUT search - .unbind('addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter ')) + .unbind( $.trim(events) ) // remove the filter row even if refreshing, because the column might have been moved .find('.' + ts.css.filterRow).remove(); if (refreshing) { return; } @@ -660,7 +661,7 @@ ts.filter = { } txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter '); - c.$table.bind(txt, function(event, filter) { + c.$table.bind( $.trim(txt), function(event, filter) { val = (wo.filter_hideEmpty && $.isEmptyObject(c.cache) && !(c.delayInit && event.type === 'appendCache')); // hide filter row using the "filtered" class name c.$table.find('.' + ts.css.filterRow).toggleClass(wo.filter_filteredRow, val ); // fixes #450 @@ -752,7 +753,9 @@ ts.filter = { // show processing icon if (c.showProcessing) { - c.$table.bind('filterStart' + c.namespace + 'filter filterEnd' + c.namespace + 'filter', function(event, columns) { + c.$table + .unbind( $.trim('filterStart filterEnd '.split(' ').join(c.namespace + 'filter ')) ) + .bind( $.trim('filterStart filterEnd '.split(' ').join(c.namespace + 'filter ')), function(event, columns) { // only add processing to certain columns to all columns $header = (columns) ? c.$table.find('.' + ts.css.header).filter('[data-column]').filter(function() { return columns[$(this).data('column')] !== ''; @@ -765,7 +768,9 @@ ts.filter = { c.filteredRows = c.totalRows; // add default values - c.$table.bind('tablesorter-initialized pagerBeforeInitialized', function() { + c.$table + .unbind( $.trim('tablesorter-initialized pagerBeforeInitialized '.split(' ').join(c.namespace + 'filter ')) ) + .bind( $.trim('tablesorter-initialized pagerBeforeInitialized '.split(' ').join(c.namespace + 'filter ')), function() { // redefine "wo" as it does not update properly inside this callback var wo = this.config.widgetOptions; filters = ts.filter.setDefaults(table, c, wo) || []; @@ -937,7 +942,7 @@ ts.filter = { $el // use data attribute instead of jQuery data since the head is cloned without including the data/binding .attr('data-lastSearchTime', new Date().getTime()) - .unbind('keypress keyup search change '.split(' ').join(c.namespace + 'filter ')) + .unbind( $.trim('keypress keyup search change '.split(' ').join(c.namespace + 'filter ')) ) // include change for select - fixes #473 .bind('keyup' + c.namespace + 'filter', function(event) { $(this).attr('data-lastSearchTime', new Date().getTime()); @@ -958,7 +963,7 @@ ts.filter = { // change event = no delay; last true flag tells getFilters to skip newest timed input ts.filter.searching( table, true, true ); }) - .bind('search change keypress '.split(' ').join(c.namespace + 'filter '), function(event){ + .bind( $.trim('search change keypress '.split(' ').join(c.namespace + 'filter ')), function(event){ var column = $(this).data('column'); // don't allow "change" event to process if the input value is the same - fixes #685 if (event.which === 13 || event.type === 'search' || event.type === 'change' && this.value !== c.lastSearch[column]) { @@ -1772,7 +1777,7 @@ ts.addWidget({ // update sticky header class names to match real header after sorting $table .addClass('hasStickyHeaders') - .bind('pagerComplete' + namespace, function() { + .bind( $.trim('pagerComplete' + namespace), function() { resizeHeader(); }); @@ -1791,8 +1796,8 @@ ts.addWidget({ // make it sticky! $xScroll.add($yScroll) - .unbind('scroll resize '.split(' ').join( namespace ) ) - .bind('scroll resize '.split(' ').join( namespace ), function(event) { + .unbind( $.trim('scroll resize '.split(' ').join( namespace )) ) + .bind( $.trim('scroll resize '.split(' ').join( namespace )), function(event) { if (!$table.is(':visible')) { return; } // fixes #278 // Detect nested tables - fixes #724 nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; @@ -1833,7 +1838,7 @@ ts.addWidget({ // look for filter widget if ($table.hasClass('hasFilters') && wo.filter_columnFilters) { // scroll table into view after filtering, if sticky header is active - #482 - $table.bind('filterEnd' + namespace, function() { + $table.bind( $.trim('filterEnd' + namespace), function() { // $(':focus') needs jQuery 1.6+ var $td = $(document.activeElement).closest('td'), column = $td.parent().children().index($td); @@ -1861,14 +1866,14 @@ ts.addWidget({ var namespace = c.namespace + 'stickyheaders '; c.$table .removeClass('hasStickyHeaders') - .unbind( 'pagerComplete filterEnd '.split(' ').join(namespace) ) + .unbind( $.trim('pagerComplete filterEnd '.split(' ').join(namespace)) ) .next('.' + ts.css.stickyWrap).remove(); if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table $(window) .add(wo.stickyHeaders_xScroll) .add(wo.stickyHeaders_yScroll) .add(wo.stickyHeaders_attachTo) - .unbind( 'scroll resize '.split(' ').join(namespace) ); + .unbind( $.trim('scroll resize '.split(' ').join(namespace)) ); ts.addHeaderResizeEvent(table, false); } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js index 10a57b2..8048ce0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js @@ -234,8 +234,8 @@ }); }, - remove: function(c) { - c.$table.off(chart.event); + remove: function(c, wo) { + c.$table.off(wo.chart_event); } }; @@ -269,7 +269,7 @@ }, remove: function(table, c, wo) { - chart.remove(c); + chart.remove(c, wo); } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js index d5419fd..c4e3f02 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js @@ -1,4 +1,4 @@ -/*! tablesorter CSS Sticky Headers widget - updated 2/7/2015 (v2.19.0) +/*! tablesorter CSS Sticky Headers widget - updated 2/9/2015 (v2.19.1) * Requires a modern browser, tablesorter v2.8+ */ /*jshint jquery:true, unused:false */ @@ -59,8 +59,8 @@ } $win - .unbind('scroll resize '.split(' ').join(namespace)) - .bind('scroll resize '.split(' ').join(namespace), function() { + .unbind( $.trim('scroll resize '.split(' ').join(namespace)) ) + .bind( $.trim('scroll resize '.split(' ').join(namespace)), function() { // make sure "wo" is current otherwise changes to widgetOptions // are not dynamic (like the add caption button in the demo) wo = c.widgetOptions; @@ -126,7 +126,7 @@ setTransform( $cells, finalY ); }); - $table.unbind('filterEnd' + namespace).bind('filterEnd' + namespace, function() { + $table.unbind( $.trim('filterEnd' + namespace) ).bind( $.trim('filterEnd' + namespace), function() { if (wo.cssStickyHeaders_filteredToTop) { // scroll top of table into view window.scrollTo(0, $table.position().top); @@ -137,9 +137,9 @@ remove: function(table, c, wo, refreshing) { if (refreshing) { return; } var namespace = c.namespace + 'cssstickyheader '; - $(window).unbind('scroll resize '.split(' ').join(namespace)); + $(window).unbind( $.trim('scroll resize '.split(' ').join(namespace)) ); c.$table - .unbind('filterEnd scroll resize '.split(' ').join(namespace)) + .unbind( $.trim('filterEnd scroll resize '.split(' ').join(namespace)) ) .add( c.$table.children('thead').children().children() ) .children('thead, caption').css({ 'transform' : '', diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js index b1f17f1..d536e83 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js @@ -1,4 +1,4 @@ -/*! tablesorter Editable Content widget - updated 2/7/2015 (v2.19.0) +/*! tablesorter Editable Content widget - updated 2/9/2015 (v2.19.1) * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -95,13 +95,13 @@ var tse = $.tablesorter.editable = { bindEvents: function( c, wo ) { c.$table - .off( 'updateComplete pagerComplete '.split( ' ' ).join( '.tseditable' ) ) - .on( 'updateComplete pagerComplete '.split( ' ' ).join( '.tseditable' ), function() { + .off( $.trim( 'updateComplete pagerComplete '.split( ' ' ).join( '.tseditable' ) ) ) + .on( $.trim( 'updateComplete pagerComplete '.split( ' ' ).join( '.tseditable' ) ), function() { tse.update( c, c.widgetOptions ); }); c.$tbodies - .off( 'mouseleave focus blur focusout keydown '.split( ' ' ).join( '.tseditable ' ) ) + .off( $.trim( 'mouseleave focus blur focusout keydown '.split( ' ' ).join( '.tseditable ' ) ) ) .on( 'mouseleave.tseditable', function() { if ( c.$table.data( 'contentFocused' ) ) { // change to 'true' instead of element to allow focusout to process @@ -140,7 +140,7 @@ var tse = $.tablesorter.editable = { } } }) - .on( 'blur focusout keydown '.split( ' ' ).join( '.tseditable ' ), '[contenteditable]', function( e ) { + .on( $.trim( 'blur focusout keydown '.split( ' ' ).join( '.tseditable ' ) ), '[contenteditable]', function( e ) { if ( !c.$table.data( 'contentFocused' ) ) { return; } var t, validate, valid = false, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js index 2e7e688..ae80964 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js @@ -1,4 +1,4 @@ -/*! tablesorter Formatter widget - 2/7/2015 (v2.19.0) +/*! tablesorter Formatter widget - 2/9/2015 (v2.19.1) * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -10,11 +10,13 @@ ts.formatter = { init : function( c ) { - var events = $.trim( c.widgetOptions.formatter_event ) + ' pagerComplete updateComplete ' - .split(' ').join('.tsformatter '); - c.$table.on( events, function() { - ts.formatter.setup( c ); - }); + var events = $.trim( c.widgetOptions.formatter_event ) + + ' pagerComplete updateComplete '.split(' ').join('.tsformatter '); + c.$table + .off( $.trim(events) ) + .on( $.trim(events), function() { + ts.formatter.setup( c ); + }); ts.formatter.setup( c ); }, setup : function( c ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index 5d0ddcb..07e0064 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -1,4 +1,4 @@ -/*! tablesorter math widget - updated 2/7/2015 (v2.19.0) +/*! tablesorter math widget - updated 2/9/2015 (v2.19.1) * Requires tablesorter v2.16+ and jQuery 1.7+ * by Rob Garrison */ @@ -8,10 +8,12 @@ "use strict"; var ts = $.tablesorter, - events = ( 'tablesorter-initialized update updateAll updateRows addRows updateCell ' + - 'filterReset filterEnd recalculate ' ).split(' ').join('.tsmath '), + math = { + events : ( 'tablesorter-initialized update updateAll updateRows addRows updateCell ' + + 'filterReset filterEnd ' ).split(' ').join('.tsmath '), + // get all of the row numerical values in an arry getRow : function(table, wo, $el, dataAttrib) { var $t, txt, @@ -386,12 +388,13 @@ // template for or just prepend the mask prefix & suffix with this HTML // e.g. '<span class="red">{content}</span>' math_prefix : '', - math_suffix : '' + math_suffix : '', + math_event : 'recalculate' }, init : function(table, thisWidget, c, wo){ c.$table - .unbind(events + ' updateComplete.tsmath') - .bind(events, function(e){ + .off( $.trim(math.events) + ' ' + $.trim('updateComplete.tsmath ' + wo.math_event) ) + .on( $.trim(math.events) + ' ' + wo.math_event, function(e){ var init = e.type === 'tablesorter-initialized'; if (e.type === 'updateAll') { // redo data-column indexes in case columns were rearranged @@ -400,7 +403,7 @@ math.recalculate( table, c, wo, init ); } }) - .bind('updateComplete.tsmath', function(){ + .on('updateComplete.tsmath', function(){ setTimeout(function(){ wo.math_isUpdating = false; }, 500); @@ -412,9 +415,9 @@ remove: function(table, c, wo, refreshing){ if (refreshing) { return; } $(table) - .unbind(events + ' updateComplete.tsmath') + .off( $.trim(math.events) + ' ' + $.trim('updateComplete.tsmath ' + wo.math_event) ) .find('[data-' + wo.math_data + ']').empty(); } }); -})(jQuery); \ No newline at end of file +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index 6d7b94a..26b8044 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -1,4 +1,4 @@ -/* Output widget for TableSorter 2/7/2015 (v2.19.0) +/* Output widget for TableSorter 2/9/2015 (v2.19.1) * Requires tablesorter v2.8+ and jQuery 1.7+ * Modified from: * HTML Table to CSV: http://www.kunalbabre.com/projects/table2CSV.php (License unknown?) diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 3d00632..728dbb8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/* Pager widget for TableSorter 2/7/2015 (v2.19.0) - requires jQuery 1.7+ */ +/* Pager widget for TableSorter 2/9/2015 (v2.19.1) - requires jQuery 1.7+ */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ "use strict"; @@ -229,7 +229,7 @@ tsp = ts.pager = { s = wo.pager_selectors; c.$table - .off(p.events.split(' ').join('.pager ')) + .off( $.trim(p.events.split(' ').join('.pager ')) ) .on('filterInit.pager filterStart.pager', function(e, filters) { p.currentFilters = $.isArray(filters) ? filters : c.$table.data('lastSearch'); // don't change page if filters are the same (pager updating, etc) @@ -330,8 +330,8 @@ tsp = ts.pager = { if ( p.$goto.length ) { p.$goto - .off('change') - .on('change', function(){ + .off('change.pager') + .on('change.pager', function(){ p.page = $(this).val() - 1; tsp.moveToPage(table, p, true); tsp.updatePageDisplay(table, c, false); @@ -443,7 +443,7 @@ tsp = ts.pager = { if ($out.length) { $out[ ($out[0].tagName === 'INPUT') ? 'val' : 'html' ](s); // rebind startRow/page inputs - $out.find('.ts-startRow, .ts-page').off('change').on('change', function(){ + $out.find('.ts-startRow, .ts-page').off('change.pager').on('change.pager', function(){ var v = $(this).val(), pg = $(this).hasClass('ts-startRow') ? Math.floor( v/p.size ) + 1 : v; c.$table.trigger('pageSet.pager', [ pg ]); @@ -1012,7 +1012,7 @@ tsp = ts.pager = { destroyPager: function(table, c, refreshing){ var p = c.pager; p.initialized = false; - c.$table.off(p.events.split(' ').join('.pager ')); + c.$table.off( $.trim(p.events.split(' ').join('.pager ')) ); if (refreshing) { return; } tsp.showAllRows(table, c); p.$container.hide(); // hide pager diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js index 70817a9..1687731 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js @@ -1,6 +1,5 @@ -/* - * StaticRow widget for jQuery TableSorter 2.0 - * Version 1.2 - modified by Rob Garrison (6/28/2014 for tablesorter v2.16.1-beta+) +/* StaticRow widget for jQuery TableSorter 2.0 - updated 2/9/2015 (v2.19.1) + * Version 1.2 mod by Rob Garrison (requires tablesorter v2.16+) * Requires: * jQuery v1.4+ * tablesorter plugin, v2.8+, available at http://mottie.github.com/tablesorter/docs/ @@ -16,9 +15,6 @@ "use strict"; var ts = $.tablesorter, -// events triggered on the table that update this widget -events = 'staticRowsRefresh updateComplete '.split(' ').join('.tsstaticrows '), - // add/refresh row indexes addIndexes = function(table){ var $tr, wo, v, indx, rows, @@ -55,15 +51,16 @@ ts.addWidget({ options: { staticRow_class : '.static', staticRow_data : 'static-index', - staticRow_index : 'row-index' + staticRow_index : 'row-index', + staticRow_event : 'staticRowsRefresh' }, init: function(table, thisWidget, c, wo){ addIndexes(table); // refresh static rows after updates c.$table - .unbind(events) - .bind(events, function(){ + .unbind( $.trim('updateComplete.tsstaticrows ' + wo.staticRow_event) ) + .bind( $.trim('updateComplete.tsstaticrows ' + wo.staticRow_event), function(){ addIndexes(table); c.$table.trigger('applyWidgets'); }); @@ -116,7 +113,7 @@ ts.addWidget({ }, remove : function(table, c, wo){ - c.$table.unbind(events); + c.$table.unbind( $.trim('updateComplete.tsstaticrows ' + wo.staticRow_event) ); } }); From 48db77bf4a8a17f4329a0e9c6edd9761d2d221cf Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Tue, 10 Feb 2015 16:44:07 +0100 Subject: [PATCH 048/138] * changed minimum required Rails version to 3.2 --- README.md | 2 +- jquery-tablesorter.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8c6557e..12b492d 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Or install it yourself as: ## Requirements -Rails 3.1 and higher (tested up to 4.2) +Rails 3.2 and higher (tested up to 4.2) Tested with ruby 1.9.3 - 2.2.0 diff --git a/jquery-tablesorter.gemspec b/jquery-tablesorter.gemspec index 579195e..5786d02 100644 --- a/jquery-tablesorter.gemspec +++ b/jquery-tablesorter.gemspec @@ -18,5 +18,5 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 1.9.3' - s.add_dependency 'railties', '>= 3.1', '< 5' + s.add_dependency 'railties', '>= 3.2', '< 5' end From e2385297249b8b26487a0339544588c8c5ed661e Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Sat, 21 Feb 2015 09:58:26 +0100 Subject: [PATCH 049/138] * updated tablesorter to latest version (2.20.1) --- CHANGELOG.md | 5 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../assets/javascripts/jquery-tablesorter.js | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 14 +- .../extras/jquery.dragtable.mod.js | 5 +- .../{ => extras}/jquery.metadata.js | 1 + .../extras/jquery.quicksearch.js | 12 +- .../jquery-tablesorter/jquery.tablesorter.js | 223 +-- .../jquery.tablesorter.widgets.js | 403 ++--- .../parsers/parser-file-type.js | 2 +- .../parsers/parser-input-select.js | 2 +- .../widgets/widget-chart.js | 5 +- .../widgets/widget-columns.js | 78 + .../widgets/widget-cssStickyHeaders.js | 26 +- .../widgets/widget-editable.js | 8 +- .../widgets/widget-filter-formatter-html5.js | 429 ++++++ .../widget-filter-formatter-jui.js} | 382 +---- .../widget-filter-formatter-select2.js} | 0 .../widgets/widget-filter.js | 1307 +++++++++++++++++ .../widgets/widget-formatter.js | 6 +- .../widgets/widget-grouping.js | 8 +- .../jquery-tablesorter/widgets/widget-math.js | 6 +- .../widgets/widget-pager.js | 12 +- .../widgets/widget-resizable.js | 176 +++ .../widgets/widget-saveSort.js | 68 + .../widgets/widget-staticRow.js | 6 +- .../widgets/widget-stickyHeaders.js | 269 ++++ .../widgets/widget-storage.js | 76 + .../widgets/widget-uitheme.js | 184 +++ .../jquery-tablesorter/theme.black-ice.css | 2 +- .../jquery-tablesorter/theme.blue.css | 2 +- .../jquery-tablesorter/theme.dark.css | 2 +- .../jquery-tablesorter/theme.default.css | 2 +- .../jquery-tablesorter/theme.dropbox.css | 2 +- .../jquery-tablesorter/theme.green.css | 2 +- .../jquery-tablesorter/theme.grey.css | 2 +- .../jquery-tablesorter/theme.ice.css | 2 +- .../jquery-tablesorter/theme.jui.css | 2 +- .../jquery-tablesorter/theme.metro-dark.css | 2 +- 41 files changed, 3009 insertions(+), 732 deletions(-) rename vendor/assets/javascripts/jquery-tablesorter/{ => extras}/jquery.metadata.js (99%) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columns.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js rename vendor/assets/javascripts/jquery-tablesorter/{jquery.tablesorter.widgets-filter-formatter.js => widgets/widget-filter-formatter-jui.js} (65%) rename vendor/assets/javascripts/jquery-tablesorter/{jquery.tablesorter.widgets-filter-formatter-select2.js => widgets/widget-filter-formatter-select2.js} (100%) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js diff --git a/CHANGELOG.md b/CHANGELOG.md index e8130f2..356fa86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ Changelog === +#### v1.15.0 (2015-02-21) + +* Upgrade tablesorter to v2.20.1 +* Increased minimum required railties version to 3.2 + #### v1.14.1 (2015-02-10) * Upgrade tablesorter to v2.19.1 diff --git a/README.md b/README.md index 12b492d..1946553 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.19.1 (2/9/2015), [documentation] +Current tablesorter version: 2.20.1 (2/20/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 19d5335..f5dbc86 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.14.1' + VERSION = '1.15.0' end diff --git a/tablesorter b/tablesorter index c4a84f3..44ee945 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit c4a84f3dd513063856c9b976afc4396e672a886d +Subproject commit 44ee94502bfafaf540092649fde6a7ccb428507a diff --git a/vendor/assets/javascripts/jquery-tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter.js index 6c6d4ec..42c6aa8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter.js @@ -1,3 +1,3 @@ -//= require jquery-tablesorter/jquery.metadata +//= require jquery-tablesorter/extras/jquery.metadata //= require jquery-tablesorter/jquery.tablesorter //= require jquery-tablesorter/jquery.tablesorter.widgets \ No newline at end of file diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index a921d01..b2c6120 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -135,18 +135,15 @@ }, calcFilters = function(table, p) { - var tbodyIndex, - c = table.config, + var c = table.config, hasFilters = c.$table.hasClass('hasFilters'); if (hasFilters && !p.ajaxUrl) { if ($.isEmptyObject(c.cache)) { // delayInit: true so nothing is in the cache p.filteredRows = p.totalRows = c.$tbodies.eq(0).children('tr').not( p.countChildRows ? '' : '.' + c.cssChildRow ).length; } else { - // just in case the pager tbody isn't the first tbody - tbodyIndex = c.$table.children('tbody').index( c.$tbodies.eq(0) ); p.filteredRows = 0; - $.each(c.cache[tbodyIndex].normalized, function(i, el) { + $.each(c.cache[0].normalized, function(i, el) { p.filteredRows += p.regexRows.test(el[c.columns].$row[0].className) ? 0 : 1; }); } @@ -659,8 +656,7 @@ c.$table.trigger('updateCache', [ function(){ var i, rows = [], - tbodyIndex = c.$table.children('tbody').index( c.$tbodies.eq(0) ), - n = table.config.cache[tbodyIndex].normalized; + n = table.config.cache[0].normalized; p.totalRows = n.length; for (i = 0; i < p.totalRows; i++) { rows.push(n[i][c.columns].$row); @@ -768,7 +764,7 @@ table.config.appender = null; // remove pager appender function p.initialized = false; delete table.config.rowsCopy; - $(table).unbind( $.trim(pagerEvents.split(' ').join('.pager ')) ); + $(table).unbind( pagerEvents.split(' ').join('.pager ').replace(/\s+/g, ' ') ); if (ts.storage) { ts.storage(table, p.storageKey, ''); } @@ -848,7 +844,7 @@ p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.slice(1) + '|' + c.cssChildRow + ')'); $t - .unbind( $.trim(pagerEvents.split(' ').join('.pager ')) ) + .unbind( pagerEvents.split(' ').join('.pager ').replace(/\s+/g, ' ') ) .bind('filterInit.pager filterStart.pager', function(e, filters) { p.currentFilters = $.isArray(filters) ? filters : c.$table.data('lastSearch'); // don't change page if filters are the same (pager updating, etc) diff --git a/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.dragtable.mod.js b/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.dragtable.mod.js index c3309f6..debb642 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.dragtable.mod.js +++ b/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.dragtable.mod.js @@ -198,15 +198,12 @@ } }; -/*! - * dragtable +/*! dragtable v2.0.14 Mod *//* * _____ _ * | |___ _| | * | | | | . | . | * |_|_|_|___|___| * - * @Version 2.0.14 MOD - * * Copyright (c) 2010-2013, Andres akottr@gmail.com * Dual licensed under the MIT (MIT-LICENSE.txt) * and GPL (GPL-LICENSE.txt) licenses. diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.metadata.js b/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.metadata.js similarity index 99% rename from vendor/assets/javascripts/jquery-tablesorter/jquery.metadata.js rename to vendor/assets/javascripts/jquery-tablesorter/extras/jquery.metadata.js index 07b10ba..ddff5e8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.metadata.js +++ b/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.metadata.js @@ -92,6 +92,7 @@ $.extend({ if ( data.indexOf( '{' ) <0 ) { data = "{" + data + "}"; } + /*jshint evil:true */ data = eval("(" + data + ")"); $.data( elem, settings.single, data ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.quicksearch.js b/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.quicksearch.js index ee783ac..f1e311c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.quicksearch.js +++ b/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.quicksearch.js @@ -32,7 +32,7 @@ prepareQuery: function (val) { return val.toLowerCase().split(' '); }, - testQuery: function (query, txt, _row) { + testQuery: function (query, txt) { for (var i = 0; i < query.length; i += 1) { if (txt.indexOf(query[i]) === -1) { return false; @@ -44,13 +44,13 @@ this.go = function () { - var i = 0, + var len, i = 0, numMatchedRows = 0, noresults = true, query = options.prepareQuery(val), val_empty = (val.replace(' ', '').length === 0); - for (var i = 0, len = rowcache.length; i < len; i++) { + for (i = 0, len = rowcache.length; i < len; i++) { if (val_empty || options.testQuery(query, cache[i], rowcache[i]) || ($(rowcache[i]).hasClass(options.childRow) && $(rowcache[i > 1 ? i - 1 : 0]).is(':visible'))) { options.show.apply(rowcache[i]); @@ -125,7 +125,11 @@ this.loader = function (bool) { if (typeof options.loader === "string" && options.loader !== "") { - (bool) ? $(options.loader).show() : $(options.loader).hide(); + if (bool) { + $(options.loader).show(); + } else { + $(options.loader).hide(); + } } return this; }; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 43158fd..fb16b44 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,8 +1,10 @@ -/**! -* TableSorter (FORK) 2.19.1 - Client-side table sorting with ease! +/*! TableSorter (FORK) v{{version}} *//* +* Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach +* fork maintained by Rob Garrison +* * Examples and docs at: http://tablesorter.com * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php @@ -11,8 +13,8 @@ * @type jQuery * @name tablesorter (FORK) * @cat Plugins/Tablesorter -* @author Christian Bach/christian.bach@polyester.se -* @contributor Rob Garrison/https://github.com/Mottie/tablesorter +* @author Christian Bach - christian.bach@polyester.se +* @contributor Rob Garrison - https://github.com/Mottie/tablesorter */ /*jshint browser:true, jquery:true, unused:false, expr: true */ /*global console:false, alert:false, require:false, define:false, module:false */ @@ -32,7 +34,7 @@ var ts = this; - ts.version = '2.19.1'; + ts.version = '{{version}}'; ts.parsers = []; ts.widgets = []; @@ -104,7 +106,7 @@ cssIconAsc : '', // class name added to the icon when the column has an ascending sort cssIconDesc : '', // class name added to the icon when the column has a descending sort cssInfoBlock : 'tablesorter-infoOnly', // don't sort tbody with this class name (only one class name allowed here!) - cssAllowClicks : 'tablesorter-allowClicks', // class name added to table header which allows clicks to bubble up + cssNoSort : 'tablesorter-noSort', // class name added to element inside header; clicking on it won't cause a sort cssIgnoreRow : 'tablesorter-ignoreRow', // header row to ignore; cells within this row will not be added to c.$headers // *** selectors @@ -136,7 +138,6 @@ headerRow : 'tablesorter-headerRow', headerIn : 'tablesorter-header-inner', icon : 'tablesorter-icon', - info : 'tablesorter-infoOnly', processing : 'tablesorter-processing', sortAsc : 'tablesorter-headerAsc', sortDesc : 'tablesorter-headerDesc', @@ -180,37 +181,39 @@ return true; } - function getElementText(table, node, cellIndex) { + ts.getElementText = function(c, node, cellIndex) { if (!node) { return ''; } var te, - $node = $(node), - c = table.config, - t = c.textExtraction || ''; + t = c.textExtraction || '', + // node could be a jquery object + // http://jsperf.com/jquery-vs-instanceof-jquery/2 + $node = node.jquery ? node : $(node); if (typeof(t) === 'string') { // check data-attribute first when set to 'basic'; don't use node.innerText - it's really slow! - return $.trim( (t === 'basic' ? $node.attr(c.textAttribute) || node.textContent : node.textContent ) || $node.text() || '' ); + return $.trim( ( t === 'basic' ? $node.attr(c.textAttribute) || node.textContent : node.textContent ) || $node.text() || '' ); } else { if (typeof(t) === 'function') { - return $.trim( t(node, table, cellIndex) ); - } else if (typeof (te = ts.getColumnData( table, t, cellIndex )) === 'function') { - return $.trim( te(node, table, cellIndex) ); + return $.trim( t($node[0], c.table, cellIndex) ); + } else if (typeof (te = ts.getColumnData( c.table, t, cellIndex )) === 'function') { + return $.trim( te($node[0], c.table, cellIndex) ); } } // fallback - return $.trim( node.textContent || $node.text() || '' ); - } + return $.trim( $node[0].textContent || $node.text() || '' ); + }; function detectParserForColumn(table, rows, rowIndex, cellIndex) { var cur, $node, - i = ts.parsers.length, - node = false, - nodeValue = '', - keepLooking = true; + c = table.config, + i = ts.parsers.length, + node = false, + nodeValue = '', + keepLooking = true; while (nodeValue === '' && keepLooking) { rowIndex++; if (rows[rowIndex]) { node = rows[rowIndex].cells[cellIndex]; - nodeValue = getElementText(table, node, cellIndex); + nodeValue = ts.getElementText(c, node, cellIndex); $node = $(node); if (table.config.debug) { log('Checking if value was empty on row ' + rowIndex + ', column: ' + cellIndex + ': "' + nodeValue + '"'); @@ -293,10 +296,10 @@ /* utils */ function buildCache(table) { - var cc, t, tx, v, i, j, k, $row, rows, cols, cacheTime, + var cc, t, tx, v, i, j, k, $row, cols, cacheTime, totalRows, rowData, colMax, c = table.config, - $tb = c.$table.children('tbody'), + $tb = c.$tbodies, extractors = c.extractors, parsers = c.parsers; c.cache = {}; @@ -319,68 +322,65 @@ // colMax: # // added at the end }; - // ignore tbodies with class name from c.cssInfoBlock - if (!$tb.eq(k).hasClass(c.cssInfoBlock)) { - totalRows = ($tb[k] && $tb[k].rows.length) || 0; - for (i = 0; i < totalRows; ++i) { - rowData = { - // order: original row order # - // $row : jQuery Object[] - child: [], // child row text (filter widget) - raw: [] // original row text - }; - /** Add the table data to main data array */ - $row = $($tb[k].rows[i]); - rows = [ new Array(c.columns) ]; - cols = []; - // if this is a child row, add it to the last row's children and continue to the next row - // ignore child row class, if it is the first row - if ($row.hasClass(c.cssChildRow) && i !== 0) { - t = cc.normalized.length - 1; - cc.normalized[t][c.columns].$row = cc.normalized[t][c.columns].$row.add($row); - // add 'hasChild' class name to parent row - if (!$row.prev().hasClass(c.cssChildRow)) { - $row.prev().addClass(ts.css.cssHasChild); + totalRows = ($tb[k] && $tb[k].rows.length) || 0; + for (i = 0; i < totalRows; ++i) { + rowData = { + // order: original row order # + // $row : jQuery Object[] + child: [], // child row text (filter widget) + raw: [] // original row text + }; + /** Add the table data to main data array */ + $row = $($tb[k].rows[i]); + cols = []; + // if this is a child row, add it to the last row's children and continue to the next row + // ignore child row class, if it is the first row + if ($row.hasClass(c.cssChildRow) && i !== 0) { + t = cc.normalized.length - 1; + cc.normalized[t][c.columns].$row = cc.normalized[t][c.columns].$row.add($row); + // add 'hasChild' class name to parent row + if (!$row.prev().hasClass(c.cssChildRow)) { + $row.prev().addClass(ts.css.cssHasChild); + } + // save child row content (un-parsed!) + rowData.child[t] = $.trim( $row[0].textContent || $row.text() || '' ); + // go to the next for loop + continue; + } + rowData.$row = $row; + rowData.order = i; // add original row position to rowCache + for (j = 0; j < c.columns; ++j) { + if (typeof parsers[j] === 'undefined') { + if (c.debug) { + log('No parser found for cell:', $row[0].cells[j], 'does it have a header?'); } - // save child row content (un-parsed!) - rowData.child[t] = $.trim( $row[0].textContent || $row.text() || '' ); - // go to the next for loop continue; } - rowData.$row = $row; - rowData.order = i; // add original row position to rowCache - for (j = 0; j < c.columns; ++j) { - if (typeof parsers[j] === 'undefined') { - if (c.debug) { - log('No parser found for cell:', $row[0].cells[j], 'does it have a header?'); - } - continue; - } - t = getElementText(table, $row[0].cells[j], j); - rowData.raw.push(t); // save original row text - // do extract before parsing if there is one - if (typeof extractors[j].id === 'undefined') { - tx = t; - } else { - tx = extractors[j].format(t, table, $row[0].cells[j], j); - } - // allow parsing if the string is empty, previously parsing would change it to zero, - // in case the parser needs to extract data from the table cell attributes - v = parsers[j].id === 'no-parser' ? '' : parsers[j].format(tx, table, $row[0].cells[j], j); - cols.push( c.ignoreCase && typeof v === 'string' ? v.toLowerCase() : v ); - if ((parsers[j].type || '').toLowerCase() === 'numeric') { - // determine column max value (ignore sign) - colMax[j] = Math.max(Math.abs(v) || 0, colMax[j] || 0); - } + t = ts.getElementText(c, $row[0].cells[j], j); + rowData.raw.push(t); // save original row text + // do extract before parsing if there is one + if (typeof extractors[j].id === 'undefined') { + tx = t; + } else { + tx = extractors[j].format(t, table, $row[0].cells[j], j); + } + // allow parsing if the string is empty, previously parsing would change it to zero, + // in case the parser needs to extract data from the table cell attributes + v = parsers[j].id === 'no-parser' ? '' : parsers[j].format(tx, table, $row[0].cells[j], j); + cols.push( c.ignoreCase && typeof v === 'string' ? v.toLowerCase() : v ); + if ((parsers[j].type || '').toLowerCase() === 'numeric') { + // determine column max value (ignore sign) + colMax[j] = Math.max(Math.abs(v) || 0, colMax[j] || 0); } - // ensure rowData is always in the same location (after the last column) - cols[c.columns] = rowData; - cc.normalized.push(cols); } - cc.colMax = colMax; - // total up rows, not including child rows - c.totalRows += cc.normalized.length; + // ensure rowData is always in the same location (after the last column) + cols[c.columns] = rowData; + cc.normalized.push(cols); } + cc.colMax = colMax; + // total up rows, not including child rows + c.totalRows += cc.normalized.length; + } if (c.showProcessing) { ts.isProcessing(table); // remove processing icon @@ -394,7 +394,7 @@ function appendToTable(table, init) { var c = table.config, wo = c.widgetOptions, - b = table.tBodies, + $tbodies = c.$tbodies, rows = [], cc = c.cache, n, totalRows, $bk, $tb, @@ -408,9 +408,9 @@ if (c.debug) { appendTime = new Date(); } - for (k = 0; k < b.length; k++) { - $bk = $(b[k]); - if ($bk.length && !$bk.hasClass(c.cssInfoBlock)) { + for (k = 0; k < $tbodies.length; k++) { + $bk = $tbodies.eq(k); + if ($bk.length) { // get tbody $tb = ts.processTbody(table, $bk, true); n = cc[k].normalized; @@ -752,23 +752,23 @@ // sort multiple columns function multisort(table) { /*jshint loopfunc:true */ var i, k, num, col, sortTime, colMax, - cache, order, sort, x, y, + rows, order, sort, x, y, dir = 0, c = table.config, cts = c.textSorter || '', sortList = c.sortList, l = sortList.length, - bl = table.tBodies.length; + bl = c.$tbodies.length; if (c.serverSideSorting || isEmptyObject(c.cache)) { // empty table - fixes #206/#346 return; } if (c.debug) { sortTime = new Date(); } for (k = 0; k < bl; k++) { colMax = c.cache[k].colMax; - cache = c.cache[k].normalized; + rows = c.cache[k].normalized; - cache.sort(function(a, b) { - // cache is undefined here in IE, so don't use it! + rows.sort(function(a, b) { + // rows is undefined here in IE, so don't use it! for (i = 0; i < l; i++) { col = sortList[i][0]; order = sortList[i][1]; @@ -851,12 +851,12 @@ function bindMethods(table){ var c = table.config, $table = c.$table, - events = 'sortReset update updateRows updateCell updateAll addRows updateComplete sorton appendCache ' + - 'updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave '.split(' ') + events = ('sortReset update updateRows updateCell updateAll addRows updateComplete sorton appendCache ' + + 'updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave ').split(' ') .join(c.namespace + ' '); // apply easy methods that trigger bound events $table - .unbind( $.trim(events) ) + .unbind( events.replace(/\s+/g, ' ') ) .bind('sortReset' + c.namespace, function(e, callback){ e.stopPropagation(); c.sortList = []; @@ -889,7 +889,7 @@ $table.find(c.selectorRemove).remove(); // get position from the dom var v, t, row, icell, - $tb = $table.find('tbody'), + $tb = c.$tbodies, $cell = $(cell), // update cache - format: function(s, table, cell, cellIndex) // no closest in jQuery v1.2.6 - tbdy = $tb.index( $(cell).closest('tbody') ),$row = $(cell).closest('tr'); @@ -902,9 +902,9 @@ icell = $cell.index(); c.cache[tbdy].normalized[row][c.columns].$row = $row; if (typeof c.extractors[icell].id === 'undefined') { - t = getElementText(table, cell, icell); + t = ts.getElementText(c, cell, icell); } else { - t = c.extractors[icell].format( getElementText(table, cell, icell), table, cell, icell ); + t = c.extractors[icell].format( ts.getElementText(c, cell, icell), table, cell, icell ); } v = c.parsers[icell].id === 'no-parser' ? '' : c.parsers[icell].format( t, table, cell, icell ); @@ -938,7 +938,7 @@ $row = $($row).attr('role', 'row'); // make sure we're using a jQuery object var i, j, l, t, v, rowData, cells, rows = $row.filter('tr').length, - tbdy = $table.find('tbody').index( $row.parents('tbody').filter(':first') ); + tbdy = c.$tbodies.index( $row.parents('tbody').filter(':first') ); // fixes adding rows to an empty table - see issue #179 if (!(c.parsers && c.parsers.length)) { buildParserCache(table); @@ -955,9 +955,9 @@ // add each cell for (j = 0; j < l; j++) { if (typeof c.extractors[j].id === 'undefined') { - t = getElementText(table, $row[i].cells[j], j); + t = ts.getElementText(c, $row[i].cells[j], j); } else { - t = c.extractors[j].format( getElementText(table, $row[i].cells[j], j), table, $row[i].cells[j], j ); + t = c.extractors[j].format( ts.getElementText(c, $row[i].cells[j], j), table, $row[i].cells[j], j ); } v = c.parsers[j].id === 'no-parser' ? '' : c.parsers[j].format( t, table, $row[i].cells[j], j ); @@ -1198,7 +1198,7 @@ colgroup = $('<colgroup class="' + ts.css.colgroup + '">'); overallWidth = c.$table.width(); // only add col for visible columns - fixes #371 - $(table.tBodies).not('.' + c.cssInfoBlock).find('tr:first').children(':visible').each(function() { + c.$tbodies.find('tr:first').children(':visible').each(function() { percent = parseInt( ( $(this).width() / overallWidth ) * 1000, 10 ) / 10 + '%'; colgroup.append( $('<col>').css('width', percent) ); }); @@ -1330,9 +1330,11 @@ $headers // http://stackoverflow.com/questions/5312849/jquery-find-self; .find(c.selectorSort).add( $headers.filter(c.selectorSort) ) - .unbind( $.trim('mousedown mouseup sort keyup '.split(' ').join(c.namespace + ' ')) ) - .bind( $.trim('mousedown mouseup sort keyup '.split(' ').join(c.namespace + ' ')), function(e, external) { - var cell, type = e.type; + .unbind( ('mousedown mouseup sort keyup '.split(' ').join(c.namespace + ' ')).replace(/\s+/g, ' ') ) + .bind( 'mousedown mouseup sort keyup '.split(' ').join(c.namespace + ' '), function(e, external) { + var cell, + $target = $(e.target), + type = e.type; // only recognize left clicks or enter if ( ((e.which || e.button) !== 1 && !/sort|keyup/.test(type)) || (type === 'keyup' && e.which !== 13) ) { return; @@ -1342,9 +1344,16 @@ // set timer on mousedown if (type === 'mousedown') { downTime = new Date().getTime(); - return /(input|select|button|textarea)/i.test(e.target.tagName) || - // allow clicks to contents of selected cells - $(e.target).closest('td,th').hasClass(c.cssAllowClicks) ? '' : !c.cancelSelection; + return; + } + cell = $.fn.closest ? $target.closest('td,th') : $target.parents('td,th').filter(':first'); + // prevent sort being triggered on form elements + if ( /(input|select|button|textarea)/i.test(e.target.tagName) || + // nosort class name, or elements within a nosort container + $target.hasClass(c.cssNoSort) || $target.parents('.' + c.cssNoSort).length > 0 || + // elements within a button + $target.parents('button').length > 0 ) { + return !c.cancelSelection; } if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); } // jQuery v1.2.6 doesn't have closest() @@ -1406,13 +1415,13 @@ .join(c.namespace + ' '); $t .removeData('tablesorter') - .unbind( $.trim(events) ); + .unbind( events.replace(/\s+/g, ' ') ); c.$headers.add($f) .removeClass( [ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone].join(' ') ) .removeAttr('data-column') .removeAttr('aria-label') .attr('aria-disabled', 'true'); - $r.find(c.selectorSort).unbind( $.trim('mousedown mouseup keypress '.split(' ').join(c.namespace + ' ')) ); + $r.find(c.selectorSort).unbind( ('mousedown mouseup keypress '.split(' ').join(c.namespace + ' ')).replace(/\s+/g, ' ') ); ts.restoreHeaders(table); $t.toggleClass(ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme, removeClasses === false); // clear flag in case the plugin is initialized again diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index d32e830..e2325ef 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,74 +1,16 @@ -/*! tableSorter (FORK) 2.16+ widgets - updated 2/9/2015 (v2.19.1) - * - * Column Styles - * Column Filters - * Column Resizing - * Sticky Header - * UI Theme (generalized) - * Save Sort - * [ "columns", "filter", "resizable", "stickyHeaders", "uitheme", "saveSort" ] - */ -/*jshint browser:true, jquery:true, unused:false, loopfunc:true */ -/*global jQuery: false, localStorage: false */ -;(function ($, window) { +/*** This file is dynamically generated *** +█████▄ ▄████▄ █████▄ ▄████▄ ██████ ███████▄ ▄████▄ █████▄ ██ ██████ ██ ██ +██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ +█████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ +*/ +/*! tablesorter (FORK) widgets - updated 02-20-2015 (v2.20.1)*/ +/* Includes: storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort */ +/*! Widget: storage */ +;(function ($, window, document) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; - -ts.themes = { - 'bootstrap' : { - table : 'table table-bordered table-striped', - caption : 'caption', - // header class names - header : 'bootstrap-header', // give the header a gradient background (theme.bootstrap_2.css) - sortNone : '', - sortAsc : '', - sortDesc : '', - active : '', // applied when column is sorted - hover : '', // custom css required - a defined bootstrap style may not override other classes - // icon class names - icons : '', // add "icon-white" to make them white; this icon class is added to the <i> in the header - iconSortNone : 'bootstrap-icon-unsorted', // class name added to icon when column is not sorted - iconSortAsc : 'icon-chevron-up glyphicon glyphicon-chevron-up', // class name added to icon when column has ascending sort - iconSortDesc : 'icon-chevron-down glyphicon glyphicon-chevron-down', // class name added to icon when column has descending sort - filterRow : '', // filter row class - footerRow : '', - footerCells : '', - even : '', // even row zebra striping - odd : '' // odd row zebra striping - }, - 'jui' : { - table : 'ui-widget ui-widget-content ui-corner-all', // table classes - caption : 'ui-widget-content', - // header class names - header : 'ui-widget-header ui-corner-all ui-state-default', // header classes - sortNone : '', - sortAsc : '', - sortDesc : '', - active : 'ui-state-active', // applied when column is sorted - hover : 'ui-state-hover', // hover class - // icon class names - icons : 'ui-icon', // icon class added to the <i> in the header - iconSortNone : 'ui-icon-carat-2-n-s', // class name added to icon when column is not sorted - iconSortAsc : 'ui-icon-carat-1-n', // class name added to icon when column has ascending sort - iconSortDesc : 'ui-icon-carat-1-s', // class name added to icon when column has descending sort - filterRow : '', - footerRow : '', - footerCells : '', - even : 'ui-widget-content', // even row zebra striping - odd : 'ui-state-default' // odd row zebra striping - } -}; - -$.extend(ts.css, { - filterRow : 'tablesorter-filter-row', // filter - filter : 'tablesorter-filter', - wrapper : 'tablesorter-wrapper', // ui theme & resizable - resizer : 'tablesorter-resizer', // resizable - sticky : 'tablesorter-stickyHeader', // stickyHeader - stickyVis : 'tablesorter-sticky-visible', - stickyWrap: 'tablesorter-sticky-wrapper' -}); +var ts = $.tablesorter = $.tablesorter || {}; // *** Store data in local storage, with a cookie fallback *** /* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json) if you need it, then include https://github.com/douglascrockford/JSON-js @@ -100,7 +42,7 @@ ts.storage = function(table, key, value, options) { url = options && options.url || $table.attr(options && options.page || 'data-table-page') || c && c.fixedUrl || window.location.pathname; // https://gist.github.com/paulirish/5558557 - if ("localStorage" in window) { + if ('localStorage' in window) { try { window.localStorage.setItem('_tmptest', 'temp'); hasLocalStorage = true; @@ -139,50 +81,62 @@ ts.storage = function(table, key, value, options) { } }; -// Add a resize event to table headers -// ************************** -ts.addHeaderResizeEvent = function(table, disable, settings) { - table = $(table)[0]; // make sure we're using a dom element - var headers, - defaults = { - timer : 250 - }, - options = $.extend({}, defaults, settings), - c = table.config, - wo = c.widgetOptions, - checkSizes = function(triggerEvent) { - wo.resize_flag = true; - headers = []; - c.$headers.each(function() { - var $header = $(this), - sizes = $header.data('savedSizes') || [0,0], // fixes #394 - width = this.offsetWidth, - height = this.offsetHeight; - if (width !== sizes[0] || height !== sizes[1]) { - $header.data('savedSizes', [ width, height ]); - headers.push(this); - } - }); - if (headers.length && triggerEvent !== false) { - c.$table.trigger('resize', [ headers ]); - } - wo.resize_flag = false; - }; - checkSizes(false); - clearInterval(wo.resize_timer); - if (disable) { - wo.resize_flag = false; - return false; +})(jQuery, window, document); + +/*! Widget: uitheme */ +;(function ($) { +'use strict'; +var ts = $.tablesorter = $.tablesorter || {}; + +ts.themes = { + 'bootstrap' : { + table : 'table table-bordered table-striped', + caption : 'caption', + // header class names + header : 'bootstrap-header', // give the header a gradient background (theme.bootstrap_2.css) + sortNone : '', + sortAsc : '', + sortDesc : '', + active : '', // applied when column is sorted + hover : '', // custom css required - a defined bootstrap style may not override other classes + // icon class names + icons : '', // add "icon-white" to make them white; this icon class is added to the <i> in the header + iconSortNone : 'bootstrap-icon-unsorted', // class name added to icon when column is not sorted + iconSortAsc : 'icon-chevron-up glyphicon glyphicon-chevron-up', // class name added to icon when column has ascending sort + iconSortDesc : 'icon-chevron-down glyphicon glyphicon-chevron-down', // class name added to icon when column has descending sort + filterRow : '', // filter row class + footerRow : '', + footerCells : '', + even : '', // even row zebra striping + odd : '' // odd row zebra striping + }, + 'jui' : { + table : 'ui-widget ui-widget-content ui-corner-all', // table classes + caption : 'ui-widget-content', + // header class names + header : 'ui-widget-header ui-corner-all ui-state-default', // header classes + sortNone : '', + sortAsc : '', + sortDesc : '', + active : 'ui-state-active', // applied when column is sorted + hover : 'ui-state-hover', // hover class + // icon class names + icons : 'ui-icon', // icon class added to the <i> in the header + iconSortNone : 'ui-icon-carat-2-n-s', // class name added to icon when column is not sorted + iconSortAsc : 'ui-icon-carat-1-n', // class name added to icon when column has ascending sort + iconSortDesc : 'ui-icon-carat-1-s', // class name added to icon when column has descending sort + filterRow : '', + footerRow : '', + footerCells : '', + even : 'ui-widget-content', // even row zebra striping + odd : 'ui-state-default' // odd row zebra striping } - wo.resize_timer = setInterval(function() { - if (wo.resize_flag) { return; } - checkSizes(); - }, options.timer); }; -// Widget: General UI theme -// "uitheme" option in "widgetOptions" -// ************************** +$.extend(ts.css, { + wrapper : 'tablesorter-wrapper' // ui theme & resizable +}); + ts.addWidget({ id: "uitheme", priority: 10, @@ -312,10 +266,13 @@ ts.addWidget({ } }); -// Widget: Column styles -// "columns", "columns_thead" (true) and -// "columns_tfoot" (true) options in "widgetOptions" -// ************************** +})(jQuery); + +/*! Widget: columns */ +;(function ($) { +'use strict'; +var ts = $.tablesorter = $.tablesorter || {}; + ts.addWidget({ id: "columns", priority: 30, @@ -388,14 +345,25 @@ ts.addWidget({ } }); -// Widget: filter -// ************************** +})(jQuery); + +/*! Widget: filter */ +;(function ($) { +'use strict'; +var ts = $.tablesorter = $.tablesorter || {}; + +$.extend(ts.css, { + filterRow : 'tablesorter-filter-row', + filter : 'tablesorter-filter' +}); + ts.addWidget({ id: "filter", priority: 50, options : { filter_childRows : false, // if true, filter includes child row content in the search filter_columnFilters : true, // if true, a filter will be added to the top of each table column + filter_columnAnyMatch: true, // if true, allows using "#:{query}" in AnyMatch searches (column:query) filter_cellFilter : '', // css class name added to the filter cell (string or array) filter_cssFilter : '', // css class name added to the filter row & each input in the row (tablesorter-filter is ALWAYS added) filter_defaultFilter : {}, // add a default column filter type "~{query}" to make fuzzy searches default; "{q1} AND {q2}" to make all searches use a logical AND. @@ -434,7 +402,7 @@ ts.addWidget({ $table .removeClass('hasFilters') // add .tsfilter namespace to all BUT search - .unbind( $.trim(events) ) + .unbind( events.replace(/\s+/g, ' ') ) // remove the filter row even if refreshing, because the column might have been moved .find('.' + ts.css.filterRow).remove(); if (refreshing) { return; } @@ -661,7 +629,7 @@ ts.filter = { } txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter '); - c.$table.bind( $.trim(txt), function(event, filter) { + c.$table.bind( txt, function(event, filter) { val = (wo.filter_hideEmpty && $.isEmptyObject(c.cache) && !(c.delayInit && event.type === 'appendCache')); // hide filter row using the "filtered" class name c.$table.find('.' + ts.css.filterRow).toggleClass(wo.filter_filteredRow, val ); // fixes #450 @@ -754,8 +722,8 @@ ts.filter = { // show processing icon if (c.showProcessing) { c.$table - .unbind( $.trim('filterStart filterEnd '.split(' ').join(c.namespace + 'filter ')) ) - .bind( $.trim('filterStart filterEnd '.split(' ').join(c.namespace + 'filter ')), function(event, columns) { + .unbind( ('filterStart filterEnd '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') ) + .bind( 'filterStart filterEnd '.split(' ').join(c.namespace + 'filter '), function(event, columns) { // only add processing to certain columns to all columns $header = (columns) ? c.$table.find('.' + ts.css.header).filter('[data-column]').filter(function() { return columns[$(this).data('column')] !== ''; @@ -769,8 +737,8 @@ ts.filter = { // add default values c.$table - .unbind( $.trim('tablesorter-initialized pagerBeforeInitialized '.split(' ').join(c.namespace + 'filter ')) ) - .bind( $.trim('tablesorter-initialized pagerBeforeInitialized '.split(' ').join(c.namespace + 'filter ')), function() { + .unbind( ('tablesorter-initialized pagerBeforeInitialized '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') ) + .bind( 'tablesorter-initialized pagerBeforeInitialized '.split(' ').join(c.namespace + 'filter '), function() { // redefine "wo" as it does not update properly inside this callback var wo = this.config.widgetOptions; filters = ts.filter.setDefaults(table, c, wo) || []; @@ -942,7 +910,7 @@ ts.filter = { $el // use data attribute instead of jQuery data since the head is cloned without including the data/binding .attr('data-lastSearchTime', new Date().getTime()) - .unbind( $.trim('keypress keyup search change '.split(' ').join(c.namespace + 'filter ')) ) + .unbind( ('keypress keyup search change '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') ) // include change for select - fixes #473 .bind('keyup' + c.namespace + 'filter', function(event) { $(this).attr('data-lastSearchTime', new Date().getTime()); @@ -963,7 +931,7 @@ ts.filter = { // change event = no delay; last true flag tells getFilters to skip newest timed input ts.filter.searching( table, true, true ); }) - .bind( $.trim('search change keypress '.split(' ').join(c.namespace + 'filter ')), function(event){ + .bind( 'search change keypress '.split(' ').join(c.namespace + 'filter '), function(event){ var column = $(this).data('column'); // don't allow "change" event to process if the input value is the same - fixes #685 if (event.which === 13 || event.type === 'search' || event.type === 'change' && this.value !== c.lastSearch[column]) { @@ -1094,9 +1062,12 @@ ts.filter = { return val; }, getLatestSearch: function( $input ) { - return $input.sort(function(a, b) { - return $(b).attr('data-lastSearchTime') - $(a).attr('data-lastSearchTime'); - }); + if ($input) { + return $input.sort(function(a, b) { + return $(b).attr('data-lastSearchTime') - $(a).attr('data-lastSearchTime'); + }); + } + return $(); }, multipleColumns: function( c, $input ) { // look for multiple columns "1-3,4-6,8" in data-column @@ -1106,7 +1077,7 @@ ts.filter = { // & don't target "all" column inputs if they don't exist targets = wo.filter_initialized || !$input.filter(wo.filter_anyColumnSelector).length, columns = [], - val = $.trim( ts.filter.getLatestSearch( $input ).attr('data-column') ); + val = $.trim( ts.filter.getLatestSearch( $input ).attr('data-column') || '' ); // process column range if ( targets && /-/.test( val ) ) { ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); @@ -1146,13 +1117,13 @@ ts.filter = { }, findRows: function(table, filters, combinedFilters) { if (table.config.lastCombinedFilter === combinedFilters || !table.config.widgetOptions.filter_initialized) { return; } - var len, $rows, rowIndex, tbodyIndex, $tbody, $cells, $cell, columnIndex, + var len, norm_rows, $rows, rowIndex, tbodyIndex, $tbody, $cells, $cell, columnIndex, childRow, lastSearch, hasSelect, matches, result, showRow, time, val, indx, notFiltered, searchFiltered, filterMatched, excludeMatch, fxn, ffxn, + query, injected, res, id, regex = ts.filter.regex, c = table.config, wo = c.widgetOptions, - $tbodies = c.$table.children('tbody'), // target all tbodies #568 // data object passed to filters; anyMatch is a flag for the filters data = { anyMatch: false }, // anyMatch really screws up with these types of filters @@ -1176,14 +1147,14 @@ ts.filter = { // combindedFilters are undefined on init combinedFilters = (filters || []).join(''); - for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { - if ($tbodies.eq(tbodyIndex).hasClass(c.cssInfoBlock || ts.css.info)) { continue; } // ignore info blocks, issue #264 - $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); + for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, c.$tbodies.eq(tbodyIndex), true); // skip child rows & widget added (removable) rows - fixes #448 thanks to @hempel! // $rows = $tbody.children('tr').not(c.selectorRemove); columnIndex = c.columns; // convert stored rows into a jQuery object - $rows = $( $.map(c.cache[tbodyIndex].normalized, function(el){ return el[columnIndex].$row.get(); }) ); + norm_rows = c.cache[tbodyIndex].normalized; + $rows = $( $.map(norm_rows, function(el){ return el[columnIndex].$row.get(); }) ); if (combinedFilters === '' || wo.filter_serversideFiltering) { $rows.removeClass(wo.filter_filteredRow).not('.' + c.cssChildRow).show(); @@ -1191,6 +1162,33 @@ ts.filter = { // filter out child rows $rows = $rows.not('.' + c.cssChildRow); len = $rows.length; + + if ( (wo.filter_$anyMatch && wo.filter_$anyMatch.length) || typeof filters[c.columns] !== 'undefined' ) { + data.anyMatchFlag = true; + data.anyMatchFilter = wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || ( '' + filters[c.columns] ) || ''; + if (wo.filter_columnAnyMatch) { + // specific columns search + query = data.anyMatchFilter.split( ts.filter.regex.andSplit ); + injected = false; + for (indx = 0; indx < query.length; indx++) { + res = query[indx].split(':'); + if ( res.length > 1 ) { + // make the column a one-based index ( non-developers start counting from one :P ) + id = parseInt( res[0], 10 ) - 1; + if ( id >= 0 && id < c.columns ) { // if id is an integer + filters[id] = res[1]; + query.splice(indx, 1); + indx--; + injected = true; + } + } + } + if (injected) { + data.anyMatchFilter = query.join(' && '); + } + } + } + // optimize searching only through already filtered rows - see #313 searchFiltered = wo.filter_searchFiltered; lastSearch = c.lastSearch || c.$table.data('lastSearch') || []; @@ -1220,9 +1218,7 @@ ts.filter = { if (c.debug) { ts.log( "Searching through " + ( searchFiltered && notFiltered < len ? notFiltered : "all" ) + " rows" ); } - if ((wo.filter_$anyMatch && wo.filter_$anyMatch.length) || filters[c.columns]) { - data.anyMatchFlag = true; - data.anyMatchFilter = wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || filters[c.columns] || ''; + if (data.anyMatchFlag) { if (c.sortLocaleCompare) { // replace accents data.anyMatchFilter = ts.replaceAccents(data.anyMatchFilter); @@ -1240,7 +1236,7 @@ ts.filter = { // loop through the rows for (rowIndex = 0; rowIndex < len; rowIndex++) { - data.cacheArray = c.cache[tbodyIndex].normalized[rowIndex]; + data.cacheArray = norm_rows[rowIndex]; childRow = $rows[rowIndex].className; // skip child rows & already filtered rows @@ -1264,7 +1260,7 @@ ts.filter = { if (data.parsed[i]) { txt = data.cacheArray[i]; } else { - txt = this.getAttribute( c.textAttribute ) || this.textContent || $(this).text(); + txt = this ? this.getAttribute( c.textAttribute ) || this.textContent || $(this).text() : ''; txt = $.trim( wo.filter_ignoreCase ? txt.toLowerCase() : txt ); if (c.sortLocaleCompare) { txt = ts.replaceAccents(txt); @@ -1320,7 +1316,7 @@ ts.filter = { data.exact = data.cache; } else { val = $cells[columnIndex]; - result = $.trim( val.getAttribute( c.textAttribute ) || val.textContent || $cells.eq(columnIndex).text() ); + result = val ? $.trim( val.getAttribute( c.textAttribute ) || val.textContent || $cells.eq(columnIndex).text() ) : ''; data.exact = c.sortLocaleCompare ? ts.replaceAccents(result) : result; // issue #405 } data.iExact = !regex.type.test(typeof data.exact) && wo.filter_ignoreCase ? data.exact.toLocaleLowerCase() : data.exact; @@ -1488,26 +1484,23 @@ ts.filter = { var rowIndex, tbodyIndex, len, row, cache, cell, c = table.config, wo = c.widgetOptions, - $tbodies = c.$table.children('tbody'), arry = []; - for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { - if (!$tbodies.eq(tbodyIndex).hasClass(c.cssInfoBlock)) { - cache = c.cache[tbodyIndex]; - len = c.cache[tbodyIndex].normalized.length; - // loop through the rows - for (rowIndex = 0; rowIndex < len; rowIndex++) { - // get cached row from cache.row (old) or row data object (new; last item in normalized array) - row = cache.row ? cache.row[rowIndex] : cache.normalized[rowIndex][c.columns].$row[0]; - // check if has class filtered - if (onlyAvail && row.className.match(wo.filter_filteredRow)) { continue; } - // get non-normalized cell content - if (wo.filter_useParsedData || c.parsers[column].parsed || c.$headers.filter('[data-column="' + column + '"]:last').hasClass('filter-parsed')) { - arry.push( '' + cache.normalized[rowIndex][column] ); - } else { - cell = row.cells[column]; - if (cell) { - arry.push( $.trim( cell.getAttribute( c.textAttribute ) || cell.textContent || $(cell).text() ) ); - } + for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + cache = c.cache[tbodyIndex]; + len = c.cache[tbodyIndex].normalized.length; + // loop through the rows + for (rowIndex = 0; rowIndex < len; rowIndex++) { + // get cached row from cache.row (old) or row data object (new; last item in normalized array) + row = cache.row ? cache.row[rowIndex] : cache.normalized[rowIndex][c.columns].$row[0]; + // check if has class filtered + if (onlyAvail && row.className.match(wo.filter_filteredRow)) { continue; } + // get non-normalized cell content + if (wo.filter_useParsedData || c.parsers[column].parsed || c.$headers.filter('[data-column="' + column + '"]:last').hasClass('filter-parsed')) { + arry.push( '' + cache.normalized[rowIndex][column] ); + } else { + cell = row.cells[column]; + if (cell) { + arry.push( $.trim( cell.getAttribute( c.textAttribute ) || cell.textContent || $(cell).text() ) ); } } } @@ -1660,8 +1653,60 @@ ts.setFilters = function(table, filter, apply, skipFirst) { return !!valid; }; -// Widget: Sticky headers -// based on this awesome article: +})(jQuery); + +/*! Widget: stickyHeaders */ +;(function ($, window) { +'use strict'; +var ts = $.tablesorter = $.tablesorter || {}; + +$.extend(ts.css, { + sticky : 'tablesorter-stickyHeader', // stickyHeader + stickyVis : 'tablesorter-sticky-visible', + stickyWrap: 'tablesorter-sticky-wrapper' +}); + +// Add a resize event to table headers +ts.addHeaderResizeEvent = function(table, disable, settings) { + table = $(table)[0]; // make sure we're using a dom element + var headers, + defaults = { + timer : 250 + }, + options = $.extend({}, defaults, settings), + c = table.config, + wo = c.widgetOptions, + checkSizes = function(triggerEvent) { + wo.resize_flag = true; + headers = []; + c.$headers.each(function() { + var $header = $(this), + sizes = $header.data('savedSizes') || [0,0], // fixes #394 + width = this.offsetWidth, + height = this.offsetHeight; + if (width !== sizes[0] || height !== sizes[1]) { + $header.data('savedSizes', [ width, height ]); + headers.push(this); + } + }); + if (headers.length && triggerEvent !== false) { + c.$table.trigger('resize', [ headers ]); + } + wo.resize_flag = false; + }; + checkSizes(false); + clearInterval(wo.resize_timer); + if (disable) { + wo.resize_flag = false; + return false; + } + wo.resize_timer = setInterval(function() { + if (wo.resize_flag) { return; } + checkSizes(); + }, options.timer); +}; + +// Sticky headers based on this awesome article: // http://css-tricks.com/13465-persistent-headers/ // and https://github.com/jmosbech/StickyTableHeaders by Jonas Mosbech // ************************** @@ -1777,7 +1822,7 @@ ts.addWidget({ // update sticky header class names to match real header after sorting $table .addClass('hasStickyHeaders') - .bind( $.trim('pagerComplete' + namespace), function() { + .bind('pagerComplete' + namespace, function() { resizeHeader(); }); @@ -1796,8 +1841,8 @@ ts.addWidget({ // make it sticky! $xScroll.add($yScroll) - .unbind( $.trim('scroll resize '.split(' ').join( namespace )) ) - .bind( $.trim('scroll resize '.split(' ').join( namespace )), function(event) { + .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') ) + .bind('scroll resize '.split(' ').join( namespace ), function(event) { if (!$table.is(':visible')) { return; } // fixes #278 // Detect nested tables - fixes #724 nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; @@ -1838,7 +1883,7 @@ ts.addWidget({ // look for filter widget if ($table.hasClass('hasFilters') && wo.filter_columnFilters) { // scroll table into view after filtering, if sticky header is active - #482 - $table.bind( $.trim('filterEnd' + namespace), function() { + $table.bind('filterEnd' + namespace, function() { // $(':focus') needs jQuery 1.6+ var $td = $(document.activeElement).closest('td'), column = $td.parent().children().index($td); @@ -1866,19 +1911,29 @@ ts.addWidget({ var namespace = c.namespace + 'stickyheaders '; c.$table .removeClass('hasStickyHeaders') - .unbind( $.trim('pagerComplete filterEnd '.split(' ').join(namespace)) ) + .unbind( ('pagerComplete filterEnd '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) .next('.' + ts.css.stickyWrap).remove(); if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table $(window) .add(wo.stickyHeaders_xScroll) .add(wo.stickyHeaders_yScroll) .add(wo.stickyHeaders_attachTo) - .unbind( $.trim('scroll resize '.split(' ').join(namespace)) ); + .unbind( ('scroll resize '.split(' ').join(namespace)).replace(/\s+/g, ' ') ); ts.addHeaderResizeEvent(table, false); } }); -// Add Column resizing widget +})(jQuery, window); + +/*! Widget: resizable */ +;(function ($, window) { +'use strict'; +var ts = $.tablesorter = $.tablesorter || {}; + +$.extend(ts.css, { + resizer : 'tablesorter-resizer' // resizable +}); + // this widget saves the column widths if // $.tablesorter.storage function is included // ************************** @@ -2045,7 +2100,13 @@ ts.resizableReset = function(table, nosave) { }); }; -// Save table sort widget +})(jQuery, window); + +/*! Widget: saveSort */ +;(function ($) { +'use strict'; +var ts = $.tablesorter = $.tablesorter || {}; + // this widget saves the last sort only if the // saveSort widget option is true AND the // $.tablesorter.storage function is included @@ -2108,4 +2169,4 @@ ts.addWidget({ } }); -})(jQuery, window); +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js index c64a82c..0a328e7 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js @@ -49,7 +49,7 @@ types = $.tablesorter.fileTypes.equivalents; if (!m) { // make a string to "quick" match the existing equivalents - var t = []; + t = []; $.each(types, function(i,v){ t.push(v); }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index 131c882..d684cbb 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -141,7 +141,7 @@ return restoreValue(); } // ignore change event if nothing changed - if ($tar.val() !== $tar.data('ts-original-value')) { + if ($tar.val() !== $tar.data('ts-original-value') || e.target.type === 'checkbox') { $tar.data('ts-original-value', $tar.val()); // pass undefined resort value so it falls back to config.resort setting $table.trigger('updateCell', [ $tar.closest('td'), undef, function(){ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js index 8048ce0..4ec4370 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js @@ -149,14 +149,13 @@ }, getRows: function(c, wo) { - // the cache may not have a zero index if there are any "info-only" tbodies above the main tbody - var cache = c.cache[0].normalized, + var norm_rows = c.cache[0].normalized, rows = []; chart_rows = []; chart_categories = []; chart_category = []; - $.each(cache, function(indx, rowVal) { + $.each(norm_rows, function(indx, rowVal) { var i, txt, $tr = rowVal[c.columns].$row, $cells = $tr.children('th,td'), diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columns.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columns.js new file mode 100644 index 0000000..e37751b --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columns.js @@ -0,0 +1,78 @@ +/*! Widget: columns */ +;(function ($) { +'use strict'; +var ts = $.tablesorter = $.tablesorter || {}; + +ts.addWidget({ + id: "columns", + priority: 30, + options : { + columns : [ "primary", "secondary", "tertiary" ] + }, + format: function(table, c, wo) { + var $tbody, tbodyIndex, $rows, rows, $row, $cells, remove, indx, + $table = c.$table, + $tbodies = c.$tbodies, + sortList = c.sortList, + len = sortList.length, + // removed c.widgetColumns support + css = wo && wo.columns || [ "primary", "secondary", "tertiary" ], + last = css.length - 1; + remove = css.join(' '); + // check if there is a sort (on initialization there may not be one) + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // detach tbody + $rows = $tbody.children('tr'); + // loop through the visible rows + $rows.each(function() { + $row = $(this); + if (this.style.display !== 'none') { + // remove all columns class names + $cells = $row.children().removeClass(remove); + // add appropriate column class names + if (sortList && sortList[0]) { + // primary sort column class + $cells.eq(sortList[0][0]).addClass(css[0]); + if (len > 1) { + for (indx = 1; indx < len; indx++) { + // secondary, tertiary, etc sort column classes + $cells.eq(sortList[indx][0]).addClass( css[indx] || css[last] ); + } + } + } + } + }); + ts.processTbody(table, $tbody, false); + } + // add classes to thead and tfoot + rows = wo.columns_thead !== false ? ['thead tr'] : []; + if (wo.columns_tfoot !== false) { + rows.push('tfoot tr'); + } + if (rows.length) { + $rows = $table.find( rows.join(',') ).children().removeClass(remove); + if (len) { + for (indx = 0; indx < len; indx++) { + // add primary. secondary, tertiary, etc sort column classes + $rows.filter('[data-column="' + sortList[indx][0] + '"]').addClass(css[indx] || css[last]); + } + } + } + }, + remove: function(table, c, wo) { + var tbodyIndex, $tbody, + $tbodies = c.$tbodies, + remove = (wo.columns || [ "primary", "secondary", "tertiary" ]).join(' '); + c.$headers.removeClass(remove); + c.$table.children('tfoot').children('tr').children('th, td').removeClass(remove); + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody + $tbody.children('tr').each(function() { + $(this).children().removeClass(remove); + }); + ts.processTbody(table, $tbody, false); // restore tbody + } + } +}); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js index c4e3f02..6185e82 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js @@ -2,7 +2,7 @@ * Requires a modern browser, tablesorter v2.8+ */ /*jshint jquery:true, unused:false */ -;(function($){ +;(function($, window){ 'use strict'; var ts = $.tablesorter; @@ -59,8 +59,8 @@ } $win - .unbind( $.trim('scroll resize '.split(' ').join(namespace)) ) - .bind( $.trim('scroll resize '.split(' ').join(namespace)), function() { + .unbind( ('scroll resize '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) + .bind('scroll resize '.split(' ').join(namespace), function() { // make sure "wo" is current otherwise changes to widgetOptions // are not dynamic (like the add caption button in the demo) wo = c.widgetOptions; @@ -126,20 +126,22 @@ setTransform( $cells, finalY ); }); - $table.unbind( $.trim('filterEnd' + namespace) ).bind( $.trim('filterEnd' + namespace), function() { - if (wo.cssStickyHeaders_filteredToTop) { - // scroll top of table into view - window.scrollTo(0, $table.position().top); - } - }); + $table + .unbind( ('filterEnd' + namespace).replace(/\s+/g, ' ') ) + .bind('filterEnd' + namespace, function() { + if (wo.cssStickyHeaders_filteredToTop) { + // scroll top of table into view + window.scrollTo(0, $table.position().top); + } + }); }, remove: function(table, c, wo, refreshing) { if (refreshing) { return; } var namespace = c.namespace + 'cssstickyheader '; - $(window).unbind( $.trim('scroll resize '.split(' ').join(namespace)) ); + $(window).unbind( ('scroll resize '.split(' ').join(namespace)).replace(/\s+/g, ' ') ); c.$table - .unbind( $.trim('filterEnd scroll resize '.split(' ').join(namespace)) ) + .unbind( ('filterEnd scroll resize '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) .add( c.$table.children('thead').children().children() ) .children('thead, caption').css({ 'transform' : '', @@ -149,4 +151,4 @@ } }); -})(jQuery); +})(jQuery, window); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js index d536e83..4faa8bb 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js @@ -95,13 +95,13 @@ var tse = $.tablesorter.editable = { bindEvents: function( c, wo ) { c.$table - .off( $.trim( 'updateComplete pagerComplete '.split( ' ' ).join( '.tseditable' ) ) ) - .on( $.trim( 'updateComplete pagerComplete '.split( ' ' ).join( '.tseditable' ) ), function() { + .off( ( 'updateComplete pagerComplete '.split( ' ' ).join( '.tseditable' ) ).replace( /\s+/g, ' ' ) ) + .on( 'updateComplete pagerComplete '.split( ' ' ).join( '.tseditable' ), function() { tse.update( c, c.widgetOptions ); }); c.$tbodies - .off( $.trim( 'mouseleave focus blur focusout keydown '.split( ' ' ).join( '.tseditable ' ) ) ) + .off( ( 'mouseleave focus blur focusout keydown '.split( ' ' ).join( '.tseditable ' ) ).replace( /\s+/g, ' ' ) ) .on( 'mouseleave.tseditable', function() { if ( c.$table.data( 'contentFocused' ) ) { // change to 'true' instead of element to allow focusout to process @@ -140,7 +140,7 @@ var tse = $.tablesorter.editable = { } } }) - .on( $.trim( 'blur focusout keydown '.split( ' ' ).join( '.tseditable ' ) ), '[contenteditable]', function( e ) { + .on( 'blur focusout keydown '.split( ' ' ).join( '.tseditable ' ), '[contenteditable]', function( e ) { if ( !c.$table.data( 'contentFocused' ) ) { return; } var t, validate, valid = false, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js new file mode 100644 index 0000000..d72a969 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js @@ -0,0 +1,429 @@ +/*! Filter widget formatter html5 functions *//* updated 7/17/2014 (v2.17.5) + * requires: tableSorter (FORK) 2.15+ and jQuery 1.4.3+ + * + * html5Number (spinner) + * html5Range (slider) + * html5Color (color) + */ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;(function($){ +"use strict"; + +var ts = $.tablesorter || {}, + +// compare option selector class name (jQuery selector) +compareSelect = '.compare-select', + + +tsff = ts.filterFormatter = { + + addCompare: function($cell, indx, options){ + if (options.compare && $.isArray(options.compare) && options.compare.length > 1) { + var opt = '', + compareSelectClass = [ compareSelect.slice(1), ' ' + compareSelect.slice(1), '' ], + txt = options.cellText ? '<label class="' + compareSelectClass.join('-label') + indx + '">' + options.cellText + '</label>' : ''; + $.each(options.compare, function(i, c){ + opt += '<option ' + (options.selected === i ? 'selected' : '') + '>' + c + '</option>'; + }); + $cell + .wrapInner('<div class="' + compareSelectClass.join('-wrapper') + indx + '" />') + .prepend( txt + '<select class="' + compareSelectClass.join('') + indx + '" />' ) + .find('select') + .append(opt); + } + }, + + updateCompare : function($cell, $input, o) { + var val = $input.val() || '', + num = val.replace(/\s*?[><=]\s*?/g, ''), + compare = val.match(/[><=]/g) || ''; + if (o.compare) { + if ($.isArray(o.compare)){ + compare = (compare || []).join('') || o.compare[o.selected || 0]; + } + $cell.find(compareSelect).val( compare ); + } + return [ val, num ]; + }, + + /**********************\ + HTML5 Number (spinner) + \**********************/ + html5Number : function($cell, indx, def5Num) { + var t, o = $.extend({ + value : 0, + min : 0, + max : 100, + step : 1, + delayed : true, + disabled : false, + addToggle : false, + exactMatch : false, + cellText : '', + compare : '', + skipTest: false + }, def5Num), + + $input, + // test browser for HTML5 range support + $number = $('<input type="number" style="visibility:hidden;" value="test">').appendTo($cell), + // test if HTML5 number is supported - from Modernizr + numberSupported = o.skipTest || $number.attr('type') === 'number' && $number.val() !== 'test', + $shcell = [], + c = $cell.closest('table')[0].config, + + updateNumber = function(delayed, notrigger){ + var chkd = o.addToggle ? $cell.find('.toggle').is(':checked') : true, + v = $cell.find('.number').val(), + compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', + searchType = c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) || '' : true; + $input + // add equal to the beginning, so we filter exact numbers + .val( !o.addToggle || chkd ? (compare ? compare : o.exactMatch ? '=' : '') + v : '' ) + .trigger( notrigger ? '' : 'search', searchType ).end() + .find('.number').val(v); + if ($cell.find('.number').length) { + $cell.find('.number')[0].disabled = (o.disabled || !chkd); + } + // update sticky header cell + if ($shcell.length) { + $shcell.find('.number').val(v)[0].disabled = (o.disabled || !chkd); + $shcell.find(compareSelect).val(compare); + if (o.addToggle) { + $shcell.find('.toggle')[0].checked = chkd; + } + } + }; + $number.remove(); + + if (numberSupported) { + t = o.addToggle ? '<div class="button"><input id="html5button' + indx + '" type="checkbox" class="toggle" />' + + '<label for="html5button' + indx + '"></label></div>' : ''; + t += '<input class="number" type="number" min="' + o.min + '" max="' + o.max + '" value="' + + o.value + '" step="' + o.step + '" />'; + // add HTML5 number (spinner) + $cell + .append(t + '<input type="hidden" />') + .find('.toggle, .number').bind('change', function(){ + updateNumber(); + }) + .closest('thead').find('th[data-column=' + indx + ']') + .addClass('filter-parsed') // get exact numbers from column + // on reset + .closest('table').bind('filterReset', function(){ + if ($.isArray(o.compare)) { + $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); + } + // turn off the toggle checkbox + if (o.addToggle) { + $cell.find('.toggle')[0].checked = false; + if ($shcell.length) { + $shcell.find('.toggle')[0].checked = false; + } + } + $cell.find('.number').val( o.value ); + setTimeout(function(){ + updateNumber(); + }, 0); + }); + $input = $cell.find('input[type=hidden]').bind('change', function(){ + $cell.find('.number').val( this.value ); + updateNumber(); + }); + + // update slider from hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + var val = tsff.updateCompare($cell, $input, o)[0] || o.value; + $cell.find('.number').val( ((val || '') + '').replace(/[><=]/g,'') ); + updateNumber(false, true); + ts.filter.formatterUpdated($cell, indx); + }); + + if (o.compare) { + // add compare select + tsff.addCompare($cell, indx, o); + $cell.find(compareSelect).bind('change', function(){ + updateNumber(); + }); + } + + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + $shcell + .append(t) + .find('.toggle, .number').bind('change', function(){ + $cell.find('.number').val( $(this).val() ); + updateNumber(); + }); + + if (o.compare) { + // add compare select + tsff.addCompare($shcell, indx, o); + $shcell.find(compareSelect).bind('change', function(){ + $cell.find(compareSelect).val( $(this).val() ); + updateNumber(); + }); + } + + updateNumber(); + }); + + updateNumber(); + + } + + return numberSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">'); + }, + + /**********************\ + HTML5 range slider + \**********************/ + html5Range : function($cell, indx, def5Range) { + var o = $.extend({ + value : 0, + min : 0, + max : 100, + step : 1, + delayed : true, + valueToHeader : true, + exactMatch : true, + cellText : '', + compare : '', + allText : 'all', + skipTest : false + }, def5Range), + + $input, + // test browser for HTML5 range support + $range = $('<input type="range" style="visibility:hidden;" value="test">').appendTo($cell), + // test if HTML5 range is supported - from Modernizr (but I left out the method to detect in Safari 2-4) + // see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/inputtypes.js + rangeSupported = o.skipTest || $range.attr('type') === 'range' && $range.val() !== 'test', + $shcell = [], + c = $cell.closest('table')[0].config, + + updateRange = function(v, delayed, notrigger){ + /*jshint eqeqeq:false */ + // hidden input changes may include compare symbols + v = ( typeof v === "undefined" ? $input.val() : v ).toString().replace(/[<>=]/g,'') || o.value; + var compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', + t = ' (' + (compare ? compare + v : v == o.min ? o.allText : v) + ')', + searchType = c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) || '' : true; + $cell.find('input[type=hidden]') + // add equal to the beginning, so we filter exact numbers + .val( ( compare ? compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ) ) + //( val == o.min ? '' : val + (o.exactMatch ? '=' : '')) + .trigger( notrigger ? '' : 'search', searchType ).end() + .find('.range').val(v); + // or add current value to the header cell, if desired + $cell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(t); + // update sticky header cell + if ($shcell.length) { + $shcell + .find('.range').val(v).end() + .find(compareSelect).val( compare ); + $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(t); + } + }; + $range.remove(); + + if (rangeSupported) { + // add HTML5 range + $cell + .html('<input type="hidden"><input class="range" type="range" min="' + o.min + '" max="' + o.max + '" value="' + o.value + '" />') + .closest('thead').find('th[data-column=' + indx + ']') + .addClass('filter-parsed') // get exact numbers from column + // add span to header for the current slider value + .find('.tablesorter-header-inner').append('<span class="curvalue" />'); + // hidden filter update namespace trigger by filter widget + $input = $cell.find('input[type=hidden]').bind('change' + c.namespace + 'filter', function(){ + /*jshint eqeqeq:false */ + var v = this.value, + compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || ''; + if (v !== this.lastValue) { + this.lastValue = ( compare ? compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ); + this.value = this.lastValue; + updateRange( v ); + } + }); + + $cell.find('.range').bind('change', function(){ + updateRange( this.value ); + }); + + // update spinner from hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + var val = tsff.updateCompare($cell, $input, o)[0]; + $cell.find('.range').val( val ); + updateRange(val, false, true); + ts.filter.formatterUpdated($cell, indx); + }); + + if (o.compare) { + // add compare select + tsff.addCompare($cell, indx, o); + $cell.find(compareSelect).bind('change', function(){ + updateRange(); + }); + } + + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + $shcell + .html('<input class="range" type="range" min="' + o.min + '" max="' + o.max + '" value="' + o.value + '" />') + .find('.range').bind('change', function(){ + updateRange( $shcell.find('.range').val() ); + }); + updateRange(); + + if (o.compare) { + // add compare select + tsff.addCompare($shcell, indx, o); + $shcell.find(compareSelect).bind('change', function(){ + $cell.find(compareSelect).val( $(this).val() ); + updateRange(); + }); + } + + }); + + // on reset + $cell.closest('table').bind('filterReset', function(){ + if ($.isArray(o.compare)) { + $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); + } + setTimeout(function(){ + updateRange(o.value, false, true); + }, 0); + }); + updateRange(); + + } + + return rangeSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">'); + }, + + /**********************\ + HTML5 Color picker + \**********************/ + html5Color: function($cell, indx, defColor) { + var t, o = $.extend({ + value : '#000000', + disabled : false, + addToggle : true, + exactMatch : true, + valueToHeader : false, + skipTest : false + }, defColor), + $input, + // Add a hidden input to hold the range values + $color = $('<input type="color" style="visibility:hidden;" value="test">').appendTo($cell), + // test if HTML5 color is supported - from Modernizr + colorSupported = o.skipTest || $color.attr('type') === 'color' && $color.val() !== 'test', + $shcell = [], + c = $cell.closest('table')[0].config, + + updateColor = function(v, notrigger){ + v = ( typeof v === "undefined" ? $input.val() : v ).toString().replace('=','') || o.value; + var chkd = true, + t = ' (' + v + ')'; + if (o.addToggle) { + chkd = $cell.find('.toggle').is(':checked'); + } + if ($cell.find('.colorpicker').length) { + $cell.find('.colorpicker').val(v)[0].disabled = (o.disabled || !chkd); + } + + $input + .val( chkd ? v + (o.exactMatch ? '=' : '') : '' ) + .trigger( !c.$table[0].hasInitialized || notrigger ? '' : 'search' ); + if (o.valueToHeader) { + // add current color to the header cell + $cell.closest('thead').find('th[data-column=' + indx + ']').find('.curcolor').html(t); + } else { + // current color to span in cell + $cell.find('.currentColor').html(t); + } + + // update sticky header cell + if ($shcell.length) { + $shcell.find('.colorpicker').val(v)[0].disabled = (o.disabled || !chkd); + if (o.addToggle) { + $shcell.find('.toggle')[0].checked = chkd; + } + if (o.valueToHeader) { + // add current color to the header cell + $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curcolor').html(t); + } else { + // current color to span in cell + $shcell.find('.currentColor').html(t); + } + } + }; + $color.remove(); + + if (colorSupported) { + t = '' + indx + Math.round(Math.random() * 100); + // add HTML5 color picker + t = '<div class="color-controls-wrapper">' + + (o.addToggle ? '<div class="button"><input id="colorbutton' + t + '" type="checkbox" class="toggle" /><label for="colorbutton' + + t + '"></label></div>' : '') + + '<input type="hidden"><input class="colorpicker" type="color" />' + + (o.valueToHeader ? '' : '<span class="currentColor">(#000000)</span>') + '</div>'; + $cell.html(t); + // add span to header for the current color value - only works if the line in the updateColor() function is also un-commented out + if (o.valueToHeader) { + $cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="curcolor" />'); + } + + $cell.find('.toggle, .colorpicker').bind('change', function(){ + updateColor( $cell.find('.colorpicker').val() ); + }); + + // hidden filter update namespace trigger by filter widget + $input = $cell.find('input[type=hidden]').bind('change' + c.namespace + 'filter', function(){ + updateColor( this.value ); + }); + + // update slider from hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + updateColor( $input.val(), true ); + ts.filter.formatterUpdated($cell, indx); + }); + + // on reset + $cell.closest('table').bind('filterReset', function(){ + // just turn off the colorpicker + if (o.addToggle) { + $cell.find('.toggle')[0].checked = false; + } + // delay needed because default color needs to be set in the filter + // there is no compare option here, so if addToggle = false, + // default color is #000000 (even with no value set) + setTimeout(function(){ + updateColor(); + }, 0); + }); + + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx); + $shcell + .html(t) + .find('.toggle, .colorpicker').bind('change', function(){ + updateColor( $shcell.find('.colorpicker').val() ); + }); + updateColor( $shcell.find('.colorpicker').val() ); + }); + + updateColor( o.value ); + } + return colorSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">'); + } + +}; + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js similarity index 65% rename from vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js rename to vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js index 77050cd..4c30caa 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js @@ -1,4 +1,4 @@ -/*! Filter widget formatter functions - updated 7/17/2014 (v2.17.5) +/*! Filter widget formatter jQuery UI functions *//* updated 7/17/2014 (v2.17.5) * requires: tableSorter (FORK) 2.15+ and jQuery 1.4.3+ * * uiSpinner (jQuery UI spinner) @@ -6,9 +6,6 @@ * uiRange (jQuery UI range slider) * uiDateCompare (jQuery UI datepicker; 1 input) * uiDatepicker (jQuery UI datepicker; 2 inputs, filter range) - * html5Number (spinner) - * html5Range (slider) - * html5Color (color) */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ @@ -761,383 +758,6 @@ tsff = ts.filterFormatter = { // return the hidden input so the filter widget has a reference to it return $input.val( o.from ? ( o.to ? o.from + ' - ' + o.to : '>=' + o.from ) : (o.to ? '<=' + o.to : '') ); - }, - - /**********************\ - HTML5 Number (spinner) - \**********************/ - html5Number : function($cell, indx, def5Num) { - var t, o = $.extend({ - value : 0, - min : 0, - max : 100, - step : 1, - delayed : true, - disabled : false, - addToggle : false, - exactMatch : false, - cellText : '', - compare : '', - skipTest: false - }, def5Num), - - $input, - // test browser for HTML5 range support - $number = $('<input type="number" style="visibility:hidden;" value="test">').appendTo($cell), - // test if HTML5 number is supported - from Modernizr - numberSupported = o.skipTest || $number.attr('type') === 'number' && $number.val() !== 'test', - $shcell = [], - c = $cell.closest('table')[0].config, - - updateNumber = function(delayed, notrigger){ - var chkd = o.addToggle ? $cell.find('.toggle').is(':checked') : true, - v = $cell.find('.number').val(), - compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', - searchType = c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) || '' : true; - $input - // add equal to the beginning, so we filter exact numbers - .val( !o.addToggle || chkd ? (compare ? compare : o.exactMatch ? '=' : '') + v : '' ) - .trigger( notrigger ? '' : 'search', searchType ).end() - .find('.number').val(v); - if ($cell.find('.number').length) { - $cell.find('.number')[0].disabled = (o.disabled || !chkd); - } - // update sticky header cell - if ($shcell.length) { - $shcell.find('.number').val(v)[0].disabled = (o.disabled || !chkd); - $shcell.find(compareSelect).val(compare); - if (o.addToggle) { - $shcell.find('.toggle')[0].checked = chkd; - } - } - }; - $number.remove(); - - if (numberSupported) { - t = o.addToggle ? '<div class="button"><input id="html5button' + indx + '" type="checkbox" class="toggle" />' + - '<label for="html5button' + indx + '"></label></div>' : ''; - t += '<input class="number" type="number" min="' + o.min + '" max="' + o.max + '" value="' + - o.value + '" step="' + o.step + '" />'; - // add HTML5 number (spinner) - $cell - .append(t + '<input type="hidden" />') - .find('.toggle, .number').bind('change', function(){ - updateNumber(); - }) - .closest('thead').find('th[data-column=' + indx + ']') - .addClass('filter-parsed') // get exact numbers from column - // on reset - .closest('table').bind('filterReset', function(){ - if ($.isArray(o.compare)) { - $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); - } - // turn off the toggle checkbox - if (o.addToggle) { - $cell.find('.toggle')[0].checked = false; - if ($shcell.length) { - $shcell.find('.toggle')[0].checked = false; - } - } - $cell.find('.number').val( o.value ); - setTimeout(function(){ - updateNumber(); - }, 0); - }); - $input = $cell.find('input[type=hidden]').bind('change', function(){ - $cell.find('.number').val( this.value ); - updateNumber(); - }); - - // update slider from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ - var val = tsff.updateCompare($cell, $input, o)[0] || o.value; - $cell.find('.number').val( ((val || '') + '').replace(/[><=]/g,'') ); - updateNumber(false, true); - ts.filter.formatterUpdated($cell, indx); - }); - - if (o.compare) { - // add compare select - tsff.addCompare($cell, indx, o); - $cell.find(compareSelect).bind('change', function(){ - updateNumber(); - }); - } - - // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ - $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); - $shcell - .append(t) - .find('.toggle, .number').bind('change', function(){ - $cell.find('.number').val( $(this).val() ); - updateNumber(); - }); - - if (o.compare) { - // add compare select - tsff.addCompare($shcell, indx, o); - $shcell.find(compareSelect).bind('change', function(){ - $cell.find(compareSelect).val( $(this).val() ); - updateNumber(); - }); - } - - updateNumber(); - }); - - updateNumber(); - - } - - return numberSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">'); - }, - - /**********************\ - HTML5 range slider - \**********************/ - html5Range : function($cell, indx, def5Range) { - var o = $.extend({ - value : 0, - min : 0, - max : 100, - step : 1, - delayed : true, - valueToHeader : true, - exactMatch : true, - cellText : '', - compare : '', - allText : 'all', - skipTest : false - }, def5Range), - - $input, - // test browser for HTML5 range support - $range = $('<input type="range" style="visibility:hidden;" value="test">').appendTo($cell), - // test if HTML5 range is supported - from Modernizr (but I left out the method to detect in Safari 2-4) - // see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/inputtypes.js - rangeSupported = o.skipTest || $range.attr('type') === 'range' && $range.val() !== 'test', - $shcell = [], - c = $cell.closest('table')[0].config, - - updateRange = function(v, delayed, notrigger){ - /*jshint eqeqeq:false */ - // hidden input changes may include compare symbols - v = ( typeof v === "undefined" ? $input.val() : v ).toString().replace(/[<>=]/g,'') || o.value; - var compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', - t = ' (' + (compare ? compare + v : v == o.min ? o.allText : v) + ')', - searchType = c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) || '' : true; - $cell.find('input[type=hidden]') - // add equal to the beginning, so we filter exact numbers - .val( ( compare ? compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ) ) - //( val == o.min ? '' : val + (o.exactMatch ? '=' : '')) - .trigger( notrigger ? '' : 'search', searchType ).end() - .find('.range').val(v); - // or add current value to the header cell, if desired - $cell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(t); - // update sticky header cell - if ($shcell.length) { - $shcell - .find('.range').val(v).end() - .find(compareSelect).val( compare ); - $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(t); - } - }; - $range.remove(); - - if (rangeSupported) { - // add HTML5 range - $cell - .html('<input type="hidden"><input class="range" type="range" min="' + o.min + '" max="' + o.max + '" value="' + o.value + '" />') - .closest('thead').find('th[data-column=' + indx + ']') - .addClass('filter-parsed') // get exact numbers from column - // add span to header for the current slider value - .find('.tablesorter-header-inner').append('<span class="curvalue" />'); - // hidden filter update namespace trigger by filter widget - $input = $cell.find('input[type=hidden]').bind('change' + c.namespace + 'filter', function(){ - /*jshint eqeqeq:false */ - var v = this.value, - compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || ''; - if (v !== this.lastValue) { - this.lastValue = ( compare ? compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ); - this.value = this.lastValue; - updateRange( v ); - } - }); - - $cell.find('.range').bind('change', function(){ - updateRange( this.value ); - }); - - // update spinner from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ - var val = tsff.updateCompare($cell, $input, o)[0]; - $cell.find('.range').val( val ); - updateRange(val, false, true); - ts.filter.formatterUpdated($cell, indx); - }); - - if (o.compare) { - // add compare select - tsff.addCompare($cell, indx, o); - $cell.find(compareSelect).bind('change', function(){ - updateRange(); - }); - } - - // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ - $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); - $shcell - .html('<input class="range" type="range" min="' + o.min + '" max="' + o.max + '" value="' + o.value + '" />') - .find('.range').bind('change', function(){ - updateRange( $shcell.find('.range').val() ); - }); - updateRange(); - - if (o.compare) { - // add compare select - tsff.addCompare($shcell, indx, o); - $shcell.find(compareSelect).bind('change', function(){ - $cell.find(compareSelect).val( $(this).val() ); - updateRange(); - }); - } - - }); - - // on reset - $cell.closest('table').bind('filterReset', function(){ - if ($.isArray(o.compare)) { - $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); - } - setTimeout(function(){ - updateRange(o.value, false, true); - }, 0); - }); - updateRange(); - - } - - return rangeSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">'); - }, - - /**********************\ - HTML5 Color picker - \**********************/ - html5Color: function($cell, indx, defColor) { - var t, o = $.extend({ - value : '#000000', - disabled : false, - addToggle : true, - exactMatch : true, - valueToHeader : false, - skipTest : false - }, defColor), - $input, - // Add a hidden input to hold the range values - $color = $('<input type="color" style="visibility:hidden;" value="test">').appendTo($cell), - // test if HTML5 color is supported - from Modernizr - colorSupported = o.skipTest || $color.attr('type') === 'color' && $color.val() !== 'test', - $shcell = [], - c = $cell.closest('table')[0].config, - - updateColor = function(v, notrigger){ - v = ( typeof v === "undefined" ? $input.val() : v ).toString().replace('=','') || o.value; - var chkd = true, - t = ' (' + v + ')'; - if (o.addToggle) { - chkd = $cell.find('.toggle').is(':checked'); - } - if ($cell.find('.colorpicker').length) { - $cell.find('.colorpicker').val(v)[0].disabled = (o.disabled || !chkd); - } - - $input - .val( chkd ? v + (o.exactMatch ? '=' : '') : '' ) - .trigger( !c.$table[0].hasInitialized || notrigger ? '' : 'search' ); - if (o.valueToHeader) { - // add current color to the header cell - $cell.closest('thead').find('th[data-column=' + indx + ']').find('.curcolor').html(t); - } else { - // current color to span in cell - $cell.find('.currentColor').html(t); - } - - // update sticky header cell - if ($shcell.length) { - $shcell.find('.colorpicker').val(v)[0].disabled = (o.disabled || !chkd); - if (o.addToggle) { - $shcell.find('.toggle')[0].checked = chkd; - } - if (o.valueToHeader) { - // add current color to the header cell - $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curcolor').html(t); - } else { - // current color to span in cell - $shcell.find('.currentColor').html(t); - } - } - }; - $color.remove(); - - if (colorSupported) { - t = '' + indx + Math.round(Math.random() * 100); - // add HTML5 color picker - t = '<div class="color-controls-wrapper">' + - (o.addToggle ? '<div class="button"><input id="colorbutton' + t + '" type="checkbox" class="toggle" /><label for="colorbutton' + - t + '"></label></div>' : '') + - '<input type="hidden"><input class="colorpicker" type="color" />' + - (o.valueToHeader ? '' : '<span class="currentColor">(#000000)</span>') + '</div>'; - $cell.html(t); - // add span to header for the current color value - only works if the line in the updateColor() function is also un-commented out - if (o.valueToHeader) { - $cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="curcolor" />'); - } - - $cell.find('.toggle, .colorpicker').bind('change', function(){ - updateColor( $cell.find('.colorpicker').val() ); - }); - - // hidden filter update namespace trigger by filter widget - $input = $cell.find('input[type=hidden]').bind('change' + c.namespace + 'filter', function(){ - updateColor( this.value ); - }); - - // update slider from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ - updateColor( $input.val(), true ); - ts.filter.formatterUpdated($cell, indx); - }); - - // on reset - $cell.closest('table').bind('filterReset', function(){ - // just turn off the colorpicker - if (o.addToggle) { - $cell.find('.toggle')[0].checked = false; - } - // delay needed because default color needs to be set in the filter - // there is no compare option here, so if addToggle = false, - // default color is #000000 (even with no value set) - setTimeout(function(){ - updateColor(); - }, 0); - }); - - // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ - $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx); - $shcell - .html(t) - .find('.toggle, .colorpicker').bind('change', function(){ - updateColor( $shcell.find('.colorpicker').val() ); - }); - updateColor( $shcell.find('.colorpicker').val() ); - }); - - updateColor( o.value ); - } - return colorSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">'); } }; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js similarity index 100% rename from vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter-select2.js rename to vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js new file mode 100644 index 0000000..1b0b163 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -0,0 +1,1307 @@ +/*! Widget: filter */ +;(function ($) { +'use strict'; +var ts = $.tablesorter = $.tablesorter || {}; + +$.extend(ts.css, { + filterRow : 'tablesorter-filter-row', + filter : 'tablesorter-filter' +}); + +ts.addWidget({ + id: "filter", + priority: 50, + options : { + filter_childRows : false, // if true, filter includes child row content in the search + filter_columnFilters : true, // if true, a filter will be added to the top of each table column + filter_columnAnyMatch: true, // if true, allows using "#:{query}" in AnyMatch searches (column:query) + filter_cellFilter : '', // css class name added to the filter cell (string or array) + filter_cssFilter : '', // css class name added to the filter row & each input in the row (tablesorter-filter is ALWAYS added) + filter_defaultFilter : {}, // add a default column filter type "~{query}" to make fuzzy searches default; "{q1} AND {q2}" to make all searches use a logical AND. + filter_excludeFilter : {}, // filters to exclude, per column + filter_external : '', // jQuery selector string (or jQuery object) of external filters + filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin + filter_formatter : null, // add custom filter elements to the filter row + filter_functions : null, // add custom filter functions using this option + filter_hideEmpty : true, // hide filter row when table is empty + filter_hideFilters : false, // collapse filter row when mouse leaves the area + filter_ignoreCase : true, // if true, make all searches case-insensitive + filter_liveSearch : true, // if true, search column content while the user types (with a delay) + filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available (visible) options within the drop down + filter_placeholder : { search : '', select : '' }, // default placeholder text (overridden by any header "data-placeholder" setting) + filter_reset : null, // jQuery selector string of an element used to reset the filters + filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters + filter_searchDelay : 300, // typing delay in milliseconds before starting a search + filter_searchFiltered: true, // allow searching through already filtered rows in special circumstances; will speed up searching in large tables if true + filter_selectSource : null, // include a function to return an array of values to be added to the column filter select + filter_startsWith : false, // if true, filter start from the beginning of the cell contents + filter_useParsedData : false, // filter all data using parsed content + filter_serversideFiltering : false, // if true, server-side filtering should be performed because client-side filtering will be disabled, but the ui and events will still be used. + filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value + filter_selectSourceSeparator : '|' // filter_selectSource array text left of the separator is added to the option value, right into the option text + }, + format: function(table, c, wo) { + if (!c.$table.hasClass('hasFilters')) { + ts.filter.init(table, c, wo); + } + }, + remove: function(table, c, wo, refreshing) { + var tbodyIndex, $tbody, + $table = c.$table, + $tbodies = c.$tbodies, + events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter '); + $table + .removeClass('hasFilters') + // add .tsfilter namespace to all BUT search + .unbind( events.replace(/\s+/g, ' ') ) + // remove the filter row even if refreshing, because the column might have been moved + .find('.' + ts.css.filterRow).remove(); + if (refreshing) { return; } + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody + $tbody.children().removeClass(wo.filter_filteredRow).show(); + ts.processTbody(table, $tbody, false); // restore tbody + } + if (wo.filter_reset) { + $(document).undelegate(wo.filter_reset, 'click.tsfilter'); + } + } +}); + +ts.filter = { + + // regex used in filter "check" functions - not for general use and not documented + regex: { + regex : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // regex to test for regex + child : /tablesorter-childRow/, // child row class name; this gets updated in the script + filtered : /filtered/, // filtered (hidden) row class name; updated in the script + type : /undefined|number/, // check type + exact : /(^[\"\'=]+)|([\"\'=]+$)/g, // exact match (allow '==') + nondigit : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser) + operators : /[<>=]/g, // replace operators + query : '(q|query)' // replace filter queries + }, + // function( c, data ) { } + // c = table.config + // data.filter = array of filter input values; + // data.iFilter = same array, except lowercase (if wo.filter_ignoreCase is true) + // data.exact = table cell text (or parsed data if column parser enabled) + // data.iExact = same as data.exact, except lowercase (if wo.filter_ignoreCase is true) + // data.cache = table cell text from cache, so it has been parsed (& in all lower case if config.ignoreCase is true) + // data.index = column index; table = table element (DOM) + // data.parsed = array (by column) of boolean values (from filter_useParsedData or "filter-parsed" class) + types: { + // Look for regex + regex: function( c, data ) { + if ( ts.filter.regex.regex.test(data.iFilter) ) { + var matches, + regex = ts.filter.regex.regex.exec(data.iFilter); + try { + matches = new RegExp(regex[1], regex[2]).test( data.iExact ); + } catch (error) { + matches = false; + } + return matches; + } + return null; + }, + // Look for operators >, >=, < or <= + operators: function( c, data ) { + if ( /^[<>]=?/.test(data.iFilter) ) { + var cachedValue, result, + table = c.table, + index = data.index, + parsed = data.parsed[index], + query = ts.formatFloat( data.iFilter.replace(ts.filter.regex.operators, ''), table ), + parser = c.parsers[index], + savedSearch = query; + // parse filter value in case we're comparing numbers (dates) + if (parsed || parser.type === 'numeric') { + result = ts.filter.parseFilter(c, $.trim('' + data.iFilter.replace(ts.filter.regex.operators, '')), index, parsed, true); + query = ( typeof result === "number" && result !== '' && !isNaN(result) ) ? result : query; + } + + // iExact may be numeric - see issue #149; + // check if cached is defined, because sometimes j goes out of range? (numeric columns) + cachedValue = ( parsed || parser.type === 'numeric' ) && !isNaN(query) && typeof data.cache !== 'undefined' ? data.cache : + isNaN(data.iExact) ? ts.formatFloat( data.iExact.replace(ts.filter.regex.nondigit, ''), table) : + ts.formatFloat( data.iExact, table ); + + if ( />/.test(data.iFilter) ) { result = />=/.test(data.iFilter) ? cachedValue >= query : cachedValue > query; } + if ( /</.test(data.iFilter) ) { result = /<=/.test(data.iFilter) ? cachedValue <= query : cachedValue < query; } + // keep showing all rows if nothing follows the operator + if ( !result && savedSearch === '' ) { result = true; } + return result; + } + return null; + }, + // Look for a not match + notMatch: function( c, data ) { + if ( /^\!/.test(data.iFilter) ) { + var indx, + filter = ts.filter.parseFilter(c, data.iFilter.replace('!', ''), data.index, data.parsed[data.index]); + if (ts.filter.regex.exact.test(filter)) { + // look for exact not matches - see #628 + filter = filter.replace(ts.filter.regex.exact, ''); + return filter === '' ? true : $.trim(filter) !== data.iExact; + } else { + indx = data.iExact.search( $.trim(filter) ); + return filter === '' ? true : !(c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0); + } + } + return null; + }, + // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric + exact: function( c, data ) { + /*jshint eqeqeq:false */ + if (ts.filter.regex.exact.test(data.iFilter)) { + var filter = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.exact, ''), data.index, data.parsed[data.index]); + return data.anyMatch ? $.inArray(filter, data.rowArray) >= 0 : filter == data.iExact; + } + return null; + }, + // Look for an AND or && operator (logical and) + and : function( c, data ) { + if ( ts.filter.regex.andTest.test(data.filter) ) { + var index = data.index, + parsed = data.parsed[index], + query = data.iFilter.split( ts.filter.regex.andSplit ), + result = data.iExact.search( $.trim( ts.filter.parseFilter(c, query[0], index, parsed) ) ) >= 0, + indx = query.length - 1; + while (result && indx) { + result = result && data.iExact.search( $.trim( ts.filter.parseFilter(c, query[indx], index, parsed) ) ) >= 0; + indx--; + } + return result; + } + return null; + }, + // Look for a range (using " to " or " - ") - see issue #166; thanks matzhu! + range : function( c, data ) { + if ( ts.filter.regex.toTest.test(data.iFilter) ) { + var result, tmp, + table = c.table, + index = data.index, + parsed = data.parsed[index], + // make sure the dash is for a range and not indicating a negative number + query = data.iFilter.split( ts.filter.regex.toSplit ), + range1 = ts.formatFloat( ts.filter.parseFilter(c, query[0].replace(ts.filter.regex.nondigit, ''), index, parsed), table ), + range2 = ts.formatFloat( ts.filter.parseFilter(c, query[1].replace(ts.filter.regex.nondigit, ''), index, parsed), table ); + // parse filter value in case we're comparing numbers (dates) + if (parsed || c.parsers[index].type === 'numeric') { + result = c.parsers[index].format('' + query[0], table, c.$headers.eq(index), index); + range1 = (result !== '' && !isNaN(result)) ? result : range1; + result = c.parsers[index].format('' + query[1], table, c.$headers.eq(index), index); + range2 = (result !== '' && !isNaN(result)) ? result : range2; + } + result = ( parsed || c.parsers[index].type === 'numeric' ) && !isNaN(range1) && !isNaN(range2) ? data.cache : + isNaN(data.iExact) ? ts.formatFloat( data.iExact.replace(ts.filter.regex.nondigit, ''), table) : + ts.formatFloat( data.iExact, table ); + if (range1 > range2) { tmp = range1; range1 = range2; range2 = tmp; } // swap + return (result >= range1 && result <= range2) || (range1 === '' || range2 === ''); + } + return null; + }, + // Look for wild card: ? = single, * = multiple, or | = logical OR + wild : function( c, data ) { + if ( /[\?\*\|]/.test(data.iFilter) || ts.filter.regex.orReplace.test(data.filter) ) { + var index = data.index, + parsed = data.parsed[index], + query = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.orReplace, "|"), index, parsed); + // look for an exact match with the "or" unless the "filter-match" class is found + if (!c.$headers.filter('[data-column="' + index + '"]:last').hasClass('filter-match') && /\|/.test(query)) { + // show all results while using filter match. Fixes #727 + if (query[ query.length - 1 ] === '|') { query += '*'; } + query = data.anyMatch && $.isArray(data.rowArray) ? '(' + query + ')' : '^(' + query + ')$'; + } + // parsing the filter may not work properly when using wildcards =/ + return new RegExp( query.replace(/\?/g, '\\S{1}').replace(/\*/g, '\\S*') ).test(data.iExact); + } + return null; + }, + // fuzzy text search; modified from https://github.com/mattyork/fuzzy (MIT license) + fuzzy: function( c, data ) { + if ( /^~/.test(data.iFilter) ) { + var indx, + patternIndx = 0, + len = data.iExact.length, + pattern = ts.filter.parseFilter(c, data.iFilter.slice(1), data.index, data.parsed[data.index]); + for (indx = 0; indx < len; indx++) { + if (data.iExact[indx] === pattern[patternIndx]) { + patternIndx += 1; + } + } + if (patternIndx === pattern.length) { + return true; + } + return false; + } + return null; + } + }, + init: function(table, c, wo) { + // filter language options + ts.language = $.extend(true, {}, { + to : 'to', + or : 'or', + and : 'and' + }, ts.language); + + var options, string, txt, $header, column, filters, val, fxn, noSelect, + regex = ts.filter.regex; + c.$table.addClass('hasFilters'); + + // define timers so using clearTimeout won't cause an undefined error + wo.searchTimer = null; + wo.filter_initTimer = null; + wo.filter_formatterCount = 0; + wo.filter_formatterInit = []; + wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; + wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; + + txt = '\\{' + ts.filter.regex.query + '\\}'; + $.extend( regex, { + child : new RegExp(c.cssChildRow), + filtered : new RegExp(wo.filter_filteredRow), + alreadyFiltered : new RegExp('(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i'), + toTest : new RegExp('\\s+(-|' + ts.language.to + ')\\s+', 'i'), + toSplit : new RegExp('(?:\\s+(?:-|' + ts.language.to + ')\\s+)' ,'gi'), + andTest : new RegExp('\\s+(' + ts.language.and + '|&&)\\s+', 'i'), + andSplit : new RegExp('(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi'), + orReplace : new RegExp('\\s+(' + ts.language.or + ')\\s+', 'gi'), + iQuery : new RegExp(txt, 'i'), + igQuery : new RegExp(txt, 'ig') + }); + + // don't build filter row if columnFilters is false or all columns are set to "filter-false" - issue #156 + if (wo.filter_columnFilters !== false && c.$headers.filter('.filter-false, .parser-false').length !== c.$headers.length) { + // build filter row + ts.filter.buildRow(table, c, wo); + } + + txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter '); + c.$table.bind( txt, function(event, filter) { + val = (wo.filter_hideEmpty && $.isEmptyObject(c.cache) && !(c.delayInit && event.type === 'appendCache')); + // hide filter row using the "filtered" class name + c.$table.find('.' + ts.css.filterRow).toggleClass(wo.filter_filteredRow, val ); // fixes #450 + if ( !/(search|filter)/.test(event.type) ) { + event.stopPropagation(); + ts.filter.buildDefault(table, true); + } + if (event.type === 'filterReset') { + c.$table.find('.' + ts.css.filter).add(wo.filter_$externalFilters).val(''); + ts.filter.searching(table, []); + } else if (event.type === 'filterEnd') { + ts.filter.buildDefault(table, true); + } else { + // send false argument to force a new search; otherwise if the filter hasn't changed, it will return + filter = event.type === 'search' ? filter : event.type === 'updateComplete' ? c.$table.data('lastSearch') : ''; + if (/(update|add)/.test(event.type) && event.type !== "updateComplete") { + // force a new search since content has changed + c.lastCombinedFilter = null; + c.lastSearch = []; + } + // pass true (skipFirst) to prevent the tablesorter.setFilters function from skipping the first input + // ensures all inputs are updated when a search is triggered on the table $('table').trigger('search', [...]); + ts.filter.searching(table, filter, true); + } + return false; + }); + + // reset button/link + if (wo.filter_reset) { + if (wo.filter_reset instanceof $) { + // reset contains a jQuery object, bind to it + wo.filter_reset.click(function(){ + c.$table.trigger('filterReset'); + }); + } else if ($(wo.filter_reset).length) { + // reset is a jQuery selector, use event delegation + $(document) + .undelegate(wo.filter_reset, 'click.tsfilter') + .delegate(wo.filter_reset, 'click.tsfilter', function() { + // trigger a reset event, so other functions (filter_formatter) know when to reset + c.$table.trigger('filterReset'); + }); + } + } + if (wo.filter_functions) { + for (column = 0; column < c.columns; column++) { + fxn = ts.getColumnData( table, wo.filter_functions, column ); + if (fxn) { + // remove "filter-select" from header otherwise the options added here are replaced with all options + $header = c.$headers.filter('[data-column="' + column + '"]:last').removeClass('filter-select'); + // don't build select if "filter-false" or "parser-false" set + noSelect = !($header.hasClass('filter-false') || $header.hasClass('parser-false')); + options = ''; + if ( fxn === true && noSelect ) { + ts.filter.buildSelect(table, column); + } else if ( typeof fxn === 'object' && noSelect ) { + // add custom drop down list + for (string in fxn) { + if (typeof string === 'string') { + options += options === '' ? + '<option value="">' + ($header.data('placeholder') || $header.attr('data-placeholder') || wo.filter_placeholder.select || '') + '</option>' : ''; + val = string; + txt = string; + if (string.indexOf(wo.filter_selectSourceSeparator) >= 0) { + val = string.split(wo.filter_selectSourceSeparator); + txt = val[1]; + val = val[0]; + } + options += '<option ' + (txt === val ? '' : 'data-function-name="' + string + '" ') + 'value="' + val + '">' + txt + '</option>'; + } + } + c.$table.find('thead').find('select.' + ts.css.filter + '[data-column="' + column + '"]').append(options); + } + } + } + } + // not really updating, but if the column has both the "filter-select" class & filter_functions set to true, + // it would append the same options twice. + ts.filter.buildDefault(table, true); + + ts.filter.bindSearch( table, c.$table.find('.' + ts.css.filter), true ); + if (wo.filter_external) { + ts.filter.bindSearch( table, wo.filter_external ); + } + + if (wo.filter_hideFilters) { + ts.filter.hideFilters(table, c); + } + + // show processing icon + if (c.showProcessing) { + c.$table + .unbind( ('filterStart filterEnd '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') ) + .bind( 'filterStart filterEnd '.split(' ').join(c.namespace + 'filter '), function(event, columns) { + // only add processing to certain columns to all columns + $header = (columns) ? c.$table.find('.' + ts.css.header).filter('[data-column]').filter(function() { + return columns[$(this).data('column')] !== ''; + }) : ''; + ts.isProcessing(table, event.type === 'filterStart', columns ? $header : ''); + }); + } + + // set filtered rows count (intially unfiltered) + c.filteredRows = c.totalRows; + + // add default values + c.$table + .unbind( ('tablesorter-initialized pagerBeforeInitialized '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') ) + .bind( 'tablesorter-initialized pagerBeforeInitialized '.split(' ').join(c.namespace + 'filter '), function() { + // redefine "wo" as it does not update properly inside this callback + var wo = this.config.widgetOptions; + filters = ts.filter.setDefaults(table, c, wo) || []; + if (filters.length) { + // prevent delayInit from triggering a cache build if filters are empty + if ( !(c.delayInit && filters.join('') === '') ) { + ts.setFilters(table, filters, true); + } + } + c.$table.trigger('filterFomatterUpdate'); + // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers + setTimeout(function(){ + if (!wo.filter_initialized) { + ts.filter.filterInitComplete(c); + } + }, 100); + }); + // if filter widget is added after pager has initialized; then set filter init flag + if (c.pager && c.pager.initialized && !wo.filter_initialized) { + c.$table.trigger('filterFomatterUpdate'); + setTimeout(function(){ + ts.filter.filterInitComplete(c); + }, 100); + } + }, + // $cell parameter, but not the config, is passed to the + // filter_formatters, so we have to work with it instead + formatterUpdated: function($cell, column) { + var wo = $cell.closest('table')[0].config.widgetOptions; + if (!wo.filter_initialized) { + // add updates by column since this function + // may be called numerous times before initialization + wo.filter_formatterInit[column] = 1; + } + }, + filterInitComplete: function(c){ + var wo = c.widgetOptions, + count = 0, + completed = function(){ + wo.filter_initialized = true; + c.$table.trigger('filterInit', c); + ts.filter.findRows(c.table, c.$table.data('lastSearch') || []); + }; + if ( $.isEmptyObject( wo.filter_formatter ) ) { + completed(); + } else { + $.each( wo.filter_formatterInit, function(i, val) { + if (val === 1) { + count++; + } + }); + clearTimeout(wo.filter_initTimer); + if (!wo.filter_initialized && count === wo.filter_formatterCount) { + // filter widget initialized + completed(); + } else if (!wo.filter_initialized) { + // fall back in case a filter_formatter doesn't call + // $.tablesorter.filter.formatterUpdated($cell, column), and the count is off + wo.filter_initTimer = setTimeout(function(){ + completed(); + }, 500); + } + } + }, + + setDefaults: function(table, c, wo) { + var isArray, saved, indx, + // get current (default) filters + filters = ts.getFilters(table) || []; + if (wo.filter_saveFilters && ts.storage) { + saved = ts.storage( table, 'tablesorter-filters' ) || []; + isArray = $.isArray(saved); + // make sure we're not just getting an empty array + if ( !(isArray && saved.join('') === '' || !isArray) ) { filters = saved; } + } + // if no filters saved, then check default settings + if (filters.join('') === '') { + for (indx = 0; indx < c.columns; indx++) { + filters[indx] = c.$headers.filter('[data-column="' + indx + '"]:last').attr(wo.filter_defaultAttrib) || filters[indx]; + } + } + c.$table.data('lastSearch', filters); + return filters; + }, + parseFilter: function(c, filter, column, parsed, forceParse){ + return forceParse || parsed ? + c.parsers[column].format( filter, c.table, [], column ) : + filter; + }, + buildRow: function(table, c, wo) { + var col, column, $header, buildSelect, disabled, name, ffxn, + // c.columns defined in computeThIndexes() + columns = c.columns, + arry = $.isArray(wo.filter_cellFilter), + buildFilter = '<tr role="row" class="' + ts.css.filterRow + '">'; + for (column = 0; column < columns; column++) { + if (arry) { + buildFilter += '<td' + ( wo.filter_cellFilter[column] ? ' class="' + wo.filter_cellFilter[column] + '"' : '' ) + '></td>'; + } else { + buildFilter += '<td' + ( wo.filter_cellFilter !== '' ? ' class="' + wo.filter_cellFilter + '"' : '' ) + '></td>'; + } + } + c.$filters = $(buildFilter += '</tr>').appendTo( c.$table.children('thead').eq(0) ).find('td'); + // build each filter input + for (column = 0; column < columns; column++) { + disabled = false; + // assuming last cell of a column is the main column + $header = c.$headers.filter('[data-column="' + column + '"]:last'); + ffxn = ts.getColumnData( table, wo.filter_functions, column ); + buildSelect = (wo.filter_functions && ffxn && typeof ffxn !== "function" ) || + $header.hasClass('filter-select'); + // get data from jQuery data, metadata, headers option or header class name + col = ts.getColumnData( table, c.headers, column ); + disabled = ts.getData($header[0], col, 'filter') === 'false' || ts.getData($header[0], col, 'parser') === 'false'; + + if (buildSelect) { + buildFilter = $('<select>').appendTo( c.$filters.eq(column) ); + } else { + ffxn = ts.getColumnData( table, wo.filter_formatter, column ); + if (ffxn) { + wo.filter_formatterCount++; + buildFilter = ffxn( c.$filters.eq(column), column ); + // no element returned, so lets go find it + if (buildFilter && buildFilter.length === 0) { + buildFilter = c.$filters.eq(column).children('input'); + } + // element not in DOM, so lets attach it + if ( buildFilter && (buildFilter.parent().length === 0 || + (buildFilter.parent().length && buildFilter.parent()[0] !== c.$filters[column])) ) { + c.$filters.eq(column).append(buildFilter); + } + } else { + buildFilter = $('<input type="search">').appendTo( c.$filters.eq(column) ); + } + if (buildFilter) { + buildFilter.attr('placeholder', $header.data('placeholder') || $header.attr('data-placeholder') || wo.filter_placeholder.search || ''); + } + } + if (buildFilter) { + // add filter class name + name = ( $.isArray(wo.filter_cssFilter) ? + (typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '') : + wo.filter_cssFilter ) || ''; + buildFilter.addClass( ts.css.filter + ' ' + name ).attr('data-column', column); + if (disabled) { + buildFilter.attr('placeholder', '').addClass('disabled')[0].disabled = true; // disabled! + } + } + } + }, + bindSearch: function(table, $el, internal) { + table = $(table)[0]; + $el = $($el); // allow passing a selector string + if (!$el.length) { return; } + var c = table.config, + wo = c.widgetOptions, + $ext = wo.filter_$externalFilters; + if (internal !== true) { + // save anyMatch element + wo.filter_$anyMatch = $el.filter(wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector); + if ($ext && $ext.length) { + wo.filter_$externalFilters = wo.filter_$externalFilters.add( $el ); + } else { + wo.filter_$externalFilters = $el; + } + // update values (external filters added after table initialization) + ts.setFilters(table, c.$table.data('lastSearch') || [], internal === false); + } + $el + // use data attribute instead of jQuery data since the head is cloned without including the data/binding + .attr('data-lastSearchTime', new Date().getTime()) + .unbind( ('keypress keyup search change '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') ) + // include change for select - fixes #473 + .bind('keyup' + c.namespace + 'filter', function(event) { + $(this).attr('data-lastSearchTime', new Date().getTime()); + // emulate what webkit does.... escape clears the filter + if (event.which === 27) { + this.value = ''; + // live search + } else if ( wo.filter_liveSearch === false ) { + return; + // don't return if the search value is empty (all rows need to be revealed) + } else if ( this.value !== '' && ( + // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace + ( typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch ) || + // let return & backspace continue on, but ignore arrows & non-valid characters + ( event.which !== 13 && event.which !== 8 && ( event.which < 32 || (event.which >= 37 && event.which <= 40) ) ) ) ) { + return; + } + // change event = no delay; last true flag tells getFilters to skip newest timed input + ts.filter.searching( table, true, true ); + }) + .bind( 'search change keypress '.split(' ').join(c.namespace + 'filter '), function(event){ + var column = $(this).data('column'); + // don't allow "change" event to process if the input value is the same - fixes #685 + if (event.which === 13 || event.type === 'search' || event.type === 'change' && this.value !== c.lastSearch[column]) { + event.preventDefault(); + // init search with no delay + $(this).attr('data-lastSearchTime', new Date().getTime()); + ts.filter.searching( table, false, true ); + } + }); + }, + searching: function(table, filter, skipFirst) { + var wo = table.config.widgetOptions; + clearTimeout(wo.searchTimer); + if (typeof filter === 'undefined' || filter === true) { + // delay filtering + wo.searchTimer = setTimeout(function() { + ts.filter.checkFilters(table, filter, skipFirst ); + }, wo.filter_liveSearch ? wo.filter_searchDelay : 10); + } else { + // skip delay + ts.filter.checkFilters(table, filter, skipFirst); + } + }, + checkFilters: function(table, filter, skipFirst) { + var c = table.config, + wo = c.widgetOptions, + filterArray = $.isArray(filter), + filters = (filterArray) ? filter : ts.getFilters(table, true), + combinedFilters = (filters || []).join(''); // combined filter values + // prevent errors if delay init is set + if ($.isEmptyObject(c.cache)) { + // update cache if delayInit set & pager has initialized (after user initiates a search) + if (c.delayInit && c.pager && c.pager.initialized) { + c.$table.trigger('updateCache', [function(){ + ts.filter.checkFilters(table, false, skipFirst); + }] ); + } + return; + } + // add filter array back into inputs + if (filterArray) { + ts.setFilters( table, filters, false, skipFirst !== true ); + if (!wo.filter_initialized) { c.lastCombinedFilter = ''; } + } + if (wo.filter_hideFilters) { + // show/hide filter row as needed + c.$table.find('.' + ts.css.filterRow).trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); + } + // return if the last search is the same; but filter === false when updating the search + // see example-widget-filter.html filter toggle buttons + if (c.lastCombinedFilter === combinedFilters && filter !== false) { + return; + } else if (filter === false) { + // force filter refresh + c.lastCombinedFilter = null; + c.lastSearch = []; + } + if (wo.filter_initialized) { c.$table.trigger('filterStart', [filters]); } + if (c.showProcessing) { + // give it time for the processing icon to kick in + setTimeout(function() { + ts.filter.findRows(table, filters, combinedFilters); + return false; + }, 30); + } else { + ts.filter.findRows(table, filters, combinedFilters); + return false; + } + }, + hideFilters: function(table, c) { + var $filterRow, $filterRow2, timer; + $(table) + .find('.' + ts.css.filterRow) + .addClass('hideme') + .bind('mouseenter mouseleave', function(e) { + // save event object - http://bugs.jquery.com/ticket/12140 + var event = e; + $filterRow = $(this); + clearTimeout(timer); + timer = setTimeout(function() { + if ( /enter|over/.test(event.type) ) { + $filterRow.removeClass('hideme'); + } else { + // don't hide if input has focus + // $(':focus') needs jQuery 1.6+ + if ( $(document.activeElement).closest('tr')[0] !== $filterRow[0] ) { + // don't hide row if any filter has a value + if (c.lastCombinedFilter === '') { + $filterRow.addClass('hideme'); + } + } + } + }, 200); + }) + .find('input, select').bind('focus blur', function(e) { + $filterRow2 = $(this).closest('tr'); + clearTimeout(timer); + var event = e; + timer = setTimeout(function() { + // don't hide row if any filter has a value + if (ts.getFilters(c.$table).join('') === '') { + $filterRow2[ event.type === 'focus' ? 'removeClass' : 'addClass']('hideme'); + } + }, 200); + }); + }, + defaultFilter: function(filter, mask){ + if (filter === '') { return filter; } + var regex = ts.filter.regex.iQuery, + maskLen = mask.match( ts.filter.regex.igQuery ).length, + query = maskLen > 1 ? $.trim(filter).split(/\s/) : [ $.trim(filter) ], + len = query.length - 1, + indx = 0, + val = mask; + if ( len < 1 && maskLen > 1 ) { + // only one "word" in query but mask has >1 slots + query[1] = query[0]; + } + // replace all {query} with query words... + // if query = "Bob", then convert mask from "!{query}" to "!Bob" + // if query = "Bob Joe Frank", then convert mask "{q} OR {q}" to "Bob OR Joe OR Frank" + while (regex.test(val)) { + val = val.replace(regex, query[indx++] || ''); + if (regex.test(val) && indx < len && (query[indx] || '') !== '') { + val = mask.replace(regex, val); + } + } + return val; + }, + getLatestSearch: function( $input ) { + if ($input) { + return $input.sort(function(a, b) { + return $(b).attr('data-lastSearchTime') - $(a).attr('data-lastSearchTime'); + }); + } + return $(); + }, + multipleColumns: function( c, $input ) { + // look for multiple columns "1-3,4-6,8" in data-column + var ranges, singles, indx, + wo = c.widgetOptions, + // only target "all" column inputs on initialization + // & don't target "all" column inputs if they don't exist + targets = wo.filter_initialized || !$input.filter(wo.filter_anyColumnSelector).length, + columns = [], + val = $.trim( ts.filter.getLatestSearch( $input ).attr('data-column') || '' ); + // process column range + if ( targets && /-/.test( val ) ) { + ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); + $.each(ranges, function(i,v){ + var t, + range = v.split( /\s*-\s*/ ), + start = parseInt( range[0], 10 ) || 0, + end = parseInt( range[1], 10 ) || ( c.columns - 1 ); + if ( start > end ) { t = start; start = end; end = t; } // swap + if ( end >= c.columns ) { end = c.columns - 1; } + for ( ; start <= end; start++ ) { + columns.push(start); + } + // remove processed range from val + val = val.replace( v, '' ); + }); + } + // process single columns + if ( targets && /,/.test( val ) ) { + singles = val.split( /\s*,\s*/ ); + $.each( singles, function(i,v) { + if (v !== '') { + indx = parseInt( v, 10 ); + if ( indx < c.columns ) { + columns.push( indx ); + } + } + }); + } + // return all columns + if (!columns.length) { + for ( indx = 0; indx < c.columns; indx++ ) { + columns.push( indx ); + } + } + return columns; + }, + findRows: function(table, filters, combinedFilters) { + if (table.config.lastCombinedFilter === combinedFilters || !table.config.widgetOptions.filter_initialized) { return; } + var len, norm_rows, $rows, rowIndex, tbodyIndex, $tbody, $cells, $cell, columnIndex, + childRow, lastSearch, hasSelect, matches, result, showRow, time, val, indx, + notFiltered, searchFiltered, filterMatched, excludeMatch, fxn, ffxn, + query, injected, res, id, + regex = ts.filter.regex, + c = table.config, + wo = c.widgetOptions, + // data object passed to filters; anyMatch is a flag for the filters + data = { anyMatch: false }, + // anyMatch really screws up with these types of filters + noAnyMatch = [ 'range', 'notMatch', 'operators' ]; + + // parse columns after formatter, in case the class is added at that point + data.parsed = c.$headers.map(function(columnIndex) { + return c.parsers && c.parsers[columnIndex] && c.parsers[columnIndex].parsed || + // getData won't return "parsed" if other "filter-" class names exist (e.g. <th class="filter-select filter-parsed">) + ts.getData && ts.getData(c.$headers.filter('[data-column="' + columnIndex + '"]:last'), ts.getColumnData( table, c.headers, columnIndex ), 'filter') === 'parsed' || + $(this).hasClass('filter-parsed'); + }).get(); + + if (c.debug) { + ts.log('Starting filter widget search', filters); + time = new Date(); + } + // filtered rows count + c.filteredRows = 0; + c.totalRows = 0; + // combindedFilters are undefined on init + combinedFilters = (filters || []).join(''); + + for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, c.$tbodies.eq(tbodyIndex), true); + // skip child rows & widget added (removable) rows - fixes #448 thanks to @hempel! + // $rows = $tbody.children('tr').not(c.selectorRemove); + columnIndex = c.columns; + // convert stored rows into a jQuery object + norm_rows = c.cache[tbodyIndex].normalized; + $rows = $( $.map(norm_rows, function(el){ return el[columnIndex].$row.get(); }) ); + + if (combinedFilters === '' || wo.filter_serversideFiltering) { + $rows.removeClass(wo.filter_filteredRow).not('.' + c.cssChildRow).show(); + } else { + // filter out child rows + $rows = $rows.not('.' + c.cssChildRow); + len = $rows.length; + + if ( (wo.filter_$anyMatch && wo.filter_$anyMatch.length) || typeof filters[c.columns] !== 'undefined' ) { + data.anyMatchFlag = true; + data.anyMatchFilter = wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || ( '' + filters[c.columns] ) || ''; + if (wo.filter_columnAnyMatch) { + // specific columns search + query = data.anyMatchFilter.split( ts.filter.regex.andSplit ); + injected = false; + for (indx = 0; indx < query.length; indx++) { + res = query[indx].split(':'); + if ( res.length > 1 ) { + // make the column a one-based index ( non-developers start counting from one :P ) + id = parseInt( res[0], 10 ) - 1; + if ( id >= 0 && id < c.columns ) { // if id is an integer + filters[id] = res[1]; + query.splice(indx, 1); + indx--; + injected = true; + } + } + } + if (injected) { + data.anyMatchFilter = query.join(' && '); + } + } + } + + // optimize searching only through already filtered rows - see #313 + searchFiltered = wo.filter_searchFiltered; + lastSearch = c.lastSearch || c.$table.data('lastSearch') || []; + if (searchFiltered) { + // cycle through all filters; include last (columnIndex + 1 = match any column). Fixes #669 + for (indx = 0; indx < columnIndex + 1; indx++) { + val = filters[indx] || ''; + // break out of loop if we've already determined not to search filtered rows + if (!searchFiltered) { indx = columnIndex; } + // search already filtered rows if... + searchFiltered = searchFiltered && lastSearch.length && + // there are no changes from beginning of filter + val.indexOf(lastSearch[indx] || '') === 0 && + // if there is NOT a logical "or", or range ("to" or "-") in the string + !regex.alreadyFiltered.test(val) && + // if we are not doing exact matches, using "|" (logical or) or not "!" + !/[=\"\|!]/.test(val) && + // don't search only filtered if the value is negative ('> -10' => '> -100' will ignore hidden rows) + !(/(>=?\s*-\d)/.test(val) || /(<=?\s*\d)/.test(val)) && + // if filtering using a select without a "filter-match" class (exact match) - fixes #593 + !( val !== '' && c.$filters && c.$filters.eq(indx).find('select').length && !c.$headers.filter('[data-column="' + indx + '"]:last').hasClass('filter-match') ); + } + } + notFiltered = $rows.not('.' + wo.filter_filteredRow).length; + // can't search when all rows are hidden - this happens when looking for exact matches + if (searchFiltered && notFiltered === 0) { searchFiltered = false; } + if (c.debug) { + ts.log( "Searching through " + ( searchFiltered && notFiltered < len ? notFiltered : "all" ) + " rows" ); + } + if (data.anyMatchFlag) { + if (c.sortLocaleCompare) { + // replace accents + data.anyMatchFilter = ts.replaceAccents(data.anyMatchFilter); + } + if (wo.filter_defaultFilter && regex.iQuery.test( ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '')) { + data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) ); + // clear search filtered flag because default filters are not saved to the last search + searchFiltered = false; + } + // make iAnyMatchFilter lowercase unless both filter widget & core ignoreCase options are true + // when c.ignoreCase is true, the cache contains all lower case data + data.iAnyMatchFilter = !(wo.filter_ignoreCase && c.ignoreCase) ? data.anyMatchFilter : data.anyMatchFilter.toLocaleLowerCase(); + } + + // loop through the rows + for (rowIndex = 0; rowIndex < len; rowIndex++) { + + data.cacheArray = norm_rows[rowIndex]; + + childRow = $rows[rowIndex].className; + // skip child rows & already filtered rows + if ( regex.child.test(childRow) || (searchFiltered && regex.filtered.test(childRow)) ) { continue; } + showRow = true; + // *** nextAll/nextUntil not supported by Zepto! *** + childRow = $rows.eq(rowIndex).nextUntil('tr:not(.' + c.cssChildRow + ')'); + // so, if "table.config.widgetOptions.filter_childRows" is true and there is + // a match anywhere in the child row, then it will make the row visible + // checked here so the option can be changed dynamically + data.childRowText = (childRow.length && wo.filter_childRows) ? childRow.text() : ''; + data.childRowText = wo.filter_ignoreCase ? data.childRowText.toLocaleLowerCase() : data.childRowText; + $cells = $rows.eq(rowIndex).children(); + if (data.anyMatchFlag) { + // look for multiple columns "1-3,4-6,8" + columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); + data.anyMatch = true; + data.rowArray = $cells.map(function(i){ + if ( $.inArray(i, columnIndex) > -1 ) { + var txt; + if (data.parsed[i]) { + txt = data.cacheArray[i]; + } else { + txt = this ? this.getAttribute( c.textAttribute ) || this.textContent || $(this).text() : ''; + txt = $.trim( wo.filter_ignoreCase ? txt.toLowerCase() : txt ); + if (c.sortLocaleCompare) { + txt = ts.replaceAccents(txt); + } + } + return txt; + } + }).get(); + data.filter = data.anyMatchFilter; + data.iFilter = data.iAnyMatchFilter; + data.exact = data.rowArray.join(' '); + data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; + data.cache = data.cacheArray.slice(0,-1).join(' '); + filterMatched = null; + $.each(ts.filter.types, function(type, typeFunction) { + if ($.inArray(type, noAnyMatch) < 0) { + matches = typeFunction( c, data ); + if (matches !== null) { + filterMatched = matches; + return false; + } + } + }); + if (filterMatched !== null) { + showRow = filterMatched; + } else { + if (wo.filter_startsWith) { + showRow = false; + columnIndex = c.columns; + while (!showRow && columnIndex > 0) { + columnIndex--; + showRow = showRow || data.rowArray[columnIndex].indexOf(data.iFilter) === 0; + } + } else { + showRow = (data.iExact + data.childRowText).indexOf(data.iFilter) >= 0; + } + } + data.anyMatch = false; + } + + for (columnIndex = 0; columnIndex < c.columns; columnIndex++) { + data.filter = filters[columnIndex]; + data.index = columnIndex; + + // filter types to exclude, per column + excludeMatch = ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split(/\s+/); + + // ignore if filter is empty or disabled + if (data.filter) { + data.cache = data.cacheArray[columnIndex]; + // check if column data should be from the cell or from parsed data + if (wo.filter_useParsedData || data.parsed[columnIndex]) { + data.exact = data.cache; + } else { + val = $cells[columnIndex]; + result = val ? $.trim( val.getAttribute( c.textAttribute ) || val.textContent || $cells.eq(columnIndex).text() ) : ''; + data.exact = c.sortLocaleCompare ? ts.replaceAccents(result) : result; // issue #405 + } + data.iExact = !regex.type.test(typeof data.exact) && wo.filter_ignoreCase ? data.exact.toLocaleLowerCase() : data.exact; + result = showRow; // if showRow is true, show that row + + // in case select filter option has a different value vs text "a - z|A through Z" + ffxn = wo.filter_columnFilters ? + c.$filters.add(c.$externalFilters).filter('[data-column="'+ columnIndex + '"]').find('select option:selected').attr('data-function-name') || '' : ''; + // replace accents - see #357 + if (c.sortLocaleCompare) { + data.filter = ts.replaceAccents(data.filter); + } + + val = true; + if (wo.filter_defaultFilter && regex.iQuery.test( ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || '')) { + data.filter = ts.filter.defaultFilter( data.filter, ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) ); + // val is used to indicate that a filter select is using a default filter; so we override the exact & partial matches + val = false; + } + // data.iFilter = case insensitive (if wo.filter_ignoreCase is true), data.filter = case sensitive + data.iFilter = wo.filter_ignoreCase ? (data.filter || '').toLocaleLowerCase() : data.filter; + fxn = ts.getColumnData( table, wo.filter_functions, columnIndex ); + $cell = c.$headers.filter('[data-column="' + columnIndex + '"]:last'); + hasSelect = $cell.hasClass('filter-select'); + if ( fxn || ( hasSelect && val ) ) { + if (fxn === true || hasSelect) { + // default selector uses exact match unless "filter-match" class is found + result = ($cell.hasClass('filter-match')) ? data.iExact.search(data.iFilter) >= 0 : data.filter === data.exact; + } else if (typeof fxn === 'function') { + // filter callback( exact cell content, parser normalized content, filter input value, column index, jQuery row object ) + result = fxn(data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex)); + } else if (typeof fxn[ffxn || data.filter] === 'function') { + // selector option function + result = fxn[ffxn || data.filter](data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex)); + } + } else { + filterMatched = null; + // cycle through the different filters + // filters return a boolean or null if nothing matches + $.each(ts.filter.types, function(type, typeFunction) { + if ($.inArray(type, excludeMatch) < 0) { + matches = typeFunction( c, data ); + if (matches !== null) { + filterMatched = matches; + return false; + } + } + }); + if (filterMatched !== null) { + result = filterMatched; + // Look for match, and add child row data for matching + } else { + data.exact = (data.iExact + data.childRowText).indexOf( ts.filter.parseFilter(c, data.iFilter, columnIndex, data.parsed[columnIndex]) ); + result = ( (!wo.filter_startsWith && data.exact >= 0) || (wo.filter_startsWith && data.exact === 0) ); + } + } + showRow = (result) ? showRow : false; + } + } + $rows.eq(rowIndex) + .toggle(showRow) + .toggleClass(wo.filter_filteredRow, !showRow); + if (childRow.length) { + childRow.toggleClass(wo.filter_filteredRow, !showRow); + } + } + } + c.filteredRows += $rows.not('.' + wo.filter_filteredRow).length; + c.totalRows += $rows.length; + ts.processTbody(table, $tbody, false); + } + c.lastCombinedFilter = combinedFilters; // save last search + c.lastSearch = filters; + c.$table.data('lastSearch', filters); + if (wo.filter_saveFilters && ts.storage) { + ts.storage( table, 'tablesorter-filters', filters ); + } + if (c.debug) { + ts.benchmark("Completed filter widget search", time); + } + if (wo.filter_initialized) { c.$table.trigger('filterEnd', c ); } + setTimeout(function(){ + c.$table.trigger('applyWidgets'); // make sure zebra widget is applied + }, 0); + }, + getOptionSource: function(table, column, onlyAvail) { + table = $(table)[0]; + var cts, + c = table.config, + wo = c.widgetOptions, + parsed = [], + arry = false, + source = wo.filter_selectSource, + last = c.$table.data('lastSearch') || [], + fxn = $.isFunction(source) ? true : ts.getColumnData( table, source, column ); + + if (onlyAvail && last[column] !== '') { + onlyAvail = false; + } + + // filter select source option + if (fxn === true) { + // OVERALL source + arry = source(table, column, onlyAvail); + } else if ( fxn instanceof $ || ($.type(fxn) === 'string' && fxn.indexOf('</option>') >= 0) ) { + // selectSource is a jQuery object or string of options + return fxn; + } else if ($.isArray(fxn)) { + arry = fxn; + } else if ($.type(source) === 'object' && fxn) { + // custom select source function for a SPECIFIC COLUMN + arry = fxn(table, column, onlyAvail); + } + if (arry === false) { + // fall back to original method + arry = ts.filter.getOptions(table, column, onlyAvail); + } + + // get unique elements and sort the list + // if $.tablesorter.sortText exists (not in the original tablesorter), + // then natural sort the list otherwise use a basic sort + arry = $.grep(arry, function(value, indx) { + return $.inArray(value, arry) === indx; + }); + + if (c.$headers.filter('[data-column="' + column + '"]:last').hasClass('filter-select-nosort')) { + // unsorted select options + return arry; + } else { + // parse select option values + $.each(arry, function(i, v){ + // parse array data using set column parser; this DOES NOT pass the original + // table cell to the parser format function + parsed.push({ t : v, p : c.parsers && c.parsers[column].format( v, table, [], column ) }); + }); + + // sort parsed select options + cts = c.textSorter || ''; + parsed.sort(function(a, b){ + // sortNatural breaks if you don't pass it strings + var x = a.p.toString(), y = b.p.toString(); + if ($.isFunction(cts)) { + // custom OVERALL text sorter + return cts(x, y, true, column, table); + } else if (typeof(cts) === 'object' && cts.hasOwnProperty(column)) { + // custom text sorter for a SPECIFIC COLUMN + return cts[column](x, y, true, column, table); + } else if (ts.sortNatural) { + // fall back to natural sort + return ts.sortNatural(x, y); + } + // using an older version! do a basic sort + return true; + }); + // rebuild arry from sorted parsed data + arry = []; + $.each(parsed, function(i, v){ + arry.push(v.t); + }); + return arry; + } + }, + getOptions: function(table, column, onlyAvail) { + table = $(table)[0]; + var rowIndex, tbodyIndex, len, row, cache, cell, + c = table.config, + wo = c.widgetOptions, + arry = []; + for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + cache = c.cache[tbodyIndex]; + len = c.cache[tbodyIndex].normalized.length; + // loop through the rows + for (rowIndex = 0; rowIndex < len; rowIndex++) { + // get cached row from cache.row (old) or row data object (new; last item in normalized array) + row = cache.row ? cache.row[rowIndex] : cache.normalized[rowIndex][c.columns].$row[0]; + // check if has class filtered + if (onlyAvail && row.className.match(wo.filter_filteredRow)) { continue; } + // get non-normalized cell content + if (wo.filter_useParsedData || c.parsers[column].parsed || c.$headers.filter('[data-column="' + column + '"]:last').hasClass('filter-parsed')) { + arry.push( '' + cache.normalized[rowIndex][column] ); + } else { + cell = row.cells[column]; + if (cell) { + arry.push( $.trim( cell.getAttribute( c.textAttribute ) || cell.textContent || $(cell).text() ) ); + } + } + } + } + return arry; + }, + buildSelect: function(table, column, arry, updating, onlyAvail) { + table = $(table)[0]; + column = parseInt(column, 10); + if (!table.config.cache || $.isEmptyObject(table.config.cache)) { return; } + var indx, val, txt, t, $filters, $filter, + c = table.config, + wo = c.widgetOptions, + node = c.$headers.filter('[data-column="' + column + '"]:last'), + // t.data('placeholder') won't work in jQuery older than 1.4.3 + options = '<option value="">' + ( node.data('placeholder') || node.attr('data-placeholder') || wo.filter_placeholder.select || '' ) + '</option>', + // Get curent filter value + currentValue = c.$table.find('thead').find('select.' + ts.css.filter + '[data-column="' + column + '"]').val(); + // nothing included in arry (external source), so get the options from filter_selectSource or column data + if (typeof arry === 'undefined' || arry === '') { + arry = ts.filter.getOptionSource(table, column, onlyAvail); + } + + if ($.isArray(arry)) { + // build option list + for (indx = 0; indx < arry.length; indx++) { + txt = arry[indx] = ('' + arry[indx]).replace(/\"/g, """); + val = txt; + // allow including a symbol in the selectSource array + // "a-z|A through Z" so that "a-z" becomes the option value + // and "A through Z" becomes the option text + if (txt.indexOf(wo.filter_selectSourceSeparator) >= 0) { + t = txt.split(wo.filter_selectSourceSeparator); + val = t[0]; + txt = t[1]; + } + // replace quotes - fixes #242 & ignore empty strings - see http://stackoverflow.com/q/14990971/145346 + options += arry[indx] !== '' ? '<option ' + (val === txt ? '' : 'data-function-name="' + arry[indx] + '" ') + 'value="' + val + '">' + txt + '</option>' : ''; + } + // clear arry so it doesn't get appended twice + arry = []; + } + + // update all selects in the same column (clone thead in sticky headers & any external selects) - fixes 473 + $filters = ( c.$filters ? c.$filters : c.$table.children('thead') ).find('.' + ts.css.filter); + if (wo.filter_$externalFilters) { + $filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters; + } + $filter = $filters.filter('select[data-column="' + column + '"]'); + + // make sure there is a select there! + if ($filter.length) { + $filter[ updating ? 'html' : 'append' ](options); + if (!$.isArray(arry)) { + // append options if arry is provided externally as a string or jQuery object + // options (default value) was already added + $filter.append(arry).val(currentValue); + } + $filter.val(currentValue); + } + }, + buildDefault: function(table, updating) { + var columnIndex, $header, noSelect, + c = table.config, + wo = c.widgetOptions, + columns = c.columns; + // build default select dropdown + for (columnIndex = 0; columnIndex < columns; columnIndex++) { + $header = c.$headers.filter('[data-column="' + columnIndex + '"]:last'); + noSelect = !($header.hasClass('filter-false') || $header.hasClass('parser-false')); + // look for the filter-select class; build/update it if found + if (($header.hasClass('filter-select') || ts.getColumnData( table, wo.filter_functions, columnIndex ) === true) && noSelect) { + ts.filter.buildSelect(table, columnIndex, '', updating, $header.hasClass(wo.filter_onlyAvail)); + } + } + } +}; + +ts.getFilters = function(table, getRaw, setFilters, skipFirst) { + var i, $filters, $column, cols, + filters = false, + c = table ? $(table)[0].config : '', + wo = c ? c.widgetOptions : ''; + if (getRaw !== true && wo && !wo.filter_columnFilters) { + return $(table).data('lastSearch'); + } + if (c) { + if (c.$filters) { + $filters = c.$filters.find('.' + ts.css.filter); + } + if (wo.filter_$externalFilters) { + $filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters; + } + if ($filters && $filters.length) { + filters = setFilters || []; + for (i = 0; i < c.columns + 1; i++) { + cols = ( i === c.columns ? + // "all" columns can now include a range or set of columms (data-column="0-2,4,6-7") + wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector : + '[data-column="' + i + '"]' ); + $column = $filters.filter(cols); + if ($column.length) { + // move the latest search to the first slot in the array + $column = ts.filter.getLatestSearch( $column ); + if ($.isArray(setFilters)) { + // skip first (latest input) to maintain cursor position while typing + if (skipFirst) { $column.slice(1); } + if (i === c.columns) { + // prevent data-column="all" from filling data-column="0,1" (etc) + cols = $column.filter(wo.filter_anyColumnSelector); + $column = cols.length ? cols : $column; + } + $column + .val( setFilters[i] ) + .trigger('change.tsfilter'); + } else { + filters[i] = $column.val() || ''; + // don't change the first... it will move the cursor + if (i === c.columns) { + // don't update range columns from "all" setting + $column.slice(1).filter('[data-column*="' + $column.attr('data-column') + '"]').val( filters[i] ); + } else { + $column.slice(1).val( filters[i] ); + } + } + // save any match input dynamically + if (i === c.columns && $column.length) { + wo.filter_$anyMatch = $column; + } + } + } + } + } + if (filters.length === 0) { + filters = false; + } + return filters; +}; + +ts.setFilters = function(table, filter, apply, skipFirst) { + var c = table ? $(table)[0].config : '', + valid = ts.getFilters(table, true, filter, skipFirst); + if (c && apply) { + // ensure new set filters are applied, even if the search is the same + c.lastCombinedFilter = null; + c.lastSearch = []; + ts.filter.searching(c.table, filter, skipFirst); + c.$table.trigger('filterFomatterUpdate'); + } + return !!valid; +}; + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js index ae80964..8b8cd4f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js @@ -10,11 +10,11 @@ ts.formatter = { init : function( c ) { - var events = $.trim( c.widgetOptions.formatter_event ) + + var events = c.widgetOptions.formatter_event + ' pagerComplete updateComplete '.split(' ').join('.tsformatter '); c.$table - .off( $.trim(events) ) - .on( $.trim(events), function() { + .off( events.replace(/\s+/g, ' ') ) + .on( events, function() { ts.formatter.setup( c ); }); ts.formatter.setup( c ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index bdfa28e..546743d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -54,7 +54,7 @@ ts.grouping = { update : function(table, c, wo){ if ($.isEmptyObject(c.cache)) { return; } - var rowIndex, tbodyIndex, currentGroup, $rows, groupClass, grouping, cache, saveName, direction, + var rowIndex, tbodyIndex, currentGroup, $rows, groupClass, grouping, norm_rows, saveName, direction, lang = wo.grouping_language, group = '', savedGroup = false, @@ -89,15 +89,15 @@ ts.grouping = { } } for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++) { - cache = c.cache[tbodyIndex].normalized; + norm_rows = c.cache[tbodyIndex].normalized; group = ''; // clear grouping across tbodies $rows = c.$tbodies.eq(tbodyIndex).children('tr').not('.' + c.cssChildRow); for (rowIndex = 0; rowIndex < $rows.length; rowIndex++) { if ( $rows.eq(rowIndex).is(':visible') ) { // fixes #438 if (ts.grouping.types[grouping[1]]) { - currentGroup = cache[rowIndex] ? - ts.grouping.types[grouping[1]]( c, c.$headers.filter('[data-column="' + column + '"]:last'), cache[rowIndex][column], /date/.test(groupClass) ? + currentGroup = norm_rows[rowIndex] ? + ts.grouping.types[grouping[1]]( c, c.$headers.filter('[data-column="' + column + '"]:last'), norm_rows[rowIndex][column], /date/.test(groupClass) ? grouping[2] : parseInt(grouping[2] || 1, 10) || 1, group, lang ) : currentGroup; if (group !== currentGroup) { group = currentGroup; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index 07e0064..50f9bd4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -393,8 +393,8 @@ }, init : function(table, thisWidget, c, wo){ c.$table - .off( $.trim(math.events) + ' ' + $.trim('updateComplete.tsmath ' + wo.math_event) ) - .on( $.trim(math.events) + ' ' + wo.math_event, function(e){ + .off( (math.events + ' updateComplete.tsmath ' + wo.math_event).replace(/\s+/g, ' ') ) + .on(math.events + ' ' + wo.math_event, function(e){ var init = e.type === 'tablesorter-initialized'; if (e.type === 'updateAll') { // redo data-column indexes in case columns were rearranged @@ -415,7 +415,7 @@ remove: function(table, c, wo, refreshing){ if (refreshing) { return; } $(table) - .off( $.trim(math.events) + ' ' + $.trim('updateComplete.tsmath ' + wo.math_event) ) + .off( (math.events + ' updateComplete.tsmath ' + wo.math_event).replace(/\s+/g, ' ') ) .find('[data-' + wo.math_data + ']').empty(); } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 728dbb8..6c4f968 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -167,7 +167,7 @@ tsp = ts.pager = { p.initializing = true; if (wo.pager_savePages && ts.storage) { t = ts.storage(table, wo.pager_storageKey) || {}; // fixes #387 - p.page = ( isNaN(t.page) ? p.page : t.page ) || p.setPage || 1; + p.page = ( isNaN(t.page) ? p.page : t.page ) || p.setPage || 0; p.size = ( isNaN(t.size) ? p.size : t.size ) || p.setSize || 10; $.data(table, 'pagerLastSize', p.size); } @@ -371,8 +371,7 @@ tsp = ts.pager = { }, calcFilters: function(table, c) { - var tbodyIndex, - wo = c.widgetOptions, + var wo = c.widgetOptions, p = c.pager, hasFilters = c.$table.hasClass('hasFilters'); if (hasFilters && !wo.pager_ajaxUrl) { @@ -380,10 +379,8 @@ tsp = ts.pager = { // delayInit: true so nothing is in the cache p.filteredRows = p.totalRows = c.$tbodies.eq(0).children('tr').not( wo.pager_countChildRows ? '' : '.' + c.cssChildRow ).length; } else { - // just in case the pager tbody isn't the first tbody - tbodyIndex = c.$table.children('tbody').index( c.$tbodies.eq(0) ); p.filteredRows = 0; - $.each(c.cache[tbodyIndex].normalized, function(i, el) { + $.each(c.cache[0].normalized, function(i, el) { p.filteredRows += p.regexRows.test(el[c.columns].$row[0].className) ? 0 : 1; }); } @@ -898,8 +895,7 @@ tsp = ts.pager = { if ( !$.isEmptyObject(table.config.cache) ) { var i, rows = [], - tbodyIndex = c.$table.children('tbody').index( c.$tbodies.eq(0) ), - n = table.config.cache[tbodyIndex].normalized; + n = table.config.cache[0].normalized; p.totalRows = n.length; for (i = 0; i < p.totalRows; i++) { rows.push(n[i][c.columns].$row); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js new file mode 100644 index 0000000..fd8e8fc --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js @@ -0,0 +1,176 @@ +/*! Widget: resizable */ +;(function ($, window) { +'use strict'; +var ts = $.tablesorter = $.tablesorter || {}; + +$.extend(ts.css, { + resizer : 'tablesorter-resizer' // resizable +}); + +// this widget saves the column widths if +// $.tablesorter.storage function is included +// ************************** +ts.addWidget({ + id: "resizable", + priority: 40, + options: { + resizable : true, + resizable_addLastColumn : false, + resizable_widths : [], + resizable_throttle : false // set to true (5ms) or any number 0-10 range + }, + format: function(table, c, wo) { + if (c.$table.hasClass('hasResizable')) { return; } + c.$table.addClass('hasResizable'); + ts.resizableReset(table, true); // set default widths + var $rows, $columns, $column, column, timer, + storedSizes = {}, + $table = c.$table, + $wrap = $table.parent(), + overflow = $table.parent().css('overflow') === 'auto', + mouseXPosition = 0, + $target = null, + $next = null, + fullWidth = Math.abs($table.parent().width() - $table.width()) < 20, + mouseMove = function(event){ + if (mouseXPosition === 0 || !$target) { return; } + // resize columns + var leftEdge = event.pageX - mouseXPosition, + targetWidth = $target.width(); + $target.width( targetWidth + leftEdge ); + if ($target.width() !== targetWidth && fullWidth) { + $next.width( $next.width() - leftEdge ); + } else if (overflow) { + $table.width(function(i, w){ + return w + leftEdge; + }); + if (!$next.length) { + // if expanding right-most column, scroll the wrapper + $wrap[0].scrollLeft = $table.width(); + } + } + mouseXPosition = event.pageX; + }, + stopResize = function() { + if (ts.storage && $target && $next) { + storedSizes = {}; + storedSizes[$target.index()] = $target.width(); + storedSizes[$next.index()] = $next.width(); + $target.width( storedSizes[$target.index()] ); + $next.width( storedSizes[$next.index()] ); + if (wo.resizable !== false) { + // save all column widths + ts.storage(table, 'tablesorter-resizable', c.$headers.map(function(){ return $(this).width(); }).get() ); + } + } + mouseXPosition = 0; + $target = $next = null; + $(window).trigger('resize'); // will update stickyHeaders, just in case + }; + storedSizes = (ts.storage && wo.resizable !== false) ? ts.storage(table, 'tablesorter-resizable') : {}; + // process only if table ID or url match + if (storedSizes) { + for (column in storedSizes) { + if (!isNaN(column) && column < c.$headers.length) { + c.$headers.eq(column).width(storedSizes[column]); // set saved resizable widths + } + } + } + $rows = $table.children('thead:first').children('tr'); + // add resizable-false class name to headers (across rows as needed) + $rows.children().each(function() { + var canResize, + $column = $(this); + column = $column.attr('data-column'); + canResize = ts.getData( $column, ts.getColumnData( table, c.headers, column ), 'resizable') === "false"; + $rows.children().filter('[data-column="' + column + '"]')[canResize ? 'addClass' : 'removeClass']('resizable-false'); + }); + // add wrapper inside each cell to allow for positioning of the resizable target block + $rows.each(function() { + $column = $(this).children().not('.resizable-false'); + if (!$(this).find('.' + ts.css.wrapper).length) { + // Firefox needs this inner div to position the resizer correctly + $column.wrapInner('<div class="' + ts.css.wrapper + '" style="position:relative;height:100%;width:100%"></div>'); + } + // don't include the last column of the row + if (!wo.resizable_addLastColumn) { $column = $column.slice(0,-1); } + $columns = $columns ? $columns.add($column) : $column; + }); + $columns + .each(function() { + var $column = $(this), + padding = parseInt($column.css('padding-right'), 10) + 10; // 10 is 1/2 of the 20px wide resizer + $column + .find('.' + ts.css.wrapper) + .append('<div class="' + ts.css.resizer + '" style="cursor:w-resize;position:absolute;z-index:1;right:-' + + padding + 'px;top:0;height:100%;width:20px;"></div>'); + }) + .find('.' + ts.css.resizer) + .bind('mousedown', function(event) { + // save header cell and mouse position + $target = $(event.target).closest('th'); + var $header = c.$headers.filter('[data-column="' + $target.attr('data-column') + '"]'); + if ($header.length > 1) { $target = $target.add($header); } + // if table is not as wide as it's parent, then resize the table + $next = event.shiftKey ? $target.parent().find('th').not('.resizable-false').filter(':last') : $target.nextAll(':not(.resizable-false)').eq(0); + mouseXPosition = event.pageX; + }); + $(document) + .bind('mousemove.tsresize', function(event) { + // ignore mousemove if no mousedown + if (mouseXPosition === 0 || !$target) { return; } + if (wo.resizable_throttle) { + clearTimeout(timer); + timer = setTimeout(function(){ + mouseMove(event); + }, isNaN(wo.resizable_throttle) ? 5 : wo.resizable_throttle ); + } else { + mouseMove(event); + } + }) + .bind('mouseup.tsresize', function() { + stopResize(); + }); + + // right click to reset columns to default widths + $table.find('thead:first').bind('contextmenu.tsresize', function() { + ts.resizableReset(table); + // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset + var allowClick = $.isEmptyObject ? $.isEmptyObject(storedSizes) : true; + storedSizes = {}; + return allowClick; + }); + }, + remove: function(table, c) { + c.$table + .removeClass('hasResizable') + .children('thead') + .unbind('mouseup.tsresize mouseleave.tsresize contextmenu.tsresize') + .children('tr').children() + .unbind('mousemove.tsresize mouseup.tsresize') + // don't remove "tablesorter-wrapper" as uitheme uses it too + .find('.' + ts.css.resizer).remove(); + ts.resizableReset(table); + } +}); +ts.resizableReset = function(table, nosave) { + $(table).each(function(){ + var $t, + c = this.config, + wo = c && c.widgetOptions; + if (table && c) { + c.$headers.each(function(i){ + $t = $(this); + if (wo.resizable_widths && wo.resizable_widths[i]) { + $t.css('width', wo.resizable_widths[i]); + } else if (!$t.hasClass('resizable-false')) { + // don't clear the width of any column that is not resizable + $t.css('width',''); + } + }); + if (ts.storage && !nosave) { ts.storage(this, 'tablesorter-resizable', {}); } + } + }); +}; + +})(jQuery, window); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js new file mode 100644 index 0000000..d09310d --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js @@ -0,0 +1,68 @@ +/*! Widget: saveSort */ +;(function ($) { +'use strict'; +var ts = $.tablesorter = $.tablesorter || {}; + +// this widget saves the last sort only if the +// saveSort widget option is true AND the +// $.tablesorter.storage function is included +// ************************** +ts.addWidget({ + id: 'saveSort', + priority: 20, + options: { + saveSort : true + }, + init: function(table, thisWidget, c, wo) { + // run widget format before all other widgets are applied to the table + thisWidget.format(table, c, wo, true); + }, + format: function(table, c, wo, init) { + var stored, time, + $table = c.$table, + saveSort = wo.saveSort !== false, // make saveSort active/inactive; default to true + sortList = { "sortList" : c.sortList }; + if (c.debug) { + time = new Date(); + } + if ($table.hasClass('hasSaveSort')) { + if (saveSort && table.hasInitialized && ts.storage) { + ts.storage( table, 'tablesorter-savesort', sortList ); + if (c.debug) { + ts.benchmark('saveSort widget: Saving last sort: ' + c.sortList, time); + } + } + } else { + // set table sort on initial run of the widget + $table.addClass('hasSaveSort'); + sortList = ''; + // get data + if (ts.storage) { + stored = ts.storage( table, 'tablesorter-savesort' ); + sortList = (stored && stored.hasOwnProperty('sortList') && $.isArray(stored.sortList)) ? stored.sortList : ''; + if (c.debug) { + ts.benchmark('saveSort: Last sort loaded: "' + sortList + '"', time); + } + $table.bind('saveSortReset', function(event) { + event.stopPropagation(); + ts.storage( table, 'tablesorter-savesort', '' ); + }); + } + // init is true when widget init is run, this will run this widget before all other widgets have initialized + // this method allows using this widget in the original tablesorter plugin; but then it will run all widgets twice. + if (init && sortList && sortList.length > 0) { + c.sortList = sortList; + } else if (table.hasInitialized && sortList && sortList.length > 0) { + // update sort change + $table.trigger('sorton', [sortList]); + } + } + }, + remove: function(table, c) { + c.$table.removeClass('hasSaveSort'); + // clear storage + if (ts.storage) { ts.storage( table, 'tablesorter-savesort', '' ); } + } +}); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js index 1687731..ccdfacf 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js @@ -59,8 +59,8 @@ ts.addWidget({ addIndexes(table); // refresh static rows after updates c.$table - .unbind( $.trim('updateComplete.tsstaticrows ' + wo.staticRow_event) ) - .bind( $.trim('updateComplete.tsstaticrows ' + wo.staticRow_event), function(){ + .unbind( ('updateComplete.tsstaticrows ' + wo.staticRow_event).replace(/\s+/g, ' ') ) + .bind('updateComplete.tsstaticrows ' + wo.staticRow_event, function(){ addIndexes(table); c.$table.trigger('applyWidgets'); }); @@ -113,7 +113,7 @@ ts.addWidget({ }, remove : function(table, c, wo){ - c.$table.unbind( $.trim('updateComplete.tsstaticrows ' + wo.staticRow_event) ); + c.$table.unbind( ('updateComplete.tsstaticrows ' + wo.staticRow_event).replace(/\s+/g, ' ') ); } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js new file mode 100644 index 0000000..97c822b --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js @@ -0,0 +1,269 @@ +/*! Widget: stickyHeaders */ +;(function ($, window) { +'use strict'; +var ts = $.tablesorter = $.tablesorter || {}; + +$.extend(ts.css, { + sticky : 'tablesorter-stickyHeader', // stickyHeader + stickyVis : 'tablesorter-sticky-visible', + stickyWrap: 'tablesorter-sticky-wrapper' +}); + +// Add a resize event to table headers +ts.addHeaderResizeEvent = function(table, disable, settings) { + table = $(table)[0]; // make sure we're using a dom element + var headers, + defaults = { + timer : 250 + }, + options = $.extend({}, defaults, settings), + c = table.config, + wo = c.widgetOptions, + checkSizes = function(triggerEvent) { + wo.resize_flag = true; + headers = []; + c.$headers.each(function() { + var $header = $(this), + sizes = $header.data('savedSizes') || [0,0], // fixes #394 + width = this.offsetWidth, + height = this.offsetHeight; + if (width !== sizes[0] || height !== sizes[1]) { + $header.data('savedSizes', [ width, height ]); + headers.push(this); + } + }); + if (headers.length && triggerEvent !== false) { + c.$table.trigger('resize', [ headers ]); + } + wo.resize_flag = false; + }; + checkSizes(false); + clearInterval(wo.resize_timer); + if (disable) { + wo.resize_flag = false; + return false; + } + wo.resize_timer = setInterval(function() { + if (wo.resize_flag) { return; } + checkSizes(); + }, options.timer); +}; + +// Sticky headers based on this awesome article: +// http://css-tricks.com/13465-persistent-headers/ +// and https://github.com/jmosbech/StickyTableHeaders by Jonas Mosbech +// ************************** +ts.addWidget({ + id: "stickyHeaders", + priority: 60, // sticky widget must be initialized after the filter widget! + options: { + stickyHeaders : '', // extra class name added to the sticky header row + stickyHeaders_attachTo : null, // jQuery selector or object to attach sticky header to + stickyHeaders_xScroll : null, // jQuery selector or object to monitor horizontal scroll position (defaults: xScroll > attachTo > window) + stickyHeaders_yScroll : null, // jQuery selector or object to monitor vertical scroll position (defaults: yScroll > attachTo > window) + stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element + stickyHeaders_filteredToTop: true, // scroll table top into view after filtering + stickyHeaders_cloneId : '-sticky', // added to table ID, if it exists + stickyHeaders_addResizeEvent : true, // trigger "resize" event on headers + stickyHeaders_includeCaption : true, // if false and a caption exist, it won't be included in the sticky header + stickyHeaders_zIndex : 2 // The zIndex of the stickyHeaders, allows the user to adjust this to their needs + }, + format: function(table, c, wo) { + // filter widget doesn't initialize on an empty table. Fixes #449 + if ( c.$table.hasClass('hasStickyHeaders') || ($.inArray('filter', c.widgets) >= 0 && !c.$table.hasClass('hasFilters')) ) { + return; + } + var $table = c.$table, + $attach = $(wo.stickyHeaders_attachTo), + namespace = c.namespace + 'stickyheaders ', + // element to watch for the scroll event + $yScroll = $(wo.stickyHeaders_yScroll || wo.stickyHeaders_attachTo || window), + $xScroll = $(wo.stickyHeaders_xScroll || wo.stickyHeaders_attachTo || window), + $thead = $table.children('thead:first'), + $header = $thead.children('tr').not('.sticky-false').children(), + $tfoot = $table.children('tfoot'), + $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '', + stickyOffset = $attach.length ? 0 : $stickyOffset.length ? + $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, + // is this table nested? If so, find parent sticky header wrapper (div, not table) + $nestedSticky = $table.parent().closest('.' + ts.css.table).hasClass('hasStickyHeaders') ? + $table.parent().closest('table.tablesorter')[0].config.widgetOptions.$sticky.parent() : [], + nestedStickyTop = $nestedSticky.length ? $nestedSticky.height() : 0, + // clone table, then wrap to make sticky header + $stickyTable = wo.$sticky = $table.clone() + .addClass('containsStickyHeaders ' + ts.css.sticky + ' ' + wo.stickyHeaders) + .wrap('<div class="' + ts.css.stickyWrap + '">'), + $stickyWrap = $stickyTable.parent().css({ + position : $attach.length ? 'absolute' : 'fixed', + padding : parseInt( $stickyTable.parent().parent().css('padding-left'), 10 ), + top : stickyOffset + nestedStickyTop, + left : 0, + visibility : 'hidden', + zIndex : wo.stickyHeaders_zIndex || 2 + }), + $stickyThead = $stickyTable.children('thead:first'), + $stickyCells, + laststate = '', + spacing = 0, + setWidth = function($orig, $clone){ + $orig.filter(':visible').each(function(i) { + var width, border, + $cell = $clone.filter(':visible').eq(i), + $this = $(this); + // code from https://github.com/jmosbech/StickyTableHeaders + if ($this.css('box-sizing') === 'border-box') { + width = $this.outerWidth(); + } else { + if ($cell.css('border-collapse') === 'collapse') { + if (window.getComputedStyle) { + width = parseFloat( window.getComputedStyle(this, null).width ); + } else { + // ie8 only + border = parseFloat( $this.css('border-width') ); + width = $this.outerWidth() - parseFloat( $this.css('padding-left') ) - parseFloat( $this.css('padding-right') ) - border; + } + } else { + width = $this.width(); + } + } + $cell.css({ + 'min-width': width, + 'max-width': width + }); + }); + }, + resizeHeader = function() { + stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0; + spacing = 0; + $stickyWrap.css({ + left : $attach.length ? parseInt($attach.css('padding-left'), 10) || 0 : + $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing, + width: $table.outerWidth() + }); + setWidth( $table, $stickyTable ); + setWidth( $header, $stickyCells ); + }; + // save stickyTable element to config + // it is also saved to wo.$sticky + if (c.$extraTables && c.$extraTables.length) { + c.$extraTables.add($stickyTable); + } else { + c.$extraTables = $stickyTable; + } + // fix clone ID, if it exists - fixes #271 + if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; } + // clear out cloned table, except for sticky header + // include caption & filter row (fixes #126 & #249) - don't remove cells to get correct cell indexing + $stickyTable.find('thead:gt(0), tr.sticky-false').hide(); + $stickyTable.find('tbody, tfoot').remove(); + $stickyTable.find('caption').toggle(wo.stickyHeaders_includeCaption); + // issue #172 - find td/th in sticky header + $stickyCells = $stickyThead.children().children(); + $stickyTable.css({ height:0, width:0, margin: 0 }); + // remove resizable block + $stickyCells.find('.' + ts.css.resizer).remove(); + // update sticky header class names to match real header after sorting + $table + .addClass('hasStickyHeaders') + .bind('pagerComplete' + namespace, function() { + resizeHeader(); + }); + + ts.bindEvents(table, $stickyThead.children().children('.tablesorter-header')); + + // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. + $table.after( $stickyWrap ); + + // onRenderHeader is defined, we need to do something about it (fixes #641) + if (c.onRenderHeader) { + $stickyThead.children('tr').children().each(function(index){ + // send second parameter + c.onRenderHeader.apply( $(this), [ index, c, $stickyTable ] ); + }); + } + + // make it sticky! + $xScroll.add($yScroll) + .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') ) + .bind('scroll resize '.split(' ').join( namespace ), function(event) { + if (!$table.is(':visible')) { return; } // fixes #278 + // Detect nested tables - fixes #724 + nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; + var prefix = 'tablesorter-sticky-', + offset = $table.offset(), + yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 + xWindow = $.isWindow( $xScroll[0] ), + // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, + scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, + tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)), + isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', + cssSettings = { visibility : isVisible }; + + if ($attach.length) { + cssSettings.top = yWindow ? scrollTop : $attach.scrollTop(); + } + if (xWindow) { + // adjust when scrolling horizontally - fixes issue #143 + cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing; + } + if ($nestedSticky.length) { + cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; + } + $stickyWrap + .removeClass(prefix + 'visible ' + prefix + 'hidden') + .addClass(prefix + isVisible) + .css(cssSettings); + if (isVisible !== laststate || event.type === 'resize') { + // make sure the column widths match + resizeHeader(); + laststate = isVisible; + } + }); + if (wo.stickyHeaders_addResizeEvent) { + ts.addHeaderResizeEvent(table); + } + + // look for filter widget + if ($table.hasClass('hasFilters') && wo.filter_columnFilters) { + // scroll table into view after filtering, if sticky header is active - #482 + $table.bind('filterEnd' + namespace, function() { + // $(':focus') needs jQuery 1.6+ + var $td = $(document.activeElement).closest('td'), + column = $td.parent().children().index($td); + // only scroll if sticky header is active + if ($stickyWrap.hasClass(ts.css.stickyVis) && wo.stickyHeaders_filteredToTop) { + // scroll to original table (not sticky clone) + window.scrollTo(0, $table.position().top); + // give same input/select focus; check if c.$filters exists; fixes #594 + if (column >= 0 && c.$filters) { + c.$filters.eq(column).find('a, select, input').filter(':visible').focus(); + } + } + }); + ts.filter.bindSearch( $table, $stickyCells.find('.' + ts.css.filter) ); + // support hideFilters + if (wo.filter_hideFilters) { + ts.filter.hideFilters($stickyTable, c); + } + } + + $table.trigger('stickyHeadersInit'); + + }, + remove: function(table, c, wo) { + var namespace = c.namespace + 'stickyheaders '; + c.$table + .removeClass('hasStickyHeaders') + .unbind( ('pagerComplete filterEnd '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) + .next('.' + ts.css.stickyWrap).remove(); + if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table + $(window) + .add(wo.stickyHeaders_xScroll) + .add(wo.stickyHeaders_yScroll) + .add(wo.stickyHeaders_attachTo) + .unbind( ('scroll resize '.split(' ').join(namespace)).replace(/\s+/g, ' ') ); + ts.addHeaderResizeEvent(table, false); + } +}); + +})(jQuery, window); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js new file mode 100644 index 0000000..12af34c --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js @@ -0,0 +1,76 @@ +/*! Widget: storage */ +;(function ($, window, document) { +'use strict'; + +var ts = $.tablesorter = $.tablesorter || {}; +// *** Store data in local storage, with a cookie fallback *** +/* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json) + if you need it, then include https://github.com/douglascrockford/JSON-js + + $.parseJSON is not available is jQuery versions older than 1.4.1, using older + versions will only allow storing information for one page at a time + + // *** Save data (JSON format only) *** + // val must be valid JSON... use http://jsonlint.com/ to ensure it is valid + var val = { "mywidget" : "data1" }; // valid JSON uses double quotes + // $.tablesorter.storage(table, key, val); + $.tablesorter.storage(table, 'tablesorter-mywidget', val); + + // *** Get data: $.tablesorter.storage(table, key); *** + v = $.tablesorter.storage(table, 'tablesorter-mywidget'); + // val may be empty, so also check for your data + val = (v && v.hasOwnProperty('mywidget')) ? v.mywidget : ''; + alert(val); // "data1" if saved, or "" if not +*/ +ts.storage = function(table, key, value, options) { + table = $(table)[0]; + var cookieIndex, cookies, date, + hasLocalStorage = false, + values = {}, + c = table.config, + $table = $(table), + id = options && options.id || $table.attr(options && options.group || + 'data-table-group') || table.id || $('.tablesorter').index( $table ), + url = options && options.url || $table.attr(options && options.page || + 'data-table-page') || c && c.fixedUrl || window.location.pathname; + // https://gist.github.com/paulirish/5558557 + if ('localStorage' in window) { + try { + window.localStorage.setItem('_tmptest', 'temp'); + hasLocalStorage = true; + window.localStorage.removeItem('_tmptest'); + } catch(error) {} + } + // *** get value *** + if ($.parseJSON) { + if (hasLocalStorage) { + values = $.parseJSON(localStorage[key] || 'null') || {}; + } else { + // old browser, using cookies + cookies = document.cookie.split(/[;\s|=]/); + // add one to get from the key to the value + cookieIndex = $.inArray(key, cookies) + 1; + values = (cookieIndex !== 0) ? $.parseJSON(cookies[cookieIndex] || 'null') || {} : {}; + } + } + // allow value to be an empty string too + if ((value || value === '') && window.JSON && JSON.hasOwnProperty('stringify')) { + // add unique identifiers = url pathname > table ID/index on page > data + if (!values[url]) { + values[url] = {}; + } + values[url][id] = value; + // *** set value *** + if (hasLocalStorage) { + localStorage[key] = JSON.stringify(values); + } else { + date = new Date(); + date.setTime(date.getTime() + (31536e+6)); // 365 days + document.cookie = key + '=' + (JSON.stringify(values)).replace(/\"/g,'\"') + '; expires=' + date.toGMTString() + '; path=/'; + } + } else { + return values && values[url] ? values[url][id] : ''; + } +}; + +})(jQuery, window, document); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js new file mode 100644 index 0000000..90e046d --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js @@ -0,0 +1,184 @@ +/*! Widget: uitheme */ +;(function ($) { +'use strict'; +var ts = $.tablesorter = $.tablesorter || {}; + +ts.themes = { + 'bootstrap' : { + table : 'table table-bordered table-striped', + caption : 'caption', + // header class names + header : 'bootstrap-header', // give the header a gradient background (theme.bootstrap_2.css) + sortNone : '', + sortAsc : '', + sortDesc : '', + active : '', // applied when column is sorted + hover : '', // custom css required - a defined bootstrap style may not override other classes + // icon class names + icons : '', // add "icon-white" to make them white; this icon class is added to the <i> in the header + iconSortNone : 'bootstrap-icon-unsorted', // class name added to icon when column is not sorted + iconSortAsc : 'icon-chevron-up glyphicon glyphicon-chevron-up', // class name added to icon when column has ascending sort + iconSortDesc : 'icon-chevron-down glyphicon glyphicon-chevron-down', // class name added to icon when column has descending sort + filterRow : '', // filter row class + footerRow : '', + footerCells : '', + even : '', // even row zebra striping + odd : '' // odd row zebra striping + }, + 'jui' : { + table : 'ui-widget ui-widget-content ui-corner-all', // table classes + caption : 'ui-widget-content', + // header class names + header : 'ui-widget-header ui-corner-all ui-state-default', // header classes + sortNone : '', + sortAsc : '', + sortDesc : '', + active : 'ui-state-active', // applied when column is sorted + hover : 'ui-state-hover', // hover class + // icon class names + icons : 'ui-icon', // icon class added to the <i> in the header + iconSortNone : 'ui-icon-carat-2-n-s', // class name added to icon when column is not sorted + iconSortAsc : 'ui-icon-carat-1-n', // class name added to icon when column has ascending sort + iconSortDesc : 'ui-icon-carat-1-s', // class name added to icon when column has descending sort + filterRow : '', + footerRow : '', + footerCells : '', + even : 'ui-widget-content', // even row zebra striping + odd : 'ui-state-default' // odd row zebra striping + } +}; + +$.extend(ts.css, { + wrapper : 'tablesorter-wrapper' // ui theme & resizable +}); + +ts.addWidget({ + id: "uitheme", + priority: 10, + format: function(table, c, wo) { + var i, hdr, icon, time, $header, $icon, $tfoot, $h, oldtheme, oldremove, oldIconRmv, hasOldTheme, + themesAll = ts.themes, + $table = c.$table.add( c.$extraTables ), + $headers = c.$headers.add( c.$extraHeaders ), + theme = c.theme || 'jui', + themes = themesAll[theme] || {}, + remove = $.trim( [ themes.sortNone, themes.sortDesc, themes.sortAsc, themes.active ].join( ' ' ) ), + iconRmv = $.trim( [ themes.iconSortNone, themes.iconSortDesc, themes.iconSortAsc ].join( ' ' ) ); + if (c.debug) { time = new Date(); } + // initialization code - run once + if (!$table.hasClass('tablesorter-' + theme) || c.theme !== c.appliedTheme || !wo.uitheme_applied) { + wo.uitheme_applied = true; + oldtheme = themesAll[c.appliedTheme] || {}; + hasOldTheme = !$.isEmptyObject(oldtheme); + oldremove = hasOldTheme ? [ oldtheme.sortNone, oldtheme.sortDesc, oldtheme.sortAsc, oldtheme.active ].join( ' ' ) : ''; + oldIconRmv = hasOldTheme ? [ oldtheme.iconSortNone, oldtheme.iconSortDesc, oldtheme.iconSortAsc ].join( ' ' ) : ''; + if (hasOldTheme) { + wo.zebra[0] = $.trim( ' ' + wo.zebra[0].replace(' ' + oldtheme.even, '') ); + wo.zebra[1] = $.trim( ' ' + wo.zebra[1].replace(' ' + oldtheme.odd, '') ); + c.$tbodies.children().removeClass( [oldtheme.even, oldtheme.odd].join(' ') ); + } + // update zebra stripes + if (themes.even) { wo.zebra[0] += ' ' + themes.even; } + if (themes.odd) { wo.zebra[1] += ' ' + themes.odd; } + // add caption style + $table.children('caption') + .removeClass(oldtheme.caption || '') + .addClass(themes.caption); + // add table/footer class names + $tfoot = $table + // remove other selected themes + .removeClass( (c.appliedTheme ? 'tablesorter-' + (c.appliedTheme || '') : '') + ' ' + (oldtheme.table || '') ) + .addClass('tablesorter-' + theme + ' ' + (themes.table || '')) // add theme widget class name + .children('tfoot'); + c.appliedTheme = c.theme; + + if ($tfoot.length) { + $tfoot + // if oldtheme.footerRow or oldtheme.footerCells are undefined, all class names are removed + .children('tr').removeClass(oldtheme.footerRow || '').addClass(themes.footerRow) + .children('th, td').removeClass(oldtheme.footerCells || '').addClass(themes.footerCells); + } + // update header classes + $headers + .removeClass( (hasOldTheme ? [oldtheme.header, oldtheme.hover, oldremove].join(' ') : '') || '' ) + .addClass(themes.header) + .not('.sorter-false') + .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') + .bind('mouseenter.tsuitheme mouseleave.tsuitheme', function(event) { + // toggleClass with switch added in jQuery 1.3 + $(this)[ event.type === 'mouseenter' ? 'addClass' : 'removeClass' ](themes.hover || ''); + }); + + $headers.each(function(){ + var $this = $(this); + if (!$this.find('.' + ts.css.wrapper).length) { + // Firefox needs this inner div to position the icon & resizer correctly + $this.wrapInner('<div class="' + ts.css.wrapper + '" style="position:relative;height:100%;width:100%"></div>'); + } + }); + if (c.cssIcon) { + // if c.cssIcon is '', then no <i> is added to the header + $headers + .find('.' + ts.css.icon) + .removeClass(hasOldTheme ? [oldtheme.icons, oldIconRmv].join(' ') : '') + .addClass(themes.icons || ''); + } + if ($table.hasClass('hasFilters')) { + $table.children('thead').children('.' + ts.css.filterRow) + .removeClass(hasOldTheme ? oldtheme.filterRow || '' : '') + .addClass(themes.filterRow || ''); + } + } + for (i = 0; i < c.columns; i++) { + $header = c.$headers.add(c.$extraHeaders).not('.sorter-false').filter('[data-column="' + i + '"]'); + $icon = (ts.css.icon) ? $header.find('.' + ts.css.icon) : $(); + $h = $headers.not('.sorter-false').filter('[data-column="' + i + '"]:last'); + if ($h.length) { + $header.removeClass(remove); + $icon.removeClass(iconRmv); + if ($h[0].sortDisabled) { + // no sort arrows for disabled columns! + $icon.removeClass(themes.icons || ''); + } else { + hdr = themes.sortNone; + icon = themes.iconSortNone; + if ($h.hasClass(ts.css.sortAsc)) { + hdr = [themes.sortAsc, themes.active].join(' '); + icon = themes.iconSortAsc; + } else if ($h.hasClass(ts.css.sortDesc)) { + hdr = [themes.sortDesc, themes.active].join(' '); + icon = themes.iconSortDesc; + } + $h + .addClass(hdr) + .find('.' + ts.css.icon) + .addClass(icon || ''); + } + } + } + if (c.debug) { + ts.benchmark("Applying " + theme + " theme", time); + } + }, + remove: function(table, c, wo, refreshing) { + if (!wo.uitheme_applied) { return; } + var $table = c.$table, + theme = c.appliedTheme || 'jui', + themes = ts.themes[ theme ] || ts.themes.jui, + $headers = $table.children('thead').children(), + remove = themes.sortNone + ' ' + themes.sortDesc + ' ' + themes.sortAsc, + iconRmv = themes.iconSortNone + ' ' + themes.iconSortDesc + ' ' + themes.iconSortAsc; + $table.removeClass('tablesorter-' + theme + ' ' + themes.table); + wo.uitheme_applied = false; + if (refreshing) { return; } + $table.find(ts.css.header).removeClass(themes.header); + $headers + .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') // remove hover + .removeClass(themes.hover + ' ' + remove + ' ' + themes.active) + .filter('.' + ts.css.filterRow) + .removeClass(themes.filterRow); + $headers.find('.' + ts.css.icon).removeClass(themes.icons + ' ' + iconRmv); + } +}); + +})(jQuery); diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css index 1b38c52..1c05db4 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css @@ -79,7 +79,7 @@ .tablesorter-blackice .tablesorter-processing { background-position: center center !important; background-repeat: no-repeat !important; - /* background-image: url(../addons/pager/icons/loading.gif) !important; */ + /* background-image: url(/assets/jquery-tablesorter/loading.gif) !important; */ background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css index 509c2f8..5e51f56 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css @@ -114,7 +114,7 @@ .tablesorter-blue .tablesorter-processing { background-position: center center !important; background-repeat: no-repeat !important; - /* background-image: url(../addons/pager/icons/loading.gif) !important; */ + /* background-image: url(/assets/jquery-tablesorter/loading.gif) !important; */ background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css index adca809..35bd6b8 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css @@ -78,7 +78,7 @@ .tablesorter-dark .tablesorter-processing { background-position: center center !important; background-repeat: no-repeat !important; - /* background-image: url(../addons/pager/icons/loading.gif) !important; */ + /* background-image: url(/assets/jquery-tablesorter/loading.gif) !important; */ background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css index 20fc153..4771513 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css @@ -81,7 +81,7 @@ Default Theme .tablesorter-default .tablesorter-processing { background-position: center center !important; background-repeat: no-repeat !important; - /* background-image: url(../addons/pager/icons/loading.gif) !important; */ + /* background-image: url(/assets/jquery-tablesorter/loading.gif) !important; */ background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css index dffddf5..fe33ff3 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css @@ -111,7 +111,7 @@ .tablesorter-dropbox .tablesorter-processing { background-position: center center !important; background-repeat: no-repeat !important; - /* background-image: url(../addons/pager/icons/loading.gif) !important; */ + /* background-image: url(/assets/jquery-tablesorter/loading.gif) !important; */ background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css index af002e5..3a0884e 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css @@ -96,7 +96,7 @@ .tablesorter-green .tablesorter-processing { background-position: center center !important; background-repeat: no-repeat !important; - /* background-image: url(../addons/pager/icons/loading.gif) !important; */ + /* background-image: url(/assets/jquery-tablesorter/loading.gif) !important; */ background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css index 540c35d..9a06d71 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css @@ -132,7 +132,7 @@ .tablesorter-grey .tablesorter-processing { background-position: center center !important; background-repeat: no-repeat !important; - /* background-image: url(../addons/pager/icons/loading.gif) !important; */ + /* background-image: url(/assets/jquery-tablesorter/loading.gif) !important; */ background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css index afd2ad0..25bba9d 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css @@ -88,7 +88,7 @@ .tablesorter-ice .tablesorter-processing { background-position: center center !important; background-repeat: no-repeat !important; - /* background-image: url(../addons/pager/icons/loading.gif) !important; */ + /* background-image: url(/assets/jquery-tablesorter/loading.gif) !important; */ background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css index 17e53c3..65e5fa3 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css @@ -71,7 +71,7 @@ .tablesorter-jui .tablesorter-processing .tablesorter-header-inner { background-position: center center !important; background-repeat: no-repeat !important; - /* background-image: url(../addons/pager/icons/loading.gif) !important; */ + /* background-image: url(/assets/jquery-tablesorter/loading.gif) !important; */ background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css index 7649bb8..4eb7cd7 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css @@ -79,7 +79,7 @@ Metro Dark Theme .tablesorter-metro-dark .tablesorter-processing { background-position: center center !important; background-repeat: no-repeat !important; - /* background-image: url(../addons/pager/icons/loading.gif) !important; */ + /* background-image: url(/assets/jquery-tablesorter/loading.gif) !important; */ background-image: url(data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=) !important; } From 16ddb0d0592205ccbec74269bbe1ac06a033f303 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Thu, 5 Mar 2015 22:08:27 +0100 Subject: [PATCH 050/138] * updated tablesorter to latest version (2.21.0) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 101 +++++++--- .../jquery-tablesorter/jquery.tablesorter.js | 174 +++++++++++------- .../jquery.tablesorter.widgets.js | 145 ++++++++------- .../parsers/parser-date-range.js | 93 ++++++++++ .../parsers/parser-input-select.js | 10 +- .../parsers/parser-metric.js | 2 +- .../widgets/widget-columnSelector.js | 6 +- .../widgets/widget-filter-formatter-html5.js | 4 +- .../widgets/widget-filter-formatter-jui.js | 5 +- .../widget-filter-formatter-select2.js | 2 +- .../widgets/widget-filter-type-insideRange.js | 42 +++++ .../widgets/widget-filter.js | 102 +++++----- .../widgets/widget-formatter.js | 2 +- .../widgets/widget-grouping.js | 16 +- .../widgets/widget-output.js | 10 +- .../widgets/widget-pager.js | 98 +++++++--- .../widgets/widget-scroller.js | 82 ++++----- .../widgets/widget-stickyHeaders.js | 41 +++-- 22 files changed, 631 insertions(+), 314 deletions(-) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-range.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 356fa86..e872ca6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.16.0 (2015-03-05) + +* Upgrade tablesorter to v2.21.0 + #### v1.15.0 (2015-02-21) * Upgrade tablesorter to v2.20.1 diff --git a/README.md b/README.md index 1946553..d95cb19 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.20.1 (2/20/2015), [documentation] +Current tablesorter version: 2.21.0 (3/5/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index f5dbc86..04cdd0e 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.15.0' + VERSION = '1.16.0' end diff --git a/tablesorter b/tablesorter index 44ee945..087ade7 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 44ee94502bfafaf540092649fde6a7ccb428507a +Subproject commit 087ade72551349d1c23fc3c2ac4e53412ffa9a12 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index b2c6120..0408957 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin - * updated 2/9/2015 (v2.19.1) + * updated 3/5/2015 (v2.21.0) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -135,7 +135,8 @@ }, calcFilters = function(table, p) { - var c = table.config, + var normalized, indx, len, + c = table.config, hasFilters = c.$table.hasClass('hasFilters'); if (hasFilters && !p.ajaxUrl) { if ($.isEmptyObject(c.cache)) { @@ -143,9 +144,11 @@ p.filteredRows = p.totalRows = c.$tbodies.eq(0).children('tr').not( p.countChildRows ? '' : '.' + c.cssChildRow ).length; } else { p.filteredRows = 0; - $.each(c.cache[0].normalized, function(i, el) { - p.filteredRows += p.regexRows.test(el[c.columns].$row[0].className) ? 0 : 1; - }); + normalized = c.cache[0].normalized; + len = normalized.length; + for (indx = 0; indx < len; indx++) { + p.filteredRows += p.regexRows.test(normalized[indx][c.columns].$row[0].className) ? 0 : 1; + } } } else if (!hasFilters) { p.filteredRows = p.totalRows; @@ -154,7 +157,7 @@ updatePageDisplay = function(table, p, completed) { if ( p.initializing ) { return; } - var s, t, $out, + var s, t, $out, indx, len, options, c = table.config, sz = p.size || p.settings.size || 10; // don't allow dividing by zero if (p.countChildRows) { t.push(c.cssChildRow); } @@ -192,9 +195,11 @@ }); if ( p.$goto.length ) { t = ''; - $.each(buildPageSelect(p), function(i, opt){ - t += '<option value="' + opt + '">' + opt + '</option>'; - }); + options = buildPageSelect(p); + len = options.length; + for (indx = 0; indx < len; indx++) { + t += '<option value="' + options[indx] + '">' + options[indx] + '</option>'; + } // innerHTML doesn't work in IE9 - http://support2.microsoft.com/kb/276228 p.$goto.html(t).val( p.page + 1 ); } @@ -211,6 +216,9 @@ pagerArrows(p); fixHeight(table, p); if (p.initialized && completed !== false) { + if (c.debug) { + ts.log('Pager: Triggering pagerComplete'); + } c.$table.trigger('pagerComplete', p); // save pager info to storage if (p.savePages && ts.storage) { @@ -381,7 +389,7 @@ if ( exception ) { if (c.debug) { - ts.log('Ajax Error', xhr, exception); + ts.log('Pager: >> Ajax Error', xhr, exception); } ts.showError(table, xhr.status === 0 ? 'Not connected, verify Network' : @@ -476,6 +484,9 @@ // apply widgets after table has rendered & after a delay to prevent // multiple applyWidget blocking code from blocking this trigger setTimeout(function(){ + if (c.debug) { + ts.log('Pager: Triggering pagerChange'); + } $t .trigger('applyWidgets') .trigger('pagerChange', p); @@ -488,6 +499,9 @@ if (!p.initialized) { p.initialized = true; p.initializing = false; + if (table.config.debug) { + ts.log('Pager: Triggering pagerInitialized'); + } $(table) .trigger('applyWidgets') .trigger('pagerInitialized', p); @@ -525,48 +539,51 @@ } }; if (c.debug) { - ts.log('ajax initialized', p.ajaxObject); + ts.log('Pager: Ajax initialized', p.ajaxObject); } $.ajax(p.ajaxObject); } }, getAjaxUrl = function(table, p) { - var c = table.config, + var indx, len, + c = table.config, url = (p.ajaxUrl) ? p.ajaxUrl // allow using "{page+1}" in the url string to switch to a non-zero based index .replace(/\{page([\-+]\d+)?\}/, function(s,n){ return p.page + (n ? parseInt(n, 10) : 0); }) .replace(/\{size\}/g, p.size) : '', - sl = c.sortList, - fl = p.currentFilters || $(table).data('lastSearch') || [], + sortList = c.sortList, + filterList = p.currentFilters || $(table).data('lastSearch') || [], sortCol = url.match(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/), filterCol = url.match(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/), arry = []; if (sortCol) { sortCol = sortCol[1]; - $.each(sl, function(i,v){ - arry.push(sortCol + '[' + v[0] + ']=' + v[1]); - }); + len = sortList.length; + for (indx = 0; indx < len; indx++) { + arry.push(sortCol + '[' + sortList[indx][0] + ']=' + sortList[indx][1]); + } // if the arry is empty, just add the col parameter... "&{sortList:col}" becomes "&col" url = url.replace(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : sortCol ); arry = []; } if (filterCol) { filterCol = filterCol[1]; - $.each(fl, function(i,v){ - if (v) { - arry.push(filterCol + '[' + i + ']=' + encodeURIComponent(v)); + len = filterList.length; + for (indx = 0; indx < len; indx++) { + if (filterList[indx]) { + arry.push(filterCol + '[' + indx + ']=' + encodeURIComponent(filterList[indx])); } - }); + } // if the arry is empty, just add the fcol parameter... "&{filterList:fcol}" becomes "&fcol" url = url.replace(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : filterCol ); - p.currentFilters = fl; + p.currentFilters = filterList; } if ( typeof(p.customAjaxUrl) === "function" ) { url = p.customAjaxUrl(table, url); } if (c.debug) { - ts.log('Pager ajax url: ' + url); + ts.log('Pager: Ajax url = ' + url); } return url; }, @@ -581,7 +598,7 @@ e = p.size; if ( l < 1 ) { if (c.debug) { - ts.log('Pager: no rows for pager to render'); + ts.log('Pager: >> No rows for pager to render'); } // empty table, abort! return; @@ -592,7 +609,12 @@ } p.cacheIndex = []; p.isDisabled = false; // needed because sorting will change the page and re-enable the pager - if (p.initialized) { $t.trigger('pagerChange', p); } + if (p.initialized) { + if (c.debug) { + ts.log('Pager: Triggering pagerChange'); + } + $t.trigger('pagerChange', p); + } if ( !p.removeRows ) { hideRows(table, p); @@ -619,6 +641,9 @@ } updatePageDisplay(table, p); if (table.isUpdating) { + if (c.debug) { + ts.log('Pager: Triggering updateComplete'); + } $t.trigger('updateComplete', [ table, true ]); } }, @@ -640,7 +665,7 @@ renderTable(table, table.config.rowsCopy, p); $(table).trigger('applyWidgets'); if (table.config.debug) { - ts.log('pager disabled'); + ts.log('Pager: Disabled'); } } // disable size selector @@ -694,7 +719,7 @@ (l.optAjaxUrl || '') === (p.ajaxUrl || '') && l.sortList === (c.sortList || []).join(',') ) { return; } if (c.debug) { - ts.log('Pager changing to page ' + p.page); + ts.log('Pager: Changing to page ' + p.page); } p.last = { page : p.page, @@ -713,10 +738,16 @@ } $.data(table, 'pagerLastPage', p.page); if (p.initialized && pageMoved !== false) { + if (c.debug) { + ts.log('Pager: Triggering pageMoved'); + } $t .trigger('pageMoved', p) .trigger('applyWidgets'); if (table.isUpdating) { + if (c.debug) { + ts.log('Pager: Triggering updateComplete'); + } $t.trigger('updateComplete', [ table, true ]); } } @@ -794,7 +825,7 @@ setPageSize(table, p.size, p); hideRowsSetup(table, p); if (c.debug) { - ts.log('pager enabled'); + ts.log('Pager: Enabled'); } } }; @@ -828,7 +859,7 @@ // save a copy of the original settings p.settings = $.extend( true, {}, $.tablesorterPager.defaults, settings ); if (c.debug) { - ts.log('Pager initializing'); + ts.log('Pager: Initializing'); } p.oldAjaxSuccess = p.oldAjaxSuccess || p.ajaxObject.success; c.appender = $this.appender; @@ -921,6 +952,9 @@ // clicked controls ctrls = [ p.cssFirst, p.cssPrev, p.cssNext, p.cssLast ]; fxn = [ moveToFirstPage, moveToPrevPage, moveToNextPage, moveToLastPage ]; + if (c.debug && !pager.length) { + ts.log('Pager: >> Container not found'); + } pager.find(ctrls.join(',')) .attr("tabindex", 0) .unbind('click.pager') @@ -947,6 +981,8 @@ moveToPage(table, p, true); updatePageDisplay(table, p, false); }); + } else if (c.debug) { + ts.log('Pager: >> Goto selector not found'); } // page size selector @@ -962,6 +998,8 @@ } return false; }); + } else if (c.debug) { + ts.log('Pager: >> Size selector not found'); } // clear initialized flag @@ -989,7 +1027,10 @@ p.initializing = false; p.initialized = true; moveToPage(table, p); - $(table).trigger('pagerInitialized', p); + if (c.debug) { + ts.log('Pager: Triggering pagerInitialized'); + } + c.$table.trigger('pagerInitialized', p); if ( !( c.widgetOptions.filter_initialized && ts.hasWidget(table, 'filter') ) ) { updatePageDisplay(table, p, false); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index fb16b44..6003232 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v{{version}} *//* +/*! TableSorter (FORK) v2.21.0 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -34,7 +34,7 @@ var ts = this; - ts.version = '{{version}}'; + ts.version = '2.21.0'; ts.parsers = []; ts.widgets = []; @@ -154,6 +154,9 @@ nextNone : 'activate to remove the sort' }; + // These methods can be applied on table.config instance + ts.instanceMethods = {}; + /* debuging utils */ function log() { var a = arguments[0], @@ -256,7 +259,7 @@ if (rows.length) { l = c.columns; // rows[j].cells.length; for (i = 0; i < l; i++) { - h = c.$headers.filter('[data-column="' + i + '"]:last'); + h = c.$headerIndexed[i]; // get column indexed table cell ch = ts.getColumnData( table, c.headers, i ); // get column parser/extractor @@ -445,8 +448,7 @@ } function buildHeaders(table) { - var ch, $t, - h, i, t, lock, time, + var ch, $t, h, i, t, lock, time, indx, c = table.config; c.headerList = []; c.headerContent = []; @@ -495,6 +497,13 @@ if (c.tabIndex) { $t.attr('tabindex', 0); } return elem; })); + // cache headers per column + c.$headerIndexed = []; + for (indx = 0; indx < c.columns; indx++) { + $t = c.$headers.filter('[data-column="' + indx + '"]'); + // target sortable column cells, unless there are none, then use non-sortable cells + c.$headerIndexed[indx] = $t.not('.sorter-false').length ? $t.not('.sorter-false').last() : $t.last(); + } $(table).find(c.selectorHeaders).attr({ scope: 'col', role : 'columnheader' @@ -592,49 +601,51 @@ }); } - function updateHeaderSortCount(table, list) { - var s, t, o, col, primary, + function updateHeaderSortCount( table, list ) { + var col, dir, group, header, indx, primary, temp, val, c = table.config, - sl = list || c.sortList; + sortList = list || c.sortList, + len = sortList.length; c.sortList = []; - $.each(sl, function(i,v){ + for (indx = 0; indx < len; indx++) { + val = sortList[indx]; // ensure all sortList values are numeric - fixes #127 - col = parseInt(v[0], 10); + col = parseInt(val[0], 10); // make sure header exists - o = c.$headers.filter('[data-column="' + col + '"]:last')[0]; - if (o) { // prevents error if sorton array is wrong + header = c.$headerIndexed[col][0]; + if (header) { // prevents error if sorton array is wrong // o.count = o.count + 1; - t = ('' + v[1]).match(/^(1|d|s|o|n)/); - t = t ? t[0] : ''; + dir = ('' + val[1]).match(/^(1|d|s|o|n)/); + dir = dir ? dir[0] : ''; // 0/(a)sc (default), 1/(d)esc, (s)ame, (o)pposite, (n)ext - switch(t) { + switch(dir) { case '1': case 'd': // descending - t = 1; + dir = 1; break; case 's': // same direction (as primary column) // if primary sort is set to 's', make it ascending - t = primary || 0; + dir = primary || 0; break; case 'o': - s = o.order[(primary || 0) % (c.sortReset ? 3 : 2)]; + temp = header.order[(primary || 0) % (c.sortReset ? 3 : 2)]; // opposite of primary column; but resets if primary resets - t = s === 0 ? 1 : s === 1 ? 0 : 2; + dir = temp === 0 ? 1 : temp === 1 ? 0 : 2; break; case 'n': - o.count = o.count + 1; - t = o.order[(o.count) % (c.sortReset ? 3 : 2)]; + header.count = header.count + 1; + dir = header.order[(header.count) % (c.sortReset ? 3 : 2)]; break; default: // ascending - t = 0; + dir = 0; break; } - primary = i === 0 ? t : primary; - s = [ col, parseInt(t, 10) || 0 ]; - c.sortList.push(s); - t = $.inArray(s[1], o.order); // fixes issue #167 - o.count = t >= 0 ? t : s[1] % (c.sortReset ? 3 : 2); + primary = indx === 0 ? dir : primary; + group = [ col, parseInt(dir, 10) || 0 ]; + c.sortList.push(group); + dir = $.inArray(group[1], header.order); // fixes issue #167 + header.count = dir >= 0 ? dir : group[1] % (c.sortReset ? 3 : 2); } - }); + } } function getCachedSortType(parsers, i) { @@ -705,7 +716,7 @@ // reverse the sorting direction for (col = 0; col < c.sortList.length; col++) { s = c.sortList[col]; - order = c.$headers.filter('[data-column="' + s[0] + '"]:last')[0]; + order = c.$headerIndexed[ s[0] ][0]; if (s[0] === indx) { // order.count seems to be incorrect when compared to cell.count s[1] = order.order[cell.count]; @@ -1051,7 +1062,7 @@ return this.each(function() { var table = this, // merge & extend config options - c = $.extend(true, {}, ts.defaults, settings); + c = $.extend(true, {}, ts.defaults, settings, ts.instanceMethods); // save initial settings c.originalSettings = settings; // create a table from data (build table widget) @@ -1129,6 +1140,8 @@ // fixate columns if the users supplies the fixedWidth option // do this after theme has been applied ts.fixColumnWidth(table); + // add widget options before parsing (e.g. grouping widget has parser settings) + ts.applyWidgetOptions(table, c); // try to auto detect column type, and store in tables config buildParserCache(table); // start total row count at zero @@ -1600,6 +1613,12 @@ } }; + // Use it to add a set of methods to table.config which will be available for all tables. + // This should be done before table initialization + ts.addInstanceMethods = function(methods) { + $.extend(ts.instanceMethods, methods); + }; + ts.getParserById = function(name) { /*jshint eqeqeq:false */ if (name == 'false') { return false; } @@ -1631,9 +1650,24 @@ } }; + ts.applyWidgetOptions = function( table, c ){ + var indx, widget, + len = c.widgets.length, + wo = c.widgetOptions; + if (len) { + for (indx = 0; indx < len; indx++) { + widget = ts.getWidgetById( c.widgets[indx] ); + if ( widget && 'options' in widget ) { + wo = table.config.widgetOptions = $.extend( true, {}, widget.options, wo ); + } + } + } + }; + ts.applyWidget = function(table, init, callback) { table = $(table)[0]; // in case this is called externally - var c = table.config, + var indx, len, name, + c = table.config, wo = c.widgetOptions, tableClass = ' ' + c.table.className + ' ', widgets = [], @@ -1648,9 +1682,10 @@ // extract out the widget id from the table class (widget id's can include dashes) w = tableClass.match( wd ); if ( w ) { - $.each( w, function( i,n ){ - c.widgets.push( n.replace( wd, '$1' ) ); - }); + len = w.length; + for (indx = 0; indx < len; indx++) { + c.widgets.push( w[indx].replace( wd, '$1' ) ); + } } } if (c.widgets.length) { @@ -1659,41 +1694,45 @@ c.widgets = $.grep(c.widgets, function(v, k){ return $.inArray(v, c.widgets) === k; }); + name = c.widgets || []; + len = name.length; // build widget array & add priority as needed - $.each(c.widgets || [], function(i,n){ - wd = ts.getWidgetById(n); + for (indx = 0; indx < len; indx++) { + wd = ts.getWidgetById(name[indx]); if (wd && wd.id) { // set priority to 10 if not defined if (!wd.priority) { wd.priority = 10; } - widgets[i] = wd; + widgets[indx] = wd; } - }); + } // sort widgets by priority widgets.sort(function(a, b){ return a.priority < b.priority ? -1 : a.priority === b.priority ? 0 : 1; }); // add/update selected widgets - $.each(widgets, function(i,w){ - if (w) { - if (init || !(c.widgetInit[w.id])) { + len = widgets.length; + for (indx = 0; indx < len; indx++) { + if (widgets[indx]) { + if ( init || !( c.widgetInit[ widgets[indx].id ] ) ) { // set init flag first to prevent calling init more than once (e.g. pager) - c.widgetInit[w.id] = true; - if (w.hasOwnProperty('options')) { - wo = table.config.widgetOptions = $.extend( true, {}, w.options, wo ); + c.widgetInit[ widgets[indx].id ] = true; + if (table.hasInitialized) { + // don't reapply widget options on tablesorter init + ts.applyWidgetOptions( table, c ); } - if (w.hasOwnProperty('init')) { + if ( 'init' in widgets[indx] ) { if (c.debug) { time2 = new Date(); } - w.init(table, w, c, wo); - if (c.debug) { ts.benchmark('Initializing ' + w.id + ' widget', time2); } + widgets[indx].init(table, widgets[indx], c, wo); + if (c.debug) { ts.benchmark('Initializing ' + widgets[indx].id + ' widget', time2); } } } - if (!init && w.hasOwnProperty('format')) { + if ( !init && 'format' in widgets[indx] ) { if (c.debug) { time2 = new Date(); } - w.format(table, c, wo, false); - if (c.debug) { ts.benchmark( ( init ? 'Initializing ' : 'Applying ' ) + w.id + ' widget', time2); } + widgets[indx].format(table, c, wo, false); + if (c.debug) { ts.benchmark( ( init ? 'Initializing ' : 'Applying ' ) + widgets[indx].id + ' widget', time2); } } } - }); + } // callback executed on init only if (!init && typeof callback === 'function') { callback(table); @@ -1711,29 +1750,31 @@ ts.removeWidget = function(table, name, refreshing){ table = $(table)[0]; + var i, widget, indx, len, + c = table.config; // if name === true, add all widgets from $.tablesorter.widgets if (name === true) { name = []; - $.each( ts.widgets, function(i, w){ - if (w && w.id) { - name.push( w.id ); + len = ts.widgets.length; + for (indx = 0; indx < len; indx++) { + widget = ts.widgets[indx]; + if (widget && widget.id) { + name.push( widget.id ); } - }); + } } else { // name can be either an array of widgets names, // or a space/comma separated list of widget names name = ( $.isArray(name) ? name.join(',') : name || '' ).toLowerCase().split( /[\s,]+/ ); } - var i, widget, indx, - c = table.config, - len = name.length; + len = name.length; for (i = 0; i < len; i++) { widget = ts.getWidgetById(name[i]); indx = $.inArray( name[i], c.widgets ); if ( widget && 'remove' in widget ) { if (c.debug && indx >= 0) { log( 'Removing "' + name[i] + '" widget' ); } widget.remove(table, c, c.widgetOptions, refreshing); - c.widgetInit[name[i]] = false; + c.widgetInit[ name[i] ] = false; } // don't remove the widget from config.widget if refreshing if (indx >= 0 && refreshing !== true) { @@ -1744,18 +1785,21 @@ ts.refreshWidgets = function(table, doAll, dontapply) { table = $(table)[0]; // see issue #243 - var c = table.config, + var indx, + c = table.config, cw = c.widgets, + widgets = ts.widgets, + len = widgets.length, list = [], callback = function(table){ $(table).trigger('refreshComplete'); }; // remove widgets not defined in config.widgets, unless doAll is true - $.each( ts.widgets, function(i, w){ - if (w && w.id && (doAll || $.inArray( w.id, cw ) < 0)) { - list.push( w.id ); + for (indx = 0; indx < len; indx++) { + if (widgets[indx] && widgets[indx].id && (doAll || $.inArray( widgets[indx].id, cw ) < 0)) { + list.push( widgets[indx].id ); } - }); + } ts.removeWidget( table, list.join(','), true ); if (dontapply !== true) { // call widget init if @@ -1955,7 +1999,7 @@ if (s) { var date, d, c = table.config, - ci = c.$headers.filter('[data-column="' + cellIndex + '"]:last'), + ci = c.$headerIndexed[ cellIndex ], format = ci.length && ci[0].dateFormat || ts.getData( ci, ts.getColumnData( table, c.headers, cellIndex ), 'dateFormat') || c.dateFormat; d = s.replace(/\s+/g, ' ').replace(/[\-.,]/g, '/'); // escaped - because JSHint in Firefox was showing it as an error if (format === 'mmddyyyy') { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index e2325ef..f417eb1 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) widgets - updated 02-20-2015 (v2.20.1)*/ +/*! tablesorter (FORK) widgets - updated 03-05-2015 (v2.21.0)*/ /* Includes: storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort */ /*! Widget: storage */ ;(function ($, window, document) { @@ -347,7 +347,10 @@ ts.addWidget({ })(jQuery); -/*! Widget: filter */ +/*! Widget: filter - updated 3/5/2015 (v2.21.0) *//* + * Requires tablesorter v2.8+ and jQuery 1.7+ + * by Rob Garrison + */ ;(function ($) { 'use strict'; var ts = $.tablesorter = $.tablesorter || {}; @@ -558,7 +561,7 @@ ts.filter = { parsed = data.parsed[index], query = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.orReplace, "|"), index, parsed); // look for an exact match with the "or" unless the "filter-match" class is found - if (!c.$headers.filter('[data-column="' + index + '"]:last').hasClass('filter-match') && /\|/.test(query)) { + if (!c.$headerIndexed[index].hasClass('filter-match') && /\|/.test(query)) { // show all results while using filter match. Fixes #727 if (query[ query.length - 1 ] === '|') { query += '*'; } query = data.anyMatch && $.isArray(data.rowArray) ? '(' + query + ')' : '^(' + query + ')$'; @@ -679,7 +682,7 @@ ts.filter = { fxn = ts.getColumnData( table, wo.filter_functions, column ); if (fxn) { // remove "filter-select" from header otherwise the options added here are replaced with all options - $header = c.$headers.filter('[data-column="' + column + '"]:last').removeClass('filter-select'); + $header = c.$headerIndexed[column].removeClass('filter-select'); // don't build select if "filter-false" or "parser-false" set noSelect = !($header.hasClass('filter-false') || $header.hasClass('parser-false')); options = ''; @@ -775,7 +778,8 @@ ts.filter = { } }, filterInitComplete: function(c){ - var wo = c.widgetOptions, + var indx, len, + wo = c.widgetOptions, count = 0, completed = function(){ wo.filter_initialized = true; @@ -785,11 +789,12 @@ ts.filter = { if ( $.isEmptyObject( wo.filter_formatter ) ) { completed(); } else { - $.each( wo.filter_formatterInit, function(i, val) { - if (val === 1) { + len = wo.filter_formatterInit.length; + for (indx = 0; indx < len; indx++) { + if (wo.filter_formatterInit[indx] === 1) { count++; } - }); + } clearTimeout(wo.filter_initTimer); if (!wo.filter_initialized && count === wo.filter_formatterCount) { // filter widget initialized @@ -805,7 +810,7 @@ ts.filter = { }, setDefaults: function(table, c, wo) { - var isArray, saved, indx, + var isArray, saved, indx, col, $filters, // get current (default) filters filters = ts.getFilters(table) || []; if (wo.filter_saveFilters && ts.storage) { @@ -816,8 +821,12 @@ ts.filter = { } // if no filters saved, then check default settings if (filters.join('') === '') { - for (indx = 0; indx < c.columns; indx++) { - filters[indx] = c.$headers.filter('[data-column="' + indx + '"]:last').attr(wo.filter_defaultAttrib) || filters[indx]; + // allow adding default setting to external filters + $filters = c.$headers.add( wo.filter_$externalFilters ).filter('[' + wo.filter_defaultAttrib + ']'); + for (indx = 0; indx <= c.columns; indx++) { + // include data-column="all" external filters + col = indx === c.columns ? 'all' : indx; + filters[indx] = $filters.filter('[data-column="' + col + '"]').attr(wo.filter_defaultAttrib) || filters[indx] || ''; } } c.$table.data('lastSearch', filters); @@ -846,7 +855,7 @@ ts.filter = { for (column = 0; column < columns; column++) { disabled = false; // assuming last cell of a column is the main column - $header = c.$headers.filter('[data-column="' + column + '"]:last'); + $header = c.$headerIndexed[column]; ffxn = ts.getColumnData( table, wo.filter_functions, column ); buildSelect = (wo.filter_functions && ffxn && typeof ffxn !== "function" ) || $header.hasClass('filter-select'); @@ -1071,7 +1080,7 @@ ts.filter = { }, multipleColumns: function( c, $input ) { // look for multiple columns "1-3,4-6,8" in data-column - var ranges, singles, indx, + var temp, ranges, range, start, end, singles, i, indx, len, wo = c.widgetOptions, // only target "all" column inputs on initialization // & don't target "all" column inputs if they don't exist @@ -1081,31 +1090,32 @@ ts.filter = { // process column range if ( targets && /-/.test( val ) ) { ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); - $.each(ranges, function(i,v){ - var t, - range = v.split( /\s*-\s*/ ), - start = parseInt( range[0], 10 ) || 0, - end = parseInt( range[1], 10 ) || ( c.columns - 1 ); - if ( start > end ) { t = start; start = end; end = t; } // swap + len = ranges.length; + for (indx = 0; indx < len; indx++) { + range = ranges[indx].split( /\s*-\s*/ ); + start = parseInt( range[0], 10 ) || 0; + end = parseInt( range[1], 10 ) || ( c.columns - 1 ); + if ( start > end ) { temp = start; start = end; end = temp; } // swap if ( end >= c.columns ) { end = c.columns - 1; } for ( ; start <= end; start++ ) { columns.push(start); } // remove processed range from val - val = val.replace( v, '' ); - }); + val = val.replace( ranges[indx], '' ); + } } // process single columns if ( targets && /,/.test( val ) ) { singles = val.split( /\s*,\s*/ ); - $.each( singles, function(i,v) { - if (v !== '') { - indx = parseInt( v, 10 ); + len = singles.length; + for (i = 0; i < len; i++) { + if (singles[i] !== '') { + indx = parseInt( singles[i], 10 ); if ( indx < c.columns ) { columns.push( indx ); } } - }); + } } // return all columns if (!columns.length) { @@ -1133,12 +1143,12 @@ ts.filter = { data.parsed = c.$headers.map(function(columnIndex) { return c.parsers && c.parsers[columnIndex] && c.parsers[columnIndex].parsed || // getData won't return "parsed" if other "filter-" class names exist (e.g. <th class="filter-select filter-parsed">) - ts.getData && ts.getData(c.$headers.filter('[data-column="' + columnIndex + '"]:last'), ts.getColumnData( table, c.headers, columnIndex ), 'filter') === 'parsed' || + ts.getData && ts.getData(c.$headerIndexed[columnIndex], ts.getColumnData( table, c.headers, columnIndex ), 'filter') === 'parsed' || $(this).hasClass('filter-parsed'); }).get(); if (c.debug) { - ts.log('Starting filter widget search', filters); + ts.log('Filter: Starting filter widget search', filters); time = new Date(); } // filtered rows count @@ -1157,7 +1167,7 @@ ts.filter = { $rows = $( $.map(norm_rows, function(el){ return el[columnIndex].$row.get(); }) ); if (combinedFilters === '' || wo.filter_serversideFiltering) { - $rows.removeClass(wo.filter_filteredRow).not('.' + c.cssChildRow).show(); + $rows.removeClass(wo.filter_filteredRow).not('.' + c.cssChildRow).css('display', ''); } else { // filter out child rows $rows = $rows.not('.' + c.cssChildRow); @@ -1209,14 +1219,14 @@ ts.filter = { // don't search only filtered if the value is negative ('> -10' => '> -100' will ignore hidden rows) !(/(>=?\s*-\d)/.test(val) || /(<=?\s*\d)/.test(val)) && // if filtering using a select without a "filter-match" class (exact match) - fixes #593 - !( val !== '' && c.$filters && c.$filters.eq(indx).find('select').length && !c.$headers.filter('[data-column="' + indx + '"]:last').hasClass('filter-match') ); + !( val !== '' && c.$filters && c.$filters.eq(indx).find('select').length && !c.$headerIndexed[indx].hasClass('filter-match') ); } } notFiltered = $rows.not('.' + wo.filter_filteredRow).length; // can't search when all rows are hidden - this happens when looking for exact matches if (searchFiltered && notFiltered === 0) { searchFiltered = false; } if (c.debug) { - ts.log( "Searching through " + ( searchFiltered && notFiltered < len ? notFiltered : "all" ) + " rows" ); + ts.log( 'Filter: Searching through ' + ( searchFiltered && notFiltered < len ? notFiltered : 'all' ) + ' rows' ); } if (data.anyMatchFlag) { if (c.sortLocaleCompare) { @@ -1339,7 +1349,7 @@ ts.filter = { // data.iFilter = case insensitive (if wo.filter_ignoreCase is true), data.filter = case sensitive data.iFilter = wo.filter_ignoreCase ? (data.filter || '').toLocaleLowerCase() : data.filter; fxn = ts.getColumnData( table, wo.filter_functions, columnIndex ); - $cell = c.$headers.filter('[data-column="' + columnIndex + '"]:last'); + $cell = c.$headerIndexed[columnIndex]; hasSelect = $cell.hasClass('filter-select'); if ( fxn || ( hasSelect && val ) ) { if (fxn === true || hasSelect) { @@ -1347,10 +1357,10 @@ ts.filter = { result = ($cell.hasClass('filter-match')) ? data.iExact.search(data.iFilter) >= 0 : data.filter === data.exact; } else if (typeof fxn === 'function') { // filter callback( exact cell content, parser normalized content, filter input value, column index, jQuery row object ) - result = fxn(data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex)); + result = fxn(data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); } else if (typeof fxn[ffxn || data.filter] === 'function') { // selector option function - result = fxn[ffxn || data.filter](data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex)); + result = fxn[ffxn || data.filter](data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); } } else { filterMatched = null; @@ -1377,8 +1387,8 @@ ts.filter = { } } $rows.eq(rowIndex) - .toggle(showRow) - .toggleClass(wo.filter_filteredRow, !showRow); + .toggleClass(wo.filter_filteredRow, !showRow)[0] + .display = showRow ? '' : 'none'; if (childRow.length) { childRow.toggleClass(wo.filter_filteredRow, !showRow); } @@ -1404,7 +1414,7 @@ ts.filter = { }, getOptionSource: function(table, column, onlyAvail) { table = $(table)[0]; - var cts, + var cts, indx, len, c = table.config, wo = c.widgetOptions, parsed = [], @@ -1442,16 +1452,17 @@ ts.filter = { return $.inArray(value, arry) === indx; }); - if (c.$headers.filter('[data-column="' + column + '"]:last').hasClass('filter-select-nosort')) { + if (c.$headerIndexed[column].hasClass('filter-select-nosort')) { // unsorted select options return arry; } else { + len = arry.length; // parse select option values - $.each(arry, function(i, v){ + for (indx = 0; indx < len; indx++) { // parse array data using set column parser; this DOES NOT pass the original // table cell to the parser format function - parsed.push({ t : v, p : c.parsers && c.parsers[column].format( v, table, [], column ) }); - }); + parsed.push({ t : arry[indx], p : c.parsers && c.parsers[column].format( arry[indx], table, [], column ) }); + } // sort parsed select options cts = c.textSorter || ''; @@ -1473,9 +1484,10 @@ ts.filter = { }); // rebuild arry from sorted parsed data arry = []; - $.each(parsed, function(i, v){ - arry.push(v.t); - }); + len = parsed.length; + for (indx = 0; indx < len; indx++) { + arry.push( parsed[indx].t ); + } return arry; } }, @@ -1495,7 +1507,7 @@ ts.filter = { // check if has class filtered if (onlyAvail && row.className.match(wo.filter_filteredRow)) { continue; } // get non-normalized cell content - if (wo.filter_useParsedData || c.parsers[column].parsed || c.$headers.filter('[data-column="' + column + '"]:last').hasClass('filter-parsed')) { + if (wo.filter_useParsedData || c.parsers[column].parsed || c.$headerIndexed[column].hasClass('filter-parsed')) { arry.push( '' + cache.normalized[rowIndex][column] ); } else { cell = row.cells[column]; @@ -1514,7 +1526,7 @@ ts.filter = { var indx, val, txt, t, $filters, $filter, c = table.config, wo = c.widgetOptions, - node = c.$headers.filter('[data-column="' + column + '"]:last'), + node = c.$headerIndexed[column], // t.data('placeholder') won't work in jQuery older than 1.4.3 options = '<option value="">' + ( node.data('placeholder') || node.attr('data-placeholder') || wo.filter_placeholder.select || '' ) + '</option>', // Get curent filter value @@ -1569,7 +1581,7 @@ ts.filter = { columns = c.columns; // build default select dropdown for (columnIndex = 0; columnIndex < columns; columnIndex++) { - $header = c.$headers.filter('[data-column="' + columnIndex + '"]:last'); + $header = c.$headerIndexed[columnIndex]; noSelect = !($header.hasClass('filter-false') || $header.hasClass('parser-false')); // look for the filter-select class; build/update it if found if (($header.hasClass('filter-select') || ts.getColumnData( table, wo.filter_functions, columnIndex ) === true) && noSelect) { @@ -1655,7 +1667,10 @@ ts.setFilters = function(table, filter, apply, skipFirst) { })(jQuery); -/*! Widget: stickyHeaders */ +/*! Widget: stickyHeaders - updated 3/5/2015 (v2.21.0) *//* + * Requires tablesorter v2.8+ and jQuery 1.4.3+ + * by Rob Garrison + */ ;(function ($, window) { 'use strict'; var ts = $.tablesorter = $.tablesorter || {}; @@ -1663,6 +1678,7 @@ var ts = $.tablesorter = $.tablesorter || {}; $.extend(ts.css, { sticky : 'tablesorter-stickyHeader', // stickyHeader stickyVis : 'tablesorter-sticky-visible', + stickyHide: 'tablesorter-sticky-hidden', stickyWrap: 'tablesorter-sticky-wrapper' }); @@ -1731,6 +1747,7 @@ ts.addWidget({ return; } var $table = c.$table, + // add position: relative to attach element, hopefully it won't cause trouble. $attach = $(wo.stickyHeaders_attachTo), namespace = c.namespace + 'stickyheaders ', // element to watch for the scroll event @@ -1740,8 +1757,7 @@ ts.addWidget({ $header = $thead.children('tr').not('.sticky-false').children(), $tfoot = $table.children('tfoot'), $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '', - stickyOffset = $attach.length ? 0 : $stickyOffset.length ? - $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, + stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, // is this table nested? If so, find parent sticky header wrapper (div, not table) $nestedSticky = $table.parent().closest('.' + ts.css.table).hasClass('hasStickyHeaders') ? $table.parent().closest('table.tablesorter')[0].config.widgetOptions.$sticky.parent() : [], @@ -1750,14 +1766,16 @@ ts.addWidget({ $stickyTable = wo.$sticky = $table.clone() .addClass('containsStickyHeaders ' + ts.css.sticky + ' ' + wo.stickyHeaders) .wrap('<div class="' + ts.css.stickyWrap + '">'), - $stickyWrap = $stickyTable.parent().css({ - position : $attach.length ? 'absolute' : 'fixed', - padding : parseInt( $stickyTable.parent().parent().css('padding-left'), 10 ), - top : stickyOffset + nestedStickyTop, - left : 0, - visibility : 'hidden', - zIndex : wo.stickyHeaders_zIndex || 2 - }), + $stickyWrap = $stickyTable.parent() + .addClass(ts.css.stickyHide) + .css({ + position : $attach.length ? 'absolute' : 'fixed', + padding : parseInt( $stickyTable.parent().parent().css('padding-left'), 10 ), + top : stickyOffset + nestedStickyTop, + left : 0, + visibility : 'hidden', + zIndex : wo.stickyHeaders_zIndex || 2 + }), $stickyThead = $stickyTable.children('thead:first'), $stickyCells, laststate = '', @@ -1800,6 +1818,10 @@ ts.addWidget({ setWidth( $table, $stickyTable ); setWidth( $header, $stickyCells ); }; + // only add a position relative if a position isn't already defined + if ($attach.length && !$attach.css('position')) { + $attach.css('position', 'relative'); + } // save stickyTable element to config // it is also saved to wo.$sticky if (c.$extraTables && c.$extraTables.length) { @@ -1846,8 +1868,7 @@ ts.addWidget({ if (!$table.is(':visible')) { return; } // fixes #278 // Detect nested tables - fixes #724 nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; - var prefix = 'tablesorter-sticky-', - offset = $table.offset(), + var offset = $table.offset(), yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 xWindow = $.isWindow( $xScroll[0] ), // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, @@ -1857,7 +1878,7 @@ ts.addWidget({ cssSettings = { visibility : isVisible }; if ($attach.length) { - cssSettings.top = yWindow ? scrollTop : $attach.scrollTop(); + cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); } if (xWindow) { // adjust when scrolling horizontally - fixes issue #143 @@ -1867,8 +1888,8 @@ ts.addWidget({ cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; } $stickyWrap - .removeClass(prefix + 'visible ' + prefix + 'hidden') - .addClass(prefix + isVisible) + .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) + .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) .css(cssSettings); if (isVisible !== laststate || event.type === 'resize') { // make sure the column widths match diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-range.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-range.js new file mode 100644 index 0000000..9947f8b --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-range.js @@ -0,0 +1,93 @@ +/*! + * Range date parsers + * 2/23/2015 (v2.21.0) + */ +/* Include the widget-filter-type-insideRange.js to filter ranges */ +/*jshint jquery:true */ +;(function($){ +'use strict'; + + var regex = { + mdy : /(\d{1,2}[-\s]\d{1,2}[-\s]\d{4}(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?)/gi, + + dmy : /(\d{1,2}[-\s]\d{1,2}[-\s]\d{4}(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?)/gi, + dmyreplace : /(\d{1,2})[-\s](\d{1,2})[-\s](\d{4})/, + + ymd : /(\d{4}[-\s]\d{1,2}[-\s]\d{1,2}(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?)/gi, + ymdreplace : /(\d{4})[-\s](\d{1,2})[-\s](\d{1,2})/ + }; + + /*! date-range MMDDYYYY *//* (2/15/2000 - 5/18/2000) */ + $.tablesorter.addParser({ + id: 'date-range-mdy', + is: function () { + return false; + }, + format: function (text) { + var date, str, i, len, + parsed = []; + str = text.replace( /\s+/g, ' ' ).replace( /[\/\-.,]/g, '-' ).match( regex.mdy ); + len = str && str.length; + // work on dates, even if there is no range + if ( len ) { + for (i = 0; i < len; i++) { + date = new Date( str[i] ); + parsed.push( date instanceof Date && isFinite(date) ? date.getTime() : str[i] ); + } + // sort from min to max + return parsed.sort().join( ' - ' ); + } + return text; + }, + type: 'text' + }); + + /*! date-range DDMMYYYY *//* (15/2/2000 - 18/5/2000) */ + $.tablesorter.addParser({ + id: 'date-range-dmy', + is: function () { + return false; + }, + format: function (text) { + var date, str, i, len, + parsed = []; + str = text.replace( /\s+/g, ' ' ).replace( /[\/\-.,]/g, '-' ).match( regex.dmy ); + len = str && str.length; + if ( len ) { + for (i = 0; i < len; i++) { + date = new Date( ( '' + str[i] ).replace( regex.dmyreplace, '$2/$1/$3' ) ); + parsed.push( date instanceof Date && isFinite(date) ? date.getTime() : str[i] ); + } + // sort from min to max + return parsed.sort().join( ' - ' ); + } + return text; + }, + type: 'text' + }); + + /*! date-range DDMMYYYY *//* (2000/2/15 - 2000/5/18) */ + $.tablesorter.addParser({ + id: 'date-range-ymd', + is: function () { + return false; + }, + format: function (text) { + var date, str, i, len, + parsed = []; + str = text.replace( /\s+/g, ' ' ).replace( /[\/\-.,]/g, '-' ).match( regex.ymd ); + len = str && str.length; + if ( len ) { + for (i = 0; i < len; i++) { + date = new Date( ( '' + str[i] ).replace( regex.ymdreplace, '$2/$3/$1' ) ); + parsed.push( date instanceof Date && isFinite(date) ? date.getTime() : str[i] ); + } + // sort from min to max + return parsed.sort().join( ' - ' ); + } + return text; + }, + type: 'text' + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index d684cbb..a83c9a4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,5 +1,5 @@ /*! input & select parsers for jQuery 1.7+ & tablesorter 2.7.11+ - * Updated 2/7/2015 (v2.19.0) + * Updated 3/5/2015 (v2.21.0) * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ /*jshint browser: true, jquery:true, unused:false */ @@ -36,14 +36,16 @@ }, format: function(s, table, cell, cellIndex) { var $c = $(cell), + wo = table.config.widgetOptions, + // returning plain language here because this is what is shown in the + // group headers - change it as desired + txt = wo.group_checkbox ? wo.group_checkbox : [ 'checked', 'unchecked' ], $input = $c.find('input[type="checkbox"]'), isChecked = $input.length ? $input[0].checked : ''; // adding class to row, indicating that a checkbox is checked; includes // a column index in case more than one checkbox happens to be in a row $c.closest('tr').toggleClass('checked-' + cellIndex, isChecked); - // returning plain language here because this is what is shown in the - // group headers - change it as desired - return $input.length ? isChecked ? 'checked' : 'unchecked' : s; + return $input.length ? txt[ isChecked ? 0 : 1 ] : s; }, parsed : true, // filter widget flag type: "text" diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js index db1f85a..19c5c20 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js @@ -47,7 +47,7 @@ b, t, // process number here to get a numerical format (us or eu) n = $.tablesorter.formatFloat(s.replace(/[^\w,. \-()]/g, ""), table), - $t = table.config.$headers.filter('[data-column="' + cellIndex + '"]'), + $t = table.config.$headerIndexed[cellIndex], m = $t.data('metric'); if (!m) { // stored values diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index dfa8018..86898e6 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Column Selector/Responsive table widget for TableSorter - 2/7/2015 (v2.19.0) +/* Column Selector/Responsive table widget for TableSorter - 3/5/2015 (v2.21.0) * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -21,7 +21,7 @@ tsColSel = ts.columnSelector = { $t = $(wo.columnSelector_layout); if (!$t.find('input').add( $t.filter('input') ).length) { if (c.debug) { - ts.log('*** ERROR: Column Selector aborting, no input found in the layout! ***'); + ts.log('ColumnSelector: >> ERROR: Column Selector aborting, no input found in the layout! ***'); } return; } @@ -45,6 +45,8 @@ tsColSel = ts.columnSelector = { colSel.isInitializing = false; if (colSel.$container.length) { tsColSel.updateCols(c, wo); + } else if (c.debug) { + ts.log('ColumnSelector: >> container not found'); } c.$table diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js index d72a969..5550a63 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js @@ -16,7 +16,7 @@ var ts = $.tablesorter || {}, compareSelect = '.compare-select', -tsff = ts.filterFormatter = { +tsff = ts.filterFormatter = $.extend( {}, ts.filterFormatter, { addCompare: function($cell, indx, options){ if (options.compare && $.isArray(options.compare) && options.compare.length > 1) { @@ -424,6 +424,6 @@ tsff = ts.filterFormatter = { return colorSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">'); } -}; +}); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js index 4c30caa..2539f4c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js @@ -17,8 +17,7 @@ var ts = $.tablesorter || {}, // compare option selector class name (jQuery selector) compareSelect = '.compare-select', - -tsff = ts.filterFormatter = { +tsff = ts.filterFormatter = $.extend( {}, ts.filterFormatter, { addCompare: function($cell, indx, options){ if (options.compare && $.isArray(options.compare) && options.compare.length > 1) { @@ -760,6 +759,6 @@ tsff = ts.filterFormatter = { return $input.val( o.from ? ( o.to ? o.from + ' - ' + o.to : '>=' + o.from ) : (o.to ? '<=' + o.to : '') ); } -}; +}); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js index 9b1ad25..8556599 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js @@ -36,7 +36,7 @@ ts.filterFormatter.select2 = function($cell, indx, select2Def) { $cell.find('.select2').select2('val', val); updateSelect2(); }), - $header = c.$headers.filter('[data-column="' + indx + '"]:last'), + $header = c.$headerIndexed[indx], onlyAvail = $header.hasClass(wo.filter_onlyAvail), $shcell = [], matchPrefix = o.match ? '' : '^', diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js new file mode 100644 index 0000000..7dddb23 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js @@ -0,0 +1,42 @@ +/*! + * insideRange filter type + * 2/23/2015 (v2.21.0) + */ +;(function($){ +'use strict'; + + // Add insideRange filter type + // ============================ + // This allows you to enter a number (e.g. 8) and show the + // resulting rows that will have that query within it's range + // demo at http://mottie.github.io/tablesorter/docs/example-widget-filter-custom-search2.html + var ts = $.tablesorter, + isDigit = /\d+/, + range = /\s+-\s+/, + parseNumber = function(num) { + return isNaN(num) ? num : parseFloat(num); + }; + + ts.filter.types.insideRange = function( c, data ) { + if ( isDigit.test( data.iFilter ) && range.test( data.iExact ) ) { + var t, val, low, high, + parts = data.iExact.split( range ), + format = c.parsers[data.index].format; + // the cell does not contain a range + if ( parts && parts.length < 2 ) { + return null; + } + // format each side part of the range using the assigned parser + low = parseNumber( format( parts[0], c.table ) ); + high = parseNumber( format( parts[1], c.table ) ); + val = parseNumber( format( data.iFilter, c.table ) ); + if ( high < low ) { + // swap high & low + t = high; high = low; low = t; + } + return low <= val && val <= high; + } + return null; + }; + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 1b0b163..1cc84fc 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,7 @@ -/*! Widget: filter */ +/*! Widget: filter - updated 3/5/2015 (v2.21.0) *//* + * Requires tablesorter v2.8+ and jQuery 1.7+ + * by Rob Garrison + */ ;(function ($) { 'use strict'; var ts = $.tablesorter = $.tablesorter || {}; @@ -209,7 +212,7 @@ ts.filter = { parsed = data.parsed[index], query = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.orReplace, "|"), index, parsed); // look for an exact match with the "or" unless the "filter-match" class is found - if (!c.$headers.filter('[data-column="' + index + '"]:last').hasClass('filter-match') && /\|/.test(query)) { + if (!c.$headerIndexed[index].hasClass('filter-match') && /\|/.test(query)) { // show all results while using filter match. Fixes #727 if (query[ query.length - 1 ] === '|') { query += '*'; } query = data.anyMatch && $.isArray(data.rowArray) ? '(' + query + ')' : '^(' + query + ')$'; @@ -330,7 +333,7 @@ ts.filter = { fxn = ts.getColumnData( table, wo.filter_functions, column ); if (fxn) { // remove "filter-select" from header otherwise the options added here are replaced with all options - $header = c.$headers.filter('[data-column="' + column + '"]:last').removeClass('filter-select'); + $header = c.$headerIndexed[column].removeClass('filter-select'); // don't build select if "filter-false" or "parser-false" set noSelect = !($header.hasClass('filter-false') || $header.hasClass('parser-false')); options = ''; @@ -426,7 +429,8 @@ ts.filter = { } }, filterInitComplete: function(c){ - var wo = c.widgetOptions, + var indx, len, + wo = c.widgetOptions, count = 0, completed = function(){ wo.filter_initialized = true; @@ -436,11 +440,12 @@ ts.filter = { if ( $.isEmptyObject( wo.filter_formatter ) ) { completed(); } else { - $.each( wo.filter_formatterInit, function(i, val) { - if (val === 1) { + len = wo.filter_formatterInit.length; + for (indx = 0; indx < len; indx++) { + if (wo.filter_formatterInit[indx] === 1) { count++; } - }); + } clearTimeout(wo.filter_initTimer); if (!wo.filter_initialized && count === wo.filter_formatterCount) { // filter widget initialized @@ -456,7 +461,7 @@ ts.filter = { }, setDefaults: function(table, c, wo) { - var isArray, saved, indx, + var isArray, saved, indx, col, $filters, // get current (default) filters filters = ts.getFilters(table) || []; if (wo.filter_saveFilters && ts.storage) { @@ -467,8 +472,12 @@ ts.filter = { } // if no filters saved, then check default settings if (filters.join('') === '') { - for (indx = 0; indx < c.columns; indx++) { - filters[indx] = c.$headers.filter('[data-column="' + indx + '"]:last').attr(wo.filter_defaultAttrib) || filters[indx]; + // allow adding default setting to external filters + $filters = c.$headers.add( wo.filter_$externalFilters ).filter('[' + wo.filter_defaultAttrib + ']'); + for (indx = 0; indx <= c.columns; indx++) { + // include data-column="all" external filters + col = indx === c.columns ? 'all' : indx; + filters[indx] = $filters.filter('[data-column="' + col + '"]').attr(wo.filter_defaultAttrib) || filters[indx] || ''; } } c.$table.data('lastSearch', filters); @@ -497,7 +506,7 @@ ts.filter = { for (column = 0; column < columns; column++) { disabled = false; // assuming last cell of a column is the main column - $header = c.$headers.filter('[data-column="' + column + '"]:last'); + $header = c.$headerIndexed[column]; ffxn = ts.getColumnData( table, wo.filter_functions, column ); buildSelect = (wo.filter_functions && ffxn && typeof ffxn !== "function" ) || $header.hasClass('filter-select'); @@ -722,7 +731,7 @@ ts.filter = { }, multipleColumns: function( c, $input ) { // look for multiple columns "1-3,4-6,8" in data-column - var ranges, singles, indx, + var temp, ranges, range, start, end, singles, i, indx, len, wo = c.widgetOptions, // only target "all" column inputs on initialization // & don't target "all" column inputs if they don't exist @@ -732,31 +741,32 @@ ts.filter = { // process column range if ( targets && /-/.test( val ) ) { ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); - $.each(ranges, function(i,v){ - var t, - range = v.split( /\s*-\s*/ ), - start = parseInt( range[0], 10 ) || 0, - end = parseInt( range[1], 10 ) || ( c.columns - 1 ); - if ( start > end ) { t = start; start = end; end = t; } // swap + len = ranges.length; + for (indx = 0; indx < len; indx++) { + range = ranges[indx].split( /\s*-\s*/ ); + start = parseInt( range[0], 10 ) || 0; + end = parseInt( range[1], 10 ) || ( c.columns - 1 ); + if ( start > end ) { temp = start; start = end; end = temp; } // swap if ( end >= c.columns ) { end = c.columns - 1; } for ( ; start <= end; start++ ) { columns.push(start); } // remove processed range from val - val = val.replace( v, '' ); - }); + val = val.replace( ranges[indx], '' ); + } } // process single columns if ( targets && /,/.test( val ) ) { singles = val.split( /\s*,\s*/ ); - $.each( singles, function(i,v) { - if (v !== '') { - indx = parseInt( v, 10 ); + len = singles.length; + for (i = 0; i < len; i++) { + if (singles[i] !== '') { + indx = parseInt( singles[i], 10 ); if ( indx < c.columns ) { columns.push( indx ); } } - }); + } } // return all columns if (!columns.length) { @@ -784,12 +794,12 @@ ts.filter = { data.parsed = c.$headers.map(function(columnIndex) { return c.parsers && c.parsers[columnIndex] && c.parsers[columnIndex].parsed || // getData won't return "parsed" if other "filter-" class names exist (e.g. <th class="filter-select filter-parsed">) - ts.getData && ts.getData(c.$headers.filter('[data-column="' + columnIndex + '"]:last'), ts.getColumnData( table, c.headers, columnIndex ), 'filter') === 'parsed' || + ts.getData && ts.getData(c.$headerIndexed[columnIndex], ts.getColumnData( table, c.headers, columnIndex ), 'filter') === 'parsed' || $(this).hasClass('filter-parsed'); }).get(); if (c.debug) { - ts.log('Starting filter widget search', filters); + ts.log('Filter: Starting filter widget search', filters); time = new Date(); } // filtered rows count @@ -808,7 +818,7 @@ ts.filter = { $rows = $( $.map(norm_rows, function(el){ return el[columnIndex].$row.get(); }) ); if (combinedFilters === '' || wo.filter_serversideFiltering) { - $rows.removeClass(wo.filter_filteredRow).not('.' + c.cssChildRow).show(); + $rows.removeClass(wo.filter_filteredRow).not('.' + c.cssChildRow).css('display', ''); } else { // filter out child rows $rows = $rows.not('.' + c.cssChildRow); @@ -860,14 +870,14 @@ ts.filter = { // don't search only filtered if the value is negative ('> -10' => '> -100' will ignore hidden rows) !(/(>=?\s*-\d)/.test(val) || /(<=?\s*\d)/.test(val)) && // if filtering using a select without a "filter-match" class (exact match) - fixes #593 - !( val !== '' && c.$filters && c.$filters.eq(indx).find('select').length && !c.$headers.filter('[data-column="' + indx + '"]:last').hasClass('filter-match') ); + !( val !== '' && c.$filters && c.$filters.eq(indx).find('select').length && !c.$headerIndexed[indx].hasClass('filter-match') ); } } notFiltered = $rows.not('.' + wo.filter_filteredRow).length; // can't search when all rows are hidden - this happens when looking for exact matches if (searchFiltered && notFiltered === 0) { searchFiltered = false; } if (c.debug) { - ts.log( "Searching through " + ( searchFiltered && notFiltered < len ? notFiltered : "all" ) + " rows" ); + ts.log( 'Filter: Searching through ' + ( searchFiltered && notFiltered < len ? notFiltered : 'all' ) + ' rows' ); } if (data.anyMatchFlag) { if (c.sortLocaleCompare) { @@ -990,7 +1000,7 @@ ts.filter = { // data.iFilter = case insensitive (if wo.filter_ignoreCase is true), data.filter = case sensitive data.iFilter = wo.filter_ignoreCase ? (data.filter || '').toLocaleLowerCase() : data.filter; fxn = ts.getColumnData( table, wo.filter_functions, columnIndex ); - $cell = c.$headers.filter('[data-column="' + columnIndex + '"]:last'); + $cell = c.$headerIndexed[columnIndex]; hasSelect = $cell.hasClass('filter-select'); if ( fxn || ( hasSelect && val ) ) { if (fxn === true || hasSelect) { @@ -998,10 +1008,10 @@ ts.filter = { result = ($cell.hasClass('filter-match')) ? data.iExact.search(data.iFilter) >= 0 : data.filter === data.exact; } else if (typeof fxn === 'function') { // filter callback( exact cell content, parser normalized content, filter input value, column index, jQuery row object ) - result = fxn(data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex)); + result = fxn(data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); } else if (typeof fxn[ffxn || data.filter] === 'function') { // selector option function - result = fxn[ffxn || data.filter](data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex)); + result = fxn[ffxn || data.filter](data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); } } else { filterMatched = null; @@ -1028,8 +1038,8 @@ ts.filter = { } } $rows.eq(rowIndex) - .toggle(showRow) - .toggleClass(wo.filter_filteredRow, !showRow); + .toggleClass(wo.filter_filteredRow, !showRow)[0] + .display = showRow ? '' : 'none'; if (childRow.length) { childRow.toggleClass(wo.filter_filteredRow, !showRow); } @@ -1055,7 +1065,7 @@ ts.filter = { }, getOptionSource: function(table, column, onlyAvail) { table = $(table)[0]; - var cts, + var cts, indx, len, c = table.config, wo = c.widgetOptions, parsed = [], @@ -1093,16 +1103,17 @@ ts.filter = { return $.inArray(value, arry) === indx; }); - if (c.$headers.filter('[data-column="' + column + '"]:last').hasClass('filter-select-nosort')) { + if (c.$headerIndexed[column].hasClass('filter-select-nosort')) { // unsorted select options return arry; } else { + len = arry.length; // parse select option values - $.each(arry, function(i, v){ + for (indx = 0; indx < len; indx++) { // parse array data using set column parser; this DOES NOT pass the original // table cell to the parser format function - parsed.push({ t : v, p : c.parsers && c.parsers[column].format( v, table, [], column ) }); - }); + parsed.push({ t : arry[indx], p : c.parsers && c.parsers[column].format( arry[indx], table, [], column ) }); + } // sort parsed select options cts = c.textSorter || ''; @@ -1124,9 +1135,10 @@ ts.filter = { }); // rebuild arry from sorted parsed data arry = []; - $.each(parsed, function(i, v){ - arry.push(v.t); - }); + len = parsed.length; + for (indx = 0; indx < len; indx++) { + arry.push( parsed[indx].t ); + } return arry; } }, @@ -1146,7 +1158,7 @@ ts.filter = { // check if has class filtered if (onlyAvail && row.className.match(wo.filter_filteredRow)) { continue; } // get non-normalized cell content - if (wo.filter_useParsedData || c.parsers[column].parsed || c.$headers.filter('[data-column="' + column + '"]:last').hasClass('filter-parsed')) { + if (wo.filter_useParsedData || c.parsers[column].parsed || c.$headerIndexed[column].hasClass('filter-parsed')) { arry.push( '' + cache.normalized[rowIndex][column] ); } else { cell = row.cells[column]; @@ -1165,7 +1177,7 @@ ts.filter = { var indx, val, txt, t, $filters, $filter, c = table.config, wo = c.widgetOptions, - node = c.$headers.filter('[data-column="' + column + '"]:last'), + node = c.$headerIndexed[column], // t.data('placeholder') won't work in jQuery older than 1.4.3 options = '<option value="">' + ( node.data('placeholder') || node.attr('data-placeholder') || wo.filter_placeholder.select || '' ) + '</option>', // Get curent filter value @@ -1220,7 +1232,7 @@ ts.filter = { columns = c.columns; // build default select dropdown for (columnIndex = 0; columnIndex < columns; columnIndex++) { - $header = c.$headers.filter('[data-column="' + columnIndex + '"]:last'); + $header = c.$headerIndexed[columnIndex]; noSelect = !($header.hasClass('filter-false') || $header.hasClass('parser-false')); // look for the filter-select class; build/update it if found if (($header.hasClass('filter-select') || ts.getColumnData( table, wo.filter_functions, columnIndex ) === true) && noSelect) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js index 8b8cd4f..3b735bc 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js @@ -29,7 +29,7 @@ $headers = []; // set up variables for ( column = 0; column < c.columns; column++ ) { - $headers[ column ] = c.$headers.filter('[data-column="' + column + '"]:last'); + $headers[ column ] = c.$headerIndexed[ column ]; formatter[ column ] = ts.getColumnData( c.table, wo.formatter_column, column ) || false; } // main loop diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index 546743d..8df9e9b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -1,4 +1,4 @@ -/*! tablesorter Grouping widget - updated 10/26/2014 (v2.18.0) +/*! tablesorter Grouping widget - updated 3/5/2015 (v2.21.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -66,12 +66,12 @@ ts.grouping = { // clear pager saved spacer height (in case the rows are collapsed) c.$table.data('pagerSavedHeight', 0); } - if (column >= 0 && !c.$headers.filter('[data-column="' + column + '"]:last').hasClass('group-false')) { + if (column >= 0 && !c.$headerIndexed[column].hasClass('group-false')) { wo.group_currentGroup = ''; // save current groups wo.group_currentGroups = {}; // group class finds "group-{word/separator/letter/number/date/false}-{optional:#/year/month/day/week/time}" - groupClass = (c.$headers.filter('[data-column="' + column + '"]:last').attr('class') || '').match(/(group-\w+(-\w+)?)/g); + groupClass = (c.$headerIndexed[column].attr('class') || '').match(/(group-\w+(-\w+)?)/g); // grouping = [ 'group', '{word/separator/letter/number/date/false}', '{#/year/month/day/week/time}' ] grouping = groupClass ? groupClass[0].split('-') : ['group','letter',1]; // default to letter 1 @@ -96,15 +96,15 @@ ts.grouping = { if ( $rows.eq(rowIndex).is(':visible') ) { // fixes #438 if (ts.grouping.types[grouping[1]]) { - currentGroup = norm_rows[rowIndex] ? - ts.grouping.types[grouping[1]]( c, c.$headers.filter('[data-column="' + column + '"]:last'), norm_rows[rowIndex][column], /date/.test(groupClass) ? + currentGroup = norm_rows[rowIndex] ? + ts.grouping.types[grouping[1]]( c, c.$headerIndexed[column], norm_rows[rowIndex][column], /date/.test(groupClass) ? grouping[2] : parseInt(grouping[2] || 1, 10) || 1, group, lang ) : currentGroup; if (group !== currentGroup) { group = currentGroup; // show range if number > 1 if (grouping[1] === 'number' && grouping[2] > 1 && currentGroup !== '') { currentGroup += ' - ' + (parseInt(currentGroup, 10) + - ((parseInt(grouping[2],10) - 1) * (c.$headers.filter('[data-column="' + column + '"]:last').hasClass(ts.css.sortAsc) ? 1 : -1))); + ((parseInt(grouping[2],10) - 1) * (c.$headerIndexed[column].hasClass(ts.css.sortAsc) ? 1 : -1))); } if ($.isFunction(wo.group_formatter)) { currentGroup = wo.group_formatter((currentGroup || '').toString(), column, table, c, wo) || currentGroup; @@ -139,7 +139,7 @@ ts.grouping = { } } } - if (wo.group_saveGroups && wo.group_currentGroups[wo.group_currentGroup].length) { + if (wo.group_saveGroups && wo.group_currentGroups.length && wo.group_currentGroups[wo.group_currentGroup].length) { name = $row.find('.group-name').text().toLowerCase(); isHidden = $.inArray( name, wo.group_currentGroups[wo.group_currentGroup] ) > -1; $row.toggleClass('collapsed', isHidden); @@ -219,6 +219,8 @@ ts.addWidget({ group_callback : null, // function($cell, $rows, column, table){}, callback allowing modification of the group header labels group_complete : 'groupingComplete', // event triggered on the table when the grouping widget has finished work + // checkbox parser text used for checked/unchecked values + group_checkbox : [ 'checked', 'unchecked' ], // change these default date names based on your language preferences group_months : [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ], group_week : [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ], diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index 26b8044..3181576 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -1,4 +1,4 @@ -/* Output widget for TableSorter 2/9/2015 (v2.19.1) +/* Output widget for TableSorter 3/5/2015 (v2.21.0) * Requires tablesorter v2.8+ and jQuery 1.7+ * Modified from: * HTML Table to CSV: http://www.kunalbabre.com/projects/table2CSV.php (License unknown?) @@ -120,6 +120,13 @@ output = ts.output = { // all tbody rows $rows = $el.children('tbody').children('tr'); + + if (wo.output_includeFooter) { + // clone, to force the tfoot rows to the end of this selection of rows + // otherwise they appear after the thead (the order in the HTML) + $rows = $rows.add( $el.children('tfoot').children('tr').clone() ); + } + // get (f)iltered, (v)isible or all rows (look for the first letter only) $rows = /f/.test(wo.output_saveRows) ? $rows.not('.' + (wo.filter_filteredRow || 'filtered') ) : /v/.test(wo.output_saveRows) ? $rows.filter(':visible') : $rows; @@ -287,6 +294,7 @@ ts.addWidget({ options: { output_separator : ',', // set to "json", "array" or any separator output_ignoreColumns : [], // columns to ignore [0, 1,... ] (zero-based index) + output_includeFooter : false, // include footer rows in the output output_dataAttrib : 'data-name', // header attrib containing modified header name output_headerRows : false, // if true, include multiple header rows (JSON only) output_delivery : 'popup', // popup, download diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 6c4f968..aeb7f46 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/* Pager widget for TableSorter 2/9/2015 (v2.19.1) - requires jQuery 1.7+ */ +/* Pager widget for TableSorter 3/5/2015 (v2.21.0) - requires jQuery 1.7+ */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ "use strict"; @@ -151,7 +151,7 @@ tsp = ts.pager = { p.isInitializing = true; if (c.debug) { - ts.log('Pager initializing'); + ts.log('Pager: Initializing'); } p.size = $.data(table, 'pagerLastSize') || wo.pager_size; @@ -196,7 +196,7 @@ tsp = ts.pager = { } else { p.ajax = false; // Regular pager; all rows stored in memory - c.$table.trigger("appendCache", [{}, true]); + c.$table.trigger('appendCache', [{}, true]); } }, @@ -214,6 +214,9 @@ tsp = ts.pager = { p.initialized = true; p.initializing = false; p.isInitializing = false; + if (c.debug) { + ts.log('Pager: Triggering pagerInitialized'); + } c.$table.trigger('pagerInitialized', c); // filter widget not initialized; it will update the output display & fire off the pagerComplete event if ( !( c.widgetOptions.filter_initialized && ts.hasWidget(table, 'filter') ) ) { @@ -310,6 +313,9 @@ tsp = ts.pager = { // clicked controls ctrls = [ s.first, s.prev, s.next, s.last ]; fxn = [ 'moveToFirstPage', 'moveToPrevPage', 'moveToNextPage', 'moveToLastPage' ]; + if (c.debug && !p.$container.length) { + ts.log('Pager: >> Container not found'); + } p.$container.find(ctrls.join(',')) .attr("tabindex", 0) .off('click.pager') @@ -336,6 +342,8 @@ tsp = ts.pager = { tsp.moveToPage(table, p, true); tsp.updatePageDisplay(table, c, false); }); + } else if (c.debug) { + ts.log('Pager: >> Goto selector not found'); } if ( p.$size.length ) { @@ -351,6 +359,8 @@ tsp = ts.pager = { } return false; }); + } else if (c.debug) { + ts.log('Pager: >> Size selector not found'); } }, @@ -371,7 +381,8 @@ tsp = ts.pager = { }, calcFilters: function(table, c) { - var wo = c.widgetOptions, + var normalized, indx, len, + wo = c.widgetOptions, p = c.pager, hasFilters = c.$table.hasClass('hasFilters'); if (hasFilters && !wo.pager_ajaxUrl) { @@ -380,9 +391,11 @@ tsp = ts.pager = { p.filteredRows = p.totalRows = c.$tbodies.eq(0).children('tr').not( wo.pager_countChildRows ? '' : '.' + c.cssChildRow ).length; } else { p.filteredRows = 0; - $.each(c.cache[0].normalized, function(i, el) { - p.filteredRows += p.regexRows.test(el[c.columns].$row[0].className) ? 0 : 1; - }); + normalized = c.cache[0].normalized; + len = normalized.length; + for (indx = 0; indx < len; indx++) { + p.filteredRows += p.regexRows.test(normalized[indx][c.columns].$row[0].className) ? 0 : 1; + } } } else if (!hasFilters) { p.filteredRows = p.totalRows; @@ -391,7 +404,7 @@ tsp = ts.pager = { updatePageDisplay: function(table, c, completed) { if ( c.pager.initializing ) { return; } - var s, t, $out, + var s, t, $out, options, indx, len, wo = c.widgetOptions, p = c.pager, sz = p.size || p.setSize || 10; // don't allow dividing by zero @@ -431,9 +444,11 @@ tsp = ts.pager = { }); if ( p.$goto.length ) { t = ''; - $.each(tsp.buildPageSelect(p, c), function(i, opt){ - t += '<option value="' + opt + '">' + opt + '</option>'; - }); + options = tsp.buildPageSelect(p, c); + len = options.length; + for (indx = 0; indx < len; indx++) { + t += '<option value="' + options[indx] + '">' + options[indx] + '</option>'; + } // innerHTML doesn't work in IE9 - http://support2.microsoft.com/kb/276228 p.$goto.html(t).val( p.page + 1 ); } @@ -450,6 +465,9 @@ tsp = ts.pager = { tsp.pagerArrows(c); tsp.fixHeight(table, c); if (p.initialized && completed !== false) { + if (c.debug) { + ts.log('Pager: Triggering pagerComplete'); + } c.$table.trigger('pagerComplete', c); // save pager info to storage if (wo.pager_savePages && ts.storage) { @@ -624,7 +642,7 @@ tsp = ts.pager = { if ( exception ) { if (c.debug) { - ts.log('Ajax Error', xhr, exception); + ts.log('Pager: >> Ajax Error', xhr, exception); } ts.showError(table, exception.message + ' (' + xhr.status + ')'); c.$tbodies.eq(0).children('tr').detach(); @@ -714,6 +732,9 @@ tsp = ts.pager = { // apply widgets after table has rendered & after a delay to prevent // multiple applyWidget blocking code from blocking this trigger setTimeout(function(){ + if (c.debug) { + ts.log('Pager: Triggering pagerChange'); + } $t .trigger('applyWidgets') .trigger('pagerChange', p); @@ -755,49 +776,52 @@ tsp = ts.pager = { } }; if (c.debug) { - ts.log('ajax initialized', p.ajaxObject); + ts.log('Pager: Ajax initialized', p.ajaxObject); } $.ajax(p.ajaxObject); } }, getAjaxUrl: function(table, c) { - var p = c.pager, + var indx, len, + p = c.pager, wo = c.widgetOptions, url = (wo.pager_ajaxUrl) ? wo.pager_ajaxUrl // allow using "{page+1}" in the url string to switch to a non-zero based index .replace(/\{page([\-+]\d+)?\}/, function(s,n){ return p.page + (n ? parseInt(n, 10) : 0); }) .replace(/\{size\}/g, p.size) : '', - sl = c.sortList, - fl = p.currentFilters || $(table).data('lastSearch') || [], + sortList = c.sortList, + filterList = p.currentFilters || $(table).data('lastSearch') || [], sortCol = url.match(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/), filterCol = url.match(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/), arry = []; if (sortCol) { sortCol = sortCol[1]; - $.each(sl, function(i,v){ - arry.push(sortCol + '[' + v[0] + ']=' + v[1]); - }); + len = sortList.length; + for (indx = 0; indx < len; indx++) { + arry.push(sortCol + '[' + sortList[indx][0] + ']=' + sortList[indx][1]); + } // if the arry is empty, just add the col parameter... "&{sortList:col}" becomes "&col" url = url.replace(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : sortCol ); arry = []; } if (filterCol) { filterCol = filterCol[1]; - $.each(fl, function(i,v){ - if (v) { - arry.push(filterCol + '[' + i + ']=' + encodeURIComponent(v)); + len = filterList.length; + for (indx = 0; indx < len; indx++) { + if (filterList[indx]) { + arry.push(filterCol + '[' + indx + ']=' + encodeURIComponent(filterList[indx])); } - }); + } // if the arry is empty, just add the fcol parameter... "&{filterList:fcol}" becomes "&fcol" url = url.replace(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : filterCol ); - p.currentFilters = fl; + p.currentFilters = filterList; } if ( $.isFunction(wo.pager_customAjaxUrl) ) { url = wo.pager_customAjaxUrl(table, url); } if (c.debug) { - ts.log('Pager ajax url: ' + url); + ts.log('Pager: Ajax url = ' + url); } return url; }, @@ -813,7 +837,7 @@ tsp = ts.pager = { e = p.size; if ( l < 1 ) { if (c.debug) { - ts.log('Pager: no rows for pager to render'); + ts.log('Pager: >> No rows for pager to render'); } // empty table, abort! return; @@ -824,7 +848,12 @@ tsp = ts.pager = { } p.cacheIndex = []; p.isDisabled = false; // needed because sorting will change the page and re-enable the pager - if (p.initialized) { c.$table.trigger('pagerChange', c); } + if (p.initialized) { + if (c.debug) { + ts.log('Pager: Triggering pagerChange'); + } + c.$table.trigger('pagerChange', c); + } if ( !wo.pager_removeRows ) { tsp.hideRows(table, c); } else { @@ -853,6 +882,9 @@ tsp = ts.pager = { wo.pager_startPage = p.page; wo.pager_size = p.size; if (table.isUpdating) { + if (c.debug) { + ts.log('Pager: Triggering updateComplete'); + } c.$table.trigger('updateComplete', [ table, true ]); } @@ -877,7 +909,7 @@ tsp = ts.pager = { tsp.renderTable(table, c.rowsCopy); c.$table.trigger('applyWidgets'); if (c.debug) { - ts.log('pager disabled'); + ts.log('Pager: Disabled'); } } // disable size selector @@ -940,7 +972,7 @@ tsp = ts.pager = { return; } if (c.debug) { - ts.log('Pager changing to page ' + p.page); + ts.log('Pager: Changing to page ' + p.page); } p.last = { page : p.page, @@ -959,10 +991,16 @@ tsp = ts.pager = { } $.data(table, 'pagerLastPage', p.page); if (p.initialized && pageMoved !== false) { + if (c.debug) { + ts.log('Pager: Triggering pageMoved'); + } c.$table .trigger('pageMoved', c) .trigger('applyWidgets'); if (!p.ajax && table.isUpdating) { + if (c.debug) { + ts.log('Pager: Triggering updateComplete'); + } c.$table.trigger('updateComplete', [ table, true ]); } } @@ -1039,7 +1077,7 @@ tsp = ts.pager = { tsp.setPageSize(table, p.size, c); tsp.hideRowsSetup(table, c); if (c.debug) { - ts.log('pager enabled'); + ts.log('Pager: Enabled'); } } }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index 5a11d14..bcccd55 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -10,7 +10,7 @@ Resizable scroller widget for the jQuery tablesorter plugin - Version 2.0 - modified by Rob Garrison 4/12/2013; updated 2/7/2015 (v2.19.0) + Version 2.0 - modified by Rob Garrison 4/12/2013; updated 3/5/2015 (v2.21.0) Requires jQuery v1.7+ Requires the tablesorter plugin, v2.8+, available at http://mottie.github.com/tablesorter/docs/ @@ -57,10 +57,11 @@ $(function(){ '.tablesorter-scroller { text-align: left; overflow: hidden; }' + '.tablesorter-scroller-header { overflow: hidden; }' + '.tablesorter-scroller-header table.tablesorter { margin-bottom: 0; }' + + '.tablesorter-scroller-footer table.tablesorter thead { visibility: hidden, height: 0; overflow: hidden; }' + '.tablesorter-scroller-table { overflow-y: scroll; }' + - '.tablesorter-scroller-table table.tablesorter { margin-top: 0; overflow: scroll; } ' + - '.tablesorter-scroller-table .tablesorter-filter-row, .tablesorter-scroller-table tfoot { display: none; }' + - '.tablesorter-scroller-table table.tablesorter thead tr.tablesorter-headerRow * {' + + '.tablesorter-scroller-table table.tablesorter { margin-top: 0; margin-bottom: 0; overflow: scroll; } ' + + '.tablesorter-scroller-table .tablesorter-filter-row,.tablesorter-scroller-footer .tablesorter-filter-row,.tablesorter-scroller-table tfoot { display: none; }' + + '.tablesorter-scroller-table table.tablesorter thead tr.tablesorter-headerRow *,.tablesorter-scroller-footer table.tablesorter thead * {' + 'line-height:0;height:0;border:none;background-image:none;padding-top:0;padding-bottom:0;margin-top:0;margin-bottom:0;overflow:hidden;' + '}</style>'; $(s).appendTo('body'); @@ -95,24 +96,37 @@ ts.addWidget({ }); }, format: function(table, c, wo) { - var maxHt, tbHt, $hdr, resize, getBarWidth, $cells, + var maxHt, tbHt, $hdr, $t, resize, getBarWidth, $hCells, $fCells, + $ft = [], // c.namespace contains a unique tablesorter ID, per table id = c.namespace.slice(1) + 'tsscroller', $win = $(window), $tbl = c.$table; if (!c.isScrolling) { + // force developer to set fixedWidth to maintain column widths + c.widthFixed = true; maxHt = wo.scroller_height || 300; tbHt = $tbl.children('tbody').height(); if (tbHt !== 0 && maxHt > tbHt) { maxHt = tbHt + 10; } // Table is less than h px $hdr = $('<table class="' + $tbl.attr('class') + '" cellpadding=0 cellspacing=0>' + - '<thead>' + $tbl.find('thead:first').html() + '</thead>' + + $tbl.children('thead')[0].outerHTML + '</table>'); + + $t = $tbl.children('tfoot'); + if ($t.length) { + $ft = $('<table class="' + $tbl.attr('class') + '" cellpadding=0 cellspacing=0 style="margin-top:0"></table>') + .append( $t.clone(true) ) // maintain any bindings on the tfoot cells + .append( $tbl.children('thead')[0].outerHTML ) + .wrap('<div class="tablesorter-scroller-footer"/>'); + $fCells = $ft.children('tfoot').eq(0).children('tr').children(); + } + if (c.$extraTables && c.$extraTables.length) { - c.$extraTables = c.$extraTables.add($hdr); + c.$extraTables = c.$extraTables.add($hdr).add($ft); } else { - c.$extraTables = $hdr; + c.$extraTables = $hdr.add($ft); } $tbl .wrap('<div id="' + id + '" class="tablesorter-scroller" />') @@ -120,15 +134,20 @@ ts.addWidget({ // shrink filter row but don't completely hide it because the inputs/selectors may distort the columns .find('.tablesorter-filter-row').addClass('hideme'); - $cells = $hdr - .wrap('<div class="tablesorter-scroller-header" style="width:' + $tbl.width() + ';" />') + if ($ft.length) { + // $ft.parent() to include <div> wrapper + $tbl.after( $ft.parent() ); + } + + $hCells = $hdr + .wrap('<div class="tablesorter-scroller-header" />') .find('.' + ts.css.header); // use max-height, so the height resizes dynamically while filtering - $tbl.wrap('<div class="tablesorter-scroller-table" style="max-height:' + maxHt + 'px;width:' + $tbl.width() + ';" />'); + $tbl.wrap('<div class="tablesorter-scroller-table" style="max-height:' + maxHt + 'px;" />'); // make scroller header sortable - ts.bindEvents(table, $cells); + ts.bindEvents(table, $hCells); // look for filter widget if ($tbl.hasClass('hasFilters')) { @@ -145,7 +164,7 @@ ts.addWidget({ }; resize = function(){ - var d, b, $h, $th, w, + var d, b, $h, w, // Hide other scrollers so we can resize $div = $('div.tablesorter-scroller[id != "' + id + '"]').hide(); @@ -153,8 +172,7 @@ ts.addWidget({ // only remove colgroup if it was added by the plugin // the $.tablesorter.fixColumnWidth() function already does this (v2.19.0) // but we need to get "accurate" resized measurements here - see issue #680 - $tbl.children('colgroup.tablesorter-colgroup').remove(); - $hdr.children('colgroup').remove(); + $tbl.add( $hdr ).add( $ft ).children('colgroup').remove(); // Reset sizes so parent can resize. $tbl @@ -172,43 +190,24 @@ ts.addWidget({ // Shrink a bit to accommodate scrollbar w = ( wo.scroller_barWidth || getBarWidth() ) + b; - d.width( d.parent().innerWidth() - ( d.parent().hasScrollBar() ? w : 0 ) ); w = d.innerWidth() - ( d.hasScrollBar() ? w : 0 ); - $tbl.width( w ); - $hdr.width( w ); - $hdr.parent().width( w ); + $tbl.add( $hdr ).add( $hdr.parent() ).add( $ft ).width( w ); $tbl .closest('.tablesorter-scroller') .find('.tablesorter-scroller-reset') .removeClass('tablesorter-scroller-reset'); - $h = $hdr.find('thead').children().children(); - - // adjust cloned header to match original table width - includes wrappers, headers, and header inner div - $tbl.children('thead').children().children().each(function(i, c){ - $th = $(c).find('.tablesorter-header-inner'); - if ($th.length) { - // I have no idea why this is in here anymore LOL - w = parseInt( $th.css('min-width').replace('auto', '0').replace(/(px|em)/, ''), 10 ); - if ( $th.width() < w ) { - $th.width(w); - } else { - w = $th.width(); - } - - $h.eq(i) - .parent() - .width( $th.parent().width() - b ); - } - }); - // refresh colgroup & copy to cloned header $.tablesorter.fixColumnWidth( table ); - $h = $tbl.children('colgroup').clone(); + $h = $tbl.children('colgroup'); if ($h.length) { - $hdr.prepend($h); + b = $h[0].outerHTML; + $hdr.prepend(b); + if ($ft.length) { + $ft.prepend(b); + } } // hide filter row because filterEnd event fires @@ -250,6 +249,7 @@ ts.addWidget({ var $table = c.$table, namespace = c.namespace + 'tsscroller'; $table.closest('.tablesorter-scroller').find('.tablesorter-scroller-header').remove(); + $table.closest('.tablesorter-scroller').find('.tablesorter-scroller-footer').remove(); $table .unwrap() .find('.tablesorter-filter-row').removeClass('hideme').end() diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js index 97c822b..bd89fda 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js @@ -1,4 +1,7 @@ -/*! Widget: stickyHeaders */ +/*! Widget: stickyHeaders - updated 3/5/2015 (v2.21.0) *//* + * Requires tablesorter v2.8+ and jQuery 1.4.3+ + * by Rob Garrison + */ ;(function ($, window) { 'use strict'; var ts = $.tablesorter = $.tablesorter || {}; @@ -6,6 +9,7 @@ var ts = $.tablesorter = $.tablesorter || {}; $.extend(ts.css, { sticky : 'tablesorter-stickyHeader', // stickyHeader stickyVis : 'tablesorter-sticky-visible', + stickyHide: 'tablesorter-sticky-hidden', stickyWrap: 'tablesorter-sticky-wrapper' }); @@ -74,6 +78,7 @@ ts.addWidget({ return; } var $table = c.$table, + // add position: relative to attach element, hopefully it won't cause trouble. $attach = $(wo.stickyHeaders_attachTo), namespace = c.namespace + 'stickyheaders ', // element to watch for the scroll event @@ -83,8 +88,7 @@ ts.addWidget({ $header = $thead.children('tr').not('.sticky-false').children(), $tfoot = $table.children('tfoot'), $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '', - stickyOffset = $attach.length ? 0 : $stickyOffset.length ? - $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, + stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, // is this table nested? If so, find parent sticky header wrapper (div, not table) $nestedSticky = $table.parent().closest('.' + ts.css.table).hasClass('hasStickyHeaders') ? $table.parent().closest('table.tablesorter')[0].config.widgetOptions.$sticky.parent() : [], @@ -93,14 +97,16 @@ ts.addWidget({ $stickyTable = wo.$sticky = $table.clone() .addClass('containsStickyHeaders ' + ts.css.sticky + ' ' + wo.stickyHeaders) .wrap('<div class="' + ts.css.stickyWrap + '">'), - $stickyWrap = $stickyTable.parent().css({ - position : $attach.length ? 'absolute' : 'fixed', - padding : parseInt( $stickyTable.parent().parent().css('padding-left'), 10 ), - top : stickyOffset + nestedStickyTop, - left : 0, - visibility : 'hidden', - zIndex : wo.stickyHeaders_zIndex || 2 - }), + $stickyWrap = $stickyTable.parent() + .addClass(ts.css.stickyHide) + .css({ + position : $attach.length ? 'absolute' : 'fixed', + padding : parseInt( $stickyTable.parent().parent().css('padding-left'), 10 ), + top : stickyOffset + nestedStickyTop, + left : 0, + visibility : 'hidden', + zIndex : wo.stickyHeaders_zIndex || 2 + }), $stickyThead = $stickyTable.children('thead:first'), $stickyCells, laststate = '', @@ -143,6 +149,10 @@ ts.addWidget({ setWidth( $table, $stickyTable ); setWidth( $header, $stickyCells ); }; + // only add a position relative if a position isn't already defined + if ($attach.length && !$attach.css('position')) { + $attach.css('position', 'relative'); + } // save stickyTable element to config // it is also saved to wo.$sticky if (c.$extraTables && c.$extraTables.length) { @@ -189,8 +199,7 @@ ts.addWidget({ if (!$table.is(':visible')) { return; } // fixes #278 // Detect nested tables - fixes #724 nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; - var prefix = 'tablesorter-sticky-', - offset = $table.offset(), + var offset = $table.offset(), yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 xWindow = $.isWindow( $xScroll[0] ), // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, @@ -200,7 +209,7 @@ ts.addWidget({ cssSettings = { visibility : isVisible }; if ($attach.length) { - cssSettings.top = yWindow ? scrollTop : $attach.scrollTop(); + cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); } if (xWindow) { // adjust when scrolling horizontally - fixes issue #143 @@ -210,8 +219,8 @@ ts.addWidget({ cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; } $stickyWrap - .removeClass(prefix + 'visible ' + prefix + 'hidden') - .addClass(prefix + isVisible) + .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) + .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) .css(cssSettings); if (isVisible !== laststate || event.type === 'resize') { // make sure the column widths match From 3913e60ea6281db15fe71a799273d33c0ef3a077 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Tue, 10 Mar 2015 20:16:29 +0100 Subject: [PATCH 051/138] * updated tablesorter to latest version (2.21.1) --- CHANGELOG.md | 4 ++ README.md | 4 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery-tablesorter/jquery.tablesorter.js | 19 ++++--- .../jquery.tablesorter.widgets.js | 16 +++++- .../widgets/widget-scroller.js | 56 ++++++++++++++----- 7 files changed, 77 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e872ca6..4235591 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.16.1 (2015-03-10) + +* Upgrade tablesorter to v2.21.1 + #### v1.16.0 (2015-03-05) * Upgrade tablesorter to v2.21.0 diff --git a/README.md b/README.md index d95cb19..85a8d4d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.21.0 (3/5/2015), [documentation] +Current tablesorter version: 2.21.1 (3/10/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. @@ -26,7 +26,7 @@ Or install it yourself as: Rails 3.2 and higher (tested up to 4.2) -Tested with ruby 1.9.3 - 2.2.0 +Tested with ruby 1.9.3 - 2.2.1 ## Usage diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 04cdd0e..8ea0863 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.16.0' + VERSION = '1.16.1' end diff --git a/tablesorter b/tablesorter index 087ade7..53694db 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 087ade72551349d1c23fc3c2ac4e53412ffa9a12 +Subproject commit 53694db90bc05ae279ac93c611a3c8d81e67da69 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 6003232..28b7e0d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.21.0 *//* +/*! TableSorter (FORK) v2.21.1 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -34,7 +34,7 @@ var ts = this; - ts.version = '2.21.0'; + ts.version = '2.21.1'; ts.parsers = []; ts.widgets = []; @@ -502,7 +502,8 @@ for (indx = 0; indx < c.columns; indx++) { $t = c.$headers.filter('[data-column="' + indx + '"]'); // target sortable column cells, unless there are none, then use non-sortable cells - c.$headerIndexed[indx] = $t.not('.sorter-false').length ? $t.not('.sorter-false').last() : $t.last(); + // .last() added in jQuery 1.4; use .filter(':last') to maintain compatibility with jQuery v1.2.6 + c.$headerIndexed[indx] = $t.not('.sorter-false').length ? $t.not('.sorter-false').filter(':last') : $t.filter(':last'); } $(table).find(c.selectorHeaders).attr({ scope: 'col', @@ -563,7 +564,7 @@ c.$headers .removeClass(css.join(' ')) .addClass(none).attr('aria-sort', 'none') - .find('.' + c.cssIcon) + .find('.' + ts.css.icon) .removeClass(cssIcon.join(' ')) .addClass(cssIcon[2]); for (i = 0; i < len; i++) { @@ -1224,17 +1225,19 @@ table = $(table)[0]; var $h, k, c = table.config, - $cell = ( $headers || c.$headers ); + $cells = ( $headers || c.$headers ), + // c.$headerIndexed is not defined initially + $cell = c.$headerIndexed && c.$headerIndexed[indx] || $cells.filter('[data-column="' + indx + '"]:last'); if (obj[indx]) { - return getCell ? obj[indx] : obj[$cell.index( $cell.filter('[data-column="' + indx + '"]:last') )]; + return getCell ? obj[indx] : obj[$cells.index( $cell )]; } for (k in obj) { if (typeof k === 'string') { - $h = $cell.filter('[data-column="' + indx + '"]:last') + $h = $cell // header cell with class/id .filter(k) // find elements within the header cell with cell/id - .add( $cell.filter('[data-column="' + indx + '"]:last').find(k) ); + .add( $cell.find(k) ); if ($h.length) { return obj[k]; } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index f417eb1..2496bd4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,8 +4,18 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) widgets - updated 03-05-2015 (v2.21.0)*/ +/*! tablesorter (FORK) widgets - updated 03-10-2015 (v2.21.1)*/ /* Includes: storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort */ +(function(factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + module.exports = factory(require('jquery')); + } else { + factory(jQuery); + } +}(function($) { + /*! Widget: storage */ ;(function ($, window, document) { 'use strict'; @@ -2191,3 +2201,7 @@ ts.addWidget({ }); })(jQuery); + + + return $.tablesorter; +})); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index bcccd55..9ded668 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -55,7 +55,7 @@ $(function(){ '.tablesorter-scrollbar-measure { width: 100px; height: 100px; overflow: scroll; position: absolute; top: -9999px; } ' + '.tablesorter-scroller-reset { width: auto !important; } ' + '.tablesorter-scroller { text-align: left; overflow: hidden; }' + - '.tablesorter-scroller-header { overflow: hidden; }' + + '.tablesorter-scroller-header, .tablesorter-scroller-footer { overflow: hidden; }' + '.tablesorter-scroller-header table.tablesorter { margin-bottom: 0; }' + '.tablesorter-scroller-footer table.tablesorter thead { visibility: hidden, height: 0; overflow: hidden; }' + '.tablesorter-scroller-table { overflow-y: scroll; }' + @@ -96,8 +96,8 @@ ts.addWidget({ }); }, format: function(table, c, wo) { - var maxHt, tbHt, $hdr, $t, resize, getBarWidth, $hCells, $fCells, - $ft = [], + var maxHt, tbHt, $hdr, $t, resize, getBarWidth, $hCells, $fCells, $tblWrap, + $ft = $(), // c.namespace contains a unique tablesorter ID, per table id = c.namespace.slice(1) + 'tsscroller', $win = $(window), @@ -145,6 +145,7 @@ ts.addWidget({ // use max-height, so the height resizes dynamically while filtering $tbl.wrap('<div class="tablesorter-scroller-table" style="max-height:' + maxHt + 'px;" />'); + $tblWrap = $tbl.parent(); // make scroller header sortable ts.bindEvents(table, $hCells); @@ -164,7 +165,7 @@ ts.addWidget({ }; resize = function(){ - var d, b, $h, w, + var b, $h, $f, w, // Hide other scrollers so we can resize $div = $('div.tablesorter-scroller[id != "' + id + '"]').hide(); @@ -180,19 +181,48 @@ ts.addWidget({ .children('thead') .find('.tablesorter-header-inner').addClass('tablesorter-scroller-reset').end() .find('.tablesorter-filter-row').show(); - d = $tbl.parent(); - d.addClass('tablesorter-scroller-reset'); + $tblWrap.addClass('tablesorter-scroller-reset'); - d.parent().trigger('resize'); + $tblWrap.parent().trigger('resize'); // include left & right border widths b = parseInt( $tbl.css('border-left-width'), 10 ) + parseInt( $tbl.css('border-right-width'), 10 ); // Shrink a bit to accommodate scrollbar w = ( wo.scroller_barWidth || getBarWidth() ) + b; - d.width( d.parent().innerWidth() - ( d.parent().hasScrollBar() ? w : 0 ) ); - w = d.innerWidth() - ( d.hasScrollBar() ? w : 0 ); - $tbl.add( $hdr ).add( $hdr.parent() ).add( $ft ).width( w ); + + $tblWrap.width( $tblWrap.parent().innerWidth() - ( $tblWrap.parent().hasScrollBar() ? w : 0 ) ); + w = $tblWrap.innerWidth() - ( $tblWrap.hasScrollBar() ? w : 0 ); + $hdr.parent().add( $ft.parent() ).width( w ); + + w = $tbl.width(); + + $h = $hdr.children('thead').children().children('th, td').filter(':visible'); + $f = $ft.children('tfoot').children().children('th, td').filter(':visible'); + $tbl.children('thead').children().eq(0).children('th, td').each(function(indx, el) { + var width, border, + $this = $(this); + // code from https://github.com/jmosbech/StickyTableHeaders + if ($this.css('box-sizing') === 'border-box') { + width = $this.outerWidth(); + } else { + if ($h.eq(indx).css('border-collapse') === 'collapse') { + if (window.getComputedStyle) { + width = parseFloat( window.getComputedStyle(this, null).width ); + } else { + // ie8 only + border = parseFloat( $this.css('border-width') ); + width = $this.outerWidth() - parseFloat( $this.css('padding-left') ) - parseFloat( $this.css('padding-right') ) - border; + } + } else { + width = $this.width(); + } + } + $h.eq(indx).add( $f.eq(indx) ).css({ + 'min-width': width, + 'max-width': width + }); + }); $tbl .closest('.tablesorter-scroller') @@ -224,17 +254,17 @@ ts.addWidget({ $tbl.find('thead').css('visibility', 'hidden'); c.isScrolling = true; - tbHt = $tbl.parent().parent().height(); + tbHt = $tblWrap.parent().height(); // The header will always jump into view if scrolling the table body - $tbl.parent().bind('scroll', function(){ + $tblWrap.bind('scroll', function(){ if (wo.scroller_jumpToHeader) { var pos = $win.scrollTop() - $hdr.offset().top; if ($(this).scrollTop() !== 0 && pos < tbHt && pos > 0) { $win.scrollTop( $hdr.offset().top ); } } - $hdr.parent().scrollLeft( $(this).scrollLeft() ); + $hdr.parent().add( $ft.parent() ).scrollLeft( $(this).scrollLeft() ); }); } From 7744bab7c10d6842fc0c9db3f641afb6acaf2344 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Sun, 15 Mar 2015 19:37:31 +0100 Subject: [PATCH 052/138] * updated tablesorter to latest version (2.21.2) --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 4 ++-- .../jquery-tablesorter/jquery.tablesorter.js | 9 +++------ .../jquery.tablesorter.widgets.js | 20 +++++++++---------- .../widgets/widget-editable.js | 4 ++-- .../widgets/widget-filter.js | 12 +++++------ .../widgets/widget-output.js | 6 +++--- .../widgets/widget-pager.js | 3 ++- .../widgets/widget-uitheme.js | 6 ++---- 12 files changed, 36 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4235591..de78ca3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.16.2 (2015-03-15) + +* Upgrade tablesorter to v2.21.2 + #### v1.16.1 (2015-03-10) * Upgrade tablesorter to v2.21.1 diff --git a/README.md b/README.md index 85a8d4d..adcbea5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.21.1 (3/10/2015), [documentation] +Current tablesorter version: 2.21.2 (3/13/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 8ea0863..610c581 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.16.1' + VERSION = '1.16.2' end diff --git a/tablesorter b/tablesorter index 53694db..2340dc7 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 53694db90bc05ae279ac93c611a3c8d81e67da69 +Subproject commit 2340dc7de8cf510b8fcdf3011f3d21e3c6450052 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 0408957..5b2040f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -705,7 +705,6 @@ pg = Math.min( p.totalPages, p.filteredPages ); if ( p.page < 0 ) { p.page = 0; } if ( p.page > ( pg - 1 ) && pg !== 0 ) { p.page = pg - 1; } - // fixes issue where one currentFilter is [] and the other is ['','',''], // making the next if comparison think the filters are different (joined by commas). Fixes #202. l.currentFilters = (l.currentFilters || []).join('') === '' ? [] : l.currentFilters; @@ -911,7 +910,8 @@ .bind('updateComplete.pager', function(e, table, triggered){ e.stopPropagation(); // table can be unintentionally undefined in tablesorter v2.17.7 and earlier - if ( !table || triggered ) { return; } + // don't recalculate total rows/pages if using ajax + if ( !table || triggered || p.ajax ) { return; } var $rows = c.$tbodies.eq(0).children('tr').not(c.selectorRemove); p.totalRows = $rows.length - ( p.countChildRows ? 0 : $rows.filter('.' + c.cssChildRow).length ); p.totalPages = Math.ceil( p.totalRows / p.size ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 28b7e0d..b1d5299 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.21.1 *//* +/*! TableSorter (FORK) v2.21.2 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -34,7 +34,7 @@ var ts = this; - ts.version = '2.21.1'; + ts.version = '2.21.2'; ts.parsers = []; ts.widgets = []; @@ -1252,7 +1252,6 @@ ts.computeColumnIndex = function(trs) { var matrix = [], lookup = {}, - cols = 0, // determine the number of columns i, j, k, l, $cell, cell, cells, rowIndex, cellId, rowSpan, colSpan, firstAvailCol, matrixrow; for (i = 0; i < trs.length; i++) { cells = trs[i].cells; @@ -1274,7 +1273,6 @@ } } lookup[cellId] = firstAvailCol; - cols = Math.max(firstAvailCol, cols); // add data-column $cell.attr({ 'data-column' : firstAvailCol }); // 'data-row' : rowIndex for (k = rowIndex; k < rowIndex + rowSpan; k++) { @@ -1288,8 +1286,7 @@ } } } - // may not be accurate if # header columns !== # tbody columns - return cols + 1; // add one because it's a zero-based index + return matrixrow.length; }; // *** Process table *** diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 2496bd4..edd0269 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) widgets - updated 03-10-2015 (v2.21.1)*/ +/*! tablesorter (FORK) widgets - updated 03-13-2015 (v2.21.2)*/ /* Includes: storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -244,10 +244,8 @@ ts.addWidget({ hdr = [themes.sortDesc, themes.active].join(' '); icon = themes.iconSortDesc; } - $h - .addClass(hdr) - .find('.' + ts.css.icon) - .addClass(icon || ''); + $header.addClass(hdr); + $icon.addClass(icon || ''); } } } @@ -501,7 +499,7 @@ ts.filter = { notMatch: function( c, data ) { if ( /^\!/.test(data.iFilter) ) { var indx, - filter = ts.filter.parseFilter(c, data.iFilter.replace('!', ''), data.index, data.parsed[data.index]); + filter = ts.filter.parseFilter(c, data.iFilter.replace('!', ''), data.index, data.parsed[data.index]) || ''; if (ts.filter.regex.exact.test(filter)) { // look for exact not matches - see #628 filter = filter.replace(ts.filter.regex.exact, ''); @@ -517,7 +515,7 @@ ts.filter = { exact: function( c, data ) { /*jshint eqeqeq:false */ if (ts.filter.regex.exact.test(data.iFilter)) { - var filter = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.exact, ''), data.index, data.parsed[data.index]); + var filter = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.exact, ''), data.index, data.parsed[data.index]) || ''; return data.anyMatch ? $.inArray(filter, data.rowArray) >= 0 : filter == data.iExact; } return null; @@ -547,8 +545,8 @@ ts.filter = { parsed = data.parsed[index], // make sure the dash is for a range and not indicating a negative number query = data.iFilter.split( ts.filter.regex.toSplit ), - range1 = ts.formatFloat( ts.filter.parseFilter(c, query[0].replace(ts.filter.regex.nondigit, ''), index, parsed), table ), - range2 = ts.formatFloat( ts.filter.parseFilter(c, query[1].replace(ts.filter.regex.nondigit, ''), index, parsed), table ); + range1 = ts.formatFloat( ts.filter.parseFilter(c, query[0].replace(ts.filter.regex.nondigit, '') || '', index, parsed), table ), + range2 = ts.formatFloat( ts.filter.parseFilter(c, query[1].replace(ts.filter.regex.nondigit, '') || '', index, parsed), table ); // parse filter value in case we're comparing numbers (dates) if (parsed || c.parsers[index].type === 'numeric') { result = c.parsers[index].format('' + query[0], table, c.$headers.eq(index), index); @@ -569,7 +567,7 @@ ts.filter = { if ( /[\?\*\|]/.test(data.iFilter) || ts.filter.regex.orReplace.test(data.filter) ) { var index = data.index, parsed = data.parsed[index], - query = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.orReplace, "|"), index, parsed); + query = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.orReplace, "|"), index, parsed) || ''; // look for an exact match with the "or" unless the "filter-match" class is found if (!c.$headerIndexed[index].hasClass('filter-match') && /\|/.test(query)) { // show all results while using filter match. Fixes #727 @@ -587,7 +585,7 @@ ts.filter = { var indx, patternIndx = 0, len = data.iExact.length, - pattern = ts.filter.parseFilter(c, data.iFilter.slice(1), data.index, data.parsed[data.index]); + pattern = ts.filter.parseFilter(c, data.iFilter.slice(1), data.index, data.parsed[data.index]) || ''; for (indx = 0; indx < len; indx++) { if (data.iExact[indx] === pattern[patternIndx]) { patternIndx += 1; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js index 4faa8bb..255a008 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js @@ -95,8 +95,8 @@ var tse = $.tablesorter.editable = { bindEvents: function( c, wo ) { c.$table - .off( ( 'updateComplete pagerComplete '.split( ' ' ).join( '.tseditable' ) ).replace( /\s+/g, ' ' ) ) - .on( 'updateComplete pagerComplete '.split( ' ' ).join( '.tseditable' ), function() { + .off( ( 'updateComplete pagerComplete '.split( ' ' ).join( '.tseditable ' ) ).replace( /\s+/g, ' ' ) ) + .on( 'updateComplete pagerComplete '.split( ' ' ).join( '.tseditable ' ), function() { tse.update( c, c.widgetOptions ); }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 1cc84fc..787f241 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -142,7 +142,7 @@ ts.filter = { notMatch: function( c, data ) { if ( /^\!/.test(data.iFilter) ) { var indx, - filter = ts.filter.parseFilter(c, data.iFilter.replace('!', ''), data.index, data.parsed[data.index]); + filter = ts.filter.parseFilter(c, data.iFilter.replace('!', ''), data.index, data.parsed[data.index]) || ''; if (ts.filter.regex.exact.test(filter)) { // look for exact not matches - see #628 filter = filter.replace(ts.filter.regex.exact, ''); @@ -158,7 +158,7 @@ ts.filter = { exact: function( c, data ) { /*jshint eqeqeq:false */ if (ts.filter.regex.exact.test(data.iFilter)) { - var filter = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.exact, ''), data.index, data.parsed[data.index]); + var filter = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.exact, ''), data.index, data.parsed[data.index]) || ''; return data.anyMatch ? $.inArray(filter, data.rowArray) >= 0 : filter == data.iExact; } return null; @@ -188,8 +188,8 @@ ts.filter = { parsed = data.parsed[index], // make sure the dash is for a range and not indicating a negative number query = data.iFilter.split( ts.filter.regex.toSplit ), - range1 = ts.formatFloat( ts.filter.parseFilter(c, query[0].replace(ts.filter.regex.nondigit, ''), index, parsed), table ), - range2 = ts.formatFloat( ts.filter.parseFilter(c, query[1].replace(ts.filter.regex.nondigit, ''), index, parsed), table ); + range1 = ts.formatFloat( ts.filter.parseFilter(c, query[0].replace(ts.filter.regex.nondigit, '') || '', index, parsed), table ), + range2 = ts.formatFloat( ts.filter.parseFilter(c, query[1].replace(ts.filter.regex.nondigit, '') || '', index, parsed), table ); // parse filter value in case we're comparing numbers (dates) if (parsed || c.parsers[index].type === 'numeric') { result = c.parsers[index].format('' + query[0], table, c.$headers.eq(index), index); @@ -210,7 +210,7 @@ ts.filter = { if ( /[\?\*\|]/.test(data.iFilter) || ts.filter.regex.orReplace.test(data.filter) ) { var index = data.index, parsed = data.parsed[index], - query = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.orReplace, "|"), index, parsed); + query = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.orReplace, "|"), index, parsed) || ''; // look for an exact match with the "or" unless the "filter-match" class is found if (!c.$headerIndexed[index].hasClass('filter-match') && /\|/.test(query)) { // show all results while using filter match. Fixes #727 @@ -228,7 +228,7 @@ ts.filter = { var indx, patternIndx = 0, len = data.iExact.length, - pattern = ts.filter.parseFilter(c, data.iFilter.slice(1), data.index, data.parsed[data.index]); + pattern = ts.filter.parseFilter(c, data.iFilter.slice(1), data.index, data.parsed[data.index]) || ''; for (indx = 0; indx < len; indx++) { if (data.iExact[indx] === pattern[patternIndx]) { patternIndx += 1; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index 3181576..350df88 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -16,13 +16,13 @@ output = ts.output = { event : 'outputTable', // wrap line breaks & tabs in quotes - regexQuote : /([\n\t]|<[^<]+>)/, // test + regexQuote : /([\n\t\x09\x0d\x0a]|<[^<]+>)/, // test if cell needs wrapping quotes regexBR : /(<br([\s\/])?>|\n)/g, // replace regexIMG : /<img[^>]+alt\s*=\s*['"]([^'"]+)['"][^>]*>/i, // match regexHTML : /<[^<]+>/g, // replace - replaceCR : '\\n', - replaceTab : '\\t', + replaceCR : '\x0d\x0a', + replaceTab : '\x09', popupTitle : 'Output', popupStyle : 'width:100%;height:100%;', // for textarea diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index aeb7f46..8d1db11 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -268,7 +268,8 @@ tsp = ts.pager = { .on('updateComplete.pager', function(e, table, triggered){ e.stopPropagation(); // table can be unintentionally undefined in tablesorter v2.17.7 and earlier - if (!table || triggered) { return; } + // don't recalculate total rows/pages if using ajax + if (!table || triggered || p.ajax) { return; } var $rows = c.$tbodies.eq(0).children('tr').not(c.selectorRemove); p.totalRows = $rows.length - ( wo.pager_countChildRows ? 0 : $rows.filter('.' + c.cssChildRow).length ); p.totalPages = Math.ceil( p.totalRows / p.size ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js index 90e046d..1b2d4c8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js @@ -149,10 +149,8 @@ ts.addWidget({ hdr = [themes.sortDesc, themes.active].join(' '); icon = themes.iconSortDesc; } - $h - .addClass(hdr) - .find('.' + ts.css.icon) - .addClass(icon || ''); + $header.addClass(hdr); + $icon.addClass(icon || ''); } } } From daeebe7ea676294633d3c61e2797e71e39202df5 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Thu, 26 Mar 2015 22:12:04 +0100 Subject: [PATCH 053/138] * updated tablesorter to latest version (2.21.3) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 14 +- .../jquery-tablesorter/jquery.tablesorter.js | 50 +- .../jquery.tablesorter.widgets.js | 536 +++++++----- .../parsers/parser-input-select.js | 8 +- .../widgets/widget-build-table.js | 5 +- .../widget-filter-formatter-select2.js | 4 +- .../widgets/widget-filter.js | 49 +- .../widgets/widget-output.js | 5 +- .../widgets/widget-pager.js | 16 +- .../widgets/widget-resizable.js | 416 ++++++---- .../widgets/widget-scroller.js | 763 +++++++++++++----- .../widgets/widget-stickyHeaders.js | 13 +- .../widgets/widget-storage.js | 43 +- .../widgets/widget-uitheme.js | 11 +- .../jquery-tablesorter/theme.black-ice.css | 9 +- .../jquery-tablesorter/theme.blue.css | 13 +- .../jquery-tablesorter/theme.bootstrap.css | 9 +- .../jquery-tablesorter/theme.bootstrap_2.css | 11 +- .../jquery-tablesorter/theme.dark.css | 11 +- .../jquery-tablesorter/theme.default.css | 11 +- .../jquery-tablesorter/theme.dropbox.css | 10 +- .../jquery-tablesorter/theme.green.css | 21 +- .../jquery-tablesorter/theme.grey.css | 13 +- .../jquery-tablesorter/theme.ice.css | 25 +- .../jquery-tablesorter/theme.jui.css | 7 +- .../jquery-tablesorter/theme.metro-dark.css | 9 +- 30 files changed, 1381 insertions(+), 711 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de78ca3..4b9622a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.16.2 (2015-03-26) + +* Upgrade tablesorter to v2.21.3 + #### v1.16.2 (2015-03-15) * Upgrade tablesorter to v2.21.2 diff --git a/README.md b/README.md index adcbea5..2fd64f1 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.21.2 (3/13/2015), [documentation] +Current tablesorter version: 2.21.3 (3/26/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 610c581..4373c29 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.16.2' + VERSION = '1.16.3' end diff --git a/tablesorter b/tablesorter index 2340dc7..3ee06dc 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 2340dc7de8cf510b8fcdf3011f3d21e3c6450052 +Subproject commit 3ee06dceb60d3a1e717fc9ea8bfefb304e9ed720 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 5b2040f..e5e77c9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin - * updated 3/5/2015 (v2.21.0) + * updated 3/26/2015 (v2.21.3) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -115,7 +115,7 @@ }; var pagerEvents = 'filterInit filterStart filterEnd sortEnd disable enable destroy updateComplete ' + - 'pageSize pageSet pageAndSize pagerUpdate ', + 'pageSize pageSet pageAndSize pagerUpdate refreshComplete ', $this = this, @@ -204,7 +204,7 @@ p.$goto.html(t).val( p.page + 1 ); } if ($out.length) { - $out[ ($out[0].tagName === 'INPUT') ? 'val' : 'html' ](s); + $out[ ($out[0].nodeName === 'INPUT') ? 'val' : 'html' ](s); // rebind startRow/page inputs $out.find('.ts-startRow, .ts-page').unbind('change.pager').bind('change.pager', function(){ var v = $(this).val(), @@ -934,9 +934,12 @@ }) .bind('pageSet.pager pagerUpdate.pager', function(e,v){ e.stopPropagation(); - p.page = (parseInt(v, 10) || 1) - 1; // force pager refresh - if (e.type === 'pagerUpdate') { p.last.page = true; } + if (e.type === 'pagerUpdate') { + v = typeof v === 'undefined' ? p.page + 1 : v; + p.last.page = true; + } + p.page = (parseInt(v, 10) || 1) - 1; moveToPage(table, p, true); updatePageDisplay(table, p, false); }) @@ -984,7 +987,6 @@ } else if (c.debug) { ts.log('Pager: >> Goto selector not found'); } - // page size selector p.$size = pager.find(p.cssPageSize); if ( p.$size.length ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index b1d5299..f585b42 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.21.2 *//* +/*! TableSorter (FORK) v2.21.3 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -34,7 +34,7 @@ var ts = this; - ts.version = '2.21.2'; + ts.version = '2.21.3'; ts.parsers = []; ts.widgets = []; @@ -559,7 +559,9 @@ cssIcon = [ c.cssIconAsc, c.cssIconDesc, c.cssIconNone ], aria = ['ascending', 'descending'], // find the footer - $t = $(table).find('tfoot tr').children().add(c.$extraHeaders).removeClass(css.join(' ')); + $t = $(table).find('tfoot tr').children() + .add( $( c.namespace + '_extra_headers' ) ) + .removeClass( css.join( ' ' ) ); // remove all header information c.$headers .removeClass(css.join(' ')) @@ -579,7 +581,7 @@ .removeClass(none) .addClass(css[list[i][1]]) .attr('aria-sort', aria[list[i][1]]) - .find('.' + c.cssIcon) + .find('.' + ts.css.icon) .removeClass(cssIcon[2]) .addClass(cssIcon[list[i][1]]); } @@ -1067,7 +1069,7 @@ // save initial settings c.originalSettings = settings; // create a table from data (build table widget) - if (!table.hasInitialized && ts.buildTable && this.tagName !== 'TABLE') { + if (!table.hasInitialized && ts.buildTable && this.nodeName !== 'TABLE') { // return the table (in case the original target is the table's container) ts.buildTable(table, c); } else { @@ -1334,34 +1336,42 @@ ts.bindEvents = function(table, $headers, core){ table = $(table)[0]; - var downTime, + var t, downTarget = null, c = table.config; if (core !== true) { - c.$extraHeaders = c.$extraHeaders ? c.$extraHeaders.add($headers) : $headers; + $headers.addClass( c.namespace.slice(1) + '_extra_headers' ); + t = $.fn.closest ? $headers.closest('table')[0] : $headers.parents('table')[0]; + if (t && t.nodeName === 'TABLE' && t !== table) { + $(t).addClass( c.namespace.slice(1) + '_extra_table' ); + } } // apply event handling to headers and/or additional headers (stickyheaders, scroller, etc) $headers // http://stackoverflow.com/questions/5312849/jquery-find-self; .find(c.selectorSort).add( $headers.filter(c.selectorSort) ) - .unbind( ('mousedown mouseup sort keyup '.split(' ').join(c.namespace + ' ')).replace(/\s+/g, ' ') ) - .bind( 'mousedown mouseup sort keyup '.split(' ').join(c.namespace + ' '), function(e, external) { + .unbind( ('mousedown mouseup click sort keyup '.split(' ').join(c.namespace + ' ')).replace(/\s+/g, ' ') ) + .bind( 'mousedown mouseup click sort keyup '.split(' ').join(c.namespace + ' '), function(e, external) { var cell, $target = $(e.target), type = e.type; - // only recognize left clicks or enter - if ( ((e.which || e.button) !== 1 && !/sort|keyup/.test(type)) || (type === 'keyup' && e.which !== 13) ) { + // only recognize left clicks + if ( ( ( e.which || e.button ) !== 1 && !/sort|keyup|click/.test(type) ) || + // allow pressing enter + ( type === 'keyup' && e.which !== 13 ) || + // allow triggering a click event (e.which is undefined) & ignore physical clicks + ( type === 'click' && typeof e.which !== 'undefined' ) ) { return; } - // ignore long clicks (prevents resizable widget from initializing a sort) - if (type === 'mouseup' && external !== true && (new Date().getTime() - downTime > 250)) { return; } + // ignore mouseup if mousedown wasn't on the same target + if ( type === 'mouseup' && downTarget !== e.target && external !== true ) { return; } // set timer on mousedown - if (type === 'mousedown') { - downTime = new Date().getTime(); + if ( type === 'mousedown' ) { + downTarget = e.target; return; } - cell = $.fn.closest ? $target.closest('td,th') : $target.parents('td,th').filter(':first'); + downTarget = null; // prevent sort being triggered on form elements - if ( /(input|select|button|textarea)/i.test(e.target.tagName) || + if ( /(input|select|button|textarea)/i.test(e.target.nodeName) || // nosort class name, or elements within a nosort container $target.hasClass(c.cssNoSort) || $target.parents('.' + c.cssNoSort).length > 0 || // elements within a button @@ -1370,7 +1380,7 @@ } if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); } // jQuery v1.2.6 doesn't have closest() - cell = $.fn.closest ? $(this).closest('th, td')[0] : /TH|TD/.test(this.tagName) ? this : $(this).parents('th, td')[0]; + cell = $.fn.closest ? $(this).closest('th, td')[0] : /TH|TD/.test(this.nodeName) ? this : $(this).parents('th, td')[0]; // reference original table headers and find the same cell cell = c.$headers[ $headers.index( cell ) ]; if (!cell.sortDisabled) { @@ -2048,8 +2058,8 @@ priority: 90, format: function(table, c, wo) { var $tb, $tv, $tr, row, even, time, k, - child = new RegExp(c.cssChildRow, 'i'), - b = c.$tbodies; + child = new RegExp(c.cssChildRow, 'i'), + b = c.$tbodies.add( $( c.namespace + '_extra_table' ).children( 'tbody' ) ); if (c.debug) { time = new Date(); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index edd0269..03b7abe 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) widgets - updated 03-13-2015 (v2.21.2)*/ +/*! tablesorter (FORK) widgets - updated 03-26-2015 (v2.21.3)*/ /* Includes: storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! Widget: storage */ +/*! Widget: storage - updated 3/26/2015 (v2.21.3) */ ;(function ($, window, document) { 'use strict'; @@ -43,26 +43,39 @@ var ts = $.tablesorter = $.tablesorter || {}; ts.storage = function(table, key, value, options) { table = $(table)[0]; var cookieIndex, cookies, date, - hasLocalStorage = false, + hasStorage = false, values = {}, c = table.config, + wo = c && c.widgetOptions, + storageType = ( options && options.useSessionStorage ) || ( wo && wo.storage_useSessionStorage ) ? + 'sessionStorage' : 'localStorage', $table = $(table), - id = options && options.id || $table.attr(options && options.group || - 'data-table-group') || table.id || $('.tablesorter').index( $table ), - url = options && options.url || $table.attr(options && options.page || - 'data-table-page') || c && c.fixedUrl || window.location.pathname; + // id from (1) options ID, (2) table "data-table-group" attribute, (3) widgetOptions.storage_tableId, + // (4) table ID, then (5) table index + id = options && options.id || + $table.attr( options && options.group || wo && wo.storage_group || 'data-table-group') || + wo && wo.storage_tableId || table.id || $('.tablesorter').index( $table ), + // url from (1) options url, (2) table "data-table-page" attribute, (3) widgetOptions.storage_fixedUrl, + // (4) table.config.fixedUrl (deprecated), then (5) window location path + url = options && options.url || + $table.attr(options && options.page || wo && wo.storage_page || 'data-table-page') || + wo && wo.storage_fixedUrl || c && c.fixedUrl || window.location.pathname; // https://gist.github.com/paulirish/5558557 - if ('localStorage' in window) { + if (storageType in window) { try { - window.localStorage.setItem('_tmptest', 'temp'); - hasLocalStorage = true; - window.localStorage.removeItem('_tmptest'); - } catch(error) {} + window[storageType].setItem('_tmptest', 'temp'); + hasStorage = true; + window[storageType].removeItem('_tmptest'); + } catch(error) { + if (c && c.debug) { + ts.log( storageType + ' is not supported in this browser' ); + } + } } // *** get value *** if ($.parseJSON) { - if (hasLocalStorage) { - values = $.parseJSON(localStorage[key] || 'null') || {}; + if (hasStorage) { + values = $.parseJSON( window[storageType][key] || 'null' ) || {}; } else { // old browser, using cookies cookies = document.cookie.split(/[;\s|=]/); @@ -79,8 +92,8 @@ ts.storage = function(table, key, value, options) { } values[url][id] = value; // *** set value *** - if (hasLocalStorage) { - localStorage[key] = JSON.stringify(values); + if (hasStorage) { + window[storageType][key] = JSON.stringify(values); } else { date = new Date(); date.setTime(date.getTime() + (31536e+6)); // 365 days @@ -93,7 +106,7 @@ ts.storage = function(table, key, value, options) { })(jQuery, window, document); -/*! Widget: uitheme */ +/*! Widget: uitheme - updated 3/26/2015 (v2.21.3) */ ;(function ($) { 'use strict'; var ts = $.tablesorter = $.tablesorter || {}; @@ -153,8 +166,8 @@ ts.addWidget({ format: function(table, c, wo) { var i, hdr, icon, time, $header, $icon, $tfoot, $h, oldtheme, oldremove, oldIconRmv, hasOldTheme, themesAll = ts.themes, - $table = c.$table.add( c.$extraTables ), - $headers = c.$headers.add( c.$extraHeaders ), + $table = c.$table.add( $( c.namespace + '_extra_table' ) ), + $headers = c.$headers.add( $( c.namespace + '_extra_headers' ) ), theme = c.theme || 'jui', themes = themesAll[theme] || {}, remove = $.trim( [ themes.sortNone, themes.sortDesc, themes.sortAsc, themes.active ].join( ' ' ) ), @@ -225,7 +238,10 @@ ts.addWidget({ } } for (i = 0; i < c.columns; i++) { - $header = c.$headers.add(c.$extraHeaders).not('.sorter-false').filter('[data-column="' + i + '"]'); + $header = c.$headers + .add($(c.namespace + '_extra_headers')) + .not('.sorter-false') + .filter('[data-column="' + i + '"]'); $icon = (ts.css.icon) ? $header.find('.' + ts.css.icon) : $(); $h = $headers.not('.sorter-false').filter('[data-column="' + i + '"]:last'); if ($h.length) { @@ -355,17 +371,20 @@ ts.addWidget({ })(jQuery); -/*! Widget: filter - updated 3/5/2015 (v2.21.0) *//* +/*! Widget: filter - updated 3/26/2015 (v2.21.3) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ ;(function ($) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; - -$.extend(ts.css, { - filterRow : 'tablesorter-filter-row', - filter : 'tablesorter-filter' +var ts = $.tablesorter = $.tablesorter || {}, + tscss = ts.css; + +$.extend(tscss, { + filterRow : 'tablesorter-filter-row', + filter : 'tablesorter-filter', + filterDisabled : 'disabled', + filterRowHide : 'hideme' }); ts.addWidget({ @@ -415,7 +434,7 @@ ts.addWidget({ // add .tsfilter namespace to all BUT search .unbind( events.replace(/\s+/g, ' ') ) // remove the filter row even if refreshing, because the column might have been moved - .find('.' + ts.css.filterRow).remove(); + .find('.' + tscss.filterRow).remove(); if (refreshing) { return; } for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody @@ -643,13 +662,13 @@ ts.filter = { c.$table.bind( txt, function(event, filter) { val = (wo.filter_hideEmpty && $.isEmptyObject(c.cache) && !(c.delayInit && event.type === 'appendCache')); // hide filter row using the "filtered" class name - c.$table.find('.' + ts.css.filterRow).toggleClass(wo.filter_filteredRow, val ); // fixes #450 + c.$table.find('.' + tscss.filterRow).toggleClass(wo.filter_filteredRow, val ); // fixes #450 if ( !/(search|filter)/.test(event.type) ) { event.stopPropagation(); ts.filter.buildDefault(table, true); } if (event.type === 'filterReset') { - c.$table.find('.' + ts.css.filter).add(wo.filter_$externalFilters).val(''); + c.$table.find('.' + tscss.filter).add(wo.filter_$externalFilters).val(''); ts.filter.searching(table, []); } else if (event.type === 'filterEnd') { ts.filter.buildDefault(table, true); @@ -712,7 +731,7 @@ ts.filter = { options += '<option ' + (txt === val ? '' : 'data-function-name="' + string + '" ') + 'value="' + val + '">' + txt + '</option>'; } } - c.$table.find('thead').find('select.' + ts.css.filter + '[data-column="' + column + '"]').append(options); + c.$table.find('thead').find('select.' + tscss.filter + '[data-column="' + column + '"]').append(options); } } } @@ -721,7 +740,7 @@ ts.filter = { // it would append the same options twice. ts.filter.buildDefault(table, true); - ts.filter.bindSearch( table, c.$table.find('.' + ts.css.filter), true ); + ts.filter.bindSearch( table, c.$table.find('.' + tscss.filter), true ); if (wo.filter_external) { ts.filter.bindSearch( table, wo.filter_external ); } @@ -736,7 +755,7 @@ ts.filter = { .unbind( ('filterStart filterEnd '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') ) .bind( 'filterStart filterEnd '.split(' ').join(c.namespace + 'filter '), function(event, columns) { // only add processing to certain columns to all columns - $header = (columns) ? c.$table.find('.' + ts.css.header).filter('[data-column]').filter(function() { + $header = (columns) ? c.$table.find('.' + tscss.header).filter('[data-column]').filter(function() { return columns[$(this).data('column')] !== ''; }) : ''; ts.isProcessing(table, event.type === 'filterStart', columns ? $header : ''); @@ -850,7 +869,7 @@ ts.filter = { // c.columns defined in computeThIndexes() columns = c.columns, arry = $.isArray(wo.filter_cellFilter), - buildFilter = '<tr role="row" class="' + ts.css.filterRow + '">'; + buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; for (column = 0; column < columns; column++) { if (arry) { buildFilter += '<td' + ( wo.filter_cellFilter[column] ? ' class="' + wo.filter_cellFilter[column] + '"' : '' ) + '></td>'; @@ -899,9 +918,9 @@ ts.filter = { name = ( $.isArray(wo.filter_cssFilter) ? (typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '') : wo.filter_cssFilter ) || ''; - buildFilter.addClass( ts.css.filter + ' ' + name ).attr('data-column', column); + buildFilter.addClass( tscss.filter + ' ' + name ).attr('data-column', column); if (disabled) { - buildFilter.attr('placeholder', '').addClass('disabled')[0].disabled = true; // disabled! + buildFilter.attr('placeholder', '').addClass(tscss.filterDisabled)[0].disabled = true; // disabled! } } } @@ -995,7 +1014,7 @@ ts.filter = { } if (wo.filter_hideFilters) { // show/hide filter row as needed - c.$table.find('.' + ts.css.filterRow).trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); + c.$table.find('.' + tscss.filterRow).trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons @@ -1021,8 +1040,8 @@ ts.filter = { hideFilters: function(table, c) { var $filterRow, $filterRow2, timer; $(table) - .find('.' + ts.css.filterRow) - .addClass('hideme') + .find('.' + tscss.filterRow) + .addClass(tscss.filterRowHide) .bind('mouseenter mouseleave', function(e) { // save event object - http://bugs.jquery.com/ticket/12140 var event = e; @@ -1030,14 +1049,14 @@ ts.filter = { clearTimeout(timer); timer = setTimeout(function() { if ( /enter|over/.test(event.type) ) { - $filterRow.removeClass('hideme'); + $filterRow.removeClass(tscss.filterRowHide); } else { // don't hide if input has focus // $(':focus') needs jQuery 1.6+ if ( $(document.activeElement).closest('tr')[0] !== $filterRow[0] ) { // don't hide row if any filter has a value if (c.lastCombinedFilter === '') { - $filterRow.addClass('hideme'); + $filterRow.addClass(tscss.filterRowHide); } } } @@ -1050,7 +1069,7 @@ ts.filter = { timer = setTimeout(function() { // don't hide row if any filter has a value if (ts.getFilters(c.$table).join('') === '') { - $filterRow2[ event.type === 'focus' ? 'removeClass' : 'addClass']('hideme'); + $filterRow2[ event.type === 'focus' ? 'removeClass' : 'addClass'](tscss.filterRowHide); } }, 200); }); @@ -1538,7 +1557,7 @@ ts.filter = { // t.data('placeholder') won't work in jQuery older than 1.4.3 options = '<option value="">' + ( node.data('placeholder') || node.attr('data-placeholder') || wo.filter_placeholder.select || '' ) + '</option>', // Get curent filter value - currentValue = c.$table.find('thead').find('select.' + ts.css.filter + '[data-column="' + column + '"]').val(); + currentValue = c.$table.find('thead').find('select.' + tscss.filter + '[data-column="' + column + '"]').val(); // nothing included in arry (external source), so get the options from filter_selectSource or column data if (typeof arry === 'undefined' || arry === '') { arry = ts.filter.getOptionSource(table, column, onlyAvail); @@ -1565,7 +1584,7 @@ ts.filter = { } // update all selects in the same column (clone thead in sticky headers & any external selects) - fixes 473 - $filters = ( c.$filters ? c.$filters : c.$table.children('thead') ).find('.' + ts.css.filter); + $filters = ( c.$filters ? c.$filters : c.$table.children('thead') ).find('.' + tscss.filter); if (wo.filter_$externalFilters) { $filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters; } @@ -1609,7 +1628,7 @@ ts.getFilters = function(table, getRaw, setFilters, skipFirst) { } if (c) { if (c.$filters) { - $filters = c.$filters.find('.' + ts.css.filter); + $filters = c.$filters.find('.' + tscss.filter); } if (wo.filter_$externalFilters) { $filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters; @@ -1675,7 +1694,7 @@ ts.setFilters = function(table, filter, apply, skipFirst) { })(jQuery); -/*! Widget: stickyHeaders - updated 3/5/2015 (v2.21.0) *//* +/*! Widget: stickyHeaders - updated 3/26/2015 (v2.21.3) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -1772,7 +1791,7 @@ ts.addWidget({ nestedStickyTop = $nestedSticky.length ? $nestedSticky.height() : 0, // clone table, then wrap to make sticky header $stickyTable = wo.$sticky = $table.clone() - .addClass('containsStickyHeaders ' + ts.css.sticky + ' ' + wo.stickyHeaders) + .addClass('containsStickyHeaders ' + ts.css.sticky + ' ' + wo.stickyHeaders + ' ' + c.namespace.slice(1) + '_extra_table' ) .wrap('<div class="' + ts.css.stickyWrap + '">'), $stickyWrap = $stickyTable.parent() .addClass(ts.css.stickyHide) @@ -1830,13 +1849,6 @@ ts.addWidget({ if ($attach.length && !$attach.css('position')) { $attach.css('position', 'relative'); } - // save stickyTable element to config - // it is also saved to wo.$sticky - if (c.$extraTables && c.$extraTables.length) { - c.$extraTables.add($stickyTable); - } else { - c.$extraTables = $stickyTable; - } // fix clone ID, if it exists - fixes #271 if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; } // clear out cloned table, except for sticky header @@ -1856,7 +1868,7 @@ ts.addWidget({ resizeHeader(); }); - ts.bindEvents(table, $stickyThead.children().children('.tablesorter-header')); + ts.bindEvents(table, $stickyThead.children().children('.' + ts.css.header)); // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. $table.after( $stickyWrap ); @@ -1954,182 +1966,320 @@ ts.addWidget({ })(jQuery, window); -/*! Widget: resizable */ +/*! Widget: resizable - updated 3/26/2015 (v2.21.3) */ ;(function ($, window) { 'use strict'; var ts = $.tablesorter = $.tablesorter || {}; $.extend(ts.css, { - resizer : 'tablesorter-resizer' // resizable + resizableContainer : 'tablesorter-resizable-container', + resizableHandle : 'tablesorter-resizable-handle', + resizableNoSelect : 'tablesorter-disableSelection', + resizableStorage : 'tablesorter-resizable' }); -// this widget saves the column widths if -// $.tablesorter.storage function is included -// ************************** -ts.addWidget({ - id: "resizable", - priority: 40, - options: { - resizable : true, - resizable_addLastColumn : false, - resizable_widths : [], - resizable_throttle : false // set to true (5ms) or any number 0-10 range +// Add extra scroller css +$(function(){ + var s = '<style>' + + 'body.' + ts.css.resizableNoSelect + ' { -ms-user-select: none; -moz-user-select: -moz-none;' + + '-khtml-user-select: none; -webkit-user-select: none; user-select: none; }' + + '.' + ts.css.resizableContainer + ' { position: relative; height: 1px; }' + + // make handle z-index > than stickyHeader z-index, so the handle stays above sticky header + '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px; top: 1px;' + + 'cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' + + '</style>'; + $(s).appendTo('body'); +}); + +ts.resizable = { + init : function( c, wo ) { + if ( c.$table.hasClass( 'hasResizable' ) ) { return; } + c.$table.addClass( 'hasResizable' ); + ts.resizableReset( c.table, true ); // set default widths + + // internal variables + wo.resizable_ = { + $wrap : c.$table.parent(), + mouseXPosition : 0, + $target : null, + $next : null, + overflow : c.$table.parent().css('overflow') === 'auto', + fullWidth : Math.abs(c.$table.parent().width() - c.$table.width()) < 20, + storedSizes : [] + }; + + var noResize, $header, column, storedSizes, + marginTop = parseInt( c.$table.css( 'margin-top' ), 10 ); + + wo.resizable_.storedSizes = storedSizes = ( ( ts.storage && wo.resizable !== false ) ? + ts.storage( c.table, ts.css.resizableStorage ) : + [] ) || []; + ts.resizable.setWidths( c, wo, storedSizes ); + + wo.$resizable_container = $( '<div class="' + ts.css.resizableContainer + '">' ) + .css({ top : marginTop }) + .insertBefore( c.$table ); + // add container + for ( column = 0; column < c.columns; column++ ) { + $header = c.$headerIndexed[ column ]; + noResize = ts.getData( $header, ts.getColumnData( c.table, c.headers, column ), 'resizable' ) === 'false'; + if ( !noResize ) { + $( '<div class="' + ts.css.resizableHandle + '">' ) + .appendTo( wo.$resizable_container ) + .attr({ + 'data-column' : column, + 'unselectable' : 'on' + }) + .data( 'header', $header ) + .bind( 'selectstart', false ); + } + } + c.$table.one('tablesorter-initialized', function() { + ts.resizable.setHandlePosition( c, wo ); + ts.resizable.bindings( this.config, this.config.widgetOptions ); + }); }, - format: function(table, c, wo) { - if (c.$table.hasClass('hasResizable')) { return; } - c.$table.addClass('hasResizable'); - ts.resizableReset(table, true); // set default widths - var $rows, $columns, $column, column, timer, - storedSizes = {}, - $table = c.$table, - $wrap = $table.parent(), - overflow = $table.parent().css('overflow') === 'auto', - mouseXPosition = 0, - $target = null, - $next = null, - fullWidth = Math.abs($table.parent().width() - $table.width()) < 20, - mouseMove = function(event){ - if (mouseXPosition === 0 || !$target) { return; } - // resize columns - var leftEdge = event.pageX - mouseXPosition, - targetWidth = $target.width(); - $target.width( targetWidth + leftEdge ); - if ($target.width() !== targetWidth && fullWidth) { - $next.width( $next.width() - leftEdge ); - } else if (overflow) { - $table.width(function(i, w){ - return w + leftEdge; - }); - if (!$next.length) { - // if expanding right-most column, scroll the wrapper - $wrap[0].scrollLeft = $table.width(); - } - } - mouseXPosition = event.pageX; - }, - stopResize = function() { - if (ts.storage && $target && $next) { - storedSizes = {}; - storedSizes[$target.index()] = $target.width(); - storedSizes[$next.index()] = $next.width(); - $target.width( storedSizes[$target.index()] ); - $next.width( storedSizes[$next.index()] ); - if (wo.resizable !== false) { - // save all column widths - ts.storage(table, 'tablesorter-resizable', c.$headers.map(function(){ return $(this).width(); }).get() ); - } - } - mouseXPosition = 0; - $target = $next = null; - $(window).trigger('resize'); // will update stickyHeaders, just in case - }; - storedSizes = (ts.storage && wo.resizable !== false) ? ts.storage(table, 'tablesorter-resizable') : {}; + + setWidth : function( $el, width ) { + $el.css({ + 'width' : width, + 'min-width' : '', + 'max-width' : '' + }); + }, + + setWidths : function( c, wo, storedSizes ) { + var column, + $extra = $( c.namespace + '_extra_headers' ), + $col = c.$table.children( 'colgroup' ).children( 'col' ); + storedSizes = storedSizes || wo.resizable_.storedSizes || []; // process only if table ID or url match - if (storedSizes) { - for (column in storedSizes) { - if (!isNaN(column) && column < c.$headers.length) { - c.$headers.eq(column).width(storedSizes[column]); // set saved resizable widths + if ( storedSizes.length ) { + for ( column = 0; column < c.columns; column++ ) { + // set saved resizable widths + c.$headers.eq( column ).width( storedSizes[ column ] ); + if ( $extra.length ) { + // stickyHeaders needs to modify min & max width as well + ts.resizable.setWidth( $extra.eq( column ).add( $col.eq( column ) ), storedSizes[ column ] ); } } + if ( $( c.namespace + '_extra_table' ).length && !ts.hasWidget( c.table, 'scroller' ) ) { + ts.resizable.setWidth( $( c.namespace + '_extra_table' ), c.$table.outerWidth() ); + } } - $rows = $table.children('thead:first').children('tr'); - // add resizable-false class name to headers (across rows as needed) - $rows.children().each(function() { - var canResize, - $column = $(this); - column = $column.attr('data-column'); - canResize = ts.getData( $column, ts.getColumnData( table, c.headers, column ), 'resizable') === "false"; - $rows.children().filter('[data-column="' + column + '"]')[canResize ? 'addClass' : 'removeClass']('resizable-false'); - }); - // add wrapper inside each cell to allow for positioning of the resizable target block - $rows.each(function() { - $column = $(this).children().not('.resizable-false'); - if (!$(this).find('.' + ts.css.wrapper).length) { - // Firefox needs this inner div to position the resizer correctly - $column.wrapInner('<div class="' + ts.css.wrapper + '" style="position:relative;height:100%;width:100%"></div>'); + }, + + setHandlePosition : function( c, wo ) { + var tableWidth = c.$table.outerWidth(), + hasScroller = ts.hasWidget( c.table, 'scroller' ), + tableHeight = c.$table.height(), + $handles = wo.$resizable_container.children(), + handleCenter = Math.floor( $handles.width() / 2 - parseFloat( c.$headers.css( 'border-right-width' ) ) * 2 ); + + if ( hasScroller ) { + tableHeight = 0; + c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function(){ + var $this = $(this); + // center table has a max-height set + tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); + }); + } + $handles.each( function() { + var $this = $(this), + column = parseInt( $this.attr( 'data-column' ), 10 ), + columns = c.columns - 1, + $header = $this.data( 'header' ); + if ( column < columns || column === columns && wo.resizable_addLastColumn ) { + $this.css({ + height : tableHeight, + left : $header.position().left + $header.width() - handleCenter + }); } - // don't include the last column of the row - if (!wo.resizable_addLastColumn) { $column = $column.slice(0,-1); } - $columns = $columns ? $columns.add($column) : $column; }); - $columns - .each(function() { - var $column = $(this), - padding = parseInt($column.css('padding-right'), 10) + 10; // 10 is 1/2 of the 20px wide resizer - $column - .find('.' + ts.css.wrapper) - .append('<div class="' + ts.css.resizer + '" style="cursor:w-resize;position:absolute;z-index:1;right:-' + - padding + 'px;top:0;height:100%;width:20px;"></div>'); - }) - .find('.' + ts.css.resizer) - .bind('mousedown', function(event) { + }, + + // prevent text selection while dragging resize bar + toggleTextSelection : function( c, toggle ) { + var namespace = c.namespace + 'tsresize'; + c.widgetOptions.resizable_.disabled = toggle; + $( 'body' ).toggleClass( ts.css.resizableNoSelect, toggle ); + if ( toggle ) { + $( 'body' ) + .attr( 'unselectable', 'on' ) + .bind( 'selectstart' + namespace, false ); + } else { + $( 'body' ) + .removeAttr( 'unselectable' ) + .unbind( 'selectstart' + namespace ); + } + }, + + bindings : function( c, wo ) { + var namespace = c.namespace + 'tsresize'; + wo.$resizable_container.children().bind( 'mousedown', function( event ) { // save header cell and mouse position - $target = $(event.target).closest('th'); - var $header = c.$headers.filter('[data-column="' + $target.attr('data-column') + '"]'); - if ($header.length > 1) { $target = $target.add($header); } + var column, + vars = wo.resizable_, + $extras = $( c.namespace + '_extra_headers' ), + $header = $( event.target ).data( 'header' ); + + column = parseInt( $header.attr( 'data-column' ), 10 ); + vars.$target = $header = $header.add( $extras.filter('[data-column="' + column + '"]') ); + vars.target = column; + // if table is not as wide as it's parent, then resize the table - $next = event.shiftKey ? $target.parent().find('th').not('.resizable-false').filter(':last') : $target.nextAll(':not(.resizable-false)').eq(0); - mouseXPosition = event.pageX; + vars.$next = event.shiftKey || wo.resizable_targetLast ? + $header.parent().children().not( '.resizable-false' ).filter( ':last' ) : + $header.nextAll( ':not(.resizable-false)' ).eq( 0 ); + + column = parseInt( vars.$next.attr( 'data-column' ), 10 ); + vars.$next = vars.$next.add( $extras.filter('[data-column="' + column + '"]') ); + vars.next = column; + + vars.mouseXPosition = event.pageX; + vars.storedSizes = c.$headers.map(function(){ return $(this).width(); }).get(); + ts.resizable.toggleTextSelection( c, true ); }); - $(document) - .bind('mousemove.tsresize', function(event) { - // ignore mousemove if no mousedown - if (mouseXPosition === 0 || !$target) { return; } - if (wo.resizable_throttle) { - clearTimeout(timer); - timer = setTimeout(function(){ - mouseMove(event); - }, isNaN(wo.resizable_throttle) ? 5 : wo.resizable_throttle ); - } else { - mouseMove(event); - } - }) - .bind('mouseup.tsresize', function() { - stopResize(); + + $( document ) + .bind( 'mousemove' + namespace, function( event ) { + var vars = wo.resizable_; + // ignore mousemove if no mousedown + if ( !vars.disabled || vars.mouseXPosition === 0 || !vars.$target ) { return; } + if ( wo.resizable_throttle ) { + clearTimeout( vars.timer ); + vars.timer = setTimeout( function() { + ts.resizable.mouseMove( c, wo, event ); + }, isNaN( wo.resizable_throttle ) ? 5 : wo.resizable_throttle ); + } else { + ts.resizable.mouseMove( c, wo, event ); + } + }) + .bind( 'mouseup' + namespace, function() { + if (!wo.resizable_.disabled) { return; } + ts.resizable.toggleTextSelection( c, false ); + ts.resizable.stopResize( c, wo ); + ts.resizable.setHandlePosition( c, wo ); + }); + + // resizeEnd event triggered by scroller widget + $( window ).bind( 'resize' + namespace + ' resizeEnd' + namespace, function() { + ts.resizable.setHandlePosition( c, wo ); }); // right click to reset columns to default widths - $table.find('thead:first').bind('contextmenu.tsresize', function() { - ts.resizableReset(table); + c.$table.find( 'thead:first' ).add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) + .bind( 'contextmenu' + namespace, function() { // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset - var allowClick = $.isEmptyObject ? $.isEmptyObject(storedSizes) : true; - storedSizes = {}; + var allowClick = wo.resizable_.storedSizes.length === 0; + ts.resizableReset( c.table ); + ts.resizable.setHandlePosition( c, wo ); + wo.resizable_.storedSizes = []; return allowClick; }); + }, - remove: function(table, c) { - c.$table - .removeClass('hasResizable') - .children('thead') - .unbind('mouseup.tsresize mouseleave.tsresize contextmenu.tsresize') - .children('tr').children() - .unbind('mousemove.tsresize mouseup.tsresize') - // don't remove "tablesorter-wrapper" as uitheme uses it too - .find('.' + ts.css.resizer).remove(); - ts.resizableReset(table); + + mouseMove : function( c, wo, event ) { + if ( wo.resizable_.mouseXPosition === 0 || !wo.resizable_.$target ) { return; } + // resize columns + var vars = wo.resizable_, + $target = vars.$target, + $next = vars.$next, + leftEdge = event.pageX - vars.mouseXPosition, + targetWidth = $target.width(); + if ( vars.fullWidth ) { + vars.storedSizes[ vars.target ] += leftEdge; + vars.storedSizes[ vars.next ] -= leftEdge; + ts.resizable.setWidths( c, wo ); + + } else if ( vars.overflow ) { + c.$table.add( $( c.namespace + '_extra_table' ) ).width(function(i, w){ + return w + leftEdge; + }); + if ( !$next.length ) { + // if expanding right-most column, scroll the wrapper + vars.$wrap[0].scrollLeft = c.$table.width(); + } + } else { + vars.storedSizes[ vars.target ] += leftEdge; + ts.resizable.setWidths( c, wo ); + } + vars.mouseXPosition = event.pageX; + }, + + stopResize : function( c, wo ) { + var vars = wo.resizable_; + vars.storedSizes = []; + if ( ts.storage ) { + vars.storedSizes = c.$headers.map(function(){ return $(this).width(); }).get(); + if ( wo.resizable !== false ) { + // save all column widths + ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); + } + } + vars.mouseXPosition = 0; + vars.$target = vars.$next = null; + $(window).trigger('resize'); // will update stickyHeaders, just in case + } +}; + +// this widget saves the column widths if +// $.tablesorter.storage function is included +// ************************** +ts.addWidget({ + id: "resizable", + priority: 40, + options: { + resizable : true, + resizable_addLastColumn : false, + resizable_widths : [], + resizable_throttle : false, // set to true (5ms) or any number 0-10 range + resizable_targetLast : false + }, + init: function(table, thisWidget, c, wo) { + ts.resizable.init( c, wo ); + }, + remove: function( table, c, wo ) { + if (wo.$resizable_container) { + var namespace = c.namespace + 'tsresize'; + c.$table.add( $( c.namespace + '_extra_table' ) ) + .removeClass('hasResizable') + .children( 'thead' ).unbind( 'contextmenu' + namespace ); + + wo.$resizable_container.remove(); + ts.resizable.toggleTextSelection( c, false ); + ts.resizableReset( table ); + $( document ).unbind( 'mousemove' + namespace + ' mouseup' + namespace ); + } } }); -ts.resizableReset = function(table, nosave) { - $(table).each(function(){ + +ts.resizableReset = function( table, nosave ) { + $( table ).each(function(){ var $t, c = this.config, wo = c && c.widgetOptions; - if (table && c) { - c.$headers.each(function(i){ + if ( table && c ) { + c.$headers.each( function( i ) { $t = $(this); - if (wo.resizable_widths && wo.resizable_widths[i]) { - $t.css('width', wo.resizable_widths[i]); - } else if (!$t.hasClass('resizable-false')) { + if ( wo.resizable_widths && wo.resizable_widths[ i ] ) { + $t.css( 'width', wo.resizable_widths[ i ] ); + } else if ( !$t.hasClass( 'resizable-false' ) ) { // don't clear the width of any column that is not resizable - $t.css('width',''); + $t.css( 'width', '' ); } }); - if (ts.storage && !nosave) { ts.storage(this, 'tablesorter-resizable', {}); } + // reset stickyHeader widths + $( window ).trigger( 'resize' ); + if ( ts.storage && !nosave ) { + ts.storage( this, ts.css.resizableStorage, {} ); + } } }); }; -})(jQuery, window); +})( jQuery, window ); /*! Widget: saveSort */ ;(function ($) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index a83c9a4..20ca159 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,5 +1,5 @@ -/*! input & select parsers for jQuery 1.7+ & tablesorter 2.7.11+ - * Updated 3/5/2015 (v2.21.0) +/*! parser: input & select - updated 3/26/2015 (v2.21.3) *//* + * for jQuery 1.7+ & tablesorter 2.7.11+ * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ /*jshint browser: true, jquery:true, unused:false */ @@ -111,7 +111,7 @@ // bind to .tablesorter (default class name) $(this).children('tbody') .on('mouseleave', function(e){ - restoreValue(e.target.tagName === 'TBODY'); + restoreValue(e.target.nodeName === 'TBODY'); }) .on('focus', 'select, input, textarea', function(){ $(this).data('ts-original-value', this.value); @@ -129,7 +129,7 @@ } // Update cell cache using... select: change, input: enter or textarea: alt + enter if ( ( e.type === 'change' ) || - ( e.type === 'keyup' && e.which === 13 && ( e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' && e.altKey ) ) ) { + ( e.type === 'keyup' && e.which === 13 && ( e.target.nodeName === 'INPUT' || e.target.nodeName === 'TEXTAREA' && e.altKey ) ) ) { var undef, $tar = $(e.target), $cell = $tar.closest('td'), diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js index 046a52f..04fa1cf 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js @@ -1,4 +1,5 @@ -/*! Build Table widget for tableSorter v2.16.0; updated 2/7/2015 (v2.19.0) +/*! Widget: Build Table - updated 3/26/2015 (v2.21.3) *//* + * for tableSorter v2.16.0+ * by Rob Garrison */ /*jshint browser:true, jquery:true, unused:false */ @@ -12,7 +13,7 @@ var ts = $.tablesorter = $.tablesorter || {}, // data.rows contains an array of rows which contains an array of cells bt = ts.buildTable = function(tar, c){ // add table if one doesn't exist - var $tbl = tar.tagName === 'TABLE' ? $(tar) : $('<table>').appendTo(tar), + var $tbl = tar.nodeName === 'TABLE' ? $(tar) : $('<table>').appendTo(tar), table = $tbl[0], wo = c.widgetOptions = $.extend( true, {}, bt.defaults, c.widgetOptions ), p = wo.build_processing, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js index 8556599..c9bfc85 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js @@ -1,4 +1,4 @@ -/*! Filter widget select2 formatter function - updated 2/7/2015 (v2.19.0) +/*! Widget: Filter formatter function select2 - updated 3/26/2015 (v2.21.3) *//* * requires: jQuery 1.7.2+, tableSorter (FORK) 2.16+, filter widget 2.16+ and select2 v3.4.6+ plugin */ /*jshint browser:true, jquery:true, unused:false */ @@ -52,7 +52,7 @@ ts.filterFormatter.select2 = function($cell, indx, select2Def) { v = v.join('\u0000'); } // escape special regex characters (http://stackoverflow.com/a/9310752/145346) - v = v.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); + v = v.replace(/[-[\]{}()*+?.,/\\^$|#\s]/g, '\\$&'); // convert string back into an array if (arry) { v = v.split('\u0000'); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 787f241..e16a5a1 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,14 +1,17 @@ -/*! Widget: filter - updated 3/5/2015 (v2.21.0) *//* +/*! Widget: filter - updated 3/26/2015 (v2.21.3) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ ;(function ($) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; +var ts = $.tablesorter = $.tablesorter || {}, + tscss = ts.css; -$.extend(ts.css, { - filterRow : 'tablesorter-filter-row', - filter : 'tablesorter-filter' +$.extend(tscss, { + filterRow : 'tablesorter-filter-row', + filter : 'tablesorter-filter', + filterDisabled : 'disabled', + filterRowHide : 'hideme' }); ts.addWidget({ @@ -58,7 +61,7 @@ ts.addWidget({ // add .tsfilter namespace to all BUT search .unbind( events.replace(/\s+/g, ' ') ) // remove the filter row even if refreshing, because the column might have been moved - .find('.' + ts.css.filterRow).remove(); + .find('.' + tscss.filterRow).remove(); if (refreshing) { return; } for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody @@ -286,13 +289,13 @@ ts.filter = { c.$table.bind( txt, function(event, filter) { val = (wo.filter_hideEmpty && $.isEmptyObject(c.cache) && !(c.delayInit && event.type === 'appendCache')); // hide filter row using the "filtered" class name - c.$table.find('.' + ts.css.filterRow).toggleClass(wo.filter_filteredRow, val ); // fixes #450 + c.$table.find('.' + tscss.filterRow).toggleClass(wo.filter_filteredRow, val ); // fixes #450 if ( !/(search|filter)/.test(event.type) ) { event.stopPropagation(); ts.filter.buildDefault(table, true); } if (event.type === 'filterReset') { - c.$table.find('.' + ts.css.filter).add(wo.filter_$externalFilters).val(''); + c.$table.find('.' + tscss.filter).add(wo.filter_$externalFilters).val(''); ts.filter.searching(table, []); } else if (event.type === 'filterEnd') { ts.filter.buildDefault(table, true); @@ -355,7 +358,7 @@ ts.filter = { options += '<option ' + (txt === val ? '' : 'data-function-name="' + string + '" ') + 'value="' + val + '">' + txt + '</option>'; } } - c.$table.find('thead').find('select.' + ts.css.filter + '[data-column="' + column + '"]').append(options); + c.$table.find('thead').find('select.' + tscss.filter + '[data-column="' + column + '"]').append(options); } } } @@ -364,7 +367,7 @@ ts.filter = { // it would append the same options twice. ts.filter.buildDefault(table, true); - ts.filter.bindSearch( table, c.$table.find('.' + ts.css.filter), true ); + ts.filter.bindSearch( table, c.$table.find('.' + tscss.filter), true ); if (wo.filter_external) { ts.filter.bindSearch( table, wo.filter_external ); } @@ -379,7 +382,7 @@ ts.filter = { .unbind( ('filterStart filterEnd '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') ) .bind( 'filterStart filterEnd '.split(' ').join(c.namespace + 'filter '), function(event, columns) { // only add processing to certain columns to all columns - $header = (columns) ? c.$table.find('.' + ts.css.header).filter('[data-column]').filter(function() { + $header = (columns) ? c.$table.find('.' + tscss.header).filter('[data-column]').filter(function() { return columns[$(this).data('column')] !== ''; }) : ''; ts.isProcessing(table, event.type === 'filterStart', columns ? $header : ''); @@ -493,7 +496,7 @@ ts.filter = { // c.columns defined in computeThIndexes() columns = c.columns, arry = $.isArray(wo.filter_cellFilter), - buildFilter = '<tr role="row" class="' + ts.css.filterRow + '">'; + buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; for (column = 0; column < columns; column++) { if (arry) { buildFilter += '<td' + ( wo.filter_cellFilter[column] ? ' class="' + wo.filter_cellFilter[column] + '"' : '' ) + '></td>'; @@ -542,9 +545,9 @@ ts.filter = { name = ( $.isArray(wo.filter_cssFilter) ? (typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '') : wo.filter_cssFilter ) || ''; - buildFilter.addClass( ts.css.filter + ' ' + name ).attr('data-column', column); + buildFilter.addClass( tscss.filter + ' ' + name ).attr('data-column', column); if (disabled) { - buildFilter.attr('placeholder', '').addClass('disabled')[0].disabled = true; // disabled! + buildFilter.attr('placeholder', '').addClass(tscss.filterDisabled)[0].disabled = true; // disabled! } } } @@ -638,7 +641,7 @@ ts.filter = { } if (wo.filter_hideFilters) { // show/hide filter row as needed - c.$table.find('.' + ts.css.filterRow).trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); + c.$table.find('.' + tscss.filterRow).trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons @@ -664,8 +667,8 @@ ts.filter = { hideFilters: function(table, c) { var $filterRow, $filterRow2, timer; $(table) - .find('.' + ts.css.filterRow) - .addClass('hideme') + .find('.' + tscss.filterRow) + .addClass(tscss.filterRowHide) .bind('mouseenter mouseleave', function(e) { // save event object - http://bugs.jquery.com/ticket/12140 var event = e; @@ -673,14 +676,14 @@ ts.filter = { clearTimeout(timer); timer = setTimeout(function() { if ( /enter|over/.test(event.type) ) { - $filterRow.removeClass('hideme'); + $filterRow.removeClass(tscss.filterRowHide); } else { // don't hide if input has focus // $(':focus') needs jQuery 1.6+ if ( $(document.activeElement).closest('tr')[0] !== $filterRow[0] ) { // don't hide row if any filter has a value if (c.lastCombinedFilter === '') { - $filterRow.addClass('hideme'); + $filterRow.addClass(tscss.filterRowHide); } } } @@ -693,7 +696,7 @@ ts.filter = { timer = setTimeout(function() { // don't hide row if any filter has a value if (ts.getFilters(c.$table).join('') === '') { - $filterRow2[ event.type === 'focus' ? 'removeClass' : 'addClass']('hideme'); + $filterRow2[ event.type === 'focus' ? 'removeClass' : 'addClass'](tscss.filterRowHide); } }, 200); }); @@ -1181,7 +1184,7 @@ ts.filter = { // t.data('placeholder') won't work in jQuery older than 1.4.3 options = '<option value="">' + ( node.data('placeholder') || node.attr('data-placeholder') || wo.filter_placeholder.select || '' ) + '</option>', // Get curent filter value - currentValue = c.$table.find('thead').find('select.' + ts.css.filter + '[data-column="' + column + '"]').val(); + currentValue = c.$table.find('thead').find('select.' + tscss.filter + '[data-column="' + column + '"]').val(); // nothing included in arry (external source), so get the options from filter_selectSource or column data if (typeof arry === 'undefined' || arry === '') { arry = ts.filter.getOptionSource(table, column, onlyAvail); @@ -1208,7 +1211,7 @@ ts.filter = { } // update all selects in the same column (clone thead in sticky headers & any external selects) - fixes 473 - $filters = ( c.$filters ? c.$filters : c.$table.children('thead') ).find('.' + ts.css.filter); + $filters = ( c.$filters ? c.$filters : c.$table.children('thead') ).find('.' + tscss.filter); if (wo.filter_$externalFilters) { $filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters; } @@ -1252,7 +1255,7 @@ ts.getFilters = function(table, getRaw, setFilters, skipFirst) { } if (c) { if (c.$filters) { - $filters = c.$filters.find('.' + ts.css.filter); + $filters = c.$filters.find('.' + tscss.filter); } if (wo.filter_$externalFilters) { $filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index 350df88..ceeb4ab 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -1,4 +1,4 @@ -/* Output widget for TableSorter 3/5/2015 (v2.21.0) +/*! Widget: Output - updated 3/26/2015 (v2.21.3) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * Modified from: * HTML Table to CSV: http://www.kunalbabre.com/projects/table2CSV.php (License unknown?) @@ -160,7 +160,7 @@ output = ts.output = { // callback; if true returned, continue processing if ($.isFunction(wo.output_callback) && !wo.output_callback(c, mydata)) { return; } - if ( /p/.test( (wo.output_delivery || '').toLowerCase() ) ) { + if ( /p/i.test( wo.output_delivery || '' ) ) { output.popup(mydata, wo.output_popupStyle, outputJSON || outputArray); } else { output.download(wo, mydata); @@ -314,7 +314,6 @@ ts.addWidget({ output_callbackJSON : function($cell, txt, cellIndex) { return txt + '(' + (cellIndex) + ')'; }, // the need to modify this for Excel no longer exists output_encoding : 'data:application/octet-stream;charset=utf8,' - }, init: function(table, thisWidget, c) { output.init(c); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 8d1db11..98c2a91 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,7 @@ -/* Pager widget for TableSorter 3/5/2015 (v2.21.0) - requires jQuery 1.7+ */ +/*! Widget: Pager - updated 3/26/2015 (v2.21.3) */ +/* Requires tablesorter v2.8+ and jQuery 1.7+ + * by Rob Garrison + */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ "use strict"; @@ -143,7 +146,7 @@ tsp = ts.pager = { setSize: wo.pager_size, setPage: wo.pager_startPage, events: 'filterInit filterStart filterEnd sortEnd disable enable destroy updateComplete ' + - 'pageSize pageSet pageAndSize pagerUpdate ' + 'pageSize pageSet pageAndSize pagerUpdate refreshComplete ' }, c.pager); // pager initializes multiple times before table has completed initialization @@ -296,9 +299,12 @@ tsp = ts.pager = { }) .on('pageSet.pager pagerUpdate.pager', function(e,v){ e.stopPropagation(); - p.page = (parseInt(v, 10) || 1) - 1; // force pager refresh - if (e.type === 'pagerUpdate') { p.last.page = true; } + if (e.type === 'pagerUpdate') { + v = typeof v === 'undefined' ? p.page + 1 : v; + p.last.page = true; + } + p.page = (parseInt(v, 10) || 1) - 1; tsp.moveToPage(table, p, true); tsp.updatePageDisplay(table, c, false); }) @@ -454,7 +460,7 @@ tsp = ts.pager = { p.$goto.html(t).val( p.page + 1 ); } if ($out.length) { - $out[ ($out[0].tagName === 'INPUT') ? 'val' : 'html' ](s); + $out[ ($out[0].nodeName === 'INPUT') ? 'val' : 'html' ](s); // rebind startRow/page inputs $out.find('.ts-startRow, .ts-page').off('change.pager').on('change.pager', function(){ var v = $(this).val(), diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js index fd8e8fc..8ca49a0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js @@ -1,176 +1,314 @@ -/*! Widget: resizable */ +/*! Widget: resizable - updated 3/26/2015 (v2.21.3) */ ;(function ($, window) { 'use strict'; var ts = $.tablesorter = $.tablesorter || {}; $.extend(ts.css, { - resizer : 'tablesorter-resizer' // resizable + resizableContainer : 'tablesorter-resizable-container', + resizableHandle : 'tablesorter-resizable-handle', + resizableNoSelect : 'tablesorter-disableSelection', + resizableStorage : 'tablesorter-resizable' }); -// this widget saves the column widths if -// $.tablesorter.storage function is included -// ************************** -ts.addWidget({ - id: "resizable", - priority: 40, - options: { - resizable : true, - resizable_addLastColumn : false, - resizable_widths : [], - resizable_throttle : false // set to true (5ms) or any number 0-10 range +// Add extra scroller css +$(function(){ + var s = '<style>' + + 'body.' + ts.css.resizableNoSelect + ' { -ms-user-select: none; -moz-user-select: -moz-none;' + + '-khtml-user-select: none; -webkit-user-select: none; user-select: none; }' + + '.' + ts.css.resizableContainer + ' { position: relative; height: 1px; }' + + // make handle z-index > than stickyHeader z-index, so the handle stays above sticky header + '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px; top: 1px;' + + 'cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' + + '</style>'; + $(s).appendTo('body'); +}); + +ts.resizable = { + init : function( c, wo ) { + if ( c.$table.hasClass( 'hasResizable' ) ) { return; } + c.$table.addClass( 'hasResizable' ); + ts.resizableReset( c.table, true ); // set default widths + + // internal variables + wo.resizable_ = { + $wrap : c.$table.parent(), + mouseXPosition : 0, + $target : null, + $next : null, + overflow : c.$table.parent().css('overflow') === 'auto', + fullWidth : Math.abs(c.$table.parent().width() - c.$table.width()) < 20, + storedSizes : [] + }; + + var noResize, $header, column, storedSizes, + marginTop = parseInt( c.$table.css( 'margin-top' ), 10 ); + + wo.resizable_.storedSizes = storedSizes = ( ( ts.storage && wo.resizable !== false ) ? + ts.storage( c.table, ts.css.resizableStorage ) : + [] ) || []; + ts.resizable.setWidths( c, wo, storedSizes ); + + wo.$resizable_container = $( '<div class="' + ts.css.resizableContainer + '">' ) + .css({ top : marginTop }) + .insertBefore( c.$table ); + // add container + for ( column = 0; column < c.columns; column++ ) { + $header = c.$headerIndexed[ column ]; + noResize = ts.getData( $header, ts.getColumnData( c.table, c.headers, column ), 'resizable' ) === 'false'; + if ( !noResize ) { + $( '<div class="' + ts.css.resizableHandle + '">' ) + .appendTo( wo.$resizable_container ) + .attr({ + 'data-column' : column, + 'unselectable' : 'on' + }) + .data( 'header', $header ) + .bind( 'selectstart', false ); + } + } + c.$table.one('tablesorter-initialized', function() { + ts.resizable.setHandlePosition( c, wo ); + ts.resizable.bindings( this.config, this.config.widgetOptions ); + }); }, - format: function(table, c, wo) { - if (c.$table.hasClass('hasResizable')) { return; } - c.$table.addClass('hasResizable'); - ts.resizableReset(table, true); // set default widths - var $rows, $columns, $column, column, timer, - storedSizes = {}, - $table = c.$table, - $wrap = $table.parent(), - overflow = $table.parent().css('overflow') === 'auto', - mouseXPosition = 0, - $target = null, - $next = null, - fullWidth = Math.abs($table.parent().width() - $table.width()) < 20, - mouseMove = function(event){ - if (mouseXPosition === 0 || !$target) { return; } - // resize columns - var leftEdge = event.pageX - mouseXPosition, - targetWidth = $target.width(); - $target.width( targetWidth + leftEdge ); - if ($target.width() !== targetWidth && fullWidth) { - $next.width( $next.width() - leftEdge ); - } else if (overflow) { - $table.width(function(i, w){ - return w + leftEdge; - }); - if (!$next.length) { - // if expanding right-most column, scroll the wrapper - $wrap[0].scrollLeft = $table.width(); - } - } - mouseXPosition = event.pageX; - }, - stopResize = function() { - if (ts.storage && $target && $next) { - storedSizes = {}; - storedSizes[$target.index()] = $target.width(); - storedSizes[$next.index()] = $next.width(); - $target.width( storedSizes[$target.index()] ); - $next.width( storedSizes[$next.index()] ); - if (wo.resizable !== false) { - // save all column widths - ts.storage(table, 'tablesorter-resizable', c.$headers.map(function(){ return $(this).width(); }).get() ); - } - } - mouseXPosition = 0; - $target = $next = null; - $(window).trigger('resize'); // will update stickyHeaders, just in case - }; - storedSizes = (ts.storage && wo.resizable !== false) ? ts.storage(table, 'tablesorter-resizable') : {}; + + setWidth : function( $el, width ) { + $el.css({ + 'width' : width, + 'min-width' : '', + 'max-width' : '' + }); + }, + + setWidths : function( c, wo, storedSizes ) { + var column, + $extra = $( c.namespace + '_extra_headers' ), + $col = c.$table.children( 'colgroup' ).children( 'col' ); + storedSizes = storedSizes || wo.resizable_.storedSizes || []; // process only if table ID or url match - if (storedSizes) { - for (column in storedSizes) { - if (!isNaN(column) && column < c.$headers.length) { - c.$headers.eq(column).width(storedSizes[column]); // set saved resizable widths + if ( storedSizes.length ) { + for ( column = 0; column < c.columns; column++ ) { + // set saved resizable widths + c.$headers.eq( column ).width( storedSizes[ column ] ); + if ( $extra.length ) { + // stickyHeaders needs to modify min & max width as well + ts.resizable.setWidth( $extra.eq( column ).add( $col.eq( column ) ), storedSizes[ column ] ); } } + if ( $( c.namespace + '_extra_table' ).length && !ts.hasWidget( c.table, 'scroller' ) ) { + ts.resizable.setWidth( $( c.namespace + '_extra_table' ), c.$table.outerWidth() ); + } } - $rows = $table.children('thead:first').children('tr'); - // add resizable-false class name to headers (across rows as needed) - $rows.children().each(function() { - var canResize, - $column = $(this); - column = $column.attr('data-column'); - canResize = ts.getData( $column, ts.getColumnData( table, c.headers, column ), 'resizable') === "false"; - $rows.children().filter('[data-column="' + column + '"]')[canResize ? 'addClass' : 'removeClass']('resizable-false'); - }); - // add wrapper inside each cell to allow for positioning of the resizable target block - $rows.each(function() { - $column = $(this).children().not('.resizable-false'); - if (!$(this).find('.' + ts.css.wrapper).length) { - // Firefox needs this inner div to position the resizer correctly - $column.wrapInner('<div class="' + ts.css.wrapper + '" style="position:relative;height:100%;width:100%"></div>'); + }, + + setHandlePosition : function( c, wo ) { + var tableWidth = c.$table.outerWidth(), + hasScroller = ts.hasWidget( c.table, 'scroller' ), + tableHeight = c.$table.height(), + $handles = wo.$resizable_container.children(), + handleCenter = Math.floor( $handles.width() / 2 - parseFloat( c.$headers.css( 'border-right-width' ) ) * 2 ); + + if ( hasScroller ) { + tableHeight = 0; + c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function(){ + var $this = $(this); + // center table has a max-height set + tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); + }); + } + $handles.each( function() { + var $this = $(this), + column = parseInt( $this.attr( 'data-column' ), 10 ), + columns = c.columns - 1, + $header = $this.data( 'header' ); + if ( column < columns || column === columns && wo.resizable_addLastColumn ) { + $this.css({ + height : tableHeight, + left : $header.position().left + $header.width() - handleCenter + }); } - // don't include the last column of the row - if (!wo.resizable_addLastColumn) { $column = $column.slice(0,-1); } - $columns = $columns ? $columns.add($column) : $column; }); - $columns - .each(function() { - var $column = $(this), - padding = parseInt($column.css('padding-right'), 10) + 10; // 10 is 1/2 of the 20px wide resizer - $column - .find('.' + ts.css.wrapper) - .append('<div class="' + ts.css.resizer + '" style="cursor:w-resize;position:absolute;z-index:1;right:-' + - padding + 'px;top:0;height:100%;width:20px;"></div>'); - }) - .find('.' + ts.css.resizer) - .bind('mousedown', function(event) { + }, + + // prevent text selection while dragging resize bar + toggleTextSelection : function( c, toggle ) { + var namespace = c.namespace + 'tsresize'; + c.widgetOptions.resizable_.disabled = toggle; + $( 'body' ).toggleClass( ts.css.resizableNoSelect, toggle ); + if ( toggle ) { + $( 'body' ) + .attr( 'unselectable', 'on' ) + .bind( 'selectstart' + namespace, false ); + } else { + $( 'body' ) + .removeAttr( 'unselectable' ) + .unbind( 'selectstart' + namespace ); + } + }, + + bindings : function( c, wo ) { + var namespace = c.namespace + 'tsresize'; + wo.$resizable_container.children().bind( 'mousedown', function( event ) { // save header cell and mouse position - $target = $(event.target).closest('th'); - var $header = c.$headers.filter('[data-column="' + $target.attr('data-column') + '"]'); - if ($header.length > 1) { $target = $target.add($header); } + var column, + vars = wo.resizable_, + $extras = $( c.namespace + '_extra_headers' ), + $header = $( event.target ).data( 'header' ); + + column = parseInt( $header.attr( 'data-column' ), 10 ); + vars.$target = $header = $header.add( $extras.filter('[data-column="' + column + '"]') ); + vars.target = column; + // if table is not as wide as it's parent, then resize the table - $next = event.shiftKey ? $target.parent().find('th').not('.resizable-false').filter(':last') : $target.nextAll(':not(.resizable-false)').eq(0); - mouseXPosition = event.pageX; + vars.$next = event.shiftKey || wo.resizable_targetLast ? + $header.parent().children().not( '.resizable-false' ).filter( ':last' ) : + $header.nextAll( ':not(.resizable-false)' ).eq( 0 ); + + column = parseInt( vars.$next.attr( 'data-column' ), 10 ); + vars.$next = vars.$next.add( $extras.filter('[data-column="' + column + '"]') ); + vars.next = column; + + vars.mouseXPosition = event.pageX; + vars.storedSizes = c.$headers.map(function(){ return $(this).width(); }).get(); + ts.resizable.toggleTextSelection( c, true ); }); - $(document) - .bind('mousemove.tsresize', function(event) { - // ignore mousemove if no mousedown - if (mouseXPosition === 0 || !$target) { return; } - if (wo.resizable_throttle) { - clearTimeout(timer); - timer = setTimeout(function(){ - mouseMove(event); - }, isNaN(wo.resizable_throttle) ? 5 : wo.resizable_throttle ); - } else { - mouseMove(event); - } - }) - .bind('mouseup.tsresize', function() { - stopResize(); + + $( document ) + .bind( 'mousemove' + namespace, function( event ) { + var vars = wo.resizable_; + // ignore mousemove if no mousedown + if ( !vars.disabled || vars.mouseXPosition === 0 || !vars.$target ) { return; } + if ( wo.resizable_throttle ) { + clearTimeout( vars.timer ); + vars.timer = setTimeout( function() { + ts.resizable.mouseMove( c, wo, event ); + }, isNaN( wo.resizable_throttle ) ? 5 : wo.resizable_throttle ); + } else { + ts.resizable.mouseMove( c, wo, event ); + } + }) + .bind( 'mouseup' + namespace, function() { + if (!wo.resizable_.disabled) { return; } + ts.resizable.toggleTextSelection( c, false ); + ts.resizable.stopResize( c, wo ); + ts.resizable.setHandlePosition( c, wo ); + }); + + // resizeEnd event triggered by scroller widget + $( window ).bind( 'resize' + namespace + ' resizeEnd' + namespace, function() { + ts.resizable.setHandlePosition( c, wo ); }); // right click to reset columns to default widths - $table.find('thead:first').bind('contextmenu.tsresize', function() { - ts.resizableReset(table); + c.$table.find( 'thead:first' ).add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) + .bind( 'contextmenu' + namespace, function() { // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset - var allowClick = $.isEmptyObject ? $.isEmptyObject(storedSizes) : true; - storedSizes = {}; + var allowClick = wo.resizable_.storedSizes.length === 0; + ts.resizableReset( c.table ); + ts.resizable.setHandlePosition( c, wo ); + wo.resizable_.storedSizes = []; return allowClick; }); + }, - remove: function(table, c) { - c.$table - .removeClass('hasResizable') - .children('thead') - .unbind('mouseup.tsresize mouseleave.tsresize contextmenu.tsresize') - .children('tr').children() - .unbind('mousemove.tsresize mouseup.tsresize') - // don't remove "tablesorter-wrapper" as uitheme uses it too - .find('.' + ts.css.resizer).remove(); - ts.resizableReset(table); + + mouseMove : function( c, wo, event ) { + if ( wo.resizable_.mouseXPosition === 0 || !wo.resizable_.$target ) { return; } + // resize columns + var vars = wo.resizable_, + $target = vars.$target, + $next = vars.$next, + leftEdge = event.pageX - vars.mouseXPosition, + targetWidth = $target.width(); + if ( vars.fullWidth ) { + vars.storedSizes[ vars.target ] += leftEdge; + vars.storedSizes[ vars.next ] -= leftEdge; + ts.resizable.setWidths( c, wo ); + + } else if ( vars.overflow ) { + c.$table.add( $( c.namespace + '_extra_table' ) ).width(function(i, w){ + return w + leftEdge; + }); + if ( !$next.length ) { + // if expanding right-most column, scroll the wrapper + vars.$wrap[0].scrollLeft = c.$table.width(); + } + } else { + vars.storedSizes[ vars.target ] += leftEdge; + ts.resizable.setWidths( c, wo ); + } + vars.mouseXPosition = event.pageX; + }, + + stopResize : function( c, wo ) { + var vars = wo.resizable_; + vars.storedSizes = []; + if ( ts.storage ) { + vars.storedSizes = c.$headers.map(function(){ return $(this).width(); }).get(); + if ( wo.resizable !== false ) { + // save all column widths + ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); + } + } + vars.mouseXPosition = 0; + vars.$target = vars.$next = null; + $(window).trigger('resize'); // will update stickyHeaders, just in case + } +}; + +// this widget saves the column widths if +// $.tablesorter.storage function is included +// ************************** +ts.addWidget({ + id: "resizable", + priority: 40, + options: { + resizable : true, + resizable_addLastColumn : false, + resizable_widths : [], + resizable_throttle : false, // set to true (5ms) or any number 0-10 range + resizable_targetLast : false + }, + init: function(table, thisWidget, c, wo) { + ts.resizable.init( c, wo ); + }, + remove: function( table, c, wo ) { + if (wo.$resizable_container) { + var namespace = c.namespace + 'tsresize'; + c.$table.add( $( c.namespace + '_extra_table' ) ) + .removeClass('hasResizable') + .children( 'thead' ).unbind( 'contextmenu' + namespace ); + + wo.$resizable_container.remove(); + ts.resizable.toggleTextSelection( c, false ); + ts.resizableReset( table ); + $( document ).unbind( 'mousemove' + namespace + ' mouseup' + namespace ); + } } }); -ts.resizableReset = function(table, nosave) { - $(table).each(function(){ + +ts.resizableReset = function( table, nosave ) { + $( table ).each(function(){ var $t, c = this.config, wo = c && c.widgetOptions; - if (table && c) { - c.$headers.each(function(i){ + if ( table && c ) { + c.$headers.each( function( i ) { $t = $(this); - if (wo.resizable_widths && wo.resizable_widths[i]) { - $t.css('width', wo.resizable_widths[i]); - } else if (!$t.hasClass('resizable-false')) { + if ( wo.resizable_widths && wo.resizable_widths[ i ] ) { + $t.css( 'width', wo.resizable_widths[ i ] ); + } else if ( !$t.hasClass( 'resizable-false' ) ) { // don't clear the width of any column that is not resizable - $t.css('width',''); + $t.css( 'width', '' ); } }); - if (ts.storage && !nosave) { ts.storage(this, 'tablesorter-resizable', {}); } + // reset stickyHeader widths + $( window ).trigger( 'resize' ); + if ( ts.storage && !nosave ) { + ts.storage( this, ts.css.resizableStorage, {} ); + } } }); }; -})(jQuery, window); +})( jQuery, window ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index 9ded668..af69784 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -1,12 +1,13 @@ -/*! +/*! Widget: scroller - updated 3/26/2015 (v2.21.3) *//* Copyright (C) 2011 T. Connell & Associates, Inc. Dual-licensed under the MIT and GPL licenses - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE - FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Resizable scroller widget for the jQuery tablesorter plugin @@ -32,261 +33,583 @@ Website: www.tconnell.com */ /*jshint browser:true, jquery:true, unused:false */ -;(function($, window){ -"use strict"; +;( function( $, window ) { +'use strict'; + +var ts = $.tablesorter, + tscss = ts.css; + +$.extend( ts.css, { + scrollerWrap : 'tablesorter-scroller', + scrollerHeader : 'tablesorter-scroller-header', + scrollerTable : 'tablesorter-scroller-table', + scrollerFooter : 'tablesorter-scroller-footer', + scrollerFixed : 'tablesorter-scroller-fixed', + scrollerHasFix : 'tablesorter-scroller-has-fixed-columns', + scrollerReset : 'tablesorter-scroller-reset', + scrollerRtl : 'tablesorter-scroller-rtl' +}); -$.fn.hasScrollBar = function(){ - return this.get(0).scrollHeight > this.height(); -}; -var ts = $.tablesorter; +ts.addWidget({ + id : 'scroller', + priority : 60, // run after the filter widget + options : { + scroller_height : 300, + scroller_jumpToHeader : true, + scroller_upAfterSort : true, + // set number of columns to fix + scroller_fixedColumns : 0, + // add hover highlighting to the fixed column (disable if it causes slowing) + scroller_rowHighlight : 'hover', + // bar width is now calculated; set a value to override + scroller_barWidth : null + }, + format: function( table, c, wo ) { + if ( !c.isScrolling ) { + // initialize here instead of in widget init to give the + // filter widget time to finish building the filter row + ts.scroller.setup( c, wo ); + } + }, + remove : function( table, c, wo ) { + ts.scroller.remove( c, wo ); + } +}); -ts.window_resize = function(){ - if (this.resize_timer) { - clearTimeout(this.resize_timer); +/* Add window resizeEnd event */ +ts.window_resize = function() { + if ( this.resize_timer ) { + clearTimeout( this.resize_timer ); } - this.resize_timer = setTimeout(function(){ - $(this).trigger('resizeEnd'); - }, 250); + this.resize_timer = setTimeout( function() { + $( this ).trigger( 'resizeEnd' ); + }, 250 ); }; // Add extra scroller css -$(function(){ - var s = '<style>' + - '.tablesorter-scrollbar-measure { width: 100px; height: 100px; overflow: scroll; position: absolute; top: -9999px; } ' + - '.tablesorter-scroller-reset { width: auto !important; } ' + - '.tablesorter-scroller { text-align: left; overflow: hidden; }' + - '.tablesorter-scroller-header, .tablesorter-scroller-footer { overflow: hidden; }' + - '.tablesorter-scroller-header table.tablesorter { margin-bottom: 0; }' + - '.tablesorter-scroller-footer table.tablesorter thead { visibility: hidden, height: 0; overflow: hidden; }' + - '.tablesorter-scroller-table { overflow-y: scroll; }' + - '.tablesorter-scroller-table table.tablesorter { margin-top: 0; margin-bottom: 0; overflow: scroll; } ' + - '.tablesorter-scroller-table .tablesorter-filter-row,.tablesorter-scroller-footer .tablesorter-filter-row,.tablesorter-scroller-table tfoot { display: none; }' + - '.tablesorter-scroller-table table.tablesorter thead tr.tablesorter-headerRow *,.tablesorter-scroller-footer table.tablesorter thead * {' + - 'line-height:0;height:0;border:none;background-image:none;padding-top:0;padding-bottom:0;margin-top:0;margin-bottom:0;overflow:hidden;' + - '}</style>'; - $(s).appendTo('body'); +$( function() { + var style = '<style>' + + /* measure scroll bar width */ + '.' + tscss.scrollerWrap + 'Measure { width: 100px; height: 100px; overflow: scroll; position: absolute; top: -9999px; }' + + /* reset width to get accurate measurements after window resize */ + '.' + tscss.scrollerReset + ' { width: auto !important; min-width: auto !important; max-width: auto !important; }' + + /* overall wrapper & table section wrappers */ + '.' + tscss.scrollerWrap + ' { position: relative; overflow: hidden; }' + + '.' + tscss.scrollerHeader + ', .' + tscss.scrollerFooter + ' { overflow: hidden; }' + + '.' + tscss.scrollerHeader + ' table.' + tscss.table + ' { margin-bottom: 0; }' + + '.' + tscss.scrollerFooter + ' table.' + tscss.table + ' thead { visibility: hidden, height: 0; overflow: hidden; }' + + /* always leave the scroll bar visible for tbody, or table overflows into the scrollbar when height < max height (filtering) */ + '.' + tscss.scrollerTable + ' { overflow-y: scroll; }' + + '.' + tscss.scrollerTable + ' table.' + tscss.table + ' { border-top: 0; margin-top: 0; margin-bottom: 0; overflow-y: scroll; }' + + /* hide filter row in clones */ + '.' + tscss.scrollerTable + ' .' + ( tscss.filterRow || 'tablesorter-filter-row' ) + ',.' + tscss.scrollerFooter + ' .' + + ( tscss.filterRow || 'tablesorter-filter-row' ) + ',.' + tscss.scrollerTable + ' tfoot { display: none; }' + + '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixed + ' { position: absolute; top: 0; z-index: 1; left: 0 } ' + + '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixed + '.' + tscss.scrollerRtl + ' { left: auto; right: 0 } ' + + /* visibly hide header rows in clones, so we can still set a width on it and still effect the rest of the column */ + '.' + tscss.scrollerTable + ' table.' + tscss.table + ' thead tr.' + tscss.headerRow + ' *, .' + tscss.scrollerFooter + + ' table.' + tscss.table + ' thead * { line-height: 0; height: 0; border: none; background-image: none; padding-top: 0;' + + ' padding-bottom: 0; margin-top: 0; margin-bottom: 0; overflow: hidden; }' + + + /*** fixed column ***/ + '.' + tscss.scrollerFixed + ' { pointer-events: none; }' + + /* add horizontal scroll bar */ + '.' + tscss.scrollerWrap + '.' + tscss.scrollerHasFix + ' > .' + tscss.scrollerTable + ' { overflow-x: scroll; }' + + /* need to position the tbody & tfoot absolutely to hide the scrollbar & move the footer below the horizontal scrollbar */ + '.' + tscss.scrollerFixed + ' .' + tscss.scrollerFooter + ' { position: absolute; bottom: 0; }' + + /* hide fixed tbody scrollbar - see http://goo.gl/VsLe6n */ + '.' + tscss.scrollerFixed + ' .' + tscss.scrollerTable + ' { position: relative; left: 0; overflow-x: hidden; overflow-y: scroll; -ms-overflow-style: none; }' + + '.' + tscss.scrollerFixed + ' .' + tscss.scrollerTable + '::-webkit-scrollbar { display: none; }' + + /* remove right border of fixed header tables to hide the boundary */ + '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixed + ' table { border-right-color: transparent; padding-right: 0; }' + + '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixed + '.' + tscss.scrollerRtl + ' table { border-left-color: transparent; padding-left: 0; }' + + '</style>'; + $( style ).appendTo( 'body' ); }); -ts.addWidget({ - id: 'scroller', - priority: 60, // run after the filter widget - options: { - scroller_height : 300, - scroller_jumpToHeader: true, - scroller_upAfterSort: true, - // bar width is now calculated; set a value to override - scroller_barWidth : null +ts.scroller = { + + // Ugh.. Firefox misbehaves, so it needs to be detected + isFirefox : navigator.userAgent.toLowerCase().indexOf( 'firefox' ) > -1, + + hasScrollBar : function( $target ) { + return $target.get(0).scrollHeight > $target.height(); }, - init: function(table, thisWidget, c, wo){ - var $win = $(window), - namespace = c.namespace + 'tsscroller'; + + setWidth : function( $el, width ) { + $el.css({ + 'width' : width, + 'min-width' : width, + 'max-width' : width + }); + }, + + // modified from http://davidwalsh.name/detect-scrollbar-width + getBarWidth : function() { + var $scrollDiv = $( '<div class="' + tscss.scrollerWrap + 'Measure">' ).appendTo( 'body' ), + div = $scrollDiv[ 0 ], + barWidth = div.offsetWidth - div.clientWidth; + $scrollDiv.remove(); + return barWidth; + }, + + setup : function( c, wo ) { + var maxHt, tbHt, $hdr, $t, $hCells, $fCells, $tableWrap, + $win = $( window ), + namespace = c.namespace + 'tsscroller', + $foot = $(), + // c.namespace contains a unique tablesorter ID, per table + id = c.namespace.slice( 1 ) + 'tsscroller', + $table = c.$table; + + // force developer to set fixedWidth to maintain column widths + c.widthFixed = true; + maxHt = wo.scroller_height || 300; + tbHt = $table.children( 'tbody' ).height(); + if ( tbHt !== 0 && maxHt > tbHt ) { maxHt = tbHt + 10; } // Table is less than h px + + wo.scroller_$header = $hdr = $( '<table class="' + $table.attr( 'class' ) + '" cellpadding=0 cellspacing=0>' + + $table.children( 'thead' )[0].outerHTML + + '</table>' ) + .addClass( c.namespace.slice(1) + '_extra_table' ); + + $t = $table.children( 'tfoot' ); + if ( $t.length ) { + $foot = $( '<table class="' + $table.attr('class') + '" cellpadding=0 cellspacing=0 style="margin-top:0"></table>' ) + .addClass( c.namespace.slice(1) + '_extra_table' ) + .append( $t.clone( true ) ) // maintain any bindings on the tfoot cells + .append( $table.children( 'thead' )[ 0 ].outerHTML ) + .wrap( '<div class="' + tscss.scrollerFooter + '"/>' ); + $fCells = $foot.children( 'tfoot' ).eq( 0 ).children( 'tr' ).children(); + } + wo.scroller_$footer = $foot; + + $table + .wrap( '<div id="' + id + '" class="' + tscss.scrollerWrap + '" />' ) + .before( $hdr ) + // shrink filter row but don't completely hide it because the inputs/selectors may distort the columns + .find( '.' + tscss.filterRow ).addClass( tscss.filterRowHide ); + + wo.scroller_$container = $table.parent(); + + if ( $foot.length ) { + // $foot.parent() to include <div> wrapper + $table.after( $foot.parent() ); + } + + $hCells = $hdr + .wrap( '<div class="' + tscss.scrollerHeader + '" />' ) + .find( '.' + tscss.header ); + + // use max-height, so the height resizes dynamically while filtering + $table.wrap( '<div class="' + tscss.scrollerTable + '" style="max-height:' + maxHt + 'px;" />' ); + $tableWrap = $table.parent(); + + // make scroller header sortable + ts.bindEvents( c.table, $hCells ); + + // look for filter widget + if ( $table.hasClass( 'hasFilters' ) ) { + ts.filter.bindSearch( $table, $hdr.find('.' + tscss.filter) ); + } + + // remove any previous fixed columns ( in case we're updating ) + wo.scroller_$container.find( '.' + tscss.scrollerFixed ).remove(); + + if ( wo.scroller_fixedColumns > 0 ) { + ts.scroller.setupFixed( c, wo ); + } + + ts.scroller.resize( c, wo ); + + $table.find( 'thead' ).css( 'visibility', 'hidden' ); + + tbHt = $tableWrap.parent().height(); + + // The header will always jump into view if scrolling the table body + $tableWrap + .off( 'scroll' + namespace ) + .on( 'scroll' + namespace, function() { + if ( wo.scroller_jumpToHeader ) { + var pos = $win.scrollTop() - $hdr.offset().top; + if ( $( this ).scrollTop() !== 0 && pos < tbHt && pos > 0 ) { + $win.scrollTop( $hdr.offset().top ); + } + } + $hdr.parent().add( $foot.parent() ).scrollLeft( $( this ).scrollLeft() ); + }); + + // Sorting, so scroll to top + $table + .off( 'sortEnd' + namespace + ' setFixedColumnSize' + namespace ) + .on( 'sortEnd' + namespace, function() { + if ( wo.scroller_upAfterSort ) { + $table.parent().animate({ scrollTop: 0 }, 'fast' ); + } + }) + .on( 'setFixedColumnSize' + namespace, function( event, size ) { + if ( typeof size !== 'undefined' && !isNaN( size ) ) { + wo.scroller_fixedColumns = parseInt( size, 10 ); + } + // remove fixed columns + wo.scroller_$container.find( '.' + tscss.scrollerFixed ).remove(); + if ( size > 0 && size < c.columns - 1 ) { + ts.scroller.setupFixed( c, wo ); + } else { + wo.scroller_$container.removeClass( tscss.scrollerHasFix ); + } + }); + // Setup window.resizeEnd event $win - .bind('resize' + namespace, ts.window_resize) - .bind('resizeEnd' + namespace, function() { - // init is run before format, so scroller_resizeWidth - // won't be defined within the "c" or "wo" parameters - if ($.isFunction(table.config.widgetOptions.scroller_resizeWidth)) { - // IE calls resize when you modify content, so we have to unbind the resize event - // so we don't end up with an infinite loop. we can rebind after we're done. - $win.unbind('resize' + namespace, ts.window_resize); - table.config.widgetOptions.scroller_resizeWidth(); - $win.bind('resize' + namespace, ts.window_resize); - } + .off( 'resize resizeEnd '.split( ' ' ).join( namespace + ' ' ) ) + .on( 'resize' + namespace, ts.window_resize ) + .on( 'resizeEnd' + namespace, function() { + // IE calls resize when you modify content, so we have to unbind the resize event + // so we don't end up with an infinite loop. we can rebind after we're done. + $win.off( 'resize' + namespace, ts.window_resize ); + ts.scroller.resize( c, wo ); + $win.on( 'resize' + namespace, ts.window_resize ); }); + + // initialization flag + c.isScrolling = true; + }, - format: function(table, c, wo) { - var maxHt, tbHt, $hdr, $t, resize, getBarWidth, $hCells, $fCells, $tblWrap, - $ft = $(), - // c.namespace contains a unique tablesorter ID, per table - id = c.namespace.slice(1) + 'tsscroller', - $win = $(window), - $tbl = c.$table; - - if (!c.isScrolling) { - // force developer to set fixedWidth to maintain column widths - c.widthFixed = true; - maxHt = wo.scroller_height || 300; - tbHt = $tbl.children('tbody').height(); - if (tbHt !== 0 && maxHt > tbHt) { maxHt = tbHt + 10; } // Table is less than h px - - $hdr = $('<table class="' + $tbl.attr('class') + '" cellpadding=0 cellspacing=0>' + - $tbl.children('thead')[0].outerHTML + - '</table>'); - - $t = $tbl.children('tfoot'); - if ($t.length) { - $ft = $('<table class="' + $tbl.attr('class') + '" cellpadding=0 cellspacing=0 style="margin-top:0"></table>') - .append( $t.clone(true) ) // maintain any bindings on the tfoot cells - .append( $tbl.children('thead')[0].outerHTML ) - .wrap('<div class="tablesorter-scroller-footer"/>'); - $fCells = $ft.children('tfoot').eq(0).children('tr').children(); - } - if (c.$extraTables && c.$extraTables.length) { - c.$extraTables = c.$extraTables.add($hdr).add($ft); + resize : function( c, wo ) { + var index, borderWidth, setWidth, $hCells, $bCells, $fCells, $headers, $this, + $table = c.$table, + $tableWrap = $table.parent(), + $hdr = wo.scroller_$header, + $foot = wo.scroller_$footer, + id = c.namespace.slice( 1 ) + 'tsscroller', + // Hide other scrollers so we can resize + $div = $( 'div.' + tscss.scrollerWrap + '[id != "' + id + '"]' ).hide(); + + $table.children( 'thead' ).show(); + // only remove colgroup if it was added by the plugin + // the $.tablesorter.fixColumnWidth() function already does this (v2.19.0) + // but we need to get "accurate" resized measurements here - see issue #680 + $table.add( $hdr ).add( $foot ).children( 'colgroup' ).remove(); + + // Reset sizes so parent can resize. + $table + .addClass( tscss.scrollerReset ) + .children( 'thead' ) + .find( '.' + tscss.headerIn ).addClass( tscss.scrollerReset ).end() + .find( '.' + tscss.filterRow ).show(); + $tableWrap.addClass( tscss.scrollerReset ); + + // include left & right border widths + borderWidth = parseInt( $table.css( 'border-left-width' ), 10 ); + + // Shrink a bit to accommodate scrollbar + wo.scroller_barSetWidth = ( wo.scroller_barWidth || ts.scroller.getBarWidth() || 18 ) + borderWidth; + + $tableWrap.width( $tableWrap.parent().innerWidth() - ( ts.scroller.hasScrollBar( $tableWrap.parent() ) ? wo.scroller_barSetWidth : 0 ) ); + setWidth = $tableWrap.innerWidth() - ( ts.scroller.hasScrollBar( $tableWrap ) ? wo.scroller_barSetWidth : 0 ) + borderWidth; + $hdr.parent().add( $foot.parent() ).width( setWidth ); + + $hCells = $hdr.children( 'thead' ).children().children( 'th, td' ).filter( ':visible' ); + $bCells = $table.children('tbody').eq( 0 ).children().eq( 0 ).children( 'th, td' ).filter( ':visible' ); + $fCells = $foot.children( 'tfoot' ).children().children( 'th, td' ).filter( ':visible' ); + + ts.scroller.setWidth( $hCells.add( $bCells ).add( $fCells ), '' ); + $headers = $table.children( 'thead' ).children().eq( 0 ).children( 'th, td' ); + for ( index = 0; index < $headers.length; index++ ) { + $this = $headers.eq( index ); + // code from https://github.com/jmosbech/StickyTableHeaders + if ( $this.css( 'box-sizing' ) === 'border-box' ) { + setWidth = $this.outerWidth(); } else { - c.$extraTables = $hdr.add($ft); + if ( $hCells.eq( index ).css( 'border-collapse' ) === 'collapse' ) { + if ( $this.length && window.getComputedStyle ) { + setWidth = parseFloat( window.getComputedStyle( $this[0], null ).width ); + } else { + // ie8 only + borderWidth = parseFloat( $this.css( 'border-width' ) ) || 0; + setWidth = $this.outerWidth() - parseFloat( $this.css( 'padding-left' ) ) - parseFloat( $this.css( 'padding-right' ) ) - borderWidth; + } + } else { + setWidth = $this.width(); + } } - $tbl - .wrap('<div id="' + id + '" class="tablesorter-scroller" />') - .before($hdr) - // shrink filter row but don't completely hide it because the inputs/selectors may distort the columns - .find('.tablesorter-filter-row').addClass('hideme'); - - if ($ft.length) { - // $ft.parent() to include <div> wrapper - $tbl.after( $ft.parent() ); + ts.scroller.setWidth( $hCells.eq( index ).add( $bCells.eq( index ) ).add( $fCells.eq( index ) ), setWidth ); + } + + wo.scroller_$container + .find( '.' + tscss.scrollerReset ) + .removeClass( tscss.scrollerReset ); + + // refresh colgroup & copy to cloned header + ts.fixColumnWidth( c.table ); + + // add colgroup to all clones + $hCells = $table.children( 'colgroup' ); + if ( $hCells.length ) { + $bCells = $hCells[0].outerHTML; + $hdr.prepend( $bCells ); + if ( $foot.length ) { + $foot.prepend( $bCells ); } + } - $hCells = $hdr - .wrap('<div class="tablesorter-scroller-header" />') - .find('.' + ts.css.header); + // update fixed column sizes + if ( wo.scroller_fixedColumns > 0 ) { + ts.scroller.updateFixed( c, wo, true ); + } - // use max-height, so the height resizes dynamically while filtering - $tbl.wrap('<div class="tablesorter-scroller-table" style="max-height:' + maxHt + 'px;" />'); - $tblWrap = $tbl.parent(); + // hide filter row because filterEnd event fires + $table.children( 'thead' ).find( '.' + tscss.filterRow ).hide(); - // make scroller header sortable - ts.bindEvents(table, $hCells); + $div.show(); - // look for filter widget - if ($tbl.hasClass('hasFilters')) { - ts.filter.bindSearch( $tbl, $hdr.find('.' + ts.css.filter) ); - } + }, - // modified from http://davidwalsh.name/detect-scrollbar-width - getBarWidth = function(){ - var $scrollDiv = $('<div class="tablesorter-scrollbar-measure">').appendTo('body'), - div = $scrollDiv[0], - barWidth = div.offsetWidth - div.clientWidth; - $scrollDiv.remove(); - return barWidth; - }; - - resize = function(){ - var b, $h, $f, w, - // Hide other scrollers so we can resize - $div = $('div.tablesorter-scroller[id != "' + id + '"]').hide(); - - $tbl.children('thead').show(); - // only remove colgroup if it was added by the plugin - // the $.tablesorter.fixColumnWidth() function already does this (v2.19.0) - // but we need to get "accurate" resized measurements here - see issue #680 - $tbl.add( $hdr ).add( $ft ).children('colgroup').remove(); - - // Reset sizes so parent can resize. - $tbl - .addClass('tablesorter-scroller-reset') - .children('thead') - .find('.tablesorter-header-inner').addClass('tablesorter-scroller-reset').end() - .find('.tablesorter-filter-row').show(); - $tblWrap.addClass('tablesorter-scroller-reset'); - - $tblWrap.parent().trigger('resize'); - - // include left & right border widths - b = parseInt( $tbl.css('border-left-width'), 10 ) + parseInt( $tbl.css('border-right-width'), 10 ); - - // Shrink a bit to accommodate scrollbar - w = ( wo.scroller_barWidth || getBarWidth() ) + b; - - $tblWrap.width( $tblWrap.parent().innerWidth() - ( $tblWrap.parent().hasScrollBar() ? w : 0 ) ); - w = $tblWrap.innerWidth() - ( $tblWrap.hasScrollBar() ? w : 0 ); - $hdr.parent().add( $ft.parent() ).width( w ); - - w = $tbl.width(); - - $h = $hdr.children('thead').children().children('th, td').filter(':visible'); - $f = $ft.children('tfoot').children().children('th, td').filter(':visible'); - $tbl.children('thead').children().eq(0).children('th, td').each(function(indx, el) { - var width, border, - $this = $(this); - // code from https://github.com/jmosbech/StickyTableHeaders - if ($this.css('box-sizing') === 'border-box') { - width = $this.outerWidth(); - } else { - if ($h.eq(indx).css('border-collapse') === 'collapse') { - if (window.getComputedStyle) { - width = parseFloat( window.getComputedStyle(this, null).width ); - } else { - // ie8 only - border = parseFloat( $this.css('border-width') ); - width = $this.outerWidth() - parseFloat( $this.css('padding-left') ) - parseFloat( $this.css('padding-right') ) - border; - } - } else { - width = $this.width(); - } - } - $h.eq(indx).add( $f.eq(indx) ).css({ - 'min-width': width, - 'max-width': width - }); - }); + // Add fixed (frozen) columns + setupFixed : function( c, wo ) { + var index, index2, $el, len, temp, $fixedColumn, $fixedTbody, $fixedContainer, + $table = c.$table, + namespace = c.namespace + 'tsscrollerFixed', + $wrapper = wo.scroller_$container, + fixedColumns = wo.scroller_fixedColumns; + + $fixedColumn = $wrapper + .addClass( tscss.scrollerHasFix ) + .clone() + .addClass( tscss.scrollerFixed ) + .removeClass( tscss.scrollerWrap ) + .attr( 'id', '' ); + $fixedTbody = $fixedColumn.find( '.' + tscss.scrollerTable ); + $fixedTbody.find( 'table' ) + .addClass( c.namespace.slice(1) + '_extra_table' ) + .attr( 'id', '' ); + $fixedContainer = $fixedTbody.find( 'tbody' ); + + wo.scroller_$fixedColumns = $fixedColumn; + + // RTL support (fixes column on right) + if ( $table.hasClass( tscss.scrollerRtl ) ) { + $fixedColumn.addClass( tscss.scrollerRtl ); + } - $tbl - .closest('.tablesorter-scroller') - .find('.tablesorter-scroller-reset') - .removeClass('tablesorter-scroller-reset'); - - // refresh colgroup & copy to cloned header - $.tablesorter.fixColumnWidth( table ); - $h = $tbl.children('colgroup'); - if ($h.length) { - b = $h[0].outerHTML; - $hdr.prepend(b); - if ($ft.length) { - $ft.prepend(b); - } + $el = $fixedColumn.find( 'tr' ); + len = $el.length; + for ( index = 0; index < len; index++ ) { + $el.eq( index ).children( ':gt(' + ( fixedColumns - 1 ) + ')' ).remove(); + } + $fixedColumn.hide().prependTo( $wrapper ); + + // look for filter widget + if ( c.$table.hasClass( 'hasFilters' ) ) { + ts.filter.bindSearch( $table, $fixedColumn.find( '.' + tscss.filter ) ); + // disable/enable filters behind fixed column + $el = $wrapper.children( '.' + tscss.scrollerHeader ).find( '.' + tscss.filter ); + len = $el.length; + for ( index = 0; index < len; index++ ) { + // previously disabled filter; don't mess with it! filterDisabled class added by filter widget + if ( !$el.eq( index ).hasClass( tscss.filterDisabled || 'disabled' ) ) { + // disable filters behind fixed column; don't disable visible filters + $el.eq( index ).prop( 'disabled', index < fixedColumns ); } + } + // enable visible fixed column filters + $fixedColumn.children( '.' + tscss.scrollerHeader ).find( '.' + tscss.filter ).css( 'pointer-events', 'all' ); + } - // hide filter row because filterEnd event fires - $tbl.children('thead').find('.tablesorter-filter-row').hide(); + // disable/enable tab indexes behind fixed column + c.$table.children( 'thead' ).children( 'tr.' + tscss.headerRow ).children().attr( 'tabindex', -1 ); + $el = wo.scroller_$header + .add( $fixedColumn.find( '.' + tscss.scrollerTable + ' table, .' + tscss.scrollerFooter + ' table' ) ) + .children( 'thead' ).children( 'tr.' + tscss.headerRow ); + len = $el.length; + for ( index = 0; index < len; index++ ) { + temp = $el.eq( index ).children(); + for ( index2 = 0; index2 < temp.length; index2++ ) { + temp.eq( index2 ).attr( 'tabindex', index2 < fixedColumns ? -1 : 0 ); + } + } - $div.show(); - }; + ts.bindEvents( c.table, $fixedColumn.find( '.' + tscss.header ) ); + + // update thead & tbody in fixed column + temp = ( 'sortEnd filterEnd ' ).split( ' ' ).join( namespace + ' ' ); + c.$table + .off( temp ) + .on( temp, function( event, size ) { + ts.scroller.updateFixed( c, wo, false ); + }) + .parent() + // *** SCROLL *** scroll fixed column along with main + .off( 'scroll' + namespace ) + .on( 'scroll' + namespace, function() { + $fixedTbody.scrollTop( $( this ).scrollTop() ); + }); + // scroll main along with fixed column + $fixedTbody + .off( 'scroll' + namespace ) + .on( 'scroll' + namespace, function() { + c.$table.parent().scrollTop( $fixedTbody.scrollTop() ); + }) + .scroll(); + + // *** ROW HIGHLIGHT *** + if ( wo.scroller_rowHighlight !== '' ) { + temp = 'mouseover mouseleave '.split( ' ' ).join( namespace + ' ' ); + c.$table + .off( temp, 'tr' ) + .on( temp, 'tr', function( event ) { + var indx = $( this ).index(); + $fixedContainer.children().eq( indx ) + .add( this ) + .toggleClass( wo.scroller_rowHighlight, event.type === 'mouseover' ); + }); + $fixedTbody.find( 'table' ) + .off( temp, 'tr' ) + .on( temp, 'tr', function( event ) { + var indx = $( this ).index(); + c.$tbodies.children().eq( indx ) + .add( this ) + .toggleClass( wo.scroller_rowHighlight, event.type === 'mouseover' ); + }); + } - // Expose to external calls - wo.scroller_resizeWidth = resize; + /*** STUPID FIREFOX HACK! Since we can't hide the scrollbar with css ***/ + if ( ts.scroller.isFirefox ) { + $fixedTbody.wrap( '<div class="scroller-firefox-hack" style="overflow:hidden;">' ); + } - resize(); + ts.scroller.updateFixed( c, wo, true ); - $tbl.find('thead').css('visibility', 'hidden'); - c.isScrolling = true; + }, - tbHt = $tblWrap.parent().height(); + updateFixed : function( c, wo ) { + if ( !c.isScrolling ) { return; } + + // no idea why this happens, but sometimes the main table wrapper gets the scrollbar width + // subtracted from it on load and on theme change - it can be very sporatic; this fixes it. + c.$table.parent().width( wo.scroller_$container.width() ); + + // scroller_fixedColumns + var index, tbodyIndex, rowIndex, $tbody, $adjCol, $fb, totalRows, widths, + $table = c.$table, + $wrapper = wo.scroller_$container, + + // source cells for measurement + $mainTbodies = wo.scroller_$container.children( '.' + tscss.scrollerTable ).children( 'table' ).children( 'tbody' ), + $rows = wo.scroller_$header.children( 'thead' ).children( '.' + tscss.headerRow ), // variable gets redefined + + // hide fixed column during resize, or we get a FOUC + $fixedColumn = wo.scroller_$fixedColumns.hide(), + + // target cells + $fixedTbodiesTable = $fixedColumn.find( '.' + tscss.scrollerTable ).children( 'table' ), + $fixedTbodies = $fixedTbodiesTable.children( 'tbody' ), + $fixedHeader = $fixedColumn.find( '.' + tscss.scrollerHeader ).children( 'table' ).children( 'thead' ), + // variables + isFirefox = ts.scroller.isFirefox, + scrollBarWidth = wo.scroller_barSetWidth, + fixedColumns = wo.scroller_fixedColumns, + // get dimensions + $temp = $table.find( 'tbody td' ), + borderRightWidth = parseInt( $temp.css( 'border-right-width' ), 10 ) || 1, + borderBottomWidth = parseInt( $temp.css( 'border-bottom-width' ), 10 ) || 1, + borderSpacing = parseInt( $temp.css( 'border-spacing' ).split( /\s/ )[ 0 ], 10 ) / 2 || 0, + totalWidth = parseInt( $table.css( 'padding-left' ), 10 ) + parseInt( $table.css( 'padding-right' ), 10 ) - borderRightWidth; + + // fixed header cell height + $temp = $fixedHeader.children( '.' + tscss.headerRow ); + for ( index = 0; index < $temp.length; index++ ) { + $temp.eq( index ).height( $rows.eq( index ).outerHeight() ); + } - // The header will always jump into view if scrolling the table body - $tblWrap.bind('scroll', function(){ - if (wo.scroller_jumpToHeader) { - var pos = $win.scrollTop() - $hdr.offset().top; - if ($(this).scrollTop() !== 0 && pos < tbHt && pos > 0) { - $win.scrollTop( $hdr.offset().top ); + // body cell dimensions seem to be more accurate *shrug* + $rows = ( c.filteredRows > 0 ? c.$tbodies : $table.children( 'thead' ) ).children( 'tr:visible' ); + // recalculate widths + widths = $rows.children( ':lt(' + fixedColumns + ')' ).map( function() { + totalWidth += $( this ).outerWidth() + borderSpacing; + return $( this ).outerWidth(); + }).get(); + + // set fixed column width + ts.scroller.setWidth( $fixedColumn.add( $fixedColumn.children() ), totalWidth + borderRightWidth * 2 - borderSpacing ); + ts.scroller.setWidth( $fixedColumn.find( 'table' ), totalWidth + borderRightWidth ); + + // set fixed column height ( changes with filtering ) + $fixedColumn.height( $wrapper.height() ); + + if ( wo.scroller_$footer.length ) { + // adjust footer row heights (text could wrap on resize) + $temp = $wrapper.children( '.' + tscss.scrollerFooter ).find( 'tfoot tr' ); + $rows = $fixedColumn.find( '.' + tscss.scrollerFooter + ' tfoot tr' ); + for ( index = 0; index < $rows.length; index++ ) { + $rows.eq( index ).height( $temp.eq( index ).height() ); + } + } + // leave a gap under the tbody for the horizontal scrollbar + $fixedColumn.find( '.' + tscss.scrollerTable ) + .height( $table.parent().height() - scrollBarWidth + borderBottomWidth ); + + // update fixed column tbody content, set row height & set cell widths for first row + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + $tbody = $mainTbodies.eq( tbodyIndex ); + if ( $tbody.length ) { + // get tbody + $rows = $tbody.children(); + totalRows = $rows.length; + $fb = ts.processTbody( $fixedTbodiesTable, $fixedTbodies.eq( tbodyIndex ), true); + $fb.empty(); + // update tbody cells after sort/filtering + for ( rowIndex = 0; rowIndex < totalRows; rowIndex++ ) { + $adjCol = $( $rows[ rowIndex ].outerHTML ); + $adjCol.children( 'td, th' ).slice( fixedColumns ).remove(); + // set row height + $adjCol.children().eq( 0 ).height( $rows.eq( rowIndex ).outerHeight() - ( isFirefox ? borderBottomWidth * 2 : 0 ) ); + // still need to adjust tbody cell widths ( the previous row may now be filtered ) + if ( rowIndex === 0 ) { + ts.scroller.setWidth( $adjCol.children().eq( 0 ), widths[ 0 ] ); } + $fb.append( $adjCol ); + } + + // adjust fixed header cell widths + $temp = $fixedColumn.find( 'thead' ).children( 'tr.' + tscss.headerRow ); + for ( index = 0; index < fixedColumns; index++ ) { + ts.scroller.setWidth( $temp.children( ':eq(' + index + ')' ), widths[ index ] ); } - $hdr.parent().add( $ft.parent() ).scrollLeft( $(this).scrollLeft() ); - }); + // restore tbody + ts.processTbody( $fixedTbodiesTable, $fb, false ); + } } - // Sorting, so scroll to top - if (wo.scroller_upAfterSort) { - $tbl.parent().animate({ scrollTop: 0 }, 'fast'); + /*** STUPID FIREFOX HACK! Since we can't hide the scrollbar with css ***/ + if ( isFirefox ) { + $fixedTbodiesTable.parent().css({ + 'width' : totalWidth + scrollBarWidth + borderRightWidth + }); } + $fixedColumn.show(); + }, - remove : function(table, c){ - var $table = c.$table, + + remove : function( c, wo ) { + var $wrap = wo.scroller_$container, namespace = c.namespace + 'tsscroller'; - $table.closest('.tablesorter-scroller').find('.tablesorter-scroller-header').remove(); - $table.closest('.tablesorter-scroller').find('.tablesorter-scroller-footer').remove(); - $table - .unwrap() - .find('.tablesorter-filter-row').removeClass('hideme').end() - .find('thead').show().css('visibility', 'visible'); - $(window).unbind('resize' + namespace + ' resizeEnd' + namespace); + c.$table + .off( namespace ) + .insertBefore( $wrap ) + .find( 'thead' ).show().css( 'visibility', 'visible' ) + .children( 'tr.' + tscss.headerRow + ' > *' ).attr( 'tabindex', 0 ) + .end() + .find( '.' + tscss.filterRow ).show().removeClass( tscss.filterRowHide ); + $wrap.remove(); + $( window ).off( namespace ); c.isScrolling = false; } -}); -})(jQuery, window); +}; + +})( jQuery, window ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js index bd89fda..8b1611b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js @@ -1,4 +1,4 @@ -/*! Widget: stickyHeaders - updated 3/5/2015 (v2.21.0) *//* +/*! Widget: stickyHeaders - updated 3/26/2015 (v2.21.3) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -95,7 +95,7 @@ ts.addWidget({ nestedStickyTop = $nestedSticky.length ? $nestedSticky.height() : 0, // clone table, then wrap to make sticky header $stickyTable = wo.$sticky = $table.clone() - .addClass('containsStickyHeaders ' + ts.css.sticky + ' ' + wo.stickyHeaders) + .addClass('containsStickyHeaders ' + ts.css.sticky + ' ' + wo.stickyHeaders + ' ' + c.namespace.slice(1) + '_extra_table' ) .wrap('<div class="' + ts.css.stickyWrap + '">'), $stickyWrap = $stickyTable.parent() .addClass(ts.css.stickyHide) @@ -153,13 +153,6 @@ ts.addWidget({ if ($attach.length && !$attach.css('position')) { $attach.css('position', 'relative'); } - // save stickyTable element to config - // it is also saved to wo.$sticky - if (c.$extraTables && c.$extraTables.length) { - c.$extraTables.add($stickyTable); - } else { - c.$extraTables = $stickyTable; - } // fix clone ID, if it exists - fixes #271 if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; } // clear out cloned table, except for sticky header @@ -179,7 +172,7 @@ ts.addWidget({ resizeHeader(); }); - ts.bindEvents(table, $stickyThead.children().children('.tablesorter-header')); + ts.bindEvents(table, $stickyThead.children().children('.' + ts.css.header)); // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. $table.after( $stickyWrap ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js index 12af34c..0220d32 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js @@ -1,4 +1,4 @@ -/*! Widget: storage */ +/*! Widget: storage - updated 3/26/2015 (v2.21.3) */ ;(function ($, window, document) { 'use strict'; @@ -25,26 +25,39 @@ var ts = $.tablesorter = $.tablesorter || {}; ts.storage = function(table, key, value, options) { table = $(table)[0]; var cookieIndex, cookies, date, - hasLocalStorage = false, + hasStorage = false, values = {}, c = table.config, + wo = c && c.widgetOptions, + storageType = ( options && options.useSessionStorage ) || ( wo && wo.storage_useSessionStorage ) ? + 'sessionStorage' : 'localStorage', $table = $(table), - id = options && options.id || $table.attr(options && options.group || - 'data-table-group') || table.id || $('.tablesorter').index( $table ), - url = options && options.url || $table.attr(options && options.page || - 'data-table-page') || c && c.fixedUrl || window.location.pathname; + // id from (1) options ID, (2) table "data-table-group" attribute, (3) widgetOptions.storage_tableId, + // (4) table ID, then (5) table index + id = options && options.id || + $table.attr( options && options.group || wo && wo.storage_group || 'data-table-group') || + wo && wo.storage_tableId || table.id || $('.tablesorter').index( $table ), + // url from (1) options url, (2) table "data-table-page" attribute, (3) widgetOptions.storage_fixedUrl, + // (4) table.config.fixedUrl (deprecated), then (5) window location path + url = options && options.url || + $table.attr(options && options.page || wo && wo.storage_page || 'data-table-page') || + wo && wo.storage_fixedUrl || c && c.fixedUrl || window.location.pathname; // https://gist.github.com/paulirish/5558557 - if ('localStorage' in window) { + if (storageType in window) { try { - window.localStorage.setItem('_tmptest', 'temp'); - hasLocalStorage = true; - window.localStorage.removeItem('_tmptest'); - } catch(error) {} + window[storageType].setItem('_tmptest', 'temp'); + hasStorage = true; + window[storageType].removeItem('_tmptest'); + } catch(error) { + if (c && c.debug) { + ts.log( storageType + ' is not supported in this browser' ); + } + } } // *** get value *** if ($.parseJSON) { - if (hasLocalStorage) { - values = $.parseJSON(localStorage[key] || 'null') || {}; + if (hasStorage) { + values = $.parseJSON( window[storageType][key] || 'null' ) || {}; } else { // old browser, using cookies cookies = document.cookie.split(/[;\s|=]/); @@ -61,8 +74,8 @@ ts.storage = function(table, key, value, options) { } values[url][id] = value; // *** set value *** - if (hasLocalStorage) { - localStorage[key] = JSON.stringify(values); + if (hasStorage) { + window[storageType][key] = JSON.stringify(values); } else { date = new Date(); date.setTime(date.getTime() + (31536e+6)); // 365 days diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js index 1b2d4c8..e0e9643 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js @@ -1,4 +1,4 @@ -/*! Widget: uitheme */ +/*! Widget: uitheme - updated 3/26/2015 (v2.21.3) */ ;(function ($) { 'use strict'; var ts = $.tablesorter = $.tablesorter || {}; @@ -58,8 +58,8 @@ ts.addWidget({ format: function(table, c, wo) { var i, hdr, icon, time, $header, $icon, $tfoot, $h, oldtheme, oldremove, oldIconRmv, hasOldTheme, themesAll = ts.themes, - $table = c.$table.add( c.$extraTables ), - $headers = c.$headers.add( c.$extraHeaders ), + $table = c.$table.add( $( c.namespace + '_extra_table' ) ), + $headers = c.$headers.add( $( c.namespace + '_extra_headers' ) ), theme = c.theme || 'jui', themes = themesAll[theme] || {}, remove = $.trim( [ themes.sortNone, themes.sortDesc, themes.sortAsc, themes.active ].join( ' ' ) ), @@ -130,7 +130,10 @@ ts.addWidget({ } } for (i = 0; i < c.columns; i++) { - $header = c.$headers.add(c.$extraHeaders).not('.sorter-false').filter('[data-column="' + i + '"]'); + $header = c.$headers + .add($(c.namespace + '_extra_headers')) + .not('.sorter-false') + .filter('[data-column="' + i + '"]'); $icon = (ts.css.icon) ? $header.find('.' + ts.css.icon) : $(); $h = $headers.not('.sorter-false').filter('[data-column="' + i + '"]:last'); if ($h.length) { diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css index 1c05db4..57714f3 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css @@ -69,10 +69,11 @@ } /* hovered row colors */ +.tablesorter-blackice tbody > tr.hover > td, .tablesorter-blackice tbody > tr:hover > td, .tablesorter-blackice tbody > tr.even:hover > td, .tablesorter-blackice tbody > tr.odd:hover > td { - background: #000; + background-color: #000; } /* table processing indicator */ @@ -116,15 +117,15 @@ /* caption */ caption { - background: #fff; + background-color: #fff; } /* filter widget */ .tablesorter-blackice .tablesorter-filter-row { - background: #222; + background-color: #222; } .tablesorter-blackice .tablesorter-filter-row td { - background: #222; + background-color: #222; line-height: normal; text-align: center; /* center the input */ -webkit-transition: line-height 0.1s ease; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css index 5e51f56..8a313ac 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css @@ -96,18 +96,21 @@ you'll need to add additional lines for rows with more than 2 child rows */ +.tablesorter-blue tbody > tr.hover > td, .tablesorter-blue tbody > tr:hover > td, .tablesorter-blue tbody > tr:hover + tr.tablesorter-childRow > td, .tablesorter-blue tbody > tr:hover + tr.tablesorter-childRow + tr.tablesorter-childRow > td, +.tablesorter-blue tbody > tr.even.hover > td, .tablesorter-blue tbody > tr.even:hover > td, .tablesorter-blue tbody > tr.even:hover + tr.tablesorter-childRow > td, .tablesorter-blue tbody > tr.even:hover + tr.tablesorter-childRow + tr.tablesorter-childRow > td { - background: #d9d9d9; + background-color: #d9d9d9; } +.tablesorter-blue tbody > tr.odd.hover > td, .tablesorter-blue tbody > tr.odd:hover > td, .tablesorter-blue tbody > tr.odd:hover + tr.tablesorter-childRow > td, .tablesorter-blue tbody > tr.odd:hover + tr.tablesorter-childRow + tr.tablesorter-childRow > td { - background: #bfbfbf; + background-color: #bfbfbf; } /* table processing indicator */ @@ -151,15 +154,15 @@ /* caption */ caption { - background: #fff; + background-color: #fff; } /* filter widget */ .tablesorter-blue .tablesorter-filter-row { - background: #eee; + background-color: #eee; } .tablesorter-blue .tablesorter-filter-row td { - background: #eee; + background-color: #eee; line-height: normal; text-align: center; /* center the input */ -webkit-transition: line-height 0.1s ease; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index a66b581..2be2901 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -53,6 +53,7 @@ .tablesorter-bootstrap > tbody > tr.tablesorter-hasChildRow.odd:hover ~ tr.tablesorter-hasChildRow.odd ~ .tablesorter-childRow.odd > td { background-color: #f9f9f9; } +.tablesorter-bootstrap > tbody > tr.hover > td, .tablesorter-bootstrap > tbody > tr.odd:hover > td, .tablesorter-bootstrap > tbody > tr.even:hover > td, .tablesorter-bootstrap > tbody > tr.tablesorter-hasChildRow.odd:hover ~ .tablesorter-childRow.odd > td, @@ -73,14 +74,14 @@ /* caption */ .caption { - background: #fff; + background-color: #fff; } /* filter widget */ .tablesorter-bootstrap .tablesorter-filter-row input.tablesorter-filter, .tablesorter-bootstrap .tablesorter-filter-row select.tablesorter-filter { width: 98%; - margin: 0 auto; + margin: 0; padding: 4px 6px; color: #333; -webkit-box-sizing: border-box; @@ -102,10 +103,10 @@ transition: height 0.1s ease; } .tablesorter-bootstrap .tablesorter-filter-row { - background: #efefef; + background-color: #efefef; } .tablesorter-bootstrap .tablesorter-filter-row td { - background: #efefef; + background-color: #efefef; line-height: normal; text-align: center; padding: 4px 6px; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css index d036082..9715d4d 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css @@ -63,6 +63,7 @@ .tablesorter-bootstrap tr.odd > td { background-color: #f9f9f9; } +.tablesorter-bootstrap tbody > tr.hover > td, .tablesorter-bootstrap tbody > .odd:hover > td, .tablesorter-bootstrap tbody > .even:hover > td { background-color: #f5f5f5; @@ -80,14 +81,14 @@ /* caption */ caption { - background: #fff; + background-color: #fff; } /* filter widget */ .tablesorter-bootstrap .tablesorter-filter-row input.tablesorter-filter, .tablesorter-bootstrap .tablesorter-filter-row select.tablesorter-filter { width: 98%; - margin: 0 auto; + margin: 0; padding: 4px 6px; background-color: #fff; color: #333; @@ -100,14 +101,14 @@ caption { transition: height 0.1s ease; } .tablesorter-bootstrap .tablesorter-filter-row .tablesorter-filter.disabled { - background: #eee; + background-color: #eee; cursor: not-allowed; } .tablesorter-bootstrap .tablesorter-filter-row { - background: #eee; + background-color: #eee; } .tablesorter-bootstrap .tablesorter-filter-row td { - background: #eee; + background-color: #eee; line-height: normal; text-align: center; padding: 4px 6px; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css index 35bd6b8..8a3decf 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css @@ -68,10 +68,11 @@ } /* hovered row colors */ +.tablesorter-dark tbody > tr.hover > td, .tablesorter-dark tbody > tr:hover > td, .tablesorter-dark tbody > tr.even:hover > td, .tablesorter-dark tbody > tr.odd:hover > td { - background: #000; + background-color: #000; } /* table processing indicator */ @@ -115,15 +116,15 @@ /* caption */ caption { - background: #202020; + background-color: #202020; } /* filter widget */ .tablesorter-dark .tablesorter-filter-row { - background: #202020; + background-color: #202020; } .tablesorter-dark .tablesorter-filter-row td { - background: #202020; + background-color: #202020; line-height: normal; text-align: center; /* center the input */ -webkit-transition: line-height 0.1s ease; @@ -165,7 +166,7 @@ caption { .tablesorter-dark select.tablesorter-filter { width: 98%; height: auto; - margin: 4px; + margin: 0; padding: 4px; background-color: #111; border: 1px solid #222; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css index 4771513..ee32607 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css @@ -70,10 +70,11 @@ Default Theme } /* hovered row colors */ +.tablesorter-default tbody > tr.hover > td, .tablesorter-default tbody > tr:hover > td, .tablesorter-default tbody > tr.even:hover > td, .tablesorter-default tbody > tr.odd:hover > td { - background: #fff; + background-color: #fff; color: #000; } @@ -118,15 +119,15 @@ Default Theme /* caption */ caption { - background: #fff; + background-color: #fff; } /* filter widget */ .tablesorter-default .tablesorter-filter-row { - background: #eee; + background-color: #eee; } .tablesorter-default .tablesorter-filter-row td { - background: #eee; + background-color: #eee; border-bottom: #ccc 1px solid; line-height: normal; text-align: center; /* center the input */ @@ -167,7 +168,7 @@ caption { .tablesorter-default select.tablesorter-filter { width: 95%; height: auto; - margin: 4px; + margin: 4px auto; padding: 4px; background-color: #fff; border: 1px solid #bbb; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css index fe33ff3..1e41a37 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css @@ -96,6 +96,7 @@ } /* hovered row colors */ +.tablesorter-dropbox tbody > tr.hover > td, .tablesorter-dropbox tbody > tr:hover > td, .tablesorter-dropbox tbody > tr.even:hover > td, .tablesorter-dropbox tbody > tr.odd:hover > td { @@ -140,15 +141,15 @@ /* caption */ caption { - background: #fff; + background-color: #fff; } /* Filter Widget */ .tablesorter-dropbox .tablesorter-filter-row { - background: #fff; + background-color: #fff; } .tablesorter-dropbox .tablesorter-filter-row td { - background: #fff; + background-color: #fff; line-height: normal; text-align: center; /* center the input */ -webkit-transition: line-height 0.1s ease; @@ -190,8 +191,7 @@ caption { .tablesorter-dropbox select.tablesorter-filter { width: 98%; height: auto; - margin: 4px; - + margin: 0; background-color: #fff; border: 1px solid #bbb; color: #333; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css index 3a0884e..ef30e37 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css @@ -19,7 +19,8 @@ /* header */ .tablesorter-green thead tr .tablesorter-header, .tablesorter-green tfoot tr { - background: center center repeat-x; + background-position: center center; + background-repeat: repeat-x; background-image: url(data:image/gif;base64,R0lGODlhAQBkAOYAAN/e39XU1fX19tTU1eXm5uTl5ePk5OLj4+Hi4vX29fT19PP08/Lz8vHy8fDx8O/w7+7v7uzt7Orr6ufo5/T08/Pz8ufn5uLi4eDg39/f3t3d3Nzc29HR0NDQz8/Pzuvq6urp6eno6Ojn5+fm5tfW1tbV1dTT09PS0tLR0dHQ0NDPz/f39/b29vX19fT09PPz8/Ly8vHx8e/v7+7u7u3t7ezs7Ovr6+rq6unp6ejo6Ofn5+bm5uXl5eTk5OPj4+Li4uHh4eDg4N/f397e3t3d3dzc3Nvb29ra2tnZ2djY2NfX19XV1dPT09LS0tHR0dDQ0M/Pz8rKysXFxf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAFMALAAAAAABAGQAAAdegCsrLC0tLi+ILi6FCSwsCS0KkhQVDA0OMjM0NTYfICEiIzw9P0AYGUQaG0ZHSEoDTU9Qs08pTk1MSyRJR0VDQT8+PTw7Ojg3NTMyMTAvi4WOhC0vMTI1OT9GTlFSgQA7); /* background-image: url(/assets/jquery-tablesorter/green-header.gif); */ } @@ -32,7 +33,8 @@ } .tablesorter-green .header, .tablesorter-green .tablesorter-header-inner { - background: no-repeat 5px center; + background-position: 5px center; + background-repeat: no-repeat; background-image: url(data:image/gif;base64,R0lGODlhEAAQAOYAAA5NDBBYDlWWUzRUM5DVjp7inJ/fnQ1ECiCsGhyYFxqKFRFdDhBXDQxCCiO8HSK2HCCqGh2aGByUFxuPFhqNFhmHFRZ2EhVvERRpEBBVDSS8HiGyHB+mGh6fGRuTFxiAFBd5Eww/Cgs5CRp7Fiu+JRx8GCy/JjHAKyynKCuhJzXCMDbCMDnDMyNuHz3EODy9N0LFPSl7JkvIRjycOFDKS1LKTVPLT1XLUFTCT17OWTBkLmbQYnDTbHXVcXnWdoXago/djGmUZ112XCJEIEdjRf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAEUALAAAAAAQABAAAAdlgEWCg4SFhoIvh4cVLECKhCMeJjwFj0UlEwgaMD4Gii0WFAkRHQ47BIY6IQAZDAwBCyAPOJa1toRBGBAwNTY3OT0/AoZCDQoOKi4yNDOKRCIfGycrKZYDBxIkKLZDFxy3RTHgloEAOw==); /* background-image: url(/assets/jquery-tablesorter/green-unsorted.gif); */ border-collapse: collapse; @@ -78,18 +80,21 @@ you'll need to add additional lines for rows with more than 2 child rows */ +.tablesorter-green tbody > tr.hover > td, .tablesorter-green tbody > tr:hover > td, .tablesorter-green tbody > tr:hover + tr.tablesorter-childRow > td, .tablesorter-green tbody > tr:hover + tr.tablesorter-childRow + tr.tablesorter-childRow > td, +.tablesorter-green tbody > tr.even.hover > td, .tablesorter-green tbody > tr.even:hover > td, .tablesorter-green tbody > tr.even:hover + tr.tablesorter-childRow > td, .tablesorter-green tbody > tr.even:hover + tr.tablesorter-childRow + tr.tablesorter-childRow > td { - background: #d9d9d9; + background-color: #d9d9d9; } +.tablesorter-green tbody > tr.odd.hover > td, .tablesorter-green tbody > tr.odd:hover > td, .tablesorter-green tbody > tr.odd:hover + tr.tablesorter-childRow > td, .tablesorter-green tbody > tr.odd:hover + tr.tablesorter-childRow + tr.tablesorter-childRow > td { - background: #bfbfbf; + background-color: #bfbfbf; } /* table processing indicator */ @@ -133,15 +138,15 @@ /* caption */ caption { - background: #fff; + background-color: #fff; } /* filter widget */ .tablesorter-green .tablesorter-filter-row { - background: #eee; + background-color: #eee; } .tablesorter-green .tablesorter-filter-row td { - background: #eee; + background-color: #eee; line-height: normal; text-align: center; /* center the input */ -webkit-transition: line-height 0.1s ease; @@ -181,7 +186,7 @@ caption { .tablesorter-green select.tablesorter-filter { width: 98%; height: auto; - margin: 4px; + margin: 0; padding: 4px; background-color: #fff; border: 1px solid #bbb; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css index 9a06d71..0b75817 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css @@ -114,18 +114,21 @@ you'll need to add additional lines for rows with more than 2 child rows */ +.tablesorter-grey tbody > tr.hover > td, .tablesorter-grey tbody > tr:hover > td, .tablesorter-grey tbody > tr:hover + tr.tablesorter-childRow > td, .tablesorter-grey tbody > tr:hover + tr.tablesorter-childRow + tr.tablesorter-childRow > td, +.tablesorter-grey tbody > tr.even.hover > td, .tablesorter-grey tbody > tr.even:hover > td, .tablesorter-grey tbody > tr.even:hover + tr.tablesorter-childRow > td, .tablesorter-grey tbody > tr.even:hover + tr.tablesorter-childRow + tr.tablesorter-childRow > td { - background: #134b78; + background-color: #134b78; } +.tablesorter-grey tbody > tr.odd.hover > td, .tablesorter-grey tbody > tr.odd:hover > td, .tablesorter-grey tbody > tr.odd:hover + tr.tablesorter-childRow > td, .tablesorter-grey tbody > tr.odd:hover + tr.tablesorter-childRow + tr.tablesorter-childRow > td { - background: #134b78; + background-color: #134b78; } /* table processing indicator */ @@ -175,15 +178,15 @@ /* caption */ caption { - background: #fff; + background-color: #fff; } /* filter widget */ .tablesorter-grey .tablesorter-filter-row { - background: #3c3c3c; + background-color: #3c3c3c; } .tablesorter-grey .tablesorter-filter-row td { - background: #3c3c3c; + background-color: #3c3c3c; line-height: normal; text-align: center; /* center the input */ -webkit-transition: line-height 0.1s ease; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css index 25bba9d..71e0fcb 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css @@ -35,7 +35,9 @@ } .tablesorter-ice .header, .tablesorter-ice .tablesorter-header { - background: #f6f8f9 no-repeat center right; + background-color: #f6f8f9; + background-position: center right; + background-repeat: no-repeat; background-image: url(data:image/gif;base64,R0lGODlhDAAMAMQAAAJEjAJCiwJBigJAiANFjgNGjgNEjQRIkQRHkANIkAVMlAVQmAZWnQZUnAdYoAhdpAhZoAlhqQlepQliqQppsApmrQxutgtutQtutAxwtwxwtg1yug1zugxtsw1yuP8A/yH5BAEAAB8ALAAAAAAMAAwAAAUx4Cd+3GiOW4ado2d9VMVm1xg9ptadTsP+QNZEcjoQTBDGCAFgLRSfQgCYMAiCn8EvBAA7); /* background-image: url(/assets/jquery-tablesorter/ice-unsorted.gif) */ padding: 4px 20px 4px 4px; @@ -46,7 +48,9 @@ .tablesorter-ice .tablesorter-headerSortUp, .tablesorter-ice .tablesorter-headerAsc { color: #333; - background: #ebedee no-repeat center right; + background-color: #ebedee; + background-position: center right; + background-repeat: no-repeat; background-image: url(data:image/gif;base64,R0lGODlhDAAMANUAAAJCiwNHkANFjgNEjQRIkQNJkQRMlARKkwRKkgVPlwZSmgdaogdYnwhfpghcowlhqgliqglgqAlgpwljqwporwpmrQplrAtsswtqsgtrsgtqsQxttAtvtQtttAxyuQxwtwxxtwxvtg10uw1zuQ1xuP8A/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACUALAAAAAAMAAwAAAY6wJKwJBoahyNQ6Dj0fDoZCpPEuWgqk4jxs8FQLI+Gg8Esm5kQydFQMC7IwkOAqUiUCAIzIjA4lwBlQQA7); /* background-image: url(/assets/jquery-tablesorter/ice-desc.gif) */ } @@ -54,7 +58,9 @@ .tablesorter-ice .tablesorter-headerSortDown, .tablesorter-ice .tablesorter-headerDesc { color: #333; - background: #ebedee no-repeat center right; + background-color: #ebedee; + background-position: center right; + background-repeat: no-repeat; background-image: url(data:image/gif;base64,R0lGODlhDAAMANUAAAE/iAJBigNFjgNEjQNFjQNDiwRHkQRHjwNHjwROlgRMlQRMlARJkgRKkgZQmAVPlgZWnQZSmgZRmAdXoAdXnwdUnAdbogdZoQhbowlhqAlepglkrAliqQtstAtqsQxyugxyuQxwuAxxuAxxtwxwtgxvtQ10vA12vA10u/8A/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACkALAAAAAAMAAwAAAY6wJQwdRoah6bP6DhEiVIdDxNEGm4yxlDpiJkwv2AmR2OhVCSJBsJ4gUQeCwOB6VAwBAXwYRAIpwBfQQA7); /* background-image: url(/assets/jquery-tablesorter/ice-asc.gif); */ } @@ -69,7 +75,7 @@ .tablesorter-ice tfoot .tablesorter-headerSortDown, .tablesorter-ice tfoot .tablesorter-headerAsc, .tablesorter-ice tfoot .tablesorter-headerDesc { - background: #ebedee; + background-color: #ebedee; } /* tbody */ @@ -78,10 +84,11 @@ } /* hovered row colors */ +.tablesorter-ice tbody > tr.hover > td, .tablesorter-ice tbody > tr:hover > td, .tablesorter-ice tbody > tr.even:hover > td, .tablesorter-ice tbody > tr.odd:hover > td { - background: #ebf2fa; + background-color: #ebf2fa; } /* table processing indicator */ @@ -131,15 +138,15 @@ /* caption */ caption { - background: #fff; + background-color: #fff; } /* filter widget */ .tablesorter-ice .tablesorter-filter-row { - background: #eee; + background-color: #eee; } .tablesorter-ice .tablesorter-filter-row td { - background: #eee; + background-color: #eee; line-height: normal; text-align: center; /* center the input */ -webkit-transition: line-height 0.1s ease; @@ -179,7 +186,7 @@ caption { .tablesorter-ice select.tablesorter-filter { width: 98%; height: auto; - margin: 4px; + margin: 0; padding: 4px; background-color: #fff; border: 1px solid #bbb; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css index 65e5fa3..5d9ee29 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css @@ -62,7 +62,8 @@ } /* hovered row colors */ -.tablesorter-jui tbody > tr:hover td { +.tablesorter-jui tbody > tr.hover > td, +.tablesorter-jui tbody > tr:hover > td { opacity: 0.7; filter: alpha(opacity=70); } @@ -94,10 +95,10 @@ /* filter widget */ .tablesorter-jui .tablesorter-filter-row { - background: transparent; + background-color: transparent; } .tablesorter-jui .tablesorter-filter-row td { - background: transparent; + background-color: transparent; line-height: normal; text-align: center; /* center the input */ -webkit-transition: line-height 0.1s ease; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css index 4eb7cd7..fd3511b 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css @@ -68,10 +68,11 @@ Metro Dark Theme } /* hovered row colors */ +.tablesorter-metro-dark tbody > tr.hover > td, .tablesorter-metro-dark tbody > tr:hover > td, .tablesorter-metro-dark tbody > tr.even:hover > td, .tablesorter-metro-dark tbody > tr.odd:hover > td { - background: #bbb; + background-color: #bbb; color: #000; } @@ -127,10 +128,10 @@ Metro Dark Theme /* filter widget */ .tablesorter-metro-dark .tablesorter-filter-row { - background: #eee; + background-color: #eee; } .tablesorter-metro-dark .tablesorter-filter-row td { - background: #eee; + background-color: #eee; line-height: normal; text-align: center; /* center the input */ -webkit-transition: line-height 0.1s ease; @@ -170,7 +171,7 @@ Metro Dark Theme .tablesorter-metro-dark select.tablesorter-filter { width: 95%; height: auto; - margin: 4px; + margin: 0; padding: 4px; background-color: #fff; border: 1px solid #bbb; From a673172cd966e7b87554b6620dd3ca5fabda8e5f Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Mon, 30 Mar 2015 20:03:20 +0200 Subject: [PATCH 054/138] Readme: Clarified how to require JS files --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2fd64f1..a372138 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ In your `application.js` //= require jquery-tablesorter ``` -This will require all jquery-tablesorter files (excluding addons and extras). +This will require all core jquery-tablesorter files. Please note: This loads only the core-widgets and will neither include the extracted widgets nor any files from the addons and extras directories. Those files must be required manually as shown below. Or you can include single file with: From 69623eb70201da102efaf613835cbb03c96a886b Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 30 Mar 2015 20:12:19 +0200 Subject: [PATCH 055/138] * updated tablesorter to latest version (2.21.4) --- CHANGELOG.md | 5 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery-tablesorter/extras/semver-mod.js | 363 +- .../jquery-tablesorter/extras/semver.js | 353 +- .../jquery.tablesorter.combined.js | 4483 +++++++++++++++++ .../jquery-tablesorter/jquery.tablesorter.js | 58 +- .../jquery.tablesorter.widgets.js | 21 +- .../parsers/parser-date-extract.js | 25 +- .../parsers/parser-date-iso8601.js | 4 +- .../parsers/parser-date-month.js | 7 +- .../parsers/parser-date-range.js | 7 +- .../parsers/parser-date-two-digit-year.js | 5 +- .../parsers/parser-date-weekday.js | 5 +- .../jquery-tablesorter/parsers/parser-date.js | 16 +- .../parsers/parser-duration.js | 2 +- .../parsers/parser-feet-inch-fraction.js | 2 +- .../parsers/parser-file-type.js | 2 +- .../parsers/parser-ignore-articles.js | 2 +- .../parsers/parser-image.js | 5 +- .../parsers/parser-input-select.js | 2 +- .../parsers/parser-metric.js | 2 +- .../parsers/parser-named-numbers.js | 2 +- .../parsers/parser-network.js | 15 +- .../parsers/parser-roman.js | 2 +- .../widgets/widget-alignChar.js | 4 +- .../widgets/widget-chart.js | 2 +- .../widgets/widget-columnSelector.js | 2 +- .../widgets/widget-cssStickyHeaders.js | 2 +- .../widgets/widget-editable.js | 2 +- .../widgets/widget-filter-formatter-html5.js | 2 +- .../widgets/widget-filter-formatter-jui.js | 2 +- .../widget-filter-formatter-select2.js | 2 +- .../widgets/widget-filter-type-insideRange.js | 5 +- .../widgets/widget-formatter.js | 2 +- .../widgets/widget-grouping.js | 2 +- .../widgets/widget-headerTitles.js | 2 +- .../jquery-tablesorter/widgets/widget-math.js | 2 +- .../widgets/widget-output.js | 2 +- .../widgets/widget-print.js | 2 +- .../widgets/widget-reflow.js | 2 +- .../widgets/widget-repeatheaders.js | 2 +- .../widgets/widget-staticRow.js | 2 +- 44 files changed, 5159 insertions(+), 276 deletions(-) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b9622a..6be2d6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ Changelog === +#### v1.16.3 (2015-03-30) + +* Upgrade tablesorter to v2.21.4 +* Readme: Clarified how to require JS files + #### v1.16.2 (2015-03-26) * Upgrade tablesorter to v2.21.3 diff --git a/README.md b/README.md index a372138..e1ad95e 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.21.3 (3/26/2015), [documentation] +Current tablesorter version: 2.21.4 (3/28/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 4373c29..47a7a3c 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.16.3' + VERSION = '1.16.4' end diff --git a/tablesorter b/tablesorter index 3ee06dc..655a0f0 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 3ee06dceb60d3a1e717fc9ea8bfefb304e9ed720 +Subproject commit 655a0f09b4b9bbb869faa9da7ccef7e3d7e25bb3 diff --git a/vendor/assets/javascripts/jquery-tablesorter/extras/semver-mod.js b/vendor/assets/javascripts/jquery-tablesorter/extras/semver-mod.js index ac64303..c9a3826 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/extras/semver-mod.js +++ b/vendor/assets/javascripts/jquery-tablesorter/extras/semver-mod.js @@ -1,18 +1,17 @@ -/** - Modified semver.js for node.js by R.Garrison (@Mottie) - Original by @isaacs: https://github.com/isaacs/node-semver +/*! Modified semver.js for node.js (v4.3.3, 3/27/2015) *//* + semver-mod.js by R.Garrison (@Mottie) + semver.js by @isaacs: https://github.com/isaacs/node-semver ( all modifications have been labeled ) */ // ***** MODIFIED LINE BELOW ***** (function(){ // ***** MODIFIED LINE BELOW ***** var module = { exports : {} }; - // export the class if we are in a Node-like system. // ***** MODIFIED LINE BELOW ***** // if (typeof module === 'object' && module.exports === exports) // ***** MODIFIED LINE BELOW ***** - var exports = module.exports = SemVer; +var exports = module.exports = SemVer; // The debug function is excluded entirely from the minified version. /* nomin */ var debug; @@ -32,6 +31,9 @@ var module = { exports : {} }; // Not necessarily the package version of this code. exports.SEMVER_SPEC_VERSION = '2.0.0'; +var MAX_LENGTH = 256; +var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; + // The actual regexps go on exports.re var re = exports.re = []; var src = exports.src = []; @@ -150,18 +152,18 @@ var XRANGEPLAIN = R++; src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + - '(?:(' + src[PRERELEASE] + ')' + - ')?)?)?'; + '(?:' + src[PRERELEASE] + ')?' + + src[BUILD] + '?' + + ')?)?'; var XRANGEPLAINLOOSE = R++; src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + - '(?:(' + src[PRERELEASELOOSE] + ')' + - ')?)?)?'; + '(?:' + src[PRERELEASELOOSE] + ')?' + + src[BUILD] + '?' + + ')?)?'; -// >=2.x, for example, means >=2.0.0-0 -// <1.x would be the same as "<1.0.0-0", though. var XRANGE = R++; src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$'; var XRANGELOOSE = R++; @@ -245,8 +247,24 @@ for (var i = 0; i < R; i++) { exports.parse = parse; function parse(version, loose) { + if (version instanceof SemVer) + return version; + + if (typeof version !== 'string') + return null; + + if (version.length > MAX_LENGTH) + return null; + var r = loose ? re[LOOSE] : re[FULL]; - return (r.test(version)) ? new SemVer(version, loose) : null; + if (!r.test(version)) + return null; + + try { + return new SemVer(version, loose); + } catch (er) { + return null; + } } exports.valid = valid; @@ -258,7 +276,7 @@ function valid(version, loose) { exports.clean = clean; function clean(version, loose) { - var s = parse(version, loose); + var s = parse(version.trim().replace(/^[=v]+/, ''), loose); return s ? s.version : null; } @@ -271,8 +289,13 @@ function SemVer(version, loose) { return version; else version = version.version; + } else if (typeof version !== 'string') { + throw new TypeError('Invalid Version: ' + version); } + if (version.length > MAX_LENGTH) + throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') + if (!(this instanceof SemVer)) return new SemVer(version, loose); @@ -290,12 +313,26 @@ function SemVer(version, loose) { this.minor = +m[2]; this.patch = +m[3]; + if (this.major > MAX_SAFE_INTEGER || this.major < 0) + throw new TypeError('Invalid major version') + + if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) + throw new TypeError('Invalid minor version') + + if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) + throw new TypeError('Invalid patch version') + // numberify any prerelease numeric ids if (!m[4]) this.prerelease = []; else this.prerelease = m[4].split('.').map(function(id) { - return (/^[0-9]+$/.test(id)) ? +id : id; + if (/^[0-9]+$/.test(id)) { + var num = +id + if (num >= 0 && num < MAX_SAFE_INTEGER) + return num + } + return id; }); this.build = m[5] ? m[5].split('.') : []; @@ -343,7 +380,7 @@ SemVer.prototype.comparePre = function(other) { return -1; else if (!this.prerelease.length && other.prerelease.length) return 1; - else if (!this.prerelease.lenth && !other.prerelease.length) + else if (!this.prerelease.length && !other.prerelease.length) return 0; var i = 0; @@ -364,19 +401,72 @@ SemVer.prototype.comparePre = function(other) { } while (++i); }; -SemVer.prototype.inc = function(release) { +// preminor will bump the version up to the next minor release, and immediately +// down to pre-release. premajor and prepatch work the same way. +SemVer.prototype.inc = function(release, identifier) { switch (release) { - case 'major': + case 'premajor': + this.prerelease.length = 0; + this.patch = 0; + this.minor = 0; this.major++; - this.minor = -1; - case 'minor': + this.inc('pre', identifier); + break; + case 'preminor': + this.prerelease.length = 0; + this.patch = 0; this.minor++; - this.patch = -1; + this.inc('pre', identifier); + break; + case 'prepatch': + // If this is already a prerelease, it will bump to the next version + // drop any prereleases that might already exist, since they are not + // relevant at this point. + this.prerelease.length = 0; + this.inc('patch', identifier); + this.inc('pre', identifier); + break; + // If the input is a non-prerelease version, this acts the same as + // prepatch. + case 'prerelease': + if (this.prerelease.length === 0) + this.inc('patch', identifier); + this.inc('pre', identifier); + break; + + case 'major': + // If this is a pre-major version, bump up to the same major version. + // Otherwise increment major. + // 1.0.0-5 bumps to 1.0.0 + // 1.1.0 bumps to 2.0.0 + if (this.minor !== 0 || this.patch !== 0 || this.prerelease.length === 0) + this.major++; + this.minor = 0; + this.patch = 0; + this.prerelease = []; + break; + case 'minor': + // If this is a pre-minor version, bump up to the same minor version. + // Otherwise increment minor. + // 1.2.0-5 bumps to 1.2.0 + // 1.2.1 bumps to 1.3.0 + if (this.patch !== 0 || this.prerelease.length === 0) + this.minor++; + this.patch = 0; + this.prerelease = []; + break; case 'patch': - this.patch++; + // If this is not a pre-release version, it will increment the patch. + // If it is a pre-release it will bump up to the same patch version. + // 1.2.0-5 patches to 1.2.0 + // 1.2.0 patches to 1.2.1 + if (this.prerelease.length === 0) + this.patch++; this.prerelease = []; break; - case 'prerelease': + // This probably shouldn't be used publicly. + // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. + case 'pre': if (this.prerelease.length === 0) this.prerelease = [0]; else { @@ -390,6 +480,15 @@ SemVer.prototype.inc = function(release) { if (i === -1) // didn't increment anything this.prerelease.push(0); } + if (identifier) { + // 1.2.0-beta.1 bumps to 1.2.0-beta.2, + // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 + if (this.prerelease[0] === identifier) { + if (isNaN(this.prerelease[1])) + this.prerelease = [identifier, 0]; + } else + this.prerelease = [identifier, 0]; + } break; default: @@ -400,14 +499,46 @@ SemVer.prototype.inc = function(release) { }; exports.inc = inc; -function inc(version, release, loose) { +function inc(version, release, loose, identifier) { + if (typeof(loose) === 'string') { + identifier = loose; + loose = undefined; + } + try { - return new SemVer(version, loose).inc(release).version; + return new SemVer(version, loose).inc(release, identifier).version; } catch (er) { return null; } } +exports.diff = diff; +function diff(version1, version2) { + if (eq(version1, version2)) { + return null; + } else { + var v1 = parse(version1); + var v2 = parse(version2); + if (v1.prerelease.length || v2.prerelease.length) { + for (var key in v1) { + if (key === 'major' || key === 'minor' || key === 'patch') { + if (v1[key] !== v2[key]) { + return 'pre'+key; + } + } + } + return 'prerelease'; + } + for (var key in v1) { + if (key === 'major' || key === 'minor' || key === 'patch') { + if (v1[key] !== v2[key]) { + return key; + } + } + } + } +} + exports.compareIdentifiers = compareIdentifiers; var numeric = /^[0-9]+$/; @@ -432,6 +563,21 @@ function rcompareIdentifiers(a, b) { return compareIdentifiers(b, a); } +exports.major = major; +function major(a, loose) { + return new SemVer(a, loose).major; +} + +exports.minor = minor; +function minor(a, loose) { + return new SemVer(a, loose).minor; +} + +exports.patch = patch; +function patch(a, loose) { + return new SemVer(a, loose).patch; +} + exports.compare = compare; function compare(a, b, loose) { return new SemVer(a, loose).compare(b); @@ -495,8 +641,16 @@ exports.cmp = cmp; function cmp(a, op, b, loose) { var ret; switch (op) { - case '===': ret = a === b; break; - case '!==': ret = a !== b; break; + case '===': + if (typeof a === 'object') a = a.version; + if (typeof b === 'object') b = b.version; + ret = a === b; + break; + case '!==': + if (typeof a === 'object') a = a.version; + if (typeof b === 'object') b = b.version; + ret = a !== b; + break; case '': case '=': case '==': ret = eq(a, b, loose); break; case '!=': ret = neq(a, b, loose); break; case '>': ret = gt(a, b, loose); break; @@ -528,6 +682,8 @@ function Comparator(comp, loose) { this.value = ''; else this.value = this.operator + this.semver.version; + + debug('comp', this); } var ANY = {}; @@ -539,24 +695,14 @@ Comparator.prototype.parse = function(comp) { throw new TypeError('Invalid comparator: ' + comp); this.operator = m[1]; + if (this.operator === '=') + this.operator = ''; + // if it literally is just '>' or '' then allow anything. if (!m[2]) this.semver = ANY; - else { + else this.semver = new SemVer(m[2], this.loose); - - // <1.2.3-rc DOES allow 1.2.3-beta (has prerelease) - // >=1.2.3 DOES NOT allow 1.2.3-beta - // <=1.2.3 DOES allow 1.2.3-beta - // However, <1.2.3 does NOT allow 1.2.3-beta, - // even though `1.2.3-beta < 1.2.3` - // The assumption is that the 1.2.3 version has something you - // *don't* want, so we push the prerelease down to the minimum. - if (this.operator === '<' && !this.semver.prerelease.length) { - this.semver.prerelease = ['0']; - this.semver.format(); - } - } }; Comparator.prototype.inspect = function() { @@ -569,8 +715,14 @@ Comparator.prototype.toString = function() { Comparator.prototype.test = function(version) { debug('Comparator.test', version, this.loose); - return (this.semver === ANY) ? true : - cmp(version, this.operator, this.semver, this.loose); + + if (this.semver === ANY) + return true; + + if (typeof version === 'string') + version = new SemVer(version, this.loose); + + return cmp(version, this.operator, this.semver, this.loose); }; @@ -707,20 +859,20 @@ function replaceTilde(comp, loose) { if (isX(M)) ret = ''; else if (isX(m)) - ret = '>=' + M + '.0.0-0 <' + (+M + 1) + '.0.0-0'; + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; else if (isX(p)) // ~1.2 == >=1.2.0- <1.3.0- - ret = '>=' + M + '.' + m + '.0-0 <' + M + '.' + (+m + 1) + '.0-0'; + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; else if (pr) { debug('replaceTilde pr', pr); if (pr.charAt(0) !== '-') pr = '-' + pr; ret = '>=' + M + '.' + m + '.' + p + pr + - ' <' + M + '.' + (+m + 1) + '.0-0'; + ' <' + M + '.' + (+m + 1) + '.0'; } else - // ~1.2.3 == >=1.2.3-0 <1.3.0-0 - ret = '>=' + M + '.' + m + '.' + p + '-0' + - ' <' + M + '.' + (+m + 1) + '.0-0'; + // ~1.2.3 == >=1.2.3 <1.3.0 + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0'; debug('tilde return', ret); return ret; @@ -740,6 +892,7 @@ function replaceCarets(comp, loose) { } function replaceCaret(comp, loose) { + debug('caret', comp, loose); var r = loose ? re[CARETLOOSE] : re[CARET]; return comp.replace(r, function(_, M, m, p, pr) { debug('caret', comp, _, M, m, p, pr); @@ -748,35 +901,38 @@ function replaceCaret(comp, loose) { if (isX(M)) ret = ''; else if (isX(m)) - ret = '>=' + M + '.0.0-0 <' + (+M + 1) + '.0.0-0'; + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; else if (isX(p)) { if (M === '0') - ret = '>=' + M + '.' + m + '.0-0 <' + M + '.' + (+m + 1) + '.0-0'; + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; else - ret = '>=' + M + '.' + m + '.0-0 <' + (+M + 1) + '.0.0-0'; + ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0'; } else if (pr) { debug('replaceCaret pr', pr); if (pr.charAt(0) !== '-') pr = '-' + pr; if (M === '0') { if (m === '0') - ret = '=' + M + '.' + m + '.' + p + pr; + ret = '>=' + M + '.' + m + '.' + p + pr + + ' <' + M + '.' + m + '.' + (+p + 1); else ret = '>=' + M + '.' + m + '.' + p + pr + - ' <' + M + '.' + (+m + 1) + '.0-0'; + ' <' + M + '.' + (+m + 1) + '.0'; } else ret = '>=' + M + '.' + m + '.' + p + pr + - ' <' + (+M + 1) + '.0.0-0'; + ' <' + (+M + 1) + '.0.0'; } else { + debug('no pr'); if (M === '0') { if (m === '0') - ret = '=' + M + '.' + m + '.' + p; + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + m + '.' + (+p + 1); else - ret = '>=' + M + '.' + m + '.' + p + '-0' + - ' <' + M + '.' + (+m + 1) + '.0-0'; + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0'; } else - ret = '>=' + M + '.' + m + '.' + p + '-0' + - ' <' + (+M + 1) + '.0.0-0'; + ret = '>=' + M + '.' + m + '.' + p + + ' <' + (+M + 1) + '.0.0'; } debug('caret return', ret); @@ -804,23 +960,27 @@ function replaceXRange(comp, loose) { if (gtlt === '=' && anyX) gtlt = ''; - if (gtlt && anyX) { - // replace X with 0, and then append the -0 min-prerelease - if (xM) - M = 0; + if (xM) { + if (gtlt === '>' || gtlt === '<') { + // nothing is allowed + ret = '<0.0.0'; + } else { + // nothing is forbidden + ret = '*'; + } + } else if (gtlt && anyX) { + // replace X with 0 if (xm) m = 0; if (xp) p = 0; if (gtlt === '>') { - // >1 => >=2.0.0-0 - // >1.2 => >=1.3.0-0 - // >1.2.3 => >= 1.2.4-0 + // >1 => >=2.0.0 + // >1.2 => >=1.3.0 + // >1.2.3 => >= 1.2.4 gtlt = '>='; - if (xM) { - // no change - } else if (xm) { + if (xm) { M = +M + 1; m = 0; p = 0; @@ -828,20 +988,21 @@ function replaceXRange(comp, loose) { m = +m + 1; p = 0; } + } else if (gtlt === '<=') { + // <=0.7.x is actually <0.8.0, since any 0.7.x should + // pass. Similarly, <=7.x is actually <8.0.0, etc. + gtlt = '<' + if (xm) + M = +M + 1 + else + m = +m + 1 } - - ret = gtlt + M + '.' + m + '.' + p + '-0'; - } else if (xM) { - // allow any - ret = '*'; + ret = gtlt + M + '.' + m + '.' + p; } else if (xm) { - // append '-0' onto the version, otherwise - // '1.x.x' matches '2.0.0-beta', since the tag - // *lowers* the version value - ret = '>=' + M + '.0.0-0 <' + (+M + 1) + '.0.0-0'; + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; } else if (xp) { - ret = '>=' + M + '.' + m + '.0-0 <' + M + '.' + (+m + 1) + '.0-0'; + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; } debug('xRange return', ret); @@ -860,9 +1021,9 @@ function replaceStars(comp, loose) { // This function is passed to string.replace(re[HYPHENRANGE]) // M, m, patch, prerelease, build -// 1.2 - 3.4.5 => >=1.2.0-0 <=3.4.5 -// 1.2.3 - 3.4 => >=1.2.0-0 <3.5.0-0 Any 3.4.x will do -// 1.2 - 3.4 => >=1.2.0-0 <3.5.0-0 +// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5 +// 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do +// 1.2 - 3.4 => >=1.2.0 <3.5.0 function hyphenReplace($0, from, fM, fm, fp, fpr, fb, to, tM, tm, tp, tpr, tb) { @@ -870,18 +1031,18 @@ function hyphenReplace($0, if (isX(fM)) from = ''; else if (isX(fm)) - from = '>=' + fM + '.0.0-0'; + from = '>=' + fM + '.0.0'; else if (isX(fp)) - from = '>=' + fM + '.' + fm + '.0-0'; + from = '>=' + fM + '.' + fm + '.0'; else from = '>=' + from; if (isX(tM)) to = ''; else if (isX(tm)) - to = '<' + (+tM + 1) + '.0.0-0'; + to = '<' + (+tM + 1) + '.0.0'; else if (isX(tp)) - to = '<' + tM + '.' + (+tm + 1) + '.0-0'; + to = '<' + tM + '.' + (+tm + 1) + '.0'; else if (tpr) to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr; else @@ -895,6 +1056,10 @@ function hyphenReplace($0, Range.prototype.test = function(version) { if (!version) return false; + + if (typeof version === 'string') + version = new SemVer(version, this.loose); + for (var i = 0; i < this.set.length; i++) { if (testSet(this.set[i], version)) return true; @@ -907,6 +1072,31 @@ function testSet(set, version) { if (!set[i].test(version)) return false; } + + if (version.prerelease.length) { + // Find the set of versions that are allowed to have prereleases + // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 + // That should allow `1.2.3-pr.2` to pass. + // However, `1.2.4-alpha.notready` should NOT be allowed, + // even though it's within the range set by the comparators. + for (var i = 0; i < set.length; i++) { + debug(set[i].semver); + if (set[i].semver === ANY) + return true; + + if (set[i].semver.prerelease.length > 0) { + var allowed = set[i].semver; + if (allowed.major === version.major && + allowed.minor === version.minor && + allowed.patch === version.patch) + return true; + } + } + + // Version has a -pre, but it's not one of the ones we like. + return false; + } + return true; } @@ -1022,5 +1212,6 @@ function outside(version, range, hilo, loose) { // Use the define() function if we're in AMD land if (typeof define === 'function' && define.amd) define(exports); + // ***** MODIFIED LINE BELOW ***** })(); \ No newline at end of file diff --git a/vendor/assets/javascripts/jquery-tablesorter/extras/semver.js b/vendor/assets/javascripts/jquery-tablesorter/extras/semver.js index 9e9470d..b95992b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/extras/semver.js +++ b/vendor/assets/javascripts/jquery-tablesorter/extras/semver.js @@ -20,6 +20,9 @@ if (typeof module === 'object' && module.exports === exports) // Not necessarily the package version of this code. exports.SEMVER_SPEC_VERSION = '2.0.0'; +var MAX_LENGTH = 256; +var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; + // The actual regexps go on exports.re var re = exports.re = []; var src = exports.src = []; @@ -138,18 +141,18 @@ var XRANGEPLAIN = R++; src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + - '(?:(' + src[PRERELEASE] + ')' + - ')?)?)?'; + '(?:' + src[PRERELEASE] + ')?' + + src[BUILD] + '?' + + ')?)?'; var XRANGEPLAINLOOSE = R++; src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + - '(?:(' + src[PRERELEASELOOSE] + ')' + - ')?)?)?'; + '(?:' + src[PRERELEASELOOSE] + ')?' + + src[BUILD] + '?' + + ')?)?'; -// >=2.x, for example, means >=2.0.0-0 -// <1.x would be the same as "<1.0.0-0", though. var XRANGE = R++; src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$'; var XRANGELOOSE = R++; @@ -233,8 +236,24 @@ for (var i = 0; i < R; i++) { exports.parse = parse; function parse(version, loose) { + if (version instanceof SemVer) + return version; + + if (typeof version !== 'string') + return null; + + if (version.length > MAX_LENGTH) + return null; + var r = loose ? re[LOOSE] : re[FULL]; - return (r.test(version)) ? new SemVer(version, loose) : null; + if (!r.test(version)) + return null; + + try { + return new SemVer(version, loose); + } catch (er) { + return null; + } } exports.valid = valid; @@ -246,7 +265,7 @@ function valid(version, loose) { exports.clean = clean; function clean(version, loose) { - var s = parse(version, loose); + var s = parse(version.trim().replace(/^[=v]+/, ''), loose); return s ? s.version : null; } @@ -258,8 +277,13 @@ function SemVer(version, loose) { return version; else version = version.version; + } else if (typeof version !== 'string') { + throw new TypeError('Invalid Version: ' + version); } + if (version.length > MAX_LENGTH) + throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') + if (!(this instanceof SemVer)) return new SemVer(version, loose); @@ -277,12 +301,26 @@ function SemVer(version, loose) { this.minor = +m[2]; this.patch = +m[3]; + if (this.major > MAX_SAFE_INTEGER || this.major < 0) + throw new TypeError('Invalid major version') + + if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) + throw new TypeError('Invalid minor version') + + if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) + throw new TypeError('Invalid patch version') + // numberify any prerelease numeric ids if (!m[4]) this.prerelease = []; else this.prerelease = m[4].split('.').map(function(id) { - return (/^[0-9]+$/.test(id)) ? +id : id; + if (/^[0-9]+$/.test(id)) { + var num = +id + if (num >= 0 && num < MAX_SAFE_INTEGER) + return num + } + return id; }); this.build = m[5] ? m[5].split('.') : []; @@ -330,7 +368,7 @@ SemVer.prototype.comparePre = function(other) { return -1; else if (!this.prerelease.length && other.prerelease.length) return 1; - else if (!this.prerelease.lenth && !other.prerelease.length) + else if (!this.prerelease.length && !other.prerelease.length) return 0; var i = 0; @@ -351,19 +389,72 @@ SemVer.prototype.comparePre = function(other) { } while (++i); }; -SemVer.prototype.inc = function(release) { +// preminor will bump the version up to the next minor release, and immediately +// down to pre-release. premajor and prepatch work the same way. +SemVer.prototype.inc = function(release, identifier) { switch (release) { - case 'major': + case 'premajor': + this.prerelease.length = 0; + this.patch = 0; + this.minor = 0; this.major++; - this.minor = -1; - case 'minor': + this.inc('pre', identifier); + break; + case 'preminor': + this.prerelease.length = 0; + this.patch = 0; this.minor++; - this.patch = -1; + this.inc('pre', identifier); + break; + case 'prepatch': + // If this is already a prerelease, it will bump to the next version + // drop any prereleases that might already exist, since they are not + // relevant at this point. + this.prerelease.length = 0; + this.inc('patch', identifier); + this.inc('pre', identifier); + break; + // If the input is a non-prerelease version, this acts the same as + // prepatch. + case 'prerelease': + if (this.prerelease.length === 0) + this.inc('patch', identifier); + this.inc('pre', identifier); + break; + + case 'major': + // If this is a pre-major version, bump up to the same major version. + // Otherwise increment major. + // 1.0.0-5 bumps to 1.0.0 + // 1.1.0 bumps to 2.0.0 + if (this.minor !== 0 || this.patch !== 0 || this.prerelease.length === 0) + this.major++; + this.minor = 0; + this.patch = 0; + this.prerelease = []; + break; + case 'minor': + // If this is a pre-minor version, bump up to the same minor version. + // Otherwise increment minor. + // 1.2.0-5 bumps to 1.2.0 + // 1.2.1 bumps to 1.3.0 + if (this.patch !== 0 || this.prerelease.length === 0) + this.minor++; + this.patch = 0; + this.prerelease = []; + break; case 'patch': - this.patch++; + // If this is not a pre-release version, it will increment the patch. + // If it is a pre-release it will bump up to the same patch version. + // 1.2.0-5 patches to 1.2.0 + // 1.2.0 patches to 1.2.1 + if (this.prerelease.length === 0) + this.patch++; this.prerelease = []; break; - case 'prerelease': + // This probably shouldn't be used publicly. + // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. + case 'pre': if (this.prerelease.length === 0) this.prerelease = [0]; else { @@ -377,6 +468,15 @@ SemVer.prototype.inc = function(release) { if (i === -1) // didn't increment anything this.prerelease.push(0); } + if (identifier) { + // 1.2.0-beta.1 bumps to 1.2.0-beta.2, + // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 + if (this.prerelease[0] === identifier) { + if (isNaN(this.prerelease[1])) + this.prerelease = [identifier, 0]; + } else + this.prerelease = [identifier, 0]; + } break; default: @@ -387,14 +487,46 @@ SemVer.prototype.inc = function(release) { }; exports.inc = inc; -function inc(version, release, loose) { +function inc(version, release, loose, identifier) { + if (typeof(loose) === 'string') { + identifier = loose; + loose = undefined; + } + try { - return new SemVer(version, loose).inc(release).version; + return new SemVer(version, loose).inc(release, identifier).version; } catch (er) { return null; } } +exports.diff = diff; +function diff(version1, version2) { + if (eq(version1, version2)) { + return null; + } else { + var v1 = parse(version1); + var v2 = parse(version2); + if (v1.prerelease.length || v2.prerelease.length) { + for (var key in v1) { + if (key === 'major' || key === 'minor' || key === 'patch') { + if (v1[key] !== v2[key]) { + return 'pre'+key; + } + } + } + return 'prerelease'; + } + for (var key in v1) { + if (key === 'major' || key === 'minor' || key === 'patch') { + if (v1[key] !== v2[key]) { + return key; + } + } + } + } +} + exports.compareIdentifiers = compareIdentifiers; var numeric = /^[0-9]+$/; @@ -419,6 +551,21 @@ function rcompareIdentifiers(a, b) { return compareIdentifiers(b, a); } +exports.major = major; +function major(a, loose) { + return new SemVer(a, loose).major; +} + +exports.minor = minor; +function minor(a, loose) { + return new SemVer(a, loose).minor; +} + +exports.patch = patch; +function patch(a, loose) { + return new SemVer(a, loose).patch; +} + exports.compare = compare; function compare(a, b, loose) { return new SemVer(a, loose).compare(b); @@ -482,8 +629,16 @@ exports.cmp = cmp; function cmp(a, op, b, loose) { var ret; switch (op) { - case '===': ret = a === b; break; - case '!==': ret = a !== b; break; + case '===': + if (typeof a === 'object') a = a.version; + if (typeof b === 'object') b = b.version; + ret = a === b; + break; + case '!==': + if (typeof a === 'object') a = a.version; + if (typeof b === 'object') b = b.version; + ret = a !== b; + break; case '': case '=': case '==': ret = eq(a, b, loose); break; case '!=': ret = neq(a, b, loose); break; case '>': ret = gt(a, b, loose); break; @@ -515,6 +670,8 @@ function Comparator(comp, loose) { this.value = ''; else this.value = this.operator + this.semver.version; + + debug('comp', this); } var ANY = {}; @@ -526,24 +683,14 @@ Comparator.prototype.parse = function(comp) { throw new TypeError('Invalid comparator: ' + comp); this.operator = m[1]; + if (this.operator === '=') + this.operator = ''; + // if it literally is just '>' or '' then allow anything. if (!m[2]) this.semver = ANY; - else { + else this.semver = new SemVer(m[2], this.loose); - - // <1.2.3-rc DOES allow 1.2.3-beta (has prerelease) - // >=1.2.3 DOES NOT allow 1.2.3-beta - // <=1.2.3 DOES allow 1.2.3-beta - // However, <1.2.3 does NOT allow 1.2.3-beta, - // even though `1.2.3-beta < 1.2.3` - // The assumption is that the 1.2.3 version has something you - // *don't* want, so we push the prerelease down to the minimum. - if (this.operator === '<' && !this.semver.prerelease.length) { - this.semver.prerelease = ['0']; - this.semver.format(); - } - } }; Comparator.prototype.inspect = function() { @@ -556,8 +703,14 @@ Comparator.prototype.toString = function() { Comparator.prototype.test = function(version) { debug('Comparator.test', version, this.loose); - return (this.semver === ANY) ? true : - cmp(version, this.operator, this.semver, this.loose); + + if (this.semver === ANY) + return true; + + if (typeof version === 'string') + version = new SemVer(version, this.loose); + + return cmp(version, this.operator, this.semver, this.loose); }; @@ -694,20 +847,20 @@ function replaceTilde(comp, loose) { if (isX(M)) ret = ''; else if (isX(m)) - ret = '>=' + M + '.0.0-0 <' + (+M + 1) + '.0.0-0'; + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; else if (isX(p)) // ~1.2 == >=1.2.0- <1.3.0- - ret = '>=' + M + '.' + m + '.0-0 <' + M + '.' + (+m + 1) + '.0-0'; + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; else if (pr) { debug('replaceTilde pr', pr); if (pr.charAt(0) !== '-') pr = '-' + pr; ret = '>=' + M + '.' + m + '.' + p + pr + - ' <' + M + '.' + (+m + 1) + '.0-0'; + ' <' + M + '.' + (+m + 1) + '.0'; } else - // ~1.2.3 == >=1.2.3-0 <1.3.0-0 - ret = '>=' + M + '.' + m + '.' + p + '-0' + - ' <' + M + '.' + (+m + 1) + '.0-0'; + // ~1.2.3 == >=1.2.3 <1.3.0 + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0'; debug('tilde return', ret); return ret; @@ -727,6 +880,7 @@ function replaceCarets(comp, loose) { } function replaceCaret(comp, loose) { + debug('caret', comp, loose); var r = loose ? re[CARETLOOSE] : re[CARET]; return comp.replace(r, function(_, M, m, p, pr) { debug('caret', comp, _, M, m, p, pr); @@ -735,35 +889,38 @@ function replaceCaret(comp, loose) { if (isX(M)) ret = ''; else if (isX(m)) - ret = '>=' + M + '.0.0-0 <' + (+M + 1) + '.0.0-0'; + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; else if (isX(p)) { if (M === '0') - ret = '>=' + M + '.' + m + '.0-0 <' + M + '.' + (+m + 1) + '.0-0'; + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; else - ret = '>=' + M + '.' + m + '.0-0 <' + (+M + 1) + '.0.0-0'; + ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0'; } else if (pr) { debug('replaceCaret pr', pr); if (pr.charAt(0) !== '-') pr = '-' + pr; if (M === '0') { if (m === '0') - ret = '=' + M + '.' + m + '.' + p + pr; + ret = '>=' + M + '.' + m + '.' + p + pr + + ' <' + M + '.' + m + '.' + (+p + 1); else ret = '>=' + M + '.' + m + '.' + p + pr + - ' <' + M + '.' + (+m + 1) + '.0-0'; + ' <' + M + '.' + (+m + 1) + '.0'; } else ret = '>=' + M + '.' + m + '.' + p + pr + - ' <' + (+M + 1) + '.0.0-0'; + ' <' + (+M + 1) + '.0.0'; } else { + debug('no pr'); if (M === '0') { if (m === '0') - ret = '=' + M + '.' + m + '.' + p; + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + m + '.' + (+p + 1); else - ret = '>=' + M + '.' + m + '.' + p + '-0' + - ' <' + M + '.' + (+m + 1) + '.0-0'; + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0'; } else - ret = '>=' + M + '.' + m + '.' + p + '-0' + - ' <' + (+M + 1) + '.0.0-0'; + ret = '>=' + M + '.' + m + '.' + p + + ' <' + (+M + 1) + '.0.0'; } debug('caret return', ret); @@ -791,23 +948,27 @@ function replaceXRange(comp, loose) { if (gtlt === '=' && anyX) gtlt = ''; - if (gtlt && anyX) { - // replace X with 0, and then append the -0 min-prerelease - if (xM) - M = 0; + if (xM) { + if (gtlt === '>' || gtlt === '<') { + // nothing is allowed + ret = '<0.0.0'; + } else { + // nothing is forbidden + ret = '*'; + } + } else if (gtlt && anyX) { + // replace X with 0 if (xm) m = 0; if (xp) p = 0; if (gtlt === '>') { - // >1 => >=2.0.0-0 - // >1.2 => >=1.3.0-0 - // >1.2.3 => >= 1.2.4-0 + // >1 => >=2.0.0 + // >1.2 => >=1.3.0 + // >1.2.3 => >= 1.2.4 gtlt = '>='; - if (xM) { - // no change - } else if (xm) { + if (xm) { M = +M + 1; m = 0; p = 0; @@ -815,20 +976,21 @@ function replaceXRange(comp, loose) { m = +m + 1; p = 0; } + } else if (gtlt === '<=') { + // <=0.7.x is actually <0.8.0, since any 0.7.x should + // pass. Similarly, <=7.x is actually <8.0.0, etc. + gtlt = '<' + if (xm) + M = +M + 1 + else + m = +m + 1 } - - ret = gtlt + M + '.' + m + '.' + p + '-0'; - } else if (xM) { - // allow any - ret = '*'; + ret = gtlt + M + '.' + m + '.' + p; } else if (xm) { - // append '-0' onto the version, otherwise - // '1.x.x' matches '2.0.0-beta', since the tag - // *lowers* the version value - ret = '>=' + M + '.0.0-0 <' + (+M + 1) + '.0.0-0'; + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; } else if (xp) { - ret = '>=' + M + '.' + m + '.0-0 <' + M + '.' + (+m + 1) + '.0-0'; + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; } debug('xRange return', ret); @@ -847,9 +1009,9 @@ function replaceStars(comp, loose) { // This function is passed to string.replace(re[HYPHENRANGE]) // M, m, patch, prerelease, build -// 1.2 - 3.4.5 => >=1.2.0-0 <=3.4.5 -// 1.2.3 - 3.4 => >=1.2.0-0 <3.5.0-0 Any 3.4.x will do -// 1.2 - 3.4 => >=1.2.0-0 <3.5.0-0 +// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5 +// 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do +// 1.2 - 3.4 => >=1.2.0 <3.5.0 function hyphenReplace($0, from, fM, fm, fp, fpr, fb, to, tM, tm, tp, tpr, tb) { @@ -857,18 +1019,18 @@ function hyphenReplace($0, if (isX(fM)) from = ''; else if (isX(fm)) - from = '>=' + fM + '.0.0-0'; + from = '>=' + fM + '.0.0'; else if (isX(fp)) - from = '>=' + fM + '.' + fm + '.0-0'; + from = '>=' + fM + '.' + fm + '.0'; else from = '>=' + from; if (isX(tM)) to = ''; else if (isX(tm)) - to = '<' + (+tM + 1) + '.0.0-0'; + to = '<' + (+tM + 1) + '.0.0'; else if (isX(tp)) - to = '<' + tM + '.' + (+tm + 1) + '.0-0'; + to = '<' + tM + '.' + (+tm + 1) + '.0'; else if (tpr) to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr; else @@ -882,6 +1044,10 @@ function hyphenReplace($0, Range.prototype.test = function(version) { if (!version) return false; + + if (typeof version === 'string') + version = new SemVer(version, this.loose); + for (var i = 0; i < this.set.length; i++) { if (testSet(this.set[i], version)) return true; @@ -894,6 +1060,31 @@ function testSet(set, version) { if (!set[i].test(version)) return false; } + + if (version.prerelease.length) { + // Find the set of versions that are allowed to have prereleases + // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 + // That should allow `1.2.3-pr.2` to pass. + // However, `1.2.4-alpha.notready` should NOT be allowed, + // even though it's within the range set by the comparators. + for (var i = 0; i < set.length; i++) { + debug(set[i].semver); + if (set[i].semver === ANY) + return true; + + if (set[i].semver.prerelease.length > 0) { + var allowed = set[i].semver; + if (allowed.major === version.major && + allowed.minor === version.minor && + allowed.patch === version.patch) + return true; + } + } + + // Version has a -pre, but it's not one of the ones we like. + return false; + } + return true; } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js new file mode 100644 index 0000000..cd9de29 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -0,0 +1,4483 @@ +/*** This file is dynamically generated *** +█████▄ ▄████▄ █████▄ ▄████▄ ██████ ███████▄ ▄████▄ █████▄ ██ ██████ ██ ██ +██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ +█████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ +*/ +/*! tablesorter (FORK) - updated 03-30-2015 (v2.21.4)*/ +/* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ +(function(factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + module.exports = factory(require('jquery')); + } else { + factory(jQuery); + } +}(function($) { + +/*! TableSorter (FORK) v2.21.4 *//* +* Client-side table sorting with ease! +* @requires jQuery v1.2.6+ +* +* Copyright (c) 2007 Christian Bach +* fork maintained by Rob Garrison +* +* Examples and docs at: http://tablesorter.com +* Dual licensed under the MIT and GPL licenses: +* http://www.opensource.org/licenses/mit-license.php +* http://www.gnu.org/licenses/gpl.html +* +* @type jQuery +* @name tablesorter (FORK) +* @cat Plugins/Tablesorter +* @author Christian Bach - christian.bach@polyester.se +* @contributor Rob Garrison - https://github.com/Mottie/tablesorter +*/ +/*jshint browser:true, jquery:true, unused:false, expr: true */ +/*global console:false, alert:false, require:false, define:false, module:false */ +;(function($){ + 'use strict'; + $.extend({ + /*jshint supernew:true */ + tablesorter: new function() { + + var ts = this; + + ts.version = '2.21.4'; + + ts.parsers = []; + ts.widgets = []; + ts.defaults = { + + // *** appearance + theme : 'default', // adds tablesorter-{theme} to the table for styling + widthFixed : false, // adds colgroup to fix widths of columns + showProcessing : false, // show an indeterminate timer icon in the header when the table is sorted or filtered. + + headerTemplate : '{content}',// header layout template (HTML ok); {content} = innerHTML, {icon} = <i/> (class from cssIcon) + onRenderTemplate : null, // function(index, template){ return template; }, (template is a string) + onRenderHeader : null, // function(index){}, (nothing to return) + + // *** functionality + cancelSelection : true, // prevent text selection in the header + tabIndex : true, // add tabindex to header for keyboard accessibility + dateFormat : 'mmddyyyy', // other options: 'ddmmyyy' or 'yyyymmdd' + sortMultiSortKey : 'shiftKey', // key used to select additional columns + sortResetKey : 'ctrlKey', // key used to remove sorting on a column + usNumberFormat : true, // false for German '1.234.567,89' or French '1 234 567,89' + delayInit : false, // if false, the parsed table contents will not update until the first sort + serverSideSorting: false, // if true, server-side sorting should be performed because client-side sorting will be disabled, but the ui and events will still be used. + resort : true, // default setting to trigger a resort after an 'update', 'addRows', 'updateCell', etc has completed + + // *** sort options + headers : {}, // set sorter, string, empty, locked order, sortInitialOrder, filter, etc. + ignoreCase : true, // ignore case while sorting + sortForce : null, // column(s) first sorted; always applied + sortList : [], // Initial sort order; applied initially; updated when manually sorted + sortAppend : null, // column(s) sorted last; always applied + sortStable : false, // when sorting two rows with exactly the same content, the original sort order is maintained + + sortInitialOrder : 'asc', // sort direction on first click + sortLocaleCompare: false, // replace equivalent character (accented characters) + sortReset : false, // third click on the header will reset column to default - unsorted + sortRestart : false, // restart sort to 'sortInitialOrder' when clicking on previously unsorted columns + + emptyTo : 'bottom', // sort empty cell to bottom, top, none, zero, emptyMax, emptyMin + stringTo : 'max', // sort strings in numerical column as max, min, top, bottom, zero + textExtraction : 'basic', // text extraction method/function - function(node, table, cellIndex){} + textAttribute : 'data-text',// data-attribute that contains alternate cell text (used in default textExtraction function) + textSorter : null, // choose overall or specific column sorter function(a, b, direction, table, columnIndex) [alt: ts.sortText] + numberSorter : null, // choose overall numeric sorter function(a, b, direction, maxColumnValue) + + // *** widget options + widgets: [], // method to add widgets, e.g. widgets: ['zebra'] + widgetOptions : { + zebra : [ 'even', 'odd' ] // zebra widget alternating row class names + }, + initWidgets : true, // apply widgets on tablesorter initialization + widgetClass : 'widget-{name}', // table class name template to match to include a widget + + // *** callbacks + initialized : null, // function(table){}, + + // *** extra css class names + tableClass : '', + cssAsc : '', + cssDesc : '', + cssNone : '', + cssHeader : '', + cssHeaderRow : '', + cssProcessing : '', // processing icon applied to header during sort/filter + + cssChildRow : 'tablesorter-childRow', // class name indiciating that a row is to be attached to the its parent + cssIcon : 'tablesorter-icon', // if this class does not exist, the {icon} will not be added from the headerTemplate + cssIconNone : '', // class name added to the icon when there is no column sort + cssIconAsc : '', // class name added to the icon when the column has an ascending sort + cssIconDesc : '', // class name added to the icon when the column has a descending sort + cssInfoBlock : 'tablesorter-infoOnly', // don't sort tbody with this class name (only one class name allowed here!) + cssNoSort : 'tablesorter-noSort', // class name added to element inside header; clicking on it won't cause a sort + cssIgnoreRow : 'tablesorter-ignoreRow', // header row to ignore; cells within this row will not be added to c.$headers + + // *** selectors + selectorHeaders : '> thead th, > thead td', + selectorSort : 'th, td', // jQuery selector of content within selectorHeaders that is clickable to trigger a sort + selectorRemove : '.remove-me', + + // *** advanced + debug : false, + + // *** Internal variables + headerList: [], + empties: {}, + strings: {}, + parsers: [] + + // removed: widgetZebra: { css: ['even', 'odd'] } + + }; + + // internal css classes - these will ALWAYS be added to + // the table and MUST only contain one class name - fixes #381 + ts.css = { + table : 'tablesorter', + cssHasChild: 'tablesorter-hasChildRow', + childRow : 'tablesorter-childRow', + colgroup : 'tablesorter-colgroup', + header : 'tablesorter-header', + headerRow : 'tablesorter-headerRow', + headerIn : 'tablesorter-header-inner', + icon : 'tablesorter-icon', + processing : 'tablesorter-processing', + sortAsc : 'tablesorter-headerAsc', + sortDesc : 'tablesorter-headerDesc', + sortNone : 'tablesorter-headerUnSorted' + }; + + // labels applied to sortable headers for accessibility (aria) support + ts.language = { + sortAsc : 'Ascending sort applied, ', + sortDesc : 'Descending sort applied, ', + sortNone : 'No sort applied, ', + nextAsc : 'activate to apply an ascending sort', + nextDesc : 'activate to apply a descending sort', + nextNone : 'activate to remove the sort' + }; + + // These methods can be applied on table.config instance + ts.instanceMethods = {}; + + /* debuging utils */ + function log() { + var a = arguments[0], + s = arguments.length > 1 ? Array.prototype.slice.call(arguments) : a; + if (typeof console !== 'undefined' && typeof console.log !== 'undefined') { + console[ /error/i.test(a) ? 'error' : /warn/i.test(a) ? 'warn' : 'log' ](s); + } else { + alert(s); + } + } + + function benchmark(s, d) { + log(s + ' (' + (new Date().getTime() - d.getTime()) + 'ms)'); + } + + ts.log = log; + ts.benchmark = benchmark; + + // $.isEmptyObject from jQuery v1.4 + function isEmptyObject(obj) { + /*jshint forin: false */ + for (var name in obj) { + return false; + } + return true; + } + + ts.getElementText = function(c, node, cellIndex) { + if (!node) { return ''; } + var te, + t = c.textExtraction || '', + // node could be a jquery object + // http://jsperf.com/jquery-vs-instanceof-jquery/2 + $node = node.jquery ? node : $(node); + if (typeof(t) === 'string') { + // check data-attribute first when set to 'basic'; don't use node.innerText - it's really slow! + return $.trim( ( t === 'basic' ? $node.attr(c.textAttribute) || node.textContent : node.textContent ) || $node.text() || '' ); + } else { + if (typeof(t) === 'function') { + return $.trim( t($node[0], c.table, cellIndex) ); + } else if (typeof (te = ts.getColumnData( c.table, t, cellIndex )) === 'function') { + return $.trim( te($node[0], c.table, cellIndex) ); + } + } + // fallback + return $.trim( $node[0].textContent || $node.text() || '' ); + }; + + function detectParserForColumn(table, rows, rowIndex, cellIndex) { + var cur, $node, + c = table.config, + i = ts.parsers.length, + node = false, + nodeValue = '', + keepLooking = true; + while (nodeValue === '' && keepLooking) { + rowIndex++; + if (rows[rowIndex]) { + node = rows[rowIndex].cells[cellIndex]; + nodeValue = ts.getElementText(c, node, cellIndex); + $node = $(node); + if (table.config.debug) { + log('Checking if value was empty on row ' + rowIndex + ', column: ' + cellIndex + ': "' + nodeValue + '"'); + } + } else { + keepLooking = false; + } + } + while (--i >= 0) { + cur = ts.parsers[i]; + // ignore the default text parser because it will always be true + if (cur && cur.id !== 'text' && cur.is && cur.is(nodeValue, table, node, $node)) { + return cur; + } + } + // nothing found, return the generic parser (text) + return ts.getParserById('text'); + } + + function buildParserCache(table) { + var c = table.config, + // update table bodies in case we start with an empty table + tb = c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'), + rows, list, l, i, h, ch, np, p, e, time, + j = 0, + parsersDebug = '', + len = tb.length; + if ( len === 0) { + return c.debug ? log('Warning: *Empty table!* Not building a parser cache') : ''; + } else if (c.debug) { + time = new Date(); + log('Detecting parsers for each column'); + } + list = { + extractors: [], + parsers: [] + }; + while (j < len) { + rows = tb[j].rows; + if (rows.length) { + l = c.columns; // rows[j].cells.length; + for (i = 0; i < l; i++) { + h = c.$headerIndexed[i]; + // get column indexed table cell + ch = ts.getColumnData( table, c.headers, i ); + // get column parser/extractor + e = ts.getParserById( ts.getData(h, ch, 'extractor') ); + p = ts.getParserById( ts.getData(h, ch, 'sorter') ); + np = ts.getData(h, ch, 'parser') === 'false'; + // empty cells behaviour - keeping emptyToBottom for backwards compatibility + c.empties[i] = ( ts.getData(h, ch, 'empty') || c.emptyTo || (c.emptyToBottom ? 'bottom' : 'top' ) ).toLowerCase(); + // text strings behaviour in numerical sorts + c.strings[i] = ( ts.getData(h, ch, 'string') || c.stringTo || 'max' ).toLowerCase(); + if (np) { + p = ts.getParserById('no-parser'); + } + if (!e) { + // For now, maybe detect someday + e = false; + } + if (!p) { + p = detectParserForColumn(table, rows, -1, i); + } + if (c.debug) { + parsersDebug += 'column:' + i + '; extractor:' + e.id + '; parser:' + p.id + '; string:' + c.strings[i] + '; empty: ' + c.empties[i] + '\n'; + } + list.parsers[i] = p; + list.extractors[i] = e; + } + } + j += (list.parsers.length) ? len : 1; + } + if (c.debug) { + log(parsersDebug ? parsersDebug : 'No parsers detected'); + benchmark('Completed detecting parsers', time); + } + c.parsers = list.parsers; + c.extractors = list.extractors; + } + + /* utils */ + function buildCache(table) { + var cc, t, tx, v, i, j, k, $row, cols, cacheTime, + totalRows, rowData, colMax, + c = table.config, + $tb = c.$tbodies, + extractors = c.extractors, + parsers = c.parsers; + c.cache = {}; + c.totalRows = 0; + // if no parsers found, return - it's an empty table. + if (!parsers) { + return c.debug ? log('Warning: *Empty table!* Not building a cache') : ''; + } + if (c.debug) { + cacheTime = new Date(); + } + // processing icon + if (c.showProcessing) { + ts.isProcessing(table, true); + } + for (k = 0; k < $tb.length; k++) { + colMax = []; // column max value per tbody + cc = c.cache[k] = { + normalized: [] // array of normalized row data; last entry contains 'rowData' above + // colMax: # // added at the end + }; + + totalRows = ($tb[k] && $tb[k].rows.length) || 0; + for (i = 0; i < totalRows; ++i) { + rowData = { + // order: original row order # + // $row : jQuery Object[] + child: [], // child row text (filter widget) + raw: [] // original row text + }; + /** Add the table data to main data array */ + $row = $($tb[k].rows[i]); + cols = []; + // if this is a child row, add it to the last row's children and continue to the next row + // ignore child row class, if it is the first row + if ($row.hasClass(c.cssChildRow) && i !== 0) { + t = cc.normalized.length - 1; + cc.normalized[t][c.columns].$row = cc.normalized[t][c.columns].$row.add($row); + // add 'hasChild' class name to parent row + if (!$row.prev().hasClass(c.cssChildRow)) { + $row.prev().addClass(ts.css.cssHasChild); + } + // save child row content (un-parsed!) + rowData.child[t] = $.trim( $row[0].textContent || $row.text() || '' ); + // go to the next for loop + continue; + } + rowData.$row = $row; + rowData.order = i; // add original row position to rowCache + for (j = 0; j < c.columns; ++j) { + if (typeof parsers[j] === 'undefined') { + if (c.debug) { + log('No parser found for cell:', $row[0].cells[j], 'does it have a header?'); + } + continue; + } + t = ts.getElementText(c, $row[0].cells[j], j); + rowData.raw.push(t); // save original row text + // do extract before parsing if there is one + if (typeof extractors[j].id === 'undefined') { + tx = t; + } else { + tx = extractors[j].format(t, table, $row[0].cells[j], j); + } + // allow parsing if the string is empty, previously parsing would change it to zero, + // in case the parser needs to extract data from the table cell attributes + v = parsers[j].id === 'no-parser' ? '' : parsers[j].format(tx, table, $row[0].cells[j], j); + cols.push( c.ignoreCase && typeof v === 'string' ? v.toLowerCase() : v ); + if ((parsers[j].type || '').toLowerCase() === 'numeric') { + // determine column max value (ignore sign) + colMax[j] = Math.max(Math.abs(v) || 0, colMax[j] || 0); + } + } + // ensure rowData is always in the same location (after the last column) + cols[c.columns] = rowData; + cc.normalized.push(cols); + } + cc.colMax = colMax; + // total up rows, not including child rows + c.totalRows += cc.normalized.length; + + } + if (c.showProcessing) { + ts.isProcessing(table); // remove processing icon + } + if (c.debug) { + benchmark('Building cache for ' + totalRows + ' rows', cacheTime); + } + } + + // init flag (true) used by pager plugin to prevent widget application + function appendToTable(table, init) { + var c = table.config, + wo = c.widgetOptions, + $tbodies = c.$tbodies, + rows = [], + cc = c.cache, + n, totalRows, $bk, $tb, + i, k, appendTime; + // empty table - fixes #206/#346 + if (isEmptyObject(cc)) { + // run pager appender in case the table was just emptied + return c.appender ? c.appender(table, rows) : + table.isUpdating ? c.$table.trigger('updateComplete', table) : ''; // Fixes #532 + } + if (c.debug) { + appendTime = new Date(); + } + for (k = 0; k < $tbodies.length; k++) { + $bk = $tbodies.eq(k); + if ($bk.length) { + // get tbody + $tb = ts.processTbody(table, $bk, true); + n = cc[k].normalized; + totalRows = n.length; + for (i = 0; i < totalRows; i++) { + rows.push(n[i][c.columns].$row); + // removeRows used by the pager plugin; don't render if using ajax - fixes #411 + if (!c.appender || (c.pager && (!c.pager.removeRows || !wo.pager_removeRows) && !c.pager.ajax)) { + $tb.append(n[i][c.columns].$row); + } + } + // restore tbody + ts.processTbody(table, $tb, false); + } + } + if (c.appender) { + c.appender(table, rows); + } + if (c.debug) { + benchmark('Rebuilt table', appendTime); + } + // apply table widgets; but not before ajax completes + if (!init && !c.appender) { ts.applyWidget(table); } + if (table.isUpdating) { + c.$table.trigger('updateComplete', table); + } + } + + function formatSortingOrder(v) { + // look for 'd' in 'desc' order; return true + return (/^d/i.test(v) || v === 1); + } + + function buildHeaders(table) { + var ch, $t, h, i, t, lock, time, indx, + c = table.config; + c.headerList = []; + c.headerContent = []; + if (c.debug) { + time = new Date(); + } + // children tr in tfoot - see issue #196 & #547 + c.columns = ts.computeColumnIndex( c.$table.children('thead, tfoot').children('tr') ); + // add icon if cssIcon option exists + i = c.cssIcon ? '<i class="' + ( c.cssIcon === ts.css.icon ? ts.css.icon : c.cssIcon + ' ' + ts.css.icon ) + '"></i>' : ''; + // redefine c.$headers here in case of an updateAll that replaces or adds an entire header cell - see #683 + c.$headers = $( $.map( $(table).find(c.selectorHeaders), function(elem, index) { + $t = $(elem); + // ignore cell (don't add it to c.$headers) if row has ignoreRow class + if ($t.parent().hasClass(c.cssIgnoreRow)) { return; } + // make sure to get header cell & not column indexed cell + ch = ts.getColumnData( table, c.headers, index, true ); + // save original header content + c.headerContent[index] = $t.html(); + // if headerTemplate is empty, don't reformat the header cell + if ( c.headerTemplate !== '' && !$t.find('.' + ts.css.headerIn).length ) { + // set up header template + t = c.headerTemplate.replace(/\{content\}/g, $t.html()).replace(/\{icon\}/g, $t.find('.' + ts.css.icon).length ? '' : i); + if (c.onRenderTemplate) { + h = c.onRenderTemplate.apply($t, [index, t]); + if (h && typeof h === 'string') { t = h; } // only change t if something is returned + } + $t.html('<div class="' + ts.css.headerIn + '">' + t + '</div>'); // faster than wrapInner + } + if (c.onRenderHeader) { c.onRenderHeader.apply($t, [index, c, c.$table]); } + // *** remove this.column value if no conflicts found + elem.column = parseInt( $t.attr('data-column'), 10); + elem.order = formatSortingOrder( ts.getData($t, ch, 'sortInitialOrder') || c.sortInitialOrder ) ? [1,0,2] : [0,1,2]; + elem.count = -1; // set to -1 because clicking on the header automatically adds one + elem.lockedOrder = false; + lock = ts.getData($t, ch, 'lockedOrder') || false; + if (typeof lock !== 'undefined' && lock !== false) { + elem.order = elem.lockedOrder = formatSortingOrder(lock) ? [1,1,1] : [0,0,0]; + } + $t.addClass(ts.css.header + ' ' + c.cssHeader); + // add cell to headerList + c.headerList[index] = elem; + // add to parent in case there are multiple rows + $t.parent().addClass(ts.css.headerRow + ' ' + c.cssHeaderRow).attr('role', 'row'); + // allow keyboard cursor to focus on element + if (c.tabIndex) { $t.attr('tabindex', 0); } + return elem; + })); + // cache headers per column + c.$headerIndexed = []; + for (indx = 0; indx < c.columns; indx++) { + $t = c.$headers.filter('[data-column="' + indx + '"]'); + // target sortable column cells, unless there are none, then use non-sortable cells + // .last() added in jQuery 1.4; use .filter(':last') to maintain compatibility with jQuery v1.2.6 + c.$headerIndexed[indx] = $t.not('.sorter-false').length ? $t.not('.sorter-false').filter(':last') : $t.filter(':last'); + } + $(table).find(c.selectorHeaders).attr({ + scope: 'col', + role : 'columnheader' + }); + // enable/disable sorting + updateHeader(table); + if (c.debug) { + benchmark('Built headers:', time); + log(c.$headers); + } + } + + function commonUpdate(table, resort, callback) { + var c = table.config; + // remove rows/elements before update + c.$table.find(c.selectorRemove).remove(); + // rebuild parsers + buildParserCache(table); + // rebuild the cache map + buildCache(table); + checkResort(c, resort, callback); + } + + function updateHeader(table) { + var s, $th, col, + c = table.config; + c.$headers.each(function(index, th){ + $th = $(th); + col = ts.getColumnData( table, c.headers, index, true ); + // add 'sorter-false' class if 'parser-false' is set + s = ts.getData( th, col, 'sorter' ) === 'false' || ts.getData( th, col, 'parser' ) === 'false'; + th.sortDisabled = s; + $th[ s ? 'addClass' : 'removeClass' ]('sorter-false').attr('aria-disabled', '' + s); + // aria-controls - requires table ID + if (table.id) { + if (s) { + $th.removeAttr('aria-controls'); + } else { + $th.attr('aria-controls', table.id); + } + } + }); + } + + function setHeadersCss(table) { + var f, i, j, + c = table.config, + list = c.sortList, + len = list.length, + none = ts.css.sortNone + ' ' + c.cssNone, + css = [ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc], + cssIcon = [ c.cssIconAsc, c.cssIconDesc, c.cssIconNone ], + aria = ['ascending', 'descending'], + // find the footer + $t = $(table).find('tfoot tr').children() + .add( $( c.namespace + '_extra_headers' ) ) + .removeClass( css.join( ' ' ) ); + // remove all header information + c.$headers + .removeClass(css.join(' ')) + .addClass(none).attr('aria-sort', 'none') + .find('.' + ts.css.icon) + .removeClass(cssIcon.join(' ')) + .addClass(cssIcon[2]); + for (i = 0; i < len; i++) { + // direction = 2 means reset! + if (list[i][1] !== 2) { + // multicolumn sorting updating - choose the :last in case there are nested columns + f = c.$headers.not('.sorter-false').filter('[data-column="' + list[i][0] + '"]' + (len === 1 ? ':last' : '') ); + if (f.length) { + for (j = 0; j < f.length; j++) { + if (!f[j].sortDisabled) { + f.eq(j) + .removeClass(none) + .addClass(css[list[i][1]]) + .attr('aria-sort', aria[list[i][1]]) + .find('.' + ts.css.icon) + .removeClass(cssIcon[2]) + .addClass(cssIcon[list[i][1]]); + } + } + // add sorted class to footer & extra headers, if they exist + if ($t.length) { + $t.filter('[data-column="' + list[i][0] + '"]').removeClass(none).addClass(css[list[i][1]]); + } + } + } + } + // add verbose aria labels + c.$headers.not('.sorter-false').each(function(){ + var $this = $(this), + nextSort = this.order[(this.count + 1) % (c.sortReset ? 3 : 2)], + txt = $.trim( $this.text() ) + ': ' + + ts.language[ $this.hasClass(ts.css.sortAsc) ? 'sortAsc' : $this.hasClass(ts.css.sortDesc) ? 'sortDesc' : 'sortNone' ] + + ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ]; + $this.attr('aria-label', txt ); + }); + } + + function updateHeaderSortCount( table, list ) { + var col, dir, group, header, indx, primary, temp, val, + c = table.config, + sortList = list || c.sortList, + len = sortList.length; + c.sortList = []; + for (indx = 0; indx < len; indx++) { + val = sortList[indx]; + // ensure all sortList values are numeric - fixes #127 + col = parseInt(val[0], 10); + // make sure header exists + header = c.$headerIndexed[col][0]; + if (header) { // prevents error if sorton array is wrong + // o.count = o.count + 1; + dir = ('' + val[1]).match(/^(1|d|s|o|n)/); + dir = dir ? dir[0] : ''; + // 0/(a)sc (default), 1/(d)esc, (s)ame, (o)pposite, (n)ext + switch(dir) { + case '1': case 'd': // descending + dir = 1; + break; + case 's': // same direction (as primary column) + // if primary sort is set to 's', make it ascending + dir = primary || 0; + break; + case 'o': + temp = header.order[(primary || 0) % (c.sortReset ? 3 : 2)]; + // opposite of primary column; but resets if primary resets + dir = temp === 0 ? 1 : temp === 1 ? 0 : 2; + break; + case 'n': + header.count = header.count + 1; + dir = header.order[(header.count) % (c.sortReset ? 3 : 2)]; + break; + default: // ascending + dir = 0; + break; + } + primary = indx === 0 ? dir : primary; + group = [ col, parseInt(dir, 10) || 0 ]; + c.sortList.push(group); + dir = $.inArray(group[1], header.order); // fixes issue #167 + header.count = dir >= 0 ? dir : group[1] % (c.sortReset ? 3 : 2); + } + } + } + + function getCachedSortType(parsers, i) { + return (parsers && parsers[i]) ? parsers[i].type || '' : ''; + } + + function initSort(table, cell, event){ + if (table.isUpdating) { + // let any updates complete before initializing a sort + return setTimeout(function(){ initSort(table, cell, event); }, 50); + } + var arry, indx, col, order, s, + c = table.config, + key = !event[c.sortMultiSortKey], + $table = c.$table; + // Only call sortStart if sorting is enabled + $table.trigger('sortStart', table); + // get current column sort order + cell.count = event[c.sortResetKey] ? 2 : (cell.count + 1) % (c.sortReset ? 3 : 2); + // reset all sorts on non-current column - issue #30 + if (c.sortRestart) { + indx = cell; + c.$headers.each(function() { + // only reset counts on columns that weren't just clicked on and if not included in a multisort + if (this !== indx && (key || !$(this).is('.' + ts.css.sortDesc + ',.' + ts.css.sortAsc))) { + this.count = -1; + } + }); + } + // get current column index + indx = parseInt( $(cell).attr('data-column'), 10 ); + // user only wants to sort on one column + if (key) { + // flush the sort list + c.sortList = []; + if (c.sortForce !== null) { + arry = c.sortForce; + for (col = 0; col < arry.length; col++) { + if (arry[col][0] !== indx) { + c.sortList.push(arry[col]); + } + } + } + // add column to sort list + order = cell.order[cell.count]; + if (order < 2) { + c.sortList.push([indx, order]); + // add other columns if header spans across multiple + if (cell.colSpan > 1) { + for (col = 1; col < cell.colSpan; col++) { + c.sortList.push([indx + col, order]); + } + } + } + // multi column sorting + } else { + // get rid of the sortAppend before adding more - fixes issue #115 & #523 + if (c.sortAppend && c.sortList.length > 1) { + for (col = 0; col < c.sortAppend.length; col++) { + s = ts.isValueInArray(c.sortAppend[col][0], c.sortList); + if (s >= 0) { + c.sortList.splice(s,1); + } + } + } + // the user has clicked on an already sorted column + if (ts.isValueInArray(indx, c.sortList) >= 0) { + // reverse the sorting direction + for (col = 0; col < c.sortList.length; col++) { + s = c.sortList[col]; + order = c.$headerIndexed[ s[0] ][0]; + if (s[0] === indx) { + // order.count seems to be incorrect when compared to cell.count + s[1] = order.order[cell.count]; + if (s[1] === 2) { + c.sortList.splice(col,1); + order.count = -1; + } + } + } + } else { + // add column to sort list array + order = cell.order[cell.count]; + if (order < 2) { + c.sortList.push([indx, order]); + // add other columns if header spans across multiple + if (cell.colSpan > 1) { + for (col = 1; col < cell.colSpan; col++) { + c.sortList.push([indx + col, order]); + } + } + } + } + } + if (c.sortAppend !== null) { + arry = c.sortAppend; + for (col = 0; col < arry.length; col++) { + if (arry[col][0] !== indx) { + c.sortList.push(arry[col]); + } + } + } + // sortBegin event triggered immediately before the sort + $table.trigger('sortBegin', table); + // setTimeout needed so the processing icon shows up + setTimeout(function(){ + // set css for headers + setHeadersCss(table); + multisort(table); + appendToTable(table); + $table.trigger('sortEnd', table); + }, 1); + } + + // sort multiple columns + function multisort(table) { /*jshint loopfunc:true */ + var i, k, num, col, sortTime, colMax, + rows, order, sort, x, y, + dir = 0, + c = table.config, + cts = c.textSorter || '', + sortList = c.sortList, + l = sortList.length, + bl = c.$tbodies.length; + if (c.serverSideSorting || isEmptyObject(c.cache)) { // empty table - fixes #206/#346 + return; + } + if (c.debug) { sortTime = new Date(); } + for (k = 0; k < bl; k++) { + colMax = c.cache[k].colMax; + rows = c.cache[k].normalized; + + rows.sort(function(a, b) { + // rows is undefined here in IE, so don't use it! + for (i = 0; i < l; i++) { + col = sortList[i][0]; + order = sortList[i][1]; + // sort direction, true = asc, false = desc + dir = order === 0; + + if (c.sortStable && a[col] === b[col] && l === 1) { + return a[c.columns].order - b[c.columns].order; + } + + // fallback to natural sort since it is more robust + num = /n/i.test(getCachedSortType(c.parsers, col)); + if (num && c.strings[col]) { + // sort strings in numerical columns + if (typeof (c.string[c.strings[col]]) === 'boolean') { + num = (dir ? 1 : -1) * (c.string[c.strings[col]] ? -1 : 1); + } else { + num = (c.strings[col]) ? c.string[c.strings[col]] || 0 : 0; + } + // fall back to built-in numeric sort + // var sort = $.tablesorter['sort' + s](table, a[c], b[c], c, colMax[c], dir); + sort = c.numberSorter ? c.numberSorter(a[col], b[col], dir, colMax[col], table) : + ts[ 'sortNumeric' + (dir ? 'Asc' : 'Desc') ](a[col], b[col], num, colMax[col], col, table); + } else { + // set a & b depending on sort direction + x = dir ? a : b; + y = dir ? b : a; + // text sort function + if (typeof(cts) === 'function') { + // custom OVERALL text sorter + sort = cts(x[col], y[col], dir, col, table); + } else if (typeof(cts) === 'object' && cts.hasOwnProperty(col)) { + // custom text sorter for a SPECIFIC COLUMN + sort = cts[col](x[col], y[col], dir, col, table); + } else { + // fall back to natural sort + sort = ts[ 'sortNatural' + (dir ? 'Asc' : 'Desc') ](a[col], b[col], col, table, c); + } + } + if (sort) { return sort; } + } + return a[c.columns].order - b[c.columns].order; + }); + } + if (c.debug) { benchmark('Sorting on ' + sortList.toString() + ' and dir ' + order + ' time', sortTime); } + } + + function resortComplete(c, callback){ + if (c.table.isUpdating) { + c.$table.trigger('updateComplete', c.table); + } + if ($.isFunction(callback)) { + callback(c.table); + } + } + + function checkResort(c, resort, callback) { + var sl = $.isArray(resort) ? resort : c.sortList, + // if no resort parameter is passed, fallback to config.resort (true by default) + resrt = typeof resort === 'undefined' ? c.resort : resort; + // don't try to resort if the table is still processing + // this will catch spamming of the updateCell method + if (resrt !== false && !c.serverSideSorting && !c.table.isProcessing) { + if (sl.length) { + c.$table.trigger('sorton', [sl, function(){ + resortComplete(c, callback); + }, true]); + } else { + c.$table.trigger('sortReset', [function(){ + resortComplete(c, callback); + ts.applyWidget(c.table, false); + }]); + } + } else { + resortComplete(c, callback); + ts.applyWidget(c.table, false); + } + } + + function bindMethods(table){ + var c = table.config, + $table = c.$table, + events = ('sortReset update updateRows updateCell updateAll addRows updateComplete sorton appendCache ' + + 'updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave ').split(' ') + .join(c.namespace + ' '); + // apply easy methods that trigger bound events + $table + .unbind( events.replace(/\s+/g, ' ') ) + .bind('sortReset' + c.namespace, function(e, callback){ + e.stopPropagation(); + c.sortList = []; + setHeadersCss(table); + multisort(table); + appendToTable(table); + if ($.isFunction(callback)) { + callback(table); + } + }) + .bind('updateAll' + c.namespace, function(e, resort, callback){ + e.stopPropagation(); + table.isUpdating = true; + ts.refreshWidgets(table, true, true); + buildHeaders(table); + ts.bindEvents(table, c.$headers, true); + bindMethods(table); + commonUpdate(table, resort, callback); + }) + .bind('update' + c.namespace + ' updateRows' + c.namespace, function(e, resort, callback) { + e.stopPropagation(); + table.isUpdating = true; + // update sorting (if enabled/disabled) + updateHeader(table); + commonUpdate(table, resort, callback); + }) + .bind('updateCell' + c.namespace, function(e, cell, resort, callback) { + e.stopPropagation(); + table.isUpdating = true; + $table.find(c.selectorRemove).remove(); + // get position from the dom + var v, t, row, icell, + $tb = c.$tbodies, + $cell = $(cell), + // update cache - format: function(s, table, cell, cellIndex) + // no closest in jQuery v1.2.6 - tbdy = $tb.index( $(cell).closest('tbody') ),$row = $(cell).closest('tr'); + tbdy = $tb.index( $.fn.closest ? $cell.closest('tbody') : $cell.parents('tbody').filter(':first') ), + $row = $.fn.closest ? $cell.closest('tr') : $cell.parents('tr').filter(':first'); + cell = $cell[0]; // in case cell is a jQuery object + // tbody may not exist if update is initialized while tbody is removed for processing + if ($tb.length && tbdy >= 0) { + row = $tb.eq(tbdy).find('tr').index( $row ); + icell = $cell.index(); + c.cache[tbdy].normalized[row][c.columns].$row = $row; + if (typeof c.extractors[icell].id === 'undefined') { + t = ts.getElementText(c, cell, icell); + } else { + t = c.extractors[icell].format( ts.getElementText(c, cell, icell), table, cell, icell ); + } + v = c.parsers[icell].id === 'no-parser' ? '' : + c.parsers[icell].format( t, table, cell, icell ); + c.cache[tbdy].normalized[row][icell] = c.ignoreCase && typeof v === 'string' ? v.toLowerCase() : v; + if ((c.parsers[icell].type || '').toLowerCase() === 'numeric') { + // update column max value (ignore sign) + c.cache[tbdy].colMax[icell] = Math.max(Math.abs(v) || 0, c.cache[tbdy].colMax[icell] || 0); + } + v = resort !== 'undefined' ? resort : c.resort; + if (v !== false) { + // widgets will be reapplied + checkResort(c, v, callback); + } else { + // don't reapply widgets is resort is false, just in case it causes + // problems with element focus + if ($.isFunction(callback)) { + callback(table); + } + c.$table.trigger('updateComplete', c.table); + } + } + }) + .bind('addRows' + c.namespace, function(e, $row, resort, callback) { + e.stopPropagation(); + table.isUpdating = true; + if (isEmptyObject(c.cache)) { + // empty table, do an update instead - fixes #450 + updateHeader(table); + commonUpdate(table, resort, callback); + } else { + $row = $($row).attr('role', 'row'); // make sure we're using a jQuery object + var i, j, l, t, v, rowData, cells, + rows = $row.filter('tr').length, + tbdy = c.$tbodies.index( $row.parents('tbody').filter(':first') ); + // fixes adding rows to an empty table - see issue #179 + if (!(c.parsers && c.parsers.length)) { + buildParserCache(table); + } + // add each row + for (i = 0; i < rows; i++) { + l = $row[i].cells.length; + cells = []; + rowData = { + child: [], + $row : $row.eq(i), + order: c.cache[tbdy].normalized.length + }; + // add each cell + for (j = 0; j < l; j++) { + if (typeof c.extractors[j].id === 'undefined') { + t = ts.getElementText(c, $row[i].cells[j], j); + } else { + t = c.extractors[j].format( ts.getElementText(c, $row[i].cells[j], j), table, $row[i].cells[j], j ); + } + v = c.parsers[j].id === 'no-parser' ? '' : + c.parsers[j].format( t, table, $row[i].cells[j], j ); + cells[j] = c.ignoreCase && typeof v === 'string' ? v.toLowerCase() : v; + if ((c.parsers[j].type || '').toLowerCase() === 'numeric') { + // update column max value (ignore sign) + c.cache[tbdy].colMax[j] = Math.max(Math.abs(cells[j]) || 0, c.cache[tbdy].colMax[j] || 0); + } + } + // add the row data to the end + cells.push(rowData); + // update cache + c.cache[tbdy].normalized.push(cells); + } + // resort using current settings + checkResort(c, resort, callback); + } + }) + .bind('updateComplete' + c.namespace, function(){ + table.isUpdating = false; + }) + .bind('sorton' + c.namespace, function(e, list, callback, init) { + var c = table.config; + e.stopPropagation(); + $table.trigger('sortStart', this); + // update header count index + updateHeaderSortCount(table, list); + // set css for headers + setHeadersCss(table); + // fixes #346 + if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); } + $table.trigger('sortBegin', this); + // sort the table and append it to the dom + multisort(table); + appendToTable(table, init); + $table.trigger('sortEnd', this); + ts.applyWidget(table); + if ($.isFunction(callback)) { + callback(table); + } + }) + .bind('appendCache' + c.namespace, function(e, callback, init) { + e.stopPropagation(); + appendToTable(table, init); + if ($.isFunction(callback)) { + callback(table); + } + }) + .bind('updateCache' + c.namespace, function(e, callback){ + // rebuild parsers + if (!(c.parsers && c.parsers.length)) { + buildParserCache(table); + } + // rebuild the cache map + buildCache(table); + if ($.isFunction(callback)) { + callback(table); + } + }) + .bind('applyWidgetId' + c.namespace, function(e, id) { + e.stopPropagation(); + ts.getWidgetById(id).format(table, c, c.widgetOptions); + }) + .bind('applyWidgets' + c.namespace, function(e, init) { + e.stopPropagation(); + // apply widgets + ts.applyWidget(table, init); + }) + .bind('refreshWidgets' + c.namespace, function(e, all, dontapply){ + e.stopPropagation(); + ts.refreshWidgets(table, all, dontapply); + }) + .bind('destroy' + c.namespace, function(e, c, cb){ + e.stopPropagation(); + ts.destroy(table, c, cb); + }) + .bind('resetToLoadState' + c.namespace, function(){ + // remove all widgets + ts.removeWidget(table, true, false); + // restore original settings; this clears out current settings, but does not clear + // values saved to storage. + c = $.extend(true, ts.defaults, c.originalSettings); + table.hasInitialized = false; + // setup the entire table again + ts.setup( table, c ); + }); + } + + /* public methods */ + ts.construct = function(settings) { + return this.each(function() { + var table = this, + // merge & extend config options + c = $.extend(true, {}, ts.defaults, settings, ts.instanceMethods); + // save initial settings + c.originalSettings = settings; + // create a table from data (build table widget) + if (!table.hasInitialized && ts.buildTable && this.nodeName !== 'TABLE') { + // return the table (in case the original target is the table's container) + ts.buildTable(table, c); + } else { + ts.setup(table, c); + } + }); + }; + + ts.setup = function(table, c) { + // if no thead or tbody, or tablesorter is already present, quit + if (!table || !table.tHead || table.tBodies.length === 0 || table.hasInitialized === true) { + return c.debug ? log('ERROR: stopping initialization! No table, thead, tbody or tablesorter has already been initialized') : ''; + } + + var k = '', + $table = $(table), + m = $.metadata; + // initialization flag + table.hasInitialized = false; + // table is being processed flag + table.isProcessing = true; + // make sure to store the config object + table.config = c; + // save the settings where they read + $.data(table, 'tablesorter', c); + if (c.debug) { $.data( table, 'startoveralltimer', new Date()); } + + // removing this in version 3 (only supports jQuery 1.7+) + c.supportsDataObject = (function(version) { + version[0] = parseInt(version[0], 10); + return (version[0] > 1) || (version[0] === 1 && parseInt(version[1], 10) >= 4); + })($.fn.jquery.split('.')); + // digit sort text location; keeping max+/- for backwards compatibility + c.string = { 'max': 1, 'min': -1, 'emptymin': 1, 'emptymax': -1, 'zero': 0, 'none': 0, 'null': 0, 'top': true, 'bottom': false }; + // ensure case insensitivity + c.emptyTo = c.emptyTo.toLowerCase(); + c.stringTo = c.stringTo.toLowerCase(); + // add table theme class only if there isn't already one there + if (!/tablesorter\-/.test($table.attr('class'))) { + k = (c.theme !== '' ? ' tablesorter-' + c.theme : ''); + } + c.table = table; + c.$table = $table + .addClass(ts.css.table + ' ' + c.tableClass + k) + .attr('role', 'grid'); + c.$headers = $table.find(c.selectorHeaders); + + // give the table a unique id, which will be used in namespace binding + if (!c.namespace) { + c.namespace = '.tablesorter' + Math.random().toString(16).slice(2); + } else { + // make sure namespace starts with a period & doesn't have weird characters + c.namespace = '.' + c.namespace.replace(/\W/g,''); + } + + c.$table.children().children('tr').attr('role', 'row'); + c.$tbodies = $table.children('tbody:not(.' + c.cssInfoBlock + ')').attr({ + 'aria-live' : 'polite', + 'aria-relevant' : 'all' + }); + if (c.$table.children('caption').length) { + k = c.$table.children('caption')[0]; + if (!k.id) { k.id = c.namespace.slice(1) + 'caption'; } + c.$table.attr('aria-labelledby', k.id); + } + c.widgetInit = {}; // keep a list of initialized widgets + // change textExtraction via data-attribute + c.textExtraction = c.$table.attr('data-text-extraction') || c.textExtraction || 'basic'; + // build headers + buildHeaders(table); + // fixate columns if the users supplies the fixedWidth option + // do this after theme has been applied + ts.fixColumnWidth(table); + // add widget options before parsing (e.g. grouping widget has parser settings) + ts.applyWidgetOptions(table, c); + // try to auto detect column type, and store in tables config + buildParserCache(table); + // start total row count at zero + c.totalRows = 0; + // build the cache for the tbody cells + // delayInit will delay building the cache until the user starts a sort + if (!c.delayInit) { buildCache(table); } + // bind all header events and methods + ts.bindEvents(table, c.$headers, true); + bindMethods(table); + // get sort list from jQuery data or metadata + // in jQuery < 1.4, an error occurs when calling $table.data() + if (c.supportsDataObject && typeof $table.data().sortlist !== 'undefined') { + c.sortList = $table.data().sortlist; + } else if (m && ($table.metadata() && $table.metadata().sortlist)) { + c.sortList = $table.metadata().sortlist; + } + // apply widget init code + ts.applyWidget(table, true); + // if user has supplied a sort list to constructor + if (c.sortList.length > 0) { + $table.trigger('sorton', [c.sortList, {}, !c.initWidgets, true]); + } else { + setHeadersCss(table); + if (c.initWidgets) { + // apply widget format + ts.applyWidget(table, false); + } + } + + // show processesing icon + if (c.showProcessing) { + $table + .unbind('sortBegin' + c.namespace + ' sortEnd' + c.namespace) + .bind('sortBegin' + c.namespace + ' sortEnd' + c.namespace, function(e) { + clearTimeout(c.processTimer); + ts.isProcessing(table); + if (e.type === 'sortBegin') { + c.processTimer = setTimeout(function(){ + ts.isProcessing(table, true); + }, 500); + } + }); + } + + // initialized + table.hasInitialized = true; + table.isProcessing = false; + if (c.debug) { + ts.benchmark('Overall initialization time', $.data( table, 'startoveralltimer')); + } + $table.trigger('tablesorter-initialized', table); + if (typeof c.initialized === 'function') { c.initialized(table); } + }; + + // automatically add a colgroup with col elements set to a percentage width + ts.fixColumnWidth = function(table) { + table = $(table)[0]; + var overallWidth, percent, + c = table.config, + colgroup = c.$table.children('colgroup'); + // remove plugin-added colgroup, in case we need to refresh the widths + if (colgroup.length && colgroup.hasClass(ts.css.colgroup)) { + colgroup.remove(); + } + if (c.widthFixed && c.$table.children('colgroup').length === 0) { + colgroup = $('<colgroup class="' + ts.css.colgroup + '">'); + overallWidth = c.$table.width(); + // only add col for visible columns - fixes #371 + c.$tbodies.find('tr:first').children(':visible').each(function() { + percent = parseInt( ( $(this).width() / overallWidth ) * 1000, 10 ) / 10 + '%'; + colgroup.append( $('<col>').css('width', percent) ); + }); + c.$table.prepend(colgroup); + } + }; + + ts.getColumnData = function(table, obj, indx, getCell, $headers){ + if (typeof obj === 'undefined' || obj === null) { return; } + table = $(table)[0]; + var $h, k, + c = table.config, + $cells = ( $headers || c.$headers ), + // c.$headerIndexed is not defined initially + $cell = c.$headerIndexed && c.$headerIndexed[indx] || $cells.filter('[data-column="' + indx + '"]:last'); + if (obj[indx]) { + return getCell ? obj[indx] : obj[$cells.index( $cell )]; + } + for (k in obj) { + if (typeof k === 'string') { + $h = $cell + // header cell with class/id + .filter(k) + // find elements within the header cell with cell/id + .add( $cell.find(k) ); + if ($h.length) { + return obj[k]; + } + } + } + return; + }; + + // computeTableHeaderCellIndexes from: + // http://www.javascripttoolbox.com/lib/table/examples.php + // http://www.javascripttoolbox.com/temp/table_cellindex.html + ts.computeColumnIndex = function(trs) { + var matrix = [], + lookup = {}, + i, j, k, l, $cell, cell, cells, rowIndex, cellId, rowSpan, colSpan, firstAvailCol, matrixrow; + for (i = 0; i < trs.length; i++) { + cells = trs[i].cells; + for (j = 0; j < cells.length; j++) { + cell = cells[j]; + $cell = $(cell); + rowIndex = cell.parentNode.rowIndex; + cellId = rowIndex + '-' + $cell.index(); + rowSpan = cell.rowSpan || 1; + colSpan = cell.colSpan || 1; + if (typeof(matrix[rowIndex]) === 'undefined') { + matrix[rowIndex] = []; + } + // Find first available column in the first row + for (k = 0; k < matrix[rowIndex].length + 1; k++) { + if (typeof(matrix[rowIndex][k]) === 'undefined') { + firstAvailCol = k; + break; + } + } + lookup[cellId] = firstAvailCol; + // add data-column + $cell.attr({ 'data-column' : firstAvailCol }); // 'data-row' : rowIndex + for (k = rowIndex; k < rowIndex + rowSpan; k++) { + if (typeof(matrix[k]) === 'undefined') { + matrix[k] = []; + } + matrixrow = matrix[k]; + for (l = firstAvailCol; l < firstAvailCol + colSpan; l++) { + matrixrow[l] = 'x'; + } + } + } + } + return matrixrow.length; + }; + + // *** Process table *** + // add processing indicator + ts.isProcessing = function(table, toggle, $ths) { + table = $(table); + var c = table[0].config, + // default to all headers + $h = $ths || table.find('.' + ts.css.header); + if (toggle) { + // don't use sortList if custom $ths used + if (typeof $ths !== 'undefined' && c.sortList.length > 0) { + // get headers from the sortList + $h = $h.filter(function(){ + // get data-column from attr to keep compatibility with jQuery 1.2.6 + return this.sortDisabled ? false : ts.isValueInArray( parseFloat($(this).attr('data-column')), c.sortList) >= 0; + }); + } + table.add($h).addClass(ts.css.processing + ' ' + c.cssProcessing); + } else { + table.add($h).removeClass(ts.css.processing + ' ' + c.cssProcessing); + } + }; + + // detach tbody but save the position + // don't use tbody because there are portions that look for a tbody index (updateCell) + ts.processTbody = function(table, $tb, getIt){ + table = $(table)[0]; + var holdr; + if (getIt) { + table.isProcessing = true; + $tb.before('<span class="tablesorter-savemyplace"/>'); + holdr = ($.fn.detach) ? $tb.detach() : $tb.remove(); + return holdr; + } + holdr = $(table).find('span.tablesorter-savemyplace'); + $tb.insertAfter( holdr ); + holdr.remove(); + table.isProcessing = false; + }; + + ts.clearTableBody = function(table) { + $(table)[0].config.$tbodies.children().detach(); + }; + + ts.bindEvents = function(table, $headers, core){ + table = $(table)[0]; + var t, downTarget = null, + c = table.config; + if (core !== true) { + $headers.addClass( c.namespace.slice(1) + '_extra_headers' ); + t = $.fn.closest ? $headers.closest('table')[0] : $headers.parents('table')[0]; + if (t && t.nodeName === 'TABLE' && t !== table) { + $(t).addClass( c.namespace.slice(1) + '_extra_table' ); + } + } + // apply event handling to headers and/or additional headers (stickyheaders, scroller, etc) + $headers + // http://stackoverflow.com/questions/5312849/jquery-find-self; + .find(c.selectorSort).add( $headers.filter(c.selectorSort) ) + .unbind( ('mousedown mouseup click sort keyup '.split(' ').join(c.namespace + ' ')).replace(/\s+/g, ' ') ) + .bind( 'mousedown mouseup click sort keyup '.split(' ').join(c.namespace + ' '), function(e, external) { + var cell, + $target = $(e.target), + type = e.type; + // only recognize left clicks + if ( ( ( e.which || e.button ) !== 1 && !/sort|keyup|click/.test(type) ) || + // allow pressing enter + ( type === 'keyup' && e.which !== 13 ) || + // allow triggering a click event (e.which is undefined) & ignore physical clicks + ( type === 'click' && typeof e.which !== 'undefined' ) ) { + return; + } + // ignore mouseup if mousedown wasn't on the same target + if ( type === 'mouseup' && downTarget !== e.target && external !== true ) { return; } + // set timer on mousedown + if ( type === 'mousedown' ) { + downTarget = e.target; + return; + } + downTarget = null; + // prevent sort being triggered on form elements + if ( /(input|select|button|textarea)/i.test(e.target.nodeName) || + // nosort class name, or elements within a nosort container + $target.hasClass(c.cssNoSort) || $target.parents('.' + c.cssNoSort).length > 0 || + // elements within a button + $target.parents('button').length > 0 ) { + return !c.cancelSelection; + } + if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); } + // jQuery v1.2.6 doesn't have closest() + cell = $.fn.closest ? $(this).closest('th, td')[0] : /TH|TD/.test(this.nodeName) ? this : $(this).parents('th, td')[0]; + // reference original table headers and find the same cell + cell = c.$headers[ $headers.index( cell ) ]; + if (!cell.sortDisabled) { + initSort(table, cell, e); + } + }); + if (c.cancelSelection) { + // cancel selection + $headers + .attr('unselectable', 'on') + .bind('selectstart', false) + .css({ + 'user-select': 'none', + 'MozUserSelect': 'none' // not needed for jQuery 1.8+ + }); + } + }; + + // restore headers + ts.restoreHeaders = function(table){ + var $cell, + c = $(table)[0].config; + // don't use c.$headers here in case header cells were swapped + c.$table.find(c.selectorHeaders).each(function(i){ + $cell = $(this); + // only restore header cells if it is wrapped + // because this is also used by the updateAll method + if ($cell.find('.' + ts.css.headerIn).length){ + $cell.html( c.headerContent[i] ); + } + }); + }; + + ts.destroy = function(table, removeClasses, callback){ + table = $(table)[0]; + if (!table.hasInitialized) { return; } + // remove all widgets + ts.removeWidget(table, true, false); + var events, + $t = $(table), + c = table.config, + $h = $t.find('thead:first'), + $r = $h.find('tr.' + ts.css.headerRow).removeClass(ts.css.headerRow + ' ' + c.cssHeaderRow), + $f = $t.find('tfoot:first > tr').children('th, td'); + if (removeClasses === false && $.inArray('uitheme', c.widgets) >= 0) { + // reapply uitheme classes, in case we want to maintain appearance + $t.trigger('applyWidgetId', ['uitheme']); + $t.trigger('applyWidgetId', ['zebra']); + } + // remove widget added rows, just in case + $h.find('tr').not($r).remove(); + // disable tablesorter + events = 'sortReset update updateAll updateRows updateCell addRows updateComplete sorton appendCache updateCache ' + + 'applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress sortBegin sortEnd resetToLoadState '.split(' ') + .join(c.namespace + ' '); + $t + .removeData('tablesorter') + .unbind( events.replace(/\s+/g, ' ') ); + c.$headers.add($f) + .removeClass( [ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone].join(' ') ) + .removeAttr('data-column') + .removeAttr('aria-label') + .attr('aria-disabled', 'true'); + $r.find(c.selectorSort).unbind( ('mousedown mouseup keypress '.split(' ').join(c.namespace + ' ')).replace(/\s+/g, ' ') ); + ts.restoreHeaders(table); + $t.toggleClass(ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme, removeClasses === false); + // clear flag in case the plugin is initialized again + table.hasInitialized = false; + delete table.config.cache; + if (typeof callback === 'function') { + callback(table); + } + }; + + // *** sort functions *** + // regex used in natural sort + ts.regex = { + chunk : /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi, // chunk/tokenize numbers & letters + chunks: /(^\\0|\\0$)/, // replace chunks @ ends + hex: /^0x[0-9a-f]+$/i // hex + }; + + // Natural sort - https://github.com/overset/javascript-natural-sort (date sorting removed) + // this function will only accept strings, or you'll see 'TypeError: undefined is not a function' + // I could add a = a.toString(); b = b.toString(); but it'll slow down the sort overall + ts.sortNatural = function(a, b) { + if (a === b) { return 0; } + var xN, xD, yN, yD, xF, yF, i, mx, + r = ts.regex; + // first try and sort Hex codes + if (r.hex.test(b)) { + xD = parseInt(a.match(r.hex), 16); + yD = parseInt(b.match(r.hex), 16); + if ( xD < yD ) { return -1; } + if ( xD > yD ) { return 1; } + } + // chunk/tokenize + xN = a.replace(r.chunk, '\\0$1\\0').replace(r.chunks, '').split('\\0'); + yN = b.replace(r.chunk, '\\0$1\\0').replace(r.chunks, '').split('\\0'); + mx = Math.max(xN.length, yN.length); + // natural sorting through split numeric strings and default strings + for (i = 0; i < mx; i++) { + // find floats not starting with '0', string or 0 if not defined + xF = isNaN(xN[i]) ? xN[i] || 0 : parseFloat(xN[i]) || 0; + yF = isNaN(yN[i]) ? yN[i] || 0 : parseFloat(yN[i]) || 0; + // handle numeric vs string comparison - number < string - (Kyle Adams) + if (isNaN(xF) !== isNaN(yF)) { return (isNaN(xF)) ? 1 : -1; } + // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2' + if (typeof xF !== typeof yF) { + xF += ''; + yF += ''; + } + if (xF < yF) { return -1; } + if (xF > yF) { return 1; } + } + return 0; + }; + + ts.sortNaturalAsc = function(a, b, col, table, c) { + if (a === b) { return 0; } + var e = c.string[ (c.empties[col] || c.emptyTo ) ]; + if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : -e || -1; } + if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : e || 1; } + return ts.sortNatural(a, b); + }; + + ts.sortNaturalDesc = function(a, b, col, table, c) { + if (a === b) { return 0; } + var e = c.string[ (c.empties[col] || c.emptyTo ) ]; + if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : e || 1; } + if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : -e || -1; } + return ts.sortNatural(b, a); + }; + + // basic alphabetical sort + ts.sortText = function(a, b) { + return a > b ? 1 : (a < b ? -1 : 0); + }; + + // return text string value by adding up ascii value + // so the text is somewhat sorted when using a digital sort + // this is NOT an alphanumeric sort + ts.getTextValue = function(a, num, mx) { + if (mx) { + // make sure the text value is greater than the max numerical value (mx) + var i, l = a ? a.length : 0, n = mx + num; + for (i = 0; i < l; i++) { + n += a.charCodeAt(i); + } + return num * n; + } + return 0; + }; + + ts.sortNumericAsc = function(a, b, num, mx, col, table) { + if (a === b) { return 0; } + var c = table.config, + e = c.string[ (c.empties[col] || c.emptyTo ) ]; + if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : -e || -1; } + if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : e || 1; } + if (isNaN(a)) { a = ts.getTextValue(a, num, mx); } + if (isNaN(b)) { b = ts.getTextValue(b, num, mx); } + return a - b; + }; + + ts.sortNumericDesc = function(a, b, num, mx, col, table) { + if (a === b) { return 0; } + var c = table.config, + e = c.string[ (c.empties[col] || c.emptyTo ) ]; + if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : e || 1; } + if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : -e || -1; } + if (isNaN(a)) { a = ts.getTextValue(a, num, mx); } + if (isNaN(b)) { b = ts.getTextValue(b, num, mx); } + return b - a; + }; + + ts.sortNumeric = function(a, b) { + return a - b; + }; + + // used when replacing accented characters during sorting + ts.characterEquivalents = { + 'a' : '\u00e1\u00e0\u00e2\u00e3\u00e4\u0105\u00e5', // áàâãäąå + 'A' : '\u00c1\u00c0\u00c2\u00c3\u00c4\u0104\u00c5', // ÁÀÂÃÄĄÅ + 'c' : '\u00e7\u0107\u010d', // çćč + 'C' : '\u00c7\u0106\u010c', // ÇĆČ + 'e' : '\u00e9\u00e8\u00ea\u00eb\u011b\u0119', // éèêëěę + 'E' : '\u00c9\u00c8\u00ca\u00cb\u011a\u0118', // ÉÈÊËĚĘ + 'i' : '\u00ed\u00ec\u0130\u00ee\u00ef\u0131', // íìİîïı + 'I' : '\u00cd\u00cc\u0130\u00ce\u00cf', // ÍÌİÎÏ + 'o' : '\u00f3\u00f2\u00f4\u00f5\u00f6', // óòôõö + 'O' : '\u00d3\u00d2\u00d4\u00d5\u00d6', // ÓÒÔÕÖ + 'ss': '\u00df', // ß (s sharp) + 'SS': '\u1e9e', // ẞ (Capital sharp s) + 'u' : '\u00fa\u00f9\u00fb\u00fc\u016f', // úùûüů + 'U' : '\u00da\u00d9\u00db\u00dc\u016e' // ÚÙÛÜŮ + }; + ts.replaceAccents = function(s) { + var a, acc = '[', eq = ts.characterEquivalents; + if (!ts.characterRegex) { + ts.characterRegexArray = {}; + for (a in eq) { + if (typeof a === 'string') { + acc += eq[a]; + ts.characterRegexArray[a] = new RegExp('[' + eq[a] + ']', 'g'); + } + } + ts.characterRegex = new RegExp(acc + ']'); + } + if (ts.characterRegex.test(s)) { + for (a in eq) { + if (typeof a === 'string') { + s = s.replace( ts.characterRegexArray[a], a ); + } + } + } + return s; + }; + + // *** utilities *** + ts.isValueInArray = function(column, arry) { + var indx, len = arry.length; + for (indx = 0; indx < len; indx++) { + if (arry[indx][0] === column) { + return indx; + } + } + return -1; + }; + + ts.addParser = function(parser) { + var i, l = ts.parsers.length, a = true; + for (i = 0; i < l; i++) { + if (ts.parsers[i].id.toLowerCase() === parser.id.toLowerCase()) { + a = false; + } + } + if (a) { + ts.parsers.push(parser); + } + }; + + // Use it to add a set of methods to table.config which will be available for all tables. + // This should be done before table initialization + ts.addInstanceMethods = function(methods) { + $.extend(ts.instanceMethods, methods); + }; + + ts.getParserById = function(name) { + /*jshint eqeqeq:false */ + if (name == 'false') { return false; } + var i, l = ts.parsers.length; + for (i = 0; i < l; i++) { + if (ts.parsers[i].id.toLowerCase() === (name.toString()).toLowerCase()) { + return ts.parsers[i]; + } + } + return false; + }; + + ts.addWidget = function(widget) { + ts.widgets.push(widget); + }; + + ts.hasWidget = function(table, name){ + table = $(table); + return table.length && table[0].config && table[0].config.widgetInit[name] || false; + }; + + ts.getWidgetById = function(name) { + var i, w, l = ts.widgets.length; + for (i = 0; i < l; i++) { + w = ts.widgets[i]; + if (w && w.hasOwnProperty('id') && w.id.toLowerCase() === name.toLowerCase()) { + return w; + } + } + }; + + ts.applyWidgetOptions = function( table, c ){ + var indx, widget, + len = c.widgets.length, + wo = c.widgetOptions; + if (len) { + for (indx = 0; indx < len; indx++) { + widget = ts.getWidgetById( c.widgets[indx] ); + if ( widget && 'options' in widget ) { + wo = table.config.widgetOptions = $.extend( true, {}, widget.options, wo ); + } + } + } + }; + + ts.applyWidget = function(table, init, callback) { + table = $(table)[0]; // in case this is called externally + var indx, len, name, + c = table.config, + wo = c.widgetOptions, + tableClass = ' ' + c.table.className + ' ', + widgets = [], + time, time2, w, wd; + // prevent numerous consecutive widget applications + if (init !== false && table.hasInitialized && (table.isApplyingWidgets || table.isUpdating)) { return; } + if (c.debug) { time = new Date(); } + // look for widgets to apply from in table class + // stop using \b otherwise this matches 'ui-widget-content' & adds 'content' widget + wd = new RegExp( '\\s' + c.widgetClass.replace( /\{name\}/i, '([\\w-]+)' )+ '\\s', 'g' ); + if ( tableClass.match( wd ) ) { + // extract out the widget id from the table class (widget id's can include dashes) + w = tableClass.match( wd ); + if ( w ) { + len = w.length; + for (indx = 0; indx < len; indx++) { + c.widgets.push( w[indx].replace( wd, '$1' ) ); + } + } + } + if (c.widgets.length) { + table.isApplyingWidgets = true; + // ensure unique widget ids + c.widgets = $.grep(c.widgets, function(v, k){ + return $.inArray(v, c.widgets) === k; + }); + name = c.widgets || []; + len = name.length; + // build widget array & add priority as needed + for (indx = 0; indx < len; indx++) { + wd = ts.getWidgetById(name[indx]); + if (wd && wd.id) { + // set priority to 10 if not defined + if (!wd.priority) { wd.priority = 10; } + widgets[indx] = wd; + } + } + // sort widgets by priority + widgets.sort(function(a, b){ + return a.priority < b.priority ? -1 : a.priority === b.priority ? 0 : 1; + }); + // add/update selected widgets + len = widgets.length; + for (indx = 0; indx < len; indx++) { + if (widgets[indx]) { + if ( init || !( c.widgetInit[ widgets[indx].id ] ) ) { + // set init flag first to prevent calling init more than once (e.g. pager) + c.widgetInit[ widgets[indx].id ] = true; + if (table.hasInitialized) { + // don't reapply widget options on tablesorter init + ts.applyWidgetOptions( table, c ); + } + if ( 'init' in widgets[indx] ) { + if (c.debug) { time2 = new Date(); } + widgets[indx].init(table, widgets[indx], c, wo); + if (c.debug) { ts.benchmark('Initializing ' + widgets[indx].id + ' widget', time2); } + } + } + if ( !init && 'format' in widgets[indx] ) { + if (c.debug) { time2 = new Date(); } + widgets[indx].format(table, c, wo, false); + if (c.debug) { ts.benchmark( ( init ? 'Initializing ' : 'Applying ' ) + widgets[indx].id + ' widget', time2); } + } + } + } + // callback executed on init only + if (!init && typeof callback === 'function') { + callback(table); + } + } + setTimeout(function(){ + table.isApplyingWidgets = false; + $.data(table, 'lastWidgetApplication', new Date()); + }, 0); + if (c.debug) { + w = c.widgets.length; + benchmark('Completed ' + (init === true ? 'initializing ' : 'applying ') + w + ' widget' + (w !== 1 ? 's' : ''), time); + } + }; + + ts.removeWidget = function(table, name, refreshing){ + table = $(table)[0]; + var i, widget, indx, len, + c = table.config; + // if name === true, add all widgets from $.tablesorter.widgets + if (name === true) { + name = []; + len = ts.widgets.length; + for (indx = 0; indx < len; indx++) { + widget = ts.widgets[indx]; + if (widget && widget.id) { + name.push( widget.id ); + } + } + } else { + // name can be either an array of widgets names, + // or a space/comma separated list of widget names + name = ( $.isArray(name) ? name.join(',') : name || '' ).toLowerCase().split( /[\s,]+/ ); + } + len = name.length; + for (i = 0; i < len; i++) { + widget = ts.getWidgetById(name[i]); + indx = $.inArray( name[i], c.widgets ); + if ( widget && 'remove' in widget ) { + if (c.debug && indx >= 0) { log( 'Removing "' + name[i] + '" widget' ); } + widget.remove(table, c, c.widgetOptions, refreshing); + c.widgetInit[ name[i] ] = false; + } + // don't remove the widget from config.widget if refreshing + if (indx >= 0 && refreshing !== true) { + c.widgets.splice( indx, 1 ); + } + } + }; + + ts.refreshWidgets = function(table, doAll, dontapply) { + table = $(table)[0]; // see issue #243 + var indx, + c = table.config, + cw = c.widgets, + widgets = ts.widgets, + len = widgets.length, + list = [], + callback = function(table){ + $(table).trigger('refreshComplete'); + }; + // remove widgets not defined in config.widgets, unless doAll is true + for (indx = 0; indx < len; indx++) { + if (widgets[indx] && widgets[indx].id && (doAll || $.inArray( widgets[indx].id, cw ) < 0)) { + list.push( widgets[indx].id ); + } + } + ts.removeWidget( table, list.join(','), true ); + if (dontapply !== true) { + // call widget init if + ts.applyWidget(table, doAll || false, callback ); + if (doAll) { + // apply widget format + ts.applyWidget(table, false, callback); + } + } else { + callback(table); + } + }; + + ts.getColumnText = function( table, column, callback ) { + table = $( table )[0]; + var tbodyIndex, rowIndex, cache, row, tbodyLen, rowLen, raw, parsed, $cell, result, + hasCallback = typeof callback === 'function', + allColumns = column === 'all', + data = { raw : [], parsed: [], $cell: [] }, + c = table.config; + if ( !isEmptyObject( c ) ) { + tbodyLen = c.$tbodies.length; + for ( tbodyIndex = 0; tbodyIndex < tbodyLen; tbodyIndex++ ) { + cache = c.cache[ tbodyIndex ].normalized; + rowLen = cache.length; + for ( rowIndex = 0; rowIndex < rowLen; rowIndex++ ) { + result = true; + row = cache[ rowIndex ]; + parsed = ( allColumns ) ? row.slice(0, c.columns) : row[ column ]; + row = row[ c.columns ]; + raw = ( allColumns ) ? row.raw : row.raw[ column ]; + $cell = ( allColumns ) ? row.$row.children() : row.$row.children().eq( column ); + if ( hasCallback ) { + result = callback({ + tbodyIndex: tbodyIndex, + rowIndex: rowIndex, + parsed: parsed, + raw: raw, + $row: row.$row, + $cell: $cell + }); + } + if ( result !== false ) { + data.parsed.push( parsed ); + data.raw.push( raw ); + data.$cell.push( $cell ); + } + } + } + // return everything + return data; + } + }; + + // get sorter, string, empty, etc options for each column from + // jQuery data, metadata, header option or header class name ('sorter-false') + // priority = jQuery data > meta > headers option > header class name + ts.getData = function(h, ch, key) { + var val = '', $h = $(h), m, cl; + if (!$h.length) { return ''; } + m = $.metadata ? $h.metadata() : false; + cl = ' ' + ($h.attr('class') || ''); + if (typeof $h.data(key) !== 'undefined' || typeof $h.data(key.toLowerCase()) !== 'undefined'){ + // 'data-lockedOrder' is assigned to 'lockedorder'; but 'data-locked-order' is assigned to 'lockedOrder' + // 'data-sort-initial-order' is assigned to 'sortInitialOrder' + val += $h.data(key) || $h.data(key.toLowerCase()); + } else if (m && typeof m[key] !== 'undefined') { + val += m[key]; + } else if (ch && typeof ch[key] !== 'undefined') { + val += ch[key]; + } else if (cl !== ' ' && cl.match(' ' + key + '-')) { + // include sorter class name 'sorter-text', etc; now works with 'sorter-my-custom-parser' + val = cl.match( new RegExp('\\s' + key + '-([\\w-]+)') )[1] || ''; + } + return $.trim(val); + }; + + ts.formatFloat = function(s, table) { + if (typeof s !== 'string' || s === '') { return s; } + // allow using formatFloat without a table; defaults to US number format + var i, + t = table && table.config ? table.config.usNumberFormat !== false : + typeof table !== 'undefined' ? table : true; + if (t) { + // US Format - 1,234,567.89 -> 1234567.89 + s = s.replace(/,/g,''); + } else { + // German Format = 1.234.567,89 -> 1234567.89 + // French Format = 1 234 567,89 -> 1234567.89 + s = s.replace(/[\s|\.]/g,'').replace(/,/g,'.'); + } + if(/^\s*\([.\d]+\)/.test(s)) { + // make (#) into a negative number -> (10) = -10 + s = s.replace(/^\s*\(([.\d]+)\)/, '-$1'); + } + i = parseFloat(s); + // return the text instead of zero + return isNaN(i) ? $.trim(s) : i; + }; + + ts.isDigit = function(s) { + // replace all unwanted chars and match + return isNaN(s) ? (/^[\-+(]?\d+[)]?$/).test(s.toString().replace(/[,.'"\s]/g, '')) : true; + }; + + }() + }); + + // make shortcut + var ts = $.tablesorter; + + // extend plugin scope + $.fn.extend({ + tablesorter: ts.construct + }); + + // add default parsers + ts.addParser({ + id: 'no-parser', + is: function() { + return false; + }, + format: function() { + return ''; + }, + type: 'text' + }); + + ts.addParser({ + id: 'text', + is: function() { + return true; + }, + format: function(s, table) { + var c = table.config; + if (s) { + s = $.trim( c.ignoreCase ? s.toLocaleLowerCase() : s ); + s = c.sortLocaleCompare ? ts.replaceAccents(s) : s; + } + return s; + }, + type: 'text' + }); + + ts.addParser({ + id: 'digit', + is: function(s) { + return ts.isDigit(s); + }, + format: function(s, table) { + var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ''), table); + return s && typeof n === 'number' ? n : s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; + }, + type: 'numeric' + }); + + ts.addParser({ + id: 'currency', + is: function(s) { + return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test((s || '').replace(/[+\-,. ]/g,'')); // £$€¤¥¢ + }, + format: function(s, table) { + var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ''), table); + return s && typeof n === 'number' ? n : s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; + }, + type: 'numeric' + }); + + ts.addParser({ + id: 'url', + is: function(s) { + return (/^(https?|ftp|file):\/\//).test(s); + }, + format: function(s) { + return s ? $.trim(s.replace(/(https?|ftp|file):\/\//, '')) : s; + }, + parsed : true, // filter widget flag + type: 'text' + }); + + ts.addParser({ + id: 'isoDate', + is: function(s) { + return (/^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}/).test(s); + }, + format: function(s, table) { + var date = s ? new Date( s.replace(/-/g, '/') ) : s; + return date instanceof Date && isFinite(date) ? date.getTime() : s; + }, + type: 'numeric' + }); + + ts.addParser({ + id: 'percent', + is: function(s) { + return (/(\d\s*?%|%\s*?\d)/).test(s) && s.length < 15; + }, + format: function(s, table) { + return s ? ts.formatFloat(s.replace(/%/g, ''), table) : s; + }, + type: 'numeric' + }); + + // added image parser to core v2.17.9 + ts.addParser({ + id: 'image', + is: function(s, table, node, $node){ + return $node.find('img').length > 0; + }, + format: function(s, table, cell) { + return $(cell).find('img').attr(table.config.imgAttr || 'alt') || s; + }, + parsed : true, // filter widget flag + type: 'text' + }); + + ts.addParser({ + id: 'usLongDate', + is: function(s) { + // two digit years are not allowed cross-browser + // Jan 01, 2013 12:34:56 PM or 01 Jan 2013 + return (/^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i).test(s) || (/^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i).test(s); + }, + format: function(s, table) { + var date = s ? new Date( s.replace(/(\S)([AP]M)$/i, '$1 $2') ) : s; + return date instanceof Date && isFinite(date) ? date.getTime() : s; + }, + type: 'numeric' + }); + + ts.addParser({ + id: 'shortDate', // 'mmddyyyy', 'ddmmyyyy' or 'yyyymmdd' + is: function(s) { + // testing for ##-##-#### or ####-##-##, so it's not perfect; time can be included + return (/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/).test((s || '').replace(/\s+/g,' ').replace(/[\-.,]/g, '/')); + }, + format: function(s, table, cell, cellIndex) { + if (s) { + var date, d, + c = table.config, + ci = c.$headerIndexed[ cellIndex ], + format = ci.length && ci[0].dateFormat || ts.getData( ci, ts.getColumnData( table, c.headers, cellIndex ), 'dateFormat') || c.dateFormat; + d = s.replace(/\s+/g, ' ').replace(/[\-.,]/g, '/'); // escaped - because JSHint in Firefox was showing it as an error + if (format === 'mmddyyyy') { + d = d.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, '$3/$1/$2'); + } else if (format === 'ddmmyyyy') { + d = d.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, '$3/$2/$1'); + } else if (format === 'yyyymmdd') { + d = d.replace(/(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/, '$1/$2/$3'); + } + date = new Date(d); + return date instanceof Date && isFinite(date) ? date.getTime() : s; + } + return s; + }, + type: 'numeric' + }); + + ts.addParser({ + id: 'time', + is: function(s) { + return (/^(([0-2]?\d:[0-5]\d)|([0-1]?\d:[0-5]\d\s?([AP]M)))$/i).test(s); + }, + format: function(s, table) { + var date = s ? new Date( '2000/01/01 ' + s.replace(/(\S)([AP]M)$/i, '$1 $2') ) : s; + return date instanceof Date && isFinite(date) ? date.getTime() : s; + }, + type: 'numeric' + }); + + ts.addParser({ + id: 'metadata', + is: function() { + return false; + }, + format: function(s, table, cell) { + var c = table.config, + p = (!c.parserMetadataName) ? 'sortValue' : c.parserMetadataName; + return $(cell).metadata()[p]; + }, + type: 'numeric' + }); + + // add default widgets + ts.addWidget({ + id: 'zebra', + priority: 90, + format: function(table, c, wo) { + var $tb, $tv, $tr, row, even, time, k, + child = new RegExp(c.cssChildRow, 'i'), + b = c.$tbodies.add( $( c.namespace + '_extra_table' ).children( 'tbody' ) ); + if (c.debug) { + time = new Date(); + } + for (k = 0; k < b.length; k++ ) { + // loop through the visible rows + row = 0; + $tb = b.eq(k); + $tv = $tb.children('tr:visible').not(c.selectorRemove); + // revered back to using jQuery each - strangely it's the fastest method + /*jshint loopfunc:true */ + $tv.each(function(){ + $tr = $(this); + // style child rows the same way the parent row was styled + if (!child.test(this.className)) { row++; } + even = (row % 2 === 0); + $tr.removeClass(wo.zebra[even ? 1 : 0]).addClass(wo.zebra[even ? 0 : 1]); + }); + } + }, + remove: function(table, c, wo, refreshing){ + if (refreshing) { return; } + var k, $tb, + b = c.$tbodies, + rmv = (wo.zebra || [ 'even', 'odd' ]).join(' '); + for (k = 0; k < b.length; k++ ){ + $tb = ts.processTbody(table, b.eq(k), true); // remove tbody + $tb.children().removeClass(rmv); + ts.processTbody(table, $tb, false); // restore tbody + } + } + }); + +})(jQuery); + +/*! Widget: storage - updated 3/26/2015 (v2.21.3) */ +;(function ($, window, document) { +'use strict'; + +var ts = $.tablesorter = $.tablesorter || {}; +// *** Store data in local storage, with a cookie fallback *** +/* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json) + if you need it, then include https://github.com/douglascrockford/JSON-js + + $.parseJSON is not available is jQuery versions older than 1.4.1, using older + versions will only allow storing information for one page at a time + + // *** Save data (JSON format only) *** + // val must be valid JSON... use http://jsonlint.com/ to ensure it is valid + var val = { "mywidget" : "data1" }; // valid JSON uses double quotes + // $.tablesorter.storage(table, key, val); + $.tablesorter.storage(table, 'tablesorter-mywidget', val); + + // *** Get data: $.tablesorter.storage(table, key); *** + v = $.tablesorter.storage(table, 'tablesorter-mywidget'); + // val may be empty, so also check for your data + val = (v && v.hasOwnProperty('mywidget')) ? v.mywidget : ''; + alert(val); // "data1" if saved, or "" if not +*/ +ts.storage = function(table, key, value, options) { + table = $(table)[0]; + var cookieIndex, cookies, date, + hasStorage = false, + values = {}, + c = table.config, + wo = c && c.widgetOptions, + storageType = ( options && options.useSessionStorage ) || ( wo && wo.storage_useSessionStorage ) ? + 'sessionStorage' : 'localStorage', + $table = $(table), + // id from (1) options ID, (2) table "data-table-group" attribute, (3) widgetOptions.storage_tableId, + // (4) table ID, then (5) table index + id = options && options.id || + $table.attr( options && options.group || wo && wo.storage_group || 'data-table-group') || + wo && wo.storage_tableId || table.id || $('.tablesorter').index( $table ), + // url from (1) options url, (2) table "data-table-page" attribute, (3) widgetOptions.storage_fixedUrl, + // (4) table.config.fixedUrl (deprecated), then (5) window location path + url = options && options.url || + $table.attr(options && options.page || wo && wo.storage_page || 'data-table-page') || + wo && wo.storage_fixedUrl || c && c.fixedUrl || window.location.pathname; + // https://gist.github.com/paulirish/5558557 + if (storageType in window) { + try { + window[storageType].setItem('_tmptest', 'temp'); + hasStorage = true; + window[storageType].removeItem('_tmptest'); + } catch(error) { + if (c && c.debug) { + ts.log( storageType + ' is not supported in this browser' ); + } + } + } + // *** get value *** + if ($.parseJSON) { + if (hasStorage) { + values = $.parseJSON( window[storageType][key] || 'null' ) || {}; + } else { + // old browser, using cookies + cookies = document.cookie.split(/[;\s|=]/); + // add one to get from the key to the value + cookieIndex = $.inArray(key, cookies) + 1; + values = (cookieIndex !== 0) ? $.parseJSON(cookies[cookieIndex] || 'null') || {} : {}; + } + } + // allow value to be an empty string too + if ((value || value === '') && window.JSON && JSON.hasOwnProperty('stringify')) { + // add unique identifiers = url pathname > table ID/index on page > data + if (!values[url]) { + values[url] = {}; + } + values[url][id] = value; + // *** set value *** + if (hasStorage) { + window[storageType][key] = JSON.stringify(values); + } else { + date = new Date(); + date.setTime(date.getTime() + (31536e+6)); // 365 days + document.cookie = key + '=' + (JSON.stringify(values)).replace(/\"/g,'\"') + '; expires=' + date.toGMTString() + '; path=/'; + } + } else { + return values && values[url] ? values[url][id] : ''; + } +}; + +})(jQuery, window, document); + +/*! Widget: uitheme - updated 3/26/2015 (v2.21.3) */ +;(function ($) { +'use strict'; +var ts = $.tablesorter = $.tablesorter || {}; + +ts.themes = { + 'bootstrap' : { + table : 'table table-bordered table-striped', + caption : 'caption', + // header class names + header : 'bootstrap-header', // give the header a gradient background (theme.bootstrap_2.css) + sortNone : '', + sortAsc : '', + sortDesc : '', + active : '', // applied when column is sorted + hover : '', // custom css required - a defined bootstrap style may not override other classes + // icon class names + icons : '', // add "icon-white" to make them white; this icon class is added to the <i> in the header + iconSortNone : 'bootstrap-icon-unsorted', // class name added to icon when column is not sorted + iconSortAsc : 'icon-chevron-up glyphicon glyphicon-chevron-up', // class name added to icon when column has ascending sort + iconSortDesc : 'icon-chevron-down glyphicon glyphicon-chevron-down', // class name added to icon when column has descending sort + filterRow : '', // filter row class + footerRow : '', + footerCells : '', + even : '', // even row zebra striping + odd : '' // odd row zebra striping + }, + 'jui' : { + table : 'ui-widget ui-widget-content ui-corner-all', // table classes + caption : 'ui-widget-content', + // header class names + header : 'ui-widget-header ui-corner-all ui-state-default', // header classes + sortNone : '', + sortAsc : '', + sortDesc : '', + active : 'ui-state-active', // applied when column is sorted + hover : 'ui-state-hover', // hover class + // icon class names + icons : 'ui-icon', // icon class added to the <i> in the header + iconSortNone : 'ui-icon-carat-2-n-s', // class name added to icon when column is not sorted + iconSortAsc : 'ui-icon-carat-1-n', // class name added to icon when column has ascending sort + iconSortDesc : 'ui-icon-carat-1-s', // class name added to icon when column has descending sort + filterRow : '', + footerRow : '', + footerCells : '', + even : 'ui-widget-content', // even row zebra striping + odd : 'ui-state-default' // odd row zebra striping + } +}; + +$.extend(ts.css, { + wrapper : 'tablesorter-wrapper' // ui theme & resizable +}); + +ts.addWidget({ + id: "uitheme", + priority: 10, + format: function(table, c, wo) { + var i, hdr, icon, time, $header, $icon, $tfoot, $h, oldtheme, oldremove, oldIconRmv, hasOldTheme, + themesAll = ts.themes, + $table = c.$table.add( $( c.namespace + '_extra_table' ) ), + $headers = c.$headers.add( $( c.namespace + '_extra_headers' ) ), + theme = c.theme || 'jui', + themes = themesAll[theme] || {}, + remove = $.trim( [ themes.sortNone, themes.sortDesc, themes.sortAsc, themes.active ].join( ' ' ) ), + iconRmv = $.trim( [ themes.iconSortNone, themes.iconSortDesc, themes.iconSortAsc ].join( ' ' ) ); + if (c.debug) { time = new Date(); } + // initialization code - run once + if (!$table.hasClass('tablesorter-' + theme) || c.theme !== c.appliedTheme || !wo.uitheme_applied) { + wo.uitheme_applied = true; + oldtheme = themesAll[c.appliedTheme] || {}; + hasOldTheme = !$.isEmptyObject(oldtheme); + oldremove = hasOldTheme ? [ oldtheme.sortNone, oldtheme.sortDesc, oldtheme.sortAsc, oldtheme.active ].join( ' ' ) : ''; + oldIconRmv = hasOldTheme ? [ oldtheme.iconSortNone, oldtheme.iconSortDesc, oldtheme.iconSortAsc ].join( ' ' ) : ''; + if (hasOldTheme) { + wo.zebra[0] = $.trim( ' ' + wo.zebra[0].replace(' ' + oldtheme.even, '') ); + wo.zebra[1] = $.trim( ' ' + wo.zebra[1].replace(' ' + oldtheme.odd, '') ); + c.$tbodies.children().removeClass( [oldtheme.even, oldtheme.odd].join(' ') ); + } + // update zebra stripes + if (themes.even) { wo.zebra[0] += ' ' + themes.even; } + if (themes.odd) { wo.zebra[1] += ' ' + themes.odd; } + // add caption style + $table.children('caption') + .removeClass(oldtheme.caption || '') + .addClass(themes.caption); + // add table/footer class names + $tfoot = $table + // remove other selected themes + .removeClass( (c.appliedTheme ? 'tablesorter-' + (c.appliedTheme || '') : '') + ' ' + (oldtheme.table || '') ) + .addClass('tablesorter-' + theme + ' ' + (themes.table || '')) // add theme widget class name + .children('tfoot'); + c.appliedTheme = c.theme; + + if ($tfoot.length) { + $tfoot + // if oldtheme.footerRow or oldtheme.footerCells are undefined, all class names are removed + .children('tr').removeClass(oldtheme.footerRow || '').addClass(themes.footerRow) + .children('th, td').removeClass(oldtheme.footerCells || '').addClass(themes.footerCells); + } + // update header classes + $headers + .removeClass( (hasOldTheme ? [oldtheme.header, oldtheme.hover, oldremove].join(' ') : '') || '' ) + .addClass(themes.header) + .not('.sorter-false') + .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') + .bind('mouseenter.tsuitheme mouseleave.tsuitheme', function(event) { + // toggleClass with switch added in jQuery 1.3 + $(this)[ event.type === 'mouseenter' ? 'addClass' : 'removeClass' ](themes.hover || ''); + }); + + $headers.each(function(){ + var $this = $(this); + if (!$this.find('.' + ts.css.wrapper).length) { + // Firefox needs this inner div to position the icon & resizer correctly + $this.wrapInner('<div class="' + ts.css.wrapper + '" style="position:relative;height:100%;width:100%"></div>'); + } + }); + if (c.cssIcon) { + // if c.cssIcon is '', then no <i> is added to the header + $headers + .find('.' + ts.css.icon) + .removeClass(hasOldTheme ? [oldtheme.icons, oldIconRmv].join(' ') : '') + .addClass(themes.icons || ''); + } + if ($table.hasClass('hasFilters')) { + $table.children('thead').children('.' + ts.css.filterRow) + .removeClass(hasOldTheme ? oldtheme.filterRow || '' : '') + .addClass(themes.filterRow || ''); + } + } + for (i = 0; i < c.columns; i++) { + $header = c.$headers + .add($(c.namespace + '_extra_headers')) + .not('.sorter-false') + .filter('[data-column="' + i + '"]'); + $icon = (ts.css.icon) ? $header.find('.' + ts.css.icon) : $(); + $h = $headers.not('.sorter-false').filter('[data-column="' + i + '"]:last'); + if ($h.length) { + $header.removeClass(remove); + $icon.removeClass(iconRmv); + if ($h[0].sortDisabled) { + // no sort arrows for disabled columns! + $icon.removeClass(themes.icons || ''); + } else { + hdr = themes.sortNone; + icon = themes.iconSortNone; + if ($h.hasClass(ts.css.sortAsc)) { + hdr = [themes.sortAsc, themes.active].join(' '); + icon = themes.iconSortAsc; + } else if ($h.hasClass(ts.css.sortDesc)) { + hdr = [themes.sortDesc, themes.active].join(' '); + icon = themes.iconSortDesc; + } + $header.addClass(hdr); + $icon.addClass(icon || ''); + } + } + } + if (c.debug) { + ts.benchmark("Applying " + theme + " theme", time); + } + }, + remove: function(table, c, wo, refreshing) { + if (!wo.uitheme_applied) { return; } + var $table = c.$table, + theme = c.appliedTheme || 'jui', + themes = ts.themes[ theme ] || ts.themes.jui, + $headers = $table.children('thead').children(), + remove = themes.sortNone + ' ' + themes.sortDesc + ' ' + themes.sortAsc, + iconRmv = themes.iconSortNone + ' ' + themes.iconSortDesc + ' ' + themes.iconSortAsc; + $table.removeClass('tablesorter-' + theme + ' ' + themes.table); + wo.uitheme_applied = false; + if (refreshing) { return; } + $table.find(ts.css.header).removeClass(themes.header); + $headers + .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') // remove hover + .removeClass(themes.hover + ' ' + remove + ' ' + themes.active) + .filter('.' + ts.css.filterRow) + .removeClass(themes.filterRow); + $headers.find('.' + ts.css.icon).removeClass(themes.icons + ' ' + iconRmv); + } +}); + +})(jQuery); + +/*! Widget: columns */ +;(function ($) { +'use strict'; +var ts = $.tablesorter = $.tablesorter || {}; + +ts.addWidget({ + id: "columns", + priority: 30, + options : { + columns : [ "primary", "secondary", "tertiary" ] + }, + format: function(table, c, wo) { + var $tbody, tbodyIndex, $rows, rows, $row, $cells, remove, indx, + $table = c.$table, + $tbodies = c.$tbodies, + sortList = c.sortList, + len = sortList.length, + // removed c.widgetColumns support + css = wo && wo.columns || [ "primary", "secondary", "tertiary" ], + last = css.length - 1; + remove = css.join(' '); + // check if there is a sort (on initialization there may not be one) + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // detach tbody + $rows = $tbody.children('tr'); + // loop through the visible rows + $rows.each(function() { + $row = $(this); + if (this.style.display !== 'none') { + // remove all columns class names + $cells = $row.children().removeClass(remove); + // add appropriate column class names + if (sortList && sortList[0]) { + // primary sort column class + $cells.eq(sortList[0][0]).addClass(css[0]); + if (len > 1) { + for (indx = 1; indx < len; indx++) { + // secondary, tertiary, etc sort column classes + $cells.eq(sortList[indx][0]).addClass( css[indx] || css[last] ); + } + } + } + } + }); + ts.processTbody(table, $tbody, false); + } + // add classes to thead and tfoot + rows = wo.columns_thead !== false ? ['thead tr'] : []; + if (wo.columns_tfoot !== false) { + rows.push('tfoot tr'); + } + if (rows.length) { + $rows = $table.find( rows.join(',') ).children().removeClass(remove); + if (len) { + for (indx = 0; indx < len; indx++) { + // add primary. secondary, tertiary, etc sort column classes + $rows.filter('[data-column="' + sortList[indx][0] + '"]').addClass(css[indx] || css[last]); + } + } + } + }, + remove: function(table, c, wo) { + var tbodyIndex, $tbody, + $tbodies = c.$tbodies, + remove = (wo.columns || [ "primary", "secondary", "tertiary" ]).join(' '); + c.$headers.removeClass(remove); + c.$table.children('tfoot').children('tr').children('th, td').removeClass(remove); + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody + $tbody.children('tr').each(function() { + $(this).children().removeClass(remove); + }); + ts.processTbody(table, $tbody, false); // restore tbody + } + } +}); + +})(jQuery); + +/*! Widget: filter - updated 3/26/2015 (v2.21.3) *//* + * Requires tablesorter v2.8+ and jQuery 1.7+ + * by Rob Garrison + */ +;(function ($) { +'use strict'; +var ts = $.tablesorter = $.tablesorter || {}, + tscss = ts.css; + +$.extend(tscss, { + filterRow : 'tablesorter-filter-row', + filter : 'tablesorter-filter', + filterDisabled : 'disabled', + filterRowHide : 'hideme' +}); + +ts.addWidget({ + id: "filter", + priority: 50, + options : { + filter_childRows : false, // if true, filter includes child row content in the search + filter_columnFilters : true, // if true, a filter will be added to the top of each table column + filter_columnAnyMatch: true, // if true, allows using "#:{query}" in AnyMatch searches (column:query) + filter_cellFilter : '', // css class name added to the filter cell (string or array) + filter_cssFilter : '', // css class name added to the filter row & each input in the row (tablesorter-filter is ALWAYS added) + filter_defaultFilter : {}, // add a default column filter type "~{query}" to make fuzzy searches default; "{q1} AND {q2}" to make all searches use a logical AND. + filter_excludeFilter : {}, // filters to exclude, per column + filter_external : '', // jQuery selector string (or jQuery object) of external filters + filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin + filter_formatter : null, // add custom filter elements to the filter row + filter_functions : null, // add custom filter functions using this option + filter_hideEmpty : true, // hide filter row when table is empty + filter_hideFilters : false, // collapse filter row when mouse leaves the area + filter_ignoreCase : true, // if true, make all searches case-insensitive + filter_liveSearch : true, // if true, search column content while the user types (with a delay) + filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available (visible) options within the drop down + filter_placeholder : { search : '', select : '' }, // default placeholder text (overridden by any header "data-placeholder" setting) + filter_reset : null, // jQuery selector string of an element used to reset the filters + filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters + filter_searchDelay : 300, // typing delay in milliseconds before starting a search + filter_searchFiltered: true, // allow searching through already filtered rows in special circumstances; will speed up searching in large tables if true + filter_selectSource : null, // include a function to return an array of values to be added to the column filter select + filter_startsWith : false, // if true, filter start from the beginning of the cell contents + filter_useParsedData : false, // filter all data using parsed content + filter_serversideFiltering : false, // if true, server-side filtering should be performed because client-side filtering will be disabled, but the ui and events will still be used. + filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value + filter_selectSourceSeparator : '|' // filter_selectSource array text left of the separator is added to the option value, right into the option text + }, + format: function(table, c, wo) { + if (!c.$table.hasClass('hasFilters')) { + ts.filter.init(table, c, wo); + } + }, + remove: function(table, c, wo, refreshing) { + var tbodyIndex, $tbody, + $table = c.$table, + $tbodies = c.$tbodies, + events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter '); + $table + .removeClass('hasFilters') + // add .tsfilter namespace to all BUT search + .unbind( events.replace(/\s+/g, ' ') ) + // remove the filter row even if refreshing, because the column might have been moved + .find('.' + tscss.filterRow).remove(); + if (refreshing) { return; } + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody + $tbody.children().removeClass(wo.filter_filteredRow).show(); + ts.processTbody(table, $tbody, false); // restore tbody + } + if (wo.filter_reset) { + $(document).undelegate(wo.filter_reset, 'click.tsfilter'); + } + } +}); + +ts.filter = { + + // regex used in filter "check" functions - not for general use and not documented + regex: { + regex : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // regex to test for regex + child : /tablesorter-childRow/, // child row class name; this gets updated in the script + filtered : /filtered/, // filtered (hidden) row class name; updated in the script + type : /undefined|number/, // check type + exact : /(^[\"\'=]+)|([\"\'=]+$)/g, // exact match (allow '==') + nondigit : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser) + operators : /[<>=]/g, // replace operators + query : '(q|query)' // replace filter queries + }, + // function( c, data ) { } + // c = table.config + // data.filter = array of filter input values; + // data.iFilter = same array, except lowercase (if wo.filter_ignoreCase is true) + // data.exact = table cell text (or parsed data if column parser enabled) + // data.iExact = same as data.exact, except lowercase (if wo.filter_ignoreCase is true) + // data.cache = table cell text from cache, so it has been parsed (& in all lower case if config.ignoreCase is true) + // data.index = column index; table = table element (DOM) + // data.parsed = array (by column) of boolean values (from filter_useParsedData or "filter-parsed" class) + types: { + // Look for regex + regex: function( c, data ) { + if ( ts.filter.regex.regex.test(data.iFilter) ) { + var matches, + regex = ts.filter.regex.regex.exec(data.iFilter); + try { + matches = new RegExp(regex[1], regex[2]).test( data.iExact ); + } catch (error) { + matches = false; + } + return matches; + } + return null; + }, + // Look for operators >, >=, < or <= + operators: function( c, data ) { + if ( /^[<>]=?/.test(data.iFilter) ) { + var cachedValue, result, + table = c.table, + index = data.index, + parsed = data.parsed[index], + query = ts.formatFloat( data.iFilter.replace(ts.filter.regex.operators, ''), table ), + parser = c.parsers[index], + savedSearch = query; + // parse filter value in case we're comparing numbers (dates) + if (parsed || parser.type === 'numeric') { + result = ts.filter.parseFilter(c, $.trim('' + data.iFilter.replace(ts.filter.regex.operators, '')), index, parsed, true); + query = ( typeof result === "number" && result !== '' && !isNaN(result) ) ? result : query; + } + + // iExact may be numeric - see issue #149; + // check if cached is defined, because sometimes j goes out of range? (numeric columns) + cachedValue = ( parsed || parser.type === 'numeric' ) && !isNaN(query) && typeof data.cache !== 'undefined' ? data.cache : + isNaN(data.iExact) ? ts.formatFloat( data.iExact.replace(ts.filter.regex.nondigit, ''), table) : + ts.formatFloat( data.iExact, table ); + + if ( />/.test(data.iFilter) ) { result = />=/.test(data.iFilter) ? cachedValue >= query : cachedValue > query; } + if ( /</.test(data.iFilter) ) { result = /<=/.test(data.iFilter) ? cachedValue <= query : cachedValue < query; } + // keep showing all rows if nothing follows the operator + if ( !result && savedSearch === '' ) { result = true; } + return result; + } + return null; + }, + // Look for a not match + notMatch: function( c, data ) { + if ( /^\!/.test(data.iFilter) ) { + var indx, + filter = ts.filter.parseFilter(c, data.iFilter.replace('!', ''), data.index, data.parsed[data.index]) || ''; + if (ts.filter.regex.exact.test(filter)) { + // look for exact not matches - see #628 + filter = filter.replace(ts.filter.regex.exact, ''); + return filter === '' ? true : $.trim(filter) !== data.iExact; + } else { + indx = data.iExact.search( $.trim(filter) ); + return filter === '' ? true : !(c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0); + } + } + return null; + }, + // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric + exact: function( c, data ) { + /*jshint eqeqeq:false */ + if (ts.filter.regex.exact.test(data.iFilter)) { + var filter = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.exact, ''), data.index, data.parsed[data.index]) || ''; + return data.anyMatch ? $.inArray(filter, data.rowArray) >= 0 : filter == data.iExact; + } + return null; + }, + // Look for an AND or && operator (logical and) + and : function( c, data ) { + if ( ts.filter.regex.andTest.test(data.filter) ) { + var index = data.index, + parsed = data.parsed[index], + query = data.iFilter.split( ts.filter.regex.andSplit ), + result = data.iExact.search( $.trim( ts.filter.parseFilter(c, query[0], index, parsed) ) ) >= 0, + indx = query.length - 1; + while (result && indx) { + result = result && data.iExact.search( $.trim( ts.filter.parseFilter(c, query[indx], index, parsed) ) ) >= 0; + indx--; + } + return result; + } + return null; + }, + // Look for a range (using " to " or " - ") - see issue #166; thanks matzhu! + range : function( c, data ) { + if ( ts.filter.regex.toTest.test(data.iFilter) ) { + var result, tmp, + table = c.table, + index = data.index, + parsed = data.parsed[index], + // make sure the dash is for a range and not indicating a negative number + query = data.iFilter.split( ts.filter.regex.toSplit ), + range1 = ts.formatFloat( ts.filter.parseFilter(c, query[0].replace(ts.filter.regex.nondigit, '') || '', index, parsed), table ), + range2 = ts.formatFloat( ts.filter.parseFilter(c, query[1].replace(ts.filter.regex.nondigit, '') || '', index, parsed), table ); + // parse filter value in case we're comparing numbers (dates) + if (parsed || c.parsers[index].type === 'numeric') { + result = c.parsers[index].format('' + query[0], table, c.$headers.eq(index), index); + range1 = (result !== '' && !isNaN(result)) ? result : range1; + result = c.parsers[index].format('' + query[1], table, c.$headers.eq(index), index); + range2 = (result !== '' && !isNaN(result)) ? result : range2; + } + result = ( parsed || c.parsers[index].type === 'numeric' ) && !isNaN(range1) && !isNaN(range2) ? data.cache : + isNaN(data.iExact) ? ts.formatFloat( data.iExact.replace(ts.filter.regex.nondigit, ''), table) : + ts.formatFloat( data.iExact, table ); + if (range1 > range2) { tmp = range1; range1 = range2; range2 = tmp; } // swap + return (result >= range1 && result <= range2) || (range1 === '' || range2 === ''); + } + return null; + }, + // Look for wild card: ? = single, * = multiple, or | = logical OR + wild : function( c, data ) { + if ( /[\?\*\|]/.test(data.iFilter) || ts.filter.regex.orReplace.test(data.filter) ) { + var index = data.index, + parsed = data.parsed[index], + query = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.orReplace, "|"), index, parsed) || ''; + // look for an exact match with the "or" unless the "filter-match" class is found + if (!c.$headerIndexed[index].hasClass('filter-match') && /\|/.test(query)) { + // show all results while using filter match. Fixes #727 + if (query[ query.length - 1 ] === '|') { query += '*'; } + query = data.anyMatch && $.isArray(data.rowArray) ? '(' + query + ')' : '^(' + query + ')$'; + } + // parsing the filter may not work properly when using wildcards =/ + return new RegExp( query.replace(/\?/g, '\\S{1}').replace(/\*/g, '\\S*') ).test(data.iExact); + } + return null; + }, + // fuzzy text search; modified from https://github.com/mattyork/fuzzy (MIT license) + fuzzy: function( c, data ) { + if ( /^~/.test(data.iFilter) ) { + var indx, + patternIndx = 0, + len = data.iExact.length, + pattern = ts.filter.parseFilter(c, data.iFilter.slice(1), data.index, data.parsed[data.index]) || ''; + for (indx = 0; indx < len; indx++) { + if (data.iExact[indx] === pattern[patternIndx]) { + patternIndx += 1; + } + } + if (patternIndx === pattern.length) { + return true; + } + return false; + } + return null; + } + }, + init: function(table, c, wo) { + // filter language options + ts.language = $.extend(true, {}, { + to : 'to', + or : 'or', + and : 'and' + }, ts.language); + + var options, string, txt, $header, column, filters, val, fxn, noSelect, + regex = ts.filter.regex; + c.$table.addClass('hasFilters'); + + // define timers so using clearTimeout won't cause an undefined error + wo.searchTimer = null; + wo.filter_initTimer = null; + wo.filter_formatterCount = 0; + wo.filter_formatterInit = []; + wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; + wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; + + txt = '\\{' + ts.filter.regex.query + '\\}'; + $.extend( regex, { + child : new RegExp(c.cssChildRow), + filtered : new RegExp(wo.filter_filteredRow), + alreadyFiltered : new RegExp('(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i'), + toTest : new RegExp('\\s+(-|' + ts.language.to + ')\\s+', 'i'), + toSplit : new RegExp('(?:\\s+(?:-|' + ts.language.to + ')\\s+)' ,'gi'), + andTest : new RegExp('\\s+(' + ts.language.and + '|&&)\\s+', 'i'), + andSplit : new RegExp('(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi'), + orReplace : new RegExp('\\s+(' + ts.language.or + ')\\s+', 'gi'), + iQuery : new RegExp(txt, 'i'), + igQuery : new RegExp(txt, 'ig') + }); + + // don't build filter row if columnFilters is false or all columns are set to "filter-false" - issue #156 + if (wo.filter_columnFilters !== false && c.$headers.filter('.filter-false, .parser-false').length !== c.$headers.length) { + // build filter row + ts.filter.buildRow(table, c, wo); + } + + txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter '); + c.$table.bind( txt, function(event, filter) { + val = (wo.filter_hideEmpty && $.isEmptyObject(c.cache) && !(c.delayInit && event.type === 'appendCache')); + // hide filter row using the "filtered" class name + c.$table.find('.' + tscss.filterRow).toggleClass(wo.filter_filteredRow, val ); // fixes #450 + if ( !/(search|filter)/.test(event.type) ) { + event.stopPropagation(); + ts.filter.buildDefault(table, true); + } + if (event.type === 'filterReset') { + c.$table.find('.' + tscss.filter).add(wo.filter_$externalFilters).val(''); + ts.filter.searching(table, []); + } else if (event.type === 'filterEnd') { + ts.filter.buildDefault(table, true); + } else { + // send false argument to force a new search; otherwise if the filter hasn't changed, it will return + filter = event.type === 'search' ? filter : event.type === 'updateComplete' ? c.$table.data('lastSearch') : ''; + if (/(update|add)/.test(event.type) && event.type !== "updateComplete") { + // force a new search since content has changed + c.lastCombinedFilter = null; + c.lastSearch = []; + } + // pass true (skipFirst) to prevent the tablesorter.setFilters function from skipping the first input + // ensures all inputs are updated when a search is triggered on the table $('table').trigger('search', [...]); + ts.filter.searching(table, filter, true); + } + return false; + }); + + // reset button/link + if (wo.filter_reset) { + if (wo.filter_reset instanceof $) { + // reset contains a jQuery object, bind to it + wo.filter_reset.click(function(){ + c.$table.trigger('filterReset'); + }); + } else if ($(wo.filter_reset).length) { + // reset is a jQuery selector, use event delegation + $(document) + .undelegate(wo.filter_reset, 'click.tsfilter') + .delegate(wo.filter_reset, 'click.tsfilter', function() { + // trigger a reset event, so other functions (filter_formatter) know when to reset + c.$table.trigger('filterReset'); + }); + } + } + if (wo.filter_functions) { + for (column = 0; column < c.columns; column++) { + fxn = ts.getColumnData( table, wo.filter_functions, column ); + if (fxn) { + // remove "filter-select" from header otherwise the options added here are replaced with all options + $header = c.$headerIndexed[column].removeClass('filter-select'); + // don't build select if "filter-false" or "parser-false" set + noSelect = !($header.hasClass('filter-false') || $header.hasClass('parser-false')); + options = ''; + if ( fxn === true && noSelect ) { + ts.filter.buildSelect(table, column); + } else if ( typeof fxn === 'object' && noSelect ) { + // add custom drop down list + for (string in fxn) { + if (typeof string === 'string') { + options += options === '' ? + '<option value="">' + ($header.data('placeholder') || $header.attr('data-placeholder') || wo.filter_placeholder.select || '') + '</option>' : ''; + val = string; + txt = string; + if (string.indexOf(wo.filter_selectSourceSeparator) >= 0) { + val = string.split(wo.filter_selectSourceSeparator); + txt = val[1]; + val = val[0]; + } + options += '<option ' + (txt === val ? '' : 'data-function-name="' + string + '" ') + 'value="' + val + '">' + txt + '</option>'; + } + } + c.$table.find('thead').find('select.' + tscss.filter + '[data-column="' + column + '"]').append(options); + } + } + } + } + // not really updating, but if the column has both the "filter-select" class & filter_functions set to true, + // it would append the same options twice. + ts.filter.buildDefault(table, true); + + ts.filter.bindSearch( table, c.$table.find('.' + tscss.filter), true ); + if (wo.filter_external) { + ts.filter.bindSearch( table, wo.filter_external ); + } + + if (wo.filter_hideFilters) { + ts.filter.hideFilters(table, c); + } + + // show processing icon + if (c.showProcessing) { + c.$table + .unbind( ('filterStart filterEnd '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') ) + .bind( 'filterStart filterEnd '.split(' ').join(c.namespace + 'filter '), function(event, columns) { + // only add processing to certain columns to all columns + $header = (columns) ? c.$table.find('.' + tscss.header).filter('[data-column]').filter(function() { + return columns[$(this).data('column')] !== ''; + }) : ''; + ts.isProcessing(table, event.type === 'filterStart', columns ? $header : ''); + }); + } + + // set filtered rows count (intially unfiltered) + c.filteredRows = c.totalRows; + + // add default values + c.$table + .unbind( ('tablesorter-initialized pagerBeforeInitialized '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') ) + .bind( 'tablesorter-initialized pagerBeforeInitialized '.split(' ').join(c.namespace + 'filter '), function() { + // redefine "wo" as it does not update properly inside this callback + var wo = this.config.widgetOptions; + filters = ts.filter.setDefaults(table, c, wo) || []; + if (filters.length) { + // prevent delayInit from triggering a cache build if filters are empty + if ( !(c.delayInit && filters.join('') === '') ) { + ts.setFilters(table, filters, true); + } + } + c.$table.trigger('filterFomatterUpdate'); + // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers + setTimeout(function(){ + if (!wo.filter_initialized) { + ts.filter.filterInitComplete(c); + } + }, 100); + }); + // if filter widget is added after pager has initialized; then set filter init flag + if (c.pager && c.pager.initialized && !wo.filter_initialized) { + c.$table.trigger('filterFomatterUpdate'); + setTimeout(function(){ + ts.filter.filterInitComplete(c); + }, 100); + } + }, + // $cell parameter, but not the config, is passed to the + // filter_formatters, so we have to work with it instead + formatterUpdated: function($cell, column) { + var wo = $cell.closest('table')[0].config.widgetOptions; + if (!wo.filter_initialized) { + // add updates by column since this function + // may be called numerous times before initialization + wo.filter_formatterInit[column] = 1; + } + }, + filterInitComplete: function(c){ + var indx, len, + wo = c.widgetOptions, + count = 0, + completed = function(){ + wo.filter_initialized = true; + c.$table.trigger('filterInit', c); + ts.filter.findRows(c.table, c.$table.data('lastSearch') || []); + }; + if ( $.isEmptyObject( wo.filter_formatter ) ) { + completed(); + } else { + len = wo.filter_formatterInit.length; + for (indx = 0; indx < len; indx++) { + if (wo.filter_formatterInit[indx] === 1) { + count++; + } + } + clearTimeout(wo.filter_initTimer); + if (!wo.filter_initialized && count === wo.filter_formatterCount) { + // filter widget initialized + completed(); + } else if (!wo.filter_initialized) { + // fall back in case a filter_formatter doesn't call + // $.tablesorter.filter.formatterUpdated($cell, column), and the count is off + wo.filter_initTimer = setTimeout(function(){ + completed(); + }, 500); + } + } + }, + + setDefaults: function(table, c, wo) { + var isArray, saved, indx, col, $filters, + // get current (default) filters + filters = ts.getFilters(table) || []; + if (wo.filter_saveFilters && ts.storage) { + saved = ts.storage( table, 'tablesorter-filters' ) || []; + isArray = $.isArray(saved); + // make sure we're not just getting an empty array + if ( !(isArray && saved.join('') === '' || !isArray) ) { filters = saved; } + } + // if no filters saved, then check default settings + if (filters.join('') === '') { + // allow adding default setting to external filters + $filters = c.$headers.add( wo.filter_$externalFilters ).filter('[' + wo.filter_defaultAttrib + ']'); + for (indx = 0; indx <= c.columns; indx++) { + // include data-column="all" external filters + col = indx === c.columns ? 'all' : indx; + filters[indx] = $filters.filter('[data-column="' + col + '"]').attr(wo.filter_defaultAttrib) || filters[indx] || ''; + } + } + c.$table.data('lastSearch', filters); + return filters; + }, + parseFilter: function(c, filter, column, parsed, forceParse){ + return forceParse || parsed ? + c.parsers[column].format( filter, c.table, [], column ) : + filter; + }, + buildRow: function(table, c, wo) { + var col, column, $header, buildSelect, disabled, name, ffxn, + // c.columns defined in computeThIndexes() + columns = c.columns, + arry = $.isArray(wo.filter_cellFilter), + buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; + for (column = 0; column < columns; column++) { + if (arry) { + buildFilter += '<td' + ( wo.filter_cellFilter[column] ? ' class="' + wo.filter_cellFilter[column] + '"' : '' ) + '></td>'; + } else { + buildFilter += '<td' + ( wo.filter_cellFilter !== '' ? ' class="' + wo.filter_cellFilter + '"' : '' ) + '></td>'; + } + } + c.$filters = $(buildFilter += '</tr>').appendTo( c.$table.children('thead').eq(0) ).find('td'); + // build each filter input + for (column = 0; column < columns; column++) { + disabled = false; + // assuming last cell of a column is the main column + $header = c.$headerIndexed[column]; + ffxn = ts.getColumnData( table, wo.filter_functions, column ); + buildSelect = (wo.filter_functions && ffxn && typeof ffxn !== "function" ) || + $header.hasClass('filter-select'); + // get data from jQuery data, metadata, headers option or header class name + col = ts.getColumnData( table, c.headers, column ); + disabled = ts.getData($header[0], col, 'filter') === 'false' || ts.getData($header[0], col, 'parser') === 'false'; + + if (buildSelect) { + buildFilter = $('<select>').appendTo( c.$filters.eq(column) ); + } else { + ffxn = ts.getColumnData( table, wo.filter_formatter, column ); + if (ffxn) { + wo.filter_formatterCount++; + buildFilter = ffxn( c.$filters.eq(column), column ); + // no element returned, so lets go find it + if (buildFilter && buildFilter.length === 0) { + buildFilter = c.$filters.eq(column).children('input'); + } + // element not in DOM, so lets attach it + if ( buildFilter && (buildFilter.parent().length === 0 || + (buildFilter.parent().length && buildFilter.parent()[0] !== c.$filters[column])) ) { + c.$filters.eq(column).append(buildFilter); + } + } else { + buildFilter = $('<input type="search">').appendTo( c.$filters.eq(column) ); + } + if (buildFilter) { + buildFilter.attr('placeholder', $header.data('placeholder') || $header.attr('data-placeholder') || wo.filter_placeholder.search || ''); + } + } + if (buildFilter) { + // add filter class name + name = ( $.isArray(wo.filter_cssFilter) ? + (typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '') : + wo.filter_cssFilter ) || ''; + buildFilter.addClass( tscss.filter + ' ' + name ).attr('data-column', column); + if (disabled) { + buildFilter.attr('placeholder', '').addClass(tscss.filterDisabled)[0].disabled = true; // disabled! + } + } + } + }, + bindSearch: function(table, $el, internal) { + table = $(table)[0]; + $el = $($el); // allow passing a selector string + if (!$el.length) { return; } + var c = table.config, + wo = c.widgetOptions, + $ext = wo.filter_$externalFilters; + if (internal !== true) { + // save anyMatch element + wo.filter_$anyMatch = $el.filter(wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector); + if ($ext && $ext.length) { + wo.filter_$externalFilters = wo.filter_$externalFilters.add( $el ); + } else { + wo.filter_$externalFilters = $el; + } + // update values (external filters added after table initialization) + ts.setFilters(table, c.$table.data('lastSearch') || [], internal === false); + } + $el + // use data attribute instead of jQuery data since the head is cloned without including the data/binding + .attr('data-lastSearchTime', new Date().getTime()) + .unbind( ('keypress keyup search change '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') ) + // include change for select - fixes #473 + .bind('keyup' + c.namespace + 'filter', function(event) { + $(this).attr('data-lastSearchTime', new Date().getTime()); + // emulate what webkit does.... escape clears the filter + if (event.which === 27) { + this.value = ''; + // live search + } else if ( wo.filter_liveSearch === false ) { + return; + // don't return if the search value is empty (all rows need to be revealed) + } else if ( this.value !== '' && ( + // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace + ( typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch ) || + // let return & backspace continue on, but ignore arrows & non-valid characters + ( event.which !== 13 && event.which !== 8 && ( event.which < 32 || (event.which >= 37 && event.which <= 40) ) ) ) ) { + return; + } + // change event = no delay; last true flag tells getFilters to skip newest timed input + ts.filter.searching( table, true, true ); + }) + .bind( 'search change keypress '.split(' ').join(c.namespace + 'filter '), function(event){ + var column = $(this).data('column'); + // don't allow "change" event to process if the input value is the same - fixes #685 + if (event.which === 13 || event.type === 'search' || event.type === 'change' && this.value !== c.lastSearch[column]) { + event.preventDefault(); + // init search with no delay + $(this).attr('data-lastSearchTime', new Date().getTime()); + ts.filter.searching( table, false, true ); + } + }); + }, + searching: function(table, filter, skipFirst) { + var wo = table.config.widgetOptions; + clearTimeout(wo.searchTimer); + if (typeof filter === 'undefined' || filter === true) { + // delay filtering + wo.searchTimer = setTimeout(function() { + ts.filter.checkFilters(table, filter, skipFirst ); + }, wo.filter_liveSearch ? wo.filter_searchDelay : 10); + } else { + // skip delay + ts.filter.checkFilters(table, filter, skipFirst); + } + }, + checkFilters: function(table, filter, skipFirst) { + var c = table.config, + wo = c.widgetOptions, + filterArray = $.isArray(filter), + filters = (filterArray) ? filter : ts.getFilters(table, true), + combinedFilters = (filters || []).join(''); // combined filter values + // prevent errors if delay init is set + if ($.isEmptyObject(c.cache)) { + // update cache if delayInit set & pager has initialized (after user initiates a search) + if (c.delayInit && c.pager && c.pager.initialized) { + c.$table.trigger('updateCache', [function(){ + ts.filter.checkFilters(table, false, skipFirst); + }] ); + } + return; + } + // add filter array back into inputs + if (filterArray) { + ts.setFilters( table, filters, false, skipFirst !== true ); + if (!wo.filter_initialized) { c.lastCombinedFilter = ''; } + } + if (wo.filter_hideFilters) { + // show/hide filter row as needed + c.$table.find('.' + tscss.filterRow).trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); + } + // return if the last search is the same; but filter === false when updating the search + // see example-widget-filter.html filter toggle buttons + if (c.lastCombinedFilter === combinedFilters && filter !== false) { + return; + } else if (filter === false) { + // force filter refresh + c.lastCombinedFilter = null; + c.lastSearch = []; + } + if (wo.filter_initialized) { c.$table.trigger('filterStart', [filters]); } + if (c.showProcessing) { + // give it time for the processing icon to kick in + setTimeout(function() { + ts.filter.findRows(table, filters, combinedFilters); + return false; + }, 30); + } else { + ts.filter.findRows(table, filters, combinedFilters); + return false; + } + }, + hideFilters: function(table, c) { + var $filterRow, $filterRow2, timer; + $(table) + .find('.' + tscss.filterRow) + .addClass(tscss.filterRowHide) + .bind('mouseenter mouseleave', function(e) { + // save event object - http://bugs.jquery.com/ticket/12140 + var event = e; + $filterRow = $(this); + clearTimeout(timer); + timer = setTimeout(function() { + if ( /enter|over/.test(event.type) ) { + $filterRow.removeClass(tscss.filterRowHide); + } else { + // don't hide if input has focus + // $(':focus') needs jQuery 1.6+ + if ( $(document.activeElement).closest('tr')[0] !== $filterRow[0] ) { + // don't hide row if any filter has a value + if (c.lastCombinedFilter === '') { + $filterRow.addClass(tscss.filterRowHide); + } + } + } + }, 200); + }) + .find('input, select').bind('focus blur', function(e) { + $filterRow2 = $(this).closest('tr'); + clearTimeout(timer); + var event = e; + timer = setTimeout(function() { + // don't hide row if any filter has a value + if (ts.getFilters(c.$table).join('') === '') { + $filterRow2[ event.type === 'focus' ? 'removeClass' : 'addClass'](tscss.filterRowHide); + } + }, 200); + }); + }, + defaultFilter: function(filter, mask){ + if (filter === '') { return filter; } + var regex = ts.filter.regex.iQuery, + maskLen = mask.match( ts.filter.regex.igQuery ).length, + query = maskLen > 1 ? $.trim(filter).split(/\s/) : [ $.trim(filter) ], + len = query.length - 1, + indx = 0, + val = mask; + if ( len < 1 && maskLen > 1 ) { + // only one "word" in query but mask has >1 slots + query[1] = query[0]; + } + // replace all {query} with query words... + // if query = "Bob", then convert mask from "!{query}" to "!Bob" + // if query = "Bob Joe Frank", then convert mask "{q} OR {q}" to "Bob OR Joe OR Frank" + while (regex.test(val)) { + val = val.replace(regex, query[indx++] || ''); + if (regex.test(val) && indx < len && (query[indx] || '') !== '') { + val = mask.replace(regex, val); + } + } + return val; + }, + getLatestSearch: function( $input ) { + if ($input) { + return $input.sort(function(a, b) { + return $(b).attr('data-lastSearchTime') - $(a).attr('data-lastSearchTime'); + }); + } + return $(); + }, + multipleColumns: function( c, $input ) { + // look for multiple columns "1-3,4-6,8" in data-column + var temp, ranges, range, start, end, singles, i, indx, len, + wo = c.widgetOptions, + // only target "all" column inputs on initialization + // & don't target "all" column inputs if they don't exist + targets = wo.filter_initialized || !$input.filter(wo.filter_anyColumnSelector).length, + columns = [], + val = $.trim( ts.filter.getLatestSearch( $input ).attr('data-column') || '' ); + // process column range + if ( targets && /-/.test( val ) ) { + ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); + len = ranges.length; + for (indx = 0; indx < len; indx++) { + range = ranges[indx].split( /\s*-\s*/ ); + start = parseInt( range[0], 10 ) || 0; + end = parseInt( range[1], 10 ) || ( c.columns - 1 ); + if ( start > end ) { temp = start; start = end; end = temp; } // swap + if ( end >= c.columns ) { end = c.columns - 1; } + for ( ; start <= end; start++ ) { + columns.push(start); + } + // remove processed range from val + val = val.replace( ranges[indx], '' ); + } + } + // process single columns + if ( targets && /,/.test( val ) ) { + singles = val.split( /\s*,\s*/ ); + len = singles.length; + for (i = 0; i < len; i++) { + if (singles[i] !== '') { + indx = parseInt( singles[i], 10 ); + if ( indx < c.columns ) { + columns.push( indx ); + } + } + } + } + // return all columns + if (!columns.length) { + for ( indx = 0; indx < c.columns; indx++ ) { + columns.push( indx ); + } + } + return columns; + }, + findRows: function(table, filters, combinedFilters) { + if (table.config.lastCombinedFilter === combinedFilters || !table.config.widgetOptions.filter_initialized) { return; } + var len, norm_rows, $rows, rowIndex, tbodyIndex, $tbody, $cells, $cell, columnIndex, + childRow, lastSearch, hasSelect, matches, result, showRow, time, val, indx, + notFiltered, searchFiltered, filterMatched, excludeMatch, fxn, ffxn, + query, injected, res, id, + regex = ts.filter.regex, + c = table.config, + wo = c.widgetOptions, + // data object passed to filters; anyMatch is a flag for the filters + data = { anyMatch: false }, + // anyMatch really screws up with these types of filters + noAnyMatch = [ 'range', 'notMatch', 'operators' ]; + + // parse columns after formatter, in case the class is added at that point + data.parsed = c.$headers.map(function(columnIndex) { + return c.parsers && c.parsers[columnIndex] && c.parsers[columnIndex].parsed || + // getData won't return "parsed" if other "filter-" class names exist (e.g. <th class="filter-select filter-parsed">) + ts.getData && ts.getData(c.$headerIndexed[columnIndex], ts.getColumnData( table, c.headers, columnIndex ), 'filter') === 'parsed' || + $(this).hasClass('filter-parsed'); + }).get(); + + if (c.debug) { + ts.log('Filter: Starting filter widget search', filters); + time = new Date(); + } + // filtered rows count + c.filteredRows = 0; + c.totalRows = 0; + // combindedFilters are undefined on init + combinedFilters = (filters || []).join(''); + + for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, c.$tbodies.eq(tbodyIndex), true); + // skip child rows & widget added (removable) rows - fixes #448 thanks to @hempel! + // $rows = $tbody.children('tr').not(c.selectorRemove); + columnIndex = c.columns; + // convert stored rows into a jQuery object + norm_rows = c.cache[tbodyIndex].normalized; + $rows = $( $.map(norm_rows, function(el){ return el[columnIndex].$row.get(); }) ); + + if (combinedFilters === '' || wo.filter_serversideFiltering) { + $rows.removeClass(wo.filter_filteredRow).not('.' + c.cssChildRow).css('display', ''); + } else { + // filter out child rows + $rows = $rows.not('.' + c.cssChildRow); + len = $rows.length; + + if ( (wo.filter_$anyMatch && wo.filter_$anyMatch.length) || typeof filters[c.columns] !== 'undefined' ) { + data.anyMatchFlag = true; + data.anyMatchFilter = wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || ( '' + filters[c.columns] ) || ''; + if (wo.filter_columnAnyMatch) { + // specific columns search + query = data.anyMatchFilter.split( ts.filter.regex.andSplit ); + injected = false; + for (indx = 0; indx < query.length; indx++) { + res = query[indx].split(':'); + if ( res.length > 1 ) { + // make the column a one-based index ( non-developers start counting from one :P ) + id = parseInt( res[0], 10 ) - 1; + if ( id >= 0 && id < c.columns ) { // if id is an integer + filters[id] = res[1]; + query.splice(indx, 1); + indx--; + injected = true; + } + } + } + if (injected) { + data.anyMatchFilter = query.join(' && '); + } + } + } + + // optimize searching only through already filtered rows - see #313 + searchFiltered = wo.filter_searchFiltered; + lastSearch = c.lastSearch || c.$table.data('lastSearch') || []; + if (searchFiltered) { + // cycle through all filters; include last (columnIndex + 1 = match any column). Fixes #669 + for (indx = 0; indx < columnIndex + 1; indx++) { + val = filters[indx] || ''; + // break out of loop if we've already determined not to search filtered rows + if (!searchFiltered) { indx = columnIndex; } + // search already filtered rows if... + searchFiltered = searchFiltered && lastSearch.length && + // there are no changes from beginning of filter + val.indexOf(lastSearch[indx] || '') === 0 && + // if there is NOT a logical "or", or range ("to" or "-") in the string + !regex.alreadyFiltered.test(val) && + // if we are not doing exact matches, using "|" (logical or) or not "!" + !/[=\"\|!]/.test(val) && + // don't search only filtered if the value is negative ('> -10' => '> -100' will ignore hidden rows) + !(/(>=?\s*-\d)/.test(val) || /(<=?\s*\d)/.test(val)) && + // if filtering using a select without a "filter-match" class (exact match) - fixes #593 + !( val !== '' && c.$filters && c.$filters.eq(indx).find('select').length && !c.$headerIndexed[indx].hasClass('filter-match') ); + } + } + notFiltered = $rows.not('.' + wo.filter_filteredRow).length; + // can't search when all rows are hidden - this happens when looking for exact matches + if (searchFiltered && notFiltered === 0) { searchFiltered = false; } + if (c.debug) { + ts.log( 'Filter: Searching through ' + ( searchFiltered && notFiltered < len ? notFiltered : 'all' ) + ' rows' ); + } + if (data.anyMatchFlag) { + if (c.sortLocaleCompare) { + // replace accents + data.anyMatchFilter = ts.replaceAccents(data.anyMatchFilter); + } + if (wo.filter_defaultFilter && regex.iQuery.test( ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '')) { + data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) ); + // clear search filtered flag because default filters are not saved to the last search + searchFiltered = false; + } + // make iAnyMatchFilter lowercase unless both filter widget & core ignoreCase options are true + // when c.ignoreCase is true, the cache contains all lower case data + data.iAnyMatchFilter = !(wo.filter_ignoreCase && c.ignoreCase) ? data.anyMatchFilter : data.anyMatchFilter.toLocaleLowerCase(); + } + + // loop through the rows + for (rowIndex = 0; rowIndex < len; rowIndex++) { + + data.cacheArray = norm_rows[rowIndex]; + + childRow = $rows[rowIndex].className; + // skip child rows & already filtered rows + if ( regex.child.test(childRow) || (searchFiltered && regex.filtered.test(childRow)) ) { continue; } + showRow = true; + // *** nextAll/nextUntil not supported by Zepto! *** + childRow = $rows.eq(rowIndex).nextUntil('tr:not(.' + c.cssChildRow + ')'); + // so, if "table.config.widgetOptions.filter_childRows" is true and there is + // a match anywhere in the child row, then it will make the row visible + // checked here so the option can be changed dynamically + data.childRowText = (childRow.length && wo.filter_childRows) ? childRow.text() : ''; + data.childRowText = wo.filter_ignoreCase ? data.childRowText.toLocaleLowerCase() : data.childRowText; + $cells = $rows.eq(rowIndex).children(); + if (data.anyMatchFlag) { + // look for multiple columns "1-3,4-6,8" + columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); + data.anyMatch = true; + data.rowArray = $cells.map(function(i){ + if ( $.inArray(i, columnIndex) > -1 ) { + var txt; + if (data.parsed[i]) { + txt = data.cacheArray[i]; + } else { + txt = this ? this.getAttribute( c.textAttribute ) || this.textContent || $(this).text() : ''; + txt = $.trim( wo.filter_ignoreCase ? txt.toLowerCase() : txt ); + if (c.sortLocaleCompare) { + txt = ts.replaceAccents(txt); + } + } + return txt; + } + }).get(); + data.filter = data.anyMatchFilter; + data.iFilter = data.iAnyMatchFilter; + data.exact = data.rowArray.join(' '); + data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; + data.cache = data.cacheArray.slice(0,-1).join(' '); + filterMatched = null; + $.each(ts.filter.types, function(type, typeFunction) { + if ($.inArray(type, noAnyMatch) < 0) { + matches = typeFunction( c, data ); + if (matches !== null) { + filterMatched = matches; + return false; + } + } + }); + if (filterMatched !== null) { + showRow = filterMatched; + } else { + if (wo.filter_startsWith) { + showRow = false; + columnIndex = c.columns; + while (!showRow && columnIndex > 0) { + columnIndex--; + showRow = showRow || data.rowArray[columnIndex].indexOf(data.iFilter) === 0; + } + } else { + showRow = (data.iExact + data.childRowText).indexOf(data.iFilter) >= 0; + } + } + data.anyMatch = false; + } + + for (columnIndex = 0; columnIndex < c.columns; columnIndex++) { + data.filter = filters[columnIndex]; + data.index = columnIndex; + + // filter types to exclude, per column + excludeMatch = ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split(/\s+/); + + // ignore if filter is empty or disabled + if (data.filter) { + data.cache = data.cacheArray[columnIndex]; + // check if column data should be from the cell or from parsed data + if (wo.filter_useParsedData || data.parsed[columnIndex]) { + data.exact = data.cache; + } else { + val = $cells[columnIndex]; + result = val ? $.trim( val.getAttribute( c.textAttribute ) || val.textContent || $cells.eq(columnIndex).text() ) : ''; + data.exact = c.sortLocaleCompare ? ts.replaceAccents(result) : result; // issue #405 + } + data.iExact = !regex.type.test(typeof data.exact) && wo.filter_ignoreCase ? data.exact.toLocaleLowerCase() : data.exact; + result = showRow; // if showRow is true, show that row + + // in case select filter option has a different value vs text "a - z|A through Z" + ffxn = wo.filter_columnFilters ? + c.$filters.add(c.$externalFilters).filter('[data-column="'+ columnIndex + '"]').find('select option:selected').attr('data-function-name') || '' : ''; + // replace accents - see #357 + if (c.sortLocaleCompare) { + data.filter = ts.replaceAccents(data.filter); + } + + val = true; + if (wo.filter_defaultFilter && regex.iQuery.test( ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || '')) { + data.filter = ts.filter.defaultFilter( data.filter, ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) ); + // val is used to indicate that a filter select is using a default filter; so we override the exact & partial matches + val = false; + } + // data.iFilter = case insensitive (if wo.filter_ignoreCase is true), data.filter = case sensitive + data.iFilter = wo.filter_ignoreCase ? (data.filter || '').toLocaleLowerCase() : data.filter; + fxn = ts.getColumnData( table, wo.filter_functions, columnIndex ); + $cell = c.$headerIndexed[columnIndex]; + hasSelect = $cell.hasClass('filter-select'); + if ( fxn || ( hasSelect && val ) ) { + if (fxn === true || hasSelect) { + // default selector uses exact match unless "filter-match" class is found + result = ($cell.hasClass('filter-match')) ? data.iExact.search(data.iFilter) >= 0 : data.filter === data.exact; + } else if (typeof fxn === 'function') { + // filter callback( exact cell content, parser normalized content, filter input value, column index, jQuery row object ) + result = fxn(data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); + } else if (typeof fxn[ffxn || data.filter] === 'function') { + // selector option function + result = fxn[ffxn || data.filter](data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); + } + } else { + filterMatched = null; + // cycle through the different filters + // filters return a boolean or null if nothing matches + $.each(ts.filter.types, function(type, typeFunction) { + if ($.inArray(type, excludeMatch) < 0) { + matches = typeFunction( c, data ); + if (matches !== null) { + filterMatched = matches; + return false; + } + } + }); + if (filterMatched !== null) { + result = filterMatched; + // Look for match, and add child row data for matching + } else { + data.exact = (data.iExact + data.childRowText).indexOf( ts.filter.parseFilter(c, data.iFilter, columnIndex, data.parsed[columnIndex]) ); + result = ( (!wo.filter_startsWith && data.exact >= 0) || (wo.filter_startsWith && data.exact === 0) ); + } + } + showRow = (result) ? showRow : false; + } + } + $rows.eq(rowIndex) + .toggleClass(wo.filter_filteredRow, !showRow)[0] + .display = showRow ? '' : 'none'; + if (childRow.length) { + childRow.toggleClass(wo.filter_filteredRow, !showRow); + } + } + } + c.filteredRows += $rows.not('.' + wo.filter_filteredRow).length; + c.totalRows += $rows.length; + ts.processTbody(table, $tbody, false); + } + c.lastCombinedFilter = combinedFilters; // save last search + c.lastSearch = filters; + c.$table.data('lastSearch', filters); + if (wo.filter_saveFilters && ts.storage) { + ts.storage( table, 'tablesorter-filters', filters ); + } + if (c.debug) { + ts.benchmark("Completed filter widget search", time); + } + if (wo.filter_initialized) { c.$table.trigger('filterEnd', c ); } + setTimeout(function(){ + c.$table.trigger('applyWidgets'); // make sure zebra widget is applied + }, 0); + }, + getOptionSource: function(table, column, onlyAvail) { + table = $(table)[0]; + var cts, indx, len, + c = table.config, + wo = c.widgetOptions, + parsed = [], + arry = false, + source = wo.filter_selectSource, + last = c.$table.data('lastSearch') || [], + fxn = $.isFunction(source) ? true : ts.getColumnData( table, source, column ); + + if (onlyAvail && last[column] !== '') { + onlyAvail = false; + } + + // filter select source option + if (fxn === true) { + // OVERALL source + arry = source(table, column, onlyAvail); + } else if ( fxn instanceof $ || ($.type(fxn) === 'string' && fxn.indexOf('</option>') >= 0) ) { + // selectSource is a jQuery object or string of options + return fxn; + } else if ($.isArray(fxn)) { + arry = fxn; + } else if ($.type(source) === 'object' && fxn) { + // custom select source function for a SPECIFIC COLUMN + arry = fxn(table, column, onlyAvail); + } + if (arry === false) { + // fall back to original method + arry = ts.filter.getOptions(table, column, onlyAvail); + } + + // get unique elements and sort the list + // if $.tablesorter.sortText exists (not in the original tablesorter), + // then natural sort the list otherwise use a basic sort + arry = $.grep(arry, function(value, indx) { + return $.inArray(value, arry) === indx; + }); + + if (c.$headerIndexed[column].hasClass('filter-select-nosort')) { + // unsorted select options + return arry; + } else { + len = arry.length; + // parse select option values + for (indx = 0; indx < len; indx++) { + // parse array data using set column parser; this DOES NOT pass the original + // table cell to the parser format function + parsed.push({ t : arry[indx], p : c.parsers && c.parsers[column].format( arry[indx], table, [], column ) }); + } + + // sort parsed select options + cts = c.textSorter || ''; + parsed.sort(function(a, b){ + // sortNatural breaks if you don't pass it strings + var x = a.p.toString(), y = b.p.toString(); + if ($.isFunction(cts)) { + // custom OVERALL text sorter + return cts(x, y, true, column, table); + } else if (typeof(cts) === 'object' && cts.hasOwnProperty(column)) { + // custom text sorter for a SPECIFIC COLUMN + return cts[column](x, y, true, column, table); + } else if (ts.sortNatural) { + // fall back to natural sort + return ts.sortNatural(x, y); + } + // using an older version! do a basic sort + return true; + }); + // rebuild arry from sorted parsed data + arry = []; + len = parsed.length; + for (indx = 0; indx < len; indx++) { + arry.push( parsed[indx].t ); + } + return arry; + } + }, + getOptions: function(table, column, onlyAvail) { + table = $(table)[0]; + var rowIndex, tbodyIndex, len, row, cache, cell, + c = table.config, + wo = c.widgetOptions, + arry = []; + for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + cache = c.cache[tbodyIndex]; + len = c.cache[tbodyIndex].normalized.length; + // loop through the rows + for (rowIndex = 0; rowIndex < len; rowIndex++) { + // get cached row from cache.row (old) or row data object (new; last item in normalized array) + row = cache.row ? cache.row[rowIndex] : cache.normalized[rowIndex][c.columns].$row[0]; + // check if has class filtered + if (onlyAvail && row.className.match(wo.filter_filteredRow)) { continue; } + // get non-normalized cell content + if (wo.filter_useParsedData || c.parsers[column].parsed || c.$headerIndexed[column].hasClass('filter-parsed')) { + arry.push( '' + cache.normalized[rowIndex][column] ); + } else { + cell = row.cells[column]; + if (cell) { + arry.push( $.trim( cell.getAttribute( c.textAttribute ) || cell.textContent || $(cell).text() ) ); + } + } + } + } + return arry; + }, + buildSelect: function(table, column, arry, updating, onlyAvail) { + table = $(table)[0]; + column = parseInt(column, 10); + if (!table.config.cache || $.isEmptyObject(table.config.cache)) { return; } + var indx, val, txt, t, $filters, $filter, + c = table.config, + wo = c.widgetOptions, + node = c.$headerIndexed[column], + // t.data('placeholder') won't work in jQuery older than 1.4.3 + options = '<option value="">' + ( node.data('placeholder') || node.attr('data-placeholder') || wo.filter_placeholder.select || '' ) + '</option>', + // Get curent filter value + currentValue = c.$table.find('thead').find('select.' + tscss.filter + '[data-column="' + column + '"]').val(); + // nothing included in arry (external source), so get the options from filter_selectSource or column data + if (typeof arry === 'undefined' || arry === '') { + arry = ts.filter.getOptionSource(table, column, onlyAvail); + } + + if ($.isArray(arry)) { + // build option list + for (indx = 0; indx < arry.length; indx++) { + txt = arry[indx] = ('' + arry[indx]).replace(/\"/g, """); + val = txt; + // allow including a symbol in the selectSource array + // "a-z|A through Z" so that "a-z" becomes the option value + // and "A through Z" becomes the option text + if (txt.indexOf(wo.filter_selectSourceSeparator) >= 0) { + t = txt.split(wo.filter_selectSourceSeparator); + val = t[0]; + txt = t[1]; + } + // replace quotes - fixes #242 & ignore empty strings - see http://stackoverflow.com/q/14990971/145346 + options += arry[indx] !== '' ? '<option ' + (val === txt ? '' : 'data-function-name="' + arry[indx] + '" ') + 'value="' + val + '">' + txt + '</option>' : ''; + } + // clear arry so it doesn't get appended twice + arry = []; + } + + // update all selects in the same column (clone thead in sticky headers & any external selects) - fixes 473 + $filters = ( c.$filters ? c.$filters : c.$table.children('thead') ).find('.' + tscss.filter); + if (wo.filter_$externalFilters) { + $filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters; + } + $filter = $filters.filter('select[data-column="' + column + '"]'); + + // make sure there is a select there! + if ($filter.length) { + $filter[ updating ? 'html' : 'append' ](options); + if (!$.isArray(arry)) { + // append options if arry is provided externally as a string or jQuery object + // options (default value) was already added + $filter.append(arry).val(currentValue); + } + $filter.val(currentValue); + } + }, + buildDefault: function(table, updating) { + var columnIndex, $header, noSelect, + c = table.config, + wo = c.widgetOptions, + columns = c.columns; + // build default select dropdown + for (columnIndex = 0; columnIndex < columns; columnIndex++) { + $header = c.$headerIndexed[columnIndex]; + noSelect = !($header.hasClass('filter-false') || $header.hasClass('parser-false')); + // look for the filter-select class; build/update it if found + if (($header.hasClass('filter-select') || ts.getColumnData( table, wo.filter_functions, columnIndex ) === true) && noSelect) { + ts.filter.buildSelect(table, columnIndex, '', updating, $header.hasClass(wo.filter_onlyAvail)); + } + } + } +}; + +ts.getFilters = function(table, getRaw, setFilters, skipFirst) { + var i, $filters, $column, cols, + filters = false, + c = table ? $(table)[0].config : '', + wo = c ? c.widgetOptions : ''; + if (getRaw !== true && wo && !wo.filter_columnFilters) { + return $(table).data('lastSearch'); + } + if (c) { + if (c.$filters) { + $filters = c.$filters.find('.' + tscss.filter); + } + if (wo.filter_$externalFilters) { + $filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters; + } + if ($filters && $filters.length) { + filters = setFilters || []; + for (i = 0; i < c.columns + 1; i++) { + cols = ( i === c.columns ? + // "all" columns can now include a range or set of columms (data-column="0-2,4,6-7") + wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector : + '[data-column="' + i + '"]' ); + $column = $filters.filter(cols); + if ($column.length) { + // move the latest search to the first slot in the array + $column = ts.filter.getLatestSearch( $column ); + if ($.isArray(setFilters)) { + // skip first (latest input) to maintain cursor position while typing + if (skipFirst) { $column.slice(1); } + if (i === c.columns) { + // prevent data-column="all" from filling data-column="0,1" (etc) + cols = $column.filter(wo.filter_anyColumnSelector); + $column = cols.length ? cols : $column; + } + $column + .val( setFilters[i] ) + .trigger('change.tsfilter'); + } else { + filters[i] = $column.val() || ''; + // don't change the first... it will move the cursor + if (i === c.columns) { + // don't update range columns from "all" setting + $column.slice(1).filter('[data-column*="' + $column.attr('data-column') + '"]').val( filters[i] ); + } else { + $column.slice(1).val( filters[i] ); + } + } + // save any match input dynamically + if (i === c.columns && $column.length) { + wo.filter_$anyMatch = $column; + } + } + } + } + } + if (filters.length === 0) { + filters = false; + } + return filters; +}; + +ts.setFilters = function(table, filter, apply, skipFirst) { + var c = table ? $(table)[0].config : '', + valid = ts.getFilters(table, true, filter, skipFirst); + if (c && apply) { + // ensure new set filters are applied, even if the search is the same + c.lastCombinedFilter = null; + c.lastSearch = []; + ts.filter.searching(c.table, filter, skipFirst); + c.$table.trigger('filterFomatterUpdate'); + } + return !!valid; +}; + +})(jQuery); + +/*! Widget: stickyHeaders - updated 3/26/2015 (v2.21.3) *//* + * Requires tablesorter v2.8+ and jQuery 1.4.3+ + * by Rob Garrison + */ +;(function ($, window) { +'use strict'; +var ts = $.tablesorter = $.tablesorter || {}; + +$.extend(ts.css, { + sticky : 'tablesorter-stickyHeader', // stickyHeader + stickyVis : 'tablesorter-sticky-visible', + stickyHide: 'tablesorter-sticky-hidden', + stickyWrap: 'tablesorter-sticky-wrapper' +}); + +// Add a resize event to table headers +ts.addHeaderResizeEvent = function(table, disable, settings) { + table = $(table)[0]; // make sure we're using a dom element + var headers, + defaults = { + timer : 250 + }, + options = $.extend({}, defaults, settings), + c = table.config, + wo = c.widgetOptions, + checkSizes = function(triggerEvent) { + wo.resize_flag = true; + headers = []; + c.$headers.each(function() { + var $header = $(this), + sizes = $header.data('savedSizes') || [0,0], // fixes #394 + width = this.offsetWidth, + height = this.offsetHeight; + if (width !== sizes[0] || height !== sizes[1]) { + $header.data('savedSizes', [ width, height ]); + headers.push(this); + } + }); + if (headers.length && triggerEvent !== false) { + c.$table.trigger('resize', [ headers ]); + } + wo.resize_flag = false; + }; + checkSizes(false); + clearInterval(wo.resize_timer); + if (disable) { + wo.resize_flag = false; + return false; + } + wo.resize_timer = setInterval(function() { + if (wo.resize_flag) { return; } + checkSizes(); + }, options.timer); +}; + +// Sticky headers based on this awesome article: +// http://css-tricks.com/13465-persistent-headers/ +// and https://github.com/jmosbech/StickyTableHeaders by Jonas Mosbech +// ************************** +ts.addWidget({ + id: "stickyHeaders", + priority: 60, // sticky widget must be initialized after the filter widget! + options: { + stickyHeaders : '', // extra class name added to the sticky header row + stickyHeaders_attachTo : null, // jQuery selector or object to attach sticky header to + stickyHeaders_xScroll : null, // jQuery selector or object to monitor horizontal scroll position (defaults: xScroll > attachTo > window) + stickyHeaders_yScroll : null, // jQuery selector or object to monitor vertical scroll position (defaults: yScroll > attachTo > window) + stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element + stickyHeaders_filteredToTop: true, // scroll table top into view after filtering + stickyHeaders_cloneId : '-sticky', // added to table ID, if it exists + stickyHeaders_addResizeEvent : true, // trigger "resize" event on headers + stickyHeaders_includeCaption : true, // if false and a caption exist, it won't be included in the sticky header + stickyHeaders_zIndex : 2 // The zIndex of the stickyHeaders, allows the user to adjust this to their needs + }, + format: function(table, c, wo) { + // filter widget doesn't initialize on an empty table. Fixes #449 + if ( c.$table.hasClass('hasStickyHeaders') || ($.inArray('filter', c.widgets) >= 0 && !c.$table.hasClass('hasFilters')) ) { + return; + } + var $table = c.$table, + // add position: relative to attach element, hopefully it won't cause trouble. + $attach = $(wo.stickyHeaders_attachTo), + namespace = c.namespace + 'stickyheaders ', + // element to watch for the scroll event + $yScroll = $(wo.stickyHeaders_yScroll || wo.stickyHeaders_attachTo || window), + $xScroll = $(wo.stickyHeaders_xScroll || wo.stickyHeaders_attachTo || window), + $thead = $table.children('thead:first'), + $header = $thead.children('tr').not('.sticky-false').children(), + $tfoot = $table.children('tfoot'), + $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '', + stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, + // is this table nested? If so, find parent sticky header wrapper (div, not table) + $nestedSticky = $table.parent().closest('.' + ts.css.table).hasClass('hasStickyHeaders') ? + $table.parent().closest('table.tablesorter')[0].config.widgetOptions.$sticky.parent() : [], + nestedStickyTop = $nestedSticky.length ? $nestedSticky.height() : 0, + // clone table, then wrap to make sticky header + $stickyTable = wo.$sticky = $table.clone() + .addClass('containsStickyHeaders ' + ts.css.sticky + ' ' + wo.stickyHeaders + ' ' + c.namespace.slice(1) + '_extra_table' ) + .wrap('<div class="' + ts.css.stickyWrap + '">'), + $stickyWrap = $stickyTable.parent() + .addClass(ts.css.stickyHide) + .css({ + position : $attach.length ? 'absolute' : 'fixed', + padding : parseInt( $stickyTable.parent().parent().css('padding-left'), 10 ), + top : stickyOffset + nestedStickyTop, + left : 0, + visibility : 'hidden', + zIndex : wo.stickyHeaders_zIndex || 2 + }), + $stickyThead = $stickyTable.children('thead:first'), + $stickyCells, + laststate = '', + spacing = 0, + setWidth = function($orig, $clone){ + $orig.filter(':visible').each(function(i) { + var width, border, + $cell = $clone.filter(':visible').eq(i), + $this = $(this); + // code from https://github.com/jmosbech/StickyTableHeaders + if ($this.css('box-sizing') === 'border-box') { + width = $this.outerWidth(); + } else { + if ($cell.css('border-collapse') === 'collapse') { + if (window.getComputedStyle) { + width = parseFloat( window.getComputedStyle(this, null).width ); + } else { + // ie8 only + border = parseFloat( $this.css('border-width') ); + width = $this.outerWidth() - parseFloat( $this.css('padding-left') ) - parseFloat( $this.css('padding-right') ) - border; + } + } else { + width = $this.width(); + } + } + $cell.css({ + 'min-width': width, + 'max-width': width + }); + }); + }, + resizeHeader = function() { + stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0; + spacing = 0; + $stickyWrap.css({ + left : $attach.length ? parseInt($attach.css('padding-left'), 10) || 0 : + $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing, + width: $table.outerWidth() + }); + setWidth( $table, $stickyTable ); + setWidth( $header, $stickyCells ); + }; + // only add a position relative if a position isn't already defined + if ($attach.length && !$attach.css('position')) { + $attach.css('position', 'relative'); + } + // fix clone ID, if it exists - fixes #271 + if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; } + // clear out cloned table, except for sticky header + // include caption & filter row (fixes #126 & #249) - don't remove cells to get correct cell indexing + $stickyTable.find('thead:gt(0), tr.sticky-false').hide(); + $stickyTable.find('tbody, tfoot').remove(); + $stickyTable.find('caption').toggle(wo.stickyHeaders_includeCaption); + // issue #172 - find td/th in sticky header + $stickyCells = $stickyThead.children().children(); + $stickyTable.css({ height:0, width:0, margin: 0 }); + // remove resizable block + $stickyCells.find('.' + ts.css.resizer).remove(); + // update sticky header class names to match real header after sorting + $table + .addClass('hasStickyHeaders') + .bind('pagerComplete' + namespace, function() { + resizeHeader(); + }); + + ts.bindEvents(table, $stickyThead.children().children('.' + ts.css.header)); + + // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. + $table.after( $stickyWrap ); + + // onRenderHeader is defined, we need to do something about it (fixes #641) + if (c.onRenderHeader) { + $stickyThead.children('tr').children().each(function(index){ + // send second parameter + c.onRenderHeader.apply( $(this), [ index, c, $stickyTable ] ); + }); + } + + // make it sticky! + $xScroll.add($yScroll) + .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') ) + .bind('scroll resize '.split(' ').join( namespace ), function(event) { + if (!$table.is(':visible')) { return; } // fixes #278 + // Detect nested tables - fixes #724 + nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; + var offset = $table.offset(), + yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 + xWindow = $.isWindow( $xScroll[0] ), + // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, + scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, + tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)), + isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', + cssSettings = { visibility : isVisible }; + + if ($attach.length) { + cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); + } + if (xWindow) { + // adjust when scrolling horizontally - fixes issue #143 + cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing; + } + if ($nestedSticky.length) { + cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; + } + $stickyWrap + .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) + .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) + .css(cssSettings); + if (isVisible !== laststate || event.type === 'resize') { + // make sure the column widths match + resizeHeader(); + laststate = isVisible; + } + }); + if (wo.stickyHeaders_addResizeEvent) { + ts.addHeaderResizeEvent(table); + } + + // look for filter widget + if ($table.hasClass('hasFilters') && wo.filter_columnFilters) { + // scroll table into view after filtering, if sticky header is active - #482 + $table.bind('filterEnd' + namespace, function() { + // $(':focus') needs jQuery 1.6+ + var $td = $(document.activeElement).closest('td'), + column = $td.parent().children().index($td); + // only scroll if sticky header is active + if ($stickyWrap.hasClass(ts.css.stickyVis) && wo.stickyHeaders_filteredToTop) { + // scroll to original table (not sticky clone) + window.scrollTo(0, $table.position().top); + // give same input/select focus; check if c.$filters exists; fixes #594 + if (column >= 0 && c.$filters) { + c.$filters.eq(column).find('a, select, input').filter(':visible').focus(); + } + } + }); + ts.filter.bindSearch( $table, $stickyCells.find('.' + ts.css.filter) ); + // support hideFilters + if (wo.filter_hideFilters) { + ts.filter.hideFilters($stickyTable, c); + } + } + + $table.trigger('stickyHeadersInit'); + + }, + remove: function(table, c, wo) { + var namespace = c.namespace + 'stickyheaders '; + c.$table + .removeClass('hasStickyHeaders') + .unbind( ('pagerComplete filterEnd '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) + .next('.' + ts.css.stickyWrap).remove(); + if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table + $(window) + .add(wo.stickyHeaders_xScroll) + .add(wo.stickyHeaders_yScroll) + .add(wo.stickyHeaders_attachTo) + .unbind( ('scroll resize '.split(' ').join(namespace)).replace(/\s+/g, ' ') ); + ts.addHeaderResizeEvent(table, false); + } +}); + +})(jQuery, window); + +/*! Widget: resizable - updated 3/26/2015 (v2.21.3) */ +;(function ($, window) { +'use strict'; +var ts = $.tablesorter = $.tablesorter || {}; + +$.extend(ts.css, { + resizableContainer : 'tablesorter-resizable-container', + resizableHandle : 'tablesorter-resizable-handle', + resizableNoSelect : 'tablesorter-disableSelection', + resizableStorage : 'tablesorter-resizable' +}); + +// Add extra scroller css +$(function(){ + var s = '<style>' + + 'body.' + ts.css.resizableNoSelect + ' { -ms-user-select: none; -moz-user-select: -moz-none;' + + '-khtml-user-select: none; -webkit-user-select: none; user-select: none; }' + + '.' + ts.css.resizableContainer + ' { position: relative; height: 1px; }' + + // make handle z-index > than stickyHeader z-index, so the handle stays above sticky header + '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px; top: 1px;' + + 'cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' + + '</style>'; + $(s).appendTo('body'); +}); + +ts.resizable = { + init : function( c, wo ) { + if ( c.$table.hasClass( 'hasResizable' ) ) { return; } + c.$table.addClass( 'hasResizable' ); + ts.resizableReset( c.table, true ); // set default widths + + // internal variables + wo.resizable_ = { + $wrap : c.$table.parent(), + mouseXPosition : 0, + $target : null, + $next : null, + overflow : c.$table.parent().css('overflow') === 'auto', + fullWidth : Math.abs(c.$table.parent().width() - c.$table.width()) < 20, + storedSizes : [] + }; + + var noResize, $header, column, storedSizes, + marginTop = parseInt( c.$table.css( 'margin-top' ), 10 ); + + wo.resizable_.storedSizes = storedSizes = ( ( ts.storage && wo.resizable !== false ) ? + ts.storage( c.table, ts.css.resizableStorage ) : + [] ) || []; + ts.resizable.setWidths( c, wo, storedSizes ); + + wo.$resizable_container = $( '<div class="' + ts.css.resizableContainer + '">' ) + .css({ top : marginTop }) + .insertBefore( c.$table ); + // add container + for ( column = 0; column < c.columns; column++ ) { + $header = c.$headerIndexed[ column ]; + noResize = ts.getData( $header, ts.getColumnData( c.table, c.headers, column ), 'resizable' ) === 'false'; + if ( !noResize ) { + $( '<div class="' + ts.css.resizableHandle + '">' ) + .appendTo( wo.$resizable_container ) + .attr({ + 'data-column' : column, + 'unselectable' : 'on' + }) + .data( 'header', $header ) + .bind( 'selectstart', false ); + } + } + c.$table.one('tablesorter-initialized', function() { + ts.resizable.setHandlePosition( c, wo ); + ts.resizable.bindings( this.config, this.config.widgetOptions ); + }); + }, + + setWidth : function( $el, width ) { + $el.css({ + 'width' : width, + 'min-width' : '', + 'max-width' : '' + }); + }, + + setWidths : function( c, wo, storedSizes ) { + var column, + $extra = $( c.namespace + '_extra_headers' ), + $col = c.$table.children( 'colgroup' ).children( 'col' ); + storedSizes = storedSizes || wo.resizable_.storedSizes || []; + // process only if table ID or url match + if ( storedSizes.length ) { + for ( column = 0; column < c.columns; column++ ) { + // set saved resizable widths + c.$headers.eq( column ).width( storedSizes[ column ] ); + if ( $extra.length ) { + // stickyHeaders needs to modify min & max width as well + ts.resizable.setWidth( $extra.eq( column ).add( $col.eq( column ) ), storedSizes[ column ] ); + } + } + if ( $( c.namespace + '_extra_table' ).length && !ts.hasWidget( c.table, 'scroller' ) ) { + ts.resizable.setWidth( $( c.namespace + '_extra_table' ), c.$table.outerWidth() ); + } + } + }, + + setHandlePosition : function( c, wo ) { + var tableWidth = c.$table.outerWidth(), + hasScroller = ts.hasWidget( c.table, 'scroller' ), + tableHeight = c.$table.height(), + $handles = wo.$resizable_container.children(), + handleCenter = Math.floor( $handles.width() / 2 - parseFloat( c.$headers.css( 'border-right-width' ) ) * 2 ); + + if ( hasScroller ) { + tableHeight = 0; + c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function(){ + var $this = $(this); + // center table has a max-height set + tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); + }); + } + $handles.each( function() { + var $this = $(this), + column = parseInt( $this.attr( 'data-column' ), 10 ), + columns = c.columns - 1, + $header = $this.data( 'header' ); + if ( column < columns || column === columns && wo.resizable_addLastColumn ) { + $this.css({ + height : tableHeight, + left : $header.position().left + $header.width() - handleCenter + }); + } + }); + }, + + // prevent text selection while dragging resize bar + toggleTextSelection : function( c, toggle ) { + var namespace = c.namespace + 'tsresize'; + c.widgetOptions.resizable_.disabled = toggle; + $( 'body' ).toggleClass( ts.css.resizableNoSelect, toggle ); + if ( toggle ) { + $( 'body' ) + .attr( 'unselectable', 'on' ) + .bind( 'selectstart' + namespace, false ); + } else { + $( 'body' ) + .removeAttr( 'unselectable' ) + .unbind( 'selectstart' + namespace ); + } + }, + + bindings : function( c, wo ) { + var namespace = c.namespace + 'tsresize'; + wo.$resizable_container.children().bind( 'mousedown', function( event ) { + // save header cell and mouse position + var column, + vars = wo.resizable_, + $extras = $( c.namespace + '_extra_headers' ), + $header = $( event.target ).data( 'header' ); + + column = parseInt( $header.attr( 'data-column' ), 10 ); + vars.$target = $header = $header.add( $extras.filter('[data-column="' + column + '"]') ); + vars.target = column; + + // if table is not as wide as it's parent, then resize the table + vars.$next = event.shiftKey || wo.resizable_targetLast ? + $header.parent().children().not( '.resizable-false' ).filter( ':last' ) : + $header.nextAll( ':not(.resizable-false)' ).eq( 0 ); + + column = parseInt( vars.$next.attr( 'data-column' ), 10 ); + vars.$next = vars.$next.add( $extras.filter('[data-column="' + column + '"]') ); + vars.next = column; + + vars.mouseXPosition = event.pageX; + vars.storedSizes = c.$headers.map(function(){ return $(this).width(); }).get(); + ts.resizable.toggleTextSelection( c, true ); + }); + + $( document ) + .bind( 'mousemove' + namespace, function( event ) { + var vars = wo.resizable_; + // ignore mousemove if no mousedown + if ( !vars.disabled || vars.mouseXPosition === 0 || !vars.$target ) { return; } + if ( wo.resizable_throttle ) { + clearTimeout( vars.timer ); + vars.timer = setTimeout( function() { + ts.resizable.mouseMove( c, wo, event ); + }, isNaN( wo.resizable_throttle ) ? 5 : wo.resizable_throttle ); + } else { + ts.resizable.mouseMove( c, wo, event ); + } + }) + .bind( 'mouseup' + namespace, function() { + if (!wo.resizable_.disabled) { return; } + ts.resizable.toggleTextSelection( c, false ); + ts.resizable.stopResize( c, wo ); + ts.resizable.setHandlePosition( c, wo ); + }); + + // resizeEnd event triggered by scroller widget + $( window ).bind( 'resize' + namespace + ' resizeEnd' + namespace, function() { + ts.resizable.setHandlePosition( c, wo ); + }); + + // right click to reset columns to default widths + c.$table.find( 'thead:first' ).add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) + .bind( 'contextmenu' + namespace, function() { + // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset + var allowClick = wo.resizable_.storedSizes.length === 0; + ts.resizableReset( c.table ); + ts.resizable.setHandlePosition( c, wo ); + wo.resizable_.storedSizes = []; + return allowClick; + }); + + }, + + mouseMove : function( c, wo, event ) { + if ( wo.resizable_.mouseXPosition === 0 || !wo.resizable_.$target ) { return; } + // resize columns + var vars = wo.resizable_, + $target = vars.$target, + $next = vars.$next, + leftEdge = event.pageX - vars.mouseXPosition, + targetWidth = $target.width(); + if ( vars.fullWidth ) { + vars.storedSizes[ vars.target ] += leftEdge; + vars.storedSizes[ vars.next ] -= leftEdge; + ts.resizable.setWidths( c, wo ); + + } else if ( vars.overflow ) { + c.$table.add( $( c.namespace + '_extra_table' ) ).width(function(i, w){ + return w + leftEdge; + }); + if ( !$next.length ) { + // if expanding right-most column, scroll the wrapper + vars.$wrap[0].scrollLeft = c.$table.width(); + } + } else { + vars.storedSizes[ vars.target ] += leftEdge; + ts.resizable.setWidths( c, wo ); + } + vars.mouseXPosition = event.pageX; + }, + + stopResize : function( c, wo ) { + var vars = wo.resizable_; + vars.storedSizes = []; + if ( ts.storage ) { + vars.storedSizes = c.$headers.map(function(){ return $(this).width(); }).get(); + if ( wo.resizable !== false ) { + // save all column widths + ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); + } + } + vars.mouseXPosition = 0; + vars.$target = vars.$next = null; + $(window).trigger('resize'); // will update stickyHeaders, just in case + } +}; + +// this widget saves the column widths if +// $.tablesorter.storage function is included +// ************************** +ts.addWidget({ + id: "resizable", + priority: 40, + options: { + resizable : true, + resizable_addLastColumn : false, + resizable_widths : [], + resizable_throttle : false, // set to true (5ms) or any number 0-10 range + resizable_targetLast : false + }, + init: function(table, thisWidget, c, wo) { + ts.resizable.init( c, wo ); + }, + remove: function( table, c, wo ) { + if (wo.$resizable_container) { + var namespace = c.namespace + 'tsresize'; + c.$table.add( $( c.namespace + '_extra_table' ) ) + .removeClass('hasResizable') + .children( 'thead' ).unbind( 'contextmenu' + namespace ); + + wo.$resizable_container.remove(); + ts.resizable.toggleTextSelection( c, false ); + ts.resizableReset( table ); + $( document ).unbind( 'mousemove' + namespace + ' mouseup' + namespace ); + } + } +}); + +ts.resizableReset = function( table, nosave ) { + $( table ).each(function(){ + var $t, + c = this.config, + wo = c && c.widgetOptions; + if ( table && c ) { + c.$headers.each( function( i ) { + $t = $(this); + if ( wo.resizable_widths && wo.resizable_widths[ i ] ) { + $t.css( 'width', wo.resizable_widths[ i ] ); + } else if ( !$t.hasClass( 'resizable-false' ) ) { + // don't clear the width of any column that is not resizable + $t.css( 'width', '' ); + } + }); + // reset stickyHeader widths + $( window ).trigger( 'resize' ); + if ( ts.storage && !nosave ) { + ts.storage( this, ts.css.resizableStorage, {} ); + } + } + }); +}; + +})( jQuery, window ); + +/*! Widget: saveSort */ +;(function ($) { +'use strict'; +var ts = $.tablesorter = $.tablesorter || {}; + +// this widget saves the last sort only if the +// saveSort widget option is true AND the +// $.tablesorter.storage function is included +// ************************** +ts.addWidget({ + id: 'saveSort', + priority: 20, + options: { + saveSort : true + }, + init: function(table, thisWidget, c, wo) { + // run widget format before all other widgets are applied to the table + thisWidget.format(table, c, wo, true); + }, + format: function(table, c, wo, init) { + var stored, time, + $table = c.$table, + saveSort = wo.saveSort !== false, // make saveSort active/inactive; default to true + sortList = { "sortList" : c.sortList }; + if (c.debug) { + time = new Date(); + } + if ($table.hasClass('hasSaveSort')) { + if (saveSort && table.hasInitialized && ts.storage) { + ts.storage( table, 'tablesorter-savesort', sortList ); + if (c.debug) { + ts.benchmark('saveSort widget: Saving last sort: ' + c.sortList, time); + } + } + } else { + // set table sort on initial run of the widget + $table.addClass('hasSaveSort'); + sortList = ''; + // get data + if (ts.storage) { + stored = ts.storage( table, 'tablesorter-savesort' ); + sortList = (stored && stored.hasOwnProperty('sortList') && $.isArray(stored.sortList)) ? stored.sortList : ''; + if (c.debug) { + ts.benchmark('saveSort: Last sort loaded: "' + sortList + '"', time); + } + $table.bind('saveSortReset', function(event) { + event.stopPropagation(); + ts.storage( table, 'tablesorter-savesort', '' ); + }); + } + // init is true when widget init is run, this will run this widget before all other widgets have initialized + // this method allows using this widget in the original tablesorter plugin; but then it will run all widgets twice. + if (init && sortList && sortList.length > 0) { + c.sortList = sortList; + } else if (table.hasInitialized && sortList && sortList.length > 0) { + // update sort change + $table.trigger('sorton', [sortList]); + } + } + }, + remove: function(table, c) { + c.$table.removeClass('hasSaveSort'); + // clear storage + if (ts.storage) { ts.storage( table, 'tablesorter-savesort', '' ); } + } +}); + +})(jQuery); + +return $.tablesorter; +})); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index f585b42..a271c05 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.21.3 *//* +/*! TableSorter (FORK) v2.21.4 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -18,15 +18,7 @@ */ /*jshint browser:true, jquery:true, unused:false, expr: true */ /*global console:false, alert:false, require:false, define:false, module:false */ -(function(factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - module.exports = factory(require('jquery')); - } else { - factory(jQuery); - } -}(function($) { +;(function($){ 'use strict'; $.extend({ /*jshint supernew:true */ @@ -34,7 +26,7 @@ var ts = this; - ts.version = '2.21.3'; + ts.version = '2.21.4'; ts.parsers = []; ts.widgets = []; @@ -1823,6 +1815,47 @@ } }; + ts.getColumnText = function( table, column, callback ) { + table = $( table )[0]; + var tbodyIndex, rowIndex, cache, row, tbodyLen, rowLen, raw, parsed, $cell, result, + hasCallback = typeof callback === 'function', + allColumns = column === 'all', + data = { raw : [], parsed: [], $cell: [] }, + c = table.config; + if ( !isEmptyObject( c ) ) { + tbodyLen = c.$tbodies.length; + for ( tbodyIndex = 0; tbodyIndex < tbodyLen; tbodyIndex++ ) { + cache = c.cache[ tbodyIndex ].normalized; + rowLen = cache.length; + for ( rowIndex = 0; rowIndex < rowLen; rowIndex++ ) { + result = true; + row = cache[ rowIndex ]; + parsed = ( allColumns ) ? row.slice(0, c.columns) : row[ column ]; + row = row[ c.columns ]; + raw = ( allColumns ) ? row.raw : row.raw[ column ]; + $cell = ( allColumns ) ? row.$row.children() : row.$row.children().eq( column ); + if ( hasCallback ) { + result = callback({ + tbodyIndex: tbodyIndex, + rowIndex: rowIndex, + parsed: parsed, + raw: raw, + $row: row.$row, + $cell: $cell + }); + } + if ( result !== false ) { + data.parsed.push( parsed ); + data.raw.push( raw ); + data.$cell.push( $cell ); + } + } + } + // return everything + return data; + } + }; + // get sorter, string, empty, etc options for each column from // jQuery data, metadata, header option or header class name ('sorter-false') // priority = jQuery data > meta > headers option > header class name @@ -2092,5 +2125,4 @@ } }); - return ts; -})); +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 03b7abe..d65e0dd 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,16 +4,16 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) widgets - updated 03-26-2015 (v2.21.3)*/ -/* Includes: storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort */ +/*! tablesorter (FORK) - updated 03-30-2015 (v2.21.4)*/ +/* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - module.exports = factory(require('jquery')); - } else { - factory(jQuery); - } + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + module.exports = factory(require('jquery')); + } else { + factory(jQuery); + } }(function($) { /*! Widget: storage - updated 3/26/2015 (v2.21.3) */ @@ -2350,6 +2350,5 @@ ts.addWidget({ })(jQuery); - - return $.tablesorter; +return $.tablesorter; })); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js index 95b8bf1..8e4a49f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js @@ -1,7 +1,4 @@ -/*! - * Extract out date parsers - * 10/26/2014 (v2.18.0) - */ +/*! Parser: Extract out date - updated 10/26/2014 (v2.18.0) */ /*jshint jquery:true */ ;(function($){ "use strict"; @@ -17,10 +14,9 @@ ymdreplace : /(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/ }; - /*! extract US Long Date (ignore any other text) + /*! extract US Long Date *//* (ignore any other text) * e.g. "Sue's Birthday! Jun 26, 2004 7:22 AM (8# 2oz)" - * demo: http://jsfiddle.net/Mottie/abkNM/4165/ - */ + * demo: http://jsfiddle.net/Mottie/abkNM/4165/ */ $.tablesorter.addParser({ id: "extractUSLongDate", is: function () { @@ -39,9 +35,8 @@ type: "numeric" }); - /*! extract MMDDYYYY (ignore any other text) - * demo: http://jsfiddle.net/Mottie/abkNM/4166/ - */ + /*! extract MMDDYYYY *//* (ignore any other text) + * demo: http://jsfiddle.net/Mottie/abkNM/4166/ */ $.tablesorter.addParser({ id: "extractMMDDYYYY", is: function () { @@ -60,9 +55,8 @@ type: "numeric" }); - /*! extract DDMMYYYY (ignore any other text) - * demo: http://jsfiddle.net/Mottie/abkNM/4167/ - */ + /*! extract DDMMYYYY *//* (ignore any other text) + * demo: http://jsfiddle.net/Mottie/abkNM/4167/ */ $.tablesorter.addParser({ id: "extractDDMMYYYY", is: function () { @@ -81,9 +75,8 @@ type: "numeric" }); - /*! extract YYYYMMDD (ignore any other text) - * demo: http://jsfiddle.net/Mottie/abkNM/4168/ - */ + /*! extract YYYYMMDD *//* (ignore any other text) + * demo: http://jsfiddle.net/Mottie/abkNM/4168/ */ $.tablesorter.addParser({ id: "extractYYYYMMDD", is: function () { diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js index fc21203..3483d35 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js @@ -1,5 +1,5 @@ -/*! ISO-8601 date parser - 10/26/2014 (v2.18.0) - * This parser will work with dates in ISO8601 format +/*! Parser: ISO-8601 date - updated 10/26/2014 (v2.18.0) */ +/* This parser works with dates in ISO8601 format * 2013-02-18T18:18:44+00:00 * Written by Sean Ellingham :https://github.com/seanellingham * See https://github.com/Mottie/tablesorter/issues/247 diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js index 04eb679..7caaae0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js @@ -1,13 +1,12 @@ -/*! Month parser - 10/26/2014 (v2.18.0) - * Demo: http://jsfiddle.net/Mottie/abkNM/4169/ - */ +/*! Parser: Month - updated 10/26/2014 (v2.18.0) */ +/* Demo: http://jsfiddle.net/Mottie/abkNM/4169/ */ /*jshint jquery:true */ ;(function($){ "use strict"; var ts = $.tablesorter; ts.dates = $.extend({}, ts.dates, { - // *** modify this array to change match the language *** + // *** modify this array to match the desired language *** monthCased : [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] }); ts.dates.monthLower = ts.dates.monthCased.join(',').toLocaleLowerCase().split(','); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-range.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-range.js index 9947f8b..eab7440 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-range.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-range.js @@ -1,8 +1,5 @@ -/*! - * Range date parsers - * 2/23/2015 (v2.21.0) - */ -/* Include the widget-filter-type-insideRange.js to filter ranges */ +/*! Parser: date ranges - updated 2/23/2015 (v2.21.0) */ +/* Include the 'widget-filter-type-insideRange.js' to filter ranges */ /*jshint jquery:true */ ;(function($){ 'use strict'; diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js index 0b43016..43571bd 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js @@ -1,6 +1,5 @@ -/*! Two digit year parser - 10/26/2014 (v2.18.0) - * Demo: http://mottie.github.io/tablesorter/docs/example-parsers-dates.html - */ +/*! Parser: two digit year - updated 10/26/2014 (v2.18.0) */ +/* Demo: http://mottie.github.io/tablesorter/docs/example-parsers-dates.html */ /*jshint jquery:true */ ;(function($){ "use strict"; diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js index 41499de..7a1b3fe 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js @@ -1,6 +1,5 @@ -/*! Weekday parser - 10/26/2014 (v2.18.0) - * Demo: http://jsfiddle.net/Mottie/abkNM/4169/ - */ +/*! Parser: weekday - updated 10/26/2014 (v2.18.0) */ +/* Demo: http://jsfiddle.net/Mottie/abkNM/4169/ */ /*jshint jquery:true */ ;(function($){ "use strict"; diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js index 9a5e3d8..8f297b4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js @@ -1,14 +1,11 @@ -/*! - * Extract dates using popular natural language date parsers - * 10/26/2014 (v2.18.0) - */ +/*! Parser: dates - updated 10/26/2014 (v2.18.0) */ +/* Extract dates using popular natural language date parsers */ /*jshint jquery:true */ ;(function($){ "use strict"; - /*! Sugar (http://sugarjs.com/dates#comparing_dates) - * demo: http://jsfiddle.net/Mottie/abkNM/4163/ - */ + /*! Sugar (http://sugarjs.com/dates#comparing_dates) */ + /* demo: http://jsfiddle.net/Mottie/abkNM/4163/ */ $.tablesorter.addParser({ id: "sugar", is: function() { @@ -21,9 +18,8 @@ type: "numeric" }); - /*! Datejs (http://www.datejs.com/) - * demo: http://jsfiddle.net/Mottie/abkNM/4164/ - */ + /*! Datejs (http://www.datejs.com/) */ + /* demo: http://jsfiddle.net/Mottie/abkNM/4164/ */ $.tablesorter.addParser({ id: "datejs", is: function() { diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-duration.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-duration.js index 1f04e67..da9d4ba 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-duration.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-duration.js @@ -1,4 +1,4 @@ -/*! Duration parser */ +/*! Parser: duration & countdown - updated 2/7/2015 (v2.19.0) */ /*jshint jquery:true, unused:false */ ;(function($){ 'use strict'; diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-feet-inch-fraction.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-feet-inch-fraction.js index 79fdea8..eb7f259 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-feet-inch-fraction.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-feet-inch-fraction.js @@ -1,4 +1,4 @@ -/*! Distance parser +/*! Parser: distance *//* * This parser will parser numbers like 5'10" (5 foot 10 inches) * and 31½ into sortable values. * Demo: http://jsfiddle.net/Mottie/abkNM/154/ diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js index 0a328e7..e60abce 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js @@ -1,4 +1,4 @@ -/*! File Type parser +/*! Parser: filetype *//* * When a file type extension is found, the equivalent name is * prefixed into the parsed data, so sorting occurs in groups */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js index 2d8b47b..3424f98 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js @@ -1,4 +1,4 @@ -/*! Title parser - updated 9/15/2014 (v2.17.8) +/*! Parser: ignoreArticles - updated 9/15/2014 (v2.17.8) *//* * This parser will remove "The", "A" and "An" from the beginning of a book * or movie title, so it sorts by the second word or number * Demo: http://jsfiddle.net/Mottie/abkNM/5/ diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-image.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-image.js index 0774403..a8b8723 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-image.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-image.js @@ -1,6 +1,5 @@ -/*! image alt attribute parser for jQuery 1.7+ & tablesorter 2.7.11+ - * New 7/17/2014 (v2.17.5) - */ +/*! Parser: image - new 7/17/2014 (v2.17.5) */ +/* alt attribute parser for jQuery 1.7+ & tablesorter 2.7.11+ */ /*jshint jquery:true, unused:false */ ;(function($){ "use strict"; diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index 20ca159..008d465 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,4 +1,4 @@ -/*! parser: input & select - updated 3/26/2015 (v2.21.3) *//* +/*! Parser: input & select - updated 3/26/2015 (v2.21.3) *//* * for jQuery 1.7+ & tablesorter 2.7.11+ * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js index 19c5c20..4f367d0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js @@ -1,4 +1,4 @@ -/*! Metric parser +/*! Parser: metric *//* * Demo: http://jsfiddle.net/Mottie/abkNM/382/ * Set the metric name in the header (defaults to "m|meter"), e.g. * <th data-metric-name="b|byte">HDD Size</th> diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js index 2e72c46..92bd007 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js @@ -1,4 +1,4 @@ -/*! Named Numbers Parser - 10/26/2014 (v2.18.0) +/*! Parser: namedNumbers - updated 10/26/2014 (v2.18.0) *//* * code modified from http://stackoverflow.com/a/12014376/145346 */ /*jshint jquery:true */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js index 04166e9..1b2dfbc 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js @@ -1,4 +1,5 @@ -/*! Network parsers - IPv4, IPv6 and MAC Addresses - 10/26/2014 (v2.18.0) */ +/*! Parser: network - updated 10/26/2014 (v2.18.0) */ +/* IPv4, IPv6 and MAC Addresses */ /*global jQuery: false */ ;(function($){ "use strict"; @@ -7,7 +8,7 @@ ipv4Format, ipv4Is; - /*! IPv6 Address parser (WIP) + /*! IPv6 Address parser (WIP) *//* * IPv6 Address (ffff:0000:0000:0000:0000:0000:0000:0000) * needs to support short versions like "::8" or "1:2::7:8" * and "::00:192.168.10.184" (embedded IPv4 address) @@ -17,8 +18,8 @@ ipv4Validate : /((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})/, ipv4Extract : /([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/, - // simplified regex from http://www.intermapper.com/support/tools/IPV6-Validator.aspx - // (specifically from http://download.dartware.com/thirdparty/ipv6validator.js) + // simplified regex from http://www.intermapper.com/support/tools/IPV6-Validator.aspx + // (specifically from http://download.dartware.com/thirdparty/ipv6validator.js) ipv6Validate : /^\s*((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/i }); @@ -83,14 +84,15 @@ }; ipv4Format = function(s, table) { var i, a = s ? s.split('.') : '', - r = '', - l = a.length; + r = '', + l = a.length; for (i = 0; i < l; i++) { r += ('000' + a[i]).slice(-3); } return s ? ts.formatFloat(r, table) : s; }; + /*! Parser: ipv4Address (a.k.a. ipAddress) */ // duplicate "ipAddress" as "ipv4Address" (to maintain backwards compatility) ts.addParser({ id: 'ipAddress', @@ -105,6 +107,7 @@ type: 'numeric' }); + /*! Parser: MAC address */ ts.addParser({ id: 'MAC', is: function(s) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-roman.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-roman.js index ca03c75..0032a74 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-roman.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-roman.js @@ -1,4 +1,4 @@ -/*! Roman numeral parsers +/*! Parser: roman - updated 6/28/MMXIV (v2.17.3) *//* * code modified from both: * Steven Levithan @ http://blog.stevenlevithan.com/archives/javascript-roman-numeral-converter * Jonathan Snook comment @ http://blog.stevenlevithan.com/archives/javascript-roman-numeral-converter#comment-16140 diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js index b385971..9284803 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js @@ -1,5 +1,5 @@ -/*! tablesorter Align Character widget - updated 2/7/2015 (v2.19.0) - * Requires tablesorter v2.8+ and jQuery 1.7+ +/*! Widget: alignChar - updated 2/7/2015 (v2.19.0) *//* + * Align Characters, Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ /*jshint browser:true, jquery:true, unused:false */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js index 4ec4370..6589fb5 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js @@ -1,4 +1,4 @@ -/* Chart widget (beta) for TableSorter 2/7/2015 (v2.19.0) +/* Widget: chart (beta) - updated 2/7/2015 (v2.19.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ */ /*jshint browser:true, jquery:true, unused:false */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index 86898e6..560c6d0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Column Selector/Responsive table widget for TableSorter - 3/5/2015 (v2.21.0) +/* Widget: columnSelector (responsive table widget) - updated 3/5/2015 (v2.21.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js index 6185e82..07ed0f6 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js @@ -1,4 +1,4 @@ -/*! tablesorter CSS Sticky Headers widget - updated 2/9/2015 (v2.19.1) +/*! Widget: cssStickyHeaders - updated 2/9/2015 (v2.19.1) *//* * Requires a modern browser, tablesorter v2.8+ */ /*jshint jquery:true, unused:false */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js index 255a008..f967aed 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js @@ -1,4 +1,4 @@ -/*! tablesorter Editable Content widget - updated 2/9/2015 (v2.19.1) +/*! Widget: editable - updated 2/9/2015 (v2.19.1) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js index 5550a63..1c5e303 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js @@ -1,4 +1,4 @@ -/*! Filter widget formatter html5 functions *//* updated 7/17/2014 (v2.17.5) +/*! Widget: filter, html5 formatter functions - updated 7/17/2014 (v2.17.5) *//* * requires: tableSorter (FORK) 2.15+ and jQuery 1.4.3+ * * html5Number (spinner) diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js index 2539f4c..00ee461 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js @@ -1,4 +1,4 @@ -/*! Filter widget formatter jQuery UI functions *//* updated 7/17/2014 (v2.17.5) +/*! Widget: filter jQuery UI formatter functions - updated 7/17/2014 (v2.17.5) *//* * requires: tableSorter (FORK) 2.15+ and jQuery 1.4.3+ * * uiSpinner (jQuery UI spinner) diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js index c9bfc85..ec5d0d3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js @@ -1,4 +1,4 @@ -/*! Widget: Filter formatter function select2 - updated 3/26/2015 (v2.21.3) *//* +/*! Widget: filter, select2 formatter function - updated 3/26/2015 (v2.21.3) *//* * requires: jQuery 1.7.2+, tableSorter (FORK) 2.16+, filter widget 2.16+ and select2 v3.4.6+ plugin */ /*jshint browser:true, jquery:true, unused:false */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js index 7dddb23..0bdd37b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js @@ -1,7 +1,4 @@ -/*! - * insideRange filter type - * 2/23/2015 (v2.21.0) - */ +/*! Widget: filter, insideRange filter type - updated 2/23/2015 (v2.21.0) */ ;(function($){ 'use strict'; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js index 3b735bc..61e1a03 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js @@ -1,4 +1,4 @@ -/*! tablesorter Formatter widget - 2/9/2015 (v2.19.1) +/*! Widget: formatter - 2/9/2015 (v2.19.1) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index 8df9e9b..6d48656 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -1,4 +1,4 @@ -/*! tablesorter Grouping widget - updated 3/5/2015 (v2.21.0) *//* +/*! Widget: grouping - updated 3/5/2015 (v2.21.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js index 39d2970..8f4f591 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js @@ -1,4 +1,4 @@ -/*! tablesorter headerTitles widget - updated 3/5/2014 (core v2.15.6) +/*! Widget: headerTitles - updated 3/5/2014 (v2.15.6) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index 50f9bd4..54962d5 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -1,4 +1,4 @@ -/*! tablesorter math widget - updated 2/9/2015 (v2.19.1) +/*! Widget: math - updated 2/9/2015 (v2.19.1) *//* * Requires tablesorter v2.16+ and jQuery 1.7+ * by Rob Garrison */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index ceeb4ab..e76174c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -1,4 +1,4 @@ -/*! Widget: Output - updated 3/26/2015 (v2.21.3) *//* +/*! Widget: output - updated 3/26/2015 (v2.21.3) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * Modified from: * HTML Table to CSV: http://www.kunalbabre.com/projects/table2CSV.php (License unknown?) diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js index c7ce509..e7a53bb 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js @@ -1,4 +1,4 @@ -/* Print widget for TableSorter 2/7/2015 (v2.19.0) +/* Widget: print - updated 2/7/2015 (v2.19.0) *//* * Requires tablesorter v2.8+ and jQuery 1.2.6+ */ /*jshint browser:true, jquery:true, unused:false */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-reflow.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-reflow.js index 07bec5b..063ebeb 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-reflow.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-reflow.js @@ -1,4 +1,4 @@ -/* table reflow widget for TableSorter 2/7/2015 (v2.19.0) +/* Widget: reflow - updated 2/7/2015 (v2.19.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * Also, this widget requires the following default css (modify as desired) diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js index edc684e..fcabde9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js @@ -1,4 +1,4 @@ -/*! tablesorter repeatHeaders widget - updated 2/7/2015 (v2.19.0) +/*! Widget: repeatHeaders - updated 2/7/2015 (v2.19.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * Original by Christian Bach from the example-widgets.html demo */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js index ccdfacf..ea04b85 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js @@ -1,4 +1,4 @@ -/* StaticRow widget for jQuery TableSorter 2.0 - updated 2/9/2015 (v2.19.1) +/*! widget: staticRow - updated 2/9/2015 (v2.19.1) *//* * Version 1.2 mod by Rob Garrison (requires tablesorter v2.16+) * Requires: * jQuery v1.4+ From 19cf770b9710aada67c81f4fd0c3be63b154cc5d Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Wed, 8 Apr 2015 17:45:24 +0200 Subject: [PATCH 056/138] * updated tablesorter to latest version (2.21.5) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 126 ++++++++++++------ .../jquery-tablesorter/jquery.tablesorter.js | 4 +- .../jquery.tablesorter.widgets.js | 122 +++++++++++------ .../widgets/widget-filter.js | 44 ++++-- .../widgets/widget-resizable.js | 76 +++++++---- .../widgets/widget-scroller.js | 4 +- 10 files changed, 255 insertions(+), 131 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6be2d6f..356884d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.16.4 (2015-04-08) + +* Upgrade tablesorter to v2.21.5 + #### v1.16.3 (2015-03-30) * Upgrade tablesorter to v2.21.4 diff --git a/README.md b/README.md index e1ad95e..ae2dfcb 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.21.4 (3/28/2015), [documentation] +Current tablesorter version: 2.21.5 (4/8/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 47a7a3c..fd628d9 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.16.4' + VERSION = '1.16.5' end diff --git a/tablesorter b/tablesorter index 655a0f0..51c6169 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 655a0f09b4b9bbb869faa9da7ccef7e3d7e25bb3 +Subproject commit 51c616997bd23057a066b837f71d9fa932e6012f diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index cd9de29..e76f985 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 03-30-2015 (v2.21.4)*/ +/*! tablesorter (FORK) - updated 04-08-2015 (v2.21.5)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.21.4 *//* +/*! TableSorter (FORK) v2.21.5 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -44,7 +44,7 @@ var ts = this; - ts.version = '2.21.4'; + ts.version = '2.21.5'; ts.parsers = []; ts.widgets = []; @@ -2861,6 +2861,12 @@ ts.filter = { } } c.$table.find('thead').find('select.' + tscss.filter + '[data-column="' + column + '"]').append(options); + txt = wo.filter_selectSource; + fxn = $.isFunction(txt) ? true : ts.getColumnData( table, txt, column ); + if (fxn) { + // updating so the extra options are appended + ts.filter.buildSelect(c.table, column, '', true, $header.hasClass(wo.filter_onlyAvail)); + } } } } @@ -3303,6 +3309,19 @@ ts.filter = { $(this).hasClass('filter-parsed'); }).get(); + // cache filter variables that use ts.getColumnData in the main loop + wo.filter_indexed = { + functions : [], + excludeFilter : [], + defaultColFilter : [], + defaultAnyFilter : ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '' + }; + for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { + wo.filter_indexed.functions[ columnIndex ] = ts.getColumnData( table, wo.filter_functions, columnIndex ); + wo.filter_indexed.defaultColFilter[ columnIndex ] = ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || ''; + wo.filter_indexed.excludeFilter[ columnIndex ] = ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split(/\s+/); + } + if (c.debug) { ts.log('Filter: Starting filter widget search', filters); time = new Date(); @@ -3389,8 +3408,8 @@ ts.filter = { // replace accents data.anyMatchFilter = ts.replaceAccents(data.anyMatchFilter); } - if (wo.filter_defaultFilter && regex.iQuery.test( ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '')) { - data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) ); + if ( wo.filter_defaultFilter && regex.iQuery.test( wo.filter_indexed.defaultAnyFilter ) ) { + data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, wo.filter_indexed.defaultAnyFilter ); // clear search filtered flag because default filters are not saved to the last search searchFiltered = false; } @@ -3472,7 +3491,7 @@ ts.filter = { data.index = columnIndex; // filter types to exclude, per column - excludeMatch = ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split(/\s+/); + excludeMatch = wo.filter_indexed.excludeFilter[ columnIndex ]; // ignore if filter is empty or disabled if (data.filter) { @@ -3497,29 +3516,30 @@ ts.filter = { } val = true; - if (wo.filter_defaultFilter && regex.iQuery.test( ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || '')) { - data.filter = ts.filter.defaultFilter( data.filter, ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) ); + if (wo.filter_defaultFilter && regex.iQuery.test( wo.filter_indexed.defaultColFilter[ columnIndex ] )) { + data.filter = ts.filter.defaultFilter( data.filter, wo.filter_indexed.defaultColFilter[ columnIndex ] ); // val is used to indicate that a filter select is using a default filter; so we override the exact & partial matches val = false; } // data.iFilter = case insensitive (if wo.filter_ignoreCase is true), data.filter = case sensitive data.iFilter = wo.filter_ignoreCase ? (data.filter || '').toLocaleLowerCase() : data.filter; - fxn = ts.getColumnData( table, wo.filter_functions, columnIndex ); + fxn = wo.filter_indexed.functions[ columnIndex ]; $cell = c.$headerIndexed[columnIndex]; hasSelect = $cell.hasClass('filter-select'); + filterMatched = null; if ( fxn || ( hasSelect && val ) ) { if (fxn === true || hasSelect) { // default selector uses exact match unless "filter-match" class is found - result = ($cell.hasClass('filter-match')) ? data.iExact.search(data.iFilter) >= 0 : data.filter === data.exact; + filterMatched = ($cell.hasClass('filter-match')) ? data.iExact.search(data.iFilter) >= 0 : data.filter === data.exact; } else if (typeof fxn === 'function') { // filter callback( exact cell content, parser normalized content, filter input value, column index, jQuery row object ) - result = fxn(data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); + filterMatched = fxn(data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); } else if (typeof fxn[ffxn || data.filter] === 'function') { // selector option function - result = fxn[ffxn || data.filter](data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); + filterMatched = fxn[ffxn || data.filter](data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); } - } else { - filterMatched = null; + } + if (filterMatched === null) { // cycle through the different filters // filters return a boolean or null if nothing matches $.each(ts.filter.types, function(type, typeFunction) { @@ -3538,6 +3558,8 @@ ts.filter = { data.exact = (data.iExact + data.childRowText).indexOf( ts.filter.parseFilter(c, data.iFilter, columnIndex, data.parsed[columnIndex]) ); result = ( (!wo.filter_startsWith && data.exact >= 0) || (wo.filter_startsWith && data.exact === 0) ); } + } else { + result = filterMatched; } showRow = (result) ? showRow : false; } @@ -4095,7 +4117,7 @@ ts.addWidget({ })(jQuery, window); -/*! Widget: resizable - updated 3/26/2015 (v2.21.3) */ +/*! Widget: resizable - updated 4/2/2015 (v2.21.5) */ ;(function ($, window) { 'use strict'; var ts = $.tablesorter = $.tablesorter || {}; @@ -4186,7 +4208,7 @@ ts.resizable = { if ( storedSizes.length ) { for ( column = 0; column < c.columns; column++ ) { // set saved resizable widths - c.$headers.eq( column ).width( storedSizes[ column ] ); + c.$headerIndexed[ column ].width( storedSizes[ column ] ); if ( $extra.length ) { // stickyHeaders needs to modify min & max width as well ts.resizable.setWidth( $extra.eq( column ).add( $col.eq( column ) ), storedSizes[ column ] ); @@ -4199,11 +4221,11 @@ ts.resizable = { }, setHandlePosition : function( c, wo ) { - var tableWidth = c.$table.outerWidth(), + var startPosition, hasScroller = ts.hasWidget( c.table, 'scroller' ), tableHeight = c.$table.height(), $handles = wo.$resizable_container.children(), - handleCenter = Math.floor( $handles.width() / 2 - parseFloat( c.$headers.css( 'border-right-width' ) ) * 2 ); + handleCenter = Math.floor( $handles.width() / 2 ); if ( hasScroller ) { tableHeight = 0; @@ -4213,15 +4235,21 @@ ts.resizable = { tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); }); } + // subtract out table left position from resizable handles. Fixes #864 + startPosition = c.$table.position().left; $handles.each( function() { var $this = $(this), column = parseInt( $this.attr( 'data-column' ), 10 ), columns = c.columns - 1, $header = $this.data( 'header' ); - if ( column < columns || column === columns && wo.resizable_addLastColumn ) { + if ( !$header ) { return; } // see #859 + if ( !$header.is(':visible') ) { + $this.hide(); + } else if ( column < columns || column === columns && wo.resizable_addLastColumn ) { $this.css({ + display: 'inline-block', height : tableHeight, - left : $header.position().left + $header.width() - handleCenter + left : $header.position().left - startPosition + $header.outerWidth() - handleCenter }); } }); @@ -4247,7 +4275,7 @@ ts.resizable = { var namespace = c.namespace + 'tsresize'; wo.$resizable_container.children().bind( 'mousedown', function( event ) { // save header cell and mouse position - var column, + var column, $this, vars = wo.resizable_, $extras = $( c.namespace + '_extra_headers' ), $header = $( event.target ).data( 'header' ); @@ -4266,7 +4294,11 @@ ts.resizable = { vars.next = column; vars.mouseXPosition = event.pageX; - vars.storedSizes = c.$headers.map(function(){ return $(this).width(); }).get(); + vars.storedSizes = []; + for ( column = 0; column < c.columns; column++ ) { + $this = c.$headerIndexed[ column ]; + vars.storedSizes[ column ] = $this.is(':visible') ? $this.width() : 0; + } ts.resizable.toggleTextSelection( c, true ); }); @@ -4297,15 +4329,20 @@ ts.resizable = { }); // right click to reset columns to default widths - c.$table.find( 'thead:first' ).add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) - .bind( 'contextmenu' + namespace, function() { - // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset - var allowClick = wo.resizable_.storedSizes.length === 0; - ts.resizableReset( c.table ); - ts.resizable.setHandlePosition( c, wo ); - wo.resizable_.storedSizes = []; - return allowClick; - }); + c.$table + .bind( 'columnUpdate' + namespace, function() { + ts.resizable.setHandlePosition( c, wo ); + }) + .find( 'thead:first' ) + .add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) + .bind( 'contextmenu' + namespace, function() { + // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset + var allowClick = wo.resizable_.storedSizes.length === 0; + ts.resizableReset( c.table ); + ts.resizable.setHandlePosition( c, wo ); + wo.resizable_.storedSizes = []; + return allowClick; + }); }, @@ -4313,10 +4350,8 @@ ts.resizable = { if ( wo.resizable_.mouseXPosition === 0 || !wo.resizable_.$target ) { return; } // resize columns var vars = wo.resizable_, - $target = vars.$target, $next = vars.$next, - leftEdge = event.pageX - vars.mouseXPosition, - targetWidth = $target.width(); + leftEdge = event.pageX - vars.mouseXPosition; if ( vars.fullWidth ) { vars.storedSizes[ vars.target ] += leftEdge; vars.storedSizes[ vars.next ] -= leftEdge; @@ -4338,10 +4373,15 @@ ts.resizable = { }, stopResize : function( c, wo ) { - var vars = wo.resizable_; + var $this, column, + vars = wo.resizable_; vars.storedSizes = []; if ( ts.storage ) { - vars.storedSizes = c.$headers.map(function(){ return $(this).width(); }).get(); + vars.storedSizes = []; + for ( column = 0; column < c.columns; column++ ) { + $this = c.$headerIndexed[ column ]; + vars.storedSizes[ column ] = $this.is(':visible') ? $this.width() : 0; + } if ( wo.resizable !== false ) { // save all column widths ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); @@ -4386,19 +4426,19 @@ ts.addWidget({ ts.resizableReset = function( table, nosave ) { $( table ).each(function(){ - var $t, + var index, $t, c = this.config, wo = c && c.widgetOptions; - if ( table && c ) { - c.$headers.each( function( i ) { - $t = $(this); - if ( wo.resizable_widths && wo.resizable_widths[ i ] ) { - $t.css( 'width', wo.resizable_widths[ i ] ); + if ( table && c && c.$headerIndexed.length ) { + for ( index = 0; index < c.columns; index++ ) { + $t = c.$headerIndexed[ index ]; + if ( wo.resizable_widths && wo.resizable_widths[ index ] ) { + $t.css( 'width', wo.resizable_widths[ index ] ); } else if ( !$t.hasClass( 'resizable-false' ) ) { // don't clear the width of any column that is not resizable $t.css( 'width', '' ); } - }); + } // reset stickyHeader widths $( window ).trigger( 'resize' ); if ( ts.storage && !nosave ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index a271c05..f760c10 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.21.4 *//* +/*! TableSorter (FORK) v2.21.5 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -26,7 +26,7 @@ var ts = this; - ts.version = '2.21.4'; + ts.version = '2.21.5'; ts.parsers = []; ts.widgets = []; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index d65e0dd..04f4d04 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 03-30-2015 (v2.21.4)*/ +/*! tablesorter (FORK) - updated 04-08-2015 (v2.21.5)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -732,6 +732,12 @@ ts.filter = { } } c.$table.find('thead').find('select.' + tscss.filter + '[data-column="' + column + '"]').append(options); + txt = wo.filter_selectSource; + fxn = $.isFunction(txt) ? true : ts.getColumnData( table, txt, column ); + if (fxn) { + // updating so the extra options are appended + ts.filter.buildSelect(c.table, column, '', true, $header.hasClass(wo.filter_onlyAvail)); + } } } } @@ -1174,6 +1180,19 @@ ts.filter = { $(this).hasClass('filter-parsed'); }).get(); + // cache filter variables that use ts.getColumnData in the main loop + wo.filter_indexed = { + functions : [], + excludeFilter : [], + defaultColFilter : [], + defaultAnyFilter : ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '' + }; + for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { + wo.filter_indexed.functions[ columnIndex ] = ts.getColumnData( table, wo.filter_functions, columnIndex ); + wo.filter_indexed.defaultColFilter[ columnIndex ] = ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || ''; + wo.filter_indexed.excludeFilter[ columnIndex ] = ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split(/\s+/); + } + if (c.debug) { ts.log('Filter: Starting filter widget search', filters); time = new Date(); @@ -1260,8 +1279,8 @@ ts.filter = { // replace accents data.anyMatchFilter = ts.replaceAccents(data.anyMatchFilter); } - if (wo.filter_defaultFilter && regex.iQuery.test( ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '')) { - data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) ); + if ( wo.filter_defaultFilter && regex.iQuery.test( wo.filter_indexed.defaultAnyFilter ) ) { + data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, wo.filter_indexed.defaultAnyFilter ); // clear search filtered flag because default filters are not saved to the last search searchFiltered = false; } @@ -1343,7 +1362,7 @@ ts.filter = { data.index = columnIndex; // filter types to exclude, per column - excludeMatch = ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split(/\s+/); + excludeMatch = wo.filter_indexed.excludeFilter[ columnIndex ]; // ignore if filter is empty or disabled if (data.filter) { @@ -1368,29 +1387,30 @@ ts.filter = { } val = true; - if (wo.filter_defaultFilter && regex.iQuery.test( ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || '')) { - data.filter = ts.filter.defaultFilter( data.filter, ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) ); + if (wo.filter_defaultFilter && regex.iQuery.test( wo.filter_indexed.defaultColFilter[ columnIndex ] )) { + data.filter = ts.filter.defaultFilter( data.filter, wo.filter_indexed.defaultColFilter[ columnIndex ] ); // val is used to indicate that a filter select is using a default filter; so we override the exact & partial matches val = false; } // data.iFilter = case insensitive (if wo.filter_ignoreCase is true), data.filter = case sensitive data.iFilter = wo.filter_ignoreCase ? (data.filter || '').toLocaleLowerCase() : data.filter; - fxn = ts.getColumnData( table, wo.filter_functions, columnIndex ); + fxn = wo.filter_indexed.functions[ columnIndex ]; $cell = c.$headerIndexed[columnIndex]; hasSelect = $cell.hasClass('filter-select'); + filterMatched = null; if ( fxn || ( hasSelect && val ) ) { if (fxn === true || hasSelect) { // default selector uses exact match unless "filter-match" class is found - result = ($cell.hasClass('filter-match')) ? data.iExact.search(data.iFilter) >= 0 : data.filter === data.exact; + filterMatched = ($cell.hasClass('filter-match')) ? data.iExact.search(data.iFilter) >= 0 : data.filter === data.exact; } else if (typeof fxn === 'function') { // filter callback( exact cell content, parser normalized content, filter input value, column index, jQuery row object ) - result = fxn(data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); + filterMatched = fxn(data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); } else if (typeof fxn[ffxn || data.filter] === 'function') { // selector option function - result = fxn[ffxn || data.filter](data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); + filterMatched = fxn[ffxn || data.filter](data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); } - } else { - filterMatched = null; + } + if (filterMatched === null) { // cycle through the different filters // filters return a boolean or null if nothing matches $.each(ts.filter.types, function(type, typeFunction) { @@ -1409,6 +1429,8 @@ ts.filter = { data.exact = (data.iExact + data.childRowText).indexOf( ts.filter.parseFilter(c, data.iFilter, columnIndex, data.parsed[columnIndex]) ); result = ( (!wo.filter_startsWith && data.exact >= 0) || (wo.filter_startsWith && data.exact === 0) ); } + } else { + result = filterMatched; } showRow = (result) ? showRow : false; } @@ -1966,7 +1988,7 @@ ts.addWidget({ })(jQuery, window); -/*! Widget: resizable - updated 3/26/2015 (v2.21.3) */ +/*! Widget: resizable - updated 4/2/2015 (v2.21.5) */ ;(function ($, window) { 'use strict'; var ts = $.tablesorter = $.tablesorter || {}; @@ -2057,7 +2079,7 @@ ts.resizable = { if ( storedSizes.length ) { for ( column = 0; column < c.columns; column++ ) { // set saved resizable widths - c.$headers.eq( column ).width( storedSizes[ column ] ); + c.$headerIndexed[ column ].width( storedSizes[ column ] ); if ( $extra.length ) { // stickyHeaders needs to modify min & max width as well ts.resizable.setWidth( $extra.eq( column ).add( $col.eq( column ) ), storedSizes[ column ] ); @@ -2070,11 +2092,11 @@ ts.resizable = { }, setHandlePosition : function( c, wo ) { - var tableWidth = c.$table.outerWidth(), + var startPosition, hasScroller = ts.hasWidget( c.table, 'scroller' ), tableHeight = c.$table.height(), $handles = wo.$resizable_container.children(), - handleCenter = Math.floor( $handles.width() / 2 - parseFloat( c.$headers.css( 'border-right-width' ) ) * 2 ); + handleCenter = Math.floor( $handles.width() / 2 ); if ( hasScroller ) { tableHeight = 0; @@ -2084,15 +2106,21 @@ ts.resizable = { tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); }); } + // subtract out table left position from resizable handles. Fixes #864 + startPosition = c.$table.position().left; $handles.each( function() { var $this = $(this), column = parseInt( $this.attr( 'data-column' ), 10 ), columns = c.columns - 1, $header = $this.data( 'header' ); - if ( column < columns || column === columns && wo.resizable_addLastColumn ) { + if ( !$header ) { return; } // see #859 + if ( !$header.is(':visible') ) { + $this.hide(); + } else if ( column < columns || column === columns && wo.resizable_addLastColumn ) { $this.css({ + display: 'inline-block', height : tableHeight, - left : $header.position().left + $header.width() - handleCenter + left : $header.position().left - startPosition + $header.outerWidth() - handleCenter }); } }); @@ -2118,7 +2146,7 @@ ts.resizable = { var namespace = c.namespace + 'tsresize'; wo.$resizable_container.children().bind( 'mousedown', function( event ) { // save header cell and mouse position - var column, + var column, $this, vars = wo.resizable_, $extras = $( c.namespace + '_extra_headers' ), $header = $( event.target ).data( 'header' ); @@ -2137,7 +2165,11 @@ ts.resizable = { vars.next = column; vars.mouseXPosition = event.pageX; - vars.storedSizes = c.$headers.map(function(){ return $(this).width(); }).get(); + vars.storedSizes = []; + for ( column = 0; column < c.columns; column++ ) { + $this = c.$headerIndexed[ column ]; + vars.storedSizes[ column ] = $this.is(':visible') ? $this.width() : 0; + } ts.resizable.toggleTextSelection( c, true ); }); @@ -2168,15 +2200,20 @@ ts.resizable = { }); // right click to reset columns to default widths - c.$table.find( 'thead:first' ).add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) - .bind( 'contextmenu' + namespace, function() { - // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset - var allowClick = wo.resizable_.storedSizes.length === 0; - ts.resizableReset( c.table ); - ts.resizable.setHandlePosition( c, wo ); - wo.resizable_.storedSizes = []; - return allowClick; - }); + c.$table + .bind( 'columnUpdate' + namespace, function() { + ts.resizable.setHandlePosition( c, wo ); + }) + .find( 'thead:first' ) + .add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) + .bind( 'contextmenu' + namespace, function() { + // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset + var allowClick = wo.resizable_.storedSizes.length === 0; + ts.resizableReset( c.table ); + ts.resizable.setHandlePosition( c, wo ); + wo.resizable_.storedSizes = []; + return allowClick; + }); }, @@ -2184,10 +2221,8 @@ ts.resizable = { if ( wo.resizable_.mouseXPosition === 0 || !wo.resizable_.$target ) { return; } // resize columns var vars = wo.resizable_, - $target = vars.$target, $next = vars.$next, - leftEdge = event.pageX - vars.mouseXPosition, - targetWidth = $target.width(); + leftEdge = event.pageX - vars.mouseXPosition; if ( vars.fullWidth ) { vars.storedSizes[ vars.target ] += leftEdge; vars.storedSizes[ vars.next ] -= leftEdge; @@ -2209,10 +2244,15 @@ ts.resizable = { }, stopResize : function( c, wo ) { - var vars = wo.resizable_; + var $this, column, + vars = wo.resizable_; vars.storedSizes = []; if ( ts.storage ) { - vars.storedSizes = c.$headers.map(function(){ return $(this).width(); }).get(); + vars.storedSizes = []; + for ( column = 0; column < c.columns; column++ ) { + $this = c.$headerIndexed[ column ]; + vars.storedSizes[ column ] = $this.is(':visible') ? $this.width() : 0; + } if ( wo.resizable !== false ) { // save all column widths ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); @@ -2257,19 +2297,19 @@ ts.addWidget({ ts.resizableReset = function( table, nosave ) { $( table ).each(function(){ - var $t, + var index, $t, c = this.config, wo = c && c.widgetOptions; - if ( table && c ) { - c.$headers.each( function( i ) { - $t = $(this); - if ( wo.resizable_widths && wo.resizable_widths[ i ] ) { - $t.css( 'width', wo.resizable_widths[ i ] ); + if ( table && c && c.$headerIndexed.length ) { + for ( index = 0; index < c.columns; index++ ) { + $t = c.$headerIndexed[ index ]; + if ( wo.resizable_widths && wo.resizable_widths[ index ] ) { + $t.css( 'width', wo.resizable_widths[ index ] ); } else if ( !$t.hasClass( 'resizable-false' ) ) { // don't clear the width of any column that is not resizable $t.css( 'width', '' ); } - }); + } // reset stickyHeader widths $( window ).trigger( 'resize' ); if ( ts.storage && !nosave ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index e16a5a1..3e43f75 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -359,6 +359,12 @@ ts.filter = { } } c.$table.find('thead').find('select.' + tscss.filter + '[data-column="' + column + '"]').append(options); + txt = wo.filter_selectSource; + fxn = $.isFunction(txt) ? true : ts.getColumnData( table, txt, column ); + if (fxn) { + // updating so the extra options are appended + ts.filter.buildSelect(c.table, column, '', true, $header.hasClass(wo.filter_onlyAvail)); + } } } } @@ -801,6 +807,19 @@ ts.filter = { $(this).hasClass('filter-parsed'); }).get(); + // cache filter variables that use ts.getColumnData in the main loop + wo.filter_indexed = { + functions : [], + excludeFilter : [], + defaultColFilter : [], + defaultAnyFilter : ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '' + }; + for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { + wo.filter_indexed.functions[ columnIndex ] = ts.getColumnData( table, wo.filter_functions, columnIndex ); + wo.filter_indexed.defaultColFilter[ columnIndex ] = ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || ''; + wo.filter_indexed.excludeFilter[ columnIndex ] = ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split(/\s+/); + } + if (c.debug) { ts.log('Filter: Starting filter widget search', filters); time = new Date(); @@ -887,8 +906,8 @@ ts.filter = { // replace accents data.anyMatchFilter = ts.replaceAccents(data.anyMatchFilter); } - if (wo.filter_defaultFilter && regex.iQuery.test( ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '')) { - data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) ); + if ( wo.filter_defaultFilter && regex.iQuery.test( wo.filter_indexed.defaultAnyFilter ) ) { + data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, wo.filter_indexed.defaultAnyFilter ); // clear search filtered flag because default filters are not saved to the last search searchFiltered = false; } @@ -970,7 +989,7 @@ ts.filter = { data.index = columnIndex; // filter types to exclude, per column - excludeMatch = ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split(/\s+/); + excludeMatch = wo.filter_indexed.excludeFilter[ columnIndex ]; // ignore if filter is empty or disabled if (data.filter) { @@ -995,29 +1014,30 @@ ts.filter = { } val = true; - if (wo.filter_defaultFilter && regex.iQuery.test( ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || '')) { - data.filter = ts.filter.defaultFilter( data.filter, ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) ); + if (wo.filter_defaultFilter && regex.iQuery.test( wo.filter_indexed.defaultColFilter[ columnIndex ] )) { + data.filter = ts.filter.defaultFilter( data.filter, wo.filter_indexed.defaultColFilter[ columnIndex ] ); // val is used to indicate that a filter select is using a default filter; so we override the exact & partial matches val = false; } // data.iFilter = case insensitive (if wo.filter_ignoreCase is true), data.filter = case sensitive data.iFilter = wo.filter_ignoreCase ? (data.filter || '').toLocaleLowerCase() : data.filter; - fxn = ts.getColumnData( table, wo.filter_functions, columnIndex ); + fxn = wo.filter_indexed.functions[ columnIndex ]; $cell = c.$headerIndexed[columnIndex]; hasSelect = $cell.hasClass('filter-select'); + filterMatched = null; if ( fxn || ( hasSelect && val ) ) { if (fxn === true || hasSelect) { // default selector uses exact match unless "filter-match" class is found - result = ($cell.hasClass('filter-match')) ? data.iExact.search(data.iFilter) >= 0 : data.filter === data.exact; + filterMatched = ($cell.hasClass('filter-match')) ? data.iExact.search(data.iFilter) >= 0 : data.filter === data.exact; } else if (typeof fxn === 'function') { // filter callback( exact cell content, parser normalized content, filter input value, column index, jQuery row object ) - result = fxn(data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); + filterMatched = fxn(data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); } else if (typeof fxn[ffxn || data.filter] === 'function') { // selector option function - result = fxn[ffxn || data.filter](data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); + filterMatched = fxn[ffxn || data.filter](data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); } - } else { - filterMatched = null; + } + if (filterMatched === null) { // cycle through the different filters // filters return a boolean or null if nothing matches $.each(ts.filter.types, function(type, typeFunction) { @@ -1036,6 +1056,8 @@ ts.filter = { data.exact = (data.iExact + data.childRowText).indexOf( ts.filter.parseFilter(c, data.iFilter, columnIndex, data.parsed[columnIndex]) ); result = ( (!wo.filter_startsWith && data.exact >= 0) || (wo.filter_startsWith && data.exact === 0) ); } + } else { + result = filterMatched; } showRow = (result) ? showRow : false; } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js index 8ca49a0..882ec04 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js @@ -1,4 +1,4 @@ -/*! Widget: resizable - updated 3/26/2015 (v2.21.3) */ +/*! Widget: resizable - updated 4/2/2015 (v2.21.5) */ ;(function ($, window) { 'use strict'; var ts = $.tablesorter = $.tablesorter || {}; @@ -89,7 +89,7 @@ ts.resizable = { if ( storedSizes.length ) { for ( column = 0; column < c.columns; column++ ) { // set saved resizable widths - c.$headers.eq( column ).width( storedSizes[ column ] ); + c.$headerIndexed[ column ].width( storedSizes[ column ] ); if ( $extra.length ) { // stickyHeaders needs to modify min & max width as well ts.resizable.setWidth( $extra.eq( column ).add( $col.eq( column ) ), storedSizes[ column ] ); @@ -102,11 +102,11 @@ ts.resizable = { }, setHandlePosition : function( c, wo ) { - var tableWidth = c.$table.outerWidth(), + var startPosition, hasScroller = ts.hasWidget( c.table, 'scroller' ), tableHeight = c.$table.height(), $handles = wo.$resizable_container.children(), - handleCenter = Math.floor( $handles.width() / 2 - parseFloat( c.$headers.css( 'border-right-width' ) ) * 2 ); + handleCenter = Math.floor( $handles.width() / 2 ); if ( hasScroller ) { tableHeight = 0; @@ -116,15 +116,21 @@ ts.resizable = { tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); }); } + // subtract out table left position from resizable handles. Fixes #864 + startPosition = c.$table.position().left; $handles.each( function() { var $this = $(this), column = parseInt( $this.attr( 'data-column' ), 10 ), columns = c.columns - 1, $header = $this.data( 'header' ); - if ( column < columns || column === columns && wo.resizable_addLastColumn ) { + if ( !$header ) { return; } // see #859 + if ( !$header.is(':visible') ) { + $this.hide(); + } else if ( column < columns || column === columns && wo.resizable_addLastColumn ) { $this.css({ + display: 'inline-block', height : tableHeight, - left : $header.position().left + $header.width() - handleCenter + left : $header.position().left - startPosition + $header.outerWidth() - handleCenter }); } }); @@ -150,7 +156,7 @@ ts.resizable = { var namespace = c.namespace + 'tsresize'; wo.$resizable_container.children().bind( 'mousedown', function( event ) { // save header cell and mouse position - var column, + var column, $this, vars = wo.resizable_, $extras = $( c.namespace + '_extra_headers' ), $header = $( event.target ).data( 'header' ); @@ -169,7 +175,11 @@ ts.resizable = { vars.next = column; vars.mouseXPosition = event.pageX; - vars.storedSizes = c.$headers.map(function(){ return $(this).width(); }).get(); + vars.storedSizes = []; + for ( column = 0; column < c.columns; column++ ) { + $this = c.$headerIndexed[ column ]; + vars.storedSizes[ column ] = $this.is(':visible') ? $this.width() : 0; + } ts.resizable.toggleTextSelection( c, true ); }); @@ -200,15 +210,20 @@ ts.resizable = { }); // right click to reset columns to default widths - c.$table.find( 'thead:first' ).add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) - .bind( 'contextmenu' + namespace, function() { - // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset - var allowClick = wo.resizable_.storedSizes.length === 0; - ts.resizableReset( c.table ); - ts.resizable.setHandlePosition( c, wo ); - wo.resizable_.storedSizes = []; - return allowClick; - }); + c.$table + .bind( 'columnUpdate' + namespace, function() { + ts.resizable.setHandlePosition( c, wo ); + }) + .find( 'thead:first' ) + .add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) + .bind( 'contextmenu' + namespace, function() { + // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset + var allowClick = wo.resizable_.storedSizes.length === 0; + ts.resizableReset( c.table ); + ts.resizable.setHandlePosition( c, wo ); + wo.resizable_.storedSizes = []; + return allowClick; + }); }, @@ -216,10 +231,8 @@ ts.resizable = { if ( wo.resizable_.mouseXPosition === 0 || !wo.resizable_.$target ) { return; } // resize columns var vars = wo.resizable_, - $target = vars.$target, $next = vars.$next, - leftEdge = event.pageX - vars.mouseXPosition, - targetWidth = $target.width(); + leftEdge = event.pageX - vars.mouseXPosition; if ( vars.fullWidth ) { vars.storedSizes[ vars.target ] += leftEdge; vars.storedSizes[ vars.next ] -= leftEdge; @@ -241,10 +254,15 @@ ts.resizable = { }, stopResize : function( c, wo ) { - var vars = wo.resizable_; + var $this, column, + vars = wo.resizable_; vars.storedSizes = []; if ( ts.storage ) { - vars.storedSizes = c.$headers.map(function(){ return $(this).width(); }).get(); + vars.storedSizes = []; + for ( column = 0; column < c.columns; column++ ) { + $this = c.$headerIndexed[ column ]; + vars.storedSizes[ column ] = $this.is(':visible') ? $this.width() : 0; + } if ( wo.resizable !== false ) { // save all column widths ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); @@ -289,19 +307,19 @@ ts.addWidget({ ts.resizableReset = function( table, nosave ) { $( table ).each(function(){ - var $t, + var index, $t, c = this.config, wo = c && c.widgetOptions; - if ( table && c ) { - c.$headers.each( function( i ) { - $t = $(this); - if ( wo.resizable_widths && wo.resizable_widths[ i ] ) { - $t.css( 'width', wo.resizable_widths[ i ] ); + if ( table && c && c.$headerIndexed.length ) { + for ( index = 0; index < c.columns; index++ ) { + $t = c.$headerIndexed[ index ]; + if ( wo.resizable_widths && wo.resizable_widths[ index ] ) { + $t.css( 'width', wo.resizable_widths[ index ] ); } else if ( !$t.hasClass( 'resizable-false' ) ) { // don't clear the width of any column that is not resizable $t.css( 'width', '' ); } - }); + } // reset stickyHeader widths $( window ).trigger( 'resize' ); if ( ts.storage && !nosave ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index af69784..d55dbcf 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -1,4 +1,4 @@ -/*! Widget: scroller - updated 3/26/2015 (v2.21.3) *//* +/*! Widget: scroller - updated 4/2/2015 (v2.21.5) *//* Copyright (C) 2011 T. Connell & Associates, Inc. Dual-licensed under the MIT and GPL licenses @@ -434,7 +434,7 @@ ts.scroller = { ts.bindEvents( c.table, $fixedColumn.find( '.' + tscss.header ) ); // update thead & tbody in fixed column - temp = ( 'sortEnd filterEnd ' ).split( ' ' ).join( namespace + ' ' ); + temp = ( 'tablesorter-initialized sortEnd filterEnd ' ).split( ' ' ).join( namespace + ' ' ); c.$table .off( temp ) .on( temp, function( event, size ) { From ea35c813b9169257d747b4d4d3382084f19c1a7b Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Sun, 17 May 2015 23:17:08 +0200 Subject: [PATCH 057/138] * updated tablesorter to latest version (2.22.0) --- CHANGELOG.md | 4 + README.md | 4 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 65 +- .../jquery.tablesorter.combined.js | 1943 ++++++++++------- .../jquery-tablesorter/jquery.tablesorter.js | 290 +-- .../jquery.tablesorter.widgets.js | 1655 ++++++++------ .../jquery-tablesorter/parsers/parser-date.js | 10 +- .../parsers/parser-globalize.js | 46 + .../parsers/parser-input-select.js | 168 +- .../parsers/parser-named-numbers.js | 11 +- .../parsers/parser-network.js | 43 +- .../widgets/widget-columns.js | 2 +- .../widgets/widget-editable.js | 137 +- .../widgets/widget-filter.js | 1621 ++++++++------ .../widgets/widget-grouping.js | 8 +- .../jquery-tablesorter/widgets/widget-math.js | 42 +- .../widgets/widget-output.js | 12 +- .../widgets/widget-pager.js | 69 +- .../widgets/widget-resizable.js | 12 +- .../widgets/widget-saveSort.js | 2 +- .../widgets/widget-scroller.js | 84 +- .../widgets/widget-stickyHeaders.js | 2 +- .../widgets/widget-storage.js | 2 +- .../widgets/widget-uitheme.js | 2 +- .../jquery-tablesorter/theme.black-ice.css | 3 +- .../jquery-tablesorter/theme.blue.css | 3 +- .../jquery-tablesorter/theme.bootstrap.css | 3 +- .../jquery-tablesorter/theme.bootstrap_2.css | 14 +- .../jquery-tablesorter/theme.dark.css | 3 +- .../jquery-tablesorter/theme.default.css | 2 +- 32 files changed, 3605 insertions(+), 2661 deletions(-) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/parsers/parser-globalize.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 356884d..06c473e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.17.0 (2015-05-17) + +* Upgrade tablesorter to v2.22.0 + #### v1.16.4 (2015-04-08) * Upgrade tablesorter to v2.21.5 diff --git a/README.md b/README.md index ae2dfcb..4ea5514 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.21.5 (4/8/2015), [documentation] +Current tablesorter version: 2.22.0 (5/17/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. @@ -26,7 +26,7 @@ Or install it yourself as: Rails 3.2 and higher (tested up to 4.2) -Tested with ruby 1.9.3 - 2.2.1 +Tested with ruby 1.9.3 - 2.2.2 ## Usage diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index fd628d9..0dd405c 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.16.5' + VERSION = '1.17.0' end diff --git a/tablesorter b/tablesorter index 51c6169..13e3d05 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 51c616997bd23057a066b837f71d9fa932e6012f +Subproject commit 13e3d0593d16bd36f5e9c3e2366823f5e0be47b6 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index e5e77c9..6fb251f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin - * updated 3/26/2015 (v2.21.3) + * updated 5/17/2015 (v2.22.0) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -377,12 +377,12 @@ // process data if ( typeof(p.ajaxProcessing) === "function" ) { // ajaxProcessing result: [ total, rows, headers ] - var i, j, hsh, $f, $sh, t, th, d, l, rr_count, + var i, j, t, hsh, $f, $sh, $headers, $h, icon, th, d, l, rr_count, len, c = table.config, - $t = c.$table, + $table = c.$table, tds = '', result = p.ajaxProcessing(data, table, xhr) || [ 0, [] ], - hl = $t.find('thead th').length; + hl = $table.find('thead th').length; // Clean up any previous error. ts.showError(table); @@ -445,28 +445,30 @@ p.processAjaxOnInit = true; // only add new header text if the length matches if ( th && th.length === hl ) { - hsh = $t.hasClass('hasStickyHeaders'); + hsh = $table.hasClass('hasStickyHeaders'); $sh = hsh ? c.widgetOptions.$sticky.children('thead:first').children('tr').children() : ''; - $f = $t.find('tfoot tr:first').children(); + $f = $table.find('tfoot tr:first').children(); // don't change td headers (may contain pager) - c.$headers.filter('th').each(function(j){ - var $t = $(this), icn; + $headers = c.$headers.filter( 'th ' ); + len = $headers.length; + for ( j = 0; j < len; j++ ) { + $h = $headers.eq( j ); // add new test within the first span it finds, or just in the header - if ( $t.find('.' + ts.css.icon).length ) { - icn = $t.find('.' + ts.css.icon).clone(true); - $t.find('.tablesorter-header-inner').html( th[j] ).append(icn); + if ( $h.find('.' + ts.css.icon).length ) { + icon = $h.find('.' + ts.css.icon).clone(true); + $h.find('.tablesorter-header-inner').html( th[j] ).append(icon); if ( hsh && $sh.length ) { - icn = $sh.eq(j).find('.' + ts.css.icon).clone(true); - $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ).append(icn); + icon = $sh.eq(j).find('.' + ts.css.icon).clone(true); + $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ).append(icon); } } else { - $t.find('.tablesorter-header-inner').html( th[j] ); + $h.find('.tablesorter-header-inner').html( th[j] ); if (hsh && $sh.length) { $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ); } } $f.eq(j).html( th[j] ); - }); + } } } if (c.showProcessing) { @@ -479,7 +481,7 @@ p.last.currentFilters = p.currentFilters; p.last.sortList = (c.sortList || []).join(','); updatePageDisplay(table, p, false); - $t.trigger('updateCache', [function(){ + $table.trigger('updateCache', [function(){ if (p.initialized) { // apply widgets after table has rendered & after a delay to prevent // multiple applyWidget blocking code from blocking this trigger @@ -487,7 +489,7 @@ if (c.debug) { ts.log('Pager: Triggering pagerChange'); } - $t + $table .trigger('applyWidgets') .trigger('pagerChange', p); updatePageDisplay(table, p, true); @@ -649,6 +651,7 @@ }, showAllRows = function(table, p){ + var index, $controls, len; if ( p.ajax ) { pagerArrows(p, true); } else { @@ -669,9 +672,15 @@ } } // disable size selector - p.$size.add(p.$goto).add(p.$container.find('.ts-startRow, .ts-page')).each(function(){ - $(this).attr('aria-disabled', 'true').addClass(p.cssDisabled)[0].disabled = true; - }); + $controls = p.$size + .add( p.$goto ) + .add( p.$container.find( '.ts-startRow, .ts-page' ) ); + len = $controls.length; + for ( index = 0; index < len; index++ ) { + $controls.eq( index ) + .attr( 'aria-disabled', 'true' ) + .addClass( p.cssDisabled )[0].disabled = true; + } }, // updateCache if delayInit: true @@ -1043,13 +1052,15 @@ }() }); // see #486 - ts.showError = function(table, message){ - $(table).each(function(){ - var $row, - c = this.config, + ts.showError = function(table, message) { + var index, $row, c, errorRow, + $table = $( table ), + len = $table.length; + for ( index = 0; index < len; index++ ) { + c = $table[ index ].config; + if ( c ) { errorRow = c.pager && c.pager.cssErrorRow || c.widgetOptions.pager_css && c.widgetOptions.pager_css.errorRow || 'tablesorter-errorRow'; - if (c) { - if (typeof message === 'undefined') { + if ( typeof message === 'undefined' ) { c.$table.find('thead').find(c.selectorRemove).remove(); } else { $row = ( /tr\>/.test(message) ? $(message) : $('<tr><td colspan="' + c.columns + '">' + message + '</td></tr>') ) @@ -1065,7 +1076,7 @@ }); } } - }); + } }; // extend plugin scope diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index e76f985..eff94f0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 04-08-2015 (v2.21.5)*/ +/*! tablesorter (FORK) - updated 05-17-2015 (v2.22.0)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.21.5 *//* +/*! TableSorter (FORK) v2.22.0 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -44,7 +44,7 @@ var ts = this; - ts.version = '2.21.5'; + ts.version = '2.22.0'; ts.parsers = []; ts.widgets = []; @@ -119,6 +119,11 @@ cssNoSort : 'tablesorter-noSort', // class name added to element inside header; clicking on it won't cause a sort cssIgnoreRow : 'tablesorter-ignoreRow', // header row to ignore; cells within this row will not be added to c.$headers + // *** events + pointerClick : 'click', + pointerDown : 'mousedown', + pointerUp : 'mouseup', + // *** selectors selectorHeaders : '> thead th, > thead td', selectorSort : 'th, td', // jQuery selector of content within selectorHeaders that is clickable to trigger a sort @@ -203,7 +208,11 @@ $node = node.jquery ? node : $(node); if (typeof(t) === 'string') { // check data-attribute first when set to 'basic'; don't use node.innerText - it's really slow! - return $.trim( ( t === 'basic' ? $node.attr(c.textAttribute) || node.textContent : node.textContent ) || $node.text() || '' ); + // http://www.kellegous.com/j/2013/02/27/innertext-vs-textcontent/ + return $.trim( + ( t === 'basic' ? $node.attr(c.textAttribute) || node.textContent : node.textContent ) || + $node.text() + ); } else { if (typeof(t) === 'function') { return $.trim( t($node[0], c.table, cellIndex) ); @@ -212,7 +221,7 @@ } } // fallback - return $.trim( $node[0].textContent || $node.text() || '' ); + return $.trim( $node[0].textContent || $node.text() ); }; function detectParserForColumn(table, rows, rowIndex, cellIndex) { @@ -246,6 +255,32 @@ return ts.getParserById('text'); } + // centralized function to extract/parse cell contents + function getParsedText( c, cell, colIndex, txt ) { + if ( typeof txt === 'undefined' ) { + txt = ts.getElementText( c, cell, colIndex ); + } + // if no parser, make sure to return the txt + var val = '' + txt, + parser = c.parsers[ colIndex ], + extractor = c.extractors[ colIndex ]; + if ( parser ) { + // do extract before parsing, if there is one + if ( extractor && typeof extractor.format === 'function' ) { + txt = extractor.format( txt, c.table, cell, colIndex ); + } + // allow parsing if the string is empty, previously parsing would change it to zero, + // in case the parser needs to extract data from the table cell attributes + val = parser.id === 'no-parser' ? '' : + // make sure txt is a string (extractor may have converted it) + parser.format( '' + txt, c.table, cell, colIndex ); + if ( c.ignoreCase && typeof val === 'string' ) { + val = val.toLowerCase(); + } + } + return val; + } + function buildParserCache(table) { var c = table.config, // update table bodies in case we start with an empty table @@ -309,11 +344,10 @@ /* utils */ function buildCache(table) { - var cc, t, tx, v, i, j, k, $row, cols, cacheTime, - totalRows, rowData, colMax, + var cc, t, v, i, j, k, $row, cols, cacheTime, + totalRows, rowData, prevRowData, colMax, c = table.config, $tb = c.$tbodies, - extractors = c.extractors, parsers = c.parsers; c.cache = {}; c.totalRows = 0; @@ -344,62 +378,61 @@ raw: [] // original row text }; /** Add the table data to main data array */ - $row = $($tb[k].rows[i]); + $row = $( $tb[ k ].rows[ i ] ); cols = []; // if this is a child row, add it to the last row's children and continue to the next row // ignore child row class, if it is the first row - if ($row.hasClass(c.cssChildRow) && i !== 0) { + if ( $row.hasClass( c.cssChildRow ) && i !== 0 ) { t = cc.normalized.length - 1; - cc.normalized[t][c.columns].$row = cc.normalized[t][c.columns].$row.add($row); + prevRowData = cc.normalized[ t ][ c.columns ]; + prevRowData.$row = prevRowData.$row.add( $row ); // add 'hasChild' class name to parent row - if (!$row.prev().hasClass(c.cssChildRow)) { - $row.prev().addClass(ts.css.cssHasChild); + if ( !$row.prev().hasClass( c.cssChildRow ) ) { + $row.prev().addClass( ts.css.cssHasChild ); } // save child row content (un-parsed!) - rowData.child[t] = $.trim( $row[0].textContent || $row.text() || '' ); + v = $row.children( 'th, td' ); + t = prevRowData.child.length; + prevRowData.child[ t ] = []; + // child row content does not account for colspans/rowspans; so indexing may be off + for ( j = 0; j < c.columns; j++ ) { + prevRowData.child[ t ][ j ] = getParsedText( c, v[ j ], j ); + } // go to the next for loop continue; } rowData.$row = $row; rowData.order = i; // add original row position to rowCache - for (j = 0; j < c.columns; ++j) { - if (typeof parsers[j] === 'undefined') { - if (c.debug) { - log('No parser found for cell:', $row[0].cells[j], 'does it have a header?'); + for ( j = 0; j < c.columns; ++j ) { + if (typeof parsers[ j ] === 'undefined') { + if ( c.debug ) { + log( 'No parser found for cell:', $row[ 0 ].cells[ j ], 'does it have a header?' ); } continue; } - t = ts.getElementText(c, $row[0].cells[j], j); - rowData.raw.push(t); // save original row text - // do extract before parsing if there is one - if (typeof extractors[j].id === 'undefined') { - tx = t; - } else { - tx = extractors[j].format(t, table, $row[0].cells[j], j); - } - // allow parsing if the string is empty, previously parsing would change it to zero, - // in case the parser needs to extract data from the table cell attributes - v = parsers[j].id === 'no-parser' ? '' : parsers[j].format(tx, table, $row[0].cells[j], j); - cols.push( c.ignoreCase && typeof v === 'string' ? v.toLowerCase() : v ); - if ((parsers[j].type || '').toLowerCase() === 'numeric') { + t = ts.getElementText( c, $row[ 0 ].cells[j], j ); + rowData.raw.push( t ); // save original row text + v = getParsedText( c, $row[ 0 ].cells[ j ], j, t ); + cols.push( v ); + if ( ( parsers[ j ].type || '' ).toLowerCase() === 'numeric' ) { // determine column max value (ignore sign) - colMax[j] = Math.max(Math.abs(v) || 0, colMax[j] || 0); + colMax[ j ] = Math.max( Math.abs( v ) || 0, colMax[ j ] || 0 ); } } // ensure rowData is always in the same location (after the last column) - cols[c.columns] = rowData; - cc.normalized.push(cols); + cols[ c.columns ] = rowData; + cc.normalized.push( cols ); } cc.colMax = colMax; // total up rows, not including child rows c.totalRows += cc.normalized.length; } - if (c.showProcessing) { - ts.isProcessing(table); // remove processing icon + if ( c.showProcessing ) { + ts.isProcessing( table ); // remove processing icon } - if (c.debug) { - benchmark('Building cache for ' + totalRows + ' rows', cacheTime); + if ( c.debug ) { + benchmark( 'Building cache for ' + totalRows + ' rows', cacheTime ); } } @@ -539,14 +572,15 @@ } function updateHeader(table) { - var s, $th, col, - c = table.config; - c.$headers.each(function(index, th){ - $th = $(th); + var index, s, $th, col, + c = table.config, + len = c.$headers.length; + for ( index = 0; index < len; index++ ) { + $th = c.$headers.eq( index ); col = ts.getColumnData( table, c.headers, index, true ); // add 'sorter-false' class if 'parser-false' is set - s = ts.getData( th, col, 'sorter' ) === 'false' || ts.getData( th, col, 'parser' ) === 'false'; - th.sortDisabled = s; + s = ts.getData( $th, col, 'sorter' ) === 'false' || ts.getData( $th, col, 'parser' ) === 'false'; + $th[0].sortDisabled = s; $th[ s ? 'addClass' : 'removeClass' ]('sorter-false').attr('aria-disabled', '' + s); // aria-controls - requires table ID if (table.id) { @@ -556,11 +590,11 @@ $th.attr('aria-controls', table.id); } } - }); + } } function setHeadersCss(table) { - var f, i, j, + var f, h, i, j, $headers, $h, nextSort, txt, c = table.config, list = c.sortList, len = list.length, @@ -604,14 +638,19 @@ } } // add verbose aria labels - c.$headers.not('.sorter-false').each(function(){ - var $this = $(this), - nextSort = this.order[(this.count + 1) % (c.sortReset ? 3 : 2)], - txt = $.trim( $this.text() ) + ': ' + - ts.language[ $this.hasClass(ts.css.sortAsc) ? 'sortAsc' : $this.hasClass(ts.css.sortDesc) ? 'sortDesc' : 'sortNone' ] + + len = c.$headers.length; + $headers = c.$headers.not('.sorter-false'); + for ( i = 0; i < len; i++ ) { + $h = $headers.eq( i ); + if ( $h.length ) { + h = $headers[ i ]; + nextSort = h.order[ ( h.count + 1 ) % ( c.sortReset ? 3 : 2 ) ], + txt = $.trim( $h.text() ) + ': ' + + ts.language[ $h.hasClass( ts.css.sortAsc ) ? 'sortAsc' : $h.hasClass( ts.css.sortDesc ) ? 'sortDesc' : 'sortNone' ] + ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ]; - $this.attr('aria-label', txt ); - }); + $h.attr( 'aria-label', txt ); + } + } } function updateHeaderSortCount( table, list ) { @@ -624,9 +663,10 @@ val = sortList[indx]; // ensure all sortList values are numeric - fixes #127 col = parseInt(val[0], 10); - // make sure header exists - header = c.$headerIndexed[col][0]; - if (header) { // prevents error if sorton array is wrong + // prevents error if sorton array is wrong + if ( col < c.columns && c.$headerIndexed[col] ) { + // make sure header exists + header = c.$headerIndexed[col][0]; // o.count = o.count + 1; dir = ('' + val[1]).match(/^(1|d|s|o|n)/); dir = dir ? dir[0] : ''; @@ -670,10 +710,11 @@ // let any updates complete before initializing a sort return setTimeout(function(){ initSort(table, cell, event); }, 50); } - var arry, indx, col, order, s, + var arry, indx, i, col, order, s, $header, c = table.config, key = !event[c.sortMultiSortKey], - $table = c.$table; + $table = c.$table, + len = c.$headers.length; // Only call sortStart if sorting is enabled $table.trigger('sortStart', table); // get current column sort order @@ -681,12 +722,13 @@ // reset all sorts on non-current column - issue #30 if (c.sortRestart) { indx = cell; - c.$headers.each(function() { + for ( i = 0; i < len; i++ ) { + $header = c.$headers.eq( i ); // only reset counts on columns that weren't just clicked on and if not included in a multisort - if (this !== indx && (key || !$(this).is('.' + ts.css.sortDesc + ',.' + ts.css.sortAsc))) { - this.count = -1; + if ( $header[0] !== indx && ( key || !$header.is('.' + ts.css.sortDesc + ',.' + ts.css.sortAsc) ) ) { + $header[0].count = -1; } - }); + } } // get current column index indx = parseInt( $(cell).attr('data-column'), 10 ); @@ -912,35 +954,31 @@ table.isUpdating = true; $table.find(c.selectorRemove).remove(); // get position from the dom - var v, t, row, icell, + var t, row, icell, cache, $tb = c.$tbodies, $cell = $(cell), // update cache - format: function(s, table, cell, cellIndex) // no closest in jQuery v1.2.6 - tbdy = $tb.index( $(cell).closest('tbody') ),$row = $(cell).closest('tr'); tbdy = $tb.index( $.fn.closest ? $cell.closest('tbody') : $cell.parents('tbody').filter(':first') ), + tbcache = c.cache[ tbdy ], $row = $.fn.closest ? $cell.closest('tr') : $cell.parents('tr').filter(':first'); cell = $cell[0]; // in case cell is a jQuery object // tbody may not exist if update is initialized while tbody is removed for processing if ($tb.length && tbdy >= 0) { - row = $tb.eq(tbdy).find('tr').index( $row ); + row = $tb.eq( tbdy ).find( 'tr' ).index( $row ); + cache = tbcache.normalized[ row ]; icell = $cell.index(); - c.cache[tbdy].normalized[row][c.columns].$row = $row; - if (typeof c.extractors[icell].id === 'undefined') { - t = ts.getElementText(c, cell, icell); - } else { - t = c.extractors[icell].format( ts.getElementText(c, cell, icell), table, cell, icell ); - } - v = c.parsers[icell].id === 'no-parser' ? '' : - c.parsers[icell].format( t, table, cell, icell ); - c.cache[tbdy].normalized[row][icell] = c.ignoreCase && typeof v === 'string' ? v.toLowerCase() : v; - if ((c.parsers[icell].type || '').toLowerCase() === 'numeric') { + t = getParsedText( c, cell, icell ); + cache[ icell ] = t; + cache[ c.columns ].$row = $row; + if ( (c.parsers[icell].type || '').toLowerCase() === 'numeric' ) { // update column max value (ignore sign) - c.cache[tbdy].colMax[icell] = Math.max(Math.abs(v) || 0, c.cache[tbdy].colMax[icell] || 0); + tbcache.colMax[icell] = Math.max(Math.abs(t) || 0, tbcache.colMax[icell] || 0); } - v = resort !== 'undefined' ? resort : c.resort; - if (v !== false) { + t = resort !== 'undefined' ? resort : c.resort; + if (t !== false) { // widgets will be reapplied - checkResort(c, v, callback); + checkResort(c, t, callback); } else { // don't reapply widgets is resort is false, just in case it causes // problems with element focus @@ -960,7 +998,7 @@ commonUpdate(table, resort, callback); } else { $row = $($row).attr('role', 'row'); // make sure we're using a jQuery object - var i, j, l, t, v, rowData, cells, + var i, j, l, rowData, cells, rows = $row.filter('tr').length, tbdy = c.$tbodies.index( $row.parents('tbody').filter(':first') ); // fixes adding rows to an empty table - see issue #179 @@ -978,14 +1016,7 @@ }; // add each cell for (j = 0; j < l; j++) { - if (typeof c.extractors[j].id === 'undefined') { - t = ts.getElementText(c, $row[i].cells[j], j); - } else { - t = c.extractors[j].format( ts.getElementText(c, $row[i].cells[j], j), table, $row[i].cells[j], j ); - } - v = c.parsers[j].id === 'no-parser' ? '' : - c.parsers[j].format( t, table, $row[i].cells[j], j ); - cells[j] = c.ignoreCase && typeof v === 'string' ? v.toLowerCase() : v; + cells[j] = getParsedText( c, $row[i].cells[j], j ); if ((c.parsers[j].type || '').toLowerCase() === 'numeric') { // update column max value (ignore sign) c.cache[tbdy].colMax[j] = Math.max(Math.abs(cells[j]) || 0, c.cache[tbdy].colMax[j] || 0); @@ -1213,7 +1244,7 @@ // automatically add a colgroup with col elements set to a percentage width ts.fixColumnWidth = function(table) { table = $(table)[0]; - var overallWidth, percent, + var overallWidth, percent, $tbodies, len, index, c = table.config, colgroup = c.$table.children('colgroup'); // remove plugin-added colgroup, in case we need to refresh the widths @@ -1224,10 +1255,12 @@ colgroup = $('<colgroup class="' + ts.css.colgroup + '">'); overallWidth = c.$table.width(); // only add col for visible columns - fixes #371 - c.$tbodies.find('tr:first').children(':visible').each(function() { - percent = parseInt( ( $(this).width() / overallWidth ) * 1000, 10 ) / 10 + '%'; + $tbodies = c.$tbodies.find('tr:first').children(':visible'); //.each(function() + len = $tbodies.length; + for ( index = 0; index < len; index++ ) { + percent = parseInt( ( $tbodies.eq( index ).width() / overallWidth ) * 1000, 10 ) / 10 + '%'; colgroup.append( $('<col>').css('width', percent) ); - }); + } c.$table.prepend(colgroup); } }; @@ -1262,9 +1295,10 @@ // http://www.javascripttoolbox.com/lib/table/examples.php // http://www.javascripttoolbox.com/temp/table_cellindex.html ts.computeColumnIndex = function(trs) { - var matrix = [], - lookup = {}, - i, j, k, l, $cell, cell, cells, rowIndex, cellId, rowSpan, colSpan, firstAvailCol, matrixrow; + var i, j, k, l, $cell, cell, cells, rowIndex, cellId, rowSpan, colSpan, firstAvailCol, + matrix = [], + matrixrow = [], + lookup = {}; for (i = 0; i < trs.length; i++) { cells = trs[i].cells; for (j = 0; j < cells.length; j++) { @@ -1344,7 +1378,7 @@ $(table)[0].config.$tbodies.children().detach(); }; - ts.bindEvents = function(table, $headers, core){ + ts.bindEvents = function(table, $headers, core) { table = $(table)[0]; var t, downTarget = null, c = table.config; @@ -1355,28 +1389,35 @@ $(t).addClass( c.namespace.slice(1) + '_extra_table' ); } } + t = ( c.pointerDown + ' ' + c.pointerUp + ' ' + c.pointerClick + ' sort keyup ' ) + .replace(/\s+/g, ' ') + .split(' ') + .join(c.namespace + ' '); // apply event handling to headers and/or additional headers (stickyheaders, scroller, etc) $headers // http://stackoverflow.com/questions/5312849/jquery-find-self; .find(c.selectorSort).add( $headers.filter(c.selectorSort) ) - .unbind( ('mousedown mouseup click sort keyup '.split(' ').join(c.namespace + ' ')).replace(/\s+/g, ' ') ) - .bind( 'mousedown mouseup click sort keyup '.split(' ').join(c.namespace + ' '), function(e, external) { + .unbind(t) + .bind(t, function(e, external) { var cell, $target = $(e.target), - type = e.type; + // wrap event type in spaces, so the match doesn't trigger on inner words + type = ' ' + e.type + ' '; // only recognize left clicks - if ( ( ( e.which || e.button ) !== 1 && !/sort|keyup|click/.test(type) ) || + if ( ( ( e.which || e.button ) !== 1 && !type.match( ' ' + c.pointerClick + ' | sort | keyup ' ) ) || // allow pressing enter - ( type === 'keyup' && e.which !== 13 ) || + ( type === ' keyup ' && e.which !== 13 ) || // allow triggering a click event (e.which is undefined) & ignore physical clicks - ( type === 'click' && typeof e.which !== 'undefined' ) ) { + ( type.match(' ' + c.pointerClick + ' ') && typeof e.which !== 'undefined' ) ) { return; } // ignore mouseup if mousedown wasn't on the same target - if ( type === 'mouseup' && downTarget !== e.target && external !== true ) { return; } + if ( type.match(' ' + c.pointerUp + ' ') && downTarget !== e.target && external !== true ) { return; } // set timer on mousedown - if ( type === 'mousedown' ) { + if ( type.match(' ' + c.pointerDown + ' ') ) { downTarget = e.target; + // needed or jQuery v1.2.6 throws an error + e.preventDefault(); return; } downTarget = null; @@ -1411,17 +1452,20 @@ // restore headers ts.restoreHeaders = function(table){ - var $cell, - c = $(table)[0].config; + var index, $cell, + c = $(table)[0].config, + $headers = c.$table.find( c.selectorHeaders ), + len = $headers.length; // don't use c.$headers here in case header cells were swapped - c.$table.find(c.selectorHeaders).each(function(i){ - $cell = $(this); + for ( index = 0; index < len; index++ ) { + // c.$table.find(c.selectorHeaders).each(function(i){ + $cell = $headers.eq( index ); // only restore header cells if it is wrapped // because this is also used by the updateAll method - if ($cell.find('.' + ts.css.headerIn).length){ - $cell.html( c.headerContent[i] ); + if ( $cell.find( '.' + ts.css.headerIn ).length ) { + $cell.html( c.headerContent[ index ] ); } - }); + } }; ts.destroy = function(table, removeClasses, callback){ @@ -1581,8 +1625,8 @@ 'E' : '\u00c9\u00c8\u00ca\u00cb\u011a\u0118', // ÉÈÊËĚĘ 'i' : '\u00ed\u00ec\u0130\u00ee\u00ef\u0131', // íìİîïı 'I' : '\u00cd\u00cc\u0130\u00ce\u00cf', // ÍÌİÎÏ - 'o' : '\u00f3\u00f2\u00f4\u00f5\u00f6', // óòôõö - 'O' : '\u00d3\u00d2\u00d4\u00d5\u00d6', // ÓÒÔÕÖ + 'o' : '\u00f3\u00f2\u00f4\u00f5\u00f6\u014d', // óòôõöō + 'O' : '\u00d3\u00d2\u00d4\u00d5\u00d6\u014c', // ÓÒÔÕÖŌ 'ss': '\u00df', // ß (s sharp) 'SS': '\u1e9e', // ẞ (Capital sharp s) 'u' : '\u00fa\u00f9\u00fb\u00fc\u016f', // úùûüů @@ -1922,7 +1966,7 @@ ts.isDigit = function(s) { // replace all unwanted chars and match - return isNaN(s) ? (/^[\-+(]?\d+[)]?$/).test(s.toString().replace(/[,.'"\s]/g, '')) : true; + return isNaN(s) ? (/^[\-+(]?\d+[)]?$/).test(s.toString().replace(/[,.'"\s]/g, '')) : s !== ''; }; }() @@ -2108,7 +2152,7 @@ id: 'zebra', priority: 90, format: function(table, c, wo) { - var $tb, $tv, $tr, row, even, time, k, + var $tv, $tr, row, even, time, k, i, len, child = new RegExp(c.cssChildRow, 'i'), b = c.$tbodies.add( $( c.namespace + '_extra_table' ).children( 'tbody' ) ); if (c.debug) { @@ -2117,17 +2161,17 @@ for (k = 0; k < b.length; k++ ) { // loop through the visible rows row = 0; - $tb = b.eq(k); - $tv = $tb.children('tr:visible').not(c.selectorRemove); - // revered back to using jQuery each - strangely it's the fastest method - /*jshint loopfunc:true */ - $tv.each(function(){ - $tr = $(this); + $tv = b.eq( k ).children( 'tr:visible' ).not( c.selectorRemove ); + len = $tv.length; + for ( i = 0; i < len; i++ ) { + $tr = $tv.eq( i ); // style child rows the same way the parent row was styled - if (!child.test(this.className)) { row++; } - even = (row % 2 === 0); - $tr.removeClass(wo.zebra[even ? 1 : 0]).addClass(wo.zebra[even ? 0 : 1]); - }); + if ( !child.test( $tr[0].className ) ) { row++; } + even = ( row % 2 === 0 ); + $tr + .removeClass( wo.zebra[ even ? 1 : 0 ] ) + .addClass( wo.zebra[ even ? 0 : 1 ] ); + } } }, remove: function(table, c, wo, refreshing){ @@ -2149,7 +2193,7 @@ ;(function ($, window, document) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; +var ts = $.tablesorter || {}; // *** Store data in local storage, with a cookie fallback *** /* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json) if you need it, then include https://github.com/douglascrockford/JSON-js @@ -2238,7 +2282,7 @@ ts.storage = function(table, key, value, options) { /*! Widget: uitheme - updated 3/26/2015 (v2.21.3) */ ;(function ($) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; +var ts = $.tablesorter || {}; ts.themes = { 'bootstrap' : { @@ -2424,7 +2468,7 @@ ts.addWidget({ /*! Widget: columns */ ;(function ($) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; +var ts = $.tablesorter || {}; ts.addWidget({ id: "columns", @@ -2500,16 +2544,16 @@ ts.addWidget({ })(jQuery); -/*! Widget: filter - updated 3/26/2015 (v2.21.3) *//* +/*! Widget: filter - updated 5/17/2015 (v2.22.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ -;(function ($) { +;( function ( $ ) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}, +var ts = $.tablesorter || {}, tscss = ts.css; -$.extend(tscss, { +$.extend( tscss, { filterRow : 'tablesorter-filter-row', filter : 'tablesorter-filter', filterDisabled : 'disabled', @@ -2517,26 +2561,27 @@ $.extend(tscss, { }); ts.addWidget({ - id: "filter", + id: 'filter', priority: 50, options : { filter_childRows : false, // if true, filter includes child row content in the search + filter_childByColumn : false, // ( filter_childRows must be true ) if true = search child rows by column; false = search all child row text grouped filter_columnFilters : true, // if true, a filter will be added to the top of each table column - filter_columnAnyMatch: true, // if true, allows using "#:{query}" in AnyMatch searches (column:query) - filter_cellFilter : '', // css class name added to the filter cell (string or array) - filter_cssFilter : '', // css class name added to the filter row & each input in the row (tablesorter-filter is ALWAYS added) - filter_defaultFilter : {}, // add a default column filter type "~{query}" to make fuzzy searches default; "{q1} AND {q2}" to make all searches use a logical AND. + filter_columnAnyMatch: true, // if true, allows using '#:{query}' in AnyMatch searches ( column:query ) + filter_cellFilter : '', // css class name added to the filter cell ( string or array ) + filter_cssFilter : '', // css class name added to the filter row & each input in the row ( tablesorter-filter is ALWAYS added ) + filter_defaultFilter : {}, // add a default column filter type '~{query}' to make fuzzy searches default; '{q1} AND {q2}' to make all searches use a logical AND. filter_excludeFilter : {}, // filters to exclude, per column - filter_external : '', // jQuery selector string (or jQuery object) of external filters + filter_external : '', // jQuery selector string ( or jQuery object ) of external filters filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin filter_formatter : null, // add custom filter elements to the filter row filter_functions : null, // add custom filter functions using this option filter_hideEmpty : true, // hide filter row when table is empty filter_hideFilters : false, // collapse filter row when mouse leaves the area filter_ignoreCase : true, // if true, make all searches case-insensitive - filter_liveSearch : true, // if true, search column content while the user types (with a delay) - filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available (visible) options within the drop down - filter_placeholder : { search : '', select : '' }, // default placeholder text (overridden by any header "data-placeholder" setting) + filter_liveSearch : true, // if true, search column content while the user types ( with a delay ) + filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available ( visible ) options within the drop down + filter_placeholder : { search : '', select : '' }, // default placeholder text ( overridden by any header 'data-placeholder' setting ) filter_reset : null, // jQuery selector string of an element used to reset the filters filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters filter_searchDelay : 300, // typing delay in milliseconds before starting a search @@ -2548,37 +2593,38 @@ ts.addWidget({ filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value filter_selectSourceSeparator : '|' // filter_selectSource array text left of the separator is added to the option value, right into the option text }, - format: function(table, c, wo) { - if (!c.$table.hasClass('hasFilters')) { - ts.filter.init(table, c, wo); + format: function( table, c, wo ) { + if ( !c.$table.hasClass( 'hasFilters' ) ) { + ts.filter.init( table, c, wo ); } }, - remove: function(table, c, wo, refreshing) { + remove: function( table, c, wo, refreshing ) { var tbodyIndex, $tbody, $table = c.$table, $tbodies = c.$tbodies, - events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter '); + events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' + .split( ' ' ).join( c.namespace + 'filter ' ); $table - .removeClass('hasFilters') + .removeClass( 'hasFilters' ) // add .tsfilter namespace to all BUT search - .unbind( events.replace(/\s+/g, ' ') ) + .unbind( events.replace( /\s+/g, ' ' ) ) // remove the filter row even if refreshing, because the column might have been moved - .find('.' + tscss.filterRow).remove(); - if (refreshing) { return; } - for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { - $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody - $tbody.children().removeClass(wo.filter_filteredRow).show(); - ts.processTbody(table, $tbody, false); // restore tbody + .find( '.' + tscss.filterRow ).remove(); + if ( refreshing ) { return; } + for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody( table, $tbodies.eq( tbodyIndex ), true ); // remove tbody + $tbody.children().removeClass( wo.filter_filteredRow ).show(); + ts.processTbody( table, $tbody, false ); // restore tbody } - if (wo.filter_reset) { - $(document).undelegate(wo.filter_reset, 'click.tsfilter'); + if ( wo.filter_reset ) { + $( document ).undelegate( wo.filter_reset, 'click.tsfilter' ); } } }); ts.filter = { - // regex used in filter "check" functions - not for general use and not documented + // regex used in filter 'check' functions - not for general use and not documented regex: { regex : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // regex to test for regex child : /tablesorter-childRow/, // child row class name; this gets updated in the script @@ -2591,22 +2637,33 @@ ts.filter = { }, // function( c, data ) { } // c = table.config - // data.filter = array of filter input values; - // data.iFilter = same array, except lowercase (if wo.filter_ignoreCase is true) - // data.exact = table cell text (or parsed data if column parser enabled) - // data.iExact = same as data.exact, except lowercase (if wo.filter_ignoreCase is true) - // data.cache = table cell text from cache, so it has been parsed (& in all lower case if config.ignoreCase is true) - // data.index = column index; table = table element (DOM) - // data.parsed = array (by column) of boolean values (from filter_useParsedData or "filter-parsed" class) + // data.$row = jQuery object of the row currently being processed + // data.$cells = jQuery object of all cells within the current row + // data.filters = array of filters for all columns ( some may be undefined ) + // data.filter = filter for the current column + // data.iFilter = same as data.filter, except lowercase ( if wo.filter_ignoreCase is true ) + // data.exact = table cell text ( or parsed data if column parser enabled ) + // data.iExact = same as data.exact, except lowercase ( if wo.filter_ignoreCase is true ) + // data.cache = table cell text from cache, so it has been parsed ( & in all lower case if c.ignoreCase is true ) + // data.cacheArray = An array of parsed content from each table cell in the row being processed + // data.index = column index; table = table element ( DOM ) + // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class ) types: { // Look for regex regex: function( c, data ) { - if ( ts.filter.regex.regex.test(data.iFilter) ) { + if ( ts.filter.regex.regex.test( data.filter ) ) { var matches, - regex = ts.filter.regex.regex.exec(data.iFilter); + // cache regex per column for optimal speed + regex = data.filter_regexCache[ data.index ] || ts.filter.regex.regex.exec( data.filter ), + isRegex = regex instanceof RegExp; try { - matches = new RegExp(regex[1], regex[2]).test( data.iExact ); - } catch (error) { + if ( !isRegex ) { + // force case insensitive search if ignoreCase option set? + // if ( c.ignoreCase && !regex[2] ) { regex[2] = 'i'; } + data.filter_regexCache[ data.index ] = regex = new RegExp( regex[1], regex[2] ); + } + matches = regex.test( data.exact ); + } catch ( error ) { matches = false; } return matches; @@ -2615,46 +2672,56 @@ ts.filter = { }, // Look for operators >, >=, < or <= operators: function( c, data ) { - if ( /^[<>]=?/.test(data.iFilter) ) { - var cachedValue, result, + // ignore empty strings... because '' < 10 is true + if ( /^[<>]=?/.test( data.iFilter ) && data.iExact !== '' ) { + var cachedValue, result, txt, table = c.table, index = data.index, parsed = data.parsed[index], - query = ts.formatFloat( data.iFilter.replace(ts.filter.regex.operators, ''), table ), + query = ts.formatFloat( data.iFilter.replace( ts.filter.regex.operators, '' ), table ), parser = c.parsers[index], savedSearch = query; - // parse filter value in case we're comparing numbers (dates) - if (parsed || parser.type === 'numeric') { - result = ts.filter.parseFilter(c, $.trim('' + data.iFilter.replace(ts.filter.regex.operators, '')), index, parsed, true); - query = ( typeof result === "number" && result !== '' && !isNaN(result) ) ? result : query; + // parse filter value in case we're comparing numbers ( dates ) + if ( parsed || parser.type === 'numeric' ) { + txt = $.trim( '' + data.iFilter.replace( ts.filter.regex.operators, '' ) ); + result = ts.filter.parseFilter( c, txt, index, true ); + query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query; } - // iExact may be numeric - see issue #149; - // check if cached is defined, because sometimes j goes out of range? (numeric columns) - cachedValue = ( parsed || parser.type === 'numeric' ) && !isNaN(query) && typeof data.cache !== 'undefined' ? data.cache : - isNaN(data.iExact) ? ts.formatFloat( data.iExact.replace(ts.filter.regex.nondigit, ''), table) : - ts.formatFloat( data.iExact, table ); - - if ( />/.test(data.iFilter) ) { result = />=/.test(data.iFilter) ? cachedValue >= query : cachedValue > query; } - if ( /</.test(data.iFilter) ) { result = /<=/.test(data.iFilter) ? cachedValue <= query : cachedValue < query; } + // check if cached is defined, because sometimes j goes out of range? ( numeric columns ) + if ( ( parsed || parser.type === 'numeric' ) && !isNaN( query ) && + typeof data.cache !== 'undefined' ) { + cachedValue = data.cache; + } else { + txt = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; + cachedValue = ts.formatFloat( txt, table ); + } + if ( />/.test( data.iFilter ) ) { + result = />=/.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; + } else if ( /</.test( data.iFilter ) ) { + result = /<=/.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; + } // keep showing all rows if nothing follows the operator - if ( !result && savedSearch === '' ) { result = true; } + if ( !result && savedSearch === '' ) { + result = true; + } return result; } return null; }, // Look for a not match notMatch: function( c, data ) { - if ( /^\!/.test(data.iFilter) ) { + if ( /^\!/.test( data.iFilter ) ) { var indx, - filter = ts.filter.parseFilter(c, data.iFilter.replace('!', ''), data.index, data.parsed[data.index]) || ''; - if (ts.filter.regex.exact.test(filter)) { + txt = data.iFilter.replace( '!', '' ), + filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + if ( ts.filter.regex.exact.test( filter ) ) { // look for exact not matches - see #628 - filter = filter.replace(ts.filter.regex.exact, ''); - return filter === '' ? true : $.trim(filter) !== data.iExact; + filter = filter.replace( ts.filter.regex.exact, '' ); + return filter === '' ? true : $.trim( filter ) !== data.iExact; } else { - indx = data.iExact.search( $.trim(filter) ); - return filter === '' ? true : !(c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0); + indx = data.iExact.search( $.trim( filter ) ); + return filter === '' ? true : !( c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0 ); } } return null; @@ -2662,84 +2729,101 @@ ts.filter = { // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric exact: function( c, data ) { /*jshint eqeqeq:false */ - if (ts.filter.regex.exact.test(data.iFilter)) { - var filter = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.exact, ''), data.index, data.parsed[data.index]) || ''; - return data.anyMatch ? $.inArray(filter, data.rowArray) >= 0 : filter == data.iExact; + if ( ts.filter.regex.exact.test( data.iFilter ) ) { + var txt = data.iFilter.replace( ts.filter.regex.exact, '' ), + filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact; } return null; }, - // Look for an AND or && operator (logical and) + // Look for an AND or && operator ( logical and ) and : function( c, data ) { - if ( ts.filter.regex.andTest.test(data.filter) ) { + if ( ts.filter.regex.andTest.test( data.filter ) ) { var index = data.index, parsed = data.parsed[index], query = data.iFilter.split( ts.filter.regex.andSplit ), - result = data.iExact.search( $.trim( ts.filter.parseFilter(c, query[0], index, parsed) ) ) >= 0, + result = data.iExact.search( $.trim( ts.filter.parseFilter( c, query[0], index, parsed ) ) ) >= 0, indx = query.length - 1; - while (result && indx) { - result = result && data.iExact.search( $.trim( ts.filter.parseFilter(c, query[indx], index, parsed) ) ) >= 0; + while ( result && indx ) { + result = result && + data.iExact.search( $.trim( ts.filter.parseFilter( c, query[indx], index, parsed ) ) ) >= 0; indx--; } return result; } return null; }, - // Look for a range (using " to " or " - ") - see issue #166; thanks matzhu! + // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu! range : function( c, data ) { - if ( ts.filter.regex.toTest.test(data.iFilter) ) { - var result, tmp, + if ( ts.filter.regex.toTest.test( data.iFilter ) ) { + var result, tmp, range1, range2, table = c.table, index = data.index, parsed = data.parsed[index], // make sure the dash is for a range and not indicating a negative number - query = data.iFilter.split( ts.filter.regex.toSplit ), - range1 = ts.formatFloat( ts.filter.parseFilter(c, query[0].replace(ts.filter.regex.nondigit, '') || '', index, parsed), table ), - range2 = ts.formatFloat( ts.filter.parseFilter(c, query[1].replace(ts.filter.regex.nondigit, '') || '', index, parsed), table ); - // parse filter value in case we're comparing numbers (dates) - if (parsed || c.parsers[index].type === 'numeric') { - result = c.parsers[index].format('' + query[0], table, c.$headers.eq(index), index); - range1 = (result !== '' && !isNaN(result)) ? result : range1; - result = c.parsers[index].format('' + query[1], table, c.$headers.eq(index), index); - range2 = (result !== '' && !isNaN(result)) ? result : range2; - } - result = ( parsed || c.parsers[index].type === 'numeric' ) && !isNaN(range1) && !isNaN(range2) ? data.cache : - isNaN(data.iExact) ? ts.formatFloat( data.iExact.replace(ts.filter.regex.nondigit, ''), table) : - ts.formatFloat( data.iExact, table ); - if (range1 > range2) { tmp = range1; range1 = range2; range2 = tmp; } // swap - return (result >= range1 && result <= range2) || (range1 === '' || range2 === ''); + query = data.iFilter.split( ts.filter.regex.toSplit ); + + tmp = query[0].replace( ts.filter.regex.nondigit, '' ) || ''; + range1 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); + tmp = query[1].replace( ts.filter.regex.nondigit, '' ) || ''; + range2 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); + // parse filter value in case we're comparing numbers ( dates ) + if ( parsed || c.parsers[index].type === 'numeric' ) { + result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index ); + range1 = ( result !== '' && !isNaN( result ) ) ? result : range1; + result = c.parsers[ index ].format( '' + query[1], table, c.$headers.eq( index ), index ); + range2 = ( result !== '' && !isNaN( result ) ) ? result : range2; + } + if ( ( parsed || c.parsers[ index ].type === 'numeric' ) && !isNaN( range1 ) && !isNaN( range2 ) ) { + result = data.cache; + } else { + tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; + result = ts.formatFloat( tmp, table ); + } + if ( range1 > range2 ) { + tmp = range1; range1 = range2; range2 = tmp; // swap + } + return ( result >= range1 && result <= range2 ) || ( range1 === '' || range2 === '' ); } return null; }, // Look for wild card: ? = single, * = multiple, or | = logical OR wild : function( c, data ) { - if ( /[\?\*\|]/.test(data.iFilter) || ts.filter.regex.orReplace.test(data.filter) ) { + if ( /[\?\*\|]/.test( data.iFilter ) || ts.filter.regex.orReplace.test( data.filter ) ) { var index = data.index, - parsed = data.parsed[index], - query = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.orReplace, "|"), index, parsed) || ''; - // look for an exact match with the "or" unless the "filter-match" class is found - if (!c.$headerIndexed[index].hasClass('filter-match') && /\|/.test(query)) { + parsed = data.parsed[ index ], + txt = data.iFilter.replace( ts.filter.regex.orReplace, '|' ), + query = ts.filter.parseFilter( c, txt, index, parsed ) || ''; + // look for an exact match with the 'or' unless the 'filter-match' class is found + if ( !c.$headerIndexed[ index ].hasClass( 'filter-match' ) && /\|/.test( query ) ) { // show all results while using filter match. Fixes #727 - if (query[ query.length - 1 ] === '|') { query += '*'; } - query = data.anyMatch && $.isArray(data.rowArray) ? '(' + query + ')' : '^(' + query + ')$'; + if ( query[ query.length - 1 ] === '|' ) { + query += '*'; + } + query = data.anyMatch && $.isArray( data.rowArray ) ? + '(' + query + ')' : + '^(' + query + ')$'; } // parsing the filter may not work properly when using wildcards =/ - return new RegExp( query.replace(/\?/g, '\\S{1}').replace(/\*/g, '\\S*') ).test(data.iExact); + return new RegExp( query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ) ) + .test( data.iExact ); } return null; }, - // fuzzy text search; modified from https://github.com/mattyork/fuzzy (MIT license) + // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license ) fuzzy: function( c, data ) { - if ( /^~/.test(data.iFilter) ) { + if ( /^~/.test( data.iFilter ) ) { var indx, patternIndx = 0, len = data.iExact.length, - pattern = ts.filter.parseFilter(c, data.iFilter.slice(1), data.index, data.parsed[data.index]) || ''; - for (indx = 0; indx < len; indx++) { - if (data.iExact[indx] === pattern[patternIndx]) { + txt = data.iFilter.slice( 1 ), + pattern = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + for ( indx = 0; indx < len; indx++ ) { + if ( data.iExact[ indx ] === pattern[ patternIndx ] ) { patternIndx += 1; } } - if (patternIndx === pattern.length) { + if ( patternIndx === pattern.length ) { return true; } return false; @@ -2747,17 +2831,17 @@ ts.filter = { return null; } }, - init: function(table, c, wo) { + init: function( table, c, wo ) { // filter language options - ts.language = $.extend(true, {}, { + ts.language = $.extend( true, {}, { to : 'to', or : 'or', and : 'and' - }, ts.language); + }, ts.language ); var options, string, txt, $header, column, filters, val, fxn, noSelect, regex = ts.filter.regex; - c.$table.addClass('hasFilters'); + c.$table.addClass( 'hasFilters' ); // define timers so using clearTimeout won't cause an undefined error wo.searchTimer = null; @@ -2767,512 +2851,566 @@ ts.filter = { wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; - txt = '\\{' + ts.filter.regex.query + '\\}'; + val = '\\{' + ts.filter.regex.query + '\\}'; $.extend( regex, { - child : new RegExp(c.cssChildRow), - filtered : new RegExp(wo.filter_filteredRow), - alreadyFiltered : new RegExp('(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i'), - toTest : new RegExp('\\s+(-|' + ts.language.to + ')\\s+', 'i'), - toSplit : new RegExp('(?:\\s+(?:-|' + ts.language.to + ')\\s+)' ,'gi'), - andTest : new RegExp('\\s+(' + ts.language.and + '|&&)\\s+', 'i'), - andSplit : new RegExp('(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi'), - orReplace : new RegExp('\\s+(' + ts.language.or + ')\\s+', 'gi'), - iQuery : new RegExp(txt, 'i'), - igQuery : new RegExp(txt, 'ig') + child : new RegExp( c.cssChildRow ), + filtered : new RegExp( wo.filter_filteredRow ), + alreadyFiltered : new RegExp( '(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i' ), + toTest : new RegExp( '\\s+(-|' + ts.language.to + ')\\s+', 'i' ), + toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)' ,'gi' ), + andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), + andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), + orReplace : new RegExp( '\\s+(' + ts.language.or + ')\\s+', 'gi' ), + iQuery : new RegExp( val, 'i' ), + igQuery : new RegExp( val, 'ig' ) }); - // don't build filter row if columnFilters is false or all columns are set to "filter-false" - issue #156 - if (wo.filter_columnFilters !== false && c.$headers.filter('.filter-false, .parser-false').length !== c.$headers.length) { + // don't build filter row if columnFilters is false or all columns are set to 'filter-false' + // see issue #156 + val = c.$headers.filter( '.filter-false, .parser-false' ).length; + if ( wo.filter_columnFilters !== false && val !== c.$headers.length ) { // build filter row - ts.filter.buildRow(table, c, wo); + ts.filter.buildRow( table, c, wo ); } - txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter '); - c.$table.bind( txt, function(event, filter) { - val = (wo.filter_hideEmpty && $.isEmptyObject(c.cache) && !(c.delayInit && event.type === 'appendCache')); - // hide filter row using the "filtered" class name - c.$table.find('.' + tscss.filterRow).toggleClass(wo.filter_filteredRow, val ); // fixes #450 - if ( !/(search|filter)/.test(event.type) ) { + txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' + .split( ' ' ).join( c.namespace + 'filter ' ); + c.$table.bind( txt, function( event, filter ) { + val = wo.filter_hideEmpty && + $.isEmptyObject( c.cache ) && + !( c.delayInit && event.type === 'appendCache' ); + // hide filter row using the 'filtered' class name + c.$table.find( '.' + tscss.filterRow ).toggleClass( wo.filter_filteredRow, val ); // fixes #450 + if ( !/(search|filter)/.test( event.type ) ) { event.stopPropagation(); - ts.filter.buildDefault(table, true); + ts.filter.buildDefault( table, true ); } - if (event.type === 'filterReset') { - c.$table.find('.' + tscss.filter).add(wo.filter_$externalFilters).val(''); - ts.filter.searching(table, []); - } else if (event.type === 'filterEnd') { - ts.filter.buildDefault(table, true); + if ( event.type === 'filterReset' ) { + c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' ); + ts.filter.searching( table, [] ); + } else if ( event.type === 'filterEnd' ) { + ts.filter.buildDefault( table, true ); } else { - // send false argument to force a new search; otherwise if the filter hasn't changed, it will return - filter = event.type === 'search' ? filter : event.type === 'updateComplete' ? c.$table.data('lastSearch') : ''; - if (/(update|add)/.test(event.type) && event.type !== "updateComplete") { + // send false argument to force a new search; otherwise if the filter hasn't changed, + // it will return + filter = event.type === 'search' ? filter : + event.type === 'updateComplete' ? c.$table.data( 'lastSearch' ) : ''; + if ( /(update|add)/.test( event.type ) && event.type !== 'updateComplete' ) { // force a new search since content has changed c.lastCombinedFilter = null; c.lastSearch = []; } - // pass true (skipFirst) to prevent the tablesorter.setFilters function from skipping the first input - // ensures all inputs are updated when a search is triggered on the table $('table').trigger('search', [...]); - ts.filter.searching(table, filter, true); + // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first + // input ensures all inputs are updated when a search is triggered on the table + // $( 'table' ).trigger( 'search', [...] ); + ts.filter.searching( table, filter, true ); } return false; }); // reset button/link - if (wo.filter_reset) { - if (wo.filter_reset instanceof $) { + if ( wo.filter_reset ) { + if ( wo.filter_reset instanceof $ ) { // reset contains a jQuery object, bind to it - wo.filter_reset.click(function(){ - c.$table.trigger('filterReset'); + wo.filter_reset.click( function() { + c.$table.trigger( 'filterReset' ); }); - } else if ($(wo.filter_reset).length) { + } else if ( $( wo.filter_reset ).length ) { // reset is a jQuery selector, use event delegation - $(document) - .undelegate(wo.filter_reset, 'click.tsfilter') - .delegate(wo.filter_reset, 'click.tsfilter', function() { - // trigger a reset event, so other functions (filter_formatter) know when to reset - c.$table.trigger('filterReset'); - }); + $( document ) + .undelegate( wo.filter_reset, 'click.tsfilter' ) + .delegate( wo.filter_reset, 'click.tsfilter', function() { + // trigger a reset event, so other functions ( filter_formatter ) know when to reset + c.$table.trigger( 'filterReset' ); + }); } } - if (wo.filter_functions) { - for (column = 0; column < c.columns; column++) { + if ( wo.filter_functions ) { + for ( column = 0; column < c.columns; column++ ) { fxn = ts.getColumnData( table, wo.filter_functions, column ); - if (fxn) { - // remove "filter-select" from header otherwise the options added here are replaced with all options - $header = c.$headerIndexed[column].removeClass('filter-select'); - // don't build select if "filter-false" or "parser-false" set - noSelect = !($header.hasClass('filter-false') || $header.hasClass('parser-false')); + if ( fxn ) { + // remove 'filter-select' from header otherwise the options added here are replaced with + // all options + $header = c.$headerIndexed[ column ].removeClass( 'filter-select' ); + // don't build select if 'filter-false' or 'parser-false' set + noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); options = ''; if ( fxn === true && noSelect ) { - ts.filter.buildSelect(table, column); + ts.filter.buildSelect( table, column ); } else if ( typeof fxn === 'object' && noSelect ) { // add custom drop down list - for (string in fxn) { - if (typeof string === 'string') { + for ( string in fxn ) { + if ( typeof string === 'string' ) { options += options === '' ? - '<option value="">' + ($header.data('placeholder') || $header.attr('data-placeholder') || wo.filter_placeholder.select || '') + '</option>' : ''; + '<option value="">' + + ( $header.data( 'placeholder' ) || + $header.attr( 'data-placeholder' ) || + wo.filter_placeholder.select || + '' + ) + + '</option>' : ''; val = string; txt = string; - if (string.indexOf(wo.filter_selectSourceSeparator) >= 0) { - val = string.split(wo.filter_selectSourceSeparator); + if ( string.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { + val = string.split( wo.filter_selectSourceSeparator ); txt = val[1]; val = val[0]; } - options += '<option ' + (txt === val ? '' : 'data-function-name="' + string + '" ') + 'value="' + val + '">' + txt + '</option>'; + options += '<option ' + + ( txt === val ? '' : 'data-function-name="' + string + '" ' ) + + 'value="' + val + '">' + txt + '</option>'; } } - c.$table.find('thead').find('select.' + tscss.filter + '[data-column="' + column + '"]').append(options); + c.$table + .find( 'thead' ) + .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) + .append( options ); txt = wo.filter_selectSource; - fxn = $.isFunction(txt) ? true : ts.getColumnData( table, txt, column ); - if (fxn) { + fxn = $.isFunction( txt ) ? true : ts.getColumnData( table, txt, column ); + if ( fxn ) { // updating so the extra options are appended - ts.filter.buildSelect(c.table, column, '', true, $header.hasClass(wo.filter_onlyAvail)); + ts.filter.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) ); } } } } } - // not really updating, but if the column has both the "filter-select" class & filter_functions set to true, - // it would append the same options twice. - ts.filter.buildDefault(table, true); + // not really updating, but if the column has both the 'filter-select' class & + // filter_functions set to true, it would append the same options twice. + ts.filter.buildDefault( table, true ); - ts.filter.bindSearch( table, c.$table.find('.' + tscss.filter), true ); - if (wo.filter_external) { + ts.filter.bindSearch( table, c.$table.find( '.' + tscss.filter ), true ); + if ( wo.filter_external ) { ts.filter.bindSearch( table, wo.filter_external ); } - if (wo.filter_hideFilters) { - ts.filter.hideFilters(table, c); + if ( wo.filter_hideFilters ) { + ts.filter.hideFilters( table, c ); } // show processing icon - if (c.showProcessing) { + if ( c.showProcessing ) { + txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter ' ); c.$table - .unbind( ('filterStart filterEnd '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') ) - .bind( 'filterStart filterEnd '.split(' ').join(c.namespace + 'filter '), function(event, columns) { + .unbind( txt.replace( /\s+/g, ' ' ) ) + .bind( txt, function( event, columns ) { // only add processing to certain columns to all columns - $header = (columns) ? c.$table.find('.' + tscss.header).filter('[data-column]').filter(function() { - return columns[$(this).data('column')] !== ''; - }) : ''; - ts.isProcessing(table, event.type === 'filterStart', columns ? $header : ''); + $header = ( columns ) ? + c.$table + .find( '.' + tscss.header ) + .filter( '[data-column]' ) + .filter( function() { + return columns[ $( this ).data( 'column' ) ] !== ''; + }) : ''; + ts.isProcessing( table, event.type === 'filterStart', columns ? $header : '' ); }); } - // set filtered rows count (intially unfiltered) + // set filtered rows count ( intially unfiltered ) c.filteredRows = c.totalRows; // add default values + txt = 'tablesorter-initialized pagerBeforeInitialized '.split( ' ' ).join( c.namespace + 'filter ' ); c.$table - .unbind( ('tablesorter-initialized pagerBeforeInitialized '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') ) - .bind( 'tablesorter-initialized pagerBeforeInitialized '.split(' ').join(c.namespace + 'filter '), function() { - // redefine "wo" as it does not update properly inside this callback + .unbind( txt.replace( /\s+/g, ' ' ) ) + .bind( txt, function() { + // redefine 'wo' as it does not update properly inside this callback var wo = this.config.widgetOptions; - filters = ts.filter.setDefaults(table, c, wo) || []; - if (filters.length) { + filters = ts.filter.setDefaults( table, c, wo ) || []; + if ( filters.length ) { // prevent delayInit from triggering a cache build if filters are empty - if ( !(c.delayInit && filters.join('') === '') ) { - ts.setFilters(table, filters, true); + if ( !( c.delayInit && filters.join( '' ) === '' ) ) { + ts.setFilters( table, filters, true ); } } - c.$table.trigger('filterFomatterUpdate'); + c.$table.trigger( 'filterFomatterUpdate' ); // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers - setTimeout(function(){ - if (!wo.filter_initialized) { - ts.filter.filterInitComplete(c); + setTimeout( function() { + if ( !wo.filter_initialized ) { + ts.filter.filterInitComplete( c ); } - }, 100); + }, 100 ); }); // if filter widget is added after pager has initialized; then set filter init flag - if (c.pager && c.pager.initialized && !wo.filter_initialized) { - c.$table.trigger('filterFomatterUpdate'); - setTimeout(function(){ - ts.filter.filterInitComplete(c); - }, 100); + if ( c.pager && c.pager.initialized && !wo.filter_initialized ) { + c.$table.trigger( 'filterFomatterUpdate' ); + setTimeout( function() { + ts.filter.filterInitComplete( c ); + }, 100 ); } }, - // $cell parameter, but not the config, is passed to the - // filter_formatters, so we have to work with it instead - formatterUpdated: function($cell, column) { - var wo = $cell.closest('table')[0].config.widgetOptions; - if (!wo.filter_initialized) { + // $cell parameter, but not the config, is passed to the filter_formatters, + // so we have to work with it instead + formatterUpdated: function( $cell, column ) { + var wo = $cell.closest( 'table' )[0].config.widgetOptions; + if ( !wo.filter_initialized ) { // add updates by column since this function // may be called numerous times before initialization - wo.filter_formatterInit[column] = 1; + wo.filter_formatterInit[ column ] = 1; } }, - filterInitComplete: function(c){ + filterInitComplete: function( c ) { var indx, len, wo = c.widgetOptions, count = 0, - completed = function(){ + completed = function() { wo.filter_initialized = true; - c.$table.trigger('filterInit', c); - ts.filter.findRows(c.table, c.$table.data('lastSearch') || []); + c.$table.trigger( 'filterInit', c ); + ts.filter.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); }; if ( $.isEmptyObject( wo.filter_formatter ) ) { completed(); } else { len = wo.filter_formatterInit.length; - for (indx = 0; indx < len; indx++) { - if (wo.filter_formatterInit[indx] === 1) { + for ( indx = 0; indx < len; indx++ ) { + if ( wo.filter_formatterInit[ indx ] === 1 ) { count++; } } - clearTimeout(wo.filter_initTimer); - if (!wo.filter_initialized && count === wo.filter_formatterCount) { + clearTimeout( wo.filter_initTimer ); + if ( !wo.filter_initialized && count === wo.filter_formatterCount ) { // filter widget initialized completed(); - } else if (!wo.filter_initialized) { + } else if ( !wo.filter_initialized ) { // fall back in case a filter_formatter doesn't call - // $.tablesorter.filter.formatterUpdated($cell, column), and the count is off - wo.filter_initTimer = setTimeout(function(){ + // $.tablesorter.filter.formatterUpdated( $cell, column ), and the count is off + wo.filter_initTimer = setTimeout( function() { completed(); - }, 500); + }, 500 ); } } }, - setDefaults: function(table, c, wo) { + setDefaults: function( table, c, wo ) { var isArray, saved, indx, col, $filters, - // get current (default) filters - filters = ts.getFilters(table) || []; - if (wo.filter_saveFilters && ts.storage) { + // get current ( default ) filters + filters = ts.getFilters( table ) || []; + if ( wo.filter_saveFilters && ts.storage ) { saved = ts.storage( table, 'tablesorter-filters' ) || []; - isArray = $.isArray(saved); + isArray = $.isArray( saved ); // make sure we're not just getting an empty array - if ( !(isArray && saved.join('') === '' || !isArray) ) { filters = saved; } + if ( !( isArray && saved.join( '' ) === '' || !isArray ) ) { + filters = saved; + } } // if no filters saved, then check default settings - if (filters.join('') === '') { + if ( filters.join( '' ) === '' ) { // allow adding default setting to external filters - $filters = c.$headers.add( wo.filter_$externalFilters ).filter('[' + wo.filter_defaultAttrib + ']'); - for (indx = 0; indx <= c.columns; indx++) { - // include data-column="all" external filters + $filters = c.$headers.add( wo.filter_$externalFilters ) + .filter( '[' + wo.filter_defaultAttrib + ']' ); + for ( indx = 0; indx <= c.columns; indx++ ) { + // include data-column='all' external filters col = indx === c.columns ? 'all' : indx; - filters[indx] = $filters.filter('[data-column="' + col + '"]').attr(wo.filter_defaultAttrib) || filters[indx] || ''; + filters[indx] = $filters + .filter( '[data-column="' + col + '"]' ) + .attr( wo.filter_defaultAttrib ) || filters[indx] || ''; } } - c.$table.data('lastSearch', filters); + c.$table.data( 'lastSearch', filters ); return filters; }, - parseFilter: function(c, filter, column, parsed, forceParse){ - return forceParse || parsed ? - c.parsers[column].format( filter, c.table, [], column ) : - filter; + parseFilter: function( c, filter, column, parsed ) { + return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter; }, - buildRow: function(table, c, wo) { - var col, column, $header, buildSelect, disabled, name, ffxn, + buildRow: function( table, c, wo ) { + var col, column, $header, buildSelect, disabled, name, ffxn, tmp, // c.columns defined in computeThIndexes() + cellFilter = wo.filter_cellFilter, columns = c.columns, - arry = $.isArray(wo.filter_cellFilter), + arry = $.isArray( cellFilter ), buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; - for (column = 0; column < columns; column++) { - if (arry) { - buildFilter += '<td' + ( wo.filter_cellFilter[column] ? ' class="' + wo.filter_cellFilter[column] + '"' : '' ) + '></td>'; + for ( column = 0; column < columns; column++ ) { + buildFilter += '<td'; + if ( arry ) { + buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' ); } else { - buildFilter += '<td' + ( wo.filter_cellFilter !== '' ? ' class="' + wo.filter_cellFilter + '"' : '' ) + '></td>'; + buildFilter += ( cellFilter !== '' ? ' class="' + cellFilter + '"' : '' ); } + buildFilter += '></td>'; } - c.$filters = $(buildFilter += '</tr>').appendTo( c.$table.children('thead').eq(0) ).find('td'); + c.$filters = $( buildFilter += '</tr>' ) + .appendTo( c.$table.children( 'thead' ).eq( 0 ) ) + .find( 'td' ); // build each filter input - for (column = 0; column < columns; column++) { + for ( column = 0; column < columns; column++ ) { disabled = false; // assuming last cell of a column is the main column - $header = c.$headerIndexed[column]; + $header = c.$headerIndexed[ column ]; ffxn = ts.getColumnData( table, wo.filter_functions, column ); - buildSelect = (wo.filter_functions && ffxn && typeof ffxn !== "function" ) || - $header.hasClass('filter-select'); + buildSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || + $header.hasClass( 'filter-select' ); // get data from jQuery data, metadata, headers option or header class name col = ts.getColumnData( table, c.headers, column ); - disabled = ts.getData($header[0], col, 'filter') === 'false' || ts.getData($header[0], col, 'parser') === 'false'; + disabled = ts.getData( $header[0], col, 'filter' ) === 'false' || + ts.getData( $header[0], col, 'parser' ) === 'false'; - if (buildSelect) { - buildFilter = $('<select>').appendTo( c.$filters.eq(column) ); + if ( buildSelect ) { + buildFilter = $( '<select>' ).appendTo( c.$filters.eq( column ) ); } else { ffxn = ts.getColumnData( table, wo.filter_formatter, column ); - if (ffxn) { + if ( ffxn ) { wo.filter_formatterCount++; - buildFilter = ffxn( c.$filters.eq(column), column ); + buildFilter = ffxn( c.$filters.eq( column ), column ); // no element returned, so lets go find it - if (buildFilter && buildFilter.length === 0) { - buildFilter = c.$filters.eq(column).children('input'); + if ( buildFilter && buildFilter.length === 0 ) { + buildFilter = c.$filters.eq( column ).children( 'input' ); } // element not in DOM, so lets attach it - if ( buildFilter && (buildFilter.parent().length === 0 || - (buildFilter.parent().length && buildFilter.parent()[0] !== c.$filters[column])) ) { - c.$filters.eq(column).append(buildFilter); + if ( buildFilter && ( buildFilter.parent().length === 0 || + ( buildFilter.parent().length && buildFilter.parent()[0] !== c.$filters[column] ) ) ) { + c.$filters.eq( column ).append( buildFilter ); } } else { - buildFilter = $('<input type="search">').appendTo( c.$filters.eq(column) ); + buildFilter = $( '<input type="search">' ).appendTo( c.$filters.eq( column ) ); } - if (buildFilter) { - buildFilter.attr('placeholder', $header.data('placeholder') || $header.attr('data-placeholder') || wo.filter_placeholder.search || ''); + if ( buildFilter ) { + tmp = $header.data( 'placeholder' ) || + $header.attr( 'data-placeholder' ) || + wo.filter_placeholder.search || ''; + buildFilter.attr( 'placeholder', tmp ); } } - if (buildFilter) { + if ( buildFilter ) { // add filter class name - name = ( $.isArray(wo.filter_cssFilter) ? - (typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '') : + name = ( $.isArray( wo.filter_cssFilter ) ? + ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : wo.filter_cssFilter ) || ''; - buildFilter.addClass( tscss.filter + ' ' + name ).attr('data-column', column); - if (disabled) { - buildFilter.attr('placeholder', '').addClass(tscss.filterDisabled)[0].disabled = true; // disabled! + buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column ); + if ( disabled ) { + buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; } } } }, - bindSearch: function(table, $el, internal) { - table = $(table)[0]; - $el = $($el); // allow passing a selector string - if (!$el.length) { return; } - var c = table.config, + bindSearch: function( table, $el, internal ) { + table = $( table )[0]; + $el = $( $el ); // allow passing a selector string + if ( !$el.length ) { return; } + var tmp, + c = table.config, wo = c.widgetOptions, + namespace = c.namespace + 'filter', $ext = wo.filter_$externalFilters; - if (internal !== true) { + if ( internal !== true ) { // save anyMatch element - wo.filter_$anyMatch = $el.filter(wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector); - if ($ext && $ext.length) { + tmp = wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector; + wo.filter_$anyMatch = $el.filter( tmp ); + if ( $ext && $ext.length ) { wo.filter_$externalFilters = wo.filter_$externalFilters.add( $el ); } else { wo.filter_$externalFilters = $el; } - // update values (external filters added after table initialization) - ts.setFilters(table, c.$table.data('lastSearch') || [], internal === false); + // update values ( external filters added after table initialization ) + ts.setFilters( table, c.$table.data( 'lastSearch' ) || [], internal === false ); } + // unbind events + tmp = ( 'keypress keyup search change '.split( ' ' ).join( namespace + ' ' ) ); $el - // use data attribute instead of jQuery data since the head is cloned without including the data/binding - .attr('data-lastSearchTime', new Date().getTime()) - .unbind( ('keypress keyup search change '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') ) + // use data attribute instead of jQuery data since the head is cloned without including + // the data/binding + .attr( 'data-lastSearchTime', new Date().getTime() ) + .unbind( tmp.replace( /\s+/g, ' ' ) ) // include change for select - fixes #473 - .bind('keyup' + c.namespace + 'filter', function(event) { - $(this).attr('data-lastSearchTime', new Date().getTime()); + .bind( 'keyup' + namespace, function( event ) { + $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); // emulate what webkit does.... escape clears the filter - if (event.which === 27) { + if ( event.which === 27 ) { this.value = ''; // live search } else if ( wo.filter_liveSearch === false ) { return; - // don't return if the search value is empty (all rows need to be revealed) + // don't return if the search value is empty ( all rows need to be revealed ) } else if ( this.value !== '' && ( // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace ( typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch ) || // let return & backspace continue on, but ignore arrows & non-valid characters - ( event.which !== 13 && event.which !== 8 && ( event.which < 32 || (event.which >= 37 && event.which <= 40) ) ) ) ) { + ( event.which !== 13 && event.which !== 8 && + ( event.which < 32 || ( event.which >= 37 && event.which <= 40 ) ) ) ) ) { return; } // change event = no delay; last true flag tells getFilters to skip newest timed input ts.filter.searching( table, true, true ); }) - .bind( 'search change keypress '.split(' ').join(c.namespace + 'filter '), function(event){ - var column = $(this).data('column'); - // don't allow "change" event to process if the input value is the same - fixes #685 - if (event.which === 13 || event.type === 'search' || event.type === 'change' && this.value !== c.lastSearch[column]) { + .bind( 'search change keypress '.split( ' ' ).join( namespace + ' ' ), function( event ) { + var column = $( this ).data( 'column' ); + // don't allow 'change' event to process if the input value is the same - fixes #685 + if ( event.which === 13 || event.type === 'search' || + event.type === 'change' && this.value !== c.lastSearch[column] ) { event.preventDefault(); // init search with no delay - $(this).attr('data-lastSearchTime', new Date().getTime()); + $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); ts.filter.searching( table, false, true ); } }); }, - searching: function(table, filter, skipFirst) { + searching: function( table, filter, skipFirst ) { var wo = table.config.widgetOptions; - clearTimeout(wo.searchTimer); - if (typeof filter === 'undefined' || filter === true) { + clearTimeout( wo.searchTimer ); + if ( typeof filter === 'undefined' || filter === true ) { // delay filtering - wo.searchTimer = setTimeout(function() { - ts.filter.checkFilters(table, filter, skipFirst ); - }, wo.filter_liveSearch ? wo.filter_searchDelay : 10); + wo.searchTimer = setTimeout( function() { + ts.filter.checkFilters( table, filter, skipFirst ); + }, wo.filter_liveSearch ? wo.filter_searchDelay : 10 ); } else { // skip delay - ts.filter.checkFilters(table, filter, skipFirst); + ts.filter.checkFilters( table, filter, skipFirst ); } }, - checkFilters: function(table, filter, skipFirst) { + checkFilters: function( table, filter, skipFirst ) { var c = table.config, wo = c.widgetOptions, - filterArray = $.isArray(filter), - filters = (filterArray) ? filter : ts.getFilters(table, true), - combinedFilters = (filters || []).join(''); // combined filter values + filterArray = $.isArray( filter ), + filters = ( filterArray ) ? filter : ts.getFilters( table, true ), + combinedFilters = ( filters || [] ).join( '' ); // combined filter values // prevent errors if delay init is set - if ($.isEmptyObject(c.cache)) { - // update cache if delayInit set & pager has initialized (after user initiates a search) - if (c.delayInit && c.pager && c.pager.initialized) { - c.$table.trigger('updateCache', [function(){ - ts.filter.checkFilters(table, false, skipFirst); - }] ); + if ( $.isEmptyObject( c.cache ) ) { + // update cache if delayInit set & pager has initialized ( after user initiates a search ) + if ( c.delayInit && c.pager && c.pager.initialized ) { + c.$table.trigger( 'updateCache', [ function() { + ts.filter.checkFilters( table, false, skipFirst ); + } ] ); } return; } // add filter array back into inputs - if (filterArray) { + if ( filterArray ) { ts.setFilters( table, filters, false, skipFirst !== true ); - if (!wo.filter_initialized) { c.lastCombinedFilter = ''; } + if ( !wo.filter_initialized ) { c.lastCombinedFilter = ''; } } - if (wo.filter_hideFilters) { + if ( wo.filter_hideFilters ) { // show/hide filter row as needed - c.$table.find('.' + tscss.filterRow).trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); + c.$table + .find( '.' + tscss.filterRow ) + .trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons - if (c.lastCombinedFilter === combinedFilters && filter !== false) { + if ( c.lastCombinedFilter === combinedFilters && filter !== false ) { return; - } else if (filter === false) { + } else if ( filter === false ) { // force filter refresh c.lastCombinedFilter = null; c.lastSearch = []; } - if (wo.filter_initialized) { c.$table.trigger('filterStart', [filters]); } - if (c.showProcessing) { + if ( wo.filter_initialized ) { + c.$table.trigger( 'filterStart', [filters] ); + } + if ( c.showProcessing ) { // give it time for the processing icon to kick in - setTimeout(function() { - ts.filter.findRows(table, filters, combinedFilters); + setTimeout( function() { + ts.filter.findRows( table, filters, combinedFilters ); return false; - }, 30); + }, 30 ); } else { - ts.filter.findRows(table, filters, combinedFilters); + ts.filter.findRows( table, filters, combinedFilters ); return false; } }, - hideFilters: function(table, c) { + hideFilters: function( table, c ) { var $filterRow, $filterRow2, timer; - $(table) - .find('.' + tscss.filterRow) - .addClass(tscss.filterRowHide) - .bind('mouseenter mouseleave', function(e) { + $( table ) + .find( '.' + tscss.filterRow ) + .addClass( tscss.filterRowHide ) + .bind( 'mouseenter mouseleave', function( e ) { // save event object - http://bugs.jquery.com/ticket/12140 var event = e; - $filterRow = $(this); - clearTimeout(timer); - timer = setTimeout(function() { - if ( /enter|over/.test(event.type) ) { - $filterRow.removeClass(tscss.filterRowHide); + $filterRow = $( this ); + clearTimeout( timer ); + timer = setTimeout( function() { + if ( /enter|over/.test( event.type ) ) { + $filterRow.removeClass( tscss.filterRowHide ); } else { // don't hide if input has focus - // $(':focus') needs jQuery 1.6+ - if ( $(document.activeElement).closest('tr')[0] !== $filterRow[0] ) { + // $( ':focus' ) needs jQuery 1.6+ + if ( $( document.activeElement ).closest( 'tr' )[0] !== $filterRow[0] ) { // don't hide row if any filter has a value - if (c.lastCombinedFilter === '') { - $filterRow.addClass(tscss.filterRowHide); + if ( c.lastCombinedFilter === '' ) { + $filterRow.addClass( tscss.filterRowHide ); } } } - }, 200); + }, 200 ); }) - .find('input, select').bind('focus blur', function(e) { - $filterRow2 = $(this).closest('tr'); - clearTimeout(timer); + .find( 'input, select' ).bind( 'focus blur', function( e ) { + $filterRow2 = $( this ).closest( 'tr' ); + clearTimeout( timer ); var event = e; - timer = setTimeout(function() { + timer = setTimeout( function() { // don't hide row if any filter has a value - if (ts.getFilters(c.$table).join('') === '') { - $filterRow2[ event.type === 'focus' ? 'removeClass' : 'addClass'](tscss.filterRowHide); + if ( ts.getFilters( c.$table ).join( '' ) === '' ) { + $filterRow2.toggleClass( tscss.filterRowHide, event.type === 'focus' ); } - }, 200); + }, 200 ); }); }, - defaultFilter: function(filter, mask){ - if (filter === '') { return filter; } + defaultFilter: function( filter, mask ) { + if ( filter === '' ) { return filter; } var regex = ts.filter.regex.iQuery, maskLen = mask.match( ts.filter.regex.igQuery ).length, - query = maskLen > 1 ? $.trim(filter).split(/\s/) : [ $.trim(filter) ], + query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ], len = query.length - 1, indx = 0, val = mask; if ( len < 1 && maskLen > 1 ) { - // only one "word" in query but mask has >1 slots + // only one 'word' in query but mask has >1 slots query[1] = query[0]; } // replace all {query} with query words... - // if query = "Bob", then convert mask from "!{query}" to "!Bob" - // if query = "Bob Joe Frank", then convert mask "{q} OR {q}" to "Bob OR Joe OR Frank" - while (regex.test(val)) { - val = val.replace(regex, query[indx++] || ''); - if (regex.test(val) && indx < len && (query[indx] || '') !== '') { - val = mask.replace(regex, val); + // if query = 'Bob', then convert mask from '!{query}' to '!Bob' + // if query = 'Bob Joe Frank', then convert mask '{q} OR {q}' to 'Bob OR Joe OR Frank' + while ( regex.test( val ) ) { + val = val.replace( regex, query[indx++] || '' ); + if ( regex.test( val ) && indx < len && ( query[indx] || '' ) !== '' ) { + val = mask.replace( regex, val ); } } return val; }, getLatestSearch: function( $input ) { - if ($input) { - return $input.sort(function(a, b) { - return $(b).attr('data-lastSearchTime') - $(a).attr('data-lastSearchTime'); + if ( $input ) { + return $input.sort( function( a, b ) { + return $( b ).attr( 'data-lastSearchTime' ) - $( a ).attr( 'data-lastSearchTime' ); }); } return $(); }, multipleColumns: function( c, $input ) { - // look for multiple columns "1-3,4-6,8" in data-column + // look for multiple columns '1-3,4-6,8' in data-column var temp, ranges, range, start, end, singles, i, indx, len, wo = c.widgetOptions, - // only target "all" column inputs on initialization - // & don't target "all" column inputs if they don't exist - targets = wo.filter_initialized || !$input.filter(wo.filter_anyColumnSelector).length, + // only target 'all' column inputs on initialization + // & don't target 'all' column inputs if they don't exist + targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, columns = [], - val = $.trim( ts.filter.getLatestSearch( $input ).attr('data-column') || '' ); + val = $.trim( ts.filter.getLatestSearch( $input ).attr( 'data-column' ) || '' ); // process column range if ( targets && /-/.test( val ) ) { ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); len = ranges.length; - for (indx = 0; indx < len; indx++) { + for ( indx = 0; indx < len; indx++ ) { range = ranges[indx].split( /\s*-\s*/ ); start = parseInt( range[0], 10 ) || 0; end = parseInt( range[1], 10 ) || ( c.columns - 1 ); - if ( start > end ) { temp = start; start = end; end = temp; } // swap - if ( end >= c.columns ) { end = c.columns - 1; } + if ( start > end ) { + temp = start; start = end; end = temp; // swap + } + if ( end >= c.columns ) { + end = c.columns - 1; + } for ( ; start <= end; start++ ) { - columns.push(start); + columns.push( start ); } // remove processed range from val - val = val.replace( ranges[indx], '' ); + val = val.replace( ranges[ indx ], '' ); } } // process single columns if ( targets && /,/.test( val ) ) { singles = val.split( /\s*,\s*/ ); len = singles.length; - for (i = 0; i < len; i++) { - if (singles[i] !== '') { - indx = parseInt( singles[i], 10 ); + for ( i = 0; i < len; i++ ) { + if ( singles[ i ] !== '' ) { + indx = parseInt( singles[ i ], 10 ); if ( indx < c.columns ) { columns.push( indx ); } @@ -3280,382 +3418,472 @@ ts.filter = { } } // return all columns - if (!columns.length) { + if ( !columns.length ) { for ( indx = 0; indx < c.columns; indx++ ) { columns.push( indx ); } } return columns; }, - findRows: function(table, filters, combinedFilters) { - if (table.config.lastCombinedFilter === combinedFilters || !table.config.widgetOptions.filter_initialized) { return; } - var len, norm_rows, $rows, rowIndex, tbodyIndex, $tbody, $cells, $cell, columnIndex, - childRow, lastSearch, hasSelect, matches, result, showRow, time, val, indx, - notFiltered, searchFiltered, filterMatched, excludeMatch, fxn, ffxn, - query, injected, res, id, + processRow: function( c, data, vars ) { + var $cell, columnIndex, hasSelect, matches, result, val, filterMatched, excludeMatch, + fxn, ffxn, txt, + regex = ts.filter.regex, + wo = c.widgetOptions, + showRow = true; + data.$cells = data.$row.children(); + + if ( data.anyMatchFlag ) { + // look for multiple columns '1-3,4-6,8' + columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); + data.anyMatch = true; + data.rowArray = data.$cells.map( function( i ) { + if ( $.inArray( i, columnIndex ) > -1 ) { + if ( data.parsed[ i ] ) { + txt = data.cacheArray[ i ]; + } else { + txt = data.rawArray[ i ]; + txt = $.trim( wo.filter_ignoreCase ? txt.toLowerCase() : txt ); + if ( c.sortLocaleCompare ) { + txt = ts.replaceAccents( txt ); + } + } + return txt; + } + }).get(); + data.filter = data.anyMatchFilter; + data.iFilter = data.iAnyMatchFilter; + data.exact = data.rowArray.join( ' ' ); + data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; + data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' ); + filterMatched = null; + matches = null; + for ( ffxn in ts.filter.types ) { + if ( $.inArray( ffxn, vars.noAnyMatch ) < 0 && matches === null ) { + matches = ts.filter.types[ffxn]( c, data ); + if ( matches !== null ) { + filterMatched = matches; + } + } + } + if ( filterMatched !== null ) { + showRow = filterMatched; + } else { + if ( wo.filter_startsWith ) { + showRow = false; + columnIndex = c.columns; + while ( !showRow && columnIndex > 0 ) { + columnIndex--; + showRow = showRow || data.rowArray[ columnIndex ].indexOf( data.iFilter ) === 0; + } + } else { + showRow = ( data.iExact + data.childRowText ).indexOf( data.iFilter ) >= 0; + } + } + data.anyMatch = false; + // no other filters to process + if ( data.filters.join( '' ) === data.filter ) { + return showRow; + } + } + + for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { + data.filter = data.filters[ columnIndex ]; + data.index = columnIndex; + + // filter types to exclude, per column + excludeMatch = vars.excludeFilter[ columnIndex ]; + + // ignore if filter is empty or disabled + if ( data.filter ) { + data.cache = data.cacheArray[ columnIndex ]; + // check if column data should be from the cell or from parsed data + if ( wo.filter_useParsedData || data.parsed[ columnIndex ] ) { + data.exact = data.cache; + } else { + result = data.rawArray[ columnIndex ] || ''; + data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 + } + data.iExact = !regex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? + data.exact.toLowerCase() : data.exact; + result = showRow; // if showRow is true, show that row + + // in case select filter option has a different value vs text 'a - z|A through Z' + ffxn = wo.filter_columnFilters ? + c.$filters.add( c.$externalFilters ) + .filter( '[data-column="'+ columnIndex + '"]' ) + .find( 'select option:selected' ) + .attr( 'data-function-name' ) || '' : ''; + // replace accents - see #357 + if ( c.sortLocaleCompare ) { + data.filter = ts.replaceAccents( data.filter ); + } + + val = true; + if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) { + data.filter = ts.filter.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); + // val is used to indicate that a filter select is using a default filter; + // so we override the exact & partial matches + val = false; + } + // data.iFilter = case insensitive ( if wo.filter_ignoreCase is true ), + // data.filter = case sensitive + data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter; + fxn = vars.functions[ columnIndex ]; + $cell = c.$headerIndexed[ columnIndex ]; + hasSelect = $cell.hasClass( 'filter-select' ); + filterMatched = null; + if ( fxn || ( hasSelect && val ) ) { + if ( fxn === true || hasSelect ) { + // default selector uses exact match unless 'filter-match' class is found + filterMatched = $cell.hasClass( 'filter-match' ) ? + data.iExact.search( data.iFilter ) >= 0 : + data.filter === data.exact; + } else if ( typeof fxn === 'function' ) { + // filter callback( exact cell content, parser normalized content, + // filter input value, column index, jQuery row object ) + filterMatched = fxn( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); + } else if ( typeof fxn[ ffxn || data.filter ] === 'function' ) { + // selector option function + txt = ffxn || data.filter; + filterMatched = + fxn[ txt ]( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); + } + } + if ( filterMatched === null ) { + // cycle through the different filters + // filters return a boolean or null if nothing matches + matches = null; + for ( ffxn in ts.filter.types ) { + if ( $.inArray( ffxn, excludeMatch ) < 0 && matches === null ) { + matches = ts.filter.types[ ffxn ]( c, data ); + if ( matches !== null ) { + filterMatched = matches; + } + } + } + if ( filterMatched !== null ) { + result = filterMatched; + // Look for match, and add child row data for matching + } else { + txt = ( data.iExact + data.childRowText ) + .indexOf( ts.filter.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) ); + result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); + } + } else { + result = filterMatched; + } + showRow = ( result ) ? showRow : false; + } + } + return showRow; + }, + findRows: function( table, filters, combinedFilters ) { + if ( table.config.lastCombinedFilter === combinedFilters || + !table.config.widgetOptions.filter_initialized ) { + return; + } + var len, norm_rows, rowData, $rows, rowIndex, tbodyIndex, $tbody, columnIndex, + isChild, childRow, lastSearch, showRow, time, val, indx, + notFiltered, searchFiltered, query, injected, res, id, txt, + storedFilters = $.extend( [], filters ), regex = ts.filter.regex, c = table.config, wo = c.widgetOptions, // data object passed to filters; anyMatch is a flag for the filters - data = { anyMatch: false }, - // anyMatch really screws up with these types of filters - noAnyMatch = [ 'range', 'notMatch', 'operators' ]; + data = { + anyMatch: false, + filters: filters, + // regex filter type cache + filter_regexCache : [], + }, + vars = { + // anyMatch really screws up with these types of filters + noAnyMatch: [ 'range', 'notMatch', 'operators' ], + // cache filter variables that use ts.getColumnData in the main loop + functions : [], + excludeFilter : [], + defaultColFilter : [], + defaultAnyFilter : ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '' + }; // parse columns after formatter, in case the class is added at that point - data.parsed = c.$headers.map(function(columnIndex) { - return c.parsers && c.parsers[columnIndex] && c.parsers[columnIndex].parsed || - // getData won't return "parsed" if other "filter-" class names exist (e.g. <th class="filter-select filter-parsed">) - ts.getData && ts.getData(c.$headerIndexed[columnIndex], ts.getColumnData( table, c.headers, columnIndex ), 'filter') === 'parsed' || - $(this).hasClass('filter-parsed'); + data.parsed = c.$headers.map( function( columnIndex ) { + return c.parsers && c.parsers[ columnIndex ] && + // force parsing if parser type is numeric + ( c.parsers[ columnIndex ].parsed || c.parsers[ columnIndex ].type === 'numeric' ) || + // getData won't return 'parsed' if other 'filter-' class names exist + // ( e.g. <th class="filter-select filter-parsed"> ) + ts.getData && ts.getData( c.$headerIndexed[ columnIndex ], + ts.getColumnData( table, c.headers, columnIndex ), 'filter' ) === 'parsed' || + $( this ).hasClass( 'filter-parsed' ); }).get(); - // cache filter variables that use ts.getColumnData in the main loop - wo.filter_indexed = { - functions : [], - excludeFilter : [], - defaultColFilter : [], - defaultAnyFilter : ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '' - }; for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { - wo.filter_indexed.functions[ columnIndex ] = ts.getColumnData( table, wo.filter_functions, columnIndex ); - wo.filter_indexed.defaultColFilter[ columnIndex ] = ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || ''; - wo.filter_indexed.excludeFilter[ columnIndex ] = ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split(/\s+/); + vars.functions[ columnIndex ] = + ts.getColumnData( table, wo.filter_functions, columnIndex ); + vars.defaultColFilter[ columnIndex ] = + ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || ''; + vars.excludeFilter[ columnIndex ] = + ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split( /\s+/ ); } - if (c.debug) { - ts.log('Filter: Starting filter widget search', filters); + if ( c.debug ) { + ts.log( 'Filter: Starting filter widget search', filters ); time = new Date(); } // filtered rows count c.filteredRows = 0; c.totalRows = 0; // combindedFilters are undefined on init - combinedFilters = (filters || []).join(''); + combinedFilters = ( storedFilters || [] ).join( '' ); - for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { - $tbody = ts.processTbody(table, c.$tbodies.eq(tbodyIndex), true); - // skip child rows & widget added (removable) rows - fixes #448 thanks to @hempel! - // $rows = $tbody.children('tr').not(c.selectorRemove); + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody( table, c.$tbodies.eq( tbodyIndex ), true ); + // skip child rows & widget added ( removable ) rows - fixes #448 thanks to @hempel! + // $rows = $tbody.children( 'tr' ).not( c.selectorRemove ); columnIndex = c.columns; // convert stored rows into a jQuery object - norm_rows = c.cache[tbodyIndex].normalized; - $rows = $( $.map(norm_rows, function(el){ return el[columnIndex].$row.get(); }) ); - - if (combinedFilters === '' || wo.filter_serversideFiltering) { - $rows.removeClass(wo.filter_filteredRow).not('.' + c.cssChildRow).css('display', ''); + norm_rows = c.cache[ tbodyIndex ].normalized; + $rows = $( $.map( norm_rows, function( el ) { + return el[ columnIndex ].$row.get(); + }) ); + + if ( combinedFilters === '' || wo.filter_serversideFiltering ) { + $rows + .removeClass( wo.filter_filteredRow ) + .not( '.' + c.cssChildRow ) + .css( 'display', '' ); } else { // filter out child rows - $rows = $rows.not('.' + c.cssChildRow); + $rows = $rows.not( '.' + c.cssChildRow ); len = $rows.length; - if ( (wo.filter_$anyMatch && wo.filter_$anyMatch.length) || typeof filters[c.columns] !== 'undefined' ) { + if ( ( wo.filter_$anyMatch && wo.filter_$anyMatch.length ) || + typeof filters[c.columns] !== 'undefined' ) { data.anyMatchFlag = true; - data.anyMatchFilter = wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || ( '' + filters[c.columns] ) || ''; - if (wo.filter_columnAnyMatch) { + data.anyMatchFilter = '' + ( + filters[ c.columns ] || + wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || + '' + ); + if ( wo.filter_columnAnyMatch ) { // specific columns search - query = data.anyMatchFilter.split( ts.filter.regex.andSplit ); + query = data.anyMatchFilter.split( regex.andSplit ); injected = false; - for (indx = 0; indx < query.length; indx++) { - res = query[indx].split(':'); + for ( indx = 0; indx < query.length; indx++ ) { + res = query[ indx ].split( ':' ); if ( res.length > 1 ) { // make the column a one-based index ( non-developers start counting from one :P ) id = parseInt( res[0], 10 ) - 1; if ( id >= 0 && id < c.columns ) { // if id is an integer - filters[id] = res[1]; - query.splice(indx, 1); + filters[ id ] = res[1]; + query.splice( indx, 1 ); indx--; injected = true; } } } - if (injected) { - data.anyMatchFilter = query.join(' && '); + if ( injected ) { + data.anyMatchFilter = query.join( ' && ' ); } } } // optimize searching only through already filtered rows - see #313 searchFiltered = wo.filter_searchFiltered; - lastSearch = c.lastSearch || c.$table.data('lastSearch') || []; - if (searchFiltered) { - // cycle through all filters; include last (columnIndex + 1 = match any column). Fixes #669 - for (indx = 0; indx < columnIndex + 1; indx++) { + lastSearch = c.lastSearch || c.$table.data( 'lastSearch' ) || []; + if ( searchFiltered ) { + // cycle through all filters; include last ( columnIndex + 1 = match any column ). Fixes #669 + for ( indx = 0; indx < columnIndex + 1; indx++ ) { val = filters[indx] || ''; // break out of loop if we've already determined not to search filtered rows - if (!searchFiltered) { indx = columnIndex; } + if ( !searchFiltered ) { indx = columnIndex; } // search already filtered rows if... searchFiltered = searchFiltered && lastSearch.length && // there are no changes from beginning of filter - val.indexOf(lastSearch[indx] || '') === 0 && - // if there is NOT a logical "or", or range ("to" or "-") in the string - !regex.alreadyFiltered.test(val) && - // if we are not doing exact matches, using "|" (logical or) or not "!" - !/[=\"\|!]/.test(val) && - // don't search only filtered if the value is negative ('> -10' => '> -100' will ignore hidden rows) - !(/(>=?\s*-\d)/.test(val) || /(<=?\s*\d)/.test(val)) && - // if filtering using a select without a "filter-match" class (exact match) - fixes #593 - !( val !== '' && c.$filters && c.$filters.eq(indx).find('select').length && !c.$headerIndexed[indx].hasClass('filter-match') ); + val.indexOf( lastSearch[indx] || '' ) === 0 && + // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string + !regex.alreadyFiltered.test( val ) && + // if we are not doing exact matches, using '|' ( logical or ) or not '!' + !/[=\"\|!]/.test( val ) && + // don't search only filtered if the value is negative + // ( '> -10' => '> -100' will ignore hidden rows ) + !( /(>=?\s*-\d)/.test( val ) || /(<=?\s*\d)/.test( val ) ) && + // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 + !( val !== '' && c.$filters && c.$filters.eq( indx ).find( 'select' ).length && + !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); } } - notFiltered = $rows.not('.' + wo.filter_filteredRow).length; + notFiltered = $rows.not( '.' + wo.filter_filteredRow ).length; // can't search when all rows are hidden - this happens when looking for exact matches - if (searchFiltered && notFiltered === 0) { searchFiltered = false; } - if (c.debug) { - ts.log( 'Filter: Searching through ' + ( searchFiltered && notFiltered < len ? notFiltered : 'all' ) + ' rows' ); + if ( searchFiltered && notFiltered === 0 ) { searchFiltered = false; } + if ( c.debug ) { + ts.log( 'Filter: Searching through ' + + ( searchFiltered && notFiltered < len ? notFiltered : 'all' ) + ' rows' ); } - if (data.anyMatchFlag) { - if (c.sortLocaleCompare) { + if ( data.anyMatchFlag ) { + if ( c.sortLocaleCompare ) { // replace accents - data.anyMatchFilter = ts.replaceAccents(data.anyMatchFilter); + data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter ); } - if ( wo.filter_defaultFilter && regex.iQuery.test( wo.filter_indexed.defaultAnyFilter ) ) { - data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, wo.filter_indexed.defaultAnyFilter ); + if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultAnyFilter ) ) { + data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter ); // clear search filtered flag because default filters are not saved to the last search searchFiltered = false; } // make iAnyMatchFilter lowercase unless both filter widget & core ignoreCase options are true // when c.ignoreCase is true, the cache contains all lower case data - data.iAnyMatchFilter = !(wo.filter_ignoreCase && c.ignoreCase) ? data.anyMatchFilter : data.anyMatchFilter.toLocaleLowerCase(); + data.iAnyMatchFilter = !( wo.filter_ignoreCase && c.ignoreCase ) ? + data.anyMatchFilter : + data.anyMatchFilter.toLowerCase(); } // loop through the rows - for (rowIndex = 0; rowIndex < len; rowIndex++) { + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { - data.cacheArray = norm_rows[rowIndex]; - - childRow = $rows[rowIndex].className; + txt = $rows[ rowIndex ].className; + // the first row can never be a child row + isChild = rowIndex && regex.child.test( txt ); // skip child rows & already filtered rows - if ( regex.child.test(childRow) || (searchFiltered && regex.filtered.test(childRow)) ) { continue; } - showRow = true; - // *** nextAll/nextUntil not supported by Zepto! *** - childRow = $rows.eq(rowIndex).nextUntil('tr:not(.' + c.cssChildRow + ')'); - // so, if "table.config.widgetOptions.filter_childRows" is true and there is - // a match anywhere in the child row, then it will make the row visible - // checked here so the option can be changed dynamically - data.childRowText = (childRow.length && wo.filter_childRows) ? childRow.text() : ''; - data.childRowText = wo.filter_ignoreCase ? data.childRowText.toLocaleLowerCase() : data.childRowText; - $cells = $rows.eq(rowIndex).children(); - if (data.anyMatchFlag) { - // look for multiple columns "1-3,4-6,8" - columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); - data.anyMatch = true; - data.rowArray = $cells.map(function(i){ - if ( $.inArray(i, columnIndex) > -1 ) { - var txt; - if (data.parsed[i]) { - txt = data.cacheArray[i]; - } else { - txt = this ? this.getAttribute( c.textAttribute ) || this.textContent || $(this).text() : ''; - txt = $.trim( wo.filter_ignoreCase ? txt.toLowerCase() : txt ); - if (c.sortLocaleCompare) { - txt = ts.replaceAccents(txt); - } - } - return txt; - } - }).get(); - data.filter = data.anyMatchFilter; - data.iFilter = data.iAnyMatchFilter; - data.exact = data.rowArray.join(' '); - data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; - data.cache = data.cacheArray.slice(0,-1).join(' '); - filterMatched = null; - $.each(ts.filter.types, function(type, typeFunction) { - if ($.inArray(type, noAnyMatch) < 0) { - matches = typeFunction( c, data ); - if (matches !== null) { - filterMatched = matches; - return false; - } - } - }); - if (filterMatched !== null) { - showRow = filterMatched; - } else { - if (wo.filter_startsWith) { - showRow = false; - columnIndex = c.columns; - while (!showRow && columnIndex > 0) { - columnIndex--; - showRow = showRow || data.rowArray[columnIndex].indexOf(data.iFilter) === 0; - } - } else { - showRow = (data.iExact + data.childRowText).indexOf(data.iFilter) >= 0; - } - } - data.anyMatch = false; + if ( isChild || ( searchFiltered && regex.filtered.test( txt ) ) ) { + continue; } - for (columnIndex = 0; columnIndex < c.columns; columnIndex++) { - data.filter = filters[columnIndex]; - data.index = columnIndex; - - // filter types to exclude, per column - excludeMatch = wo.filter_indexed.excludeFilter[ columnIndex ]; - - // ignore if filter is empty or disabled - if (data.filter) { - data.cache = data.cacheArray[columnIndex]; - // check if column data should be from the cell or from parsed data - if (wo.filter_useParsedData || data.parsed[columnIndex]) { - data.exact = data.cache; - } else { - val = $cells[columnIndex]; - result = val ? $.trim( val.getAttribute( c.textAttribute ) || val.textContent || $cells.eq(columnIndex).text() ) : ''; - data.exact = c.sortLocaleCompare ? ts.replaceAccents(result) : result; // issue #405 - } - data.iExact = !regex.type.test(typeof data.exact) && wo.filter_ignoreCase ? data.exact.toLocaleLowerCase() : data.exact; - result = showRow; // if showRow is true, show that row - - // in case select filter option has a different value vs text "a - z|A through Z" - ffxn = wo.filter_columnFilters ? - c.$filters.add(c.$externalFilters).filter('[data-column="'+ columnIndex + '"]').find('select option:selected').attr('data-function-name') || '' : ''; - // replace accents - see #357 - if (c.sortLocaleCompare) { - data.filter = ts.replaceAccents(data.filter); - } + data.$row = $rows.eq( rowIndex ); + data.cacheArray = norm_rows[ rowIndex ]; + rowData = data.cacheArray[ c.columns ]; + data.rawArray = rowData.raw; + data.childRowText = ''; + + if ( !wo.filter_childByColumn ) { + txt = ''; + // child row cached text + childRow = rowData.child; + // so, if 'table.config.widgetOptions.filter_childRows' is true and there is + // a match anywhere in the child row, then it will make the row visible + // checked here so the option can be changed dynamically + for ( indx = 0; indx < childRow.length; indx++ ) { + txt += ' ' + childRow[indx].join( '' ) || ''; + } + data.childRowText = wo.filter_childRows ? + ( wo.filter_ignoreCase ? txt.toLowerCase() : txt ) : + ''; + } - val = true; - if (wo.filter_defaultFilter && regex.iQuery.test( wo.filter_indexed.defaultColFilter[ columnIndex ] )) { - data.filter = ts.filter.defaultFilter( data.filter, wo.filter_indexed.defaultColFilter[ columnIndex ] ); - // val is used to indicate that a filter select is using a default filter; so we override the exact & partial matches - val = false; - } - // data.iFilter = case insensitive (if wo.filter_ignoreCase is true), data.filter = case sensitive - data.iFilter = wo.filter_ignoreCase ? (data.filter || '').toLocaleLowerCase() : data.filter; - fxn = wo.filter_indexed.functions[ columnIndex ]; - $cell = c.$headerIndexed[columnIndex]; - hasSelect = $cell.hasClass('filter-select'); - filterMatched = null; - if ( fxn || ( hasSelect && val ) ) { - if (fxn === true || hasSelect) { - // default selector uses exact match unless "filter-match" class is found - filterMatched = ($cell.hasClass('filter-match')) ? data.iExact.search(data.iFilter) >= 0 : data.filter === data.exact; - } else if (typeof fxn === 'function') { - // filter callback( exact cell content, parser normalized content, filter input value, column index, jQuery row object ) - filterMatched = fxn(data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); - } else if (typeof fxn[ffxn || data.filter] === 'function') { - // selector option function - filterMatched = fxn[ffxn || data.filter](data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); - } + showRow = ts.filter.processRow( c, data, vars ); + childRow = rowData.$row.filter( ':gt( 0 )' ); + + if ( wo.filter_childRows && childRow.length ) { + if ( wo.filter_childByColumn ) { + // cycle through each child row + for ( indx = 0; indx < childRow.length; indx++ ) { + data.$row = childRow.eq( indx ); + data.cacheArray = rowData.child[ indx ]; + data.rawArray = data.cacheArray; + // use OR comparison on child rows + showRow = showRow || ts.filter.processRow( c, data, vars ); } - if (filterMatched === null) { - // cycle through the different filters - // filters return a boolean or null if nothing matches - $.each(ts.filter.types, function(type, typeFunction) { - if ($.inArray(type, excludeMatch) < 0) { - matches = typeFunction( c, data ); - if (matches !== null) { - filterMatched = matches; - return false; - } - } - }); - if (filterMatched !== null) { - result = filterMatched; - // Look for match, and add child row data for matching - } else { - data.exact = (data.iExact + data.childRowText).indexOf( ts.filter.parseFilter(c, data.iFilter, columnIndex, data.parsed[columnIndex]) ); - result = ( (!wo.filter_startsWith && data.exact >= 0) || (wo.filter_startsWith && data.exact === 0) ); - } - } else { - result = filterMatched; - } - showRow = (result) ? showRow : false; } + childRow.toggleClass( wo.filter_filteredRow, !showRow ); } - $rows.eq(rowIndex) - .toggleClass(wo.filter_filteredRow, !showRow)[0] + + rowData.$row + .toggleClass( wo.filter_filteredRow, !showRow )[0] .display = showRow ? '' : 'none'; - if (childRow.length) { - childRow.toggleClass(wo.filter_filteredRow, !showRow); - } } } - c.filteredRows += $rows.not('.' + wo.filter_filteredRow).length; + c.filteredRows += $rows.not( '.' + wo.filter_filteredRow ).length; c.totalRows += $rows.length; - ts.processTbody(table, $tbody, false); + ts.processTbody( table, $tbody, false ); } c.lastCombinedFilter = combinedFilters; // save last search - c.lastSearch = filters; - c.$table.data('lastSearch', filters); - if (wo.filter_saveFilters && ts.storage) { - ts.storage( table, 'tablesorter-filters', filters ); + // don't save 'filters' directly since it may have altered ( AnyMatch column searches ) + c.lastSearch = storedFilters; + c.$table.data( 'lastSearch', storedFilters ); + if ( wo.filter_saveFilters && ts.storage ) { + ts.storage( table, 'tablesorter-filters', storedFilters ); } - if (c.debug) { - ts.benchmark("Completed filter widget search", time); + if ( c.debug ) { + ts.benchmark( 'Completed filter widget search', time ); + } + if ( wo.filter_initialized ) { + c.$table.trigger( 'filterEnd', c ); } - if (wo.filter_initialized) { c.$table.trigger('filterEnd', c ); } - setTimeout(function(){ - c.$table.trigger('applyWidgets'); // make sure zebra widget is applied - }, 0); + setTimeout( function() { + c.$table.trigger( 'applyWidgets' ); // make sure zebra widget is applied + }, 0 ); }, - getOptionSource: function(table, column, onlyAvail) { - table = $(table)[0]; + getOptionSource: function( table, column, onlyAvail ) { + table = $( table )[0]; var cts, indx, len, c = table.config, wo = c.widgetOptions, parsed = [], arry = false, source = wo.filter_selectSource, - last = c.$table.data('lastSearch') || [], - fxn = $.isFunction(source) ? true : ts.getColumnData( table, source, column ); + last = c.$table.data( 'lastSearch' ) || [], + fxn = $.isFunction( source ) ? true : ts.getColumnData( table, source, column ); - if (onlyAvail && last[column] !== '') { + if ( onlyAvail && last[column] !== '' ) { onlyAvail = false; } // filter select source option - if (fxn === true) { + if ( fxn === true ) { // OVERALL source - arry = source(table, column, onlyAvail); - } else if ( fxn instanceof $ || ($.type(fxn) === 'string' && fxn.indexOf('</option>') >= 0) ) { + arry = source( table, column, onlyAvail ); + } else if ( fxn instanceof $ || ( $.type( fxn ) === 'string' && fxn.indexOf( '</option>' ) >= 0 ) ) { // selectSource is a jQuery object or string of options return fxn; - } else if ($.isArray(fxn)) { + } else if ( $.isArray( fxn ) ) { arry = fxn; - } else if ($.type(source) === 'object' && fxn) { + } else if ( $.type( source ) === 'object' && fxn ) { // custom select source function for a SPECIFIC COLUMN - arry = fxn(table, column, onlyAvail); + arry = fxn( table, column, onlyAvail ); } - if (arry === false) { + if ( arry === false ) { // fall back to original method - arry = ts.filter.getOptions(table, column, onlyAvail); + arry = ts.filter.getOptions( table, column, onlyAvail ); } // get unique elements and sort the list - // if $.tablesorter.sortText exists (not in the original tablesorter), + // if $.tablesorter.sortText exists ( not in the original tablesorter ), // then natural sort the list otherwise use a basic sort - arry = $.grep(arry, function(value, indx) { - return $.inArray(value, arry) === indx; + arry = $.grep( arry, function( value, indx ) { + return $.inArray( value, arry ) === indx; }); - if (c.$headerIndexed[column].hasClass('filter-select-nosort')) { + if ( c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) { // unsorted select options return arry; } else { len = arry.length; // parse select option values - for (indx = 0; indx < len; indx++) { + for ( indx = 0; indx < len; indx++ ) { // parse array data using set column parser; this DOES NOT pass the original // table cell to the parser format function - parsed.push({ t : arry[indx], p : c.parsers && c.parsers[column].format( arry[indx], table, [], column ) }); + parsed.push({ + t : arry[ indx ], + p : c.parsers && c.parsers[ column ].format( arry[ indx ], table, [], column ) + }); } // sort parsed select options cts = c.textSorter || ''; - parsed.sort(function(a, b){ + parsed.sort( function( a, b ) { // sortNatural breaks if you don't pass it strings - var x = a.p.toString(), y = b.p.toString(); - if ($.isFunction(cts)) { + var x = a.p.toString(), + y = b.p.toString(); + if ( $.isFunction( cts ) ) { // custom OVERALL text sorter - return cts(x, y, true, column, table); - } else if (typeof(cts) === 'object' && cts.hasOwnProperty(column)) { + return cts( x, y, true, column, table ); + } else if ( typeof( cts ) === 'object' && cts.hasOwnProperty( column ) ) { // custom text sorter for a SPECIFIC COLUMN - return cts[column](x, y, true, column, table); - } else if (ts.sortNatural) { + return cts[column]( x, y, true, column, table ); + } else if ( ts.sortNatural ) { // fall back to natural sort - return ts.sortNatural(x, y); + return ts.sortNatural( x, y ); } // using an older version! do a basic sort return true; @@ -3663,187 +3891,224 @@ ts.filter = { // rebuild arry from sorted parsed data arry = []; len = parsed.length; - for (indx = 0; indx < len; indx++) { + for ( indx = 0; indx < len; indx++ ) { arry.push( parsed[indx].t ); } return arry; } }, - getOptions: function(table, column, onlyAvail) { - table = $(table)[0]; - var rowIndex, tbodyIndex, len, row, cache, cell, + getOptions: function( table, column, onlyAvail ) { + table = $( table )[0]; + var rowIndex, tbodyIndex, len, row, cache, c = table.config, wo = c.widgetOptions, arry = []; - for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { cache = c.cache[tbodyIndex]; len = c.cache[tbodyIndex].normalized.length; // loop through the rows - for (rowIndex = 0; rowIndex < len; rowIndex++) { - // get cached row from cache.row (old) or row data object (new; last item in normalized array) - row = cache.row ? cache.row[rowIndex] : cache.normalized[rowIndex][c.columns].$row[0]; + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { + // get cached row from cache.row ( old ) or row data object + // ( new; last item in normalized array ) + row = cache.row ? + cache.row[ rowIndex ] : + cache.normalized[ rowIndex ][ c.columns ].$row[0]; // check if has class filtered - if (onlyAvail && row.className.match(wo.filter_filteredRow)) { continue; } + if ( onlyAvail && row.className.match( wo.filter_filteredRow ) ) { + continue; + } // get non-normalized cell content - if (wo.filter_useParsedData || c.parsers[column].parsed || c.$headerIndexed[column].hasClass('filter-parsed')) { - arry.push( '' + cache.normalized[rowIndex][column] ); + if ( wo.filter_useParsedData || + c.parsers[column].parsed || + c.$headerIndexed[column].hasClass( 'filter-parsed' ) ) { + arry.push( '' + cache.normalized[ rowIndex ][ column ] ); } else { - cell = row.cells[column]; - if (cell) { - arry.push( $.trim( cell.getAttribute( c.textAttribute ) || cell.textContent || $(cell).text() ) ); - } + // get raw cached data instead of content directly from the cells + arry.push( cache.normalized[ rowIndex ][ c.columns ].raw[ column ] ); } } } return arry; }, - buildSelect: function(table, column, arry, updating, onlyAvail) { - table = $(table)[0]; - column = parseInt(column, 10); - if (!table.config.cache || $.isEmptyObject(table.config.cache)) { return; } + buildSelect: function( table, column, arry, updating, onlyAvail ) { + table = $( table )[0]; + column = parseInt( column, 10 ); + if ( !table.config.cache || $.isEmptyObject( table.config.cache ) ) { + return; + } var indx, val, txt, t, $filters, $filter, c = table.config, wo = c.widgetOptions, - node = c.$headerIndexed[column], - // t.data('placeholder') won't work in jQuery older than 1.4.3 - options = '<option value="">' + ( node.data('placeholder') || node.attr('data-placeholder') || wo.filter_placeholder.select || '' ) + '</option>', + node = c.$headerIndexed[ column ], + // t.data( 'placeholder' ) won't work in jQuery older than 1.4.3 + options = '<option value="">' + + ( node.data( 'placeholder' ) || + node.attr( 'data-placeholder' ) || + wo.filter_placeholder.select || '' + ) + '</option>', // Get curent filter value - currentValue = c.$table.find('thead').find('select.' + tscss.filter + '[data-column="' + column + '"]').val(); - // nothing included in arry (external source), so get the options from filter_selectSource or column data - if (typeof arry === 'undefined' || arry === '') { - arry = ts.filter.getOptionSource(table, column, onlyAvail); + currentValue = c.$table + .find( 'thead' ) + .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) + .val(); + // nothing included in arry ( external source ), so get the options from + // filter_selectSource or column data + if ( typeof arry === 'undefined' || arry === '' ) { + arry = ts.filter.getOptionSource( table, column, onlyAvail ); } - if ($.isArray(arry)) { + if ( $.isArray( arry ) ) { // build option list - for (indx = 0; indx < arry.length; indx++) { - txt = arry[indx] = ('' + arry[indx]).replace(/\"/g, """); + for ( indx = 0; indx < arry.length; indx++ ) { + txt = arry[indx] = ( '' + arry[indx] ).replace( /\"/g, '"' ); val = txt; // allow including a symbol in the selectSource array - // "a-z|A through Z" so that "a-z" becomes the option value - // and "A through Z" becomes the option text - if (txt.indexOf(wo.filter_selectSourceSeparator) >= 0) { - t = txt.split(wo.filter_selectSourceSeparator); + // 'a-z|A through Z' so that 'a-z' becomes the option value + // and 'A through Z' becomes the option text + if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { + t = txt.split( wo.filter_selectSourceSeparator ); val = t[0]; txt = t[1]; } - // replace quotes - fixes #242 & ignore empty strings - see http://stackoverflow.com/q/14990971/145346 - options += arry[indx] !== '' ? '<option ' + (val === txt ? '' : 'data-function-name="' + arry[indx] + '" ') + 'value="' + val + '">' + txt + '</option>' : ''; + // replace quotes - fixes #242 & ignore empty strings + // see http://stackoverflow.com/q/14990971/145346 + options += arry[indx] !== '' ? + '<option ' + + ( val === txt ? '' : 'data-function-name="' + arry[indx] + '" ' ) + + 'value="' + val + '">' + txt + + '</option>' : ''; } // clear arry so it doesn't get appended twice arry = []; } - // update all selects in the same column (clone thead in sticky headers & any external selects) - fixes 473 - $filters = ( c.$filters ? c.$filters : c.$table.children('thead') ).find('.' + tscss.filter); - if (wo.filter_$externalFilters) { - $filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters; + // update all selects in the same column ( clone thead in sticky headers & + // any external selects ) - fixes 473 + $filters = ( c.$filters ? c.$filters : c.$table.children( 'thead' ) ) + .find( '.' + tscss.filter ); + if ( wo.filter_$externalFilters ) { + $filters = $filters && $filters.length ? + $filters.add( wo.filter_$externalFilters ) : + wo.filter_$externalFilters; } - $filter = $filters.filter('select[data-column="' + column + '"]'); + $filter = $filters.filter( 'select[data-column="' + column + '"]' ); // make sure there is a select there! - if ($filter.length) { - $filter[ updating ? 'html' : 'append' ](options); - if (!$.isArray(arry)) { + if ( $filter.length ) { + $filter[ updating ? 'html' : 'append' ]( options ); + if ( !$.isArray( arry ) ) { // append options if arry is provided externally as a string or jQuery object - // options (default value) was already added - $filter.append(arry).val(currentValue); + // options ( default value ) was already added + $filter.append( arry ).val( currentValue ); } - $filter.val(currentValue); + $filter.val( currentValue ); } }, - buildDefault: function(table, updating) { + buildDefault: function( table, updating ) { var columnIndex, $header, noSelect, c = table.config, wo = c.widgetOptions, columns = c.columns; // build default select dropdown - for (columnIndex = 0; columnIndex < columns; columnIndex++) { + for ( columnIndex = 0; columnIndex < columns; columnIndex++ ) { $header = c.$headerIndexed[columnIndex]; - noSelect = !($header.hasClass('filter-false') || $header.hasClass('parser-false')); + noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); // look for the filter-select class; build/update it if found - if (($header.hasClass('filter-select') || ts.getColumnData( table, wo.filter_functions, columnIndex ) === true) && noSelect) { - ts.filter.buildSelect(table, columnIndex, '', updating, $header.hasClass(wo.filter_onlyAvail)); + if ( ( $header.hasClass( 'filter-select' ) || + ts.getColumnData( table, wo.filter_functions, columnIndex ) === true ) && noSelect ) { + ts.filter.buildSelect( table, columnIndex, '', updating, $header.hasClass( wo.filter_onlyAvail ) ); } } } }; -ts.getFilters = function(table, getRaw, setFilters, skipFirst) { +ts.getFilters = function( table, getRaw, setFilters, skipFirst ) { var i, $filters, $column, cols, filters = false, - c = table ? $(table)[0].config : '', + c = table ? $( table )[0].config : '', wo = c ? c.widgetOptions : ''; - if (getRaw !== true && wo && !wo.filter_columnFilters) { - return $(table).data('lastSearch'); + if ( ( getRaw !== true && wo && !wo.filter_columnFilters ) || + // setFilters called, but last search is exactly the same as the current + // fixes issue #733 & #903 where calling update causes the input values to reset + ( $.isArray(setFilters) && setFilters.join('') === c.lastCombinedFilter ) ) { + return $( table ).data( 'lastSearch' ); } - if (c) { - if (c.$filters) { - $filters = c.$filters.find('.' + tscss.filter); + if ( c ) { + if ( c.$filters ) { + $filters = c.$filters.find( '.' + tscss.filter ); } - if (wo.filter_$externalFilters) { - $filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters; + if ( wo.filter_$externalFilters ) { + $filters = $filters && $filters.length ? + $filters.add( wo.filter_$externalFilters ) : + wo.filter_$externalFilters; } - if ($filters && $filters.length) { + if ( $filters && $filters.length ) { filters = setFilters || []; - for (i = 0; i < c.columns + 1; i++) { + for ( i = 0; i < c.columns + 1; i++ ) { cols = ( i === c.columns ? - // "all" columns can now include a range or set of columms (data-column="0-2,4,6-7") + // 'all' columns can now include a range or set of columms ( data-column='0-2,4,6-7' ) wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector : '[data-column="' + i + '"]' ); - $column = $filters.filter(cols); - if ($column.length) { + $column = $filters.filter( cols ); + if ( $column.length ) { // move the latest search to the first slot in the array $column = ts.filter.getLatestSearch( $column ); - if ($.isArray(setFilters)) { - // skip first (latest input) to maintain cursor position while typing - if (skipFirst) { $column.slice(1); } - if (i === c.columns) { - // prevent data-column="all" from filling data-column="0,1" (etc) - cols = $column.filter(wo.filter_anyColumnSelector); + if ( $.isArray( setFilters ) ) { + // skip first ( latest input ) to maintain cursor position while typing + if ( skipFirst ) { + $column.slice( 1 ); + } + if ( i === c.columns ) { + // prevent data-column='all' from filling data-column='0,1' ( etc ) + cols = $column.filter( wo.filter_anyColumnSelector ); $column = cols.length ? cols : $column; } $column - .val( setFilters[i] ) - .trigger('change.tsfilter'); + .val( setFilters[ i ] ) + .trigger( 'change.tsfilter' ); } else { filters[i] = $column.val() || ''; // don't change the first... it will move the cursor - if (i === c.columns) { - // don't update range columns from "all" setting - $column.slice(1).filter('[data-column*="' + $column.attr('data-column') + '"]').val( filters[i] ); + if ( i === c.columns ) { + // don't update range columns from 'all' setting + $column + .slice( 1 ) + .filter( '[data-column*="' + $column.attr( 'data-column' ) + '"]' ) + .val( filters[ i ] ); } else { - $column.slice(1).val( filters[i] ); + $column + .slice( 1 ) + .val( filters[ i ] ); } } // save any match input dynamically - if (i === c.columns && $column.length) { + if ( i === c.columns && $column.length ) { wo.filter_$anyMatch = $column; } } } } } - if (filters.length === 0) { + if ( filters.length === 0 ) { filters = false; } return filters; }; -ts.setFilters = function(table, filter, apply, skipFirst) { - var c = table ? $(table)[0].config : '', - valid = ts.getFilters(table, true, filter, skipFirst); - if (c && apply) { +ts.setFilters = function( table, filter, apply, skipFirst ) { + var c = table ? $( table )[0].config : '', + valid = ts.getFilters( table, true, filter, skipFirst ); + if ( c && apply ) { // ensure new set filters are applied, even if the search is the same c.lastCombinedFilter = null; c.lastSearch = []; - ts.filter.searching(c.table, filter, skipFirst); - c.$table.trigger('filterFomatterUpdate'); + ts.filter.searching( c.table, filter, skipFirst ); + c.$table.trigger( 'filterFomatterUpdate' ); } return !!valid; }; -})(jQuery); +})( jQuery ); /*! Widget: stickyHeaders - updated 3/26/2015 (v2.21.3) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ @@ -3851,7 +4116,7 @@ ts.setFilters = function(table, filter, apply, skipFirst) { */ ;(function ($, window) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; +var ts = $.tablesorter || {}; $.extend(ts.css, { sticky : 'tablesorter-stickyHeader', // stickyHeader @@ -4117,10 +4382,10 @@ ts.addWidget({ })(jQuery, window); -/*! Widget: resizable - updated 4/2/2015 (v2.21.5) */ +/*! Widget: resizable - updated 5/17/2015 (v2.22.0) */ ;(function ($, window) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; +var ts = $.tablesorter || {}; $.extend(ts.css, { resizableContainer : 'tablesorter-resizable-container', @@ -4409,7 +4674,7 @@ ts.addWidget({ init: function(table, thisWidget, c, wo) { ts.resizable.init( c, wo ); }, - remove: function( table, c, wo ) { + remove: function( table, c, wo, refreshing ) { if (wo.$resizable_container) { var namespace = c.namespace + 'tsresize'; c.$table.add( $( c.namespace + '_extra_table' ) ) @@ -4418,13 +4683,13 @@ ts.addWidget({ wo.$resizable_container.remove(); ts.resizable.toggleTextSelection( c, false ); - ts.resizableReset( table ); + ts.resizableReset( table, refreshing ); $( document ).unbind( 'mousemove' + namespace + ' mouseup' + namespace ); } } }); -ts.resizableReset = function( table, nosave ) { +ts.resizableReset = function( table, refreshing ) { $( table ).each(function(){ var index, $t, c = this.config, @@ -4441,7 +4706,7 @@ ts.resizableReset = function( table, nosave ) { } // reset stickyHeader widths $( window ).trigger( 'resize' ); - if ( ts.storage && !nosave ) { + if ( ts.storage && !refreshing ) { ts.storage( this, ts.css.resizableStorage, {} ); } } @@ -4453,7 +4718,7 @@ ts.resizableReset = function( table, nosave ) { /*! Widget: saveSort */ ;(function ($) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; +var ts = $.tablesorter || {}; // this widget saves the last sort only if the // saveSort widget option is true AND the diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index f760c10..3001a40 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.21.5 *//* +/*! TableSorter (FORK) v2.22.0 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -26,7 +26,7 @@ var ts = this; - ts.version = '2.21.5'; + ts.version = '2.22.0'; ts.parsers = []; ts.widgets = []; @@ -101,6 +101,11 @@ cssNoSort : 'tablesorter-noSort', // class name added to element inside header; clicking on it won't cause a sort cssIgnoreRow : 'tablesorter-ignoreRow', // header row to ignore; cells within this row will not be added to c.$headers + // *** events + pointerClick : 'click', + pointerDown : 'mousedown', + pointerUp : 'mouseup', + // *** selectors selectorHeaders : '> thead th, > thead td', selectorSort : 'th, td', // jQuery selector of content within selectorHeaders that is clickable to trigger a sort @@ -185,7 +190,11 @@ $node = node.jquery ? node : $(node); if (typeof(t) === 'string') { // check data-attribute first when set to 'basic'; don't use node.innerText - it's really slow! - return $.trim( ( t === 'basic' ? $node.attr(c.textAttribute) || node.textContent : node.textContent ) || $node.text() || '' ); + // http://www.kellegous.com/j/2013/02/27/innertext-vs-textcontent/ + return $.trim( + ( t === 'basic' ? $node.attr(c.textAttribute) || node.textContent : node.textContent ) || + $node.text() + ); } else { if (typeof(t) === 'function') { return $.trim( t($node[0], c.table, cellIndex) ); @@ -194,7 +203,7 @@ } } // fallback - return $.trim( $node[0].textContent || $node.text() || '' ); + return $.trim( $node[0].textContent || $node.text() ); }; function detectParserForColumn(table, rows, rowIndex, cellIndex) { @@ -228,6 +237,32 @@ return ts.getParserById('text'); } + // centralized function to extract/parse cell contents + function getParsedText( c, cell, colIndex, txt ) { + if ( typeof txt === 'undefined' ) { + txt = ts.getElementText( c, cell, colIndex ); + } + // if no parser, make sure to return the txt + var val = '' + txt, + parser = c.parsers[ colIndex ], + extractor = c.extractors[ colIndex ]; + if ( parser ) { + // do extract before parsing, if there is one + if ( extractor && typeof extractor.format === 'function' ) { + txt = extractor.format( txt, c.table, cell, colIndex ); + } + // allow parsing if the string is empty, previously parsing would change it to zero, + // in case the parser needs to extract data from the table cell attributes + val = parser.id === 'no-parser' ? '' : + // make sure txt is a string (extractor may have converted it) + parser.format( '' + txt, c.table, cell, colIndex ); + if ( c.ignoreCase && typeof val === 'string' ) { + val = val.toLowerCase(); + } + } + return val; + } + function buildParserCache(table) { var c = table.config, // update table bodies in case we start with an empty table @@ -291,11 +326,10 @@ /* utils */ function buildCache(table) { - var cc, t, tx, v, i, j, k, $row, cols, cacheTime, - totalRows, rowData, colMax, + var cc, t, v, i, j, k, $row, cols, cacheTime, + totalRows, rowData, prevRowData, colMax, c = table.config, $tb = c.$tbodies, - extractors = c.extractors, parsers = c.parsers; c.cache = {}; c.totalRows = 0; @@ -326,62 +360,61 @@ raw: [] // original row text }; /** Add the table data to main data array */ - $row = $($tb[k].rows[i]); + $row = $( $tb[ k ].rows[ i ] ); cols = []; // if this is a child row, add it to the last row's children and continue to the next row // ignore child row class, if it is the first row - if ($row.hasClass(c.cssChildRow) && i !== 0) { + if ( $row.hasClass( c.cssChildRow ) && i !== 0 ) { t = cc.normalized.length - 1; - cc.normalized[t][c.columns].$row = cc.normalized[t][c.columns].$row.add($row); + prevRowData = cc.normalized[ t ][ c.columns ]; + prevRowData.$row = prevRowData.$row.add( $row ); // add 'hasChild' class name to parent row - if (!$row.prev().hasClass(c.cssChildRow)) { - $row.prev().addClass(ts.css.cssHasChild); + if ( !$row.prev().hasClass( c.cssChildRow ) ) { + $row.prev().addClass( ts.css.cssHasChild ); } // save child row content (un-parsed!) - rowData.child[t] = $.trim( $row[0].textContent || $row.text() || '' ); + v = $row.children( 'th, td' ); + t = prevRowData.child.length; + prevRowData.child[ t ] = []; + // child row content does not account for colspans/rowspans; so indexing may be off + for ( j = 0; j < c.columns; j++ ) { + prevRowData.child[ t ][ j ] = getParsedText( c, v[ j ], j ); + } // go to the next for loop continue; } rowData.$row = $row; rowData.order = i; // add original row position to rowCache - for (j = 0; j < c.columns; ++j) { - if (typeof parsers[j] === 'undefined') { - if (c.debug) { - log('No parser found for cell:', $row[0].cells[j], 'does it have a header?'); + for ( j = 0; j < c.columns; ++j ) { + if (typeof parsers[ j ] === 'undefined') { + if ( c.debug ) { + log( 'No parser found for cell:', $row[ 0 ].cells[ j ], 'does it have a header?' ); } continue; } - t = ts.getElementText(c, $row[0].cells[j], j); - rowData.raw.push(t); // save original row text - // do extract before parsing if there is one - if (typeof extractors[j].id === 'undefined') { - tx = t; - } else { - tx = extractors[j].format(t, table, $row[0].cells[j], j); - } - // allow parsing if the string is empty, previously parsing would change it to zero, - // in case the parser needs to extract data from the table cell attributes - v = parsers[j].id === 'no-parser' ? '' : parsers[j].format(tx, table, $row[0].cells[j], j); - cols.push( c.ignoreCase && typeof v === 'string' ? v.toLowerCase() : v ); - if ((parsers[j].type || '').toLowerCase() === 'numeric') { + t = ts.getElementText( c, $row[ 0 ].cells[j], j ); + rowData.raw.push( t ); // save original row text + v = getParsedText( c, $row[ 0 ].cells[ j ], j, t ); + cols.push( v ); + if ( ( parsers[ j ].type || '' ).toLowerCase() === 'numeric' ) { // determine column max value (ignore sign) - colMax[j] = Math.max(Math.abs(v) || 0, colMax[j] || 0); + colMax[ j ] = Math.max( Math.abs( v ) || 0, colMax[ j ] || 0 ); } } // ensure rowData is always in the same location (after the last column) - cols[c.columns] = rowData; - cc.normalized.push(cols); + cols[ c.columns ] = rowData; + cc.normalized.push( cols ); } cc.colMax = colMax; // total up rows, not including child rows c.totalRows += cc.normalized.length; } - if (c.showProcessing) { - ts.isProcessing(table); // remove processing icon + if ( c.showProcessing ) { + ts.isProcessing( table ); // remove processing icon } - if (c.debug) { - benchmark('Building cache for ' + totalRows + ' rows', cacheTime); + if ( c.debug ) { + benchmark( 'Building cache for ' + totalRows + ' rows', cacheTime ); } } @@ -521,14 +554,15 @@ } function updateHeader(table) { - var s, $th, col, - c = table.config; - c.$headers.each(function(index, th){ - $th = $(th); + var index, s, $th, col, + c = table.config, + len = c.$headers.length; + for ( index = 0; index < len; index++ ) { + $th = c.$headers.eq( index ); col = ts.getColumnData( table, c.headers, index, true ); // add 'sorter-false' class if 'parser-false' is set - s = ts.getData( th, col, 'sorter' ) === 'false' || ts.getData( th, col, 'parser' ) === 'false'; - th.sortDisabled = s; + s = ts.getData( $th, col, 'sorter' ) === 'false' || ts.getData( $th, col, 'parser' ) === 'false'; + $th[0].sortDisabled = s; $th[ s ? 'addClass' : 'removeClass' ]('sorter-false').attr('aria-disabled', '' + s); // aria-controls - requires table ID if (table.id) { @@ -538,11 +572,11 @@ $th.attr('aria-controls', table.id); } } - }); + } } function setHeadersCss(table) { - var f, i, j, + var f, h, i, j, $headers, $h, nextSort, txt, c = table.config, list = c.sortList, len = list.length, @@ -586,14 +620,19 @@ } } // add verbose aria labels - c.$headers.not('.sorter-false').each(function(){ - var $this = $(this), - nextSort = this.order[(this.count + 1) % (c.sortReset ? 3 : 2)], - txt = $.trim( $this.text() ) + ': ' + - ts.language[ $this.hasClass(ts.css.sortAsc) ? 'sortAsc' : $this.hasClass(ts.css.sortDesc) ? 'sortDesc' : 'sortNone' ] + + len = c.$headers.length; + $headers = c.$headers.not('.sorter-false'); + for ( i = 0; i < len; i++ ) { + $h = $headers.eq( i ); + if ( $h.length ) { + h = $headers[ i ]; + nextSort = h.order[ ( h.count + 1 ) % ( c.sortReset ? 3 : 2 ) ], + txt = $.trim( $h.text() ) + ': ' + + ts.language[ $h.hasClass( ts.css.sortAsc ) ? 'sortAsc' : $h.hasClass( ts.css.sortDesc ) ? 'sortDesc' : 'sortNone' ] + ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ]; - $this.attr('aria-label', txt ); - }); + $h.attr( 'aria-label', txt ); + } + } } function updateHeaderSortCount( table, list ) { @@ -606,9 +645,10 @@ val = sortList[indx]; // ensure all sortList values are numeric - fixes #127 col = parseInt(val[0], 10); - // make sure header exists - header = c.$headerIndexed[col][0]; - if (header) { // prevents error if sorton array is wrong + // prevents error if sorton array is wrong + if ( col < c.columns && c.$headerIndexed[col] ) { + // make sure header exists + header = c.$headerIndexed[col][0]; // o.count = o.count + 1; dir = ('' + val[1]).match(/^(1|d|s|o|n)/); dir = dir ? dir[0] : ''; @@ -652,10 +692,11 @@ // let any updates complete before initializing a sort return setTimeout(function(){ initSort(table, cell, event); }, 50); } - var arry, indx, col, order, s, + var arry, indx, i, col, order, s, $header, c = table.config, key = !event[c.sortMultiSortKey], - $table = c.$table; + $table = c.$table, + len = c.$headers.length; // Only call sortStart if sorting is enabled $table.trigger('sortStart', table); // get current column sort order @@ -663,12 +704,13 @@ // reset all sorts on non-current column - issue #30 if (c.sortRestart) { indx = cell; - c.$headers.each(function() { + for ( i = 0; i < len; i++ ) { + $header = c.$headers.eq( i ); // only reset counts on columns that weren't just clicked on and if not included in a multisort - if (this !== indx && (key || !$(this).is('.' + ts.css.sortDesc + ',.' + ts.css.sortAsc))) { - this.count = -1; + if ( $header[0] !== indx && ( key || !$header.is('.' + ts.css.sortDesc + ',.' + ts.css.sortAsc) ) ) { + $header[0].count = -1; } - }); + } } // get current column index indx = parseInt( $(cell).attr('data-column'), 10 ); @@ -894,35 +936,31 @@ table.isUpdating = true; $table.find(c.selectorRemove).remove(); // get position from the dom - var v, t, row, icell, + var t, row, icell, cache, $tb = c.$tbodies, $cell = $(cell), // update cache - format: function(s, table, cell, cellIndex) // no closest in jQuery v1.2.6 - tbdy = $tb.index( $(cell).closest('tbody') ),$row = $(cell).closest('tr'); tbdy = $tb.index( $.fn.closest ? $cell.closest('tbody') : $cell.parents('tbody').filter(':first') ), + tbcache = c.cache[ tbdy ], $row = $.fn.closest ? $cell.closest('tr') : $cell.parents('tr').filter(':first'); cell = $cell[0]; // in case cell is a jQuery object // tbody may not exist if update is initialized while tbody is removed for processing if ($tb.length && tbdy >= 0) { - row = $tb.eq(tbdy).find('tr').index( $row ); + row = $tb.eq( tbdy ).find( 'tr' ).index( $row ); + cache = tbcache.normalized[ row ]; icell = $cell.index(); - c.cache[tbdy].normalized[row][c.columns].$row = $row; - if (typeof c.extractors[icell].id === 'undefined') { - t = ts.getElementText(c, cell, icell); - } else { - t = c.extractors[icell].format( ts.getElementText(c, cell, icell), table, cell, icell ); - } - v = c.parsers[icell].id === 'no-parser' ? '' : - c.parsers[icell].format( t, table, cell, icell ); - c.cache[tbdy].normalized[row][icell] = c.ignoreCase && typeof v === 'string' ? v.toLowerCase() : v; - if ((c.parsers[icell].type || '').toLowerCase() === 'numeric') { + t = getParsedText( c, cell, icell ); + cache[ icell ] = t; + cache[ c.columns ].$row = $row; + if ( (c.parsers[icell].type || '').toLowerCase() === 'numeric' ) { // update column max value (ignore sign) - c.cache[tbdy].colMax[icell] = Math.max(Math.abs(v) || 0, c.cache[tbdy].colMax[icell] || 0); + tbcache.colMax[icell] = Math.max(Math.abs(t) || 0, tbcache.colMax[icell] || 0); } - v = resort !== 'undefined' ? resort : c.resort; - if (v !== false) { + t = resort !== 'undefined' ? resort : c.resort; + if (t !== false) { // widgets will be reapplied - checkResort(c, v, callback); + checkResort(c, t, callback); } else { // don't reapply widgets is resort is false, just in case it causes // problems with element focus @@ -942,7 +980,7 @@ commonUpdate(table, resort, callback); } else { $row = $($row).attr('role', 'row'); // make sure we're using a jQuery object - var i, j, l, t, v, rowData, cells, + var i, j, l, rowData, cells, rows = $row.filter('tr').length, tbdy = c.$tbodies.index( $row.parents('tbody').filter(':first') ); // fixes adding rows to an empty table - see issue #179 @@ -960,14 +998,7 @@ }; // add each cell for (j = 0; j < l; j++) { - if (typeof c.extractors[j].id === 'undefined') { - t = ts.getElementText(c, $row[i].cells[j], j); - } else { - t = c.extractors[j].format( ts.getElementText(c, $row[i].cells[j], j), table, $row[i].cells[j], j ); - } - v = c.parsers[j].id === 'no-parser' ? '' : - c.parsers[j].format( t, table, $row[i].cells[j], j ); - cells[j] = c.ignoreCase && typeof v === 'string' ? v.toLowerCase() : v; + cells[j] = getParsedText( c, $row[i].cells[j], j ); if ((c.parsers[j].type || '').toLowerCase() === 'numeric') { // update column max value (ignore sign) c.cache[tbdy].colMax[j] = Math.max(Math.abs(cells[j]) || 0, c.cache[tbdy].colMax[j] || 0); @@ -1195,7 +1226,7 @@ // automatically add a colgroup with col elements set to a percentage width ts.fixColumnWidth = function(table) { table = $(table)[0]; - var overallWidth, percent, + var overallWidth, percent, $tbodies, len, index, c = table.config, colgroup = c.$table.children('colgroup'); // remove plugin-added colgroup, in case we need to refresh the widths @@ -1206,10 +1237,12 @@ colgroup = $('<colgroup class="' + ts.css.colgroup + '">'); overallWidth = c.$table.width(); // only add col for visible columns - fixes #371 - c.$tbodies.find('tr:first').children(':visible').each(function() { - percent = parseInt( ( $(this).width() / overallWidth ) * 1000, 10 ) / 10 + '%'; + $tbodies = c.$tbodies.find('tr:first').children(':visible'); //.each(function() + len = $tbodies.length; + for ( index = 0; index < len; index++ ) { + percent = parseInt( ( $tbodies.eq( index ).width() / overallWidth ) * 1000, 10 ) / 10 + '%'; colgroup.append( $('<col>').css('width', percent) ); - }); + } c.$table.prepend(colgroup); } }; @@ -1244,9 +1277,10 @@ // http://www.javascripttoolbox.com/lib/table/examples.php // http://www.javascripttoolbox.com/temp/table_cellindex.html ts.computeColumnIndex = function(trs) { - var matrix = [], - lookup = {}, - i, j, k, l, $cell, cell, cells, rowIndex, cellId, rowSpan, colSpan, firstAvailCol, matrixrow; + var i, j, k, l, $cell, cell, cells, rowIndex, cellId, rowSpan, colSpan, firstAvailCol, + matrix = [], + matrixrow = [], + lookup = {}; for (i = 0; i < trs.length; i++) { cells = trs[i].cells; for (j = 0; j < cells.length; j++) { @@ -1326,7 +1360,7 @@ $(table)[0].config.$tbodies.children().detach(); }; - ts.bindEvents = function(table, $headers, core){ + ts.bindEvents = function(table, $headers, core) { table = $(table)[0]; var t, downTarget = null, c = table.config; @@ -1337,28 +1371,35 @@ $(t).addClass( c.namespace.slice(1) + '_extra_table' ); } } + t = ( c.pointerDown + ' ' + c.pointerUp + ' ' + c.pointerClick + ' sort keyup ' ) + .replace(/\s+/g, ' ') + .split(' ') + .join(c.namespace + ' '); // apply event handling to headers and/or additional headers (stickyheaders, scroller, etc) $headers // http://stackoverflow.com/questions/5312849/jquery-find-self; .find(c.selectorSort).add( $headers.filter(c.selectorSort) ) - .unbind( ('mousedown mouseup click sort keyup '.split(' ').join(c.namespace + ' ')).replace(/\s+/g, ' ') ) - .bind( 'mousedown mouseup click sort keyup '.split(' ').join(c.namespace + ' '), function(e, external) { + .unbind(t) + .bind(t, function(e, external) { var cell, $target = $(e.target), - type = e.type; + // wrap event type in spaces, so the match doesn't trigger on inner words + type = ' ' + e.type + ' '; // only recognize left clicks - if ( ( ( e.which || e.button ) !== 1 && !/sort|keyup|click/.test(type) ) || + if ( ( ( e.which || e.button ) !== 1 && !type.match( ' ' + c.pointerClick + ' | sort | keyup ' ) ) || // allow pressing enter - ( type === 'keyup' && e.which !== 13 ) || + ( type === ' keyup ' && e.which !== 13 ) || // allow triggering a click event (e.which is undefined) & ignore physical clicks - ( type === 'click' && typeof e.which !== 'undefined' ) ) { + ( type.match(' ' + c.pointerClick + ' ') && typeof e.which !== 'undefined' ) ) { return; } // ignore mouseup if mousedown wasn't on the same target - if ( type === 'mouseup' && downTarget !== e.target && external !== true ) { return; } + if ( type.match(' ' + c.pointerUp + ' ') && downTarget !== e.target && external !== true ) { return; } // set timer on mousedown - if ( type === 'mousedown' ) { + if ( type.match(' ' + c.pointerDown + ' ') ) { downTarget = e.target; + // needed or jQuery v1.2.6 throws an error + e.preventDefault(); return; } downTarget = null; @@ -1393,17 +1434,20 @@ // restore headers ts.restoreHeaders = function(table){ - var $cell, - c = $(table)[0].config; + var index, $cell, + c = $(table)[0].config, + $headers = c.$table.find( c.selectorHeaders ), + len = $headers.length; // don't use c.$headers here in case header cells were swapped - c.$table.find(c.selectorHeaders).each(function(i){ - $cell = $(this); + for ( index = 0; index < len; index++ ) { + // c.$table.find(c.selectorHeaders).each(function(i){ + $cell = $headers.eq( index ); // only restore header cells if it is wrapped // because this is also used by the updateAll method - if ($cell.find('.' + ts.css.headerIn).length){ - $cell.html( c.headerContent[i] ); + if ( $cell.find( '.' + ts.css.headerIn ).length ) { + $cell.html( c.headerContent[ index ] ); } - }); + } }; ts.destroy = function(table, removeClasses, callback){ @@ -1563,8 +1607,8 @@ 'E' : '\u00c9\u00c8\u00ca\u00cb\u011a\u0118', // ÉÈÊËĚĘ 'i' : '\u00ed\u00ec\u0130\u00ee\u00ef\u0131', // íìİîïı 'I' : '\u00cd\u00cc\u0130\u00ce\u00cf', // ÍÌİÎÏ - 'o' : '\u00f3\u00f2\u00f4\u00f5\u00f6', // óòôõö - 'O' : '\u00d3\u00d2\u00d4\u00d5\u00d6', // ÓÒÔÕÖ + 'o' : '\u00f3\u00f2\u00f4\u00f5\u00f6\u014d', // óòôõöō + 'O' : '\u00d3\u00d2\u00d4\u00d5\u00d6\u014c', // ÓÒÔÕÖŌ 'ss': '\u00df', // ß (s sharp) 'SS': '\u1e9e', // ẞ (Capital sharp s) 'u' : '\u00fa\u00f9\u00fb\u00fc\u016f', // úùûüů @@ -1904,7 +1948,7 @@ ts.isDigit = function(s) { // replace all unwanted chars and match - return isNaN(s) ? (/^[\-+(]?\d+[)]?$/).test(s.toString().replace(/[,.'"\s]/g, '')) : true; + return isNaN(s) ? (/^[\-+(]?\d+[)]?$/).test(s.toString().replace(/[,.'"\s]/g, '')) : s !== ''; }; }() @@ -2090,7 +2134,7 @@ id: 'zebra', priority: 90, format: function(table, c, wo) { - var $tb, $tv, $tr, row, even, time, k, + var $tv, $tr, row, even, time, k, i, len, child = new RegExp(c.cssChildRow, 'i'), b = c.$tbodies.add( $( c.namespace + '_extra_table' ).children( 'tbody' ) ); if (c.debug) { @@ -2099,17 +2143,17 @@ for (k = 0; k < b.length; k++ ) { // loop through the visible rows row = 0; - $tb = b.eq(k); - $tv = $tb.children('tr:visible').not(c.selectorRemove); - // revered back to using jQuery each - strangely it's the fastest method - /*jshint loopfunc:true */ - $tv.each(function(){ - $tr = $(this); + $tv = b.eq( k ).children( 'tr:visible' ).not( c.selectorRemove ); + len = $tv.length; + for ( i = 0; i < len; i++ ) { + $tr = $tv.eq( i ); // style child rows the same way the parent row was styled - if (!child.test(this.className)) { row++; } - even = (row % 2 === 0); - $tr.removeClass(wo.zebra[even ? 1 : 0]).addClass(wo.zebra[even ? 0 : 1]); - }); + if ( !child.test( $tr[0].className ) ) { row++; } + even = ( row % 2 === 0 ); + $tr + .removeClass( wo.zebra[ even ? 1 : 0 ] ) + .addClass( wo.zebra[ even ? 0 : 1 ] ); + } } }, remove: function(table, c, wo, refreshing){ diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 04f4d04..abc55ea 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 04-08-2015 (v2.21.5)*/ +/*! tablesorter (FORK) - updated 05-17-2015 (v2.22.0)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -20,7 +20,7 @@ ;(function ($, window, document) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; +var ts = $.tablesorter || {}; // *** Store data in local storage, with a cookie fallback *** /* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json) if you need it, then include https://github.com/douglascrockford/JSON-js @@ -109,7 +109,7 @@ ts.storage = function(table, key, value, options) { /*! Widget: uitheme - updated 3/26/2015 (v2.21.3) */ ;(function ($) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; +var ts = $.tablesorter || {}; ts.themes = { 'bootstrap' : { @@ -295,7 +295,7 @@ ts.addWidget({ /*! Widget: columns */ ;(function ($) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; +var ts = $.tablesorter || {}; ts.addWidget({ id: "columns", @@ -371,16 +371,16 @@ ts.addWidget({ })(jQuery); -/*! Widget: filter - updated 3/26/2015 (v2.21.3) *//* +/*! Widget: filter - updated 5/17/2015 (v2.22.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ -;(function ($) { +;( function ( $ ) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}, +var ts = $.tablesorter || {}, tscss = ts.css; -$.extend(tscss, { +$.extend( tscss, { filterRow : 'tablesorter-filter-row', filter : 'tablesorter-filter', filterDisabled : 'disabled', @@ -388,26 +388,27 @@ $.extend(tscss, { }); ts.addWidget({ - id: "filter", + id: 'filter', priority: 50, options : { filter_childRows : false, // if true, filter includes child row content in the search + filter_childByColumn : false, // ( filter_childRows must be true ) if true = search child rows by column; false = search all child row text grouped filter_columnFilters : true, // if true, a filter will be added to the top of each table column - filter_columnAnyMatch: true, // if true, allows using "#:{query}" in AnyMatch searches (column:query) - filter_cellFilter : '', // css class name added to the filter cell (string or array) - filter_cssFilter : '', // css class name added to the filter row & each input in the row (tablesorter-filter is ALWAYS added) - filter_defaultFilter : {}, // add a default column filter type "~{query}" to make fuzzy searches default; "{q1} AND {q2}" to make all searches use a logical AND. + filter_columnAnyMatch: true, // if true, allows using '#:{query}' in AnyMatch searches ( column:query ) + filter_cellFilter : '', // css class name added to the filter cell ( string or array ) + filter_cssFilter : '', // css class name added to the filter row & each input in the row ( tablesorter-filter is ALWAYS added ) + filter_defaultFilter : {}, // add a default column filter type '~{query}' to make fuzzy searches default; '{q1} AND {q2}' to make all searches use a logical AND. filter_excludeFilter : {}, // filters to exclude, per column - filter_external : '', // jQuery selector string (or jQuery object) of external filters + filter_external : '', // jQuery selector string ( or jQuery object ) of external filters filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin filter_formatter : null, // add custom filter elements to the filter row filter_functions : null, // add custom filter functions using this option filter_hideEmpty : true, // hide filter row when table is empty filter_hideFilters : false, // collapse filter row when mouse leaves the area filter_ignoreCase : true, // if true, make all searches case-insensitive - filter_liveSearch : true, // if true, search column content while the user types (with a delay) - filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available (visible) options within the drop down - filter_placeholder : { search : '', select : '' }, // default placeholder text (overridden by any header "data-placeholder" setting) + filter_liveSearch : true, // if true, search column content while the user types ( with a delay ) + filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available ( visible ) options within the drop down + filter_placeholder : { search : '', select : '' }, // default placeholder text ( overridden by any header 'data-placeholder' setting ) filter_reset : null, // jQuery selector string of an element used to reset the filters filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters filter_searchDelay : 300, // typing delay in milliseconds before starting a search @@ -419,37 +420,38 @@ ts.addWidget({ filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value filter_selectSourceSeparator : '|' // filter_selectSource array text left of the separator is added to the option value, right into the option text }, - format: function(table, c, wo) { - if (!c.$table.hasClass('hasFilters')) { - ts.filter.init(table, c, wo); + format: function( table, c, wo ) { + if ( !c.$table.hasClass( 'hasFilters' ) ) { + ts.filter.init( table, c, wo ); } }, - remove: function(table, c, wo, refreshing) { + remove: function( table, c, wo, refreshing ) { var tbodyIndex, $tbody, $table = c.$table, $tbodies = c.$tbodies, - events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter '); + events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' + .split( ' ' ).join( c.namespace + 'filter ' ); $table - .removeClass('hasFilters') + .removeClass( 'hasFilters' ) // add .tsfilter namespace to all BUT search - .unbind( events.replace(/\s+/g, ' ') ) + .unbind( events.replace( /\s+/g, ' ' ) ) // remove the filter row even if refreshing, because the column might have been moved - .find('.' + tscss.filterRow).remove(); - if (refreshing) { return; } - for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { - $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody - $tbody.children().removeClass(wo.filter_filteredRow).show(); - ts.processTbody(table, $tbody, false); // restore tbody + .find( '.' + tscss.filterRow ).remove(); + if ( refreshing ) { return; } + for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody( table, $tbodies.eq( tbodyIndex ), true ); // remove tbody + $tbody.children().removeClass( wo.filter_filteredRow ).show(); + ts.processTbody( table, $tbody, false ); // restore tbody } - if (wo.filter_reset) { - $(document).undelegate(wo.filter_reset, 'click.tsfilter'); + if ( wo.filter_reset ) { + $( document ).undelegate( wo.filter_reset, 'click.tsfilter' ); } } }); ts.filter = { - // regex used in filter "check" functions - not for general use and not documented + // regex used in filter 'check' functions - not for general use and not documented regex: { regex : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // regex to test for regex child : /tablesorter-childRow/, // child row class name; this gets updated in the script @@ -462,22 +464,33 @@ ts.filter = { }, // function( c, data ) { } // c = table.config - // data.filter = array of filter input values; - // data.iFilter = same array, except lowercase (if wo.filter_ignoreCase is true) - // data.exact = table cell text (or parsed data if column parser enabled) - // data.iExact = same as data.exact, except lowercase (if wo.filter_ignoreCase is true) - // data.cache = table cell text from cache, so it has been parsed (& in all lower case if config.ignoreCase is true) - // data.index = column index; table = table element (DOM) - // data.parsed = array (by column) of boolean values (from filter_useParsedData or "filter-parsed" class) + // data.$row = jQuery object of the row currently being processed + // data.$cells = jQuery object of all cells within the current row + // data.filters = array of filters for all columns ( some may be undefined ) + // data.filter = filter for the current column + // data.iFilter = same as data.filter, except lowercase ( if wo.filter_ignoreCase is true ) + // data.exact = table cell text ( or parsed data if column parser enabled ) + // data.iExact = same as data.exact, except lowercase ( if wo.filter_ignoreCase is true ) + // data.cache = table cell text from cache, so it has been parsed ( & in all lower case if c.ignoreCase is true ) + // data.cacheArray = An array of parsed content from each table cell in the row being processed + // data.index = column index; table = table element ( DOM ) + // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class ) types: { // Look for regex regex: function( c, data ) { - if ( ts.filter.regex.regex.test(data.iFilter) ) { + if ( ts.filter.regex.regex.test( data.filter ) ) { var matches, - regex = ts.filter.regex.regex.exec(data.iFilter); + // cache regex per column for optimal speed + regex = data.filter_regexCache[ data.index ] || ts.filter.regex.regex.exec( data.filter ), + isRegex = regex instanceof RegExp; try { - matches = new RegExp(regex[1], regex[2]).test( data.iExact ); - } catch (error) { + if ( !isRegex ) { + // force case insensitive search if ignoreCase option set? + // if ( c.ignoreCase && !regex[2] ) { regex[2] = 'i'; } + data.filter_regexCache[ data.index ] = regex = new RegExp( regex[1], regex[2] ); + } + matches = regex.test( data.exact ); + } catch ( error ) { matches = false; } return matches; @@ -486,46 +499,56 @@ ts.filter = { }, // Look for operators >, >=, < or <= operators: function( c, data ) { - if ( /^[<>]=?/.test(data.iFilter) ) { - var cachedValue, result, + // ignore empty strings... because '' < 10 is true + if ( /^[<>]=?/.test( data.iFilter ) && data.iExact !== '' ) { + var cachedValue, result, txt, table = c.table, index = data.index, parsed = data.parsed[index], - query = ts.formatFloat( data.iFilter.replace(ts.filter.regex.operators, ''), table ), + query = ts.formatFloat( data.iFilter.replace( ts.filter.regex.operators, '' ), table ), parser = c.parsers[index], savedSearch = query; - // parse filter value in case we're comparing numbers (dates) - if (parsed || parser.type === 'numeric') { - result = ts.filter.parseFilter(c, $.trim('' + data.iFilter.replace(ts.filter.regex.operators, '')), index, parsed, true); - query = ( typeof result === "number" && result !== '' && !isNaN(result) ) ? result : query; + // parse filter value in case we're comparing numbers ( dates ) + if ( parsed || parser.type === 'numeric' ) { + txt = $.trim( '' + data.iFilter.replace( ts.filter.regex.operators, '' ) ); + result = ts.filter.parseFilter( c, txt, index, true ); + query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query; } - // iExact may be numeric - see issue #149; - // check if cached is defined, because sometimes j goes out of range? (numeric columns) - cachedValue = ( parsed || parser.type === 'numeric' ) && !isNaN(query) && typeof data.cache !== 'undefined' ? data.cache : - isNaN(data.iExact) ? ts.formatFloat( data.iExact.replace(ts.filter.regex.nondigit, ''), table) : - ts.formatFloat( data.iExact, table ); - - if ( />/.test(data.iFilter) ) { result = />=/.test(data.iFilter) ? cachedValue >= query : cachedValue > query; } - if ( /</.test(data.iFilter) ) { result = /<=/.test(data.iFilter) ? cachedValue <= query : cachedValue < query; } + // check if cached is defined, because sometimes j goes out of range? ( numeric columns ) + if ( ( parsed || parser.type === 'numeric' ) && !isNaN( query ) && + typeof data.cache !== 'undefined' ) { + cachedValue = data.cache; + } else { + txt = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; + cachedValue = ts.formatFloat( txt, table ); + } + if ( />/.test( data.iFilter ) ) { + result = />=/.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; + } else if ( /</.test( data.iFilter ) ) { + result = /<=/.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; + } // keep showing all rows if nothing follows the operator - if ( !result && savedSearch === '' ) { result = true; } + if ( !result && savedSearch === '' ) { + result = true; + } return result; } return null; }, // Look for a not match notMatch: function( c, data ) { - if ( /^\!/.test(data.iFilter) ) { + if ( /^\!/.test( data.iFilter ) ) { var indx, - filter = ts.filter.parseFilter(c, data.iFilter.replace('!', ''), data.index, data.parsed[data.index]) || ''; - if (ts.filter.regex.exact.test(filter)) { + txt = data.iFilter.replace( '!', '' ), + filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + if ( ts.filter.regex.exact.test( filter ) ) { // look for exact not matches - see #628 - filter = filter.replace(ts.filter.regex.exact, ''); - return filter === '' ? true : $.trim(filter) !== data.iExact; + filter = filter.replace( ts.filter.regex.exact, '' ); + return filter === '' ? true : $.trim( filter ) !== data.iExact; } else { - indx = data.iExact.search( $.trim(filter) ); - return filter === '' ? true : !(c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0); + indx = data.iExact.search( $.trim( filter ) ); + return filter === '' ? true : !( c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0 ); } } return null; @@ -533,84 +556,101 @@ ts.filter = { // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric exact: function( c, data ) { /*jshint eqeqeq:false */ - if (ts.filter.regex.exact.test(data.iFilter)) { - var filter = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.exact, ''), data.index, data.parsed[data.index]) || ''; - return data.anyMatch ? $.inArray(filter, data.rowArray) >= 0 : filter == data.iExact; + if ( ts.filter.regex.exact.test( data.iFilter ) ) { + var txt = data.iFilter.replace( ts.filter.regex.exact, '' ), + filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact; } return null; }, - // Look for an AND or && operator (logical and) + // Look for an AND or && operator ( logical and ) and : function( c, data ) { - if ( ts.filter.regex.andTest.test(data.filter) ) { + if ( ts.filter.regex.andTest.test( data.filter ) ) { var index = data.index, parsed = data.parsed[index], query = data.iFilter.split( ts.filter.regex.andSplit ), - result = data.iExact.search( $.trim( ts.filter.parseFilter(c, query[0], index, parsed) ) ) >= 0, + result = data.iExact.search( $.trim( ts.filter.parseFilter( c, query[0], index, parsed ) ) ) >= 0, indx = query.length - 1; - while (result && indx) { - result = result && data.iExact.search( $.trim( ts.filter.parseFilter(c, query[indx], index, parsed) ) ) >= 0; + while ( result && indx ) { + result = result && + data.iExact.search( $.trim( ts.filter.parseFilter( c, query[indx], index, parsed ) ) ) >= 0; indx--; } return result; } return null; }, - // Look for a range (using " to " or " - ") - see issue #166; thanks matzhu! + // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu! range : function( c, data ) { - if ( ts.filter.regex.toTest.test(data.iFilter) ) { - var result, tmp, + if ( ts.filter.regex.toTest.test( data.iFilter ) ) { + var result, tmp, range1, range2, table = c.table, index = data.index, parsed = data.parsed[index], // make sure the dash is for a range and not indicating a negative number - query = data.iFilter.split( ts.filter.regex.toSplit ), - range1 = ts.formatFloat( ts.filter.parseFilter(c, query[0].replace(ts.filter.regex.nondigit, '') || '', index, parsed), table ), - range2 = ts.formatFloat( ts.filter.parseFilter(c, query[1].replace(ts.filter.regex.nondigit, '') || '', index, parsed), table ); - // parse filter value in case we're comparing numbers (dates) - if (parsed || c.parsers[index].type === 'numeric') { - result = c.parsers[index].format('' + query[0], table, c.$headers.eq(index), index); - range1 = (result !== '' && !isNaN(result)) ? result : range1; - result = c.parsers[index].format('' + query[1], table, c.$headers.eq(index), index); - range2 = (result !== '' && !isNaN(result)) ? result : range2; + query = data.iFilter.split( ts.filter.regex.toSplit ); + + tmp = query[0].replace( ts.filter.regex.nondigit, '' ) || ''; + range1 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); + tmp = query[1].replace( ts.filter.regex.nondigit, '' ) || ''; + range2 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); + // parse filter value in case we're comparing numbers ( dates ) + if ( parsed || c.parsers[index].type === 'numeric' ) { + result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index ); + range1 = ( result !== '' && !isNaN( result ) ) ? result : range1; + result = c.parsers[ index ].format( '' + query[1], table, c.$headers.eq( index ), index ); + range2 = ( result !== '' && !isNaN( result ) ) ? result : range2; } - result = ( parsed || c.parsers[index].type === 'numeric' ) && !isNaN(range1) && !isNaN(range2) ? data.cache : - isNaN(data.iExact) ? ts.formatFloat( data.iExact.replace(ts.filter.regex.nondigit, ''), table) : - ts.formatFloat( data.iExact, table ); - if (range1 > range2) { tmp = range1; range1 = range2; range2 = tmp; } // swap - return (result >= range1 && result <= range2) || (range1 === '' || range2 === ''); + if ( ( parsed || c.parsers[ index ].type === 'numeric' ) && !isNaN( range1 ) && !isNaN( range2 ) ) { + result = data.cache; + } else { + tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; + result = ts.formatFloat( tmp, table ); + } + if ( range1 > range2 ) { + tmp = range1; range1 = range2; range2 = tmp; // swap + } + return ( result >= range1 && result <= range2 ) || ( range1 === '' || range2 === '' ); } return null; }, // Look for wild card: ? = single, * = multiple, or | = logical OR wild : function( c, data ) { - if ( /[\?\*\|]/.test(data.iFilter) || ts.filter.regex.orReplace.test(data.filter) ) { + if ( /[\?\*\|]/.test( data.iFilter ) || ts.filter.regex.orReplace.test( data.filter ) ) { var index = data.index, - parsed = data.parsed[index], - query = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.orReplace, "|"), index, parsed) || ''; - // look for an exact match with the "or" unless the "filter-match" class is found - if (!c.$headerIndexed[index].hasClass('filter-match') && /\|/.test(query)) { + parsed = data.parsed[ index ], + txt = data.iFilter.replace( ts.filter.regex.orReplace, '|' ), + query = ts.filter.parseFilter( c, txt, index, parsed ) || ''; + // look for an exact match with the 'or' unless the 'filter-match' class is found + if ( !c.$headerIndexed[ index ].hasClass( 'filter-match' ) && /\|/.test( query ) ) { // show all results while using filter match. Fixes #727 - if (query[ query.length - 1 ] === '|') { query += '*'; } - query = data.anyMatch && $.isArray(data.rowArray) ? '(' + query + ')' : '^(' + query + ')$'; + if ( query[ query.length - 1 ] === '|' ) { + query += '*'; + } + query = data.anyMatch && $.isArray( data.rowArray ) ? + '(' + query + ')' : + '^(' + query + ')$'; } // parsing the filter may not work properly when using wildcards =/ - return new RegExp( query.replace(/\?/g, '\\S{1}').replace(/\*/g, '\\S*') ).test(data.iExact); + return new RegExp( query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ) ) + .test( data.iExact ); } return null; }, - // fuzzy text search; modified from https://github.com/mattyork/fuzzy (MIT license) + // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license ) fuzzy: function( c, data ) { - if ( /^~/.test(data.iFilter) ) { + if ( /^~/.test( data.iFilter ) ) { var indx, patternIndx = 0, len = data.iExact.length, - pattern = ts.filter.parseFilter(c, data.iFilter.slice(1), data.index, data.parsed[data.index]) || ''; - for (indx = 0; indx < len; indx++) { - if (data.iExact[indx] === pattern[patternIndx]) { + txt = data.iFilter.slice( 1 ), + pattern = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + for ( indx = 0; indx < len; indx++ ) { + if ( data.iExact[ indx ] === pattern[ patternIndx ] ) { patternIndx += 1; } } - if (patternIndx === pattern.length) { + if ( patternIndx === pattern.length ) { return true; } return false; @@ -618,17 +658,17 @@ ts.filter = { return null; } }, - init: function(table, c, wo) { + init: function( table, c, wo ) { // filter language options - ts.language = $.extend(true, {}, { + ts.language = $.extend( true, {}, { to : 'to', or : 'or', and : 'and' - }, ts.language); + }, ts.language ); var options, string, txt, $header, column, filters, val, fxn, noSelect, regex = ts.filter.regex; - c.$table.addClass('hasFilters'); + c.$table.addClass( 'hasFilters' ); // define timers so using clearTimeout won't cause an undefined error wo.searchTimer = null; @@ -638,512 +678,566 @@ ts.filter = { wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; - txt = '\\{' + ts.filter.regex.query + '\\}'; + val = '\\{' + ts.filter.regex.query + '\\}'; $.extend( regex, { - child : new RegExp(c.cssChildRow), - filtered : new RegExp(wo.filter_filteredRow), - alreadyFiltered : new RegExp('(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i'), - toTest : new RegExp('\\s+(-|' + ts.language.to + ')\\s+', 'i'), - toSplit : new RegExp('(?:\\s+(?:-|' + ts.language.to + ')\\s+)' ,'gi'), - andTest : new RegExp('\\s+(' + ts.language.and + '|&&)\\s+', 'i'), - andSplit : new RegExp('(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi'), - orReplace : new RegExp('\\s+(' + ts.language.or + ')\\s+', 'gi'), - iQuery : new RegExp(txt, 'i'), - igQuery : new RegExp(txt, 'ig') + child : new RegExp( c.cssChildRow ), + filtered : new RegExp( wo.filter_filteredRow ), + alreadyFiltered : new RegExp( '(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i' ), + toTest : new RegExp( '\\s+(-|' + ts.language.to + ')\\s+', 'i' ), + toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)' ,'gi' ), + andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), + andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), + orReplace : new RegExp( '\\s+(' + ts.language.or + ')\\s+', 'gi' ), + iQuery : new RegExp( val, 'i' ), + igQuery : new RegExp( val, 'ig' ) }); - // don't build filter row if columnFilters is false or all columns are set to "filter-false" - issue #156 - if (wo.filter_columnFilters !== false && c.$headers.filter('.filter-false, .parser-false').length !== c.$headers.length) { + // don't build filter row if columnFilters is false or all columns are set to 'filter-false' + // see issue #156 + val = c.$headers.filter( '.filter-false, .parser-false' ).length; + if ( wo.filter_columnFilters !== false && val !== c.$headers.length ) { // build filter row - ts.filter.buildRow(table, c, wo); - } - - txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter '); - c.$table.bind( txt, function(event, filter) { - val = (wo.filter_hideEmpty && $.isEmptyObject(c.cache) && !(c.delayInit && event.type === 'appendCache')); - // hide filter row using the "filtered" class name - c.$table.find('.' + tscss.filterRow).toggleClass(wo.filter_filteredRow, val ); // fixes #450 - if ( !/(search|filter)/.test(event.type) ) { + ts.filter.buildRow( table, c, wo ); + } + + txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' + .split( ' ' ).join( c.namespace + 'filter ' ); + c.$table.bind( txt, function( event, filter ) { + val = wo.filter_hideEmpty && + $.isEmptyObject( c.cache ) && + !( c.delayInit && event.type === 'appendCache' ); + // hide filter row using the 'filtered' class name + c.$table.find( '.' + tscss.filterRow ).toggleClass( wo.filter_filteredRow, val ); // fixes #450 + if ( !/(search|filter)/.test( event.type ) ) { event.stopPropagation(); - ts.filter.buildDefault(table, true); + ts.filter.buildDefault( table, true ); } - if (event.type === 'filterReset') { - c.$table.find('.' + tscss.filter).add(wo.filter_$externalFilters).val(''); - ts.filter.searching(table, []); - } else if (event.type === 'filterEnd') { - ts.filter.buildDefault(table, true); + if ( event.type === 'filterReset' ) { + c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' ); + ts.filter.searching( table, [] ); + } else if ( event.type === 'filterEnd' ) { + ts.filter.buildDefault( table, true ); } else { - // send false argument to force a new search; otherwise if the filter hasn't changed, it will return - filter = event.type === 'search' ? filter : event.type === 'updateComplete' ? c.$table.data('lastSearch') : ''; - if (/(update|add)/.test(event.type) && event.type !== "updateComplete") { + // send false argument to force a new search; otherwise if the filter hasn't changed, + // it will return + filter = event.type === 'search' ? filter : + event.type === 'updateComplete' ? c.$table.data( 'lastSearch' ) : ''; + if ( /(update|add)/.test( event.type ) && event.type !== 'updateComplete' ) { // force a new search since content has changed c.lastCombinedFilter = null; c.lastSearch = []; } - // pass true (skipFirst) to prevent the tablesorter.setFilters function from skipping the first input - // ensures all inputs are updated when a search is triggered on the table $('table').trigger('search', [...]); - ts.filter.searching(table, filter, true); + // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first + // input ensures all inputs are updated when a search is triggered on the table + // $( 'table' ).trigger( 'search', [...] ); + ts.filter.searching( table, filter, true ); } return false; }); // reset button/link - if (wo.filter_reset) { - if (wo.filter_reset instanceof $) { + if ( wo.filter_reset ) { + if ( wo.filter_reset instanceof $ ) { // reset contains a jQuery object, bind to it - wo.filter_reset.click(function(){ - c.$table.trigger('filterReset'); + wo.filter_reset.click( function() { + c.$table.trigger( 'filterReset' ); }); - } else if ($(wo.filter_reset).length) { + } else if ( $( wo.filter_reset ).length ) { // reset is a jQuery selector, use event delegation - $(document) - .undelegate(wo.filter_reset, 'click.tsfilter') - .delegate(wo.filter_reset, 'click.tsfilter', function() { - // trigger a reset event, so other functions (filter_formatter) know when to reset - c.$table.trigger('filterReset'); - }); + $( document ) + .undelegate( wo.filter_reset, 'click.tsfilter' ) + .delegate( wo.filter_reset, 'click.tsfilter', function() { + // trigger a reset event, so other functions ( filter_formatter ) know when to reset + c.$table.trigger( 'filterReset' ); + }); } } - if (wo.filter_functions) { - for (column = 0; column < c.columns; column++) { + if ( wo.filter_functions ) { + for ( column = 0; column < c.columns; column++ ) { fxn = ts.getColumnData( table, wo.filter_functions, column ); - if (fxn) { - // remove "filter-select" from header otherwise the options added here are replaced with all options - $header = c.$headerIndexed[column].removeClass('filter-select'); - // don't build select if "filter-false" or "parser-false" set - noSelect = !($header.hasClass('filter-false') || $header.hasClass('parser-false')); + if ( fxn ) { + // remove 'filter-select' from header otherwise the options added here are replaced with + // all options + $header = c.$headerIndexed[ column ].removeClass( 'filter-select' ); + // don't build select if 'filter-false' or 'parser-false' set + noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); options = ''; if ( fxn === true && noSelect ) { - ts.filter.buildSelect(table, column); + ts.filter.buildSelect( table, column ); } else if ( typeof fxn === 'object' && noSelect ) { // add custom drop down list - for (string in fxn) { - if (typeof string === 'string') { + for ( string in fxn ) { + if ( typeof string === 'string' ) { options += options === '' ? - '<option value="">' + ($header.data('placeholder') || $header.attr('data-placeholder') || wo.filter_placeholder.select || '') + '</option>' : ''; + '<option value="">' + + ( $header.data( 'placeholder' ) || + $header.attr( 'data-placeholder' ) || + wo.filter_placeholder.select || + '' + ) + + '</option>' : ''; val = string; txt = string; - if (string.indexOf(wo.filter_selectSourceSeparator) >= 0) { - val = string.split(wo.filter_selectSourceSeparator); + if ( string.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { + val = string.split( wo.filter_selectSourceSeparator ); txt = val[1]; val = val[0]; } - options += '<option ' + (txt === val ? '' : 'data-function-name="' + string + '" ') + 'value="' + val + '">' + txt + '</option>'; + options += '<option ' + + ( txt === val ? '' : 'data-function-name="' + string + '" ' ) + + 'value="' + val + '">' + txt + '</option>'; } } - c.$table.find('thead').find('select.' + tscss.filter + '[data-column="' + column + '"]').append(options); + c.$table + .find( 'thead' ) + .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) + .append( options ); txt = wo.filter_selectSource; - fxn = $.isFunction(txt) ? true : ts.getColumnData( table, txt, column ); - if (fxn) { + fxn = $.isFunction( txt ) ? true : ts.getColumnData( table, txt, column ); + if ( fxn ) { // updating so the extra options are appended - ts.filter.buildSelect(c.table, column, '', true, $header.hasClass(wo.filter_onlyAvail)); + ts.filter.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) ); } } } } } - // not really updating, but if the column has both the "filter-select" class & filter_functions set to true, - // it would append the same options twice. - ts.filter.buildDefault(table, true); + // not really updating, but if the column has both the 'filter-select' class & + // filter_functions set to true, it would append the same options twice. + ts.filter.buildDefault( table, true ); - ts.filter.bindSearch( table, c.$table.find('.' + tscss.filter), true ); - if (wo.filter_external) { + ts.filter.bindSearch( table, c.$table.find( '.' + tscss.filter ), true ); + if ( wo.filter_external ) { ts.filter.bindSearch( table, wo.filter_external ); } - if (wo.filter_hideFilters) { - ts.filter.hideFilters(table, c); + if ( wo.filter_hideFilters ) { + ts.filter.hideFilters( table, c ); } // show processing icon - if (c.showProcessing) { + if ( c.showProcessing ) { + txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter ' ); c.$table - .unbind( ('filterStart filterEnd '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') ) - .bind( 'filterStart filterEnd '.split(' ').join(c.namespace + 'filter '), function(event, columns) { + .unbind( txt.replace( /\s+/g, ' ' ) ) + .bind( txt, function( event, columns ) { // only add processing to certain columns to all columns - $header = (columns) ? c.$table.find('.' + tscss.header).filter('[data-column]').filter(function() { - return columns[$(this).data('column')] !== ''; - }) : ''; - ts.isProcessing(table, event.type === 'filterStart', columns ? $header : ''); + $header = ( columns ) ? + c.$table + .find( '.' + tscss.header ) + .filter( '[data-column]' ) + .filter( function() { + return columns[ $( this ).data( 'column' ) ] !== ''; + }) : ''; + ts.isProcessing( table, event.type === 'filterStart', columns ? $header : '' ); }); } - // set filtered rows count (intially unfiltered) + // set filtered rows count ( intially unfiltered ) c.filteredRows = c.totalRows; // add default values + txt = 'tablesorter-initialized pagerBeforeInitialized '.split( ' ' ).join( c.namespace + 'filter ' ); c.$table - .unbind( ('tablesorter-initialized pagerBeforeInitialized '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') ) - .bind( 'tablesorter-initialized pagerBeforeInitialized '.split(' ').join(c.namespace + 'filter '), function() { - // redefine "wo" as it does not update properly inside this callback + .unbind( txt.replace( /\s+/g, ' ' ) ) + .bind( txt, function() { + // redefine 'wo' as it does not update properly inside this callback var wo = this.config.widgetOptions; - filters = ts.filter.setDefaults(table, c, wo) || []; - if (filters.length) { + filters = ts.filter.setDefaults( table, c, wo ) || []; + if ( filters.length ) { // prevent delayInit from triggering a cache build if filters are empty - if ( !(c.delayInit && filters.join('') === '') ) { - ts.setFilters(table, filters, true); + if ( !( c.delayInit && filters.join( '' ) === '' ) ) { + ts.setFilters( table, filters, true ); } } - c.$table.trigger('filterFomatterUpdate'); + c.$table.trigger( 'filterFomatterUpdate' ); // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers - setTimeout(function(){ - if (!wo.filter_initialized) { - ts.filter.filterInitComplete(c); + setTimeout( function() { + if ( !wo.filter_initialized ) { + ts.filter.filterInitComplete( c ); } - }, 100); + }, 100 ); }); // if filter widget is added after pager has initialized; then set filter init flag - if (c.pager && c.pager.initialized && !wo.filter_initialized) { - c.$table.trigger('filterFomatterUpdate'); - setTimeout(function(){ - ts.filter.filterInitComplete(c); - }, 100); + if ( c.pager && c.pager.initialized && !wo.filter_initialized ) { + c.$table.trigger( 'filterFomatterUpdate' ); + setTimeout( function() { + ts.filter.filterInitComplete( c ); + }, 100 ); } }, - // $cell parameter, but not the config, is passed to the - // filter_formatters, so we have to work with it instead - formatterUpdated: function($cell, column) { - var wo = $cell.closest('table')[0].config.widgetOptions; - if (!wo.filter_initialized) { + // $cell parameter, but not the config, is passed to the filter_formatters, + // so we have to work with it instead + formatterUpdated: function( $cell, column ) { + var wo = $cell.closest( 'table' )[0].config.widgetOptions; + if ( !wo.filter_initialized ) { // add updates by column since this function // may be called numerous times before initialization - wo.filter_formatterInit[column] = 1; + wo.filter_formatterInit[ column ] = 1; } }, - filterInitComplete: function(c){ + filterInitComplete: function( c ) { var indx, len, wo = c.widgetOptions, count = 0, - completed = function(){ + completed = function() { wo.filter_initialized = true; - c.$table.trigger('filterInit', c); - ts.filter.findRows(c.table, c.$table.data('lastSearch') || []); + c.$table.trigger( 'filterInit', c ); + ts.filter.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); }; if ( $.isEmptyObject( wo.filter_formatter ) ) { completed(); } else { len = wo.filter_formatterInit.length; - for (indx = 0; indx < len; indx++) { - if (wo.filter_formatterInit[indx] === 1) { + for ( indx = 0; indx < len; indx++ ) { + if ( wo.filter_formatterInit[ indx ] === 1 ) { count++; } } - clearTimeout(wo.filter_initTimer); - if (!wo.filter_initialized && count === wo.filter_formatterCount) { + clearTimeout( wo.filter_initTimer ); + if ( !wo.filter_initialized && count === wo.filter_formatterCount ) { // filter widget initialized completed(); - } else if (!wo.filter_initialized) { + } else if ( !wo.filter_initialized ) { // fall back in case a filter_formatter doesn't call - // $.tablesorter.filter.formatterUpdated($cell, column), and the count is off - wo.filter_initTimer = setTimeout(function(){ + // $.tablesorter.filter.formatterUpdated( $cell, column ), and the count is off + wo.filter_initTimer = setTimeout( function() { completed(); - }, 500); + }, 500 ); } } }, - setDefaults: function(table, c, wo) { + setDefaults: function( table, c, wo ) { var isArray, saved, indx, col, $filters, - // get current (default) filters - filters = ts.getFilters(table) || []; - if (wo.filter_saveFilters && ts.storage) { + // get current ( default ) filters + filters = ts.getFilters( table ) || []; + if ( wo.filter_saveFilters && ts.storage ) { saved = ts.storage( table, 'tablesorter-filters' ) || []; - isArray = $.isArray(saved); + isArray = $.isArray( saved ); // make sure we're not just getting an empty array - if ( !(isArray && saved.join('') === '' || !isArray) ) { filters = saved; } + if ( !( isArray && saved.join( '' ) === '' || !isArray ) ) { + filters = saved; + } } // if no filters saved, then check default settings - if (filters.join('') === '') { + if ( filters.join( '' ) === '' ) { // allow adding default setting to external filters - $filters = c.$headers.add( wo.filter_$externalFilters ).filter('[' + wo.filter_defaultAttrib + ']'); - for (indx = 0; indx <= c.columns; indx++) { - // include data-column="all" external filters + $filters = c.$headers.add( wo.filter_$externalFilters ) + .filter( '[' + wo.filter_defaultAttrib + ']' ); + for ( indx = 0; indx <= c.columns; indx++ ) { + // include data-column='all' external filters col = indx === c.columns ? 'all' : indx; - filters[indx] = $filters.filter('[data-column="' + col + '"]').attr(wo.filter_defaultAttrib) || filters[indx] || ''; + filters[indx] = $filters + .filter( '[data-column="' + col + '"]' ) + .attr( wo.filter_defaultAttrib ) || filters[indx] || ''; } } - c.$table.data('lastSearch', filters); + c.$table.data( 'lastSearch', filters ); return filters; }, - parseFilter: function(c, filter, column, parsed, forceParse){ - return forceParse || parsed ? - c.parsers[column].format( filter, c.table, [], column ) : - filter; + parseFilter: function( c, filter, column, parsed ) { + return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter; }, - buildRow: function(table, c, wo) { - var col, column, $header, buildSelect, disabled, name, ffxn, + buildRow: function( table, c, wo ) { + var col, column, $header, buildSelect, disabled, name, ffxn, tmp, // c.columns defined in computeThIndexes() + cellFilter = wo.filter_cellFilter, columns = c.columns, - arry = $.isArray(wo.filter_cellFilter), + arry = $.isArray( cellFilter ), buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; - for (column = 0; column < columns; column++) { - if (arry) { - buildFilter += '<td' + ( wo.filter_cellFilter[column] ? ' class="' + wo.filter_cellFilter[column] + '"' : '' ) + '></td>'; + for ( column = 0; column < columns; column++ ) { + buildFilter += '<td'; + if ( arry ) { + buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' ); } else { - buildFilter += '<td' + ( wo.filter_cellFilter !== '' ? ' class="' + wo.filter_cellFilter + '"' : '' ) + '></td>'; + buildFilter += ( cellFilter !== '' ? ' class="' + cellFilter + '"' : '' ); } + buildFilter += '></td>'; } - c.$filters = $(buildFilter += '</tr>').appendTo( c.$table.children('thead').eq(0) ).find('td'); + c.$filters = $( buildFilter += '</tr>' ) + .appendTo( c.$table.children( 'thead' ).eq( 0 ) ) + .find( 'td' ); // build each filter input - for (column = 0; column < columns; column++) { + for ( column = 0; column < columns; column++ ) { disabled = false; // assuming last cell of a column is the main column - $header = c.$headerIndexed[column]; + $header = c.$headerIndexed[ column ]; ffxn = ts.getColumnData( table, wo.filter_functions, column ); - buildSelect = (wo.filter_functions && ffxn && typeof ffxn !== "function" ) || - $header.hasClass('filter-select'); + buildSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || + $header.hasClass( 'filter-select' ); // get data from jQuery data, metadata, headers option or header class name col = ts.getColumnData( table, c.headers, column ); - disabled = ts.getData($header[0], col, 'filter') === 'false' || ts.getData($header[0], col, 'parser') === 'false'; + disabled = ts.getData( $header[0], col, 'filter' ) === 'false' || + ts.getData( $header[0], col, 'parser' ) === 'false'; - if (buildSelect) { - buildFilter = $('<select>').appendTo( c.$filters.eq(column) ); + if ( buildSelect ) { + buildFilter = $( '<select>' ).appendTo( c.$filters.eq( column ) ); } else { ffxn = ts.getColumnData( table, wo.filter_formatter, column ); - if (ffxn) { + if ( ffxn ) { wo.filter_formatterCount++; - buildFilter = ffxn( c.$filters.eq(column), column ); + buildFilter = ffxn( c.$filters.eq( column ), column ); // no element returned, so lets go find it - if (buildFilter && buildFilter.length === 0) { - buildFilter = c.$filters.eq(column).children('input'); + if ( buildFilter && buildFilter.length === 0 ) { + buildFilter = c.$filters.eq( column ).children( 'input' ); } // element not in DOM, so lets attach it - if ( buildFilter && (buildFilter.parent().length === 0 || - (buildFilter.parent().length && buildFilter.parent()[0] !== c.$filters[column])) ) { - c.$filters.eq(column).append(buildFilter); + if ( buildFilter && ( buildFilter.parent().length === 0 || + ( buildFilter.parent().length && buildFilter.parent()[0] !== c.$filters[column] ) ) ) { + c.$filters.eq( column ).append( buildFilter ); } } else { - buildFilter = $('<input type="search">').appendTo( c.$filters.eq(column) ); + buildFilter = $( '<input type="search">' ).appendTo( c.$filters.eq( column ) ); } - if (buildFilter) { - buildFilter.attr('placeholder', $header.data('placeholder') || $header.attr('data-placeholder') || wo.filter_placeholder.search || ''); + if ( buildFilter ) { + tmp = $header.data( 'placeholder' ) || + $header.attr( 'data-placeholder' ) || + wo.filter_placeholder.search || ''; + buildFilter.attr( 'placeholder', tmp ); } } - if (buildFilter) { + if ( buildFilter ) { // add filter class name - name = ( $.isArray(wo.filter_cssFilter) ? - (typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '') : + name = ( $.isArray( wo.filter_cssFilter ) ? + ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : wo.filter_cssFilter ) || ''; - buildFilter.addClass( tscss.filter + ' ' + name ).attr('data-column', column); - if (disabled) { - buildFilter.attr('placeholder', '').addClass(tscss.filterDisabled)[0].disabled = true; // disabled! + buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column ); + if ( disabled ) { + buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; } } } }, - bindSearch: function(table, $el, internal) { - table = $(table)[0]; - $el = $($el); // allow passing a selector string - if (!$el.length) { return; } - var c = table.config, + bindSearch: function( table, $el, internal ) { + table = $( table )[0]; + $el = $( $el ); // allow passing a selector string + if ( !$el.length ) { return; } + var tmp, + c = table.config, wo = c.widgetOptions, + namespace = c.namespace + 'filter', $ext = wo.filter_$externalFilters; - if (internal !== true) { + if ( internal !== true ) { // save anyMatch element - wo.filter_$anyMatch = $el.filter(wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector); - if ($ext && $ext.length) { + tmp = wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector; + wo.filter_$anyMatch = $el.filter( tmp ); + if ( $ext && $ext.length ) { wo.filter_$externalFilters = wo.filter_$externalFilters.add( $el ); } else { wo.filter_$externalFilters = $el; } - // update values (external filters added after table initialization) - ts.setFilters(table, c.$table.data('lastSearch') || [], internal === false); + // update values ( external filters added after table initialization ) + ts.setFilters( table, c.$table.data( 'lastSearch' ) || [], internal === false ); } + // unbind events + tmp = ( 'keypress keyup search change '.split( ' ' ).join( namespace + ' ' ) ); $el - // use data attribute instead of jQuery data since the head is cloned without including the data/binding - .attr('data-lastSearchTime', new Date().getTime()) - .unbind( ('keypress keyup search change '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') ) + // use data attribute instead of jQuery data since the head is cloned without including + // the data/binding + .attr( 'data-lastSearchTime', new Date().getTime() ) + .unbind( tmp.replace( /\s+/g, ' ' ) ) // include change for select - fixes #473 - .bind('keyup' + c.namespace + 'filter', function(event) { - $(this).attr('data-lastSearchTime', new Date().getTime()); + .bind( 'keyup' + namespace, function( event ) { + $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); // emulate what webkit does.... escape clears the filter - if (event.which === 27) { + if ( event.which === 27 ) { this.value = ''; // live search } else if ( wo.filter_liveSearch === false ) { return; - // don't return if the search value is empty (all rows need to be revealed) + // don't return if the search value is empty ( all rows need to be revealed ) } else if ( this.value !== '' && ( // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace ( typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch ) || // let return & backspace continue on, but ignore arrows & non-valid characters - ( event.which !== 13 && event.which !== 8 && ( event.which < 32 || (event.which >= 37 && event.which <= 40) ) ) ) ) { + ( event.which !== 13 && event.which !== 8 && + ( event.which < 32 || ( event.which >= 37 && event.which <= 40 ) ) ) ) ) { return; } // change event = no delay; last true flag tells getFilters to skip newest timed input ts.filter.searching( table, true, true ); }) - .bind( 'search change keypress '.split(' ').join(c.namespace + 'filter '), function(event){ - var column = $(this).data('column'); - // don't allow "change" event to process if the input value is the same - fixes #685 - if (event.which === 13 || event.type === 'search' || event.type === 'change' && this.value !== c.lastSearch[column]) { + .bind( 'search change keypress '.split( ' ' ).join( namespace + ' ' ), function( event ) { + var column = $( this ).data( 'column' ); + // don't allow 'change' event to process if the input value is the same - fixes #685 + if ( event.which === 13 || event.type === 'search' || + event.type === 'change' && this.value !== c.lastSearch[column] ) { event.preventDefault(); // init search with no delay - $(this).attr('data-lastSearchTime', new Date().getTime()); + $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); ts.filter.searching( table, false, true ); } }); }, - searching: function(table, filter, skipFirst) { + searching: function( table, filter, skipFirst ) { var wo = table.config.widgetOptions; - clearTimeout(wo.searchTimer); - if (typeof filter === 'undefined' || filter === true) { + clearTimeout( wo.searchTimer ); + if ( typeof filter === 'undefined' || filter === true ) { // delay filtering - wo.searchTimer = setTimeout(function() { - ts.filter.checkFilters(table, filter, skipFirst ); - }, wo.filter_liveSearch ? wo.filter_searchDelay : 10); + wo.searchTimer = setTimeout( function() { + ts.filter.checkFilters( table, filter, skipFirst ); + }, wo.filter_liveSearch ? wo.filter_searchDelay : 10 ); } else { // skip delay - ts.filter.checkFilters(table, filter, skipFirst); + ts.filter.checkFilters( table, filter, skipFirst ); } }, - checkFilters: function(table, filter, skipFirst) { + checkFilters: function( table, filter, skipFirst ) { var c = table.config, wo = c.widgetOptions, - filterArray = $.isArray(filter), - filters = (filterArray) ? filter : ts.getFilters(table, true), - combinedFilters = (filters || []).join(''); // combined filter values + filterArray = $.isArray( filter ), + filters = ( filterArray ) ? filter : ts.getFilters( table, true ), + combinedFilters = ( filters || [] ).join( '' ); // combined filter values // prevent errors if delay init is set - if ($.isEmptyObject(c.cache)) { - // update cache if delayInit set & pager has initialized (after user initiates a search) - if (c.delayInit && c.pager && c.pager.initialized) { - c.$table.trigger('updateCache', [function(){ - ts.filter.checkFilters(table, false, skipFirst); - }] ); + if ( $.isEmptyObject( c.cache ) ) { + // update cache if delayInit set & pager has initialized ( after user initiates a search ) + if ( c.delayInit && c.pager && c.pager.initialized ) { + c.$table.trigger( 'updateCache', [ function() { + ts.filter.checkFilters( table, false, skipFirst ); + } ] ); } return; } // add filter array back into inputs - if (filterArray) { + if ( filterArray ) { ts.setFilters( table, filters, false, skipFirst !== true ); - if (!wo.filter_initialized) { c.lastCombinedFilter = ''; } + if ( !wo.filter_initialized ) { c.lastCombinedFilter = ''; } } - if (wo.filter_hideFilters) { + if ( wo.filter_hideFilters ) { // show/hide filter row as needed - c.$table.find('.' + tscss.filterRow).trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); + c.$table + .find( '.' + tscss.filterRow ) + .trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons - if (c.lastCombinedFilter === combinedFilters && filter !== false) { + if ( c.lastCombinedFilter === combinedFilters && filter !== false ) { return; - } else if (filter === false) { + } else if ( filter === false ) { // force filter refresh c.lastCombinedFilter = null; c.lastSearch = []; } - if (wo.filter_initialized) { c.$table.trigger('filterStart', [filters]); } - if (c.showProcessing) { + if ( wo.filter_initialized ) { + c.$table.trigger( 'filterStart', [filters] ); + } + if ( c.showProcessing ) { // give it time for the processing icon to kick in - setTimeout(function() { - ts.filter.findRows(table, filters, combinedFilters); + setTimeout( function() { + ts.filter.findRows( table, filters, combinedFilters ); return false; - }, 30); + }, 30 ); } else { - ts.filter.findRows(table, filters, combinedFilters); + ts.filter.findRows( table, filters, combinedFilters ); return false; } }, - hideFilters: function(table, c) { + hideFilters: function( table, c ) { var $filterRow, $filterRow2, timer; - $(table) - .find('.' + tscss.filterRow) - .addClass(tscss.filterRowHide) - .bind('mouseenter mouseleave', function(e) { + $( table ) + .find( '.' + tscss.filterRow ) + .addClass( tscss.filterRowHide ) + .bind( 'mouseenter mouseleave', function( e ) { // save event object - http://bugs.jquery.com/ticket/12140 var event = e; - $filterRow = $(this); - clearTimeout(timer); - timer = setTimeout(function() { - if ( /enter|over/.test(event.type) ) { - $filterRow.removeClass(tscss.filterRowHide); + $filterRow = $( this ); + clearTimeout( timer ); + timer = setTimeout( function() { + if ( /enter|over/.test( event.type ) ) { + $filterRow.removeClass( tscss.filterRowHide ); } else { // don't hide if input has focus - // $(':focus') needs jQuery 1.6+ - if ( $(document.activeElement).closest('tr')[0] !== $filterRow[0] ) { + // $( ':focus' ) needs jQuery 1.6+ + if ( $( document.activeElement ).closest( 'tr' )[0] !== $filterRow[0] ) { // don't hide row if any filter has a value - if (c.lastCombinedFilter === '') { - $filterRow.addClass(tscss.filterRowHide); + if ( c.lastCombinedFilter === '' ) { + $filterRow.addClass( tscss.filterRowHide ); } } } - }, 200); + }, 200 ); }) - .find('input, select').bind('focus blur', function(e) { - $filterRow2 = $(this).closest('tr'); - clearTimeout(timer); + .find( 'input, select' ).bind( 'focus blur', function( e ) { + $filterRow2 = $( this ).closest( 'tr' ); + clearTimeout( timer ); var event = e; - timer = setTimeout(function() { + timer = setTimeout( function() { // don't hide row if any filter has a value - if (ts.getFilters(c.$table).join('') === '') { - $filterRow2[ event.type === 'focus' ? 'removeClass' : 'addClass'](tscss.filterRowHide); + if ( ts.getFilters( c.$table ).join( '' ) === '' ) { + $filterRow2.toggleClass( tscss.filterRowHide, event.type === 'focus' ); } - }, 200); + }, 200 ); }); }, - defaultFilter: function(filter, mask){ - if (filter === '') { return filter; } + defaultFilter: function( filter, mask ) { + if ( filter === '' ) { return filter; } var regex = ts.filter.regex.iQuery, maskLen = mask.match( ts.filter.regex.igQuery ).length, - query = maskLen > 1 ? $.trim(filter).split(/\s/) : [ $.trim(filter) ], + query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ], len = query.length - 1, indx = 0, val = mask; if ( len < 1 && maskLen > 1 ) { - // only one "word" in query but mask has >1 slots + // only one 'word' in query but mask has >1 slots query[1] = query[0]; } // replace all {query} with query words... - // if query = "Bob", then convert mask from "!{query}" to "!Bob" - // if query = "Bob Joe Frank", then convert mask "{q} OR {q}" to "Bob OR Joe OR Frank" - while (regex.test(val)) { - val = val.replace(regex, query[indx++] || ''); - if (regex.test(val) && indx < len && (query[indx] || '') !== '') { - val = mask.replace(regex, val); + // if query = 'Bob', then convert mask from '!{query}' to '!Bob' + // if query = 'Bob Joe Frank', then convert mask '{q} OR {q}' to 'Bob OR Joe OR Frank' + while ( regex.test( val ) ) { + val = val.replace( regex, query[indx++] || '' ); + if ( regex.test( val ) && indx < len && ( query[indx] || '' ) !== '' ) { + val = mask.replace( regex, val ); } } return val; }, getLatestSearch: function( $input ) { - if ($input) { - return $input.sort(function(a, b) { - return $(b).attr('data-lastSearchTime') - $(a).attr('data-lastSearchTime'); + if ( $input ) { + return $input.sort( function( a, b ) { + return $( b ).attr( 'data-lastSearchTime' ) - $( a ).attr( 'data-lastSearchTime' ); }); } return $(); }, multipleColumns: function( c, $input ) { - // look for multiple columns "1-3,4-6,8" in data-column + // look for multiple columns '1-3,4-6,8' in data-column var temp, ranges, range, start, end, singles, i, indx, len, wo = c.widgetOptions, - // only target "all" column inputs on initialization - // & don't target "all" column inputs if they don't exist - targets = wo.filter_initialized || !$input.filter(wo.filter_anyColumnSelector).length, + // only target 'all' column inputs on initialization + // & don't target 'all' column inputs if they don't exist + targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, columns = [], - val = $.trim( ts.filter.getLatestSearch( $input ).attr('data-column') || '' ); + val = $.trim( ts.filter.getLatestSearch( $input ).attr( 'data-column' ) || '' ); // process column range if ( targets && /-/.test( val ) ) { ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); len = ranges.length; - for (indx = 0; indx < len; indx++) { + for ( indx = 0; indx < len; indx++ ) { range = ranges[indx].split( /\s*-\s*/ ); start = parseInt( range[0], 10 ) || 0; end = parseInt( range[1], 10 ) || ( c.columns - 1 ); - if ( start > end ) { temp = start; start = end; end = temp; } // swap - if ( end >= c.columns ) { end = c.columns - 1; } + if ( start > end ) { + temp = start; start = end; end = temp; // swap + } + if ( end >= c.columns ) { + end = c.columns - 1; + } for ( ; start <= end; start++ ) { - columns.push(start); + columns.push( start ); } // remove processed range from val - val = val.replace( ranges[indx], '' ); + val = val.replace( ranges[ indx ], '' ); } } // process single columns if ( targets && /,/.test( val ) ) { singles = val.split( /\s*,\s*/ ); len = singles.length; - for (i = 0; i < len; i++) { - if (singles[i] !== '') { - indx = parseInt( singles[i], 10 ); + for ( i = 0; i < len; i++ ) { + if ( singles[ i ] !== '' ) { + indx = parseInt( singles[ i ], 10 ); if ( indx < c.columns ) { columns.push( indx ); } @@ -1151,382 +1245,472 @@ ts.filter = { } } // return all columns - if (!columns.length) { + if ( !columns.length ) { for ( indx = 0; indx < c.columns; indx++ ) { columns.push( indx ); } } return columns; }, - findRows: function(table, filters, combinedFilters) { - if (table.config.lastCombinedFilter === combinedFilters || !table.config.widgetOptions.filter_initialized) { return; } - var len, norm_rows, $rows, rowIndex, tbodyIndex, $tbody, $cells, $cell, columnIndex, - childRow, lastSearch, hasSelect, matches, result, showRow, time, val, indx, - notFiltered, searchFiltered, filterMatched, excludeMatch, fxn, ffxn, - query, injected, res, id, + processRow: function( c, data, vars ) { + var $cell, columnIndex, hasSelect, matches, result, val, filterMatched, excludeMatch, + fxn, ffxn, txt, + regex = ts.filter.regex, + wo = c.widgetOptions, + showRow = true; + data.$cells = data.$row.children(); + + if ( data.anyMatchFlag ) { + // look for multiple columns '1-3,4-6,8' + columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); + data.anyMatch = true; + data.rowArray = data.$cells.map( function( i ) { + if ( $.inArray( i, columnIndex ) > -1 ) { + if ( data.parsed[ i ] ) { + txt = data.cacheArray[ i ]; + } else { + txt = data.rawArray[ i ]; + txt = $.trim( wo.filter_ignoreCase ? txt.toLowerCase() : txt ); + if ( c.sortLocaleCompare ) { + txt = ts.replaceAccents( txt ); + } + } + return txt; + } + }).get(); + data.filter = data.anyMatchFilter; + data.iFilter = data.iAnyMatchFilter; + data.exact = data.rowArray.join( ' ' ); + data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; + data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' ); + filterMatched = null; + matches = null; + for ( ffxn in ts.filter.types ) { + if ( $.inArray( ffxn, vars.noAnyMatch ) < 0 && matches === null ) { + matches = ts.filter.types[ffxn]( c, data ); + if ( matches !== null ) { + filterMatched = matches; + } + } + } + if ( filterMatched !== null ) { + showRow = filterMatched; + } else { + if ( wo.filter_startsWith ) { + showRow = false; + columnIndex = c.columns; + while ( !showRow && columnIndex > 0 ) { + columnIndex--; + showRow = showRow || data.rowArray[ columnIndex ].indexOf( data.iFilter ) === 0; + } + } else { + showRow = ( data.iExact + data.childRowText ).indexOf( data.iFilter ) >= 0; + } + } + data.anyMatch = false; + // no other filters to process + if ( data.filters.join( '' ) === data.filter ) { + return showRow; + } + } + + for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { + data.filter = data.filters[ columnIndex ]; + data.index = columnIndex; + + // filter types to exclude, per column + excludeMatch = vars.excludeFilter[ columnIndex ]; + + // ignore if filter is empty or disabled + if ( data.filter ) { + data.cache = data.cacheArray[ columnIndex ]; + // check if column data should be from the cell or from parsed data + if ( wo.filter_useParsedData || data.parsed[ columnIndex ] ) { + data.exact = data.cache; + } else { + result = data.rawArray[ columnIndex ] || ''; + data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 + } + data.iExact = !regex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? + data.exact.toLowerCase() : data.exact; + result = showRow; // if showRow is true, show that row + + // in case select filter option has a different value vs text 'a - z|A through Z' + ffxn = wo.filter_columnFilters ? + c.$filters.add( c.$externalFilters ) + .filter( '[data-column="'+ columnIndex + '"]' ) + .find( 'select option:selected' ) + .attr( 'data-function-name' ) || '' : ''; + // replace accents - see #357 + if ( c.sortLocaleCompare ) { + data.filter = ts.replaceAccents( data.filter ); + } + + val = true; + if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) { + data.filter = ts.filter.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); + // val is used to indicate that a filter select is using a default filter; + // so we override the exact & partial matches + val = false; + } + // data.iFilter = case insensitive ( if wo.filter_ignoreCase is true ), + // data.filter = case sensitive + data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter; + fxn = vars.functions[ columnIndex ]; + $cell = c.$headerIndexed[ columnIndex ]; + hasSelect = $cell.hasClass( 'filter-select' ); + filterMatched = null; + if ( fxn || ( hasSelect && val ) ) { + if ( fxn === true || hasSelect ) { + // default selector uses exact match unless 'filter-match' class is found + filterMatched = $cell.hasClass( 'filter-match' ) ? + data.iExact.search( data.iFilter ) >= 0 : + data.filter === data.exact; + } else if ( typeof fxn === 'function' ) { + // filter callback( exact cell content, parser normalized content, + // filter input value, column index, jQuery row object ) + filterMatched = fxn( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); + } else if ( typeof fxn[ ffxn || data.filter ] === 'function' ) { + // selector option function + txt = ffxn || data.filter; + filterMatched = + fxn[ txt ]( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); + } + } + if ( filterMatched === null ) { + // cycle through the different filters + // filters return a boolean or null if nothing matches + matches = null; + for ( ffxn in ts.filter.types ) { + if ( $.inArray( ffxn, excludeMatch ) < 0 && matches === null ) { + matches = ts.filter.types[ ffxn ]( c, data ); + if ( matches !== null ) { + filterMatched = matches; + } + } + } + if ( filterMatched !== null ) { + result = filterMatched; + // Look for match, and add child row data for matching + } else { + txt = ( data.iExact + data.childRowText ) + .indexOf( ts.filter.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) ); + result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); + } + } else { + result = filterMatched; + } + showRow = ( result ) ? showRow : false; + } + } + return showRow; + }, + findRows: function( table, filters, combinedFilters ) { + if ( table.config.lastCombinedFilter === combinedFilters || + !table.config.widgetOptions.filter_initialized ) { + return; + } + var len, norm_rows, rowData, $rows, rowIndex, tbodyIndex, $tbody, columnIndex, + isChild, childRow, lastSearch, showRow, time, val, indx, + notFiltered, searchFiltered, query, injected, res, id, txt, + storedFilters = $.extend( [], filters ), regex = ts.filter.regex, c = table.config, wo = c.widgetOptions, // data object passed to filters; anyMatch is a flag for the filters - data = { anyMatch: false }, - // anyMatch really screws up with these types of filters - noAnyMatch = [ 'range', 'notMatch', 'operators' ]; + data = { + anyMatch: false, + filters: filters, + // regex filter type cache + filter_regexCache : [], + }, + vars = { + // anyMatch really screws up with these types of filters + noAnyMatch: [ 'range', 'notMatch', 'operators' ], + // cache filter variables that use ts.getColumnData in the main loop + functions : [], + excludeFilter : [], + defaultColFilter : [], + defaultAnyFilter : ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '' + }; // parse columns after formatter, in case the class is added at that point - data.parsed = c.$headers.map(function(columnIndex) { - return c.parsers && c.parsers[columnIndex] && c.parsers[columnIndex].parsed || - // getData won't return "parsed" if other "filter-" class names exist (e.g. <th class="filter-select filter-parsed">) - ts.getData && ts.getData(c.$headerIndexed[columnIndex], ts.getColumnData( table, c.headers, columnIndex ), 'filter') === 'parsed' || - $(this).hasClass('filter-parsed'); + data.parsed = c.$headers.map( function( columnIndex ) { + return c.parsers && c.parsers[ columnIndex ] && + // force parsing if parser type is numeric + ( c.parsers[ columnIndex ].parsed || c.parsers[ columnIndex ].type === 'numeric' ) || + // getData won't return 'parsed' if other 'filter-' class names exist + // ( e.g. <th class="filter-select filter-parsed"> ) + ts.getData && ts.getData( c.$headerIndexed[ columnIndex ], + ts.getColumnData( table, c.headers, columnIndex ), 'filter' ) === 'parsed' || + $( this ).hasClass( 'filter-parsed' ); }).get(); - // cache filter variables that use ts.getColumnData in the main loop - wo.filter_indexed = { - functions : [], - excludeFilter : [], - defaultColFilter : [], - defaultAnyFilter : ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '' - }; for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { - wo.filter_indexed.functions[ columnIndex ] = ts.getColumnData( table, wo.filter_functions, columnIndex ); - wo.filter_indexed.defaultColFilter[ columnIndex ] = ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || ''; - wo.filter_indexed.excludeFilter[ columnIndex ] = ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split(/\s+/); + vars.functions[ columnIndex ] = + ts.getColumnData( table, wo.filter_functions, columnIndex ); + vars.defaultColFilter[ columnIndex ] = + ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || ''; + vars.excludeFilter[ columnIndex ] = + ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split( /\s+/ ); } - if (c.debug) { - ts.log('Filter: Starting filter widget search', filters); + if ( c.debug ) { + ts.log( 'Filter: Starting filter widget search', filters ); time = new Date(); } // filtered rows count c.filteredRows = 0; c.totalRows = 0; // combindedFilters are undefined on init - combinedFilters = (filters || []).join(''); + combinedFilters = ( storedFilters || [] ).join( '' ); - for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { - $tbody = ts.processTbody(table, c.$tbodies.eq(tbodyIndex), true); - // skip child rows & widget added (removable) rows - fixes #448 thanks to @hempel! - // $rows = $tbody.children('tr').not(c.selectorRemove); + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody( table, c.$tbodies.eq( tbodyIndex ), true ); + // skip child rows & widget added ( removable ) rows - fixes #448 thanks to @hempel! + // $rows = $tbody.children( 'tr' ).not( c.selectorRemove ); columnIndex = c.columns; // convert stored rows into a jQuery object - norm_rows = c.cache[tbodyIndex].normalized; - $rows = $( $.map(norm_rows, function(el){ return el[columnIndex].$row.get(); }) ); - - if (combinedFilters === '' || wo.filter_serversideFiltering) { - $rows.removeClass(wo.filter_filteredRow).not('.' + c.cssChildRow).css('display', ''); + norm_rows = c.cache[ tbodyIndex ].normalized; + $rows = $( $.map( norm_rows, function( el ) { + return el[ columnIndex ].$row.get(); + }) ); + + if ( combinedFilters === '' || wo.filter_serversideFiltering ) { + $rows + .removeClass( wo.filter_filteredRow ) + .not( '.' + c.cssChildRow ) + .css( 'display', '' ); } else { // filter out child rows - $rows = $rows.not('.' + c.cssChildRow); + $rows = $rows.not( '.' + c.cssChildRow ); len = $rows.length; - if ( (wo.filter_$anyMatch && wo.filter_$anyMatch.length) || typeof filters[c.columns] !== 'undefined' ) { + if ( ( wo.filter_$anyMatch && wo.filter_$anyMatch.length ) || + typeof filters[c.columns] !== 'undefined' ) { data.anyMatchFlag = true; - data.anyMatchFilter = wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || ( '' + filters[c.columns] ) || ''; - if (wo.filter_columnAnyMatch) { + data.anyMatchFilter = '' + ( + filters[ c.columns ] || + wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || + '' + ); + if ( wo.filter_columnAnyMatch ) { // specific columns search - query = data.anyMatchFilter.split( ts.filter.regex.andSplit ); + query = data.anyMatchFilter.split( regex.andSplit ); injected = false; - for (indx = 0; indx < query.length; indx++) { - res = query[indx].split(':'); + for ( indx = 0; indx < query.length; indx++ ) { + res = query[ indx ].split( ':' ); if ( res.length > 1 ) { // make the column a one-based index ( non-developers start counting from one :P ) id = parseInt( res[0], 10 ) - 1; if ( id >= 0 && id < c.columns ) { // if id is an integer - filters[id] = res[1]; - query.splice(indx, 1); + filters[ id ] = res[1]; + query.splice( indx, 1 ); indx--; injected = true; } } } - if (injected) { - data.anyMatchFilter = query.join(' && '); + if ( injected ) { + data.anyMatchFilter = query.join( ' && ' ); } } } // optimize searching only through already filtered rows - see #313 searchFiltered = wo.filter_searchFiltered; - lastSearch = c.lastSearch || c.$table.data('lastSearch') || []; - if (searchFiltered) { - // cycle through all filters; include last (columnIndex + 1 = match any column). Fixes #669 - for (indx = 0; indx < columnIndex + 1; indx++) { + lastSearch = c.lastSearch || c.$table.data( 'lastSearch' ) || []; + if ( searchFiltered ) { + // cycle through all filters; include last ( columnIndex + 1 = match any column ). Fixes #669 + for ( indx = 0; indx < columnIndex + 1; indx++ ) { val = filters[indx] || ''; // break out of loop if we've already determined not to search filtered rows - if (!searchFiltered) { indx = columnIndex; } + if ( !searchFiltered ) { indx = columnIndex; } // search already filtered rows if... searchFiltered = searchFiltered && lastSearch.length && // there are no changes from beginning of filter - val.indexOf(lastSearch[indx] || '') === 0 && - // if there is NOT a logical "or", or range ("to" or "-") in the string - !regex.alreadyFiltered.test(val) && - // if we are not doing exact matches, using "|" (logical or) or not "!" - !/[=\"\|!]/.test(val) && - // don't search only filtered if the value is negative ('> -10' => '> -100' will ignore hidden rows) - !(/(>=?\s*-\d)/.test(val) || /(<=?\s*\d)/.test(val)) && - // if filtering using a select without a "filter-match" class (exact match) - fixes #593 - !( val !== '' && c.$filters && c.$filters.eq(indx).find('select').length && !c.$headerIndexed[indx].hasClass('filter-match') ); + val.indexOf( lastSearch[indx] || '' ) === 0 && + // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string + !regex.alreadyFiltered.test( val ) && + // if we are not doing exact matches, using '|' ( logical or ) or not '!' + !/[=\"\|!]/.test( val ) && + // don't search only filtered if the value is negative + // ( '> -10' => '> -100' will ignore hidden rows ) + !( /(>=?\s*-\d)/.test( val ) || /(<=?\s*\d)/.test( val ) ) && + // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 + !( val !== '' && c.$filters && c.$filters.eq( indx ).find( 'select' ).length && + !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); } } - notFiltered = $rows.not('.' + wo.filter_filteredRow).length; + notFiltered = $rows.not( '.' + wo.filter_filteredRow ).length; // can't search when all rows are hidden - this happens when looking for exact matches - if (searchFiltered && notFiltered === 0) { searchFiltered = false; } - if (c.debug) { - ts.log( 'Filter: Searching through ' + ( searchFiltered && notFiltered < len ? notFiltered : 'all' ) + ' rows' ); + if ( searchFiltered && notFiltered === 0 ) { searchFiltered = false; } + if ( c.debug ) { + ts.log( 'Filter: Searching through ' + + ( searchFiltered && notFiltered < len ? notFiltered : 'all' ) + ' rows' ); } - if (data.anyMatchFlag) { - if (c.sortLocaleCompare) { + if ( data.anyMatchFlag ) { + if ( c.sortLocaleCompare ) { // replace accents - data.anyMatchFilter = ts.replaceAccents(data.anyMatchFilter); + data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter ); } - if ( wo.filter_defaultFilter && regex.iQuery.test( wo.filter_indexed.defaultAnyFilter ) ) { - data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, wo.filter_indexed.defaultAnyFilter ); + if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultAnyFilter ) ) { + data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter ); // clear search filtered flag because default filters are not saved to the last search searchFiltered = false; } // make iAnyMatchFilter lowercase unless both filter widget & core ignoreCase options are true // when c.ignoreCase is true, the cache contains all lower case data - data.iAnyMatchFilter = !(wo.filter_ignoreCase && c.ignoreCase) ? data.anyMatchFilter : data.anyMatchFilter.toLocaleLowerCase(); + data.iAnyMatchFilter = !( wo.filter_ignoreCase && c.ignoreCase ) ? + data.anyMatchFilter : + data.anyMatchFilter.toLowerCase(); } // loop through the rows - for (rowIndex = 0; rowIndex < len; rowIndex++) { + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { - data.cacheArray = norm_rows[rowIndex]; - - childRow = $rows[rowIndex].className; + txt = $rows[ rowIndex ].className; + // the first row can never be a child row + isChild = rowIndex && regex.child.test( txt ); // skip child rows & already filtered rows - if ( regex.child.test(childRow) || (searchFiltered && regex.filtered.test(childRow)) ) { continue; } - showRow = true; - // *** nextAll/nextUntil not supported by Zepto! *** - childRow = $rows.eq(rowIndex).nextUntil('tr:not(.' + c.cssChildRow + ')'); - // so, if "table.config.widgetOptions.filter_childRows" is true and there is - // a match anywhere in the child row, then it will make the row visible - // checked here so the option can be changed dynamically - data.childRowText = (childRow.length && wo.filter_childRows) ? childRow.text() : ''; - data.childRowText = wo.filter_ignoreCase ? data.childRowText.toLocaleLowerCase() : data.childRowText; - $cells = $rows.eq(rowIndex).children(); - if (data.anyMatchFlag) { - // look for multiple columns "1-3,4-6,8" - columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); - data.anyMatch = true; - data.rowArray = $cells.map(function(i){ - if ( $.inArray(i, columnIndex) > -1 ) { - var txt; - if (data.parsed[i]) { - txt = data.cacheArray[i]; - } else { - txt = this ? this.getAttribute( c.textAttribute ) || this.textContent || $(this).text() : ''; - txt = $.trim( wo.filter_ignoreCase ? txt.toLowerCase() : txt ); - if (c.sortLocaleCompare) { - txt = ts.replaceAccents(txt); - } - } - return txt; - } - }).get(); - data.filter = data.anyMatchFilter; - data.iFilter = data.iAnyMatchFilter; - data.exact = data.rowArray.join(' '); - data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; - data.cache = data.cacheArray.slice(0,-1).join(' '); - filterMatched = null; - $.each(ts.filter.types, function(type, typeFunction) { - if ($.inArray(type, noAnyMatch) < 0) { - matches = typeFunction( c, data ); - if (matches !== null) { - filterMatched = matches; - return false; - } - } - }); - if (filterMatched !== null) { - showRow = filterMatched; - } else { - if (wo.filter_startsWith) { - showRow = false; - columnIndex = c.columns; - while (!showRow && columnIndex > 0) { - columnIndex--; - showRow = showRow || data.rowArray[columnIndex].indexOf(data.iFilter) === 0; - } - } else { - showRow = (data.iExact + data.childRowText).indexOf(data.iFilter) >= 0; - } - } - data.anyMatch = false; + if ( isChild || ( searchFiltered && regex.filtered.test( txt ) ) ) { + continue; } - for (columnIndex = 0; columnIndex < c.columns; columnIndex++) { - data.filter = filters[columnIndex]; - data.index = columnIndex; - - // filter types to exclude, per column - excludeMatch = wo.filter_indexed.excludeFilter[ columnIndex ]; - - // ignore if filter is empty or disabled - if (data.filter) { - data.cache = data.cacheArray[columnIndex]; - // check if column data should be from the cell or from parsed data - if (wo.filter_useParsedData || data.parsed[columnIndex]) { - data.exact = data.cache; - } else { - val = $cells[columnIndex]; - result = val ? $.trim( val.getAttribute( c.textAttribute ) || val.textContent || $cells.eq(columnIndex).text() ) : ''; - data.exact = c.sortLocaleCompare ? ts.replaceAccents(result) : result; // issue #405 - } - data.iExact = !regex.type.test(typeof data.exact) && wo.filter_ignoreCase ? data.exact.toLocaleLowerCase() : data.exact; - result = showRow; // if showRow is true, show that row - - // in case select filter option has a different value vs text "a - z|A through Z" - ffxn = wo.filter_columnFilters ? - c.$filters.add(c.$externalFilters).filter('[data-column="'+ columnIndex + '"]').find('select option:selected').attr('data-function-name') || '' : ''; - // replace accents - see #357 - if (c.sortLocaleCompare) { - data.filter = ts.replaceAccents(data.filter); - } + data.$row = $rows.eq( rowIndex ); + data.cacheArray = norm_rows[ rowIndex ]; + rowData = data.cacheArray[ c.columns ]; + data.rawArray = rowData.raw; + data.childRowText = ''; + + if ( !wo.filter_childByColumn ) { + txt = ''; + // child row cached text + childRow = rowData.child; + // so, if 'table.config.widgetOptions.filter_childRows' is true and there is + // a match anywhere in the child row, then it will make the row visible + // checked here so the option can be changed dynamically + for ( indx = 0; indx < childRow.length; indx++ ) { + txt += ' ' + childRow[indx].join( '' ) || ''; + } + data.childRowText = wo.filter_childRows ? + ( wo.filter_ignoreCase ? txt.toLowerCase() : txt ) : + ''; + } - val = true; - if (wo.filter_defaultFilter && regex.iQuery.test( wo.filter_indexed.defaultColFilter[ columnIndex ] )) { - data.filter = ts.filter.defaultFilter( data.filter, wo.filter_indexed.defaultColFilter[ columnIndex ] ); - // val is used to indicate that a filter select is using a default filter; so we override the exact & partial matches - val = false; + showRow = ts.filter.processRow( c, data, vars ); + childRow = rowData.$row.filter( ':gt( 0 )' ); + + if ( wo.filter_childRows && childRow.length ) { + if ( wo.filter_childByColumn ) { + // cycle through each child row + for ( indx = 0; indx < childRow.length; indx++ ) { + data.$row = childRow.eq( indx ); + data.cacheArray = rowData.child[ indx ]; + data.rawArray = data.cacheArray; + // use OR comparison on child rows + showRow = showRow || ts.filter.processRow( c, data, vars ); } - // data.iFilter = case insensitive (if wo.filter_ignoreCase is true), data.filter = case sensitive - data.iFilter = wo.filter_ignoreCase ? (data.filter || '').toLocaleLowerCase() : data.filter; - fxn = wo.filter_indexed.functions[ columnIndex ]; - $cell = c.$headerIndexed[columnIndex]; - hasSelect = $cell.hasClass('filter-select'); - filterMatched = null; - if ( fxn || ( hasSelect && val ) ) { - if (fxn === true || hasSelect) { - // default selector uses exact match unless "filter-match" class is found - filterMatched = ($cell.hasClass('filter-match')) ? data.iExact.search(data.iFilter) >= 0 : data.filter === data.exact; - } else if (typeof fxn === 'function') { - // filter callback( exact cell content, parser normalized content, filter input value, column index, jQuery row object ) - filterMatched = fxn(data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); - } else if (typeof fxn[ffxn || data.filter] === 'function') { - // selector option function - filterMatched = fxn[ffxn || data.filter](data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); - } - } - if (filterMatched === null) { - // cycle through the different filters - // filters return a boolean or null if nothing matches - $.each(ts.filter.types, function(type, typeFunction) { - if ($.inArray(type, excludeMatch) < 0) { - matches = typeFunction( c, data ); - if (matches !== null) { - filterMatched = matches; - return false; - } - } - }); - if (filterMatched !== null) { - result = filterMatched; - // Look for match, and add child row data for matching - } else { - data.exact = (data.iExact + data.childRowText).indexOf( ts.filter.parseFilter(c, data.iFilter, columnIndex, data.parsed[columnIndex]) ); - result = ( (!wo.filter_startsWith && data.exact >= 0) || (wo.filter_startsWith && data.exact === 0) ); - } - } else { - result = filterMatched; - } - showRow = (result) ? showRow : false; } + childRow.toggleClass( wo.filter_filteredRow, !showRow ); } - $rows.eq(rowIndex) - .toggleClass(wo.filter_filteredRow, !showRow)[0] + + rowData.$row + .toggleClass( wo.filter_filteredRow, !showRow )[0] .display = showRow ? '' : 'none'; - if (childRow.length) { - childRow.toggleClass(wo.filter_filteredRow, !showRow); - } } } - c.filteredRows += $rows.not('.' + wo.filter_filteredRow).length; + c.filteredRows += $rows.not( '.' + wo.filter_filteredRow ).length; c.totalRows += $rows.length; - ts.processTbody(table, $tbody, false); + ts.processTbody( table, $tbody, false ); } c.lastCombinedFilter = combinedFilters; // save last search - c.lastSearch = filters; - c.$table.data('lastSearch', filters); - if (wo.filter_saveFilters && ts.storage) { - ts.storage( table, 'tablesorter-filters', filters ); + // don't save 'filters' directly since it may have altered ( AnyMatch column searches ) + c.lastSearch = storedFilters; + c.$table.data( 'lastSearch', storedFilters ); + if ( wo.filter_saveFilters && ts.storage ) { + ts.storage( table, 'tablesorter-filters', storedFilters ); } - if (c.debug) { - ts.benchmark("Completed filter widget search", time); + if ( c.debug ) { + ts.benchmark( 'Completed filter widget search', time ); + } + if ( wo.filter_initialized ) { + c.$table.trigger( 'filterEnd', c ); } - if (wo.filter_initialized) { c.$table.trigger('filterEnd', c ); } - setTimeout(function(){ - c.$table.trigger('applyWidgets'); // make sure zebra widget is applied - }, 0); + setTimeout( function() { + c.$table.trigger( 'applyWidgets' ); // make sure zebra widget is applied + }, 0 ); }, - getOptionSource: function(table, column, onlyAvail) { - table = $(table)[0]; + getOptionSource: function( table, column, onlyAvail ) { + table = $( table )[0]; var cts, indx, len, c = table.config, wo = c.widgetOptions, parsed = [], arry = false, source = wo.filter_selectSource, - last = c.$table.data('lastSearch') || [], - fxn = $.isFunction(source) ? true : ts.getColumnData( table, source, column ); + last = c.$table.data( 'lastSearch' ) || [], + fxn = $.isFunction( source ) ? true : ts.getColumnData( table, source, column ); - if (onlyAvail && last[column] !== '') { + if ( onlyAvail && last[column] !== '' ) { onlyAvail = false; } // filter select source option - if (fxn === true) { + if ( fxn === true ) { // OVERALL source - arry = source(table, column, onlyAvail); - } else if ( fxn instanceof $ || ($.type(fxn) === 'string' && fxn.indexOf('</option>') >= 0) ) { + arry = source( table, column, onlyAvail ); + } else if ( fxn instanceof $ || ( $.type( fxn ) === 'string' && fxn.indexOf( '</option>' ) >= 0 ) ) { // selectSource is a jQuery object or string of options return fxn; - } else if ($.isArray(fxn)) { + } else if ( $.isArray( fxn ) ) { arry = fxn; - } else if ($.type(source) === 'object' && fxn) { + } else if ( $.type( source ) === 'object' && fxn ) { // custom select source function for a SPECIFIC COLUMN - arry = fxn(table, column, onlyAvail); + arry = fxn( table, column, onlyAvail ); } - if (arry === false) { + if ( arry === false ) { // fall back to original method - arry = ts.filter.getOptions(table, column, onlyAvail); + arry = ts.filter.getOptions( table, column, onlyAvail ); } // get unique elements and sort the list - // if $.tablesorter.sortText exists (not in the original tablesorter), + // if $.tablesorter.sortText exists ( not in the original tablesorter ), // then natural sort the list otherwise use a basic sort - arry = $.grep(arry, function(value, indx) { - return $.inArray(value, arry) === indx; + arry = $.grep( arry, function( value, indx ) { + return $.inArray( value, arry ) === indx; }); - if (c.$headerIndexed[column].hasClass('filter-select-nosort')) { + if ( c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) { // unsorted select options return arry; } else { len = arry.length; // parse select option values - for (indx = 0; indx < len; indx++) { + for ( indx = 0; indx < len; indx++ ) { // parse array data using set column parser; this DOES NOT pass the original // table cell to the parser format function - parsed.push({ t : arry[indx], p : c.parsers && c.parsers[column].format( arry[indx], table, [], column ) }); + parsed.push({ + t : arry[ indx ], + p : c.parsers && c.parsers[ column ].format( arry[ indx ], table, [], column ) + }); } // sort parsed select options cts = c.textSorter || ''; - parsed.sort(function(a, b){ + parsed.sort( function( a, b ) { // sortNatural breaks if you don't pass it strings - var x = a.p.toString(), y = b.p.toString(); - if ($.isFunction(cts)) { + var x = a.p.toString(), + y = b.p.toString(); + if ( $.isFunction( cts ) ) { // custom OVERALL text sorter - return cts(x, y, true, column, table); - } else if (typeof(cts) === 'object' && cts.hasOwnProperty(column)) { + return cts( x, y, true, column, table ); + } else if ( typeof( cts ) === 'object' && cts.hasOwnProperty( column ) ) { // custom text sorter for a SPECIFIC COLUMN - return cts[column](x, y, true, column, table); - } else if (ts.sortNatural) { + return cts[column]( x, y, true, column, table ); + } else if ( ts.sortNatural ) { // fall back to natural sort - return ts.sortNatural(x, y); + return ts.sortNatural( x, y ); } // using an older version! do a basic sort return true; @@ -1534,187 +1718,224 @@ ts.filter = { // rebuild arry from sorted parsed data arry = []; len = parsed.length; - for (indx = 0; indx < len; indx++) { + for ( indx = 0; indx < len; indx++ ) { arry.push( parsed[indx].t ); } return arry; } }, - getOptions: function(table, column, onlyAvail) { - table = $(table)[0]; - var rowIndex, tbodyIndex, len, row, cache, cell, + getOptions: function( table, column, onlyAvail ) { + table = $( table )[0]; + var rowIndex, tbodyIndex, len, row, cache, c = table.config, wo = c.widgetOptions, arry = []; - for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { cache = c.cache[tbodyIndex]; len = c.cache[tbodyIndex].normalized.length; // loop through the rows - for (rowIndex = 0; rowIndex < len; rowIndex++) { - // get cached row from cache.row (old) or row data object (new; last item in normalized array) - row = cache.row ? cache.row[rowIndex] : cache.normalized[rowIndex][c.columns].$row[0]; + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { + // get cached row from cache.row ( old ) or row data object + // ( new; last item in normalized array ) + row = cache.row ? + cache.row[ rowIndex ] : + cache.normalized[ rowIndex ][ c.columns ].$row[0]; // check if has class filtered - if (onlyAvail && row.className.match(wo.filter_filteredRow)) { continue; } + if ( onlyAvail && row.className.match( wo.filter_filteredRow ) ) { + continue; + } // get non-normalized cell content - if (wo.filter_useParsedData || c.parsers[column].parsed || c.$headerIndexed[column].hasClass('filter-parsed')) { - arry.push( '' + cache.normalized[rowIndex][column] ); + if ( wo.filter_useParsedData || + c.parsers[column].parsed || + c.$headerIndexed[column].hasClass( 'filter-parsed' ) ) { + arry.push( '' + cache.normalized[ rowIndex ][ column ] ); } else { - cell = row.cells[column]; - if (cell) { - arry.push( $.trim( cell.getAttribute( c.textAttribute ) || cell.textContent || $(cell).text() ) ); - } + // get raw cached data instead of content directly from the cells + arry.push( cache.normalized[ rowIndex ][ c.columns ].raw[ column ] ); } } } return arry; }, - buildSelect: function(table, column, arry, updating, onlyAvail) { - table = $(table)[0]; - column = parseInt(column, 10); - if (!table.config.cache || $.isEmptyObject(table.config.cache)) { return; } + buildSelect: function( table, column, arry, updating, onlyAvail ) { + table = $( table )[0]; + column = parseInt( column, 10 ); + if ( !table.config.cache || $.isEmptyObject( table.config.cache ) ) { + return; + } var indx, val, txt, t, $filters, $filter, c = table.config, wo = c.widgetOptions, - node = c.$headerIndexed[column], - // t.data('placeholder') won't work in jQuery older than 1.4.3 - options = '<option value="">' + ( node.data('placeholder') || node.attr('data-placeholder') || wo.filter_placeholder.select || '' ) + '</option>', + node = c.$headerIndexed[ column ], + // t.data( 'placeholder' ) won't work in jQuery older than 1.4.3 + options = '<option value="">' + + ( node.data( 'placeholder' ) || + node.attr( 'data-placeholder' ) || + wo.filter_placeholder.select || '' + ) + '</option>', // Get curent filter value - currentValue = c.$table.find('thead').find('select.' + tscss.filter + '[data-column="' + column + '"]').val(); - // nothing included in arry (external source), so get the options from filter_selectSource or column data - if (typeof arry === 'undefined' || arry === '') { - arry = ts.filter.getOptionSource(table, column, onlyAvail); + currentValue = c.$table + .find( 'thead' ) + .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) + .val(); + // nothing included in arry ( external source ), so get the options from + // filter_selectSource or column data + if ( typeof arry === 'undefined' || arry === '' ) { + arry = ts.filter.getOptionSource( table, column, onlyAvail ); } - if ($.isArray(arry)) { + if ( $.isArray( arry ) ) { // build option list - for (indx = 0; indx < arry.length; indx++) { - txt = arry[indx] = ('' + arry[indx]).replace(/\"/g, """); + for ( indx = 0; indx < arry.length; indx++ ) { + txt = arry[indx] = ( '' + arry[indx] ).replace( /\"/g, '"' ); val = txt; // allow including a symbol in the selectSource array - // "a-z|A through Z" so that "a-z" becomes the option value - // and "A through Z" becomes the option text - if (txt.indexOf(wo.filter_selectSourceSeparator) >= 0) { - t = txt.split(wo.filter_selectSourceSeparator); + // 'a-z|A through Z' so that 'a-z' becomes the option value + // and 'A through Z' becomes the option text + if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { + t = txt.split( wo.filter_selectSourceSeparator ); val = t[0]; txt = t[1]; } - // replace quotes - fixes #242 & ignore empty strings - see http://stackoverflow.com/q/14990971/145346 - options += arry[indx] !== '' ? '<option ' + (val === txt ? '' : 'data-function-name="' + arry[indx] + '" ') + 'value="' + val + '">' + txt + '</option>' : ''; + // replace quotes - fixes #242 & ignore empty strings + // see http://stackoverflow.com/q/14990971/145346 + options += arry[indx] !== '' ? + '<option ' + + ( val === txt ? '' : 'data-function-name="' + arry[indx] + '" ' ) + + 'value="' + val + '">' + txt + + '</option>' : ''; } // clear arry so it doesn't get appended twice arry = []; } - // update all selects in the same column (clone thead in sticky headers & any external selects) - fixes 473 - $filters = ( c.$filters ? c.$filters : c.$table.children('thead') ).find('.' + tscss.filter); - if (wo.filter_$externalFilters) { - $filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters; + // update all selects in the same column ( clone thead in sticky headers & + // any external selects ) - fixes 473 + $filters = ( c.$filters ? c.$filters : c.$table.children( 'thead' ) ) + .find( '.' + tscss.filter ); + if ( wo.filter_$externalFilters ) { + $filters = $filters && $filters.length ? + $filters.add( wo.filter_$externalFilters ) : + wo.filter_$externalFilters; } - $filter = $filters.filter('select[data-column="' + column + '"]'); + $filter = $filters.filter( 'select[data-column="' + column + '"]' ); // make sure there is a select there! - if ($filter.length) { - $filter[ updating ? 'html' : 'append' ](options); - if (!$.isArray(arry)) { + if ( $filter.length ) { + $filter[ updating ? 'html' : 'append' ]( options ); + if ( !$.isArray( arry ) ) { // append options if arry is provided externally as a string or jQuery object - // options (default value) was already added - $filter.append(arry).val(currentValue); + // options ( default value ) was already added + $filter.append( arry ).val( currentValue ); } - $filter.val(currentValue); + $filter.val( currentValue ); } }, - buildDefault: function(table, updating) { + buildDefault: function( table, updating ) { var columnIndex, $header, noSelect, c = table.config, wo = c.widgetOptions, columns = c.columns; // build default select dropdown - for (columnIndex = 0; columnIndex < columns; columnIndex++) { + for ( columnIndex = 0; columnIndex < columns; columnIndex++ ) { $header = c.$headerIndexed[columnIndex]; - noSelect = !($header.hasClass('filter-false') || $header.hasClass('parser-false')); + noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); // look for the filter-select class; build/update it if found - if (($header.hasClass('filter-select') || ts.getColumnData( table, wo.filter_functions, columnIndex ) === true) && noSelect) { - ts.filter.buildSelect(table, columnIndex, '', updating, $header.hasClass(wo.filter_onlyAvail)); + if ( ( $header.hasClass( 'filter-select' ) || + ts.getColumnData( table, wo.filter_functions, columnIndex ) === true ) && noSelect ) { + ts.filter.buildSelect( table, columnIndex, '', updating, $header.hasClass( wo.filter_onlyAvail ) ); } } } }; -ts.getFilters = function(table, getRaw, setFilters, skipFirst) { +ts.getFilters = function( table, getRaw, setFilters, skipFirst ) { var i, $filters, $column, cols, filters = false, - c = table ? $(table)[0].config : '', + c = table ? $( table )[0].config : '', wo = c ? c.widgetOptions : ''; - if (getRaw !== true && wo && !wo.filter_columnFilters) { - return $(table).data('lastSearch'); + if ( ( getRaw !== true && wo && !wo.filter_columnFilters ) || + // setFilters called, but last search is exactly the same as the current + // fixes issue #733 & #903 where calling update causes the input values to reset + ( $.isArray(setFilters) && setFilters.join('') === c.lastCombinedFilter ) ) { + return $( table ).data( 'lastSearch' ); } - if (c) { - if (c.$filters) { - $filters = c.$filters.find('.' + tscss.filter); + if ( c ) { + if ( c.$filters ) { + $filters = c.$filters.find( '.' + tscss.filter ); } - if (wo.filter_$externalFilters) { - $filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters; + if ( wo.filter_$externalFilters ) { + $filters = $filters && $filters.length ? + $filters.add( wo.filter_$externalFilters ) : + wo.filter_$externalFilters; } - if ($filters && $filters.length) { + if ( $filters && $filters.length ) { filters = setFilters || []; - for (i = 0; i < c.columns + 1; i++) { + for ( i = 0; i < c.columns + 1; i++ ) { cols = ( i === c.columns ? - // "all" columns can now include a range or set of columms (data-column="0-2,4,6-7") + // 'all' columns can now include a range or set of columms ( data-column='0-2,4,6-7' ) wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector : '[data-column="' + i + '"]' ); - $column = $filters.filter(cols); - if ($column.length) { + $column = $filters.filter( cols ); + if ( $column.length ) { // move the latest search to the first slot in the array $column = ts.filter.getLatestSearch( $column ); - if ($.isArray(setFilters)) { - // skip first (latest input) to maintain cursor position while typing - if (skipFirst) { $column.slice(1); } - if (i === c.columns) { - // prevent data-column="all" from filling data-column="0,1" (etc) - cols = $column.filter(wo.filter_anyColumnSelector); + if ( $.isArray( setFilters ) ) { + // skip first ( latest input ) to maintain cursor position while typing + if ( skipFirst ) { + $column.slice( 1 ); + } + if ( i === c.columns ) { + // prevent data-column='all' from filling data-column='0,1' ( etc ) + cols = $column.filter( wo.filter_anyColumnSelector ); $column = cols.length ? cols : $column; } $column - .val( setFilters[i] ) - .trigger('change.tsfilter'); + .val( setFilters[ i ] ) + .trigger( 'change.tsfilter' ); } else { filters[i] = $column.val() || ''; // don't change the first... it will move the cursor - if (i === c.columns) { - // don't update range columns from "all" setting - $column.slice(1).filter('[data-column*="' + $column.attr('data-column') + '"]').val( filters[i] ); + if ( i === c.columns ) { + // don't update range columns from 'all' setting + $column + .slice( 1 ) + .filter( '[data-column*="' + $column.attr( 'data-column' ) + '"]' ) + .val( filters[ i ] ); } else { - $column.slice(1).val( filters[i] ); + $column + .slice( 1 ) + .val( filters[ i ] ); } } // save any match input dynamically - if (i === c.columns && $column.length) { + if ( i === c.columns && $column.length ) { wo.filter_$anyMatch = $column; } } } } } - if (filters.length === 0) { + if ( filters.length === 0 ) { filters = false; } return filters; }; -ts.setFilters = function(table, filter, apply, skipFirst) { - var c = table ? $(table)[0].config : '', - valid = ts.getFilters(table, true, filter, skipFirst); - if (c && apply) { +ts.setFilters = function( table, filter, apply, skipFirst ) { + var c = table ? $( table )[0].config : '', + valid = ts.getFilters( table, true, filter, skipFirst ); + if ( c && apply ) { // ensure new set filters are applied, even if the search is the same c.lastCombinedFilter = null; c.lastSearch = []; - ts.filter.searching(c.table, filter, skipFirst); - c.$table.trigger('filterFomatterUpdate'); + ts.filter.searching( c.table, filter, skipFirst ); + c.$table.trigger( 'filterFomatterUpdate' ); } return !!valid; }; -})(jQuery); +})( jQuery ); /*! Widget: stickyHeaders - updated 3/26/2015 (v2.21.3) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ @@ -1722,7 +1943,7 @@ ts.setFilters = function(table, filter, apply, skipFirst) { */ ;(function ($, window) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; +var ts = $.tablesorter || {}; $.extend(ts.css, { sticky : 'tablesorter-stickyHeader', // stickyHeader @@ -1988,10 +2209,10 @@ ts.addWidget({ })(jQuery, window); -/*! Widget: resizable - updated 4/2/2015 (v2.21.5) */ +/*! Widget: resizable - updated 5/17/2015 (v2.22.0) */ ;(function ($, window) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; +var ts = $.tablesorter || {}; $.extend(ts.css, { resizableContainer : 'tablesorter-resizable-container', @@ -2280,7 +2501,7 @@ ts.addWidget({ init: function(table, thisWidget, c, wo) { ts.resizable.init( c, wo ); }, - remove: function( table, c, wo ) { + remove: function( table, c, wo, refreshing ) { if (wo.$resizable_container) { var namespace = c.namespace + 'tsresize'; c.$table.add( $( c.namespace + '_extra_table' ) ) @@ -2289,13 +2510,13 @@ ts.addWidget({ wo.$resizable_container.remove(); ts.resizable.toggleTextSelection( c, false ); - ts.resizableReset( table ); + ts.resizableReset( table, refreshing ); $( document ).unbind( 'mousemove' + namespace + ' mouseup' + namespace ); } } }); -ts.resizableReset = function( table, nosave ) { +ts.resizableReset = function( table, refreshing ) { $( table ).each(function(){ var index, $t, c = this.config, @@ -2312,7 +2533,7 @@ ts.resizableReset = function( table, nosave ) { } // reset stickyHeader widths $( window ).trigger( 'resize' ); - if ( ts.storage && !nosave ) { + if ( ts.storage && !refreshing ) { ts.storage( this, ts.css.resizableStorage, {} ); } } @@ -2324,7 +2545,7 @@ ts.resizableReset = function( table, nosave ) { /*! Widget: saveSort */ ;(function ($) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; +var ts = $.tablesorter || {}; // this widget saves the last sort only if the // saveSort widget option is true AND the diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js index 8f297b4..dbc30ad 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js @@ -2,12 +2,12 @@ /* Extract dates using popular natural language date parsers */ /*jshint jquery:true */ ;(function($){ -"use strict"; +'use strict'; /*! Sugar (http://sugarjs.com/dates#comparing_dates) */ /* demo: http://jsfiddle.net/Mottie/abkNM/4163/ */ $.tablesorter.addParser({ - id: "sugar", + id: 'sugar', is: function() { return false; }, @@ -15,13 +15,13 @@ var date = Date.create ? Date.create(s) : s ? new Date(s) : s; return date instanceof Date && isFinite(date) ? date.getTime() : s; }, - type: "numeric" + type: 'numeric' }); /*! Datejs (http://www.datejs.com/) */ /* demo: http://jsfiddle.net/Mottie/abkNM/4164/ */ $.tablesorter.addParser({ - id: "datejs", + id: 'datejs', is: function() { return false; }, @@ -29,7 +29,7 @@ var date = Date.parse ? Date.parse(s) : s ? new Date(s) : s; return date instanceof Date && isFinite(date) ? date.getTime() : s; }, - type: "numeric" + type: 'numeric' }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-globalize.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-globalize.js new file mode 100644 index 0000000..af69a1f --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-globalize.js @@ -0,0 +1,46 @@ +/*! Parser: jQuery Globalize - updated 5/17/2015 (v2.22.0) */ +/* Extract localized data using jQuery's Globalize parsers; set + Globalize.locale( 'xx' ) prior to initializing tablesorter! */ +/*jshint jquery:true */ +;( function( $ ) { +'use strict'; + + /*! jQuery Globalize date parser (https://github.com/jquery/globalize#date-module) */ + /* demo: http://jsfiddle.net/Mottie/0j18Lw8r/ */ + $.tablesorter.addParser({ + id: 'globalize-date', + is: function () { + return false; + }, + format: function ( str, table, cell, cellIndex ) { + var c = table.config, + // add options to 'config.globalize' for all columns --> globalize : { skeleton: 'GyMMMd' } + // or per column by using the column index --> globalize : { 0 : { datetime: 'medium' } } + options = c.globalize && ( c.globalize[ cellIndex ] || c.globalize ) || {}, + date = Globalize && Globalize.dateParser ? Globalize.dateParser( options )( str ) : + str ? new Date( str ) : str; + return date instanceof Date && isFinite( date ) ? date.getTime() : str; + }, + type: 'numeric' + }); + + /*! jQuery Globalize number parser (https://github.com/jquery/globalize#number-module) */ + /* demo: http://jsfiddle.net/Mottie/0j18Lw8r/ */ + $.tablesorter.addParser({ + id: 'globalize-number', + is: function () { + return false; + }, + format: function ( str, table, cell, cellIndex ) { + var c = table.config, + // add options to 'config.globalize' for all columns --> globalize : { skeleton: 'GyMMMd' } + // or per column by using the column index --> globalize : { 0 : { datetime: 'medium' } } + options = c.globalize && ( c.globalize[ cellIndex ] || c.globalize ) || {}, + num = Globalize && Globalize.numberParser ? Globalize.numberParser( options )( str ) : + str ? $.tablesorter.formatFloat( ( str || '' ).replace( /[^\w,. \-()]/g, '' ), table ) : str; + return str && typeof num === 'number' ? num : str; + }, + type: 'numeric' + }); + +})( jQuery ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index 008d465..e009e84 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,12 +1,12 @@ -/*! Parser: input & select - updated 3/26/2015 (v2.21.3) *//* +/*! Parser: input & select - updated 5/17/2015 (v2.22.0) *//* * for jQuery 1.7+ & tablesorter 2.7.11+ * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ /*jshint browser: true, jquery:true, unused:false */ -;(function($){ -"use strict"; +;( function( $ ) { +'use strict'; - var updateServer = function(event, $table, $input){ + var updateServer = function( event, $table, $input ) { // do something here to update your server, if needed // event = change event object // $table = jQuery object of the table that was just updated @@ -14,140 +14,164 @@ }; // Custom parser for parsing input values - // updated dynamically using the "change" function below + // updated dynamically using the 'change' function below $.tablesorter.addParser({ - id: "inputs", - is: function(){ + id : 'inputs', + is : function() { return false; }, - format: function(s, table, cell) { - return $(cell).find('input').val() || s; + format : function( txt, table, cell ) { + var $input = $( cell ).find( 'input' ); + return $input.length ? $input.val() : txt; }, parsed : true, // filter widget flag - type: "text" + type : 'text' + }); + + $.tablesorter.addParser({ + id : 'inputs-numeric', + is : function() { + return false; + }, + format : function( txt, table, cell ) { + var $input = $( cell ).find( 'input' ); + var val = $input.length ? $input.val() : txt, + num = $.tablesorter.formatFloat( ( val || '' ).replace( /[^\w,. \-()]/g, '' ), table ); + return txt && typeof num === 'number' ? num : + txt ? $.trim( txt && table.config.ignoreCase ? txt.toLocaleLowerCase() : txt ) : txt; + }, + parsed : true, // filter widget flag + type : 'numeric' }); // Custom parser for including checkbox status if using the grouping widget - // updated dynamically using the "change" function below + // updated dynamically using the 'change' function below $.tablesorter.addParser({ - id: "checkbox", - is: function(){ + id : 'checkbox', + is : function() { return false; }, - format: function(s, table, cell, cellIndex) { - var $c = $(cell), + format : function( txt, table, cell, cellIndex ) { + var $cell = $( cell ), wo = table.config.widgetOptions, // returning plain language here because this is what is shown in the // group headers - change it as desired - txt = wo.group_checkbox ? wo.group_checkbox : [ 'checked', 'unchecked' ], - $input = $c.find('input[type="checkbox"]'), - isChecked = $input.length ? $input[0].checked : ''; + status = wo.group_checkbox ? wo.group_checkbox : [ 'checked', 'unchecked' ], + $input = $cell.find( 'input[type="checkbox"]' ), + isChecked = $input.length ? $input[ 0 ].checked : ''; // adding class to row, indicating that a checkbox is checked; includes // a column index in case more than one checkbox happens to be in a row - $c.closest('tr').toggleClass('checked-' + cellIndex, isChecked); - return $input.length ? txt[ isChecked ? 0 : 1 ] : s; + $cell.closest( 'tr' ).toggleClass( 'checked checked-' + cellIndex, isChecked ); + return $input.length ? status[ isChecked ? 0 : 1 ] : txt; }, parsed : true, // filter widget flag - type: "text" + type : 'text' }); // Custom parser which returns the currently selected options - // updated dynamically using the "change" function below + // updated dynamically using the 'change' function below $.tablesorter.addParser({ - id: "select", - is: function(){ + id : 'select', + is : function() { return false; }, - format: function(s, table, cell) { - return $(cell).find('select').val() || s; + format : function( txt, table, cell ) { + var $select = $( cell ).find( 'select' ); + return $select.length ? $select.val() : txt; }, parsed : true, // filter widget flag - type: "text" + type : 'text' }); // Select parser to get the selected text $.tablesorter.addParser({ - id: "select-text", - is: function(){ + id : 'select-text', + is : function() { return false; }, - format: function(s, table, cell) { - var $s = $(cell).find('select'); - return $s.length ? $s.find('option:selected').text() || '' : s; + format : function( txt, table, cell ) { + var $select = $( cell ).find( 'select' ); + return $select.length ? $select.find( 'option:selected' ).text() || '' : txt; }, parsed : true, // filter widget flag - type: "text" + type : 'text' }); // Custom parser for parsing textarea values - // updated dynamically using the "change" function below + // updated dynamically using the 'change' function below $.tablesorter.addParser({ - id: "textarea", - is: function(){ + id : 'textarea', + is : function() { return false; }, - format: function(s, table, cell) { - return $(cell).find('textarea').val() || s; + format : function( txt, table, cell ) { + var $textarea = $( cell ).find( 'textarea' ); + return $textarea.length ? $textarea.val() : txt; }, parsed : true, // filter widget flag - type: "text" + type : 'text' }); // update select and all input types in the tablesorter cache when the change event fires. // This method only works with jQuery 1.7+ // you can change it to use delegate (v1.4.3+) or live (v1.3+) as desired // if this code interferes somehow, target the specific table $('#mytable'), instead of $('table') - $(function(){ - $('table').on('tablesorter-initialized', function(){ - var restoreValue = function(isTbody){ + $( function() { + $( 'table' ).on( 'tablesorter-initialized updateComplete', function() { + var namespace = '.parser-forms', + restoreValue = function( isTbody ) { // make sure we restore original values (trigger blur) // isTbody is needed to prevent the select from closing in IE // see https://connect.microsoft.com/IE/feedbackdetail/view/962618/ - if (isTbody) { - $(':focus').blur(); + if ( isTbody ) { + $( ':focus' ).blur(); } return; }; // bind to .tablesorter (default class name) - $(this).children('tbody') - .on('mouseleave', function(e){ - restoreValue(e.target.nodeName === 'TBODY'); + $( this ).children( 'tbody' ) + .off( namespace ) + .on( 'mouseleave' + namespace, function( event ) { + restoreValue( event.target.nodeName === 'TBODY' ); }) - .on('focus', 'select, input, textarea', function(){ - $(this).data('ts-original-value', this.value); + .on( 'focus' + namespace, 'select, input, textarea', function() { + $( this ).data( 'ts-original-value', this.value ); }) - .on('blur', 'input, textarea', function(){ + .on( 'blur' + namespace, 'input, textarea', function() { // restore input value; - // "change" is triggered before "blur" so this doesn't replace the new update with the original - this.value = $(this).data('ts-original-value'); + // 'change' is triggered before 'blur' so this doesn't replace the new update with the original + this.value = $( this ).data( 'ts-original-value' ); }) - .on('change keyup', 'select, input, textarea', function(e){ - if ( e.which === 27 ) { + .on( 'change keyup '.split( ' ' ).join( namespace + ' ' ), 'select, input, textarea', function( event ) { + if ( event.which === 27 ) { // escape: restore original value - this.value = $(this).data('ts-original-value'); + this.value = $( this ).data( 'ts-original-value' ); return; } // Update cell cache using... select: change, input: enter or textarea: alt + enter - if ( ( e.type === 'change' ) || - ( e.type === 'keyup' && e.which === 13 && ( e.target.nodeName === 'INPUT' || e.target.nodeName === 'TEXTAREA' && e.altKey ) ) ) { + if ( event.type === 'change' || + ( event.type === 'keyup' && event.which === 13 && + ( event.target.nodeName === 'INPUT' || event.target.nodeName === 'TEXTAREA' && event.altKey ) ) ) { var undef, - $tar = $(e.target), - $cell = $tar.closest('td'), - $table = $cell.closest('table'), - indx = $cell[0].cellIndex, - c = $table[0].config || false, - $hdr = c && c.$headers && c.$headers.eq(indx); - // abort if not a tablesorter table, or - // don't use updateCell if column is set to "sorter-false" and "filter-false", or column is set to "parser-false" - if ( !c || ( $hdr && $hdr.length && ( $hdr.hasClass('parser-false') || ( $hdr.hasClass('sorter-false') && $hdr.hasClass('filter-false') ) ) ) ) { - return restoreValue(); + $target = $( event.target ), + $cell = $target.closest( 'td' ), + $table = $cell.closest( 'table' ), + indx = $cell[ 0 ].cellIndex, + c = $table[ 0 ].config || false, + $hdr = c && c.$headerIndexed && c.$headerIndexed[ indx ] || [], + val = $target.val(); + // abort if not a tablesorter table, or don't use updateCell if column is set + // to 'sorter-false' and 'filter-false', or column is set to 'parser-false' + if ( $hdr.length && ( $hdr.hasClass( 'parser-false' ) || + ( $hdr.hasClass( 'sorter-false' ) && $hdr.hasClass( 'filter-false' ) ) ) ) { + return; } // ignore change event if nothing changed - if ($tar.val() !== $tar.data('ts-original-value') || e.target.type === 'checkbox') { - $tar.data('ts-original-value', $tar.val()); + if ( val !== $target.data( 'ts-original-value' ) || event.target.type === 'checkbox' ) { + $target.data( 'ts-original-value', val ); // pass undefined resort value so it falls back to config.resort setting - $table.trigger('updateCell', [ $tar.closest('td'), undef, function(){ - updateServer(e, $table, $tar); + $table.trigger( 'updateCell', [ $cell, undef, function() { + updateServer( event, $table, $target ); } ]); } } @@ -155,4 +179,4 @@ }); }); -})(jQuery); +})( jQuery ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js index 92bd007..53bfd8f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js @@ -81,12 +81,13 @@ }, result, group, negativeRegex = new RegExp('(' + named.negative.join('|') + ')'), - calc = function ( word, table ) { - var num = named.numbers.hasOwnProperty( word ) ? named.numbers[ word ] : null, + calc = function ( rawWord, table ) { + // remove extra characters that might be next to the word + var word = rawWord.replace( /[,."']/g, '' ), + // formatFloat will deal with the commas & decimals in the number format + num = $.tablesorter.formatFloat( rawWord || '', table ), power = named.powers.hasOwnProperty( word ) ? named.powers[ word ] : null; - if ( !num && !isNaN( word ) ) { - num = $.tablesorter.formatFloat( word || '', table ); - } + num = typeof num === 'number' ? num : named.numbers.hasOwnProperty( word ) ? named.numbers[ word ] : null; if ( num !== null ) { group += num; } else if ( word === named.hundred ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js index 1b2dfbc..7175578 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js @@ -1,8 +1,8 @@ -/*! Parser: network - updated 10/26/2014 (v2.18.0) */ +/*! Parser: network - updated 5/17/2015 (v2.22.0) */ /* IPv4, IPv6 and MAC Addresses */ /*global jQuery: false */ ;(function($){ - "use strict"; + 'use strict'; var ts = $.tablesorter, ipv4Format, @@ -10,8 +10,8 @@ /*! IPv6 Address parser (WIP) *//* * IPv6 Address (ffff:0000:0000:0000:0000:0000:0000:0000) - * needs to support short versions like "::8" or "1:2::7:8" - * and "::00:192.168.10.184" (embedded IPv4 address) + * needs to support short versions like '::8' or '1:2::7:8' + * and '::00:192.168.10.184' (embedded IPv4 address) * see http://www.intermapper.com/support/tools/IPV6-Validator.aspx */ $.extend( ts.regex, {}, { @@ -93,7 +93,7 @@ }; /*! Parser: ipv4Address (a.k.a. ipAddress) */ - // duplicate "ipAddress" as "ipv4Address" (to maintain backwards compatility) + // duplicate 'ipAddress' as 'ipv4Address' (to maintain backwards compatility) ts.addParser({ id: 'ipAddress', is: ipv4Is, @@ -108,21 +108,30 @@ }); /*! Parser: MAC address */ + /* MAC examples: 12:34:56:78:9A:BC, 1234.5678.9ABC, 12-34-56-78-9A-BC, and 123456789ABC + */ ts.addParser({ - id: 'MAC', - is: function(s) { - return ts.regex.ipv6Validate.test(s); + id : 'MAC', + is : function( str ) { + return false; }, - format: function(s) { - var t = '', - val = s.replace(/[:.-]/g, '').match(/\w{2}/g); - $.each(val, function(i, v){ - t += ( '000' + parseInt(v, 16) ).slice(-3); - }); - return t; + format : function( str ) { + var indx, len, + mac = '', + val = ( str || '' ).replace( /[:.-]/g, '' ).match( /\w{2}/g ); + if ( val ) { + // not assuming all mac addresses in the column will end up with six + // groups of two to process, so it's not actually validating the address + len = val.length; + for ( indx = 0; indx < len; indx++ ) { + mac += ( '000' + parseInt( val[ indx ], 16 ) ).slice( -3 ); + } + return mac; + } + return str; }, // uses natural sort hex compare - type: 'numeric' + type : 'numeric' }); -})(jQuery); +})( jQuery ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columns.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columns.js index e37751b..30a2b1f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columns.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columns.js @@ -1,7 +1,7 @@ /*! Widget: columns */ ;(function ($) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; +var ts = $.tablesorter || {}; ts.addWidget({ id: "columns", diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js index f967aed..a0d519a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js @@ -1,4 +1,4 @@ -/*! Widget: editable - updated 2/9/2015 (v2.19.1) *//* +/*! Widget: editable - updated 5/17/2015 (v2.22.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -8,10 +8,13 @@ 'use strict'; var tse = $.tablesorter.editable = { + namespace : '.tseditable', + // last edited class name + lastEdited: 'tseditable-last-edited-cell', editComplete: function( c, wo, $cell, refocus ) { $cell - .removeClass( 'tseditable-last-edited-cell' ) + .removeClass( tse.lastEdited ) .trigger( wo.editable_editComplete, [ c ] ); // restore focus last cell after updating if ( refocus ) { @@ -25,16 +28,23 @@ var tse = $.tablesorter.editable = { setTimeout( function() { // select all text in contenteditable // see http://stackoverflow.com/a/6150060/145346 - var sel, range = document.createRange(); - range.selectNodeContents( cell ); - sel = window.getSelection(); - sel.removeAllRanges(); - sel.addRange( range ); + var range, selection; + if ( document.body.createTextRange ) { + range = document.body.createTextRange(); + range.moveToElementText( cell ); + range.select(); + } else if ( window.getSelection ) { + selection = window.getSelection(); + range = document.createRange(); + range.selectNodeContents( cell ); + selection.removeAllRanges(); + selection.addRange( range ); + } }, 100 ); }, - update: function( c, wo ) { - var indx, tmp, $t, + getColumns : function( c, wo ) { + var indx, tmp, colIndex = [], cols = []; if ( !wo.editable_columnsArray && $.type( wo.editable_columns ) === 'string' && wo.editable_columns.indexOf( '-' ) >= 0 ) { @@ -61,22 +71,33 @@ var tse = $.tablesorter.editable = { wo.editable_columnsArray = colIndex; wo.editable_columnsArray.sort(function(a,b){ return a - b; }); } - tmp = $( '<div>' ).wrapInner( wo.editable_wrapContent ).children().length || $.isFunction( wo.editable_wrapContent ); + return cols; + }, + + update: function( c, wo ) { + var $t, + tmp = $( '<div>' ).wrapInner( wo.editable_wrapContent ).children().length || $.isFunction( wo.editable_wrapContent ), + cols = tse.getColumns( c, wo ).join( ',' ); + + // turn off contenteditable to allow dynamically setting the wo.editable_noEdit + // class on table cells - see issue #900 + c.$tbodies.find( cols ).find( '[contenteditable]' ).prop( 'contenteditable', false ); + // IE does not allow making TR/TH/TD cells directly editable ( issue #404 ) // so add a div or span inside ( it's faster than using wrapInner() ) - c.$tbodies.find( cols.join( ',' ) ).not( '.' + wo.editable_noEdit ).each( function() { + c.$tbodies.find( cols ).not( '.' + wo.editable_noEdit ).each( function() { // test for children, if they exist, then make the children editable $t = $( this ); - if ( tmp && $t.children().length === 0 ) { + if ( tmp && $t.children( 'div, span' ).length === 0 ) { $t.wrapInner( wo.editable_wrapContent ); } - if ( $t.children().length ) { - // make all children content editable - $t.children().not( '.' + wo.editable_noEdit ).each( function() { + if ( $t.children( 'div, span' ).length ) { + // make div/span children content editable + $t.children( 'div, span' ).not( '.' + wo.editable_noEdit ).each( function() { var $this = $( this ); if ( wo.editable_trimContent ) { - $this.text( function( i, txt ) { + $this.html( function( i, txt ) { return $.trim( txt ); }); } @@ -84,7 +105,7 @@ var tse = $.tablesorter.editable = { }); } else { if ( wo.editable_trimContent ) { - $t.text( function( i, txt ) { + $t.html( function( i, txt ) { return $.trim( txt ); }); } @@ -94,36 +115,45 @@ var tse = $.tablesorter.editable = { }, bindEvents: function( c, wo ) { + var namespace = tse.namespace; c.$table - .off( ( 'updateComplete pagerComplete '.split( ' ' ).join( '.tseditable ' ) ).replace( /\s+/g, ' ' ) ) - .on( 'updateComplete pagerComplete '.split( ' ' ).join( '.tseditable ' ), function() { + .off( ( 'updateComplete pagerComplete '.split( ' ' ).join( namespace + ' ' ) ).replace( /\s+/g, ' ' ) ) + .on( 'updateComplete pagerComplete '.split( ' ' ).join( namespace + ' ' ), function() { tse.update( c, c.widgetOptions ); - }); - - c.$tbodies - .off( ( 'mouseleave focus blur focusout keydown '.split( ' ' ).join( '.tseditable ' ) ).replace( /\s+/g, ' ' ) ) - .on( 'mouseleave.tseditable', function() { + }) + // prevent sort initialized by user click on the header from changing the row indexing before + // updateCell can finish processing the change + .children( 'thead' ) + .add( $( c.namespace + '_extra_table' ).children( 'thead' ) ) + .off( 'mouseenter' + namespace ) + .on( 'mouseenter' + namespace, function() { if ( c.$table.data( 'contentFocused' ) ) { // change to 'true' instead of element to allow focusout to process c.$table.data( 'contentFocused', true ); $( ':focus' ).trigger( 'focusout' ); } - }) - .on( 'focus.tseditable', '[contenteditable]', function( e ) { + }); + + c.$tbodies + .off( ( 'focus blur focusout keydown '.split( ' ' ).join( namespace + ' ' ) ).replace( /\s+/g, ' ' ) ) + .on( 'focus' + namespace, '[contenteditable]', function( e ) { clearTimeout( $( this ).data( 'timer' ) ); c.$table.data( 'contentFocused', e.target ); var $this = $( this ), selAll = wo.editable_selectAll, column = $this.closest( 'td' ).index(), - txt = $.trim( $this.text() ); - if ( wo.editable_enterToAccept ) { - // prevent enter from adding into the content - $this.on( 'keydown.tseditable', function( e ){ - if ( e.which === 13 ) { + txt = $this.html(); + if ( wo.editable_trimContent ) { + txt = $.trim( txt ); + } + // prevent enter from adding into the content + $this + .off( 'keydown' + namespace ) + .on( 'keydown' + namespace, function( e ){ + if ( wo.editable_enterToAccept && e.which === 13 ) { e.preventDefault(); } }); - } $this.data({ before : txt, original: txt }); if ( typeof wo.editable_focused === 'function' ) { @@ -140,16 +170,19 @@ var tse = $.tablesorter.editable = { } } }) - .on( 'blur focusout keydown '.split( ' ' ).join( '.tseditable ' ), '[contenteditable]', function( e ) { + .on( 'blur focusout keydown '.split( ' ' ).join( namespace + ' ' ), '[contenteditable]', function( e ) { if ( !c.$table.data( 'contentFocused' ) ) { return; } var t, validate, valid = false, $this = $( e.target ), - txt = $.trim( $this.text() ), + txt = $this.html(), column = $this.closest( 'td' ).index(); + if ( wo.editable_trimContent ) { + txt = $.trim( txt ); + } if ( e.which === 27 ) { // user cancelled - $this.html( $.trim( $this.data( 'original' ) ) ).trigger( 'blur.tseditable' ); + $this.html( $this.data( 'original' ) ).trigger( 'blur' + namespace ); c.$table.data( 'contentFocused', false ); return false; } @@ -168,10 +201,10 @@ var tse = $.tablesorter.editable = { } if ( t && valid !== false ) { - c.$table.find( '.tseditable-last-edited-cell' ).removeClass( 'tseditable-last-edited-cell' ); + c.$table.find( '.' + tse.lastEdited ).removeClass( tse.lastEdited ); $this - .addClass( 'tseditable-last-edited-cell' ) - .html( $.trim( valid ) ) + .addClass( tse.lastEdited ) + .html( valid ) .data( 'before', valid ) .data( 'original', valid ) .trigger( 'change' ); @@ -179,11 +212,11 @@ var tse = $.tablesorter.editable = { if ( wo.editable_autoResort ) { setTimeout( function() { c.$table.trigger( 'sorton', [ c.sortList, function() { - tse.editComplete( c, wo, c.$table.find( '.tseditable-last-edited-cell' ), true ); + tse.editComplete( c, wo, c.$table.find( '.' + tse.lastEdited ), true ); }, true ] ); }, 10 ); } else { - tse.editComplete( c, wo, c.$table.find( '.tseditable-last-edited-cell' ) ); + tse.editComplete( c, wo, c.$table.find( '.' + tse.lastEdited ) ); } } ] ); return false; @@ -192,13 +225,28 @@ var tse = $.tablesorter.editable = { clearTimeout( $this.data( 'timer' ) ); $this.data( 'timer', setTimeout( function() { if ( $.isFunction( wo.editable_blur ) ) { - wo.editable_blur( $.trim( $this.text() ), column, $this ); + txt = $this.html(); + wo.editable_blur( wo.editable_trimContent ? $.trim( txt ) : txt, column, $this ); } }, 100 ) ); // restore original content on blur - $this.html( $.trim( $this.data( 'original' ) ) ); + $this.html( $this.data( 'original' ) ); } }); + }, + destroy : function( c, wo ) { + var namespace = tse.namespace, + cols = tse.getColumns( c, wo ), + + tmp = ( 'updateComplete pagerComplete '.split( ' ' ).join( namespace + ' ' ) ).replace( /\s+/g, ' ' ); + c.$table.off( tmp ); + + tmp = ( 'focus blur focusout keydown '.split( ' ' ).join( namespace + ' ' ) ).replace( /\s+/g, ' ' ); + c.$tbodies + .off( tmp ) + .find( cols.join( ',' ) ) + .find( '[contenteditable]' ) + .prop( 'contenteditable', false ); } }; @@ -223,6 +271,11 @@ var tse = $.tablesorter.editable = { if ( !wo.editable_columns.length ) { return; } tse.update( c, wo ); tse.bindEvents( c, wo ); + }, + remove : function( table, c, wo, refreshing ) { + if ( !refreshing ) { + tse.destroy( c, wo ) ; + } } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 3e43f75..1472ac3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,13 +1,13 @@ -/*! Widget: filter - updated 3/26/2015 (v2.21.3) *//* +/*! Widget: filter - updated 5/17/2015 (v2.22.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ -;(function ($) { +;( function ( $ ) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}, +var ts = $.tablesorter || {}, tscss = ts.css; -$.extend(tscss, { +$.extend( tscss, { filterRow : 'tablesorter-filter-row', filter : 'tablesorter-filter', filterDisabled : 'disabled', @@ -15,26 +15,27 @@ $.extend(tscss, { }); ts.addWidget({ - id: "filter", + id: 'filter', priority: 50, options : { filter_childRows : false, // if true, filter includes child row content in the search + filter_childByColumn : false, // ( filter_childRows must be true ) if true = search child rows by column; false = search all child row text grouped filter_columnFilters : true, // if true, a filter will be added to the top of each table column - filter_columnAnyMatch: true, // if true, allows using "#:{query}" in AnyMatch searches (column:query) - filter_cellFilter : '', // css class name added to the filter cell (string or array) - filter_cssFilter : '', // css class name added to the filter row & each input in the row (tablesorter-filter is ALWAYS added) - filter_defaultFilter : {}, // add a default column filter type "~{query}" to make fuzzy searches default; "{q1} AND {q2}" to make all searches use a logical AND. + filter_columnAnyMatch: true, // if true, allows using '#:{query}' in AnyMatch searches ( column:query ) + filter_cellFilter : '', // css class name added to the filter cell ( string or array ) + filter_cssFilter : '', // css class name added to the filter row & each input in the row ( tablesorter-filter is ALWAYS added ) + filter_defaultFilter : {}, // add a default column filter type '~{query}' to make fuzzy searches default; '{q1} AND {q2}' to make all searches use a logical AND. filter_excludeFilter : {}, // filters to exclude, per column - filter_external : '', // jQuery selector string (or jQuery object) of external filters + filter_external : '', // jQuery selector string ( or jQuery object ) of external filters filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin filter_formatter : null, // add custom filter elements to the filter row filter_functions : null, // add custom filter functions using this option filter_hideEmpty : true, // hide filter row when table is empty filter_hideFilters : false, // collapse filter row when mouse leaves the area filter_ignoreCase : true, // if true, make all searches case-insensitive - filter_liveSearch : true, // if true, search column content while the user types (with a delay) - filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available (visible) options within the drop down - filter_placeholder : { search : '', select : '' }, // default placeholder text (overridden by any header "data-placeholder" setting) + filter_liveSearch : true, // if true, search column content while the user types ( with a delay ) + filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available ( visible ) options within the drop down + filter_placeholder : { search : '', select : '' }, // default placeholder text ( overridden by any header 'data-placeholder' setting ) filter_reset : null, // jQuery selector string of an element used to reset the filters filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters filter_searchDelay : 300, // typing delay in milliseconds before starting a search @@ -46,37 +47,38 @@ ts.addWidget({ filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value filter_selectSourceSeparator : '|' // filter_selectSource array text left of the separator is added to the option value, right into the option text }, - format: function(table, c, wo) { - if (!c.$table.hasClass('hasFilters')) { - ts.filter.init(table, c, wo); + format: function( table, c, wo ) { + if ( !c.$table.hasClass( 'hasFilters' ) ) { + ts.filter.init( table, c, wo ); } }, - remove: function(table, c, wo, refreshing) { + remove: function( table, c, wo, refreshing ) { var tbodyIndex, $tbody, $table = c.$table, $tbodies = c.$tbodies, - events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter '); + events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' + .split( ' ' ).join( c.namespace + 'filter ' ); $table - .removeClass('hasFilters') + .removeClass( 'hasFilters' ) // add .tsfilter namespace to all BUT search - .unbind( events.replace(/\s+/g, ' ') ) + .unbind( events.replace( /\s+/g, ' ' ) ) // remove the filter row even if refreshing, because the column might have been moved - .find('.' + tscss.filterRow).remove(); - if (refreshing) { return; } - for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { - $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody - $tbody.children().removeClass(wo.filter_filteredRow).show(); - ts.processTbody(table, $tbody, false); // restore tbody + .find( '.' + tscss.filterRow ).remove(); + if ( refreshing ) { return; } + for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody( table, $tbodies.eq( tbodyIndex ), true ); // remove tbody + $tbody.children().removeClass( wo.filter_filteredRow ).show(); + ts.processTbody( table, $tbody, false ); // restore tbody } - if (wo.filter_reset) { - $(document).undelegate(wo.filter_reset, 'click.tsfilter'); + if ( wo.filter_reset ) { + $( document ).undelegate( wo.filter_reset, 'click.tsfilter' ); } } }); ts.filter = { - // regex used in filter "check" functions - not for general use and not documented + // regex used in filter 'check' functions - not for general use and not documented regex: { regex : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // regex to test for regex child : /tablesorter-childRow/, // child row class name; this gets updated in the script @@ -89,22 +91,33 @@ ts.filter = { }, // function( c, data ) { } // c = table.config - // data.filter = array of filter input values; - // data.iFilter = same array, except lowercase (if wo.filter_ignoreCase is true) - // data.exact = table cell text (or parsed data if column parser enabled) - // data.iExact = same as data.exact, except lowercase (if wo.filter_ignoreCase is true) - // data.cache = table cell text from cache, so it has been parsed (& in all lower case if config.ignoreCase is true) - // data.index = column index; table = table element (DOM) - // data.parsed = array (by column) of boolean values (from filter_useParsedData or "filter-parsed" class) + // data.$row = jQuery object of the row currently being processed + // data.$cells = jQuery object of all cells within the current row + // data.filters = array of filters for all columns ( some may be undefined ) + // data.filter = filter for the current column + // data.iFilter = same as data.filter, except lowercase ( if wo.filter_ignoreCase is true ) + // data.exact = table cell text ( or parsed data if column parser enabled ) + // data.iExact = same as data.exact, except lowercase ( if wo.filter_ignoreCase is true ) + // data.cache = table cell text from cache, so it has been parsed ( & in all lower case if c.ignoreCase is true ) + // data.cacheArray = An array of parsed content from each table cell in the row being processed + // data.index = column index; table = table element ( DOM ) + // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class ) types: { // Look for regex regex: function( c, data ) { - if ( ts.filter.regex.regex.test(data.iFilter) ) { + if ( ts.filter.regex.regex.test( data.filter ) ) { var matches, - regex = ts.filter.regex.regex.exec(data.iFilter); + // cache regex per column for optimal speed + regex = data.filter_regexCache[ data.index ] || ts.filter.regex.regex.exec( data.filter ), + isRegex = regex instanceof RegExp; try { - matches = new RegExp(regex[1], regex[2]).test( data.iExact ); - } catch (error) { + if ( !isRegex ) { + // force case insensitive search if ignoreCase option set? + // if ( c.ignoreCase && !regex[2] ) { regex[2] = 'i'; } + data.filter_regexCache[ data.index ] = regex = new RegExp( regex[1], regex[2] ); + } + matches = regex.test( data.exact ); + } catch ( error ) { matches = false; } return matches; @@ -113,46 +126,56 @@ ts.filter = { }, // Look for operators >, >=, < or <= operators: function( c, data ) { - if ( /^[<>]=?/.test(data.iFilter) ) { - var cachedValue, result, + // ignore empty strings... because '' < 10 is true + if ( /^[<>]=?/.test( data.iFilter ) && data.iExact !== '' ) { + var cachedValue, result, txt, table = c.table, index = data.index, parsed = data.parsed[index], - query = ts.formatFloat( data.iFilter.replace(ts.filter.regex.operators, ''), table ), + query = ts.formatFloat( data.iFilter.replace( ts.filter.regex.operators, '' ), table ), parser = c.parsers[index], savedSearch = query; - // parse filter value in case we're comparing numbers (dates) - if (parsed || parser.type === 'numeric') { - result = ts.filter.parseFilter(c, $.trim('' + data.iFilter.replace(ts.filter.regex.operators, '')), index, parsed, true); - query = ( typeof result === "number" && result !== '' && !isNaN(result) ) ? result : query; + // parse filter value in case we're comparing numbers ( dates ) + if ( parsed || parser.type === 'numeric' ) { + txt = $.trim( '' + data.iFilter.replace( ts.filter.regex.operators, '' ) ); + result = ts.filter.parseFilter( c, txt, index, true ); + query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query; } - // iExact may be numeric - see issue #149; - // check if cached is defined, because sometimes j goes out of range? (numeric columns) - cachedValue = ( parsed || parser.type === 'numeric' ) && !isNaN(query) && typeof data.cache !== 'undefined' ? data.cache : - isNaN(data.iExact) ? ts.formatFloat( data.iExact.replace(ts.filter.regex.nondigit, ''), table) : - ts.formatFloat( data.iExact, table ); - - if ( />/.test(data.iFilter) ) { result = />=/.test(data.iFilter) ? cachedValue >= query : cachedValue > query; } - if ( /</.test(data.iFilter) ) { result = /<=/.test(data.iFilter) ? cachedValue <= query : cachedValue < query; } + // check if cached is defined, because sometimes j goes out of range? ( numeric columns ) + if ( ( parsed || parser.type === 'numeric' ) && !isNaN( query ) && + typeof data.cache !== 'undefined' ) { + cachedValue = data.cache; + } else { + txt = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; + cachedValue = ts.formatFloat( txt, table ); + } + if ( />/.test( data.iFilter ) ) { + result = />=/.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; + } else if ( /</.test( data.iFilter ) ) { + result = /<=/.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; + } // keep showing all rows if nothing follows the operator - if ( !result && savedSearch === '' ) { result = true; } + if ( !result && savedSearch === '' ) { + result = true; + } return result; } return null; }, // Look for a not match notMatch: function( c, data ) { - if ( /^\!/.test(data.iFilter) ) { + if ( /^\!/.test( data.iFilter ) ) { var indx, - filter = ts.filter.parseFilter(c, data.iFilter.replace('!', ''), data.index, data.parsed[data.index]) || ''; - if (ts.filter.regex.exact.test(filter)) { + txt = data.iFilter.replace( '!', '' ), + filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + if ( ts.filter.regex.exact.test( filter ) ) { // look for exact not matches - see #628 - filter = filter.replace(ts.filter.regex.exact, ''); - return filter === '' ? true : $.trim(filter) !== data.iExact; + filter = filter.replace( ts.filter.regex.exact, '' ); + return filter === '' ? true : $.trim( filter ) !== data.iExact; } else { - indx = data.iExact.search( $.trim(filter) ); - return filter === '' ? true : !(c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0); + indx = data.iExact.search( $.trim( filter ) ); + return filter === '' ? true : !( c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0 ); } } return null; @@ -160,84 +183,101 @@ ts.filter = { // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric exact: function( c, data ) { /*jshint eqeqeq:false */ - if (ts.filter.regex.exact.test(data.iFilter)) { - var filter = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.exact, ''), data.index, data.parsed[data.index]) || ''; - return data.anyMatch ? $.inArray(filter, data.rowArray) >= 0 : filter == data.iExact; + if ( ts.filter.regex.exact.test( data.iFilter ) ) { + var txt = data.iFilter.replace( ts.filter.regex.exact, '' ), + filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact; } return null; }, - // Look for an AND or && operator (logical and) + // Look for an AND or && operator ( logical and ) and : function( c, data ) { - if ( ts.filter.regex.andTest.test(data.filter) ) { + if ( ts.filter.regex.andTest.test( data.filter ) ) { var index = data.index, parsed = data.parsed[index], query = data.iFilter.split( ts.filter.regex.andSplit ), - result = data.iExact.search( $.trim( ts.filter.parseFilter(c, query[0], index, parsed) ) ) >= 0, + result = data.iExact.search( $.trim( ts.filter.parseFilter( c, query[0], index, parsed ) ) ) >= 0, indx = query.length - 1; - while (result && indx) { - result = result && data.iExact.search( $.trim( ts.filter.parseFilter(c, query[indx], index, parsed) ) ) >= 0; + while ( result && indx ) { + result = result && + data.iExact.search( $.trim( ts.filter.parseFilter( c, query[indx], index, parsed ) ) ) >= 0; indx--; } return result; } return null; }, - // Look for a range (using " to " or " - ") - see issue #166; thanks matzhu! + // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu! range : function( c, data ) { - if ( ts.filter.regex.toTest.test(data.iFilter) ) { - var result, tmp, + if ( ts.filter.regex.toTest.test( data.iFilter ) ) { + var result, tmp, range1, range2, table = c.table, index = data.index, parsed = data.parsed[index], // make sure the dash is for a range and not indicating a negative number - query = data.iFilter.split( ts.filter.regex.toSplit ), - range1 = ts.formatFloat( ts.filter.parseFilter(c, query[0].replace(ts.filter.regex.nondigit, '') || '', index, parsed), table ), - range2 = ts.formatFloat( ts.filter.parseFilter(c, query[1].replace(ts.filter.regex.nondigit, '') || '', index, parsed), table ); - // parse filter value in case we're comparing numbers (dates) - if (parsed || c.parsers[index].type === 'numeric') { - result = c.parsers[index].format('' + query[0], table, c.$headers.eq(index), index); - range1 = (result !== '' && !isNaN(result)) ? result : range1; - result = c.parsers[index].format('' + query[1], table, c.$headers.eq(index), index); - range2 = (result !== '' && !isNaN(result)) ? result : range2; + query = data.iFilter.split( ts.filter.regex.toSplit ); + + tmp = query[0].replace( ts.filter.regex.nondigit, '' ) || ''; + range1 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); + tmp = query[1].replace( ts.filter.regex.nondigit, '' ) || ''; + range2 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); + // parse filter value in case we're comparing numbers ( dates ) + if ( parsed || c.parsers[index].type === 'numeric' ) { + result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index ); + range1 = ( result !== '' && !isNaN( result ) ) ? result : range1; + result = c.parsers[ index ].format( '' + query[1], table, c.$headers.eq( index ), index ); + range2 = ( result !== '' && !isNaN( result ) ) ? result : range2; + } + if ( ( parsed || c.parsers[ index ].type === 'numeric' ) && !isNaN( range1 ) && !isNaN( range2 ) ) { + result = data.cache; + } else { + tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; + result = ts.formatFloat( tmp, table ); + } + if ( range1 > range2 ) { + tmp = range1; range1 = range2; range2 = tmp; // swap } - result = ( parsed || c.parsers[index].type === 'numeric' ) && !isNaN(range1) && !isNaN(range2) ? data.cache : - isNaN(data.iExact) ? ts.formatFloat( data.iExact.replace(ts.filter.regex.nondigit, ''), table) : - ts.formatFloat( data.iExact, table ); - if (range1 > range2) { tmp = range1; range1 = range2; range2 = tmp; } // swap - return (result >= range1 && result <= range2) || (range1 === '' || range2 === ''); + return ( result >= range1 && result <= range2 ) || ( range1 === '' || range2 === '' ); } return null; }, // Look for wild card: ? = single, * = multiple, or | = logical OR wild : function( c, data ) { - if ( /[\?\*\|]/.test(data.iFilter) || ts.filter.regex.orReplace.test(data.filter) ) { + if ( /[\?\*\|]/.test( data.iFilter ) || ts.filter.regex.orReplace.test( data.filter ) ) { var index = data.index, - parsed = data.parsed[index], - query = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.orReplace, "|"), index, parsed) || ''; - // look for an exact match with the "or" unless the "filter-match" class is found - if (!c.$headerIndexed[index].hasClass('filter-match') && /\|/.test(query)) { + parsed = data.parsed[ index ], + txt = data.iFilter.replace( ts.filter.regex.orReplace, '|' ), + query = ts.filter.parseFilter( c, txt, index, parsed ) || ''; + // look for an exact match with the 'or' unless the 'filter-match' class is found + if ( !c.$headerIndexed[ index ].hasClass( 'filter-match' ) && /\|/.test( query ) ) { // show all results while using filter match. Fixes #727 - if (query[ query.length - 1 ] === '|') { query += '*'; } - query = data.anyMatch && $.isArray(data.rowArray) ? '(' + query + ')' : '^(' + query + ')$'; + if ( query[ query.length - 1 ] === '|' ) { + query += '*'; + } + query = data.anyMatch && $.isArray( data.rowArray ) ? + '(' + query + ')' : + '^(' + query + ')$'; } // parsing the filter may not work properly when using wildcards =/ - return new RegExp( query.replace(/\?/g, '\\S{1}').replace(/\*/g, '\\S*') ).test(data.iExact); + return new RegExp( query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ) ) + .test( data.iExact ); } return null; }, - // fuzzy text search; modified from https://github.com/mattyork/fuzzy (MIT license) + // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license ) fuzzy: function( c, data ) { - if ( /^~/.test(data.iFilter) ) { + if ( /^~/.test( data.iFilter ) ) { var indx, patternIndx = 0, len = data.iExact.length, - pattern = ts.filter.parseFilter(c, data.iFilter.slice(1), data.index, data.parsed[data.index]) || ''; - for (indx = 0; indx < len; indx++) { - if (data.iExact[indx] === pattern[patternIndx]) { + txt = data.iFilter.slice( 1 ), + pattern = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + for ( indx = 0; indx < len; indx++ ) { + if ( data.iExact[ indx ] === pattern[ patternIndx ] ) { patternIndx += 1; } } - if (patternIndx === pattern.length) { + if ( patternIndx === pattern.length ) { return true; } return false; @@ -245,17 +285,17 @@ ts.filter = { return null; } }, - init: function(table, c, wo) { + init: function( table, c, wo ) { // filter language options - ts.language = $.extend(true, {}, { + ts.language = $.extend( true, {}, { to : 'to', or : 'or', and : 'and' - }, ts.language); + }, ts.language ); var options, string, txt, $header, column, filters, val, fxn, noSelect, regex = ts.filter.regex; - c.$table.addClass('hasFilters'); + c.$table.addClass( 'hasFilters' ); // define timers so using clearTimeout won't cause an undefined error wo.searchTimer = null; @@ -265,512 +305,566 @@ ts.filter = { wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; - txt = '\\{' + ts.filter.regex.query + '\\}'; + val = '\\{' + ts.filter.regex.query + '\\}'; $.extend( regex, { - child : new RegExp(c.cssChildRow), - filtered : new RegExp(wo.filter_filteredRow), - alreadyFiltered : new RegExp('(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i'), - toTest : new RegExp('\\s+(-|' + ts.language.to + ')\\s+', 'i'), - toSplit : new RegExp('(?:\\s+(?:-|' + ts.language.to + ')\\s+)' ,'gi'), - andTest : new RegExp('\\s+(' + ts.language.and + '|&&)\\s+', 'i'), - andSplit : new RegExp('(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi'), - orReplace : new RegExp('\\s+(' + ts.language.or + ')\\s+', 'gi'), - iQuery : new RegExp(txt, 'i'), - igQuery : new RegExp(txt, 'ig') + child : new RegExp( c.cssChildRow ), + filtered : new RegExp( wo.filter_filteredRow ), + alreadyFiltered : new RegExp( '(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i' ), + toTest : new RegExp( '\\s+(-|' + ts.language.to + ')\\s+', 'i' ), + toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)' ,'gi' ), + andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), + andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), + orReplace : new RegExp( '\\s+(' + ts.language.or + ')\\s+', 'gi' ), + iQuery : new RegExp( val, 'i' ), + igQuery : new RegExp( val, 'ig' ) }); - // don't build filter row if columnFilters is false or all columns are set to "filter-false" - issue #156 - if (wo.filter_columnFilters !== false && c.$headers.filter('.filter-false, .parser-false').length !== c.$headers.length) { + // don't build filter row if columnFilters is false or all columns are set to 'filter-false' + // see issue #156 + val = c.$headers.filter( '.filter-false, .parser-false' ).length; + if ( wo.filter_columnFilters !== false && val !== c.$headers.length ) { // build filter row - ts.filter.buildRow(table, c, wo); + ts.filter.buildRow( table, c, wo ); } - txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter '); - c.$table.bind( txt, function(event, filter) { - val = (wo.filter_hideEmpty && $.isEmptyObject(c.cache) && !(c.delayInit && event.type === 'appendCache')); - // hide filter row using the "filtered" class name - c.$table.find('.' + tscss.filterRow).toggleClass(wo.filter_filteredRow, val ); // fixes #450 - if ( !/(search|filter)/.test(event.type) ) { + txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' + .split( ' ' ).join( c.namespace + 'filter ' ); + c.$table.bind( txt, function( event, filter ) { + val = wo.filter_hideEmpty && + $.isEmptyObject( c.cache ) && + !( c.delayInit && event.type === 'appendCache' ); + // hide filter row using the 'filtered' class name + c.$table.find( '.' + tscss.filterRow ).toggleClass( wo.filter_filteredRow, val ); // fixes #450 + if ( !/(search|filter)/.test( event.type ) ) { event.stopPropagation(); - ts.filter.buildDefault(table, true); + ts.filter.buildDefault( table, true ); } - if (event.type === 'filterReset') { - c.$table.find('.' + tscss.filter).add(wo.filter_$externalFilters).val(''); - ts.filter.searching(table, []); - } else if (event.type === 'filterEnd') { - ts.filter.buildDefault(table, true); + if ( event.type === 'filterReset' ) { + c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' ); + ts.filter.searching( table, [] ); + } else if ( event.type === 'filterEnd' ) { + ts.filter.buildDefault( table, true ); } else { - // send false argument to force a new search; otherwise if the filter hasn't changed, it will return - filter = event.type === 'search' ? filter : event.type === 'updateComplete' ? c.$table.data('lastSearch') : ''; - if (/(update|add)/.test(event.type) && event.type !== "updateComplete") { + // send false argument to force a new search; otherwise if the filter hasn't changed, + // it will return + filter = event.type === 'search' ? filter : + event.type === 'updateComplete' ? c.$table.data( 'lastSearch' ) : ''; + if ( /(update|add)/.test( event.type ) && event.type !== 'updateComplete' ) { // force a new search since content has changed c.lastCombinedFilter = null; c.lastSearch = []; } - // pass true (skipFirst) to prevent the tablesorter.setFilters function from skipping the first input - // ensures all inputs are updated when a search is triggered on the table $('table').trigger('search', [...]); - ts.filter.searching(table, filter, true); + // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first + // input ensures all inputs are updated when a search is triggered on the table + // $( 'table' ).trigger( 'search', [...] ); + ts.filter.searching( table, filter, true ); } return false; }); // reset button/link - if (wo.filter_reset) { - if (wo.filter_reset instanceof $) { + if ( wo.filter_reset ) { + if ( wo.filter_reset instanceof $ ) { // reset contains a jQuery object, bind to it - wo.filter_reset.click(function(){ - c.$table.trigger('filterReset'); + wo.filter_reset.click( function() { + c.$table.trigger( 'filterReset' ); }); - } else if ($(wo.filter_reset).length) { + } else if ( $( wo.filter_reset ).length ) { // reset is a jQuery selector, use event delegation - $(document) - .undelegate(wo.filter_reset, 'click.tsfilter') - .delegate(wo.filter_reset, 'click.tsfilter', function() { - // trigger a reset event, so other functions (filter_formatter) know when to reset - c.$table.trigger('filterReset'); - }); + $( document ) + .undelegate( wo.filter_reset, 'click.tsfilter' ) + .delegate( wo.filter_reset, 'click.tsfilter', function() { + // trigger a reset event, so other functions ( filter_formatter ) know when to reset + c.$table.trigger( 'filterReset' ); + }); } } - if (wo.filter_functions) { - for (column = 0; column < c.columns; column++) { + if ( wo.filter_functions ) { + for ( column = 0; column < c.columns; column++ ) { fxn = ts.getColumnData( table, wo.filter_functions, column ); - if (fxn) { - // remove "filter-select" from header otherwise the options added here are replaced with all options - $header = c.$headerIndexed[column].removeClass('filter-select'); - // don't build select if "filter-false" or "parser-false" set - noSelect = !($header.hasClass('filter-false') || $header.hasClass('parser-false')); + if ( fxn ) { + // remove 'filter-select' from header otherwise the options added here are replaced with + // all options + $header = c.$headerIndexed[ column ].removeClass( 'filter-select' ); + // don't build select if 'filter-false' or 'parser-false' set + noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); options = ''; if ( fxn === true && noSelect ) { - ts.filter.buildSelect(table, column); + ts.filter.buildSelect( table, column ); } else if ( typeof fxn === 'object' && noSelect ) { // add custom drop down list - for (string in fxn) { - if (typeof string === 'string') { + for ( string in fxn ) { + if ( typeof string === 'string' ) { options += options === '' ? - '<option value="">' + ($header.data('placeholder') || $header.attr('data-placeholder') || wo.filter_placeholder.select || '') + '</option>' : ''; + '<option value="">' + + ( $header.data( 'placeholder' ) || + $header.attr( 'data-placeholder' ) || + wo.filter_placeholder.select || + '' + ) + + '</option>' : ''; val = string; txt = string; - if (string.indexOf(wo.filter_selectSourceSeparator) >= 0) { - val = string.split(wo.filter_selectSourceSeparator); + if ( string.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { + val = string.split( wo.filter_selectSourceSeparator ); txt = val[1]; val = val[0]; } - options += '<option ' + (txt === val ? '' : 'data-function-name="' + string + '" ') + 'value="' + val + '">' + txt + '</option>'; + options += '<option ' + + ( txt === val ? '' : 'data-function-name="' + string + '" ' ) + + 'value="' + val + '">' + txt + '</option>'; } } - c.$table.find('thead').find('select.' + tscss.filter + '[data-column="' + column + '"]').append(options); + c.$table + .find( 'thead' ) + .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) + .append( options ); txt = wo.filter_selectSource; - fxn = $.isFunction(txt) ? true : ts.getColumnData( table, txt, column ); - if (fxn) { + fxn = $.isFunction( txt ) ? true : ts.getColumnData( table, txt, column ); + if ( fxn ) { // updating so the extra options are appended - ts.filter.buildSelect(c.table, column, '', true, $header.hasClass(wo.filter_onlyAvail)); + ts.filter.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) ); } } } } } - // not really updating, but if the column has both the "filter-select" class & filter_functions set to true, - // it would append the same options twice. - ts.filter.buildDefault(table, true); + // not really updating, but if the column has both the 'filter-select' class & + // filter_functions set to true, it would append the same options twice. + ts.filter.buildDefault( table, true ); - ts.filter.bindSearch( table, c.$table.find('.' + tscss.filter), true ); - if (wo.filter_external) { + ts.filter.bindSearch( table, c.$table.find( '.' + tscss.filter ), true ); + if ( wo.filter_external ) { ts.filter.bindSearch( table, wo.filter_external ); } - if (wo.filter_hideFilters) { - ts.filter.hideFilters(table, c); + if ( wo.filter_hideFilters ) { + ts.filter.hideFilters( table, c ); } // show processing icon - if (c.showProcessing) { + if ( c.showProcessing ) { + txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter ' ); c.$table - .unbind( ('filterStart filterEnd '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') ) - .bind( 'filterStart filterEnd '.split(' ').join(c.namespace + 'filter '), function(event, columns) { + .unbind( txt.replace( /\s+/g, ' ' ) ) + .bind( txt, function( event, columns ) { // only add processing to certain columns to all columns - $header = (columns) ? c.$table.find('.' + tscss.header).filter('[data-column]').filter(function() { - return columns[$(this).data('column')] !== ''; - }) : ''; - ts.isProcessing(table, event.type === 'filterStart', columns ? $header : ''); + $header = ( columns ) ? + c.$table + .find( '.' + tscss.header ) + .filter( '[data-column]' ) + .filter( function() { + return columns[ $( this ).data( 'column' ) ] !== ''; + }) : ''; + ts.isProcessing( table, event.type === 'filterStart', columns ? $header : '' ); }); } - // set filtered rows count (intially unfiltered) + // set filtered rows count ( intially unfiltered ) c.filteredRows = c.totalRows; // add default values + txt = 'tablesorter-initialized pagerBeforeInitialized '.split( ' ' ).join( c.namespace + 'filter ' ); c.$table - .unbind( ('tablesorter-initialized pagerBeforeInitialized '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') ) - .bind( 'tablesorter-initialized pagerBeforeInitialized '.split(' ').join(c.namespace + 'filter '), function() { - // redefine "wo" as it does not update properly inside this callback + .unbind( txt.replace( /\s+/g, ' ' ) ) + .bind( txt, function() { + // redefine 'wo' as it does not update properly inside this callback var wo = this.config.widgetOptions; - filters = ts.filter.setDefaults(table, c, wo) || []; - if (filters.length) { + filters = ts.filter.setDefaults( table, c, wo ) || []; + if ( filters.length ) { // prevent delayInit from triggering a cache build if filters are empty - if ( !(c.delayInit && filters.join('') === '') ) { - ts.setFilters(table, filters, true); + if ( !( c.delayInit && filters.join( '' ) === '' ) ) { + ts.setFilters( table, filters, true ); } } - c.$table.trigger('filterFomatterUpdate'); + c.$table.trigger( 'filterFomatterUpdate' ); // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers - setTimeout(function(){ - if (!wo.filter_initialized) { - ts.filter.filterInitComplete(c); + setTimeout( function() { + if ( !wo.filter_initialized ) { + ts.filter.filterInitComplete( c ); } - }, 100); + }, 100 ); }); // if filter widget is added after pager has initialized; then set filter init flag - if (c.pager && c.pager.initialized && !wo.filter_initialized) { - c.$table.trigger('filterFomatterUpdate'); - setTimeout(function(){ - ts.filter.filterInitComplete(c); - }, 100); + if ( c.pager && c.pager.initialized && !wo.filter_initialized ) { + c.$table.trigger( 'filterFomatterUpdate' ); + setTimeout( function() { + ts.filter.filterInitComplete( c ); + }, 100 ); } }, - // $cell parameter, but not the config, is passed to the - // filter_formatters, so we have to work with it instead - formatterUpdated: function($cell, column) { - var wo = $cell.closest('table')[0].config.widgetOptions; - if (!wo.filter_initialized) { + // $cell parameter, but not the config, is passed to the filter_formatters, + // so we have to work with it instead + formatterUpdated: function( $cell, column ) { + var wo = $cell.closest( 'table' )[0].config.widgetOptions; + if ( !wo.filter_initialized ) { // add updates by column since this function // may be called numerous times before initialization - wo.filter_formatterInit[column] = 1; + wo.filter_formatterInit[ column ] = 1; } }, - filterInitComplete: function(c){ + filterInitComplete: function( c ) { var indx, len, wo = c.widgetOptions, count = 0, - completed = function(){ + completed = function() { wo.filter_initialized = true; - c.$table.trigger('filterInit', c); - ts.filter.findRows(c.table, c.$table.data('lastSearch') || []); + c.$table.trigger( 'filterInit', c ); + ts.filter.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); }; if ( $.isEmptyObject( wo.filter_formatter ) ) { completed(); } else { len = wo.filter_formatterInit.length; - for (indx = 0; indx < len; indx++) { - if (wo.filter_formatterInit[indx] === 1) { + for ( indx = 0; indx < len; indx++ ) { + if ( wo.filter_formatterInit[ indx ] === 1 ) { count++; } } - clearTimeout(wo.filter_initTimer); - if (!wo.filter_initialized && count === wo.filter_formatterCount) { + clearTimeout( wo.filter_initTimer ); + if ( !wo.filter_initialized && count === wo.filter_formatterCount ) { // filter widget initialized completed(); - } else if (!wo.filter_initialized) { + } else if ( !wo.filter_initialized ) { // fall back in case a filter_formatter doesn't call - // $.tablesorter.filter.formatterUpdated($cell, column), and the count is off - wo.filter_initTimer = setTimeout(function(){ + // $.tablesorter.filter.formatterUpdated( $cell, column ), and the count is off + wo.filter_initTimer = setTimeout( function() { completed(); - }, 500); + }, 500 ); } } }, - setDefaults: function(table, c, wo) { + setDefaults: function( table, c, wo ) { var isArray, saved, indx, col, $filters, - // get current (default) filters - filters = ts.getFilters(table) || []; - if (wo.filter_saveFilters && ts.storage) { + // get current ( default ) filters + filters = ts.getFilters( table ) || []; + if ( wo.filter_saveFilters && ts.storage ) { saved = ts.storage( table, 'tablesorter-filters' ) || []; - isArray = $.isArray(saved); + isArray = $.isArray( saved ); // make sure we're not just getting an empty array - if ( !(isArray && saved.join('') === '' || !isArray) ) { filters = saved; } + if ( !( isArray && saved.join( '' ) === '' || !isArray ) ) { + filters = saved; + } } // if no filters saved, then check default settings - if (filters.join('') === '') { + if ( filters.join( '' ) === '' ) { // allow adding default setting to external filters - $filters = c.$headers.add( wo.filter_$externalFilters ).filter('[' + wo.filter_defaultAttrib + ']'); - for (indx = 0; indx <= c.columns; indx++) { - // include data-column="all" external filters + $filters = c.$headers.add( wo.filter_$externalFilters ) + .filter( '[' + wo.filter_defaultAttrib + ']' ); + for ( indx = 0; indx <= c.columns; indx++ ) { + // include data-column='all' external filters col = indx === c.columns ? 'all' : indx; - filters[indx] = $filters.filter('[data-column="' + col + '"]').attr(wo.filter_defaultAttrib) || filters[indx] || ''; + filters[indx] = $filters + .filter( '[data-column="' + col + '"]' ) + .attr( wo.filter_defaultAttrib ) || filters[indx] || ''; } } - c.$table.data('lastSearch', filters); + c.$table.data( 'lastSearch', filters ); return filters; }, - parseFilter: function(c, filter, column, parsed, forceParse){ - return forceParse || parsed ? - c.parsers[column].format( filter, c.table, [], column ) : - filter; + parseFilter: function( c, filter, column, parsed ) { + return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter; }, - buildRow: function(table, c, wo) { - var col, column, $header, buildSelect, disabled, name, ffxn, + buildRow: function( table, c, wo ) { + var col, column, $header, buildSelect, disabled, name, ffxn, tmp, // c.columns defined in computeThIndexes() + cellFilter = wo.filter_cellFilter, columns = c.columns, - arry = $.isArray(wo.filter_cellFilter), + arry = $.isArray( cellFilter ), buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; - for (column = 0; column < columns; column++) { - if (arry) { - buildFilter += '<td' + ( wo.filter_cellFilter[column] ? ' class="' + wo.filter_cellFilter[column] + '"' : '' ) + '></td>'; + for ( column = 0; column < columns; column++ ) { + buildFilter += '<td'; + if ( arry ) { + buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' ); } else { - buildFilter += '<td' + ( wo.filter_cellFilter !== '' ? ' class="' + wo.filter_cellFilter + '"' : '' ) + '></td>'; + buildFilter += ( cellFilter !== '' ? ' class="' + cellFilter + '"' : '' ); } + buildFilter += '></td>'; } - c.$filters = $(buildFilter += '</tr>').appendTo( c.$table.children('thead').eq(0) ).find('td'); + c.$filters = $( buildFilter += '</tr>' ) + .appendTo( c.$table.children( 'thead' ).eq( 0 ) ) + .find( 'td' ); // build each filter input - for (column = 0; column < columns; column++) { + for ( column = 0; column < columns; column++ ) { disabled = false; // assuming last cell of a column is the main column - $header = c.$headerIndexed[column]; + $header = c.$headerIndexed[ column ]; ffxn = ts.getColumnData( table, wo.filter_functions, column ); - buildSelect = (wo.filter_functions && ffxn && typeof ffxn !== "function" ) || - $header.hasClass('filter-select'); + buildSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || + $header.hasClass( 'filter-select' ); // get data from jQuery data, metadata, headers option or header class name col = ts.getColumnData( table, c.headers, column ); - disabled = ts.getData($header[0], col, 'filter') === 'false' || ts.getData($header[0], col, 'parser') === 'false'; + disabled = ts.getData( $header[0], col, 'filter' ) === 'false' || + ts.getData( $header[0], col, 'parser' ) === 'false'; - if (buildSelect) { - buildFilter = $('<select>').appendTo( c.$filters.eq(column) ); + if ( buildSelect ) { + buildFilter = $( '<select>' ).appendTo( c.$filters.eq( column ) ); } else { ffxn = ts.getColumnData( table, wo.filter_formatter, column ); - if (ffxn) { + if ( ffxn ) { wo.filter_formatterCount++; - buildFilter = ffxn( c.$filters.eq(column), column ); + buildFilter = ffxn( c.$filters.eq( column ), column ); // no element returned, so lets go find it - if (buildFilter && buildFilter.length === 0) { - buildFilter = c.$filters.eq(column).children('input'); + if ( buildFilter && buildFilter.length === 0 ) { + buildFilter = c.$filters.eq( column ).children( 'input' ); } // element not in DOM, so lets attach it - if ( buildFilter && (buildFilter.parent().length === 0 || - (buildFilter.parent().length && buildFilter.parent()[0] !== c.$filters[column])) ) { - c.$filters.eq(column).append(buildFilter); + if ( buildFilter && ( buildFilter.parent().length === 0 || + ( buildFilter.parent().length && buildFilter.parent()[0] !== c.$filters[column] ) ) ) { + c.$filters.eq( column ).append( buildFilter ); } } else { - buildFilter = $('<input type="search">').appendTo( c.$filters.eq(column) ); + buildFilter = $( '<input type="search">' ).appendTo( c.$filters.eq( column ) ); } - if (buildFilter) { - buildFilter.attr('placeholder', $header.data('placeholder') || $header.attr('data-placeholder') || wo.filter_placeholder.search || ''); + if ( buildFilter ) { + tmp = $header.data( 'placeholder' ) || + $header.attr( 'data-placeholder' ) || + wo.filter_placeholder.search || ''; + buildFilter.attr( 'placeholder', tmp ); } } - if (buildFilter) { + if ( buildFilter ) { // add filter class name - name = ( $.isArray(wo.filter_cssFilter) ? - (typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '') : + name = ( $.isArray( wo.filter_cssFilter ) ? + ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : wo.filter_cssFilter ) || ''; - buildFilter.addClass( tscss.filter + ' ' + name ).attr('data-column', column); - if (disabled) { - buildFilter.attr('placeholder', '').addClass(tscss.filterDisabled)[0].disabled = true; // disabled! + buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column ); + if ( disabled ) { + buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; } } } }, - bindSearch: function(table, $el, internal) { - table = $(table)[0]; - $el = $($el); // allow passing a selector string - if (!$el.length) { return; } - var c = table.config, + bindSearch: function( table, $el, internal ) { + table = $( table )[0]; + $el = $( $el ); // allow passing a selector string + if ( !$el.length ) { return; } + var tmp, + c = table.config, wo = c.widgetOptions, + namespace = c.namespace + 'filter', $ext = wo.filter_$externalFilters; - if (internal !== true) { + if ( internal !== true ) { // save anyMatch element - wo.filter_$anyMatch = $el.filter(wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector); - if ($ext && $ext.length) { + tmp = wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector; + wo.filter_$anyMatch = $el.filter( tmp ); + if ( $ext && $ext.length ) { wo.filter_$externalFilters = wo.filter_$externalFilters.add( $el ); } else { wo.filter_$externalFilters = $el; } - // update values (external filters added after table initialization) - ts.setFilters(table, c.$table.data('lastSearch') || [], internal === false); + // update values ( external filters added after table initialization ) + ts.setFilters( table, c.$table.data( 'lastSearch' ) || [], internal === false ); } + // unbind events + tmp = ( 'keypress keyup search change '.split( ' ' ).join( namespace + ' ' ) ); $el - // use data attribute instead of jQuery data since the head is cloned without including the data/binding - .attr('data-lastSearchTime', new Date().getTime()) - .unbind( ('keypress keyup search change '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') ) + // use data attribute instead of jQuery data since the head is cloned without including + // the data/binding + .attr( 'data-lastSearchTime', new Date().getTime() ) + .unbind( tmp.replace( /\s+/g, ' ' ) ) // include change for select - fixes #473 - .bind('keyup' + c.namespace + 'filter', function(event) { - $(this).attr('data-lastSearchTime', new Date().getTime()); + .bind( 'keyup' + namespace, function( event ) { + $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); // emulate what webkit does.... escape clears the filter - if (event.which === 27) { + if ( event.which === 27 ) { this.value = ''; // live search } else if ( wo.filter_liveSearch === false ) { return; - // don't return if the search value is empty (all rows need to be revealed) + // don't return if the search value is empty ( all rows need to be revealed ) } else if ( this.value !== '' && ( // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace ( typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch ) || // let return & backspace continue on, but ignore arrows & non-valid characters - ( event.which !== 13 && event.which !== 8 && ( event.which < 32 || (event.which >= 37 && event.which <= 40) ) ) ) ) { + ( event.which !== 13 && event.which !== 8 && + ( event.which < 32 || ( event.which >= 37 && event.which <= 40 ) ) ) ) ) { return; } // change event = no delay; last true flag tells getFilters to skip newest timed input ts.filter.searching( table, true, true ); }) - .bind( 'search change keypress '.split(' ').join(c.namespace + 'filter '), function(event){ - var column = $(this).data('column'); - // don't allow "change" event to process if the input value is the same - fixes #685 - if (event.which === 13 || event.type === 'search' || event.type === 'change' && this.value !== c.lastSearch[column]) { + .bind( 'search change keypress '.split( ' ' ).join( namespace + ' ' ), function( event ) { + var column = $( this ).data( 'column' ); + // don't allow 'change' event to process if the input value is the same - fixes #685 + if ( event.which === 13 || event.type === 'search' || + event.type === 'change' && this.value !== c.lastSearch[column] ) { event.preventDefault(); // init search with no delay - $(this).attr('data-lastSearchTime', new Date().getTime()); + $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); ts.filter.searching( table, false, true ); } }); }, - searching: function(table, filter, skipFirst) { + searching: function( table, filter, skipFirst ) { var wo = table.config.widgetOptions; - clearTimeout(wo.searchTimer); - if (typeof filter === 'undefined' || filter === true) { + clearTimeout( wo.searchTimer ); + if ( typeof filter === 'undefined' || filter === true ) { // delay filtering - wo.searchTimer = setTimeout(function() { - ts.filter.checkFilters(table, filter, skipFirst ); - }, wo.filter_liveSearch ? wo.filter_searchDelay : 10); + wo.searchTimer = setTimeout( function() { + ts.filter.checkFilters( table, filter, skipFirst ); + }, wo.filter_liveSearch ? wo.filter_searchDelay : 10 ); } else { // skip delay - ts.filter.checkFilters(table, filter, skipFirst); + ts.filter.checkFilters( table, filter, skipFirst ); } }, - checkFilters: function(table, filter, skipFirst) { + checkFilters: function( table, filter, skipFirst ) { var c = table.config, wo = c.widgetOptions, - filterArray = $.isArray(filter), - filters = (filterArray) ? filter : ts.getFilters(table, true), - combinedFilters = (filters || []).join(''); // combined filter values + filterArray = $.isArray( filter ), + filters = ( filterArray ) ? filter : ts.getFilters( table, true ), + combinedFilters = ( filters || [] ).join( '' ); // combined filter values // prevent errors if delay init is set - if ($.isEmptyObject(c.cache)) { - // update cache if delayInit set & pager has initialized (after user initiates a search) - if (c.delayInit && c.pager && c.pager.initialized) { - c.$table.trigger('updateCache', [function(){ - ts.filter.checkFilters(table, false, skipFirst); - }] ); + if ( $.isEmptyObject( c.cache ) ) { + // update cache if delayInit set & pager has initialized ( after user initiates a search ) + if ( c.delayInit && c.pager && c.pager.initialized ) { + c.$table.trigger( 'updateCache', [ function() { + ts.filter.checkFilters( table, false, skipFirst ); + } ] ); } return; } // add filter array back into inputs - if (filterArray) { + if ( filterArray ) { ts.setFilters( table, filters, false, skipFirst !== true ); - if (!wo.filter_initialized) { c.lastCombinedFilter = ''; } + if ( !wo.filter_initialized ) { c.lastCombinedFilter = ''; } } - if (wo.filter_hideFilters) { + if ( wo.filter_hideFilters ) { // show/hide filter row as needed - c.$table.find('.' + tscss.filterRow).trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); + c.$table + .find( '.' + tscss.filterRow ) + .trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons - if (c.lastCombinedFilter === combinedFilters && filter !== false) { + if ( c.lastCombinedFilter === combinedFilters && filter !== false ) { return; - } else if (filter === false) { + } else if ( filter === false ) { // force filter refresh c.lastCombinedFilter = null; c.lastSearch = []; } - if (wo.filter_initialized) { c.$table.trigger('filterStart', [filters]); } - if (c.showProcessing) { + if ( wo.filter_initialized ) { + c.$table.trigger( 'filterStart', [filters] ); + } + if ( c.showProcessing ) { // give it time for the processing icon to kick in - setTimeout(function() { - ts.filter.findRows(table, filters, combinedFilters); + setTimeout( function() { + ts.filter.findRows( table, filters, combinedFilters ); return false; - }, 30); + }, 30 ); } else { - ts.filter.findRows(table, filters, combinedFilters); + ts.filter.findRows( table, filters, combinedFilters ); return false; } }, - hideFilters: function(table, c) { + hideFilters: function( table, c ) { var $filterRow, $filterRow2, timer; - $(table) - .find('.' + tscss.filterRow) - .addClass(tscss.filterRowHide) - .bind('mouseenter mouseleave', function(e) { + $( table ) + .find( '.' + tscss.filterRow ) + .addClass( tscss.filterRowHide ) + .bind( 'mouseenter mouseleave', function( e ) { // save event object - http://bugs.jquery.com/ticket/12140 var event = e; - $filterRow = $(this); - clearTimeout(timer); - timer = setTimeout(function() { - if ( /enter|over/.test(event.type) ) { - $filterRow.removeClass(tscss.filterRowHide); + $filterRow = $( this ); + clearTimeout( timer ); + timer = setTimeout( function() { + if ( /enter|over/.test( event.type ) ) { + $filterRow.removeClass( tscss.filterRowHide ); } else { // don't hide if input has focus - // $(':focus') needs jQuery 1.6+ - if ( $(document.activeElement).closest('tr')[0] !== $filterRow[0] ) { + // $( ':focus' ) needs jQuery 1.6+ + if ( $( document.activeElement ).closest( 'tr' )[0] !== $filterRow[0] ) { // don't hide row if any filter has a value - if (c.lastCombinedFilter === '') { - $filterRow.addClass(tscss.filterRowHide); + if ( c.lastCombinedFilter === '' ) { + $filterRow.addClass( tscss.filterRowHide ); } } } - }, 200); + }, 200 ); }) - .find('input, select').bind('focus blur', function(e) { - $filterRow2 = $(this).closest('tr'); - clearTimeout(timer); + .find( 'input, select' ).bind( 'focus blur', function( e ) { + $filterRow2 = $( this ).closest( 'tr' ); + clearTimeout( timer ); var event = e; - timer = setTimeout(function() { + timer = setTimeout( function() { // don't hide row if any filter has a value - if (ts.getFilters(c.$table).join('') === '') { - $filterRow2[ event.type === 'focus' ? 'removeClass' : 'addClass'](tscss.filterRowHide); + if ( ts.getFilters( c.$table ).join( '' ) === '' ) { + $filterRow2.toggleClass( tscss.filterRowHide, event.type === 'focus' ); } - }, 200); + }, 200 ); }); }, - defaultFilter: function(filter, mask){ - if (filter === '') { return filter; } + defaultFilter: function( filter, mask ) { + if ( filter === '' ) { return filter; } var regex = ts.filter.regex.iQuery, maskLen = mask.match( ts.filter.regex.igQuery ).length, - query = maskLen > 1 ? $.trim(filter).split(/\s/) : [ $.trim(filter) ], + query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ], len = query.length - 1, indx = 0, val = mask; if ( len < 1 && maskLen > 1 ) { - // only one "word" in query but mask has >1 slots + // only one 'word' in query but mask has >1 slots query[1] = query[0]; } // replace all {query} with query words... - // if query = "Bob", then convert mask from "!{query}" to "!Bob" - // if query = "Bob Joe Frank", then convert mask "{q} OR {q}" to "Bob OR Joe OR Frank" - while (regex.test(val)) { - val = val.replace(regex, query[indx++] || ''); - if (regex.test(val) && indx < len && (query[indx] || '') !== '') { - val = mask.replace(regex, val); + // if query = 'Bob', then convert mask from '!{query}' to '!Bob' + // if query = 'Bob Joe Frank', then convert mask '{q} OR {q}' to 'Bob OR Joe OR Frank' + while ( regex.test( val ) ) { + val = val.replace( regex, query[indx++] || '' ); + if ( regex.test( val ) && indx < len && ( query[indx] || '' ) !== '' ) { + val = mask.replace( regex, val ); } } return val; }, getLatestSearch: function( $input ) { - if ($input) { - return $input.sort(function(a, b) { - return $(b).attr('data-lastSearchTime') - $(a).attr('data-lastSearchTime'); + if ( $input ) { + return $input.sort( function( a, b ) { + return $( b ).attr( 'data-lastSearchTime' ) - $( a ).attr( 'data-lastSearchTime' ); }); } return $(); }, multipleColumns: function( c, $input ) { - // look for multiple columns "1-3,4-6,8" in data-column + // look for multiple columns '1-3,4-6,8' in data-column var temp, ranges, range, start, end, singles, i, indx, len, wo = c.widgetOptions, - // only target "all" column inputs on initialization - // & don't target "all" column inputs if they don't exist - targets = wo.filter_initialized || !$input.filter(wo.filter_anyColumnSelector).length, + // only target 'all' column inputs on initialization + // & don't target 'all' column inputs if they don't exist + targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, columns = [], - val = $.trim( ts.filter.getLatestSearch( $input ).attr('data-column') || '' ); + val = $.trim( ts.filter.getLatestSearch( $input ).attr( 'data-column' ) || '' ); // process column range if ( targets && /-/.test( val ) ) { ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); len = ranges.length; - for (indx = 0; indx < len; indx++) { + for ( indx = 0; indx < len; indx++ ) { range = ranges[indx].split( /\s*-\s*/ ); start = parseInt( range[0], 10 ) || 0; end = parseInt( range[1], 10 ) || ( c.columns - 1 ); - if ( start > end ) { temp = start; start = end; end = temp; } // swap - if ( end >= c.columns ) { end = c.columns - 1; } + if ( start > end ) { + temp = start; start = end; end = temp; // swap + } + if ( end >= c.columns ) { + end = c.columns - 1; + } for ( ; start <= end; start++ ) { - columns.push(start); + columns.push( start ); } // remove processed range from val - val = val.replace( ranges[indx], '' ); + val = val.replace( ranges[ indx ], '' ); } } // process single columns if ( targets && /,/.test( val ) ) { singles = val.split( /\s*,\s*/ ); len = singles.length; - for (i = 0; i < len; i++) { - if (singles[i] !== '') { - indx = parseInt( singles[i], 10 ); + for ( i = 0; i < len; i++ ) { + if ( singles[ i ] !== '' ) { + indx = parseInt( singles[ i ], 10 ); if ( indx < c.columns ) { columns.push( indx ); } @@ -778,382 +872,472 @@ ts.filter = { } } // return all columns - if (!columns.length) { + if ( !columns.length ) { for ( indx = 0; indx < c.columns; indx++ ) { columns.push( indx ); } } return columns; }, - findRows: function(table, filters, combinedFilters) { - if (table.config.lastCombinedFilter === combinedFilters || !table.config.widgetOptions.filter_initialized) { return; } - var len, norm_rows, $rows, rowIndex, tbodyIndex, $tbody, $cells, $cell, columnIndex, - childRow, lastSearch, hasSelect, matches, result, showRow, time, val, indx, - notFiltered, searchFiltered, filterMatched, excludeMatch, fxn, ffxn, - query, injected, res, id, + processRow: function( c, data, vars ) { + var $cell, columnIndex, hasSelect, matches, result, val, filterMatched, excludeMatch, + fxn, ffxn, txt, + regex = ts.filter.regex, + wo = c.widgetOptions, + showRow = true; + data.$cells = data.$row.children(); + + if ( data.anyMatchFlag ) { + // look for multiple columns '1-3,4-6,8' + columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); + data.anyMatch = true; + data.rowArray = data.$cells.map( function( i ) { + if ( $.inArray( i, columnIndex ) > -1 ) { + if ( data.parsed[ i ] ) { + txt = data.cacheArray[ i ]; + } else { + txt = data.rawArray[ i ]; + txt = $.trim( wo.filter_ignoreCase ? txt.toLowerCase() : txt ); + if ( c.sortLocaleCompare ) { + txt = ts.replaceAccents( txt ); + } + } + return txt; + } + }).get(); + data.filter = data.anyMatchFilter; + data.iFilter = data.iAnyMatchFilter; + data.exact = data.rowArray.join( ' ' ); + data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; + data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' ); + filterMatched = null; + matches = null; + for ( ffxn in ts.filter.types ) { + if ( $.inArray( ffxn, vars.noAnyMatch ) < 0 && matches === null ) { + matches = ts.filter.types[ffxn]( c, data ); + if ( matches !== null ) { + filterMatched = matches; + } + } + } + if ( filterMatched !== null ) { + showRow = filterMatched; + } else { + if ( wo.filter_startsWith ) { + showRow = false; + columnIndex = c.columns; + while ( !showRow && columnIndex > 0 ) { + columnIndex--; + showRow = showRow || data.rowArray[ columnIndex ].indexOf( data.iFilter ) === 0; + } + } else { + showRow = ( data.iExact + data.childRowText ).indexOf( data.iFilter ) >= 0; + } + } + data.anyMatch = false; + // no other filters to process + if ( data.filters.join( '' ) === data.filter ) { + return showRow; + } + } + + for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { + data.filter = data.filters[ columnIndex ]; + data.index = columnIndex; + + // filter types to exclude, per column + excludeMatch = vars.excludeFilter[ columnIndex ]; + + // ignore if filter is empty or disabled + if ( data.filter ) { + data.cache = data.cacheArray[ columnIndex ]; + // check if column data should be from the cell or from parsed data + if ( wo.filter_useParsedData || data.parsed[ columnIndex ] ) { + data.exact = data.cache; + } else { + result = data.rawArray[ columnIndex ] || ''; + data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 + } + data.iExact = !regex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? + data.exact.toLowerCase() : data.exact; + result = showRow; // if showRow is true, show that row + + // in case select filter option has a different value vs text 'a - z|A through Z' + ffxn = wo.filter_columnFilters ? + c.$filters.add( c.$externalFilters ) + .filter( '[data-column="'+ columnIndex + '"]' ) + .find( 'select option:selected' ) + .attr( 'data-function-name' ) || '' : ''; + // replace accents - see #357 + if ( c.sortLocaleCompare ) { + data.filter = ts.replaceAccents( data.filter ); + } + + val = true; + if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) { + data.filter = ts.filter.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); + // val is used to indicate that a filter select is using a default filter; + // so we override the exact & partial matches + val = false; + } + // data.iFilter = case insensitive ( if wo.filter_ignoreCase is true ), + // data.filter = case sensitive + data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter; + fxn = vars.functions[ columnIndex ]; + $cell = c.$headerIndexed[ columnIndex ]; + hasSelect = $cell.hasClass( 'filter-select' ); + filterMatched = null; + if ( fxn || ( hasSelect && val ) ) { + if ( fxn === true || hasSelect ) { + // default selector uses exact match unless 'filter-match' class is found + filterMatched = $cell.hasClass( 'filter-match' ) ? + data.iExact.search( data.iFilter ) >= 0 : + data.filter === data.exact; + } else if ( typeof fxn === 'function' ) { + // filter callback( exact cell content, parser normalized content, + // filter input value, column index, jQuery row object ) + filterMatched = fxn( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); + } else if ( typeof fxn[ ffxn || data.filter ] === 'function' ) { + // selector option function + txt = ffxn || data.filter; + filterMatched = + fxn[ txt ]( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); + } + } + if ( filterMatched === null ) { + // cycle through the different filters + // filters return a boolean or null if nothing matches + matches = null; + for ( ffxn in ts.filter.types ) { + if ( $.inArray( ffxn, excludeMatch ) < 0 && matches === null ) { + matches = ts.filter.types[ ffxn ]( c, data ); + if ( matches !== null ) { + filterMatched = matches; + } + } + } + if ( filterMatched !== null ) { + result = filterMatched; + // Look for match, and add child row data for matching + } else { + txt = ( data.iExact + data.childRowText ) + .indexOf( ts.filter.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) ); + result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); + } + } else { + result = filterMatched; + } + showRow = ( result ) ? showRow : false; + } + } + return showRow; + }, + findRows: function( table, filters, combinedFilters ) { + if ( table.config.lastCombinedFilter === combinedFilters || + !table.config.widgetOptions.filter_initialized ) { + return; + } + var len, norm_rows, rowData, $rows, rowIndex, tbodyIndex, $tbody, columnIndex, + isChild, childRow, lastSearch, showRow, time, val, indx, + notFiltered, searchFiltered, query, injected, res, id, txt, + storedFilters = $.extend( [], filters ), regex = ts.filter.regex, c = table.config, wo = c.widgetOptions, // data object passed to filters; anyMatch is a flag for the filters - data = { anyMatch: false }, - // anyMatch really screws up with these types of filters - noAnyMatch = [ 'range', 'notMatch', 'operators' ]; + data = { + anyMatch: false, + filters: filters, + // regex filter type cache + filter_regexCache : [], + }, + vars = { + // anyMatch really screws up with these types of filters + noAnyMatch: [ 'range', 'notMatch', 'operators' ], + // cache filter variables that use ts.getColumnData in the main loop + functions : [], + excludeFilter : [], + defaultColFilter : [], + defaultAnyFilter : ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '' + }; // parse columns after formatter, in case the class is added at that point - data.parsed = c.$headers.map(function(columnIndex) { - return c.parsers && c.parsers[columnIndex] && c.parsers[columnIndex].parsed || - // getData won't return "parsed" if other "filter-" class names exist (e.g. <th class="filter-select filter-parsed">) - ts.getData && ts.getData(c.$headerIndexed[columnIndex], ts.getColumnData( table, c.headers, columnIndex ), 'filter') === 'parsed' || - $(this).hasClass('filter-parsed'); + data.parsed = c.$headers.map( function( columnIndex ) { + return c.parsers && c.parsers[ columnIndex ] && + // force parsing if parser type is numeric + ( c.parsers[ columnIndex ].parsed || c.parsers[ columnIndex ].type === 'numeric' ) || + // getData won't return 'parsed' if other 'filter-' class names exist + // ( e.g. <th class="filter-select filter-parsed"> ) + ts.getData && ts.getData( c.$headerIndexed[ columnIndex ], + ts.getColumnData( table, c.headers, columnIndex ), 'filter' ) === 'parsed' || + $( this ).hasClass( 'filter-parsed' ); }).get(); - // cache filter variables that use ts.getColumnData in the main loop - wo.filter_indexed = { - functions : [], - excludeFilter : [], - defaultColFilter : [], - defaultAnyFilter : ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '' - }; for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { - wo.filter_indexed.functions[ columnIndex ] = ts.getColumnData( table, wo.filter_functions, columnIndex ); - wo.filter_indexed.defaultColFilter[ columnIndex ] = ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || ''; - wo.filter_indexed.excludeFilter[ columnIndex ] = ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split(/\s+/); + vars.functions[ columnIndex ] = + ts.getColumnData( table, wo.filter_functions, columnIndex ); + vars.defaultColFilter[ columnIndex ] = + ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || ''; + vars.excludeFilter[ columnIndex ] = + ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split( /\s+/ ); } - if (c.debug) { - ts.log('Filter: Starting filter widget search', filters); + if ( c.debug ) { + ts.log( 'Filter: Starting filter widget search', filters ); time = new Date(); } // filtered rows count c.filteredRows = 0; c.totalRows = 0; // combindedFilters are undefined on init - combinedFilters = (filters || []).join(''); + combinedFilters = ( storedFilters || [] ).join( '' ); - for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { - $tbody = ts.processTbody(table, c.$tbodies.eq(tbodyIndex), true); - // skip child rows & widget added (removable) rows - fixes #448 thanks to @hempel! - // $rows = $tbody.children('tr').not(c.selectorRemove); + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody( table, c.$tbodies.eq( tbodyIndex ), true ); + // skip child rows & widget added ( removable ) rows - fixes #448 thanks to @hempel! + // $rows = $tbody.children( 'tr' ).not( c.selectorRemove ); columnIndex = c.columns; // convert stored rows into a jQuery object - norm_rows = c.cache[tbodyIndex].normalized; - $rows = $( $.map(norm_rows, function(el){ return el[columnIndex].$row.get(); }) ); + norm_rows = c.cache[ tbodyIndex ].normalized; + $rows = $( $.map( norm_rows, function( el ) { + return el[ columnIndex ].$row.get(); + }) ); - if (combinedFilters === '' || wo.filter_serversideFiltering) { - $rows.removeClass(wo.filter_filteredRow).not('.' + c.cssChildRow).css('display', ''); + if ( combinedFilters === '' || wo.filter_serversideFiltering ) { + $rows + .removeClass( wo.filter_filteredRow ) + .not( '.' + c.cssChildRow ) + .css( 'display', '' ); } else { // filter out child rows - $rows = $rows.not('.' + c.cssChildRow); + $rows = $rows.not( '.' + c.cssChildRow ); len = $rows.length; - if ( (wo.filter_$anyMatch && wo.filter_$anyMatch.length) || typeof filters[c.columns] !== 'undefined' ) { + if ( ( wo.filter_$anyMatch && wo.filter_$anyMatch.length ) || + typeof filters[c.columns] !== 'undefined' ) { data.anyMatchFlag = true; - data.anyMatchFilter = wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || ( '' + filters[c.columns] ) || ''; - if (wo.filter_columnAnyMatch) { + data.anyMatchFilter = '' + ( + filters[ c.columns ] || + wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || + '' + ); + if ( wo.filter_columnAnyMatch ) { // specific columns search - query = data.anyMatchFilter.split( ts.filter.regex.andSplit ); + query = data.anyMatchFilter.split( regex.andSplit ); injected = false; - for (indx = 0; indx < query.length; indx++) { - res = query[indx].split(':'); + for ( indx = 0; indx < query.length; indx++ ) { + res = query[ indx ].split( ':' ); if ( res.length > 1 ) { // make the column a one-based index ( non-developers start counting from one :P ) id = parseInt( res[0], 10 ) - 1; if ( id >= 0 && id < c.columns ) { // if id is an integer - filters[id] = res[1]; - query.splice(indx, 1); + filters[ id ] = res[1]; + query.splice( indx, 1 ); indx--; injected = true; } } } - if (injected) { - data.anyMatchFilter = query.join(' && '); + if ( injected ) { + data.anyMatchFilter = query.join( ' && ' ); } } } // optimize searching only through already filtered rows - see #313 searchFiltered = wo.filter_searchFiltered; - lastSearch = c.lastSearch || c.$table.data('lastSearch') || []; - if (searchFiltered) { - // cycle through all filters; include last (columnIndex + 1 = match any column). Fixes #669 - for (indx = 0; indx < columnIndex + 1; indx++) { + lastSearch = c.lastSearch || c.$table.data( 'lastSearch' ) || []; + if ( searchFiltered ) { + // cycle through all filters; include last ( columnIndex + 1 = match any column ). Fixes #669 + for ( indx = 0; indx < columnIndex + 1; indx++ ) { val = filters[indx] || ''; // break out of loop if we've already determined not to search filtered rows - if (!searchFiltered) { indx = columnIndex; } + if ( !searchFiltered ) { indx = columnIndex; } // search already filtered rows if... searchFiltered = searchFiltered && lastSearch.length && // there are no changes from beginning of filter - val.indexOf(lastSearch[indx] || '') === 0 && - // if there is NOT a logical "or", or range ("to" or "-") in the string - !regex.alreadyFiltered.test(val) && - // if we are not doing exact matches, using "|" (logical or) or not "!" - !/[=\"\|!]/.test(val) && - // don't search only filtered if the value is negative ('> -10' => '> -100' will ignore hidden rows) - !(/(>=?\s*-\d)/.test(val) || /(<=?\s*\d)/.test(val)) && - // if filtering using a select without a "filter-match" class (exact match) - fixes #593 - !( val !== '' && c.$filters && c.$filters.eq(indx).find('select').length && !c.$headerIndexed[indx].hasClass('filter-match') ); + val.indexOf( lastSearch[indx] || '' ) === 0 && + // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string + !regex.alreadyFiltered.test( val ) && + // if we are not doing exact matches, using '|' ( logical or ) or not '!' + !/[=\"\|!]/.test( val ) && + // don't search only filtered if the value is negative + // ( '> -10' => '> -100' will ignore hidden rows ) + !( /(>=?\s*-\d)/.test( val ) || /(<=?\s*\d)/.test( val ) ) && + // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 + !( val !== '' && c.$filters && c.$filters.eq( indx ).find( 'select' ).length && + !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); } } - notFiltered = $rows.not('.' + wo.filter_filteredRow).length; + notFiltered = $rows.not( '.' + wo.filter_filteredRow ).length; // can't search when all rows are hidden - this happens when looking for exact matches - if (searchFiltered && notFiltered === 0) { searchFiltered = false; } - if (c.debug) { - ts.log( 'Filter: Searching through ' + ( searchFiltered && notFiltered < len ? notFiltered : 'all' ) + ' rows' ); + if ( searchFiltered && notFiltered === 0 ) { searchFiltered = false; } + if ( c.debug ) { + ts.log( 'Filter: Searching through ' + + ( searchFiltered && notFiltered < len ? notFiltered : 'all' ) + ' rows' ); } - if (data.anyMatchFlag) { - if (c.sortLocaleCompare) { + if ( data.anyMatchFlag ) { + if ( c.sortLocaleCompare ) { // replace accents - data.anyMatchFilter = ts.replaceAccents(data.anyMatchFilter); + data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter ); } - if ( wo.filter_defaultFilter && regex.iQuery.test( wo.filter_indexed.defaultAnyFilter ) ) { - data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, wo.filter_indexed.defaultAnyFilter ); + if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultAnyFilter ) ) { + data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter ); // clear search filtered flag because default filters are not saved to the last search searchFiltered = false; } // make iAnyMatchFilter lowercase unless both filter widget & core ignoreCase options are true // when c.ignoreCase is true, the cache contains all lower case data - data.iAnyMatchFilter = !(wo.filter_ignoreCase && c.ignoreCase) ? data.anyMatchFilter : data.anyMatchFilter.toLocaleLowerCase(); + data.iAnyMatchFilter = !( wo.filter_ignoreCase && c.ignoreCase ) ? + data.anyMatchFilter : + data.anyMatchFilter.toLowerCase(); } // loop through the rows - for (rowIndex = 0; rowIndex < len; rowIndex++) { + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { - data.cacheArray = norm_rows[rowIndex]; - - childRow = $rows[rowIndex].className; + txt = $rows[ rowIndex ].className; + // the first row can never be a child row + isChild = rowIndex && regex.child.test( txt ); // skip child rows & already filtered rows - if ( regex.child.test(childRow) || (searchFiltered && regex.filtered.test(childRow)) ) { continue; } - showRow = true; - // *** nextAll/nextUntil not supported by Zepto! *** - childRow = $rows.eq(rowIndex).nextUntil('tr:not(.' + c.cssChildRow + ')'); - // so, if "table.config.widgetOptions.filter_childRows" is true and there is - // a match anywhere in the child row, then it will make the row visible - // checked here so the option can be changed dynamically - data.childRowText = (childRow.length && wo.filter_childRows) ? childRow.text() : ''; - data.childRowText = wo.filter_ignoreCase ? data.childRowText.toLocaleLowerCase() : data.childRowText; - $cells = $rows.eq(rowIndex).children(); - if (data.anyMatchFlag) { - // look for multiple columns "1-3,4-6,8" - columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); - data.anyMatch = true; - data.rowArray = $cells.map(function(i){ - if ( $.inArray(i, columnIndex) > -1 ) { - var txt; - if (data.parsed[i]) { - txt = data.cacheArray[i]; - } else { - txt = this ? this.getAttribute( c.textAttribute ) || this.textContent || $(this).text() : ''; - txt = $.trim( wo.filter_ignoreCase ? txt.toLowerCase() : txt ); - if (c.sortLocaleCompare) { - txt = ts.replaceAccents(txt); - } - } - return txt; - } - }).get(); - data.filter = data.anyMatchFilter; - data.iFilter = data.iAnyMatchFilter; - data.exact = data.rowArray.join(' '); - data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; - data.cache = data.cacheArray.slice(0,-1).join(' '); - filterMatched = null; - $.each(ts.filter.types, function(type, typeFunction) { - if ($.inArray(type, noAnyMatch) < 0) { - matches = typeFunction( c, data ); - if (matches !== null) { - filterMatched = matches; - return false; - } - } - }); - if (filterMatched !== null) { - showRow = filterMatched; - } else { - if (wo.filter_startsWith) { - showRow = false; - columnIndex = c.columns; - while (!showRow && columnIndex > 0) { - columnIndex--; - showRow = showRow || data.rowArray[columnIndex].indexOf(data.iFilter) === 0; - } - } else { - showRow = (data.iExact + data.childRowText).indexOf(data.iFilter) >= 0; - } - } - data.anyMatch = false; + if ( isChild || ( searchFiltered && regex.filtered.test( txt ) ) ) { + continue; } - for (columnIndex = 0; columnIndex < c.columns; columnIndex++) { - data.filter = filters[columnIndex]; - data.index = columnIndex; - - // filter types to exclude, per column - excludeMatch = wo.filter_indexed.excludeFilter[ columnIndex ]; + data.$row = $rows.eq( rowIndex ); + data.cacheArray = norm_rows[ rowIndex ]; + rowData = data.cacheArray[ c.columns ]; + data.rawArray = rowData.raw; + data.childRowText = ''; - // ignore if filter is empty or disabled - if (data.filter) { - data.cache = data.cacheArray[columnIndex]; - // check if column data should be from the cell or from parsed data - if (wo.filter_useParsedData || data.parsed[columnIndex]) { - data.exact = data.cache; - } else { - val = $cells[columnIndex]; - result = val ? $.trim( val.getAttribute( c.textAttribute ) || val.textContent || $cells.eq(columnIndex).text() ) : ''; - data.exact = c.sortLocaleCompare ? ts.replaceAccents(result) : result; // issue #405 - } - data.iExact = !regex.type.test(typeof data.exact) && wo.filter_ignoreCase ? data.exact.toLocaleLowerCase() : data.exact; - result = showRow; // if showRow is true, show that row + if ( !wo.filter_childByColumn ) { + txt = ''; + // child row cached text + childRow = rowData.child; + // so, if 'table.config.widgetOptions.filter_childRows' is true and there is + // a match anywhere in the child row, then it will make the row visible + // checked here so the option can be changed dynamically + for ( indx = 0; indx < childRow.length; indx++ ) { + txt += ' ' + childRow[indx].join( '' ) || ''; + } + data.childRowText = wo.filter_childRows ? + ( wo.filter_ignoreCase ? txt.toLowerCase() : txt ) : + ''; + } - // in case select filter option has a different value vs text "a - z|A through Z" - ffxn = wo.filter_columnFilters ? - c.$filters.add(c.$externalFilters).filter('[data-column="'+ columnIndex + '"]').find('select option:selected').attr('data-function-name') || '' : ''; - // replace accents - see #357 - if (c.sortLocaleCompare) { - data.filter = ts.replaceAccents(data.filter); - } + showRow = ts.filter.processRow( c, data, vars ); + childRow = rowData.$row.filter( ':gt( 0 )' ); - val = true; - if (wo.filter_defaultFilter && regex.iQuery.test( wo.filter_indexed.defaultColFilter[ columnIndex ] )) { - data.filter = ts.filter.defaultFilter( data.filter, wo.filter_indexed.defaultColFilter[ columnIndex ] ); - // val is used to indicate that a filter select is using a default filter; so we override the exact & partial matches - val = false; - } - // data.iFilter = case insensitive (if wo.filter_ignoreCase is true), data.filter = case sensitive - data.iFilter = wo.filter_ignoreCase ? (data.filter || '').toLocaleLowerCase() : data.filter; - fxn = wo.filter_indexed.functions[ columnIndex ]; - $cell = c.$headerIndexed[columnIndex]; - hasSelect = $cell.hasClass('filter-select'); - filterMatched = null; - if ( fxn || ( hasSelect && val ) ) { - if (fxn === true || hasSelect) { - // default selector uses exact match unless "filter-match" class is found - filterMatched = ($cell.hasClass('filter-match')) ? data.iExact.search(data.iFilter) >= 0 : data.filter === data.exact; - } else if (typeof fxn === 'function') { - // filter callback( exact cell content, parser normalized content, filter input value, column index, jQuery row object ) - filterMatched = fxn(data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); - } else if (typeof fxn[ffxn || data.filter] === 'function') { - // selector option function - filterMatched = fxn[ffxn || data.filter](data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex), c); - } + if ( wo.filter_childRows && childRow.length ) { + if ( wo.filter_childByColumn ) { + // cycle through each child row + for ( indx = 0; indx < childRow.length; indx++ ) { + data.$row = childRow.eq( indx ); + data.cacheArray = rowData.child[ indx ]; + data.rawArray = data.cacheArray; + // use OR comparison on child rows + showRow = showRow || ts.filter.processRow( c, data, vars ); } - if (filterMatched === null) { - // cycle through the different filters - // filters return a boolean or null if nothing matches - $.each(ts.filter.types, function(type, typeFunction) { - if ($.inArray(type, excludeMatch) < 0) { - matches = typeFunction( c, data ); - if (matches !== null) { - filterMatched = matches; - return false; - } - } - }); - if (filterMatched !== null) { - result = filterMatched; - // Look for match, and add child row data for matching - } else { - data.exact = (data.iExact + data.childRowText).indexOf( ts.filter.parseFilter(c, data.iFilter, columnIndex, data.parsed[columnIndex]) ); - result = ( (!wo.filter_startsWith && data.exact >= 0) || (wo.filter_startsWith && data.exact === 0) ); - } - } else { - result = filterMatched; - } - showRow = (result) ? showRow : false; } + childRow.toggleClass( wo.filter_filteredRow, !showRow ); } - $rows.eq(rowIndex) - .toggleClass(wo.filter_filteredRow, !showRow)[0] + + rowData.$row + .toggleClass( wo.filter_filteredRow, !showRow )[0] .display = showRow ? '' : 'none'; - if (childRow.length) { - childRow.toggleClass(wo.filter_filteredRow, !showRow); - } } } - c.filteredRows += $rows.not('.' + wo.filter_filteredRow).length; + c.filteredRows += $rows.not( '.' + wo.filter_filteredRow ).length; c.totalRows += $rows.length; - ts.processTbody(table, $tbody, false); + ts.processTbody( table, $tbody, false ); } c.lastCombinedFilter = combinedFilters; // save last search - c.lastSearch = filters; - c.$table.data('lastSearch', filters); - if (wo.filter_saveFilters && ts.storage) { - ts.storage( table, 'tablesorter-filters', filters ); + // don't save 'filters' directly since it may have altered ( AnyMatch column searches ) + c.lastSearch = storedFilters; + c.$table.data( 'lastSearch', storedFilters ); + if ( wo.filter_saveFilters && ts.storage ) { + ts.storage( table, 'tablesorter-filters', storedFilters ); + } + if ( c.debug ) { + ts.benchmark( 'Completed filter widget search', time ); } - if (c.debug) { - ts.benchmark("Completed filter widget search", time); + if ( wo.filter_initialized ) { + c.$table.trigger( 'filterEnd', c ); } - if (wo.filter_initialized) { c.$table.trigger('filterEnd', c ); } - setTimeout(function(){ - c.$table.trigger('applyWidgets'); // make sure zebra widget is applied - }, 0); + setTimeout( function() { + c.$table.trigger( 'applyWidgets' ); // make sure zebra widget is applied + }, 0 ); }, - getOptionSource: function(table, column, onlyAvail) { - table = $(table)[0]; + getOptionSource: function( table, column, onlyAvail ) { + table = $( table )[0]; var cts, indx, len, c = table.config, wo = c.widgetOptions, parsed = [], arry = false, source = wo.filter_selectSource, - last = c.$table.data('lastSearch') || [], - fxn = $.isFunction(source) ? true : ts.getColumnData( table, source, column ); + last = c.$table.data( 'lastSearch' ) || [], + fxn = $.isFunction( source ) ? true : ts.getColumnData( table, source, column ); - if (onlyAvail && last[column] !== '') { + if ( onlyAvail && last[column] !== '' ) { onlyAvail = false; } // filter select source option - if (fxn === true) { + if ( fxn === true ) { // OVERALL source - arry = source(table, column, onlyAvail); - } else if ( fxn instanceof $ || ($.type(fxn) === 'string' && fxn.indexOf('</option>') >= 0) ) { + arry = source( table, column, onlyAvail ); + } else if ( fxn instanceof $ || ( $.type( fxn ) === 'string' && fxn.indexOf( '</option>' ) >= 0 ) ) { // selectSource is a jQuery object or string of options return fxn; - } else if ($.isArray(fxn)) { + } else if ( $.isArray( fxn ) ) { arry = fxn; - } else if ($.type(source) === 'object' && fxn) { + } else if ( $.type( source ) === 'object' && fxn ) { // custom select source function for a SPECIFIC COLUMN - arry = fxn(table, column, onlyAvail); + arry = fxn( table, column, onlyAvail ); } - if (arry === false) { + if ( arry === false ) { // fall back to original method - arry = ts.filter.getOptions(table, column, onlyAvail); + arry = ts.filter.getOptions( table, column, onlyAvail ); } // get unique elements and sort the list - // if $.tablesorter.sortText exists (not in the original tablesorter), + // if $.tablesorter.sortText exists ( not in the original tablesorter ), // then natural sort the list otherwise use a basic sort - arry = $.grep(arry, function(value, indx) { - return $.inArray(value, arry) === indx; + arry = $.grep( arry, function( value, indx ) { + return $.inArray( value, arry ) === indx; }); - if (c.$headerIndexed[column].hasClass('filter-select-nosort')) { + if ( c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) { // unsorted select options return arry; } else { len = arry.length; // parse select option values - for (indx = 0; indx < len; indx++) { + for ( indx = 0; indx < len; indx++ ) { // parse array data using set column parser; this DOES NOT pass the original // table cell to the parser format function - parsed.push({ t : arry[indx], p : c.parsers && c.parsers[column].format( arry[indx], table, [], column ) }); + parsed.push({ + t : arry[ indx ], + p : c.parsers && c.parsers[ column ].format( arry[ indx ], table, [], column ) + }); } // sort parsed select options cts = c.textSorter || ''; - parsed.sort(function(a, b){ + parsed.sort( function( a, b ) { // sortNatural breaks if you don't pass it strings - var x = a.p.toString(), y = b.p.toString(); - if ($.isFunction(cts)) { + var x = a.p.toString(), + y = b.p.toString(); + if ( $.isFunction( cts ) ) { // custom OVERALL text sorter - return cts(x, y, true, column, table); - } else if (typeof(cts) === 'object' && cts.hasOwnProperty(column)) { + return cts( x, y, true, column, table ); + } else if ( typeof( cts ) === 'object' && cts.hasOwnProperty( column ) ) { // custom text sorter for a SPECIFIC COLUMN - return cts[column](x, y, true, column, table); - } else if (ts.sortNatural) { + return cts[column]( x, y, true, column, table ); + } else if ( ts.sortNatural ) { // fall back to natural sort - return ts.sortNatural(x, y); + return ts.sortNatural( x, y ); } // using an older version! do a basic sort return true; @@ -1161,184 +1345,221 @@ ts.filter = { // rebuild arry from sorted parsed data arry = []; len = parsed.length; - for (indx = 0; indx < len; indx++) { + for ( indx = 0; indx < len; indx++ ) { arry.push( parsed[indx].t ); } return arry; } }, - getOptions: function(table, column, onlyAvail) { - table = $(table)[0]; - var rowIndex, tbodyIndex, len, row, cache, cell, + getOptions: function( table, column, onlyAvail ) { + table = $( table )[0]; + var rowIndex, tbodyIndex, len, row, cache, c = table.config, wo = c.widgetOptions, arry = []; - for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { cache = c.cache[tbodyIndex]; len = c.cache[tbodyIndex].normalized.length; // loop through the rows - for (rowIndex = 0; rowIndex < len; rowIndex++) { - // get cached row from cache.row (old) or row data object (new; last item in normalized array) - row = cache.row ? cache.row[rowIndex] : cache.normalized[rowIndex][c.columns].$row[0]; + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { + // get cached row from cache.row ( old ) or row data object + // ( new; last item in normalized array ) + row = cache.row ? + cache.row[ rowIndex ] : + cache.normalized[ rowIndex ][ c.columns ].$row[0]; // check if has class filtered - if (onlyAvail && row.className.match(wo.filter_filteredRow)) { continue; } + if ( onlyAvail && row.className.match( wo.filter_filteredRow ) ) { + continue; + } // get non-normalized cell content - if (wo.filter_useParsedData || c.parsers[column].parsed || c.$headerIndexed[column].hasClass('filter-parsed')) { - arry.push( '' + cache.normalized[rowIndex][column] ); + if ( wo.filter_useParsedData || + c.parsers[column].parsed || + c.$headerIndexed[column].hasClass( 'filter-parsed' ) ) { + arry.push( '' + cache.normalized[ rowIndex ][ column ] ); } else { - cell = row.cells[column]; - if (cell) { - arry.push( $.trim( cell.getAttribute( c.textAttribute ) || cell.textContent || $(cell).text() ) ); - } + // get raw cached data instead of content directly from the cells + arry.push( cache.normalized[ rowIndex ][ c.columns ].raw[ column ] ); } } } return arry; }, - buildSelect: function(table, column, arry, updating, onlyAvail) { - table = $(table)[0]; - column = parseInt(column, 10); - if (!table.config.cache || $.isEmptyObject(table.config.cache)) { return; } + buildSelect: function( table, column, arry, updating, onlyAvail ) { + table = $( table )[0]; + column = parseInt( column, 10 ); + if ( !table.config.cache || $.isEmptyObject( table.config.cache ) ) { + return; + } var indx, val, txt, t, $filters, $filter, c = table.config, wo = c.widgetOptions, - node = c.$headerIndexed[column], - // t.data('placeholder') won't work in jQuery older than 1.4.3 - options = '<option value="">' + ( node.data('placeholder') || node.attr('data-placeholder') || wo.filter_placeholder.select || '' ) + '</option>', + node = c.$headerIndexed[ column ], + // t.data( 'placeholder' ) won't work in jQuery older than 1.4.3 + options = '<option value="">' + + ( node.data( 'placeholder' ) || + node.attr( 'data-placeholder' ) || + wo.filter_placeholder.select || '' + ) + '</option>', // Get curent filter value - currentValue = c.$table.find('thead').find('select.' + tscss.filter + '[data-column="' + column + '"]').val(); - // nothing included in arry (external source), so get the options from filter_selectSource or column data - if (typeof arry === 'undefined' || arry === '') { - arry = ts.filter.getOptionSource(table, column, onlyAvail); + currentValue = c.$table + .find( 'thead' ) + .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) + .val(); + // nothing included in arry ( external source ), so get the options from + // filter_selectSource or column data + if ( typeof arry === 'undefined' || arry === '' ) { + arry = ts.filter.getOptionSource( table, column, onlyAvail ); } - if ($.isArray(arry)) { + if ( $.isArray( arry ) ) { // build option list - for (indx = 0; indx < arry.length; indx++) { - txt = arry[indx] = ('' + arry[indx]).replace(/\"/g, """); + for ( indx = 0; indx < arry.length; indx++ ) { + txt = arry[indx] = ( '' + arry[indx] ).replace( /\"/g, '"' ); val = txt; // allow including a symbol in the selectSource array - // "a-z|A through Z" so that "a-z" becomes the option value - // and "A through Z" becomes the option text - if (txt.indexOf(wo.filter_selectSourceSeparator) >= 0) { - t = txt.split(wo.filter_selectSourceSeparator); + // 'a-z|A through Z' so that 'a-z' becomes the option value + // and 'A through Z' becomes the option text + if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { + t = txt.split( wo.filter_selectSourceSeparator ); val = t[0]; txt = t[1]; } - // replace quotes - fixes #242 & ignore empty strings - see http://stackoverflow.com/q/14990971/145346 - options += arry[indx] !== '' ? '<option ' + (val === txt ? '' : 'data-function-name="' + arry[indx] + '" ') + 'value="' + val + '">' + txt + '</option>' : ''; + // replace quotes - fixes #242 & ignore empty strings + // see http://stackoverflow.com/q/14990971/145346 + options += arry[indx] !== '' ? + '<option ' + + ( val === txt ? '' : 'data-function-name="' + arry[indx] + '" ' ) + + 'value="' + val + '">' + txt + + '</option>' : ''; } // clear arry so it doesn't get appended twice arry = []; } - // update all selects in the same column (clone thead in sticky headers & any external selects) - fixes 473 - $filters = ( c.$filters ? c.$filters : c.$table.children('thead') ).find('.' + tscss.filter); - if (wo.filter_$externalFilters) { - $filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters; + // update all selects in the same column ( clone thead in sticky headers & + // any external selects ) - fixes 473 + $filters = ( c.$filters ? c.$filters : c.$table.children( 'thead' ) ) + .find( '.' + tscss.filter ); + if ( wo.filter_$externalFilters ) { + $filters = $filters && $filters.length ? + $filters.add( wo.filter_$externalFilters ) : + wo.filter_$externalFilters; } - $filter = $filters.filter('select[data-column="' + column + '"]'); + $filter = $filters.filter( 'select[data-column="' + column + '"]' ); // make sure there is a select there! - if ($filter.length) { - $filter[ updating ? 'html' : 'append' ](options); - if (!$.isArray(arry)) { + if ( $filter.length ) { + $filter[ updating ? 'html' : 'append' ]( options ); + if ( !$.isArray( arry ) ) { // append options if arry is provided externally as a string or jQuery object - // options (default value) was already added - $filter.append(arry).val(currentValue); + // options ( default value ) was already added + $filter.append( arry ).val( currentValue ); } - $filter.val(currentValue); + $filter.val( currentValue ); } }, - buildDefault: function(table, updating) { + buildDefault: function( table, updating ) { var columnIndex, $header, noSelect, c = table.config, wo = c.widgetOptions, columns = c.columns; // build default select dropdown - for (columnIndex = 0; columnIndex < columns; columnIndex++) { + for ( columnIndex = 0; columnIndex < columns; columnIndex++ ) { $header = c.$headerIndexed[columnIndex]; - noSelect = !($header.hasClass('filter-false') || $header.hasClass('parser-false')); + noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); // look for the filter-select class; build/update it if found - if (($header.hasClass('filter-select') || ts.getColumnData( table, wo.filter_functions, columnIndex ) === true) && noSelect) { - ts.filter.buildSelect(table, columnIndex, '', updating, $header.hasClass(wo.filter_onlyAvail)); + if ( ( $header.hasClass( 'filter-select' ) || + ts.getColumnData( table, wo.filter_functions, columnIndex ) === true ) && noSelect ) { + ts.filter.buildSelect( table, columnIndex, '', updating, $header.hasClass( wo.filter_onlyAvail ) ); } } } }; -ts.getFilters = function(table, getRaw, setFilters, skipFirst) { +ts.getFilters = function( table, getRaw, setFilters, skipFirst ) { var i, $filters, $column, cols, filters = false, - c = table ? $(table)[0].config : '', + c = table ? $( table )[0].config : '', wo = c ? c.widgetOptions : ''; - if (getRaw !== true && wo && !wo.filter_columnFilters) { - return $(table).data('lastSearch'); + if ( ( getRaw !== true && wo && !wo.filter_columnFilters ) || + // setFilters called, but last search is exactly the same as the current + // fixes issue #733 & #903 where calling update causes the input values to reset + ( $.isArray(setFilters) && setFilters.join('') === c.lastCombinedFilter ) ) { + return $( table ).data( 'lastSearch' ); } - if (c) { - if (c.$filters) { - $filters = c.$filters.find('.' + tscss.filter); + if ( c ) { + if ( c.$filters ) { + $filters = c.$filters.find( '.' + tscss.filter ); } - if (wo.filter_$externalFilters) { - $filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters; + if ( wo.filter_$externalFilters ) { + $filters = $filters && $filters.length ? + $filters.add( wo.filter_$externalFilters ) : + wo.filter_$externalFilters; } - if ($filters && $filters.length) { + if ( $filters && $filters.length ) { filters = setFilters || []; - for (i = 0; i < c.columns + 1; i++) { + for ( i = 0; i < c.columns + 1; i++ ) { cols = ( i === c.columns ? - // "all" columns can now include a range or set of columms (data-column="0-2,4,6-7") + // 'all' columns can now include a range or set of columms ( data-column='0-2,4,6-7' ) wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector : '[data-column="' + i + '"]' ); - $column = $filters.filter(cols); - if ($column.length) { + $column = $filters.filter( cols ); + if ( $column.length ) { // move the latest search to the first slot in the array $column = ts.filter.getLatestSearch( $column ); - if ($.isArray(setFilters)) { - // skip first (latest input) to maintain cursor position while typing - if (skipFirst) { $column.slice(1); } - if (i === c.columns) { - // prevent data-column="all" from filling data-column="0,1" (etc) - cols = $column.filter(wo.filter_anyColumnSelector); + if ( $.isArray( setFilters ) ) { + // skip first ( latest input ) to maintain cursor position while typing + if ( skipFirst ) { + $column.slice( 1 ); + } + if ( i === c.columns ) { + // prevent data-column='all' from filling data-column='0,1' ( etc ) + cols = $column.filter( wo.filter_anyColumnSelector ); $column = cols.length ? cols : $column; } $column - .val( setFilters[i] ) - .trigger('change.tsfilter'); + .val( setFilters[ i ] ) + .trigger( 'change.tsfilter' ); } else { filters[i] = $column.val() || ''; // don't change the first... it will move the cursor - if (i === c.columns) { - // don't update range columns from "all" setting - $column.slice(1).filter('[data-column*="' + $column.attr('data-column') + '"]').val( filters[i] ); + if ( i === c.columns ) { + // don't update range columns from 'all' setting + $column + .slice( 1 ) + .filter( '[data-column*="' + $column.attr( 'data-column' ) + '"]' ) + .val( filters[ i ] ); } else { - $column.slice(1).val( filters[i] ); + $column + .slice( 1 ) + .val( filters[ i ] ); } } // save any match input dynamically - if (i === c.columns && $column.length) { + if ( i === c.columns && $column.length ) { wo.filter_$anyMatch = $column; } } } } } - if (filters.length === 0) { + if ( filters.length === 0 ) { filters = false; } return filters; }; -ts.setFilters = function(table, filter, apply, skipFirst) { - var c = table ? $(table)[0].config : '', - valid = ts.getFilters(table, true, filter, skipFirst); - if (c && apply) { +ts.setFilters = function( table, filter, apply, skipFirst ) { + var c = table ? $( table )[0].config : '', + valid = ts.getFilters( table, true, filter, skipFirst ); + if ( c && apply ) { // ensure new set filters are applied, even if the search is the same c.lastCombinedFilter = null; c.lastSearch = []; - ts.filter.searching(c.table, filter, skipFirst); - c.$table.trigger('filterFomatterUpdate'); + ts.filter.searching( c.table, filter, skipFirst ); + c.$table.trigger( 'filterFomatterUpdate' ); } return !!valid; }; -})(jQuery); +})( jQuery ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index 6d48656..178d8cc 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -110,7 +110,7 @@ ts.grouping = { currentGroup = wo.group_formatter((currentGroup || '').toString(), column, table, c, wo) || currentGroup; } $rows.eq(rowIndex).before('<tr class="group-header ' + c.selectorRemove.slice(1) + - '" unselectable="on"><td colspan="' + + '" unselectable="on"' + ( c.tabIndex ? ' tabindex="0"' : '' ) + '><td colspan="' + c.columns + '">' + (wo.group_collapsible ? '<i/>' : '') + '<span class="group-name">' + currentGroup + '</span><span class="group-count"></span></td></tr>'); if (wo.group_saveGroups && !savedGroup && wo.group_collapsed && wo.group_collapsible) { @@ -157,13 +157,15 @@ ts.grouping = { if (wo.group_collapsible) { wo.group_currentGroups = []; // .on() requires jQuery 1.7+ - c.$table.on('click toggleGroup', 'tr.group-header', function(event){ + c.$table.on('click toggleGroup keyup', 'tr.group-header', function(event){ event.stopPropagation(); + // pressing enter will toggle the group + if (event.type === 'keyup' && event.which !== 13) { return; } var isCollapsed, $groups, indx, $this = $(this), name = $this.find('.group-name').text().toLowerCase(); // use shift-click to toggle ALL groups - if (event.type === 'click' && event.shiftKey) { + if (event.shiftKey && (event.type === 'click' || event.type ==='keyup')) { $this.siblings('.group-header').trigger('toggleGroup'); } $this.toggleClass('collapsed'); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index 54962d5..8ac8b49 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -1,10 +1,10 @@ -/*! Widget: math - updated 2/9/2015 (v2.19.1) *//* +/*! Widget: math - updated 5/17/2015 (v2.22.0) *//* * Requires tablesorter v2.16+ and jQuery 1.7+ * by Rob Garrison */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ -;(function($){ +;(function($) { "use strict"; var ts = $.tablesorter, @@ -20,10 +20,10 @@ c = table.config, arry = [], $row = $el.closest('tr'), - $cells = $row.children(); + $cells = $row.children().not('[' + dataAttrib + '=ignore]'); if (!$row.hasClass(wo.filter_filteredRow || 'filtered')) { if (wo.math_ignore.length) { - $cells = $cells.not('[' + dataAttrib + '=ignore]').not('[data-column=' + wo.math_ignore.join('],[data-column=') + ']'); + $cells = $cells.not('[data-column=' + wo.math_ignore.join('],[data-column=') + ']'); } arry = $cells.not($el).map(function(){ $t = $(this); @@ -31,7 +31,7 @@ if (typeof txt === "undefined") { txt = this.textContent || $t.text(); } - txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table); + txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table) || 0; return isNaN(txt) ? 0 : txt; }).get(); } @@ -39,7 +39,7 @@ }, // get all of the column numerical values in an arry - getColumn : function(table, wo, $el, type, dataAttrib){ + getColumn : function(table, wo, $el, type, dataAttrib) { var i, txt, $t, len, mathAbove, arry = [], c = table.config, @@ -65,7 +65,7 @@ if (typeof txt === "undefined") { txt = $t[0].textContent || $t.text(); } - txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table); + txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table) || 0; arry.push(isNaN(txt) ? 0 : txt); } } @@ -79,7 +79,8 @@ if (typeof txt === "undefined") { txt = ($t[0] ? $t[0].textContent : '') || $t.text(); } - txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table); + // isNaN('') => false + txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table) || 0; arry.push(isNaN(txt) ? 0 : txt); } }); @@ -88,7 +89,7 @@ }, // get all of the column numerical values in an arry - getAll : function(table, wo, dataAttrib){ + getAll : function(table, wo, dataAttrib) { var txt, $t, col, arry = [], c = table.config, @@ -104,7 +105,7 @@ if (typeof txt === "undefined") { txt = ($t[0] ? $t[0].textContent : '') || $t.text(); } - txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table); + txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table) || 0; arry.push(isNaN(txt) ? 0 : txt); } }); @@ -113,7 +114,7 @@ return arry; }, - recalculate : function(table, c, wo, init){ + recalculate : function(table, c, wo, init) { if (c && (!wo.math_isUpdating || init)) { // add data-column attributes to all table cells @@ -364,7 +365,7 @@ return Math.sqrt( vars ); }, // standard deviation (population) - stdevp : function(arry){ + stdevp : function(arry) { var varp = ts.equations.variance(arry, true); return Math.sqrt( varp ); } @@ -391,28 +392,29 @@ math_suffix : '', math_event : 'recalculate' }, - init : function(table, thisWidget, c, wo){ + init : function(table, thisWidget, c, wo) { c.$table .off( (math.events + ' updateComplete.tsmath ' + wo.math_event).replace(/\s+/g, ' ') ) - .on(math.events + ' ' + wo.math_event, function(e){ + .on(math.events + ' ' + wo.math_event, function(e) { var init = e.type === 'tablesorter-initialized'; - if (e.type === 'updateAll') { - // redo data-column indexes in case columns were rearranged - ts.computeColumnIndex( c.$table.children('tbody').children() ); - } else if (!wo.math_isUpdating || init) { + if ( !wo.math_isUpdating || init ) { + if ( !/filter/.test(e.type) ) { + // redo data-column indexes on update + ts.computeColumnIndex( c.$table.children('tbody').children() ); + } math.recalculate( table, c, wo, init ); } }) .on('updateComplete.tsmath', function(){ setTimeout(function(){ wo.math_isUpdating = false; - }, 500); + }, 20); }); wo.math_isUpdating = false; }, // this remove function is called when using the refreshWidgets method or when destroying the tablesorter plugin // this function only applies to tablesorter v2.4+ - remove: function(table, c, wo, refreshing){ + remove: function(table, c, wo, refreshing) { if (refreshing) { return; } $(table) .off( (math.events + ' updateComplete.tsmath ' + wo.math_event).replace(/\s+/g, ' ') ) diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index e76174c..cdf5e82 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -1,4 +1,4 @@ -/*! Widget: output - updated 3/26/2015 (v2.21.3) *//* +/*! Widget: output - updated 5/17/2015 (v2.22.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * Modified from: * HTML Table to CSV: http://www.kunalbabre.com/projects/table2CSV.php (License unknown?) @@ -79,8 +79,8 @@ output = ts.output = { } } - // don't include hidden columns - if ( $this.css('display') !== 'none' ) { + // don't include hidden columns, unless option is set + if ( !wo.output_hiddenColumns && $this.css('display') !== 'none' ) { // skip column if already defined while (typeof tmpRow[rowIndex][cellIndex] !== 'undefined') { cellIndex++; } tmpRow[rowIndex][cellIndex] = tmpRow[rowIndex][cellIndex] || @@ -255,8 +255,9 @@ output = ts.output = { // Use HTML5 Blob if browser supports it if ( gotBlob ) { - window.URL = window.webkitURL || window.URL; - blob = new Blob([data], {type: wo.output_encoding}); + window.URL = window.URL || window.webkitURL; + // prepend BOM for utf-8 encoding - see https://github.com/eligrey/FileSaver.js/blob/master/FileSaver.js#L140 + blob = new Blob( [ '\ufeff', data ], { type: wo.output_encoding } ); if (nav.msSaveBlob) { // IE 10+ @@ -294,6 +295,7 @@ ts.addWidget({ options: { output_separator : ',', // set to "json", "array" or any separator output_ignoreColumns : [], // columns to ignore [0, 1,... ] (zero-based index) + output_hiddenColumns : false, // include hidden columns in the output output_includeFooter : false, // include footer rows in the output output_dataAttrib : 'data-name', // header attrib containing modified header name output_headerRows : false, // if true, include multiple header rows (JSON only) diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 98c2a91..9b65bef 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 3/26/2015 (v2.21.3) */ +/*! Widget: Pager - updated 5/17/2015 (v2.22.0) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -638,11 +638,11 @@ tsp = ts.pager = { // process data if ( $.isFunction(wo.pager_ajaxProcessing) ) { // ajaxProcessing result: [ total, rows, headers ] - var i, j, t, hsh, $f, $sh, th, d, l, rr_count, - $t = c.$table, + var i, j, t, hsh, $f, $sh, $headers, $h, icon, th, d, l, rr_count, len, + $table = c.$table, tds = '', result = wo.pager_ajaxProcessing(data, table, xhr) || [ 0, [] ], - hl = $t.find('thead th').length; + hl = $table.find('thead th').length; // Clean up any previous error. ts.showError(table); @@ -698,28 +698,30 @@ tsp = ts.pager = { wo.pager_processAjaxOnInit = true; // only add new header text if the length matches if ( th && th.length === hl ) { - hsh = $t.hasClass('hasStickyHeaders'); + hsh = $table.hasClass('hasStickyHeaders'); $sh = hsh ? wo.$sticky.children('thead:first').children('tr').children() : ''; - $f = $t.find('tfoot tr:first').children(); + $f = $table.find('tfoot tr:first').children(); // don't change td headers (may contain pager) - c.$headers.filter('th').each(function(j){ - var $t = $(this), icn; + $headers = c.$headers.filter( 'th '); + len = $headers.length; + for ( j = 0; j < len; j++ ) { + $h = $headers.eq( j ); // add new test within the first span it finds, or just in the header - if ( $t.find('.' + ts.css.icon).length ) { - icn = $t.find('.' + ts.css.icon).clone(true); - $t.find('.tablesorter-header-inner').html( th[j] ).append(icn); + if ( $h.find('.' + ts.css.icon).length ) { + icon = $h.find('.' + ts.css.icon).clone(true); + $h.find('.tablesorter-header-inner').html( th[j] ).append(icon); if ( hsh && $sh.length ) { - icn = $sh.eq(j).find('.' + ts.css.icon).clone(true); - $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ).append(icn); + icon = $sh.eq(j).find('.' + ts.css.icon).clone(true); + $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ).append(icon); } } else { - $t.find('.tablesorter-header-inner').html( th[j] ); + $h.find('.tablesorter-header-inner').html( th[j] ); if (hsh && $sh.length) { $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ); } } $f.eq(j).html( th[j] ); - }); + } } } if (c.showProcessing) { @@ -734,7 +736,7 @@ tsp = ts.pager = { p.initializing = false; // update display without triggering pager complete... before updating cache tsp.updatePageDisplay(table, c, false); - $t.trigger('updateCache', [function(){ + $table.trigger('updateCache', [function(){ if (p.initialized) { // apply widgets after table has rendered & after a delay to prevent // multiple applyWidget blocking code from blocking this trigger @@ -742,7 +744,7 @@ tsp = ts.pager = { if (c.debug) { ts.log('Pager: Triggering pagerChange'); } - $t + $table .trigger('applyWidgets') .trigger('pagerChange', p); tsp.updatePageDisplay(table, c); @@ -898,7 +900,8 @@ tsp = ts.pager = { }, showAllRows: function(table, c){ - var p = c.pager, + var index, $controls, len, + p = c.pager, wo = c.widgetOptions; if ( p.ajax ) { tsp.pagerArrows(c, true); @@ -920,9 +923,15 @@ tsp = ts.pager = { } } // disable size selector - p.$size.add(p.$goto).each(function(){ - $(this).attr('aria-disabled', 'true').addClass(wo.pager_css.disabled)[0].disabled = true; - }); + $controls = p.$size + .add( p.$goto ) + .add( p.$container.find( '.ts-startRow, .ts-page ' ) ); + len = $controls.length; + for ( index = 0; index < len; index++ ) { + $controls.eq( index ) + .attr( 'aria-disabled', 'true' ) + .addClass( wo.pager_css.disabled )[0].disabled = true; + } }, // updateCache if delayInit: true @@ -1109,14 +1118,16 @@ tsp = ts.pager = { }; // see #486 -ts.showError = function(table, message){ - $(table).each(function(){ - var $row, - c = this.config, - wo = c.widgetOptions, +ts.showError = function( table, message ) { + var index, $row, c, wo, errorRow, + $table = $( table ), + len = $table.length; + for ( index = 0; index < len; index++ ) { + c = $table[ index ].config; + if ( c ) { + wo = c.widgetOptions; errorRow = c.pager && c.pager.cssErrorRow || wo.pager_css && wo.pager_css.errorRow || 'tablesorter-errorRow'; - if (c) { - if (typeof message === 'undefined') { + if ( typeof message === 'undefined' ) { c.$table.find('thead').find(c.selectorRemove).remove(); } else { $row = ( /tr\>/.test(message) ? $(message) : $('<tr><td colspan="' + c.columns + '">' + message + '</td></tr>') ) @@ -1132,7 +1143,7 @@ ts.showError = function(table, message){ }); } } - }); + } }; })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js index 882ec04..a6733e9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js @@ -1,7 +1,7 @@ -/*! Widget: resizable - updated 4/2/2015 (v2.21.5) */ +/*! Widget: resizable - updated 5/17/2015 (v2.22.0) */ ;(function ($, window) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; +var ts = $.tablesorter || {}; $.extend(ts.css, { resizableContainer : 'tablesorter-resizable-container', @@ -290,7 +290,7 @@ ts.addWidget({ init: function(table, thisWidget, c, wo) { ts.resizable.init( c, wo ); }, - remove: function( table, c, wo ) { + remove: function( table, c, wo, refreshing ) { if (wo.$resizable_container) { var namespace = c.namespace + 'tsresize'; c.$table.add( $( c.namespace + '_extra_table' ) ) @@ -299,13 +299,13 @@ ts.addWidget({ wo.$resizable_container.remove(); ts.resizable.toggleTextSelection( c, false ); - ts.resizableReset( table ); + ts.resizableReset( table, refreshing ); $( document ).unbind( 'mousemove' + namespace + ' mouseup' + namespace ); } } }); -ts.resizableReset = function( table, nosave ) { +ts.resizableReset = function( table, refreshing ) { $( table ).each(function(){ var index, $t, c = this.config, @@ -322,7 +322,7 @@ ts.resizableReset = function( table, nosave ) { } // reset stickyHeader widths $( window ).trigger( 'resize' ); - if ( ts.storage && !nosave ) { + if ( ts.storage && !refreshing ) { ts.storage( this, ts.css.resizableStorage, {} ); } } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js index d09310d..d34e2ad 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js @@ -1,7 +1,7 @@ /*! Widget: saveSort */ ;(function ($) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; +var ts = $.tablesorter || {}; // this widget saves the last sort only if the // saveSort widget option is true AND the diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index d55dbcf..cbad13b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -1,4 +1,4 @@ -/*! Widget: scroller - updated 4/2/2015 (v2.21.5) *//* +/*! Widget: scroller - updated 5/17/2015 (v2.22.0) *//* Copyright (C) 2011 T. Connell & Associates, Inc. Dual-licensed under the MIT and GPL licenses @@ -40,14 +40,15 @@ var ts = $.tablesorter, tscss = ts.css; $.extend( ts.css, { - scrollerWrap : 'tablesorter-scroller', - scrollerHeader : 'tablesorter-scroller-header', - scrollerTable : 'tablesorter-scroller-table', - scrollerFooter : 'tablesorter-scroller-footer', - scrollerFixed : 'tablesorter-scroller-fixed', - scrollerHasFix : 'tablesorter-scroller-has-fixed-columns', - scrollerReset : 'tablesorter-scroller-reset', - scrollerRtl : 'tablesorter-scroller-rtl' + scrollerWrap : 'tablesorter-scroller', + scrollerHeader : 'tablesorter-scroller-header', + scrollerTable : 'tablesorter-scroller-table', + scrollerFooter : 'tablesorter-scroller-footer', + scrollerFixed : 'tablesorter-scroller-fixed', + scrollerFixedPanel : 'tablesorter-scroller-fixed-panel', + scrollerHasFix : 'tablesorter-scroller-has-fixed-columns', + scrollerReset : 'tablesorter-scroller-reset', + scrollerRtl : 'tablesorter-scroller-rtl' }); ts.addWidget({ @@ -61,6 +62,8 @@ ts.addWidget({ scroller_fixedColumns : 0, // add hover highlighting to the fixed column (disable if it causes slowing) scroller_rowHighlight : 'hover', + // add a fixed column overlay for styling + scroller_addFixedOverlay : false, // bar width is now calculated; set a value to override scroller_barWidth : null }, @@ -95,6 +98,8 @@ $( function() { '.' + tscss.scrollerReset + ' { width: auto !important; min-width: auto !important; max-width: auto !important; }' + /* overall wrapper & table section wrappers */ '.' + tscss.scrollerWrap + ' { position: relative; overflow: hidden; }' + + /* add border-box sizing to all scroller widget tables; see #135 */ + '.' + tscss.scrollerWrap + ' * { box-sizing: border-box; }' + '.' + tscss.scrollerHeader + ', .' + tscss.scrollerFooter + ' { overflow: hidden; }' + '.' + tscss.scrollerHeader + ' table.' + tscss.table + ' { margin-bottom: 0; }' + '.' + tscss.scrollerFooter + ' table.' + tscss.table + ' thead { visibility: hidden, height: 0; overflow: hidden; }' + @@ -104,17 +109,20 @@ $( function() { /* hide filter row in clones */ '.' + tscss.scrollerTable + ' .' + ( tscss.filterRow || 'tablesorter-filter-row' ) + ',.' + tscss.scrollerFooter + ' .' + ( tscss.filterRow || 'tablesorter-filter-row' ) + ',.' + tscss.scrollerTable + ' tfoot { display: none; }' + - '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixed + ' { position: absolute; top: 0; z-index: 1; left: 0 } ' + - '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixed + '.' + tscss.scrollerRtl + ' { left: auto; right: 0 } ' + /* visibly hide header rows in clones, so we can still set a width on it and still effect the rest of the column */ '.' + tscss.scrollerTable + ' table.' + tscss.table + ' thead tr.' + tscss.headerRow + ' *, .' + tscss.scrollerFooter + ' table.' + tscss.table + ' thead * { line-height: 0; height: 0; border: none; background-image: none; padding-top: 0;' + ' padding-bottom: 0; margin-top: 0; margin-bottom: 0; overflow: hidden; }' + /*** fixed column ***/ - '.' + tscss.scrollerFixed + ' { pointer-events: none; }' + - /* add horizontal scroll bar */ - '.' + tscss.scrollerWrap + '.' + tscss.scrollerHasFix + ' > .' + tscss.scrollerTable + ' { overflow-x: scroll; }' + + /* disable pointer-events on fixed column wrapper or the user can't interact with the horizontal scrollbar */ + '.' + tscss.scrollerFixed + ', .' + tscss.scrollerFixed + ' .' + tscss.scrollerFixedPanel + ' { pointer-events: none; }' + + /* enable pointer-events for fixed column children; see #135 & #878 */ + '.' + tscss.scrollerFixed + ' > div { pointer-events: all; }' + + '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixed + ' { position: absolute; top: 0; z-index: 1; left: 0 } ' + + '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixed + '.' + tscss.scrollerRtl + ' { left: auto; right: 0 } ' + + /* add horizontal scroll bar; set to "auto", see #135 */ + '.' + tscss.scrollerWrap + '.' + tscss.scrollerHasFix + ' > .' + tscss.scrollerTable + ' { overflow-x: auto; }' + /* need to position the tbody & tfoot absolutely to hide the scrollbar & move the footer below the horizontal scrollbar */ '.' + tscss.scrollerFixed + ' .' + tscss.scrollerFooter + ' { position: absolute; bottom: 0; }' + /* hide fixed tbody scrollbar - see http://goo.gl/VsLe6n */ @@ -123,6 +131,9 @@ $( function() { /* remove right border of fixed header tables to hide the boundary */ '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixed + ' table { border-right-color: transparent; padding-right: 0; }' + '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixed + '.' + tscss.scrollerRtl + ' table { border-left-color: transparent; padding-left: 0; }' + + + /*** fixed column panel ***/ + '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixedPanel + ' { position: absolute; top: 0; bottom: 0; z-index: 2; left: 0; right: 0; } ' + '</style>'; $( style ).appendTo( 'body' ); }); @@ -131,6 +142,8 @@ ts.scroller = { // Ugh.. Firefox misbehaves, so it needs to be detected isFirefox : navigator.userAgent.toLowerCase().indexOf( 'firefox' ) > -1, + // old IE needs a wrap to hide the fixed column scrollbar; http://stackoverflow.com/a/24408672/145346 + isOldIE : document.all && !window.atob, hasScrollBar : function( $target ) { return $target.get(0).scrollHeight > $target.height(); @@ -241,7 +254,7 @@ ts.scroller = { // Sorting, so scroll to top $table - .off( 'sortEnd' + namespace + ' setFixedColumnSize' + namespace ) + .off( 'sortEnd setFixedColumnSize updateComplete '.split( ' ' ).join( namespace + ' ' ) ) .on( 'sortEnd' + namespace, function() { if ( wo.scroller_upAfterSort ) { $table.parent().animate({ scrollTop: 0 }, 'fast' ); @@ -253,11 +266,16 @@ ts.scroller = { } // remove fixed columns wo.scroller_$container.find( '.' + tscss.scrollerFixed ).remove(); + size = wo.scroller_fixedColumns; if ( size > 0 && size < c.columns - 1 ) { ts.scroller.setupFixed( c, wo ); } else { wo.scroller_$container.removeClass( tscss.scrollerHasFix ); } + }) + .on( 'updateComplete' + namespace, function() { + // adjust column sizes after an update + ts.scroller.resize( c, wo ); }); // Setup window.resizeEnd event @@ -381,6 +399,9 @@ ts.scroller = { .addClass( tscss.scrollerFixed ) .removeClass( tscss.scrollerWrap ) .attr( 'id', '' ); + if ( wo.scroller_addFixedOverlay ) { + $fixedColumn.append( '<div class="' + tscss.scrollerFixedPanel + '">' ); + } $fixedTbody = $fixedColumn.find( '.' + tscss.scrollerTable ); $fixedTbody.find( 'table' ) .addClass( c.namespace.slice(1) + '_extra_table' ) @@ -414,14 +435,15 @@ ts.scroller = { $el.eq( index ).prop( 'disabled', index < fixedColumns ); } } - // enable visible fixed column filters - $fixedColumn.children( '.' + tscss.scrollerHeader ).find( '.' + tscss.filter ).css( 'pointer-events', 'all' ); } // disable/enable tab indexes behind fixed column - c.$table.children( 'thead' ).children( 'tr.' + tscss.headerRow ).children().attr( 'tabindex', -1 ); + c.$table + .add( '.' + tscss.scrollerFooter + ' table' ) + .children( 'thead' ) + .children( 'tr.' + tscss.headerRow ).children().attr( 'tabindex', -1 ); $el = wo.scroller_$header - .add( $fixedColumn.find( '.' + tscss.scrollerTable + ' table, .' + tscss.scrollerFooter + ' table' ) ) + .add( $fixedColumn.find( '.' + tscss.scrollerTable + ' table' ) ) .children( 'thead' ).children( 'tr.' + tscss.headerRow ); len = $el.length; for ( index = 0; index < len; index++ ) { @@ -475,9 +497,9 @@ ts.scroller = { }); } - /*** STUPID FIREFOX HACK! Since we can't hide the scrollbar with css ***/ - if ( ts.scroller.isFirefox ) { - $fixedTbody.wrap( '<div class="scroller-firefox-hack" style="overflow:hidden;">' ); + /*** Scrollbar hack! Since we can't hide the scrollbar with css ***/ + if ( ts.scroller.isFirefox || ts.scroller.isOldIE ) { + $fixedTbody.wrap( '<div class="scroller-scrollbar-hack" style="overflow:hidden;">' ); } ts.scroller.updateFixed( c, wo, true ); @@ -508,7 +530,7 @@ ts.scroller = { $fixedTbodies = $fixedTbodiesTable.children( 'tbody' ), $fixedHeader = $fixedColumn.find( '.' + tscss.scrollerHeader ).children( 'table' ).children( 'thead' ), // variables - isFirefox = ts.scroller.isFirefox, + tsScroller = ts.scroller, scrollBarWidth = wo.scroller_barSetWidth, fixedColumns = wo.scroller_fixedColumns, // get dimensions @@ -533,8 +555,8 @@ ts.scroller = { }).get(); // set fixed column width - ts.scroller.setWidth( $fixedColumn.add( $fixedColumn.children() ), totalWidth + borderRightWidth * 2 - borderSpacing ); - ts.scroller.setWidth( $fixedColumn.find( 'table' ), totalWidth + borderRightWidth ); + tsScroller.setWidth( $fixedColumn.add( $fixedColumn.children() ), totalWidth + borderRightWidth * 2 - borderSpacing ); + tsScroller.setWidth( $fixedColumn.find( 'table' ), totalWidth + borderRightWidth ); // set fixed column height ( changes with filtering ) $fixedColumn.height( $wrapper.height() ); @@ -565,10 +587,10 @@ ts.scroller = { $adjCol = $( $rows[ rowIndex ].outerHTML ); $adjCol.children( 'td, th' ).slice( fixedColumns ).remove(); // set row height - $adjCol.children().eq( 0 ).height( $rows.eq( rowIndex ).outerHeight() - ( isFirefox ? borderBottomWidth * 2 : 0 ) ); + $adjCol.children().eq( 0 ).height( $rows.eq( rowIndex ).outerHeight() - ( tsScroller.isFirefox ? borderBottomWidth * 2 : 0 ) ); // still need to adjust tbody cell widths ( the previous row may now be filtered ) if ( rowIndex === 0 ) { - ts.scroller.setWidth( $adjCol.children().eq( 0 ), widths[ 0 ] ); + tsScroller.setWidth( $adjCol.children().eq( 0 ), widths[ 0 ] ); } $fb.append( $adjCol ); } @@ -576,7 +598,7 @@ ts.scroller = { // adjust fixed header cell widths $temp = $fixedColumn.find( 'thead' ).children( 'tr.' + tscss.headerRow ); for ( index = 0; index < fixedColumns; index++ ) { - ts.scroller.setWidth( $temp.children( ':eq(' + index + ')' ), widths[ index ] ); + tsScroller.setWidth( $temp.children( ':eq(' + index + ')' ), widths[ index ] ); } // restore tbody @@ -584,8 +606,8 @@ ts.scroller = { } } - /*** STUPID FIREFOX HACK! Since we can't hide the scrollbar with css ***/ - if ( isFirefox ) { + /*** scrollbar HACK! Since we can't hide the scrollbar with css ***/ + if ( tsScroller.isFirefox || tsScroller.isOldIE ) { $fixedTbodiesTable.parent().css({ 'width' : totalWidth + scrollBarWidth + borderRightWidth }); @@ -602,7 +624,7 @@ ts.scroller = { .off( namespace ) .insertBefore( $wrap ) .find( 'thead' ).show().css( 'visibility', 'visible' ) - .children( 'tr.' + tscss.headerRow + ' > *' ).attr( 'tabindex', 0 ) + .children( 'tr.' + tscss.headerRow ).children().attr( 'tabindex', 0 ) .end() .find( '.' + tscss.filterRow ).show().removeClass( tscss.filterRowHide ); $wrap.remove(); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js index 8b1611b..4c75186 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js @@ -4,7 +4,7 @@ */ ;(function ($, window) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; +var ts = $.tablesorter || {}; $.extend(ts.css, { sticky : 'tablesorter-stickyHeader', // stickyHeader diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js index 0220d32..472d34c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js @@ -2,7 +2,7 @@ ;(function ($, window, document) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; +var ts = $.tablesorter || {}; // *** Store data in local storage, with a cookie fallback *** /* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json) if you need it, then include https://github.com/douglascrockford/JSON-js diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js index e0e9643..9d6a095 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js @@ -1,7 +1,7 @@ /*! Widget: uitheme - updated 3/26/2015 (v2.21.3) */ ;(function ($) { 'use strict'; -var ts = $.tablesorter = $.tablesorter || {}; +var ts = $.tablesorter || {}; ts.themes = { 'bootstrap' : { diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css index 57714f3..e139f7f 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css @@ -17,7 +17,8 @@ .tablesorter-blackice th, .tablesorter-blackice thead td { padding: 4px; - font: bold 13px/20px Arial, Sans-serif; + font: 13px/20px Arial, Sans-serif; + font-weight: bold; color: #e5e5e5; text-align: left; text-shadow: 0 1px 0 rgba(0, 0, 0, 0.7); diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css index 8a313ac..487f117 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css @@ -20,7 +20,8 @@ /* header */ .tablesorter-blue th, .tablesorter-blue thead td { - font: bold 12px/18px Arial, Sans-serif; + font: 12px/18px Arial, Sans-serif; + font-weight: bold; color: #000; background-color: #99bfe6; border-collapse: collapse; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index 2be2901..11a3ea7 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -9,7 +9,8 @@ .tablesorter-bootstrap thead td, .tablesorter-bootstrap tfoot th, .tablesorter-bootstrap tfoot td { - font: bold 14px/20px Arial, Sans-serif; + font: 14px/20px Arial, Sans-serif; + font-weight: bold; padding: 4px; margin: 0 0 18px; background-color: #eee; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css index 9715d4d..fe0665b 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css @@ -8,7 +8,8 @@ .tablesorter-bootstrap .tablesorter-header, .tablesorter-bootstrap tfoot th, .tablesorter-bootstrap tfoot td { - font: bold 14px/20px Arial, Sans-serif; + font: 14px/20px Arial, Sans-serif; + font-weight: bold; position: relative; padding: 8px; margin: 0 0 18px; @@ -68,7 +69,7 @@ .tablesorter-bootstrap tbody > .even:hover > td { background-color: #f5f5f5; } -.tablesorter-bootstrap tr.even > td { +.tablesorter-bootstrap tbody > tr.even > td { background-color: #fff; } @@ -87,6 +88,7 @@ caption { /* filter widget */ .tablesorter-bootstrap .tablesorter-filter-row input.tablesorter-filter, .tablesorter-bootstrap .tablesorter-filter-row select.tablesorter-filter { + height: 28px; width: 98%; margin: 0; padding: 4px 6px; @@ -105,7 +107,7 @@ caption { cursor: not-allowed; } .tablesorter-bootstrap .tablesorter-filter-row { - background-color: #eee; + background-color: #ddd; } .tablesorter-bootstrap .tablesorter-filter-row td { background-color: #eee; @@ -119,12 +121,12 @@ caption { transition: line-height 0.1s ease; } /* hidden filter row */ -.tablesorter-bootstrap .tablesorter-filter-row.hideme td { +.tablesorter-bootstrap tr.tablesorter-filter-row.hideme td { padding: 2px; /* change this to modify the thickness of the closed border row */ margin: 0; line-height: 0; } -.tablesorter-bootstrap .tablesorter-filter-row.hideme * { +.tablesorter-bootstrap tr.tablesorter-filter-row.hideme * { height: 1px; min-height: 0; border: 0; @@ -152,4 +154,4 @@ caption { text-align: center; cursor: pointer; background-color: #e6bf99; -} +} \ No newline at end of file diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css index 8a3decf..26504e2 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css @@ -15,7 +15,8 @@ .tablesorter-dark th, .tablesorter-dark thead td { padding: 4px; - font: bold 12px/20px Arial, Sans-serif; + font: 12px/20px Arial, Sans-serif; + font-weight: bold; color: #fff; background-color: #000; border-collapse: collapse; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css index ee32607..67002de 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css @@ -15,7 +15,7 @@ Default Theme /* header */ .tablesorter-default th, .tablesorter-default thead td { - font: bold 12px/18px Arial, Sans-serif; + font-weight: bold; color: #000; background-color: #fff; border-collapse: collapse; From f955e535c0cebf47bed582b16c65eb6640f9a694 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 18 May 2015 20:54:22 +0200 Subject: [PATCH 058/138] * updated tablesorter to latest version (2.22.1) --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 19 ++++++++++--------- .../jquery-tablesorter/jquery.tablesorter.js | 11 ++++++----- .../jquery.tablesorter.widgets.js | 8 ++++---- .../widgets/widget-filter.js | 6 +++--- 8 files changed, 30 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06c473e..f863a0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.17.1 (2015-05-18) + +* Upgrade tablesorter to v2.22.1 + #### v1.17.0 (2015-05-17) * Upgrade tablesorter to v2.22.0 diff --git a/README.md b/README.md index 4ea5514..c73dd83 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.22.0 (5/17/2015), [documentation] +Current tablesorter version: 2.22.1 (5/17/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 0dd405c..8d917f1 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.17.0' + VERSION = '1.17.1' end diff --git a/tablesorter b/tablesorter index 13e3d05..4410ece 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 13e3d0593d16bd36f5e9c3e2366823f5e0be47b6 +Subproject commit 4410ecee0bba8c7d42a1e787ab91efac3ddb7c7b diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index eff94f0..1d2b26d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 05-17-2015 (v2.22.0)*/ +/*! tablesorter (FORK) - updated 05-18-2015 (v2.22.1)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.22.0 *//* +/*! TableSorter (FORK) v2.22.1 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -44,7 +44,7 @@ var ts = this; - ts.version = '2.22.0'; + ts.version = '2.22.1'; ts.parsers = []; ts.widgets = []; @@ -1399,7 +1399,7 @@ .find(c.selectorSort).add( $headers.filter(c.selectorSort) ) .unbind(t) .bind(t, function(e, external) { - var cell, + var cell, temp, $target = $(e.target), // wrap event type in spaces, so the match doesn't trigger on inner words type = ' ' + e.type + ' '; @@ -1416,8 +1416,9 @@ // set timer on mousedown if ( type.match(' ' + c.pointerDown + ' ') ) { downTarget = e.target; - // needed or jQuery v1.2.6 throws an error - e.preventDefault(); + // needed or jQuery v1.3.2 or older throws an "Uncaught TypeError: handler.apply is not a function" error + temp = $target.jquery.split( '.' ); + if ( temp[0] === '1' && temp[1] < 4 ) { e.preventDefault(); } return; } downTarget = null; @@ -2544,7 +2545,7 @@ ts.addWidget({ })(jQuery); -/*! Widget: filter - updated 5/17/2015 (v2.22.0) *//* +/*! Widget: filter - updated 5/17/2015 (v2.22.1) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -2793,7 +2794,7 @@ ts.filter = { var index = data.index, parsed = data.parsed[ index ], txt = data.iFilter.replace( ts.filter.regex.orReplace, '|' ), - query = ts.filter.parseFilter( c, txt, index, parsed ) || ''; + query = '' + ( ts.filter.parseFilter( c, txt, index, parsed ) || '' ); // look for an exact match with the 'or' unless the 'filter-match' class is found if ( !c.$headerIndexed[ index ].hasClass( 'filter-match' ) && /\|/.test( query ) ) { // show all results while using filter match. Fixes #727 @@ -3611,7 +3612,7 @@ ts.filter = { data.parsed = c.$headers.map( function( columnIndex ) { return c.parsers && c.parsers[ columnIndex ] && // force parsing if parser type is numeric - ( c.parsers[ columnIndex ].parsed || c.parsers[ columnIndex ].type === 'numeric' ) || + c.parsers[ columnIndex ].parsed || // getData won't return 'parsed' if other 'filter-' class names exist // ( e.g. <th class="filter-select filter-parsed"> ) ts.getData && ts.getData( c.$headerIndexed[ columnIndex ], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 3001a40..fc58f75 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.22.0 *//* +/*! TableSorter (FORK) v2.22.1 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -26,7 +26,7 @@ var ts = this; - ts.version = '2.22.0'; + ts.version = '2.22.1'; ts.parsers = []; ts.widgets = []; @@ -1381,7 +1381,7 @@ .find(c.selectorSort).add( $headers.filter(c.selectorSort) ) .unbind(t) .bind(t, function(e, external) { - var cell, + var cell, temp, $target = $(e.target), // wrap event type in spaces, so the match doesn't trigger on inner words type = ' ' + e.type + ' '; @@ -1398,8 +1398,9 @@ // set timer on mousedown if ( type.match(' ' + c.pointerDown + ' ') ) { downTarget = e.target; - // needed or jQuery v1.2.6 throws an error - e.preventDefault(); + // needed or jQuery v1.3.2 or older throws an "Uncaught TypeError: handler.apply is not a function" error + temp = $target.jquery.split( '.' ); + if ( temp[0] === '1' && temp[1] < 4 ) { e.preventDefault(); } return; } downTarget = null; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index abc55ea..cb6d950 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 05-17-2015 (v2.22.0)*/ +/*! tablesorter (FORK) - updated 05-18-2015 (v2.22.1)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -371,7 +371,7 @@ ts.addWidget({ })(jQuery); -/*! Widget: filter - updated 5/17/2015 (v2.22.0) *//* +/*! Widget: filter - updated 5/17/2015 (v2.22.1) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -620,7 +620,7 @@ ts.filter = { var index = data.index, parsed = data.parsed[ index ], txt = data.iFilter.replace( ts.filter.regex.orReplace, '|' ), - query = ts.filter.parseFilter( c, txt, index, parsed ) || ''; + query = '' + ( ts.filter.parseFilter( c, txt, index, parsed ) || '' ); // look for an exact match with the 'or' unless the 'filter-match' class is found if ( !c.$headerIndexed[ index ].hasClass( 'filter-match' ) && /\|/.test( query ) ) { // show all results while using filter match. Fixes #727 @@ -1438,7 +1438,7 @@ ts.filter = { data.parsed = c.$headers.map( function( columnIndex ) { return c.parsers && c.parsers[ columnIndex ] && // force parsing if parser type is numeric - ( c.parsers[ columnIndex ].parsed || c.parsers[ columnIndex ].type === 'numeric' ) || + c.parsers[ columnIndex ].parsed || // getData won't return 'parsed' if other 'filter-' class names exist // ( e.g. <th class="filter-select filter-parsed"> ) ts.getData && ts.getData( c.$headerIndexed[ columnIndex ], diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 1472ac3..a7584c0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 5/17/2015 (v2.22.0) *//* +/*! Widget: filter - updated 5/17/2015 (v2.22.1) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -247,7 +247,7 @@ ts.filter = { var index = data.index, parsed = data.parsed[ index ], txt = data.iFilter.replace( ts.filter.regex.orReplace, '|' ), - query = ts.filter.parseFilter( c, txt, index, parsed ) || ''; + query = '' + ( ts.filter.parseFilter( c, txt, index, parsed ) || '' ); // look for an exact match with the 'or' unless the 'filter-match' class is found if ( !c.$headerIndexed[ index ].hasClass( 'filter-match' ) && /\|/.test( query ) ) { // show all results while using filter match. Fixes #727 @@ -1065,7 +1065,7 @@ ts.filter = { data.parsed = c.$headers.map( function( columnIndex ) { return c.parsers && c.parsers[ columnIndex ] && // force parsing if parser type is numeric - ( c.parsers[ columnIndex ].parsed || c.parsers[ columnIndex ].type === 'numeric' ) || + c.parsers[ columnIndex ].parsed || // getData won't return 'parsed' if other 'filter-' class names exist // ( e.g. <th class="filter-select filter-parsed"> ) ts.getData && ts.getData( c.$headerIndexed[ columnIndex ], From 4f533089cb05b08a13b4600bfc9f671ed5ff21c1 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Wed, 1 Jul 2015 22:29:58 +0200 Subject: [PATCH 059/138] * updated tablesorter to latest version (2.22.3) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 8 +- .../jquery.tablesorter.combined.js | 581 ++++++++------ .../jquery-tablesorter/jquery.tablesorter.js | 78 +- .../jquery.tablesorter.widgets.js | 505 +++++++----- .../parsers/parser-input-select.js | 3 +- .../parsers/parser-metric.js | 111 +-- .../widgets/widget-alignChar.js | 2 +- .../widgets/widget-columnSelector.js | 29 +- .../widgets/widget-editable.js | 15 +- .../widgets/widget-filter.js | 185 +++-- .../widgets/widget-output.js | 161 ++-- .../widgets/widget-pager.js | 5 +- .../widgets/widget-repeatheaders.js | 2 +- .../widgets/widget-resizable.js | 179 +++-- .../widgets/widget-scroller.js | 752 ++++++++++++------ .../widgets/widget-sortTbodies.js | 228 ++++++ .../widgets/widget-stickyHeaders.js | 135 ++-- 21 files changed, 1960 insertions(+), 1029 deletions(-) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js diff --git a/CHANGELOG.md b/CHANGELOG.md index f863a0e..7a19507 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.17.2 (2015-07-01) + +* Upgrade tablesorter to v2.22.3 + #### v1.17.1 (2015-05-18) * Upgrade tablesorter to v2.22.1 diff --git a/README.md b/README.md index c73dd83..7699bd6 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.22.1 (5/17/2015), [documentation] +Current tablesorter version: 2.22.3 (6/30/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 8d917f1..4ac2c35 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.17.1' + VERSION = '1.17.2' end diff --git a/tablesorter b/tablesorter index 4410ece..0e611bc 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 4410ecee0bba8c7d42a1e787ab91efac3ddb7c7b +Subproject commit 0e611bc6a89597795e16d2097949c7adf2b29e89 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 6fb251f..ed5c9aa 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -408,7 +408,7 @@ c.totalRows = p.totalRows = result.total; c.filteredRows = p.filteredRows = typeof result.filteredRows !== 'undefined' ? result.filteredRows : result.total; th = result.headers; - d = result.rows; + d = result.rows || []; } else { // allow [ total, rows, headers ] or [ rows, total, headers ] t = isNaN(result[0]) && !isNaN(result[1]); @@ -417,7 +417,8 @@ p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count; // can't set filtered rows when returning an array c.totalRows = c.filteredRows = p.filteredRows = p.totalRows; - d = p.totalRows === 0 ? [""] : result[t ? 0 : 1] || []; // row data + // set row data to empty array if nothing found - see http://stackoverflow.com/q/30875583/145346 + d = p.totalRows === 0 ? [] : result[t ? 0 : 1] || []; // row data th = result[2]; // headers } l = d && d.length; @@ -1046,6 +1047,9 @@ updatePageDisplay(table, p, false); } } + + // make the hasWidget function think that the pager widget is being used + c.widgetInit.pager = true; }); }; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 1d2b26d..1cfb15a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 05-18-2015 (v2.22.1)*/ +/*! tablesorter (FORK) - updated 06-30-2015 (v2.22.2)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.22.1 *//* +/*! TableSorter (FORK) v2.22.3 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -44,7 +44,7 @@ var ts = this; - ts.version = '2.22.1'; + ts.version = '2.22.3'; ts.parsers = []; ts.widgets = []; @@ -209,10 +209,10 @@ if (typeof(t) === 'string') { // check data-attribute first when set to 'basic'; don't use node.innerText - it's really slow! // http://www.kellegous.com/j/2013/02/27/innertext-vs-textcontent/ - return $.trim( - ( t === 'basic' ? $node.attr(c.textAttribute) || node.textContent : node.textContent ) || - $node.text() - ); + if ( t === 'basic' && typeof ( te = $node.attr(c.textAttribute) ) !== 'undefined' ) { + return $.trim( te ); + } + return $.trim( node.textContent || $node.text() ); } else { if (typeof(t) === 'function') { return $.trim( t($node[0], c.table, cellIndex) ); @@ -224,9 +224,8 @@ return $.trim( $node[0].textContent || $node.text() ); }; - function detectParserForColumn(table, rows, rowIndex, cellIndex) { + function detectParserForColumn(c, rows, rowIndex, cellIndex) { var cur, $node, - c = table.config, i = ts.parsers.length, node = false, nodeValue = '', @@ -237,7 +236,7 @@ node = rows[rowIndex].cells[cellIndex]; nodeValue = ts.getElementText(c, node, cellIndex); $node = $(node); - if (table.config.debug) { + if (c.debug) { log('Checking if value was empty on row ' + rowIndex + ', column: ' + cellIndex + ': "' + nodeValue + '"'); } } else { @@ -247,7 +246,7 @@ while (--i >= 0) { cur = ts.parsers[i]; // ignore the default text parser because it will always be true - if (cur && cur.id !== 'text' && cur.is && cur.is(nodeValue, table, node, $node)) { + if (cur && cur.id !== 'text' && cur.is && cur.is(nodeValue, c.table, node, $node)) { return cur; } } @@ -256,7 +255,7 @@ } // centralized function to extract/parse cell contents - function getParsedText( c, cell, colIndex, txt ) { + ts.getParsedText = function( c, cell, colIndex, txt ) { if ( typeof txt === 'undefined' ) { txt = ts.getElementText( c, cell, colIndex ); } @@ -279,16 +278,17 @@ } } return val; - } + }; - function buildParserCache(table) { - var c = table.config, - // update table bodies in case we start with an empty table - tb = c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'), - rows, list, l, i, h, ch, np, p, e, time, + function buildParserCache( c, $tbodies ) { + var rows, list, l, i, h, ch, np, p, e, time, tb, len, + table = c.table, j = 0, - parsersDebug = '', - len = tb.length; + parsersDebug = ''; + // update table bodies in case we start with an empty table + c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'); + tb = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies; + len = tb.length; if ( len === 0) { return c.debug ? log('Warning: *Empty table!* Not building a parser cache') : ''; } else if (c.debug) { @@ -323,7 +323,7 @@ e = false; } if (!p) { - p = detectParserForColumn(table, rows, -1, i); + p = detectParserForColumn(c, rows, -1, i); } if (c.debug) { parsersDebug += 'column:' + i + '; extractor:' + e.id + '; parser:' + p.id + '; string:' + c.strings[i] + '; empty: ' + c.empties[i] + '\n'; @@ -343,12 +343,14 @@ } /* utils */ - function buildCache(table) { - var cc, t, v, i, j, k, $row, cols, cacheTime, + function buildCache(table, $tbodies) { + var cc, t, v, i, j, k, $tb, $row, cols, cacheTime, totalRows, rowData, prevRowData, colMax, c = table.config, - $tb = c.$tbodies, parsers = c.parsers; + // update tbody variable + c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'); + $tb = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies, c.cache = {}; c.totalRows = 0; // if no parsers found, return - it's an empty table. @@ -396,7 +398,7 @@ prevRowData.child[ t ] = []; // child row content does not account for colspans/rowspans; so indexing may be off for ( j = 0; j < c.columns; j++ ) { - prevRowData.child[ t ][ j ] = getParsedText( c, v[ j ], j ); + prevRowData.child[ t ][ j ] = ts.getParsedText( c, v[ j ], j ); } // go to the next for loop continue; @@ -412,7 +414,7 @@ } t = ts.getElementText( c, $row[ 0 ].cells[j], j ); rowData.raw.push( t ); // save original row text - v = getParsedText( c, $row[ 0 ].cells[ j ], j, t ); + v = ts.getParsedText( c, $row[ 0 ].cells[ j ], j, t ); cols.push( v ); if ( ( parsers[ j ].type || '' ).toLowerCase() === 'numeric' ) { // determine column max value (ignore sign) @@ -565,7 +567,7 @@ // remove rows/elements before update c.$table.find(c.selectorRemove).remove(); // rebuild parsers - buildParserCache(table); + buildParserCache(c); // rebuild the cache map buildCache(table); checkResort(c, resort, callback); @@ -855,7 +857,7 @@ num = (c.strings[col]) ? c.string[c.strings[col]] || 0 : 0; } // fall back to built-in numeric sort - // var sort = $.tablesorter['sort' + s](table, a[c], b[c], c, colMax[c], dir); + // var sort = $.tablesorter['sort' + s]( a[c], b[c], dir, colMax[c], table); sort = c.numberSorter ? c.numberSorter(a[col], b[col], dir, colMax[col], table) : ts[ 'sortNumeric' + (dir ? 'Asc' : 'Desc') ](a[col], b[col], num, colMax[col], col, table); } else { @@ -968,7 +970,7 @@ row = $tb.eq( tbdy ).find( 'tr' ).index( $row ); cache = tbcache.normalized[ row ]; icell = $cell.index(); - t = getParsedText( c, cell, icell ); + t = ts.getParsedText( c, cell, icell ); cache[ icell ] = t; cache[ c.columns ].$row = $row; if ( (c.parsers[icell].type || '').toLowerCase() === 'numeric' ) { @@ -1003,7 +1005,7 @@ tbdy = c.$tbodies.index( $row.parents('tbody').filter(':first') ); // fixes adding rows to an empty table - see issue #179 if (!(c.parsers && c.parsers.length)) { - buildParserCache(table); + buildParserCache(c); } // add each row for (i = 0; i < rows; i++) { @@ -1016,7 +1018,7 @@ }; // add each cell for (j = 0; j < l; j++) { - cells[j] = getParsedText( c, $row[i].cells[j], j ); + cells[j] = ts.getParsedText( c, $row[i].cells[j], j ); if ((c.parsers[j].type || '').toLowerCase() === 'numeric') { // update column max value (ignore sign) c.cache[tbdy].colMax[j] = Math.max(Math.abs(cells[j]) || 0, c.cache[tbdy].colMax[j] || 0); @@ -1061,13 +1063,14 @@ callback(table); } }) - .bind('updateCache' + c.namespace, function(e, callback){ + // $tbodies variable is used by the tbody sorting widget + .bind('updateCache' + c.namespace, function(e, callback, $tbodies){ // rebuild parsers if (!(c.parsers && c.parsers.length)) { - buildParserCache(table); + buildParserCache(c, $tbodies); } // rebuild the cache map - buildCache(table); + buildCache(table, $tbodies); if ($.isFunction(callback)) { callback(table); } @@ -1187,7 +1190,7 @@ // add widget options before parsing (e.g. grouping widget has parser settings) ts.applyWidgetOptions(table, c); // try to auto detect column type, and store in tables config - buildParserCache(table); + buildParserCache(c); // start total row count at zero c.totalRows = 0; // build the cache for the tbody cells @@ -1413,10 +1416,11 @@ } // ignore mouseup if mousedown wasn't on the same target if ( type.match(' ' + c.pointerUp + ' ') && downTarget !== e.target && external !== true ) { return; } - // set timer on mousedown + // set target on mousedown if ( type.match(' ' + c.pointerDown + ' ') ) { downTarget = e.target; - // needed or jQuery v1.3.2 or older throws an "Uncaught TypeError: handler.apply is not a function" error + // preventDefault needed or jQuery v1.3.2 and older throws an + // "Uncaught TypeError: handler.apply is not a function" error temp = $target.jquery.split( '.' ); if ( temp[0] === '1' && temp[1] < 4 ) { e.preventDefault(); } return; @@ -2155,7 +2159,7 @@ format: function(table, c, wo) { var $tv, $tr, row, even, time, k, i, len, child = new RegExp(c.cssChildRow, 'i'), - b = c.$tbodies.add( $( c.namespace + '_extra_table' ).children( 'tbody' ) ); + b = c.$tbodies.add( $( c.namespace + '_extra_table' ).children( 'tbody:not(.' + c.cssInfoBlock + ')' ) ); if (c.debug) { time = new Date(); } @@ -2650,6 +2654,66 @@ ts.filter = { // data.index = column index; table = table element ( DOM ) // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class ) types: { + or : function( c, data, vars ) { + if ( /\|/.test( data.iFilter ) || ts.filter.regex.orSplit.test( data.filter ) ) { + var indx, filterMatched, txt, query, regex, + // duplicate data but split filter + data2 = $.extend( {}, data ), + index = data.index, + parsed = data.parsed[ index ], + filter = data.filter.split( ts.filter.regex.orSplit ), + iFilter = data.iFilter.split( ts.filter.regex.orSplit ), + len = filter.length; + for ( indx = 0; indx < len; indx++ ) { + data2.nestedFilters = true; + data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); + data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); + query = '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')'; + regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); + // filterMatched = data2.filter === '' && indx > 0 ? true + // look for an exact match with the 'or' unless the 'filter-match' class is found + filterMatched = regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ); + if ( filterMatched ) { + return filterMatched; + } + } + // may be null from processing types + return filterMatched || false; + } + return null; + }, + // Look for an AND or && operator ( logical and ) + and : function( c, data, vars ) { + if ( ts.filter.regex.andTest.test( data.filter ) ) { + var indx, filterMatched, result, txt, query, regex, + // duplicate data but split filter + data2 = $.extend( {}, data ), + index = data.index, + parsed = data.parsed[ index ], + filter = data.filter.split( ts.filter.regex.andSplit ), + iFilter = data.iFilter.split( ts.filter.regex.andSplit ), + len = filter.length; + for ( indx = 0; indx < len; indx++ ) { + data2.nestedFilters = true; + data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); + data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); + query = ( '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' ) + // replace wild cards since /(a*)/i will match anything + .replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ); + regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); + // look for an exact match with the 'and' unless the 'filter-match' class is found + result = ( regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ) ); + if ( indx === 0 ) { + filterMatched = result; + } else { + filterMatched = filterMatched && result; + } + } + // may be null from processing types + return filterMatched || false; + } + return null; + }, // Look for regex regex: function( c, data ) { if ( ts.filter.regex.regex.test( data.filter ) ) { @@ -2737,23 +2801,6 @@ ts.filter = { } return null; }, - // Look for an AND or && operator ( logical and ) - and : function( c, data ) { - if ( ts.filter.regex.andTest.test( data.filter ) ) { - var index = data.index, - parsed = data.parsed[index], - query = data.iFilter.split( ts.filter.regex.andSplit ), - result = data.iExact.search( $.trim( ts.filter.parseFilter( c, query[0], index, parsed ) ) ) >= 0, - indx = query.length - 1; - while ( result && indx ) { - result = result && - data.iExact.search( $.trim( ts.filter.parseFilter( c, query[indx], index, parsed ) ) ) >= 0; - indx--; - } - return result; - } - return null; - }, // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu! range : function( c, data ) { if ( ts.filter.regex.toTest.test( data.iFilter ) ) { @@ -2790,24 +2837,20 @@ ts.filter = { }, // Look for wild card: ? = single, * = multiple, or | = logical OR wild : function( c, data ) { - if ( /[\?\*\|]/.test( data.iFilter ) || ts.filter.regex.orReplace.test( data.filter ) ) { + if ( /[\?\*\|]/.test( data.iFilter ) ) { var index = data.index, parsed = data.parsed[ index ], - txt = data.iFilter.replace( ts.filter.regex.orReplace, '|' ), - query = '' + ( ts.filter.parseFilter( c, txt, index, parsed ) || '' ); + query = '' + ( ts.filter.parseFilter( c, data.iFilter, index, parsed ) || '' ); // look for an exact match with the 'or' unless the 'filter-match' class is found - if ( !c.$headerIndexed[ index ].hasClass( 'filter-match' ) && /\|/.test( query ) ) { - // show all results while using filter match. Fixes #727 - if ( query[ query.length - 1 ] === '|' ) { - query += '*'; - } - query = data.anyMatch && $.isArray( data.rowArray ) ? - '(' + query + ')' : - '^(' + query + ')$'; + if ( !/\?\*/.test( query ) && data.nestedFilters ) { + query = data.isMatch ? query : '^(' + query + ')$'; } // parsing the filter may not work properly when using wildcards =/ - return new RegExp( query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ) ) - .test( data.iExact ); + return new RegExp( + query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ), + c.widgetOptions.filter_ignoreCase ? 'i' : '' + ) + .test( data.exact ); } return null; }, @@ -2861,7 +2904,7 @@ ts.filter = { toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)' ,'gi' ), andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), - orReplace : new RegExp( '\\s+(' + ts.language.or + ')\\s+', 'gi' ), + orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ), iQuery : new RegExp( val, 'i' ), igQuery : new RegExp( val, 'ig' ) }); @@ -3081,7 +3124,6 @@ ts.filter = { } } }, - setDefaults: function( table, c, wo ) { var isArray, saved, indx, col, $filters, // get current ( default ) filters @@ -3308,14 +3350,13 @@ ts.filter = { } }, hideFilters: function( table, c ) { - var $filterRow, $filterRow2, timer; - $( table ) + var timer; + c.$table .find( '.' + tscss.filterRow ) - .addClass( tscss.filterRowHide ) .bind( 'mouseenter mouseleave', function( e ) { // save event object - http://bugs.jquery.com/ticket/12140 - var event = e; - $filterRow = $( this ); + var event = e, + $filterRow = $( this ); clearTimeout( timer ); timer = setTimeout( function() { if ( /enter|over/.test( event.type ) ) { @@ -3333,13 +3374,14 @@ ts.filter = { }, 200 ); }) .find( 'input, select' ).bind( 'focus blur', function( e ) { - $filterRow2 = $( this ).closest( 'tr' ); + var event = e, + $row = $( this ).closest( 'tr' ); clearTimeout( timer ); - var event = e; timer = setTimeout( function() { + clearTimeout( timer ); // don't hide row if any filter has a value if ( ts.getFilters( c.$table ).join( '' ) === '' ) { - $filterRow2.toggleClass( tscss.filterRowHide, event.type === 'focus' ); + $row.toggleClass( tscss.filterRowHide, event.type !== 'focus' ); } }, 200 ); }); @@ -3373,7 +3415,7 @@ ts.filter = { return $( b ).attr( 'data-lastSearchTime' ) - $( a ).attr( 'data-lastSearchTime' ); }); } - return $(); + return $input || $(); }, multipleColumns: function( c, $input ) { // look for multiple columns '1-3,4-6,8' in data-column @@ -3426,8 +3468,22 @@ ts.filter = { } return columns; }, + processTypes: function( c, data, vars ) { + var ffxn, + filterMatched = null, + matches = null; + for ( ffxn in ts.filter.types ) { + if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) { + matches = ts.filter.types[ffxn]( c, data, vars ); + if ( matches !== null ) { + filterMatched = matches; + } + } + } + return filterMatched; + }, processRow: function( c, data, vars ) { - var $cell, columnIndex, hasSelect, matches, result, val, filterMatched, excludeMatch, + var columnIndex, hasSelect, result, val, filterMatched, fxn, ffxn, txt, regex = ts.filter.regex, wo = c.widgetOptions, @@ -3438,6 +3494,7 @@ ts.filter = { // look for multiple columns '1-3,4-6,8' columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); data.anyMatch = true; + data.isMatch = true; data.rowArray = data.$cells.map( function( i ) { if ( $.inArray( i, columnIndex ) > -1 ) { if ( data.parsed[ i ] ) { @@ -3457,16 +3514,10 @@ ts.filter = { data.exact = data.rowArray.join( ' ' ); data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' ); - filterMatched = null; - matches = null; - for ( ffxn in ts.filter.types ) { - if ( $.inArray( ffxn, vars.noAnyMatch ) < 0 && matches === null ) { - matches = ts.filter.types[ffxn]( c, data ); - if ( matches !== null ) { - filterMatched = matches; - } - } - } + + vars.excludeMatch = vars.noAnyMatch; + filterMatched = ts.filter.processTypes( c, data, vars ); + if ( filterMatched !== null ) { showRow = filterMatched; } else { @@ -3493,7 +3544,7 @@ ts.filter = { data.index = columnIndex; // filter types to exclude, per column - excludeMatch = vars.excludeFilter[ columnIndex ]; + vars.excludeMatch = vars.excludeFilter[ columnIndex ]; // ignore if filter is empty or disabled if ( data.filter ) { @@ -3507,6 +3558,9 @@ ts.filter = { } data.iExact = !regex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; + + data.isMatch = c.$headerIndexed[ data.index ].hasClass( 'filter-match' ); + result = showRow; // if showRow is true, show that row // in case select filter option has a different value vs text 'a - z|A through Z' @@ -3531,13 +3585,12 @@ ts.filter = { // data.filter = case sensitive data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter; fxn = vars.functions[ columnIndex ]; - $cell = c.$headerIndexed[ columnIndex ]; - hasSelect = $cell.hasClass( 'filter-select' ); + hasSelect = c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' ); filterMatched = null; if ( fxn || ( hasSelect && val ) ) { if ( fxn === true || hasSelect ) { // default selector uses exact match unless 'filter-match' class is found - filterMatched = $cell.hasClass( 'filter-match' ) ? + filterMatched = data.isMatch ? data.iExact.search( data.iFilter ) >= 0 : data.filter === data.exact; } else if ( typeof fxn === 'function' ) { @@ -3554,15 +3607,7 @@ ts.filter = { if ( filterMatched === null ) { // cycle through the different filters // filters return a boolean or null if nothing matches - matches = null; - for ( ffxn in ts.filter.types ) { - if ( $.inArray( ffxn, excludeMatch ) < 0 && matches === null ) { - matches = ts.filter.types[ ffxn ]( c, data ); - if ( matches !== null ) { - filterMatched = matches; - } - } - } + filterMatched = ts.filter.processTypes( c, data, vars ); if ( filterMatched !== null ) { result = filterMatched; // Look for match, and add child row data for matching @@ -3596,7 +3641,7 @@ ts.filter = { anyMatch: false, filters: filters, // regex filter type cache - filter_regexCache : [], + filter_regexCache : [] }, vars = { // anyMatch really screws up with these types of filters @@ -3817,7 +3862,7 @@ ts.filter = { }, getOptionSource: function( table, column, onlyAvail ) { table = $( table )[0]; - var cts, indx, len, + var cts, txt, indx, len, c = table.config, wo = c.widgetOptions, parsed = [], @@ -3862,11 +3907,13 @@ ts.filter = { len = arry.length; // parse select option values for ( indx = 0; indx < len; indx++ ) { + txt = arry[ indx ]; // parse array data using set column parser; this DOES NOT pass the original // table cell to the parser format function parsed.push({ - t : arry[ indx ], - p : c.parsers && c.parsers[ column ].format( arry[ indx ], table, [], column ) + t : txt, + // check parser length - fixes #934 + p : c.parsers && c.parsers.length && c.parsers[ column ].format( txt, table, [], column ) || txt }); } @@ -4056,8 +4103,8 @@ ts.getFilters = function( table, getRaw, setFilters, skipFirst ) { $column = ts.filter.getLatestSearch( $column ); if ( $.isArray( setFilters ) ) { // skip first ( latest input ) to maintain cursor position while typing - if ( skipFirst ) { - $column.slice( 1 ); + if ( skipFirst && $column.length > 1 ) { + $column = $column.slice( 1 ); } if ( i === c.columns ) { // prevent data-column='all' from filling data-column='0,1' ( etc ) @@ -4129,32 +4176,34 @@ $.extend(ts.css, { // Add a resize event to table headers ts.addHeaderResizeEvent = function(table, disable, settings) { table = $(table)[0]; // make sure we're using a dom element - var headers, - defaults = { + if ( !table.config ) { return; } + var defaults = { timer : 250 }, options = $.extend({}, defaults, settings), c = table.config, wo = c.widgetOptions, - checkSizes = function(triggerEvent) { + checkSizes = function( triggerEvent ) { + var index, headers, $header, sizes, width, height, + len = c.$headers.length; wo.resize_flag = true; headers = []; - c.$headers.each(function() { - var $header = $(this), - sizes = $header.data('savedSizes') || [0,0], // fixes #394 - width = this.offsetWidth, - height = this.offsetHeight; - if (width !== sizes[0] || height !== sizes[1]) { - $header.data('savedSizes', [ width, height ]); - headers.push(this); + for ( index = 0; index < len; index++ ) { + $header = c.$headers.eq( index ); + sizes = $header.data( 'savedSizes' ) || [ 0,0 ]; // fixes #394 + width = $header[0].offsetWidth; + height = $header[0].offsetHeight; + if ( width !== sizes[0] || height !== sizes[1] ) { + $header.data( 'savedSizes', [ width, height ] ); + headers.push( $header[0] ); } - }); - if (headers.length && triggerEvent !== false) { - c.$table.trigger('resize', [ headers ]); + } + if ( headers.length && triggerEvent !== false ) { + c.$table.trigger( 'resize', [ headers ] ); } wo.resize_flag = false; }; - checkSizes(false); + checkSizes( false ); clearInterval(wo.resize_timer); if (disable) { wo.resize_flag = false; @@ -4190,7 +4239,8 @@ ts.addWidget({ if ( c.$table.hasClass('hasStickyHeaders') || ($.inArray('filter', c.widgets) >= 0 && !c.$table.hasClass('hasFilters')) ) { return; } - var $table = c.$table, + var index, len, $t, + $table = c.$table, // add position: relative to attach element, hopefully it won't cause trouble. $attach = $(wo.stickyHeaders_attachTo), namespace = c.namespace + 'stickyheaders ', @@ -4225,17 +4275,19 @@ ts.addWidget({ laststate = '', spacing = 0, setWidth = function($orig, $clone){ - $orig.filter(':visible').each(function(i) { - var width, border, - $cell = $clone.filter(':visible').eq(i), - $this = $(this); + var index, width, border, $cell, $this, + $cells = $orig.filter(':visible'), + len = $cells.length; + for ( index = 0; index < len; index++ ) { + $cell = $clone.filter(':visible').eq(index); + $this = $cells.eq(index); // code from https://github.com/jmosbech/StickyTableHeaders if ($this.css('box-sizing') === 'border-box') { width = $this.outerWidth(); } else { if ($cell.css('border-collapse') === 'collapse') { if (window.getComputedStyle) { - width = parseFloat( window.getComputedStyle(this, null).width ); + width = parseFloat( window.getComputedStyle($this[0], null).width ); } else { // ie8 only border = parseFloat( $this.css('border-width') ); @@ -4246,10 +4298,11 @@ ts.addWidget({ } } $cell.css({ + 'width': width, 'min-width': width, 'max-width': width }); - }); + } }, resizeHeader = function() { stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0; @@ -4261,6 +4314,39 @@ ts.addWidget({ }); setWidth( $table, $stickyTable ); setWidth( $header, $stickyCells ); + }, + scrollSticky = function( resizing ) { + if (!$table.is(':visible')) { return; } // fixes #278 + // Detect nested tables - fixes #724 + nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; + var offset = $table.offset(), + yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 + xWindow = $.isWindow( $xScroll[0] ), + // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, + scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, + tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)), + isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', + cssSettings = { visibility : isVisible }; + + if ($attach.length) { + cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); + } + if (xWindow) { + // adjust when scrolling horizontally - fixes issue #143 + cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing; + } + if ($nestedSticky.length) { + cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; + } + $stickyWrap + .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) + .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) + .css(cssSettings); + if (isVisible !== laststate || resizing) { + // make sure the column widths match + resizeHeader(); + laststate = isVisible; + } }; // only add a position relative if a position isn't already defined if ($attach.length && !$attach.css('position')) { @@ -4292,48 +4378,26 @@ ts.addWidget({ // onRenderHeader is defined, we need to do something about it (fixes #641) if (c.onRenderHeader) { - $stickyThead.children('tr').children().each(function(index){ + $t = $stickyThead.children('tr').children(); + len = $t.length; + for ( index = 0; index < len; index++ ) { // send second parameter - c.onRenderHeader.apply( $(this), [ index, c, $stickyTable ] ); - }); + c.onRenderHeader.apply( $t.eq( index ), [ index, c, $stickyTable ] ); + } } // make it sticky! $xScroll.add($yScroll) - .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') ) - .bind('scroll resize '.split(' ').join( namespace ), function(event) { - if (!$table.is(':visible')) { return; } // fixes #278 - // Detect nested tables - fixes #724 - nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; - var offset = $table.offset(), - yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 - xWindow = $.isWindow( $xScroll[0] ), - // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, - scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, - tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)), - isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', - cssSettings = { visibility : isVisible }; - - if ($attach.length) { - cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); - } - if (xWindow) { - // adjust when scrolling horizontally - fixes issue #143 - cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing; - } - if ($nestedSticky.length) { - cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; - } - $stickyWrap - .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) - .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) - .css(cssSettings); - if (isVisible !== laststate || event.type === 'resize') { - // make sure the column widths match - resizeHeader(); - laststate = isVisible; - } - }); + .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') ) + .bind('scroll resize '.split(' ').join( namespace ), function( event ) { + scrollSticky( event.type === 'resize' ); + }); + c.$table + .unbind('stickyHeadersUpdate' + namespace) + .bind('stickyHeadersUpdate' + namespace, function(){ + scrollSticky( true ); + }); + if (wo.stickyHeaders_addResizeEvent) { ts.addHeaderResizeEvent(table); } @@ -4369,7 +4433,7 @@ ts.addWidget({ var namespace = c.namespace + 'stickyheaders '; c.$table .removeClass('hasStickyHeaders') - .unbind( ('pagerComplete filterEnd '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) + .unbind( ('pagerComplete filterEnd stickyHeadersUpdate '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) .next('.' + ts.css.stickyWrap).remove(); if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table $(window) @@ -4383,7 +4447,8 @@ ts.addWidget({ })(jQuery, window); -/*! Widget: resizable - updated 5/17/2015 (v2.22.0) */ +/*! Widget: resizable - updated 6/26/2015 (v2.22.2) */ +/*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; var ts = $.tablesorter || {}; @@ -4402,8 +4467,8 @@ $(function(){ '-khtml-user-select: none; -webkit-user-select: none; user-select: none; }' + '.' + ts.css.resizableContainer + ' { position: relative; height: 1px; }' + // make handle z-index > than stickyHeader z-index, so the handle stays above sticky header - '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px; top: 1px;' + - 'cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' + + '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px;' + + 'top: 1px; cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' + '</style>'; $(s).appendTo('body'); }); @@ -4412,34 +4477,69 @@ ts.resizable = { init : function( c, wo ) { if ( c.$table.hasClass( 'hasResizable' ) ) { return; } c.$table.addClass( 'hasResizable' ); - ts.resizableReset( c.table, true ); // set default widths + + var noResize, $header, column, storedSizes, tmp, + $table = c.$table, + $parent = $table.parent(), + marginTop = parseInt( $table.css( 'margin-top' ), 10 ), // internal variables - wo.resizable_ = { - $wrap : c.$table.parent(), + vars = wo.resizable_ = { + useStorage : ts.storage && wo.resizable !== false, + $wrap : $parent, mouseXPosition : 0, $target : null, $next : null, - overflow : c.$table.parent().css('overflow') === 'auto', - fullWidth : Math.abs(c.$table.parent().width() - c.$table.width()) < 20, + overflow : $parent.css('overflow') === 'auto' || + $parent.css('overflow') === 'scroll' || + $parent.css('overflow-x') === 'auto' || + $parent.css('overflow-x') === 'scroll', storedSizes : [] }; - var noResize, $header, column, storedSizes, - marginTop = parseInt( c.$table.css( 'margin-top' ), 10 ); + // set default widths + ts.resizableReset( c.table, true ); + + // now get measurements! + vars.tableWidth = $table.width(); + // attempt to autodetect + vars.fullWidth = Math.abs( $parent.width() - vars.tableWidth ) < 20; + + /* + // Hacky method to determine if table width is set to "auto" + // http://stackoverflow.com/a/20892048/145346 + if ( !vars.fullWidth ) { + tmp = $table.width(); + $header = $table.wrap('<span>').parent(); // temp variable + storedSizes = parseInt( $table.css( 'margin-left' ), 10 ) || 0; + $table.css( 'margin-left', storedSizes + 50 ); + vars.tableWidth = $header.width() > tmp ? 'auto' : tmp; + $table.css( 'margin-left', storedSizes ? storedSizes : '' ); + $header = null; + $table.unwrap('<span>'); + } + */ - wo.resizable_.storedSizes = storedSizes = ( ( ts.storage && wo.resizable !== false ) ? + if ( vars.useStorage && vars.overflow ) { + // save table width + ts.storage( c.table, 'tablesorter-table-original-css-width', vars.tableWidth ); + tmp = ts.storage( c.table, 'tablesorter-table-resized-width' ) || 'auto'; + ts.resizable.setWidth( $table, tmp, true ); + } + wo.resizable_.storedSizes = storedSizes = ( vars.useStorage ? ts.storage( c.table, ts.css.resizableStorage ) : [] ) || []; ts.resizable.setWidths( c, wo, storedSizes ); + ts.resizable.updateStoredSizes( c, wo ); wo.$resizable_container = $( '<div class="' + ts.css.resizableContainer + '">' ) .css({ top : marginTop }) - .insertBefore( c.$table ); + .insertBefore( $table ); // add container for ( column = 0; column < c.columns; column++ ) { $header = c.$headerIndexed[ column ]; - noResize = ts.getData( $header, ts.getColumnData( c.table, c.headers, column ), 'resizable' ) === 'false'; + tmp = ts.getColumnData( c.table, c.headers, column ); + noResize = ts.getData( $header, tmp, 'resizable' ) === 'false'; if ( !noResize ) { $( '<div class="' + ts.css.resizableHandle + '">' ) .appendTo( wo.$resizable_container ) @@ -4451,37 +4551,52 @@ ts.resizable = { .bind( 'selectstart', false ); } } - c.$table.one('tablesorter-initialized', function() { + $table.one('tablesorter-initialized', function() { ts.resizable.setHandlePosition( c, wo ); ts.resizable.bindings( this.config, this.config.widgetOptions ); }); }, - setWidth : function( $el, width ) { + updateStoredSizes : function( c, wo ) { + var column, $header, + len = c.columns, + vars = wo.resizable_; + vars.storedSizes = []; + for ( column = 0; column < len; column++ ) { + $header = c.$headerIndexed[ column ]; + vars.storedSizes[ column ] = $header.is(':visible') ? $header.width() : 0; + } + }, + + setWidth : function( $el, width, overflow ) { + // overflow tables need min & max width set as well $el.css({ 'width' : width, - 'min-width' : '', - 'max-width' : '' + 'min-width' : overflow ? width : '', + 'max-width' : overflow ? width : '' }); }, setWidths : function( c, wo, storedSizes ) { - var column, + var column, $temp, + vars = wo.resizable_, $extra = $( c.namespace + '_extra_headers' ), $col = c.$table.children( 'colgroup' ).children( 'col' ); - storedSizes = storedSizes || wo.resizable_.storedSizes || []; + storedSizes = storedSizes || vars.storedSizes || []; // process only if table ID or url match if ( storedSizes.length ) { for ( column = 0; column < c.columns; column++ ) { // set saved resizable widths - c.$headerIndexed[ column ].width( storedSizes[ column ] ); + ts.resizable.setWidth( c.$headerIndexed[ column ], storedSizes[ column ], vars.overflow ); if ( $extra.length ) { // stickyHeaders needs to modify min & max width as well - ts.resizable.setWidth( $extra.eq( column ).add( $col.eq( column ) ), storedSizes[ column ] ); + $temp = $extra.eq( column ).add( $col.eq( column ) ); + ts.resizable.setWidth( $temp, storedSizes[ column ], vars.overflow ); } } - if ( $( c.namespace + '_extra_table' ).length && !ts.hasWidget( c.table, 'scroller' ) ) { - ts.resizable.setWidth( $( c.namespace + '_extra_table' ), c.$table.outerWidth() ); + $temp = $( c.namespace + '_extra_table' ); + if ( $temp.length && !ts.hasWidget( c.table, 'scroller' ) ) { + ts.resizable.setWidth( $temp, c.$table.outerWidth(), vars.overflow ); } } }, @@ -4541,7 +4656,7 @@ ts.resizable = { var namespace = c.namespace + 'tsresize'; wo.$resizable_container.children().bind( 'mousedown', function( event ) { // save header cell and mouse position - var column, $this, + var column, vars = wo.resizable_, $extras = $( c.namespace + '_extra_headers' ), $header = $( event.target ).data( 'header' ); @@ -4560,11 +4675,7 @@ ts.resizable = { vars.next = column; vars.mouseXPosition = event.pageX; - vars.storedSizes = []; - for ( column = 0; column < c.columns; column++ ) { - $this = c.$headerIndexed[ column ]; - vars.storedSizes[ column ] = $this.is(':visible') ? $this.width() : 0; - } + ts.resizable.updateStoredSizes( c, wo ); ts.resizable.toggleTextSelection( c, true ); }); @@ -4615,47 +4726,51 @@ ts.resizable = { mouseMove : function( c, wo, event ) { if ( wo.resizable_.mouseXPosition === 0 || !wo.resizable_.$target ) { return; } // resize columns - var vars = wo.resizable_, + var column, + total = 0, + vars = wo.resizable_, $next = vars.$next, + tar = vars.storedSizes[ vars.target ], leftEdge = event.pageX - vars.mouseXPosition; - if ( vars.fullWidth ) { - vars.storedSizes[ vars.target ] += leftEdge; - vars.storedSizes[ vars.next ] -= leftEdge; - ts.resizable.setWidths( c, wo ); - - } else if ( vars.overflow ) { - c.$table.add( $( c.namespace + '_extra_table' ) ).width(function(i, w){ - return w + leftEdge; - }); + if ( vars.overflow ) { + if ( tar + leftEdge > 0 ) { + vars.storedSizes[ vars.target ] += leftEdge; + ts.resizable.setWidth( vars.$target, vars.storedSizes[ vars.target ], true ); + // update the entire table width + for ( column = 0; column < c.columns; column++ ) { + total += vars.storedSizes[ column ]; + } + ts.resizable.setWidth( c.$table.add( $( c.namespace + '_extra_table' ) ), total ); + } if ( !$next.length ) { // if expanding right-most column, scroll the wrapper vars.$wrap[0].scrollLeft = c.$table.width(); } + } else if ( vars.fullWidth ) { + vars.storedSizes[ vars.target ] += leftEdge; + vars.storedSizes[ vars.next ] -= leftEdge; + ts.resizable.setWidths( c, wo ); } else { vars.storedSizes[ vars.target ] += leftEdge; ts.resizable.setWidths( c, wo ); } vars.mouseXPosition = event.pageX; + // dynamically update sticky header widths + c.$table.trigger('stickyHeadersUpdate'); }, stopResize : function( c, wo ) { - var $this, column, - vars = wo.resizable_; - vars.storedSizes = []; - if ( ts.storage ) { - vars.storedSizes = []; - for ( column = 0; column < c.columns; column++ ) { - $this = c.$headerIndexed[ column ]; - vars.storedSizes[ column ] = $this.is(':visible') ? $this.width() : 0; - } - if ( wo.resizable !== false ) { - // save all column widths - ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); - } + var vars = wo.resizable_; + ts.resizable.updateStoredSizes( c, wo ); + if ( vars.useStorage ) { + // save all column widths + ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); + ts.storage( c.table, 'tablesorter-table-resized-width', c.$table.width() ); } vars.mouseXPosition = 0; vars.$target = vars.$next = null; - $(window).trigger('resize'); // will update stickyHeaders, just in case + // will update stickyHeaders, just in case, see #912 + c.$table.trigger('stickyHeadersUpdate'); } }; @@ -4666,11 +4781,12 @@ ts.addWidget({ id: "resizable", priority: 40, options: { - resizable : true, + resizable : true, // save column widths to storage resizable_addLastColumn : false, resizable_widths : [], resizable_throttle : false, // set to true (5ms) or any number 0-10 range - resizable_targetLast : false + resizable_targetLast : false, + resizable_fullWidth : null }, init: function(table, thisWidget, c, wo) { ts.resizable.init( c, wo ); @@ -4694,19 +4810,28 @@ ts.resizableReset = function( table, refreshing ) { $( table ).each(function(){ var index, $t, c = this.config, - wo = c && c.widgetOptions; + wo = c && c.widgetOptions, + vars = wo.resizable_; if ( table && c && c.$headerIndexed.length ) { + // restore the initial table width + if ( vars.overflow && vars.tableWidth ) { + ts.resizable.setWidth( c.$table, vars.tableWidth, true ); + if ( vars.useStorage ) { + ts.storage( table, 'tablesorter-table-resized-width', 'auto' ); + } + } for ( index = 0; index < c.columns; index++ ) { $t = c.$headerIndexed[ index ]; if ( wo.resizable_widths && wo.resizable_widths[ index ] ) { - $t.css( 'width', wo.resizable_widths[ index ] ); + ts.resizable.setWidth( $t, wo.resizable_widths[ index ], vars.overflow ); } else if ( !$t.hasClass( 'resizable-false' ) ) { // don't clear the width of any column that is not resizable - $t.css( 'width', '' ); + ts.resizable.setWidth( $t, '', vars.overflow ); } } + // reset stickyHeader widths - $( window ).trigger( 'resize' ); + c.$table.trigger( 'stickyHeadersUpdate' ); if ( ts.storage && !refreshing ) { ts.storage( this, ts.css.resizableStorage, {} ); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index fc58f75..1ce5443 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.22.1 *//* +/*! TableSorter (FORK) v2.22.3 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -26,7 +26,7 @@ var ts = this; - ts.version = '2.22.1'; + ts.version = '2.22.3'; ts.parsers = []; ts.widgets = []; @@ -191,10 +191,10 @@ if (typeof(t) === 'string') { // check data-attribute first when set to 'basic'; don't use node.innerText - it's really slow! // http://www.kellegous.com/j/2013/02/27/innertext-vs-textcontent/ - return $.trim( - ( t === 'basic' ? $node.attr(c.textAttribute) || node.textContent : node.textContent ) || - $node.text() - ); + if ( t === 'basic' && typeof ( te = $node.attr(c.textAttribute) ) !== 'undefined' ) { + return $.trim( te ); + } + return $.trim( node.textContent || $node.text() ); } else { if (typeof(t) === 'function') { return $.trim( t($node[0], c.table, cellIndex) ); @@ -206,9 +206,8 @@ return $.trim( $node[0].textContent || $node.text() ); }; - function detectParserForColumn(table, rows, rowIndex, cellIndex) { + function detectParserForColumn(c, rows, rowIndex, cellIndex) { var cur, $node, - c = table.config, i = ts.parsers.length, node = false, nodeValue = '', @@ -219,7 +218,7 @@ node = rows[rowIndex].cells[cellIndex]; nodeValue = ts.getElementText(c, node, cellIndex); $node = $(node); - if (table.config.debug) { + if (c.debug) { log('Checking if value was empty on row ' + rowIndex + ', column: ' + cellIndex + ': "' + nodeValue + '"'); } } else { @@ -229,7 +228,7 @@ while (--i >= 0) { cur = ts.parsers[i]; // ignore the default text parser because it will always be true - if (cur && cur.id !== 'text' && cur.is && cur.is(nodeValue, table, node, $node)) { + if (cur && cur.id !== 'text' && cur.is && cur.is(nodeValue, c.table, node, $node)) { return cur; } } @@ -238,7 +237,7 @@ } // centralized function to extract/parse cell contents - function getParsedText( c, cell, colIndex, txt ) { + ts.getParsedText = function( c, cell, colIndex, txt ) { if ( typeof txt === 'undefined' ) { txt = ts.getElementText( c, cell, colIndex ); } @@ -261,16 +260,17 @@ } } return val; - } + }; - function buildParserCache(table) { - var c = table.config, - // update table bodies in case we start with an empty table - tb = c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'), - rows, list, l, i, h, ch, np, p, e, time, + function buildParserCache( c, $tbodies ) { + var rows, list, l, i, h, ch, np, p, e, time, tb, len, + table = c.table, j = 0, - parsersDebug = '', - len = tb.length; + parsersDebug = ''; + // update table bodies in case we start with an empty table + c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'); + tb = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies; + len = tb.length; if ( len === 0) { return c.debug ? log('Warning: *Empty table!* Not building a parser cache') : ''; } else if (c.debug) { @@ -305,7 +305,7 @@ e = false; } if (!p) { - p = detectParserForColumn(table, rows, -1, i); + p = detectParserForColumn(c, rows, -1, i); } if (c.debug) { parsersDebug += 'column:' + i + '; extractor:' + e.id + '; parser:' + p.id + '; string:' + c.strings[i] + '; empty: ' + c.empties[i] + '\n'; @@ -325,12 +325,14 @@ } /* utils */ - function buildCache(table) { - var cc, t, v, i, j, k, $row, cols, cacheTime, + function buildCache(table, $tbodies) { + var cc, t, v, i, j, k, $tb, $row, cols, cacheTime, totalRows, rowData, prevRowData, colMax, c = table.config, - $tb = c.$tbodies, parsers = c.parsers; + // update tbody variable + c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'); + $tb = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies, c.cache = {}; c.totalRows = 0; // if no parsers found, return - it's an empty table. @@ -378,7 +380,7 @@ prevRowData.child[ t ] = []; // child row content does not account for colspans/rowspans; so indexing may be off for ( j = 0; j < c.columns; j++ ) { - prevRowData.child[ t ][ j ] = getParsedText( c, v[ j ], j ); + prevRowData.child[ t ][ j ] = ts.getParsedText( c, v[ j ], j ); } // go to the next for loop continue; @@ -394,7 +396,7 @@ } t = ts.getElementText( c, $row[ 0 ].cells[j], j ); rowData.raw.push( t ); // save original row text - v = getParsedText( c, $row[ 0 ].cells[ j ], j, t ); + v = ts.getParsedText( c, $row[ 0 ].cells[ j ], j, t ); cols.push( v ); if ( ( parsers[ j ].type || '' ).toLowerCase() === 'numeric' ) { // determine column max value (ignore sign) @@ -547,7 +549,7 @@ // remove rows/elements before update c.$table.find(c.selectorRemove).remove(); // rebuild parsers - buildParserCache(table); + buildParserCache(c); // rebuild the cache map buildCache(table); checkResort(c, resort, callback); @@ -837,7 +839,7 @@ num = (c.strings[col]) ? c.string[c.strings[col]] || 0 : 0; } // fall back to built-in numeric sort - // var sort = $.tablesorter['sort' + s](table, a[c], b[c], c, colMax[c], dir); + // var sort = $.tablesorter['sort' + s]( a[c], b[c], dir, colMax[c], table); sort = c.numberSorter ? c.numberSorter(a[col], b[col], dir, colMax[col], table) : ts[ 'sortNumeric' + (dir ? 'Asc' : 'Desc') ](a[col], b[col], num, colMax[col], col, table); } else { @@ -950,7 +952,7 @@ row = $tb.eq( tbdy ).find( 'tr' ).index( $row ); cache = tbcache.normalized[ row ]; icell = $cell.index(); - t = getParsedText( c, cell, icell ); + t = ts.getParsedText( c, cell, icell ); cache[ icell ] = t; cache[ c.columns ].$row = $row; if ( (c.parsers[icell].type || '').toLowerCase() === 'numeric' ) { @@ -985,7 +987,7 @@ tbdy = c.$tbodies.index( $row.parents('tbody').filter(':first') ); // fixes adding rows to an empty table - see issue #179 if (!(c.parsers && c.parsers.length)) { - buildParserCache(table); + buildParserCache(c); } // add each row for (i = 0; i < rows; i++) { @@ -998,7 +1000,7 @@ }; // add each cell for (j = 0; j < l; j++) { - cells[j] = getParsedText( c, $row[i].cells[j], j ); + cells[j] = ts.getParsedText( c, $row[i].cells[j], j ); if ((c.parsers[j].type || '').toLowerCase() === 'numeric') { // update column max value (ignore sign) c.cache[tbdy].colMax[j] = Math.max(Math.abs(cells[j]) || 0, c.cache[tbdy].colMax[j] || 0); @@ -1043,13 +1045,14 @@ callback(table); } }) - .bind('updateCache' + c.namespace, function(e, callback){ + // $tbodies variable is used by the tbody sorting widget + .bind('updateCache' + c.namespace, function(e, callback, $tbodies){ // rebuild parsers if (!(c.parsers && c.parsers.length)) { - buildParserCache(table); + buildParserCache(c, $tbodies); } // rebuild the cache map - buildCache(table); + buildCache(table, $tbodies); if ($.isFunction(callback)) { callback(table); } @@ -1169,7 +1172,7 @@ // add widget options before parsing (e.g. grouping widget has parser settings) ts.applyWidgetOptions(table, c); // try to auto detect column type, and store in tables config - buildParserCache(table); + buildParserCache(c); // start total row count at zero c.totalRows = 0; // build the cache for the tbody cells @@ -1395,10 +1398,11 @@ } // ignore mouseup if mousedown wasn't on the same target if ( type.match(' ' + c.pointerUp + ' ') && downTarget !== e.target && external !== true ) { return; } - // set timer on mousedown + // set target on mousedown if ( type.match(' ' + c.pointerDown + ' ') ) { downTarget = e.target; - // needed or jQuery v1.3.2 or older throws an "Uncaught TypeError: handler.apply is not a function" error + // preventDefault needed or jQuery v1.3.2 and older throws an + // "Uncaught TypeError: handler.apply is not a function" error temp = $target.jquery.split( '.' ); if ( temp[0] === '1' && temp[1] < 4 ) { e.preventDefault(); } return; @@ -2137,7 +2141,7 @@ format: function(table, c, wo) { var $tv, $tr, row, even, time, k, i, len, child = new RegExp(c.cssChildRow, 'i'), - b = c.$tbodies.add( $( c.namespace + '_extra_table' ).children( 'tbody' ) ); + b = c.$tbodies.add( $( c.namespace + '_extra_table' ).children( 'tbody:not(.' + c.cssInfoBlock + ')' ) ); if (c.debug) { time = new Date(); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index cb6d950..9290ecc 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 05-18-2015 (v2.22.1)*/ +/*! tablesorter (FORK) - updated 06-30-2015 (v2.22.2)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -476,6 +476,66 @@ ts.filter = { // data.index = column index; table = table element ( DOM ) // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class ) types: { + or : function( c, data, vars ) { + if ( /\|/.test( data.iFilter ) || ts.filter.regex.orSplit.test( data.filter ) ) { + var indx, filterMatched, txt, query, regex, + // duplicate data but split filter + data2 = $.extend( {}, data ), + index = data.index, + parsed = data.parsed[ index ], + filter = data.filter.split( ts.filter.regex.orSplit ), + iFilter = data.iFilter.split( ts.filter.regex.orSplit ), + len = filter.length; + for ( indx = 0; indx < len; indx++ ) { + data2.nestedFilters = true; + data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); + data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); + query = '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')'; + regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); + // filterMatched = data2.filter === '' && indx > 0 ? true + // look for an exact match with the 'or' unless the 'filter-match' class is found + filterMatched = regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ); + if ( filterMatched ) { + return filterMatched; + } + } + // may be null from processing types + return filterMatched || false; + } + return null; + }, + // Look for an AND or && operator ( logical and ) + and : function( c, data, vars ) { + if ( ts.filter.regex.andTest.test( data.filter ) ) { + var indx, filterMatched, result, txt, query, regex, + // duplicate data but split filter + data2 = $.extend( {}, data ), + index = data.index, + parsed = data.parsed[ index ], + filter = data.filter.split( ts.filter.regex.andSplit ), + iFilter = data.iFilter.split( ts.filter.regex.andSplit ), + len = filter.length; + for ( indx = 0; indx < len; indx++ ) { + data2.nestedFilters = true; + data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); + data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); + query = ( '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' ) + // replace wild cards since /(a*)/i will match anything + .replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ); + regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); + // look for an exact match with the 'and' unless the 'filter-match' class is found + result = ( regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ) ); + if ( indx === 0 ) { + filterMatched = result; + } else { + filterMatched = filterMatched && result; + } + } + // may be null from processing types + return filterMatched || false; + } + return null; + }, // Look for regex regex: function( c, data ) { if ( ts.filter.regex.regex.test( data.filter ) ) { @@ -563,23 +623,6 @@ ts.filter = { } return null; }, - // Look for an AND or && operator ( logical and ) - and : function( c, data ) { - if ( ts.filter.regex.andTest.test( data.filter ) ) { - var index = data.index, - parsed = data.parsed[index], - query = data.iFilter.split( ts.filter.regex.andSplit ), - result = data.iExact.search( $.trim( ts.filter.parseFilter( c, query[0], index, parsed ) ) ) >= 0, - indx = query.length - 1; - while ( result && indx ) { - result = result && - data.iExact.search( $.trim( ts.filter.parseFilter( c, query[indx], index, parsed ) ) ) >= 0; - indx--; - } - return result; - } - return null; - }, // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu! range : function( c, data ) { if ( ts.filter.regex.toTest.test( data.iFilter ) ) { @@ -616,24 +659,20 @@ ts.filter = { }, // Look for wild card: ? = single, * = multiple, or | = logical OR wild : function( c, data ) { - if ( /[\?\*\|]/.test( data.iFilter ) || ts.filter.regex.orReplace.test( data.filter ) ) { + if ( /[\?\*\|]/.test( data.iFilter ) ) { var index = data.index, parsed = data.parsed[ index ], - txt = data.iFilter.replace( ts.filter.regex.orReplace, '|' ), - query = '' + ( ts.filter.parseFilter( c, txt, index, parsed ) || '' ); + query = '' + ( ts.filter.parseFilter( c, data.iFilter, index, parsed ) || '' ); // look for an exact match with the 'or' unless the 'filter-match' class is found - if ( !c.$headerIndexed[ index ].hasClass( 'filter-match' ) && /\|/.test( query ) ) { - // show all results while using filter match. Fixes #727 - if ( query[ query.length - 1 ] === '|' ) { - query += '*'; - } - query = data.anyMatch && $.isArray( data.rowArray ) ? - '(' + query + ')' : - '^(' + query + ')$'; + if ( !/\?\*/.test( query ) && data.nestedFilters ) { + query = data.isMatch ? query : '^(' + query + ')$'; } // parsing the filter may not work properly when using wildcards =/ - return new RegExp( query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ) ) - .test( data.iExact ); + return new RegExp( + query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ), + c.widgetOptions.filter_ignoreCase ? 'i' : '' + ) + .test( data.exact ); } return null; }, @@ -687,7 +726,7 @@ ts.filter = { toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)' ,'gi' ), andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), - orReplace : new RegExp( '\\s+(' + ts.language.or + ')\\s+', 'gi' ), + orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ), iQuery : new RegExp( val, 'i' ), igQuery : new RegExp( val, 'ig' ) }); @@ -907,7 +946,6 @@ ts.filter = { } } }, - setDefaults: function( table, c, wo ) { var isArray, saved, indx, col, $filters, // get current ( default ) filters @@ -1134,14 +1172,13 @@ ts.filter = { } }, hideFilters: function( table, c ) { - var $filterRow, $filterRow2, timer; - $( table ) + var timer; + c.$table .find( '.' + tscss.filterRow ) - .addClass( tscss.filterRowHide ) .bind( 'mouseenter mouseleave', function( e ) { // save event object - http://bugs.jquery.com/ticket/12140 - var event = e; - $filterRow = $( this ); + var event = e, + $filterRow = $( this ); clearTimeout( timer ); timer = setTimeout( function() { if ( /enter|over/.test( event.type ) ) { @@ -1159,13 +1196,14 @@ ts.filter = { }, 200 ); }) .find( 'input, select' ).bind( 'focus blur', function( e ) { - $filterRow2 = $( this ).closest( 'tr' ); + var event = e, + $row = $( this ).closest( 'tr' ); clearTimeout( timer ); - var event = e; timer = setTimeout( function() { + clearTimeout( timer ); // don't hide row if any filter has a value if ( ts.getFilters( c.$table ).join( '' ) === '' ) { - $filterRow2.toggleClass( tscss.filterRowHide, event.type === 'focus' ); + $row.toggleClass( tscss.filterRowHide, event.type !== 'focus' ); } }, 200 ); }); @@ -1199,7 +1237,7 @@ ts.filter = { return $( b ).attr( 'data-lastSearchTime' ) - $( a ).attr( 'data-lastSearchTime' ); }); } - return $(); + return $input || $(); }, multipleColumns: function( c, $input ) { // look for multiple columns '1-3,4-6,8' in data-column @@ -1252,8 +1290,22 @@ ts.filter = { } return columns; }, + processTypes: function( c, data, vars ) { + var ffxn, + filterMatched = null, + matches = null; + for ( ffxn in ts.filter.types ) { + if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) { + matches = ts.filter.types[ffxn]( c, data, vars ); + if ( matches !== null ) { + filterMatched = matches; + } + } + } + return filterMatched; + }, processRow: function( c, data, vars ) { - var $cell, columnIndex, hasSelect, matches, result, val, filterMatched, excludeMatch, + var columnIndex, hasSelect, result, val, filterMatched, fxn, ffxn, txt, regex = ts.filter.regex, wo = c.widgetOptions, @@ -1264,6 +1316,7 @@ ts.filter = { // look for multiple columns '1-3,4-6,8' columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); data.anyMatch = true; + data.isMatch = true; data.rowArray = data.$cells.map( function( i ) { if ( $.inArray( i, columnIndex ) > -1 ) { if ( data.parsed[ i ] ) { @@ -1283,16 +1336,10 @@ ts.filter = { data.exact = data.rowArray.join( ' ' ); data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' ); - filterMatched = null; - matches = null; - for ( ffxn in ts.filter.types ) { - if ( $.inArray( ffxn, vars.noAnyMatch ) < 0 && matches === null ) { - matches = ts.filter.types[ffxn]( c, data ); - if ( matches !== null ) { - filterMatched = matches; - } - } - } + + vars.excludeMatch = vars.noAnyMatch; + filterMatched = ts.filter.processTypes( c, data, vars ); + if ( filterMatched !== null ) { showRow = filterMatched; } else { @@ -1319,7 +1366,7 @@ ts.filter = { data.index = columnIndex; // filter types to exclude, per column - excludeMatch = vars.excludeFilter[ columnIndex ]; + vars.excludeMatch = vars.excludeFilter[ columnIndex ]; // ignore if filter is empty or disabled if ( data.filter ) { @@ -1333,6 +1380,9 @@ ts.filter = { } data.iExact = !regex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; + + data.isMatch = c.$headerIndexed[ data.index ].hasClass( 'filter-match' ); + result = showRow; // if showRow is true, show that row // in case select filter option has a different value vs text 'a - z|A through Z' @@ -1357,13 +1407,12 @@ ts.filter = { // data.filter = case sensitive data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter; fxn = vars.functions[ columnIndex ]; - $cell = c.$headerIndexed[ columnIndex ]; - hasSelect = $cell.hasClass( 'filter-select' ); + hasSelect = c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' ); filterMatched = null; if ( fxn || ( hasSelect && val ) ) { if ( fxn === true || hasSelect ) { // default selector uses exact match unless 'filter-match' class is found - filterMatched = $cell.hasClass( 'filter-match' ) ? + filterMatched = data.isMatch ? data.iExact.search( data.iFilter ) >= 0 : data.filter === data.exact; } else if ( typeof fxn === 'function' ) { @@ -1380,15 +1429,7 @@ ts.filter = { if ( filterMatched === null ) { // cycle through the different filters // filters return a boolean or null if nothing matches - matches = null; - for ( ffxn in ts.filter.types ) { - if ( $.inArray( ffxn, excludeMatch ) < 0 && matches === null ) { - matches = ts.filter.types[ ffxn ]( c, data ); - if ( matches !== null ) { - filterMatched = matches; - } - } - } + filterMatched = ts.filter.processTypes( c, data, vars ); if ( filterMatched !== null ) { result = filterMatched; // Look for match, and add child row data for matching @@ -1422,7 +1463,7 @@ ts.filter = { anyMatch: false, filters: filters, // regex filter type cache - filter_regexCache : [], + filter_regexCache : [] }, vars = { // anyMatch really screws up with these types of filters @@ -1643,7 +1684,7 @@ ts.filter = { }, getOptionSource: function( table, column, onlyAvail ) { table = $( table )[0]; - var cts, indx, len, + var cts, txt, indx, len, c = table.config, wo = c.widgetOptions, parsed = [], @@ -1688,11 +1729,13 @@ ts.filter = { len = arry.length; // parse select option values for ( indx = 0; indx < len; indx++ ) { + txt = arry[ indx ]; // parse array data using set column parser; this DOES NOT pass the original // table cell to the parser format function parsed.push({ - t : arry[ indx ], - p : c.parsers && c.parsers[ column ].format( arry[ indx ], table, [], column ) + t : txt, + // check parser length - fixes #934 + p : c.parsers && c.parsers.length && c.parsers[ column ].format( txt, table, [], column ) || txt }); } @@ -1882,8 +1925,8 @@ ts.getFilters = function( table, getRaw, setFilters, skipFirst ) { $column = ts.filter.getLatestSearch( $column ); if ( $.isArray( setFilters ) ) { // skip first ( latest input ) to maintain cursor position while typing - if ( skipFirst ) { - $column.slice( 1 ); + if ( skipFirst && $column.length > 1 ) { + $column = $column.slice( 1 ); } if ( i === c.columns ) { // prevent data-column='all' from filling data-column='0,1' ( etc ) @@ -1955,32 +1998,34 @@ $.extend(ts.css, { // Add a resize event to table headers ts.addHeaderResizeEvent = function(table, disable, settings) { table = $(table)[0]; // make sure we're using a dom element - var headers, - defaults = { + if ( !table.config ) { return; } + var defaults = { timer : 250 }, options = $.extend({}, defaults, settings), c = table.config, wo = c.widgetOptions, - checkSizes = function(triggerEvent) { + checkSizes = function( triggerEvent ) { + var index, headers, $header, sizes, width, height, + len = c.$headers.length; wo.resize_flag = true; headers = []; - c.$headers.each(function() { - var $header = $(this), - sizes = $header.data('savedSizes') || [0,0], // fixes #394 - width = this.offsetWidth, - height = this.offsetHeight; - if (width !== sizes[0] || height !== sizes[1]) { - $header.data('savedSizes', [ width, height ]); - headers.push(this); + for ( index = 0; index < len; index++ ) { + $header = c.$headers.eq( index ); + sizes = $header.data( 'savedSizes' ) || [ 0,0 ]; // fixes #394 + width = $header[0].offsetWidth; + height = $header[0].offsetHeight; + if ( width !== sizes[0] || height !== sizes[1] ) { + $header.data( 'savedSizes', [ width, height ] ); + headers.push( $header[0] ); } - }); - if (headers.length && triggerEvent !== false) { - c.$table.trigger('resize', [ headers ]); + } + if ( headers.length && triggerEvent !== false ) { + c.$table.trigger( 'resize', [ headers ] ); } wo.resize_flag = false; }; - checkSizes(false); + checkSizes( false ); clearInterval(wo.resize_timer); if (disable) { wo.resize_flag = false; @@ -2016,7 +2061,8 @@ ts.addWidget({ if ( c.$table.hasClass('hasStickyHeaders') || ($.inArray('filter', c.widgets) >= 0 && !c.$table.hasClass('hasFilters')) ) { return; } - var $table = c.$table, + var index, len, $t, + $table = c.$table, // add position: relative to attach element, hopefully it won't cause trouble. $attach = $(wo.stickyHeaders_attachTo), namespace = c.namespace + 'stickyheaders ', @@ -2051,17 +2097,19 @@ ts.addWidget({ laststate = '', spacing = 0, setWidth = function($orig, $clone){ - $orig.filter(':visible').each(function(i) { - var width, border, - $cell = $clone.filter(':visible').eq(i), - $this = $(this); + var index, width, border, $cell, $this, + $cells = $orig.filter(':visible'), + len = $cells.length; + for ( index = 0; index < len; index++ ) { + $cell = $clone.filter(':visible').eq(index); + $this = $cells.eq(index); // code from https://github.com/jmosbech/StickyTableHeaders if ($this.css('box-sizing') === 'border-box') { width = $this.outerWidth(); } else { if ($cell.css('border-collapse') === 'collapse') { if (window.getComputedStyle) { - width = parseFloat( window.getComputedStyle(this, null).width ); + width = parseFloat( window.getComputedStyle($this[0], null).width ); } else { // ie8 only border = parseFloat( $this.css('border-width') ); @@ -2072,10 +2120,11 @@ ts.addWidget({ } } $cell.css({ + 'width': width, 'min-width': width, 'max-width': width }); - }); + } }, resizeHeader = function() { stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0; @@ -2087,6 +2136,39 @@ ts.addWidget({ }); setWidth( $table, $stickyTable ); setWidth( $header, $stickyCells ); + }, + scrollSticky = function( resizing ) { + if (!$table.is(':visible')) { return; } // fixes #278 + // Detect nested tables - fixes #724 + nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; + var offset = $table.offset(), + yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 + xWindow = $.isWindow( $xScroll[0] ), + // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, + scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, + tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)), + isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', + cssSettings = { visibility : isVisible }; + + if ($attach.length) { + cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); + } + if (xWindow) { + // adjust when scrolling horizontally - fixes issue #143 + cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing; + } + if ($nestedSticky.length) { + cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; + } + $stickyWrap + .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) + .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) + .css(cssSettings); + if (isVisible !== laststate || resizing) { + // make sure the column widths match + resizeHeader(); + laststate = isVisible; + } }; // only add a position relative if a position isn't already defined if ($attach.length && !$attach.css('position')) { @@ -2118,48 +2200,26 @@ ts.addWidget({ // onRenderHeader is defined, we need to do something about it (fixes #641) if (c.onRenderHeader) { - $stickyThead.children('tr').children().each(function(index){ + $t = $stickyThead.children('tr').children(); + len = $t.length; + for ( index = 0; index < len; index++ ) { // send second parameter - c.onRenderHeader.apply( $(this), [ index, c, $stickyTable ] ); - }); + c.onRenderHeader.apply( $t.eq( index ), [ index, c, $stickyTable ] ); + } } // make it sticky! $xScroll.add($yScroll) - .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') ) - .bind('scroll resize '.split(' ').join( namespace ), function(event) { - if (!$table.is(':visible')) { return; } // fixes #278 - // Detect nested tables - fixes #724 - nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; - var offset = $table.offset(), - yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 - xWindow = $.isWindow( $xScroll[0] ), - // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, - scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, - tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)), - isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', - cssSettings = { visibility : isVisible }; - - if ($attach.length) { - cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); - } - if (xWindow) { - // adjust when scrolling horizontally - fixes issue #143 - cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing; - } - if ($nestedSticky.length) { - cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; - } - $stickyWrap - .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) - .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) - .css(cssSettings); - if (isVisible !== laststate || event.type === 'resize') { - // make sure the column widths match - resizeHeader(); - laststate = isVisible; - } - }); + .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') ) + .bind('scroll resize '.split(' ').join( namespace ), function( event ) { + scrollSticky( event.type === 'resize' ); + }); + c.$table + .unbind('stickyHeadersUpdate' + namespace) + .bind('stickyHeadersUpdate' + namespace, function(){ + scrollSticky( true ); + }); + if (wo.stickyHeaders_addResizeEvent) { ts.addHeaderResizeEvent(table); } @@ -2195,7 +2255,7 @@ ts.addWidget({ var namespace = c.namespace + 'stickyheaders '; c.$table .removeClass('hasStickyHeaders') - .unbind( ('pagerComplete filterEnd '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) + .unbind( ('pagerComplete filterEnd stickyHeadersUpdate '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) .next('.' + ts.css.stickyWrap).remove(); if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table $(window) @@ -2209,7 +2269,8 @@ ts.addWidget({ })(jQuery, window); -/*! Widget: resizable - updated 5/17/2015 (v2.22.0) */ +/*! Widget: resizable - updated 6/26/2015 (v2.22.2) */ +/*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; var ts = $.tablesorter || {}; @@ -2228,8 +2289,8 @@ $(function(){ '-khtml-user-select: none; -webkit-user-select: none; user-select: none; }' + '.' + ts.css.resizableContainer + ' { position: relative; height: 1px; }' + // make handle z-index > than stickyHeader z-index, so the handle stays above sticky header - '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px; top: 1px;' + - 'cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' + + '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px;' + + 'top: 1px; cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' + '</style>'; $(s).appendTo('body'); }); @@ -2238,34 +2299,69 @@ ts.resizable = { init : function( c, wo ) { if ( c.$table.hasClass( 'hasResizable' ) ) { return; } c.$table.addClass( 'hasResizable' ); - ts.resizableReset( c.table, true ); // set default widths + + var noResize, $header, column, storedSizes, tmp, + $table = c.$table, + $parent = $table.parent(), + marginTop = parseInt( $table.css( 'margin-top' ), 10 ), // internal variables - wo.resizable_ = { - $wrap : c.$table.parent(), + vars = wo.resizable_ = { + useStorage : ts.storage && wo.resizable !== false, + $wrap : $parent, mouseXPosition : 0, $target : null, $next : null, - overflow : c.$table.parent().css('overflow') === 'auto', - fullWidth : Math.abs(c.$table.parent().width() - c.$table.width()) < 20, + overflow : $parent.css('overflow') === 'auto' || + $parent.css('overflow') === 'scroll' || + $parent.css('overflow-x') === 'auto' || + $parent.css('overflow-x') === 'scroll', storedSizes : [] }; - var noResize, $header, column, storedSizes, - marginTop = parseInt( c.$table.css( 'margin-top' ), 10 ); - - wo.resizable_.storedSizes = storedSizes = ( ( ts.storage && wo.resizable !== false ) ? + // set default widths + ts.resizableReset( c.table, true ); + + // now get measurements! + vars.tableWidth = $table.width(); + // attempt to autodetect + vars.fullWidth = Math.abs( $parent.width() - vars.tableWidth ) < 20; + + /* + // Hacky method to determine if table width is set to "auto" + // http://stackoverflow.com/a/20892048/145346 + if ( !vars.fullWidth ) { + tmp = $table.width(); + $header = $table.wrap('<span>').parent(); // temp variable + storedSizes = parseInt( $table.css( 'margin-left' ), 10 ) || 0; + $table.css( 'margin-left', storedSizes + 50 ); + vars.tableWidth = $header.width() > tmp ? 'auto' : tmp; + $table.css( 'margin-left', storedSizes ? storedSizes : '' ); + $header = null; + $table.unwrap('<span>'); + } + */ + + if ( vars.useStorage && vars.overflow ) { + // save table width + ts.storage( c.table, 'tablesorter-table-original-css-width', vars.tableWidth ); + tmp = ts.storage( c.table, 'tablesorter-table-resized-width' ) || 'auto'; + ts.resizable.setWidth( $table, tmp, true ); + } + wo.resizable_.storedSizes = storedSizes = ( vars.useStorage ? ts.storage( c.table, ts.css.resizableStorage ) : [] ) || []; ts.resizable.setWidths( c, wo, storedSizes ); + ts.resizable.updateStoredSizes( c, wo ); wo.$resizable_container = $( '<div class="' + ts.css.resizableContainer + '">' ) .css({ top : marginTop }) - .insertBefore( c.$table ); + .insertBefore( $table ); // add container for ( column = 0; column < c.columns; column++ ) { $header = c.$headerIndexed[ column ]; - noResize = ts.getData( $header, ts.getColumnData( c.table, c.headers, column ), 'resizable' ) === 'false'; + tmp = ts.getColumnData( c.table, c.headers, column ); + noResize = ts.getData( $header, tmp, 'resizable' ) === 'false'; if ( !noResize ) { $( '<div class="' + ts.css.resizableHandle + '">' ) .appendTo( wo.$resizable_container ) @@ -2277,37 +2373,52 @@ ts.resizable = { .bind( 'selectstart', false ); } } - c.$table.one('tablesorter-initialized', function() { + $table.one('tablesorter-initialized', function() { ts.resizable.setHandlePosition( c, wo ); ts.resizable.bindings( this.config, this.config.widgetOptions ); }); }, - setWidth : function( $el, width ) { + updateStoredSizes : function( c, wo ) { + var column, $header, + len = c.columns, + vars = wo.resizable_; + vars.storedSizes = []; + for ( column = 0; column < len; column++ ) { + $header = c.$headerIndexed[ column ]; + vars.storedSizes[ column ] = $header.is(':visible') ? $header.width() : 0; + } + }, + + setWidth : function( $el, width, overflow ) { + // overflow tables need min & max width set as well $el.css({ 'width' : width, - 'min-width' : '', - 'max-width' : '' + 'min-width' : overflow ? width : '', + 'max-width' : overflow ? width : '' }); }, setWidths : function( c, wo, storedSizes ) { - var column, + var column, $temp, + vars = wo.resizable_, $extra = $( c.namespace + '_extra_headers' ), $col = c.$table.children( 'colgroup' ).children( 'col' ); - storedSizes = storedSizes || wo.resizable_.storedSizes || []; + storedSizes = storedSizes || vars.storedSizes || []; // process only if table ID or url match if ( storedSizes.length ) { for ( column = 0; column < c.columns; column++ ) { // set saved resizable widths - c.$headerIndexed[ column ].width( storedSizes[ column ] ); + ts.resizable.setWidth( c.$headerIndexed[ column ], storedSizes[ column ], vars.overflow ); if ( $extra.length ) { // stickyHeaders needs to modify min & max width as well - ts.resizable.setWidth( $extra.eq( column ).add( $col.eq( column ) ), storedSizes[ column ] ); + $temp = $extra.eq( column ).add( $col.eq( column ) ); + ts.resizable.setWidth( $temp, storedSizes[ column ], vars.overflow ); } } - if ( $( c.namespace + '_extra_table' ).length && !ts.hasWidget( c.table, 'scroller' ) ) { - ts.resizable.setWidth( $( c.namespace + '_extra_table' ), c.$table.outerWidth() ); + $temp = $( c.namespace + '_extra_table' ); + if ( $temp.length && !ts.hasWidget( c.table, 'scroller' ) ) { + ts.resizable.setWidth( $temp, c.$table.outerWidth(), vars.overflow ); } } }, @@ -2367,7 +2478,7 @@ ts.resizable = { var namespace = c.namespace + 'tsresize'; wo.$resizable_container.children().bind( 'mousedown', function( event ) { // save header cell and mouse position - var column, $this, + var column, vars = wo.resizable_, $extras = $( c.namespace + '_extra_headers' ), $header = $( event.target ).data( 'header' ); @@ -2386,11 +2497,7 @@ ts.resizable = { vars.next = column; vars.mouseXPosition = event.pageX; - vars.storedSizes = []; - for ( column = 0; column < c.columns; column++ ) { - $this = c.$headerIndexed[ column ]; - vars.storedSizes[ column ] = $this.is(':visible') ? $this.width() : 0; - } + ts.resizable.updateStoredSizes( c, wo ); ts.resizable.toggleTextSelection( c, true ); }); @@ -2441,47 +2548,51 @@ ts.resizable = { mouseMove : function( c, wo, event ) { if ( wo.resizable_.mouseXPosition === 0 || !wo.resizable_.$target ) { return; } // resize columns - var vars = wo.resizable_, + var column, + total = 0, + vars = wo.resizable_, $next = vars.$next, + tar = vars.storedSizes[ vars.target ], leftEdge = event.pageX - vars.mouseXPosition; - if ( vars.fullWidth ) { - vars.storedSizes[ vars.target ] += leftEdge; - vars.storedSizes[ vars.next ] -= leftEdge; - ts.resizable.setWidths( c, wo ); - - } else if ( vars.overflow ) { - c.$table.add( $( c.namespace + '_extra_table' ) ).width(function(i, w){ - return w + leftEdge; - }); + if ( vars.overflow ) { + if ( tar + leftEdge > 0 ) { + vars.storedSizes[ vars.target ] += leftEdge; + ts.resizable.setWidth( vars.$target, vars.storedSizes[ vars.target ], true ); + // update the entire table width + for ( column = 0; column < c.columns; column++ ) { + total += vars.storedSizes[ column ]; + } + ts.resizable.setWidth( c.$table.add( $( c.namespace + '_extra_table' ) ), total ); + } if ( !$next.length ) { // if expanding right-most column, scroll the wrapper vars.$wrap[0].scrollLeft = c.$table.width(); } + } else if ( vars.fullWidth ) { + vars.storedSizes[ vars.target ] += leftEdge; + vars.storedSizes[ vars.next ] -= leftEdge; + ts.resizable.setWidths( c, wo ); } else { vars.storedSizes[ vars.target ] += leftEdge; ts.resizable.setWidths( c, wo ); } vars.mouseXPosition = event.pageX; + // dynamically update sticky header widths + c.$table.trigger('stickyHeadersUpdate'); }, stopResize : function( c, wo ) { - var $this, column, - vars = wo.resizable_; - vars.storedSizes = []; - if ( ts.storage ) { - vars.storedSizes = []; - for ( column = 0; column < c.columns; column++ ) { - $this = c.$headerIndexed[ column ]; - vars.storedSizes[ column ] = $this.is(':visible') ? $this.width() : 0; - } - if ( wo.resizable !== false ) { - // save all column widths - ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); - } + var vars = wo.resizable_; + ts.resizable.updateStoredSizes( c, wo ); + if ( vars.useStorage ) { + // save all column widths + ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); + ts.storage( c.table, 'tablesorter-table-resized-width', c.$table.width() ); } vars.mouseXPosition = 0; vars.$target = vars.$next = null; - $(window).trigger('resize'); // will update stickyHeaders, just in case + // will update stickyHeaders, just in case, see #912 + c.$table.trigger('stickyHeadersUpdate'); } }; @@ -2492,11 +2603,12 @@ ts.addWidget({ id: "resizable", priority: 40, options: { - resizable : true, + resizable : true, // save column widths to storage resizable_addLastColumn : false, resizable_widths : [], resizable_throttle : false, // set to true (5ms) or any number 0-10 range - resizable_targetLast : false + resizable_targetLast : false, + resizable_fullWidth : null }, init: function(table, thisWidget, c, wo) { ts.resizable.init( c, wo ); @@ -2520,19 +2632,28 @@ ts.resizableReset = function( table, refreshing ) { $( table ).each(function(){ var index, $t, c = this.config, - wo = c && c.widgetOptions; + wo = c && c.widgetOptions, + vars = wo.resizable_; if ( table && c && c.$headerIndexed.length ) { + // restore the initial table width + if ( vars.overflow && vars.tableWidth ) { + ts.resizable.setWidth( c.$table, vars.tableWidth, true ); + if ( vars.useStorage ) { + ts.storage( table, 'tablesorter-table-resized-width', 'auto' ); + } + } for ( index = 0; index < c.columns; index++ ) { $t = c.$headerIndexed[ index ]; if ( wo.resizable_widths && wo.resizable_widths[ index ] ) { - $t.css( 'width', wo.resizable_widths[ index ] ); + ts.resizable.setWidth( $t, wo.resizable_widths[ index ], vars.overflow ); } else if ( !$t.hasClass( 'resizable-false' ) ) { // don't clear the width of any column that is not resizable - $t.css( 'width', '' ); + ts.resizable.setWidth( $t, '', vars.overflow ); } } + // reset stickyHeader widths - $( window ).trigger( 'resize' ); + c.$table.trigger( 'stickyHeadersUpdate' ); if ( ts.storage && !refreshing ) { ts.storage( this, ts.css.resizableStorage, {} ); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index e009e84..e4016ec 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -54,6 +54,7 @@ format : function( txt, table, cell, cellIndex ) { var $cell = $( cell ), wo = table.config.widgetOptions, + checkedClass = table.config.checkboxClass || 'checked', // returning plain language here because this is what is shown in the // group headers - change it as desired status = wo.group_checkbox ? wo.group_checkbox : [ 'checked', 'unchecked' ], @@ -61,7 +62,7 @@ isChecked = $input.length ? $input[ 0 ].checked : ''; // adding class to row, indicating that a checkbox is checked; includes // a column index in case more than one checkbox happens to be in a row - $cell.closest( 'tr' ).toggleClass( 'checked checked-' + cellIndex, isChecked ); + $cell.closest( 'tr' ).toggleClass( checkedClass + ' ' + checkedClass + '-' + cellIndex, isChecked ); return $input.length ? status[ isChecked ? 0 : 1 ] : txt; }, parsed : true, // filter widget flag diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js index 4f367d0..c216bf7 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js @@ -1,75 +1,90 @@ /*! Parser: metric *//* * Demo: http://jsfiddle.net/Mottie/abkNM/382/ - * Set the metric name in the header (defaults to "m|meter"), e.g. - * <th data-metric-name="b|byte">HDD Size</th> - * <th data-metric-name="m|meter">Distance</th> + * Set the metric name in the header (defaults to 'm|meter'), e.g. + * <th data-metric-name-abbr="b|B" data-metric-name-full="byte|Byte|BYTE">HDD Size</th> + * <th data-metric-name="m|meter">Distance</th> <!-- data-metric-name is deprecated in v2.22.2 --> */ /*jshint jquery:true */ -;(function($){ -"use strict"; +;( function( $ ) { +'use strict'; var prefixes = { - // "prefix" : [ base 10, base 2 ] + // 'prefix' : [ base 10, base 2 ] // skipping IEEE 1541 defined prefixes: kibibyte, mebibyte, etc, for now. - "Y|Yotta|yotta" : [ 1e24, Math.pow(1024, 8) ], // 1024^8 - "Z|Zetta|zetta" : [ 1e21, Math.pow(1024, 7) ], // 1024^7 - "E|Exa|exa" : [ 1e18, Math.pow(1024, 6) ], // 1024^6 - "P|Peta|peta" : [ 1e15, Math.pow(1024, 5) ], // 1024^5 - "T|Tera|tera" : [ 1e12, Math.pow(1024, 4) ], // 1024^4 - "G|Giga|giga" : [ 1e9, Math.pow(1024, 3) ], // 1024^3 - "M|Mega|mega" : [ 1e6, Math.pow(1024, 2) ], // 1024^2 - "k|Kilo|kilo" : [ 1e3, 1024 ], // 1024 + 'Y|Yotta|yotta' : [ 1e24, Math.pow(1024, 8) ], // 1024^8 + 'Z|Zetta|zetta' : [ 1e21, Math.pow(1024, 7) ], // 1024^7 + 'E|Exa|exa' : [ 1e18, Math.pow(1024, 6) ], // 1024^6 + 'P|Peta|peta' : [ 1e15, Math.pow(1024, 5) ], // 1024^5 + 'T|Tera|tera' : [ 1e12, Math.pow(1024, 4) ], // 1024^4 + 'G|Giga|giga' : [ 1e9, Math.pow(1024, 3) ], // 1024^3 + 'M|Mega|mega' : [ 1e6, Math.pow(1024, 2) ], // 1024^2 + 'k|Kilo|kilo' : [ 1e3, 1024 ], // 1024 // prefixes below here are rarely, if ever, used in binary - "h|hecto" : [ 1e2, 1e2 ], - "da|deka" : [ 1e1, 1e1 ], - "d|deci" : [ 1e-1, 1e-1 ], - "c|centi" : [ 1e-2, 1e-2], - "m|milli" : [ 1e-3, 1e-3 ], - "µ|micro" : [ 1e-6, 1e-6 ], - "n|nano" : [ 1e-9, 1e-9 ], - "p|pico" : [ 1e-12, 1e-12 ], - "f|femto" : [ 1e-15, 1e-15 ], - "a|atto" : [ 1e-18, 1e-18 ], - "z|zepto" : [ 1e-21, 1e-21 ], - "y|yocto" : [ 1e-24, 1e-24 ] + 'h|hecto' : [ 1e2, 1e2 ], + 'da|deka' : [ 1e1, 1e1 ], + 'd|deci' : [ 1e-1, 1e-1 ], + 'c|centi' : [ 1e-2, 1e-2], + 'm|milli' : [ 1e-3, 1e-3 ], + 'µ|micro' : [ 1e-6, 1e-6 ], + 'n|nano' : [ 1e-9, 1e-9 ], + 'p|pico' : [ 1e-12, 1e-12 ], + 'f|femto' : [ 1e-15, 1e-15 ], + 'a|atto' : [ 1e-18, 1e-18 ], + 'z|zepto' : [ 1e-21, 1e-21 ], + 'y|yocto' : [ 1e-24, 1e-24 ] }, // the \\d+ will not catch digits with spaces, commas or decimals; so use the value from n instead - RegLong = "(\\d+)(\\s+)?([Zz]etta|[Ee]xa|[Pp]eta|[Tt]era|[Gg]iga|[Mm]ega|kilo|hecto|deka|deci|centi|milli|micro|nano|pico|femto|atto|zepto|yocto)(", - RegAbbr = "(\\d+)(\\s+)?(Z|E|P|T|G|M|k|h|da|d|c|m|µ|n|p|f|a|z|y)("; + RegLong = '(\\d+)(\\s+)?([Zz]etta|[Ee]xa|[Pp]eta|[Tt]era|[Gg]iga|[Mm]ega|kilo|hecto|deka|deci|centi|milli|micro|nano|pico|femto|atto|zepto|yocto)(', + RegAbbr = '(\\d+)(\\s+)?(Z|E|P|T|G|M|k|h|da|d|c|m|µ|n|p|f|a|z|y)(', + // make these case-insensitive because we all forget the case for these binary values + byteTest = /^[b|bit|byte|o|octet]/i; $.tablesorter.addParser({ id: 'metric', is: function() { return false; }, - format: function(s, table, cell, cellIndex) { - var v = 'm|meter', - b, t, - // process number here to get a numerical format (us or eu) - n = $.tablesorter.formatFloat(s.replace(/[^\w,. \-()]/g, ""), table), - $t = table.config.$headerIndexed[cellIndex], - m = $t.data('metric'); - if (!m) { + format: function(txt, table, cell, cellIndex) { + var unit, isBinary, nameLong, nameAbbr, + // default base unit name + base = 'm|meter', + // process number here to get a numerical format (us or eu) + num = $.tablesorter.formatFloat( txt.replace(/[^\w,. \-()]/g, ''), table ), + $t = table.config.$headerIndexed[ cellIndex ], + regex = $t.data( 'metric' ); + if ( !regex ) { // stored values - t = ($t.attr('data-metric-name') || v).split('|'); - m = [ t[1] || t[0].substring(1), t[0] ]; - m[2] = new RegExp(RegLong + m[0] + "|" + m[1] + ")"); - m[3] = new RegExp(RegAbbr + m[1] + ")"); - $t.data('metric', m); + unit = ( $t.attr('data-metric-name') || base ).split( '|' ); + nameLong = $t.attr( 'data-metric-name-full' ) || ''; + nameAbbr = $t.attr( 'data-metric-name-abbr' ) || ''; + regex = [ nameLong || unit[1] || unit[0].substring(1), nameAbbr || unit[0] ]; + isBinary = byteTest.test( regex.join( '' ) ); + // adding 'data-metric-name-full' which would contain 'byte|BYTE|Byte' etc + regex[2] = new RegExp( RegLong + ( + ( nameLong === '' ? '' : nameLong + '|' + nameAbbr ) || + // with data-metric-name='b|byte', we end up with 'b|B|byte|BYTE' - maybe not the best solution for case-insensitivity + ( ( isBinary ? regex[0].toLowerCase() + '|' + regex[0].toUpperCase() : regex[0] ) + '|' + + ( isBinary ? regex[1].toLowerCase() + '|' + regex[1].toUpperCase() : regex[1] ) ) ) + + ')' ); + // adding 'data-metric-name-abbr' which would contain 'b|B' etc + regex[3] = new RegExp( RegAbbr + ( nameAbbr || + ( ( isBinary ? regex[1].toLowerCase() + '|' + regex[1].toUpperCase() : regex[1] ) ) ) + + ')' ); + $t.data( 'metric', regex ); } // find match to full name or abbreviation - t = s.match(m[2]) || s.match(m[3]); - if (t) { - for (v in prefixes) { - if (t[3].match(v)) { + unit = txt.match( regex[2] ) || txt.match( regex[3] ); + if ( unit ) { + for ( base in prefixes ) { + if ( unit[3].match( base ) ) { // exception when using binary prefix // change base for binary use - b = /^[b|bit|byte|o|octet]/.test(t[4]) ? 1 : 0; - return n * prefixes[v][b]; + isBinary = byteTest.test( unit[4] ) ? 1 : 0; + return num * prefixes[ base ][ isBinary ]; } } } - return n; + return num; }, type: 'numeric' }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js index 9284803..fddd039 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js @@ -17,7 +17,7 @@ ts.alignChar = { column : this.column, align : $this.attr(wo.alignChar_charAttrib), alignIndex : parseInt( $this.attr(wo.alignChar_indexAttrib) || 0, 10), - adjust : parseFloat($this.attr(wo.alignChar_adjustAttrib)) || 0, + adjust : parseFloat($this.attr(wo.alignChar_adjustAttrib)) || 0 }; vars.regex = new RegExp('\\' + vars.align, 'g'); if (typeof vars.align !== 'undefined') { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index 560c6d0..2c519fc 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -27,8 +27,7 @@ tsColSel = ts.columnSelector = { } // unique table class name - c.tableId = 'tablesorter' + new Date().getTime(); - c.$table.addClass( c.tableId ); + c.$table.addClass( c.namespace.slice(1) + 'columnselector' ); // build column selector/state array colSel = c.selector = { $container : $(wo.columnSelector_container || '<div>') }; @@ -205,9 +204,9 @@ tsColSel = ts.columnSelector = { }, updateBreakpoints: function(c, wo) { - var priority, column, breaks, + var priority, column, breaks, temp, colSel = c.selector, - prefix = '.' + c.tableId, + prefix = c.namespace + 'columnselector', mediaAll = [], breakpts = ''; if (wo.columnSelector_mediaquery && !colSel.auto) { @@ -222,9 +221,12 @@ tsColSel = ts.columnSelector = { breaks = []; c.$headers.filter('[' + wo.columnSelector_priority + '=' + (priority + 1) + ']').each(function(){ column = parseInt($(this).attr('data-column'), 10) + 1; - breaks.push(prefix + ' col:nth-child(' + column + ')'); - breaks.push(prefix + ' tr th:nth-child(' + column + ')'); - breaks.push(prefix + ' tr td:nth-child(' + column + ')'); + temp = ' col:nth-child(' + column + ')'; + breaks.push(prefix + temp + ',' + prefix + '_extra_table' + temp); + temp = ' tr th:nth-child(' + column + ')'; + breaks.push(prefix + temp + ',' + prefix + '_extra_table' + temp); + temp = ' tr td:nth-child(' + column + ')'; + breaks.push(prefix + temp + ',' + prefix + '_extra_table' + temp); }); if (breaks.length) { mediaAll = mediaAll.concat( breaks ); @@ -247,16 +249,19 @@ tsColSel = ts.columnSelector = { if (wo.columnSelector_mediaquery && c.selector.auto || c.selector.isInitializing) { return; } - var column, + var column, temp, colSel = c.selector, styles = [], - prefix = '.' + c.tableId; + prefix = c.namespace + 'columnselector'; colSel.$container.find('input[data-column]').filter('[data-column!="auto"]').each(function(){ if (!this.checked) { column = parseInt( $(this).attr('data-column'), 10 ) + 1; - styles.push(prefix + ' col:nth-child(' + column + ')'); - styles.push(prefix + ' tr th:nth-child(' + column + ')'); - styles.push(prefix + ' tr td:nth-child(' + column + ')'); + temp = ' col:nth-child(' + column + ')'; + styles.push(prefix + temp + ',' + prefix + '_extra_table' + temp); + temp = ' tr th:nth-child(' + column + ')'; + styles.push(prefix + temp + ',' + prefix + '_extra_table' + temp); + temp = ' tr td:nth-child(' + column + ')'; + styles.push(prefix + temp + ',' + prefix + '_extra_table' + temp); } $(this).toggleClass( wo.columnSelector_cssChecked, this.checked ); }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js index a0d519a..a493c44 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js @@ -13,7 +13,8 @@ var tse = $.tablesorter.editable = { lastEdited: 'tseditable-last-edited-cell', editComplete: function( c, wo, $cell, refocus ) { - $cell + c.$table + .find( '.' + tse.lastEdited ) .removeClass( tse.lastEdited ) .trigger( wo.editable_editComplete, [ c ] ); // restore focus last cell after updating @@ -139,6 +140,7 @@ var tse = $.tablesorter.editable = { .on( 'focus' + namespace, '[contenteditable]', function( e ) { clearTimeout( $( this ).data( 'timer' ) ); c.$table.data( 'contentFocused', e.target ); + c.table.isUpdating = true; // prevent sorting while editing var $this = $( this ), selAll = wo.editable_selectAll, column = $this.closest( 'td' ).index(), @@ -150,7 +152,7 @@ var tse = $.tablesorter.editable = { $this .off( 'keydown' + namespace ) .on( 'keydown' + namespace, function( e ){ - if ( wo.editable_enterToAccept && e.which === 13 ) { + if ( wo.editable_enterToAccept && e.which === 13 && !e.shiftKey ) { e.preventDefault(); } }); @@ -184,10 +186,11 @@ var tse = $.tablesorter.editable = { // user cancelled $this.html( $this.data( 'original' ) ).trigger( 'blur' + namespace ); c.$table.data( 'contentFocused', false ); + c.table.isUpdating = false; return false; } // accept on enter ( if set ), alt-enter ( always ) or if autoAccept is set and element is blurred or unfocused - t = e.which === 13 && ( wo.editable_enterToAccept || e.altKey ) || wo.editable_autoAccept && e.type !== 'keydown'; + t = e.which === 13 && !e.shiftKey && ( wo.editable_enterToAccept || e.altKey ) || wo.editable_autoAccept && e.type !== 'keydown'; // change if new or user hits enter ( if option set ) if ( t && $this.data( 'before' ) !== txt ) { @@ -212,11 +215,11 @@ var tse = $.tablesorter.editable = { if ( wo.editable_autoResort ) { setTimeout( function() { c.$table.trigger( 'sorton', [ c.sortList, function() { - tse.editComplete( c, wo, c.$table.find( '.' + tse.lastEdited ), true ); + tse.editComplete( c, wo, c.$table.data( 'contentFocused' ), true ); }, true ] ); }, 10 ); } else { - tse.editComplete( c, wo, c.$table.find( '.' + tse.lastEdited ) ); + tse.editComplete( c, wo, c.$table.data( 'contentFocused' ) ); } } ] ); return false; @@ -224,6 +227,8 @@ var tse = $.tablesorter.editable = { } else if ( !valid && e.type !== 'keydown' ) { clearTimeout( $this.data( 'timer' ) ); $this.data( 'timer', setTimeout( function() { + c.table.isUpdating = false; // clear flag or sorting will be disabled + if ( $.isFunction( wo.editable_blur ) ) { txt = $this.html(); wo.editable_blur( wo.editable_trimContent ? $.trim( txt ) : txt, column, $this ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index a7584c0..3436e4c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -103,6 +103,66 @@ ts.filter = { // data.index = column index; table = table element ( DOM ) // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class ) types: { + or : function( c, data, vars ) { + if ( /\|/.test( data.iFilter ) || ts.filter.regex.orSplit.test( data.filter ) ) { + var indx, filterMatched, txt, query, regex, + // duplicate data but split filter + data2 = $.extend( {}, data ), + index = data.index, + parsed = data.parsed[ index ], + filter = data.filter.split( ts.filter.regex.orSplit ), + iFilter = data.iFilter.split( ts.filter.regex.orSplit ), + len = filter.length; + for ( indx = 0; indx < len; indx++ ) { + data2.nestedFilters = true; + data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); + data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); + query = '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')'; + regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); + // filterMatched = data2.filter === '' && indx > 0 ? true + // look for an exact match with the 'or' unless the 'filter-match' class is found + filterMatched = regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ); + if ( filterMatched ) { + return filterMatched; + } + } + // may be null from processing types + return filterMatched || false; + } + return null; + }, + // Look for an AND or && operator ( logical and ) + and : function( c, data, vars ) { + if ( ts.filter.regex.andTest.test( data.filter ) ) { + var indx, filterMatched, result, txt, query, regex, + // duplicate data but split filter + data2 = $.extend( {}, data ), + index = data.index, + parsed = data.parsed[ index ], + filter = data.filter.split( ts.filter.regex.andSplit ), + iFilter = data.iFilter.split( ts.filter.regex.andSplit ), + len = filter.length; + for ( indx = 0; indx < len; indx++ ) { + data2.nestedFilters = true; + data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); + data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); + query = ( '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' ) + // replace wild cards since /(a*)/i will match anything + .replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ); + regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); + // look for an exact match with the 'and' unless the 'filter-match' class is found + result = ( regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ) ); + if ( indx === 0 ) { + filterMatched = result; + } else { + filterMatched = filterMatched && result; + } + } + // may be null from processing types + return filterMatched || false; + } + return null; + }, // Look for regex regex: function( c, data ) { if ( ts.filter.regex.regex.test( data.filter ) ) { @@ -190,23 +250,6 @@ ts.filter = { } return null; }, - // Look for an AND or && operator ( logical and ) - and : function( c, data ) { - if ( ts.filter.regex.andTest.test( data.filter ) ) { - var index = data.index, - parsed = data.parsed[index], - query = data.iFilter.split( ts.filter.regex.andSplit ), - result = data.iExact.search( $.trim( ts.filter.parseFilter( c, query[0], index, parsed ) ) ) >= 0, - indx = query.length - 1; - while ( result && indx ) { - result = result && - data.iExact.search( $.trim( ts.filter.parseFilter( c, query[indx], index, parsed ) ) ) >= 0; - indx--; - } - return result; - } - return null; - }, // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu! range : function( c, data ) { if ( ts.filter.regex.toTest.test( data.iFilter ) ) { @@ -243,24 +286,20 @@ ts.filter = { }, // Look for wild card: ? = single, * = multiple, or | = logical OR wild : function( c, data ) { - if ( /[\?\*\|]/.test( data.iFilter ) || ts.filter.regex.orReplace.test( data.filter ) ) { + if ( /[\?\*\|]/.test( data.iFilter ) ) { var index = data.index, parsed = data.parsed[ index ], - txt = data.iFilter.replace( ts.filter.regex.orReplace, '|' ), - query = '' + ( ts.filter.parseFilter( c, txt, index, parsed ) || '' ); + query = '' + ( ts.filter.parseFilter( c, data.iFilter, index, parsed ) || '' ); // look for an exact match with the 'or' unless the 'filter-match' class is found - if ( !c.$headerIndexed[ index ].hasClass( 'filter-match' ) && /\|/.test( query ) ) { - // show all results while using filter match. Fixes #727 - if ( query[ query.length - 1 ] === '|' ) { - query += '*'; - } - query = data.anyMatch && $.isArray( data.rowArray ) ? - '(' + query + ')' : - '^(' + query + ')$'; + if ( !/\?\*/.test( query ) && data.nestedFilters ) { + query = data.isMatch ? query : '^(' + query + ')$'; } // parsing the filter may not work properly when using wildcards =/ - return new RegExp( query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ) ) - .test( data.iExact ); + return new RegExp( + query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ), + c.widgetOptions.filter_ignoreCase ? 'i' : '' + ) + .test( data.exact ); } return null; }, @@ -314,7 +353,7 @@ ts.filter = { toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)' ,'gi' ), andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), - orReplace : new RegExp( '\\s+(' + ts.language.or + ')\\s+', 'gi' ), + orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ), iQuery : new RegExp( val, 'i' ), igQuery : new RegExp( val, 'ig' ) }); @@ -534,7 +573,6 @@ ts.filter = { } } }, - setDefaults: function( table, c, wo ) { var isArray, saved, indx, col, $filters, // get current ( default ) filters @@ -761,14 +799,13 @@ ts.filter = { } }, hideFilters: function( table, c ) { - var $filterRow, $filterRow2, timer; - $( table ) + var timer; + c.$table .find( '.' + tscss.filterRow ) - .addClass( tscss.filterRowHide ) .bind( 'mouseenter mouseleave', function( e ) { // save event object - http://bugs.jquery.com/ticket/12140 - var event = e; - $filterRow = $( this ); + var event = e, + $filterRow = $( this ); clearTimeout( timer ); timer = setTimeout( function() { if ( /enter|over/.test( event.type ) ) { @@ -786,13 +823,14 @@ ts.filter = { }, 200 ); }) .find( 'input, select' ).bind( 'focus blur', function( e ) { - $filterRow2 = $( this ).closest( 'tr' ); + var event = e, + $row = $( this ).closest( 'tr' ); clearTimeout( timer ); - var event = e; timer = setTimeout( function() { + clearTimeout( timer ); // don't hide row if any filter has a value if ( ts.getFilters( c.$table ).join( '' ) === '' ) { - $filterRow2.toggleClass( tscss.filterRowHide, event.type === 'focus' ); + $row.toggleClass( tscss.filterRowHide, event.type !== 'focus' ); } }, 200 ); }); @@ -826,7 +864,7 @@ ts.filter = { return $( b ).attr( 'data-lastSearchTime' ) - $( a ).attr( 'data-lastSearchTime' ); }); } - return $(); + return $input || $(); }, multipleColumns: function( c, $input ) { // look for multiple columns '1-3,4-6,8' in data-column @@ -879,8 +917,22 @@ ts.filter = { } return columns; }, + processTypes: function( c, data, vars ) { + var ffxn, + filterMatched = null, + matches = null; + for ( ffxn in ts.filter.types ) { + if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) { + matches = ts.filter.types[ffxn]( c, data, vars ); + if ( matches !== null ) { + filterMatched = matches; + } + } + } + return filterMatched; + }, processRow: function( c, data, vars ) { - var $cell, columnIndex, hasSelect, matches, result, val, filterMatched, excludeMatch, + var columnIndex, hasSelect, result, val, filterMatched, fxn, ffxn, txt, regex = ts.filter.regex, wo = c.widgetOptions, @@ -891,6 +943,7 @@ ts.filter = { // look for multiple columns '1-3,4-6,8' columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); data.anyMatch = true; + data.isMatch = true; data.rowArray = data.$cells.map( function( i ) { if ( $.inArray( i, columnIndex ) > -1 ) { if ( data.parsed[ i ] ) { @@ -910,16 +963,10 @@ ts.filter = { data.exact = data.rowArray.join( ' ' ); data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' ); - filterMatched = null; - matches = null; - for ( ffxn in ts.filter.types ) { - if ( $.inArray( ffxn, vars.noAnyMatch ) < 0 && matches === null ) { - matches = ts.filter.types[ffxn]( c, data ); - if ( matches !== null ) { - filterMatched = matches; - } - } - } + + vars.excludeMatch = vars.noAnyMatch; + filterMatched = ts.filter.processTypes( c, data, vars ); + if ( filterMatched !== null ) { showRow = filterMatched; } else { @@ -946,7 +993,7 @@ ts.filter = { data.index = columnIndex; // filter types to exclude, per column - excludeMatch = vars.excludeFilter[ columnIndex ]; + vars.excludeMatch = vars.excludeFilter[ columnIndex ]; // ignore if filter is empty or disabled if ( data.filter ) { @@ -960,6 +1007,9 @@ ts.filter = { } data.iExact = !regex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; + + data.isMatch = c.$headerIndexed[ data.index ].hasClass( 'filter-match' ); + result = showRow; // if showRow is true, show that row // in case select filter option has a different value vs text 'a - z|A through Z' @@ -984,13 +1034,12 @@ ts.filter = { // data.filter = case sensitive data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter; fxn = vars.functions[ columnIndex ]; - $cell = c.$headerIndexed[ columnIndex ]; - hasSelect = $cell.hasClass( 'filter-select' ); + hasSelect = c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' ); filterMatched = null; if ( fxn || ( hasSelect && val ) ) { if ( fxn === true || hasSelect ) { // default selector uses exact match unless 'filter-match' class is found - filterMatched = $cell.hasClass( 'filter-match' ) ? + filterMatched = data.isMatch ? data.iExact.search( data.iFilter ) >= 0 : data.filter === data.exact; } else if ( typeof fxn === 'function' ) { @@ -1007,15 +1056,7 @@ ts.filter = { if ( filterMatched === null ) { // cycle through the different filters // filters return a boolean or null if nothing matches - matches = null; - for ( ffxn in ts.filter.types ) { - if ( $.inArray( ffxn, excludeMatch ) < 0 && matches === null ) { - matches = ts.filter.types[ ffxn ]( c, data ); - if ( matches !== null ) { - filterMatched = matches; - } - } - } + filterMatched = ts.filter.processTypes( c, data, vars ); if ( filterMatched !== null ) { result = filterMatched; // Look for match, and add child row data for matching @@ -1049,7 +1090,7 @@ ts.filter = { anyMatch: false, filters: filters, // regex filter type cache - filter_regexCache : [], + filter_regexCache : [] }, vars = { // anyMatch really screws up with these types of filters @@ -1270,7 +1311,7 @@ ts.filter = { }, getOptionSource: function( table, column, onlyAvail ) { table = $( table )[0]; - var cts, indx, len, + var cts, txt, indx, len, c = table.config, wo = c.widgetOptions, parsed = [], @@ -1315,11 +1356,13 @@ ts.filter = { len = arry.length; // parse select option values for ( indx = 0; indx < len; indx++ ) { + txt = arry[ indx ]; // parse array data using set column parser; this DOES NOT pass the original // table cell to the parser format function parsed.push({ - t : arry[ indx ], - p : c.parsers && c.parsers[ column ].format( arry[ indx ], table, [], column ) + t : txt, + // check parser length - fixes #934 + p : c.parsers && c.parsers.length && c.parsers[ column ].format( txt, table, [], column ) || txt }); } @@ -1509,8 +1552,8 @@ ts.getFilters = function( table, getRaw, setFilters, skipFirst ) { $column = ts.filter.getLatestSearch( $column ); if ( $.isArray( setFilters ) ) { // skip first ( latest input ) to maintain cursor position while typing - if ( skipFirst ) { - $column.slice( 1 ); + if ( skipFirst && $column.length > 1 ) { + $column = $column.slice( 1 ); } if ( i === c.columns ) { // prevent data-column='all' from filling data-column='0,1' ( etc ) diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index cdf5e82..9415afb 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -31,7 +31,8 @@ output = ts.output = { init : function(c) { c.$table .off(output.event) - .on(output.event, function(){ + .on(output.event, function( e ) { + e.stopPropagation(); // explicitly use table.config.widgetOptions because we want // the most up-to-date values; not the "wo" from initialization output.process(c, c.widgetOptions); @@ -39,81 +40,101 @@ output = ts.output = { }, processRow: function(c, $rows, isHeader, isJSON) { - var $this, row, col, rowlen, collen, txt, + var $cell, $cells, cellsLen, rowIndex, row, col, indx, rowspanLen, colspanLen, txt, wo = c.widgetOptions, tmpRow = [], dupe = wo.output_duplicateSpans, addSpanIndex = isHeader && isJSON && wo.output_headerRows && $.isFunction(wo.output_callbackJSON), - cellIndex = 0; - $rows.each(function(rowIndex) { + cellIndex = 0, + rowsLength = $rows.length; + + for ( rowIndex = 0; rowIndex < rowsLength; rowIndex++ ) { if (!tmpRow[rowIndex]) { tmpRow[rowIndex] = []; } cellIndex = 0; - $(this).children().each(function(){ - $this = $(this); + $cells = $rows.eq( rowIndex ).children(); + cellsLen = $cells.length; + for ( indx = 0; indx < cellsLen; indx++ ) { + $cell = $cells.eq( indx ); // process rowspans - if ($this.filter('[rowspan]').length) { - rowlen = parseInt( $this.attr('rowspan'), 10) - 1; - txt = output.formatData( wo, $this.attr(wo.output_dataAttrib) || $this.html(), isHeader ); - for (row = 1; row <= rowlen; row++) { + if ($cell.filter('[rowspan]').length) { + rowspanLen = parseInt( $cell.attr('rowspan'), 10) - 1; + txt = output.formatData( wo, $cell, isHeader ); + for (row = 1; row <= rowspanLen; row++) { if (!tmpRow[rowIndex + row]) { tmpRow[rowIndex + row] = []; } tmpRow[rowIndex + row][cellIndex] = isHeader ? txt : dupe ? txt : ''; } } // process colspans - if ($this.filter('[colspan]').length) { - collen = parseInt( $this.attr('colspan'), 10) - 1; - txt = output.formatData( wo, $this.attr(wo.output_dataAttrib) || $this.html(), isHeader ); - for (col = 1; col <= collen; col++) { + if ($cell.filter('[colspan]').length) { + colspanLen = parseInt( $cell.attr('colspan'), 10) - 1; + // allow data-attribute to be an empty string + txt = output.formatData( wo, $cell, isHeader ); + for (col = 1; col <= colspanLen; col++) { // if we're processing the header & making JSON, the header names need to be unique - if ($this.filter('[rowspan]').length) { - rowlen = parseInt( $this.attr('rowspan'), 10); - for (row = 0; row < rowlen; row++) { + if ($cell.filter('[rowspan]').length) { + rowspanLen = parseInt( $cell.attr('rowspan'), 10); + for (row = 0; row < rowspanLen; row++) { if (!tmpRow[rowIndex + row]) { tmpRow[rowIndex + row] = []; } tmpRow[rowIndex + row][cellIndex + col] = addSpanIndex ? - wo.output_callbackJSON($this, txt, cellIndex + col) || txt + '(' + (cellIndex + col) + ')' : isHeader ? txt : dupe ? txt : ''; + wo.output_callbackJSON($cell, txt, cellIndex + col) || + txt + '(' + (cellIndex + col) + ')' : isHeader ? txt : dupe ? txt : ''; } } else { tmpRow[rowIndex][cellIndex + col] = addSpanIndex ? - wo.output_callbackJSON($this, txt, cellIndex + col) || txt + '(' + (cellIndex + col) + ')' : isHeader ? txt : dupe ? txt : ''; + wo.output_callbackJSON($cell, txt, cellIndex + col) || + txt + '(' + (cellIndex + col) + ')' : isHeader ? txt : dupe ? txt : ''; } } } - // don't include hidden columns, unless option is set - if ( !wo.output_hiddenColumns && $this.css('display') !== 'none' ) { - // skip column if already defined - while (typeof tmpRow[rowIndex][cellIndex] !== 'undefined') { cellIndex++; } - tmpRow[rowIndex][cellIndex] = tmpRow[rowIndex][cellIndex] || - output.formatData( wo, $this.attr(wo.output_dataAttrib) || $this.html(), isHeader ); - cellIndex++; - } - }); - }); - return tmpRow; + // skip column if already defined + while (typeof tmpRow[rowIndex][cellIndex] !== 'undefined') { cellIndex++; } + + tmpRow[rowIndex][cellIndex] = tmpRow[rowIndex][cellIndex] || + output.formatData( wo, $cell, isHeader ); + cellIndex++; + } + } + return ts.output.removeColumns( c, wo, tmpRow ); }, - ignoreColumns : function(wo, data) { - // ignore columns -> remove data from built array (because we've already processed any rowspan/colspan) - $.each( data, function(indx, val){ - data[indx] = $.grep(val, function(v, cellIndx){ - return $.inArray(cellIndx, wo.output_ignoreColumns) < 0; - }); - }); + // remove hidden/ignored columns + removeColumns : function( c, wo, arry ) { + var rowIndex, row, colIndex, + data = [], + len = arry.length; + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { + row = arry[ rowIndex ]; + data[ rowIndex ] = []; + for ( colIndex = 0; colIndex < c.columns; colIndex++ ) { + if ( !wo.output_hiddenColumnArray[ colIndex ] ) { + data[ rowIndex ].push( row[ colIndex ] ); + } + } + } return data; }, process : function(c, wo) { - var mydata, $this, $rows, headers, csvData, len, + var mydata, $this, $rows, headers, csvData, len, rowsLen, tmp, hasStringify = window.JSON && JSON.hasOwnProperty('stringify'), indx = 0, tmpData = (wo.output_separator || ',').toLowerCase(), outputJSON = tmpData === 'json', outputArray = tmpData === 'array', separator = outputJSON || outputArray ? ',' : wo.output_separator, + saveRows = wo.output_saveRows, $el = c.$table; // regex to look for the set separator or HTML wo.output_regex = new RegExp('(' + (/\\/.test(separator) ? '\\' : '' ) + separator + ')' ); + // make a list of hidden columns + wo.output_hiddenColumnArray = []; + for ( indx = 0; indx < c.columns; indx++ ) { + wo.output_hiddenColumnArray[ indx ] = $.inArray( indx, wo.output_ignoreColumns ) > -1 || + c.$headerIndexed[ indx ].css( 'display' ) === 'none'; + } + // get header cells $this = $el.find('thead tr:visible').not('.' + (ts.css.filterRow || 'tablesorter-filter-row') ); headers = output.processRow(c, $this, true, outputJSON); @@ -121,36 +142,40 @@ output = ts.output = { // all tbody rows $rows = $el.children('tbody').children('tr'); + // get (f)iltered, (v)isible, all rows (look for the first letter only), or jQuery filter selector + $rows = /^f/.test(saveRows) ? $rows.not('.' + (wo.filter_filteredRow || 'filtered') ) : + /^v/.test(saveRows) ? $rows.filter(':visible') : + // look for '.' (class selector), '#' (id selector), + // ':' (basic filters, e.g. ':not()') or '[' (attribute selector start) + /^[.#:\[]/.test(saveRows) ? $rows.filter(saveRows) : + // default to all rows + $rows; + + // process to array of arrays + csvData = output.processRow(c, $rows); + if (wo.output_includeFooter) { // clone, to force the tfoot rows to the end of this selection of rows // otherwise they appear after the thead (the order in the HTML) - $rows = $rows.add( $el.children('tfoot').children('tr').clone() ); + csvData = csvData.concat( output.processRow( c, $el.children('tfoot').children('tr:visible') ) ); } - // get (f)iltered, (v)isible or all rows (look for the first letter only) - $rows = /f/.test(wo.output_saveRows) ? $rows.not('.' + (wo.filter_filteredRow || 'filtered') ) : - /v/.test(wo.output_saveRows) ? $rows.filter(':visible') : $rows; - - // process to array of arrays - csvData = output.processRow(c, $rows); len = headers.length; - if (wo.output_ignoreColumns.length) { - headers = output.ignoreColumns(wo, headers); - csvData = output.ignoreColumns(wo, csvData); - } - if (outputJSON) { tmpData = []; - $.each( csvData, function(indx, val){ + rowsLen = csvData.length; + for ( indx = 0; indx < rowsLen; indx++ ) { // multiple header rows & output_headerRows = true, pick the last row... - tmpData.push( output.row2Hash( headers[ (len > 1 && wo.output_headerRows) ? indx % len : len - 1], val ) ); - }); + tmp = headers[ ( len > 1 && wo.output_headerRows ) ? indx % len : len - 1 ]; + tmpData.push( output.row2Hash( tmp, csvData[ indx ] ) ); + } // requires JSON stringify; if it doesn't exist, the output will show [object Object],... in the output window mydata = hasStringify ? JSON.stringify(tmpData) : tmpData; } else { - tmpData = output.row2CSV(wo, wo.output_headerRows ? headers : [ headers[ (len > 1 && wo.output_headerRows) ? indx % len : len - 1] ], outputArray) + tmp = [ headers[ ( len > 1 && wo.output_headerRows ) ? indx % len : len - 1 ] ]; + tmpData = output.row2CSV(wo, wo.output_headerRows ? headers : tmp, outputArray) .concat( output.row2CSV(wo, csvData, outputArray) ); // stringify the array; if stringify doesn't exist the array will be flattened @@ -174,30 +199,33 @@ output = ts.output = { rowLen = tmpRow.length; for (rowIndex = 0; rowIndex < rowLen; rowIndex++) { // remove any blank rows - tmp = tmpRow[rowIndex].join('').replace(/\"/g,''); - if (tmpRow[rowIndex].length > 0 && tmp !== '') { + tmp = ( tmpRow[rowIndex] || [] ).join('').replace(/\"/g,''); + if ( ( tmpRow[rowIndex] || [] ).length > 0 && tmp !== '' ) { csvData[csvData.length] = outputArray ? tmpRow[rowIndex] : tmpRow[rowIndex].join(wo.output_separator); } } return csvData; }, - row2Hash : function(keys, values) { - var json = {}; - $.each(values, function(indx, val) { + row2Hash : function( keys, values ) { + var indx, + json = {}, + len = values.length; + for ( indx = 0; indx < len; indx++ ) { if ( indx < keys.length ) { - json[ keys[indx] ] = val; + json[ keys[ indx ] ] = values[ indx ]; } - }); + } return json; }, - formatData : function(wo, input, isHeader) { - var txt, + formatData : function(wo, $el, isHeader) { + var attr = $el.attr(wo.output_dataAttrib), + txt = typeof attr !== 'undefined' ? attr : $el.html(), quotes = (wo.output_separator || ',').toLowerCase(), separator = quotes === 'json' || quotes === 'array', // replace " with “ if undefined - result = input.replace(/\"/g, wo.output_replaceQuote || '\u201c'); + result = txt.replace(/\"/g, wo.output_replaceQuote || '\u201c'); // replace line breaks with \\n & tabs with \\t if (!wo.output_trimSpaces) { result = result.replace(output.regexBR, output.replaceCR).replace(/\t/g, output.replaceTab); @@ -269,7 +297,8 @@ output = ts.output = { // Dispatching click event; using $(link).trigger() won't work if (document.createEvent) { e = document.createEvent('MouseEvents'); - // event.initMouseEvent(type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget); + // event.initMouseEvent(type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, + // ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget); e.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); link.dispatchEvent(e); } @@ -300,7 +329,7 @@ ts.addWidget({ output_dataAttrib : 'data-name', // header attrib containing modified header name output_headerRows : false, // if true, include multiple header rows (JSON only) output_delivery : 'popup', // popup, download - output_saveRows : 'filtered', // all, visible or filtered + output_saveRows : 'filtered', // (a)ll, (v)isible, (f)iltered or jQuery filter selector output_duplicateSpans: true, // duplicate output data in tbody colspan/rowspan output_replaceQuote : '\u201c;', // left double quote output_includeHTML : false, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 9b65bef..a7b1345 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -661,7 +661,7 @@ tsp = ts.pager = { c.totalRows = p.totalRows = result.total; c.filteredRows = p.filteredRows = typeof result.filteredRows !== 'undefined' ? result.filteredRows : result.total; th = result.headers; - d = result.rows; + d = result.rows || []; } else { // allow [ total, rows, headers ] or [ rows, total, headers ] t = isNaN(result[0]) && !isNaN(result[1]); @@ -670,7 +670,8 @@ tsp = ts.pager = { p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count; // can't set filtered rows when returning an array c.totalRows = c.filteredRows = p.filteredRows = p.totalRows; - d = p.totalRows === 0 ? [""] : result[t ? 0 : 1] || []; // row data + // set row data to empty array if nothing found - see http://stackoverflow.com/q/30875583/145346 + d = p.totalRows === 0 ? [] : result[t ? 0 : 1] || []; // row data th = result[2]; // headers } l = d && d.length; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js index fcabde9..00c17ea 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js @@ -17,7 +17,7 @@ var h = '', i, $tr, l, skip; // cache and collect all TH headers if (!wo.repeatHeaders) { - h = '<tr class="repeated-header remove-me">'; + h = '<tr class="repeated-header ' + c.selectorRemove.slice(1) + '">'; for (i = 0; i < c.columns; i++) { // only get the headerContent text h += '<th>' + $.trim( c.$headers.eq(i).text() ) + '</th>'; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js index a6733e9..ac91168 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js @@ -1,4 +1,5 @@ -/*! Widget: resizable - updated 5/17/2015 (v2.22.0) */ +/*! Widget: resizable - updated 6/26/2015 (v2.22.2) */ +/*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; var ts = $.tablesorter || {}; @@ -17,8 +18,8 @@ $(function(){ '-khtml-user-select: none; -webkit-user-select: none; user-select: none; }' + '.' + ts.css.resizableContainer + ' { position: relative; height: 1px; }' + // make handle z-index > than stickyHeader z-index, so the handle stays above sticky header - '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px; top: 1px;' + - 'cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' + + '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px;' + + 'top: 1px; cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' + '</style>'; $(s).appendTo('body'); }); @@ -27,34 +28,69 @@ ts.resizable = { init : function( c, wo ) { if ( c.$table.hasClass( 'hasResizable' ) ) { return; } c.$table.addClass( 'hasResizable' ); - ts.resizableReset( c.table, true ); // set default widths + + var noResize, $header, column, storedSizes, tmp, + $table = c.$table, + $parent = $table.parent(), + marginTop = parseInt( $table.css( 'margin-top' ), 10 ), // internal variables - wo.resizable_ = { - $wrap : c.$table.parent(), + vars = wo.resizable_ = { + useStorage : ts.storage && wo.resizable !== false, + $wrap : $parent, mouseXPosition : 0, $target : null, $next : null, - overflow : c.$table.parent().css('overflow') === 'auto', - fullWidth : Math.abs(c.$table.parent().width() - c.$table.width()) < 20, + overflow : $parent.css('overflow') === 'auto' || + $parent.css('overflow') === 'scroll' || + $parent.css('overflow-x') === 'auto' || + $parent.css('overflow-x') === 'scroll', storedSizes : [] }; - var noResize, $header, column, storedSizes, - marginTop = parseInt( c.$table.css( 'margin-top' ), 10 ); + // set default widths + ts.resizableReset( c.table, true ); + + // now get measurements! + vars.tableWidth = $table.width(); + // attempt to autodetect + vars.fullWidth = Math.abs( $parent.width() - vars.tableWidth ) < 20; - wo.resizable_.storedSizes = storedSizes = ( ( ts.storage && wo.resizable !== false ) ? + /* + // Hacky method to determine if table width is set to "auto" + // http://stackoverflow.com/a/20892048/145346 + if ( !vars.fullWidth ) { + tmp = $table.width(); + $header = $table.wrap('<span>').parent(); // temp variable + storedSizes = parseInt( $table.css( 'margin-left' ), 10 ) || 0; + $table.css( 'margin-left', storedSizes + 50 ); + vars.tableWidth = $header.width() > tmp ? 'auto' : tmp; + $table.css( 'margin-left', storedSizes ? storedSizes : '' ); + $header = null; + $table.unwrap('<span>'); + } + */ + + if ( vars.useStorage && vars.overflow ) { + // save table width + ts.storage( c.table, 'tablesorter-table-original-css-width', vars.tableWidth ); + tmp = ts.storage( c.table, 'tablesorter-table-resized-width' ) || 'auto'; + ts.resizable.setWidth( $table, tmp, true ); + } + wo.resizable_.storedSizes = storedSizes = ( vars.useStorage ? ts.storage( c.table, ts.css.resizableStorage ) : [] ) || []; ts.resizable.setWidths( c, wo, storedSizes ); + ts.resizable.updateStoredSizes( c, wo ); wo.$resizable_container = $( '<div class="' + ts.css.resizableContainer + '">' ) .css({ top : marginTop }) - .insertBefore( c.$table ); + .insertBefore( $table ); // add container for ( column = 0; column < c.columns; column++ ) { $header = c.$headerIndexed[ column ]; - noResize = ts.getData( $header, ts.getColumnData( c.table, c.headers, column ), 'resizable' ) === 'false'; + tmp = ts.getColumnData( c.table, c.headers, column ); + noResize = ts.getData( $header, tmp, 'resizable' ) === 'false'; if ( !noResize ) { $( '<div class="' + ts.css.resizableHandle + '">' ) .appendTo( wo.$resizable_container ) @@ -66,37 +102,52 @@ ts.resizable = { .bind( 'selectstart', false ); } } - c.$table.one('tablesorter-initialized', function() { + $table.one('tablesorter-initialized', function() { ts.resizable.setHandlePosition( c, wo ); ts.resizable.bindings( this.config, this.config.widgetOptions ); }); }, - setWidth : function( $el, width ) { + updateStoredSizes : function( c, wo ) { + var column, $header, + len = c.columns, + vars = wo.resizable_; + vars.storedSizes = []; + for ( column = 0; column < len; column++ ) { + $header = c.$headerIndexed[ column ]; + vars.storedSizes[ column ] = $header.is(':visible') ? $header.width() : 0; + } + }, + + setWidth : function( $el, width, overflow ) { + // overflow tables need min & max width set as well $el.css({ 'width' : width, - 'min-width' : '', - 'max-width' : '' + 'min-width' : overflow ? width : '', + 'max-width' : overflow ? width : '' }); }, setWidths : function( c, wo, storedSizes ) { - var column, + var column, $temp, + vars = wo.resizable_, $extra = $( c.namespace + '_extra_headers' ), $col = c.$table.children( 'colgroup' ).children( 'col' ); - storedSizes = storedSizes || wo.resizable_.storedSizes || []; + storedSizes = storedSizes || vars.storedSizes || []; // process only if table ID or url match if ( storedSizes.length ) { for ( column = 0; column < c.columns; column++ ) { // set saved resizable widths - c.$headerIndexed[ column ].width( storedSizes[ column ] ); + ts.resizable.setWidth( c.$headerIndexed[ column ], storedSizes[ column ], vars.overflow ); if ( $extra.length ) { // stickyHeaders needs to modify min & max width as well - ts.resizable.setWidth( $extra.eq( column ).add( $col.eq( column ) ), storedSizes[ column ] ); + $temp = $extra.eq( column ).add( $col.eq( column ) ); + ts.resizable.setWidth( $temp, storedSizes[ column ], vars.overflow ); } } - if ( $( c.namespace + '_extra_table' ).length && !ts.hasWidget( c.table, 'scroller' ) ) { - ts.resizable.setWidth( $( c.namespace + '_extra_table' ), c.$table.outerWidth() ); + $temp = $( c.namespace + '_extra_table' ); + if ( $temp.length && !ts.hasWidget( c.table, 'scroller' ) ) { + ts.resizable.setWidth( $temp, c.$table.outerWidth(), vars.overflow ); } } }, @@ -156,7 +207,7 @@ ts.resizable = { var namespace = c.namespace + 'tsresize'; wo.$resizable_container.children().bind( 'mousedown', function( event ) { // save header cell and mouse position - var column, $this, + var column, vars = wo.resizable_, $extras = $( c.namespace + '_extra_headers' ), $header = $( event.target ).data( 'header' ); @@ -175,11 +226,7 @@ ts.resizable = { vars.next = column; vars.mouseXPosition = event.pageX; - vars.storedSizes = []; - for ( column = 0; column < c.columns; column++ ) { - $this = c.$headerIndexed[ column ]; - vars.storedSizes[ column ] = $this.is(':visible') ? $this.width() : 0; - } + ts.resizable.updateStoredSizes( c, wo ); ts.resizable.toggleTextSelection( c, true ); }); @@ -230,47 +277,51 @@ ts.resizable = { mouseMove : function( c, wo, event ) { if ( wo.resizable_.mouseXPosition === 0 || !wo.resizable_.$target ) { return; } // resize columns - var vars = wo.resizable_, + var column, + total = 0, + vars = wo.resizable_, $next = vars.$next, + tar = vars.storedSizes[ vars.target ], leftEdge = event.pageX - vars.mouseXPosition; - if ( vars.fullWidth ) { - vars.storedSizes[ vars.target ] += leftEdge; - vars.storedSizes[ vars.next ] -= leftEdge; - ts.resizable.setWidths( c, wo ); - - } else if ( vars.overflow ) { - c.$table.add( $( c.namespace + '_extra_table' ) ).width(function(i, w){ - return w + leftEdge; - }); + if ( vars.overflow ) { + if ( tar + leftEdge > 0 ) { + vars.storedSizes[ vars.target ] += leftEdge; + ts.resizable.setWidth( vars.$target, vars.storedSizes[ vars.target ], true ); + // update the entire table width + for ( column = 0; column < c.columns; column++ ) { + total += vars.storedSizes[ column ]; + } + ts.resizable.setWidth( c.$table.add( $( c.namespace + '_extra_table' ) ), total ); + } if ( !$next.length ) { // if expanding right-most column, scroll the wrapper vars.$wrap[0].scrollLeft = c.$table.width(); } + } else if ( vars.fullWidth ) { + vars.storedSizes[ vars.target ] += leftEdge; + vars.storedSizes[ vars.next ] -= leftEdge; + ts.resizable.setWidths( c, wo ); } else { vars.storedSizes[ vars.target ] += leftEdge; ts.resizable.setWidths( c, wo ); } vars.mouseXPosition = event.pageX; + // dynamically update sticky header widths + c.$table.trigger('stickyHeadersUpdate'); }, stopResize : function( c, wo ) { - var $this, column, - vars = wo.resizable_; - vars.storedSizes = []; - if ( ts.storage ) { - vars.storedSizes = []; - for ( column = 0; column < c.columns; column++ ) { - $this = c.$headerIndexed[ column ]; - vars.storedSizes[ column ] = $this.is(':visible') ? $this.width() : 0; - } - if ( wo.resizable !== false ) { - // save all column widths - ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); - } + var vars = wo.resizable_; + ts.resizable.updateStoredSizes( c, wo ); + if ( vars.useStorage ) { + // save all column widths + ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); + ts.storage( c.table, 'tablesorter-table-resized-width', c.$table.width() ); } vars.mouseXPosition = 0; vars.$target = vars.$next = null; - $(window).trigger('resize'); // will update stickyHeaders, just in case + // will update stickyHeaders, just in case, see #912 + c.$table.trigger('stickyHeadersUpdate'); } }; @@ -281,11 +332,12 @@ ts.addWidget({ id: "resizable", priority: 40, options: { - resizable : true, + resizable : true, // save column widths to storage resizable_addLastColumn : false, resizable_widths : [], resizable_throttle : false, // set to true (5ms) or any number 0-10 range - resizable_targetLast : false + resizable_targetLast : false, + resizable_fullWidth : null }, init: function(table, thisWidget, c, wo) { ts.resizable.init( c, wo ); @@ -309,19 +361,28 @@ ts.resizableReset = function( table, refreshing ) { $( table ).each(function(){ var index, $t, c = this.config, - wo = c && c.widgetOptions; + wo = c && c.widgetOptions, + vars = wo.resizable_; if ( table && c && c.$headerIndexed.length ) { + // restore the initial table width + if ( vars.overflow && vars.tableWidth ) { + ts.resizable.setWidth( c.$table, vars.tableWidth, true ); + if ( vars.useStorage ) { + ts.storage( table, 'tablesorter-table-resized-width', 'auto' ); + } + } for ( index = 0; index < c.columns; index++ ) { $t = c.$headerIndexed[ index ]; if ( wo.resizable_widths && wo.resizable_widths[ index ] ) { - $t.css( 'width', wo.resizable_widths[ index ] ); + ts.resizable.setWidth( $t, wo.resizable_widths[ index ], vars.overflow ); } else if ( !$t.hasClass( 'resizable-false' ) ) { // don't clear the width of any column that is not resizable - $t.css( 'width', '' ); + ts.resizable.setWidth( $t, '', vars.overflow ); } } + // reset stickyHeader widths - $( window ).trigger( 'resize' ); + c.$table.trigger( 'stickyHeadersUpdate' ); if ( ts.storage && !refreshing ) { ts.storage( this, ts.css.resizableStorage, {} ); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index cbad13b..a02e6ee 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -11,23 +11,22 @@ Resizable scroller widget for the jQuery tablesorter plugin - Version 2.0 - modified by Rob Garrison 4/12/2013; updated 3/5/2015 (v2.21.0) + Version 2.0 - modified by Rob Garrison 4/12/2013; + updated 3/5/2015 (v2.22.2) with lots of help from TheSin- Requires jQuery v1.7+ Requires the tablesorter plugin, v2.8+, available at http://mottie.github.com/tablesorter/docs/ Usage: - $(function() { - $('table.tablesorter').tablesorter({ widgets: ['zebra', 'scroller'], widgetOptions : { scroller_height : 300, // height of scroll window - scroller_barWidth : 18, // scroll bar width scroller_jumpToHeader : true, // header snap to browser top when scrolling the tbody + scroller_upAfterSort : true, // scroll tbody to top after sorting + scroller_fixedColumns : 0 // set number of fixed columns } }); - }); Website: www.tconnell.com @@ -47,8 +46,15 @@ $.extend( ts.css, { scrollerFixed : 'tablesorter-scroller-fixed', scrollerFixedPanel : 'tablesorter-scroller-fixed-panel', scrollerHasFix : 'tablesorter-scroller-has-fixed-columns', - scrollerReset : 'tablesorter-scroller-reset', - scrollerRtl : 'tablesorter-scroller-rtl' + scrollerHideColumn : 'tablesorter-scroller-hidden-column', + scrollerHideElement : 'tablesorter-scroller-hidden', + scrollerSpacerRow : 'tablesorter-scroller-spacer', + scrollerBarSpacer : 'tablesorter-scroller-bar-spacer', + scrollerAddedHeight : 'tablesorter-scroller-added-height', + scrollerHack : 'tablesorter-scroller-scrollbar-hack', + // class name on table cannot start with 'tablesorter-' or the + // suffix "scroller-rtl" will match as a theme name + scrollerRtl : 'ts-scroller-rtl' }); ts.addWidget({ @@ -56,18 +62,21 @@ ts.addWidget({ priority : 60, // run after the filter widget options : { scroller_height : 300, + // pop table header into view while scrolling up the page scroller_jumpToHeader : true, + // scroll tbody to top after sorting scroller_upAfterSort : true, - // set number of columns to fix + // set number of fixed columns scroller_fixedColumns : 0, // add hover highlighting to the fixed column (disable if it causes slowing) scroller_rowHighlight : 'hover', // add a fixed column overlay for styling scroller_addFixedOverlay : false, - // bar width is now calculated; set a value to override + // In tablesorter v2.19.0 the scroll bar width is auto-detected + // add a value here to override the auto-detected setting scroller_barWidth : null }, - format: function( table, c, wo ) { + format : function( table, c, wo ) { if ( !c.isScrolling ) { // initialize here instead of in widget init to give the // filter widget time to finish building the filter row @@ -81,59 +90,52 @@ ts.addWidget({ /* Add window resizeEnd event */ ts.window_resize = function() { - if ( this.resize_timer ) { - clearTimeout( this.resize_timer ); + if ( ts.timer_resize ) { + clearTimeout( ts.timer_resize ); } - this.resize_timer = setTimeout( function() { - $( this ).trigger( 'resizeEnd' ); + ts.timer_resize = setTimeout( function() { + $( window ).trigger( 'resizeEnd' ); }, 250 ); }; // Add extra scroller css $( function() { var style = '<style>' + - /* measure scroll bar width */ - '.' + tscss.scrollerWrap + 'Measure { width: 100px; height: 100px; overflow: scroll; position: absolute; top: -9999px; }' + - /* reset width to get accurate measurements after window resize */ - '.' + tscss.scrollerReset + ' { width: auto !important; min-width: auto !important; max-width: auto !important; }' + /* overall wrapper & table section wrappers */ '.' + tscss.scrollerWrap + ' { position: relative; overflow: hidden; }' + /* add border-box sizing to all scroller widget tables; see #135 */ '.' + tscss.scrollerWrap + ' * { box-sizing: border-box; }' + - '.' + tscss.scrollerHeader + ', .' + tscss.scrollerFooter + ' { overflow: hidden; }' + + '.' + tscss.scrollerHeader + ', .' + tscss.scrollerFooter + ' { position: relative; overflow: hidden; }' + '.' + tscss.scrollerHeader + ' table.' + tscss.table + ' { margin-bottom: 0; }' + - '.' + tscss.scrollerFooter + ' table.' + tscss.table + ' thead { visibility: hidden, height: 0; overflow: hidden; }' + - /* always leave the scroll bar visible for tbody, or table overflows into the scrollbar when height < max height (filtering) */ - '.' + tscss.scrollerTable + ' { overflow-y: scroll; }' + - '.' + tscss.scrollerTable + ' table.' + tscss.table + ' { border-top: 0; margin-top: 0; margin-bottom: 0; overflow-y: scroll; }' + - /* hide filter row in clones */ - '.' + tscss.scrollerTable + ' .' + ( tscss.filterRow || 'tablesorter-filter-row' ) + ',.' + tscss.scrollerFooter + ' .' + - ( tscss.filterRow || 'tablesorter-filter-row' ) + ',.' + tscss.scrollerTable + ' tfoot { display: none; }' + - /* visibly hide header rows in clones, so we can still set a width on it and still effect the rest of the column */ - '.' + tscss.scrollerTable + ' table.' + tscss.table + ' thead tr.' + tscss.headerRow + ' *, .' + tscss.scrollerFooter + - ' table.' + tscss.table + ' thead * { line-height: 0; height: 0; border: none; background-image: none; padding-top: 0;' + - ' padding-bottom: 0; margin-top: 0; margin-bottom: 0; overflow: hidden; }' + + /* always leave the scroll bar visible for tbody, or table overflows into the scrollbar + when height < max height (filtering) */ + '.' + tscss.scrollerTable + ' { position: relative; overflow: auto; }' + + '.' + tscss.scrollerTable + ' table.' + tscss.table + + ' { border-top: 0; margin-top: 0; margin-bottom: 0; overflow: hidden; }' + + /* hide footer in original table */ + '.' + tscss.scrollerTable + ' tfoot, .' + tscss.scrollerHideElement + ', .' + tscss.scrollerHideColumn + + ' { display: none; }' + /*** fixed column ***/ /* disable pointer-events on fixed column wrapper or the user can't interact with the horizontal scrollbar */ - '.' + tscss.scrollerFixed + ', .' + tscss.scrollerFixed + ' .' + tscss.scrollerFixedPanel + ' { pointer-events: none; }' + + '.' + tscss.scrollerFixed + ', .' + tscss.scrollerFixed + ' .' + tscss.scrollerFixedPanel + + ' { pointer-events: none; }' + /* enable pointer-events for fixed column children; see #135 & #878 */ '.' + tscss.scrollerFixed + ' > div { pointer-events: all; }' + '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixed + ' { position: absolute; top: 0; z-index: 1; left: 0 } ' + '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixed + '.' + tscss.scrollerRtl + ' { left: auto; right: 0 } ' + /* add horizontal scroll bar; set to "auto", see #135 */ - '.' + tscss.scrollerWrap + '.' + tscss.scrollerHasFix + ' > .' + tscss.scrollerTable + ' { overflow-x: auto; }' + - /* need to position the tbody & tfoot absolutely to hide the scrollbar & move the footer below the horizontal scrollbar */ + '.' + tscss.scrollerWrap + '.' + tscss.scrollerHasFix + ' > .' + tscss.scrollerTable + ' { overflow: auto; }' + + /* need to position the tbody & tfoot absolutely to hide the scrollbar & move the footer + below the horizontal scrollbar */ '.' + tscss.scrollerFixed + ' .' + tscss.scrollerFooter + ' { position: absolute; bottom: 0; }' + - /* hide fixed tbody scrollbar - see http://goo.gl/VsLe6n */ - '.' + tscss.scrollerFixed + ' .' + tscss.scrollerTable + ' { position: relative; left: 0; overflow-x: hidden; overflow-y: scroll; -ms-overflow-style: none; }' + + /* hide fixed tbody scrollbar - see http://goo.gl/VsLe6n - set overflow to auto here for mousewheel scroll */ + '.' + tscss.scrollerFixed + ' .' + tscss.scrollerTable + + ' { position: relative; left: 0; overflow: auto; -ms-overflow-style: none; }' + '.' + tscss.scrollerFixed + ' .' + tscss.scrollerTable + '::-webkit-scrollbar { display: none; }' + - /* remove right border of fixed header tables to hide the boundary */ - '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixed + ' table { border-right-color: transparent; padding-right: 0; }' + - '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixed + '.' + tscss.scrollerRtl + ' table { border-left-color: transparent; padding-left: 0; }' + - /*** fixed column panel ***/ - '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixedPanel + ' { position: absolute; top: 0; bottom: 0; z-index: 2; left: 0; right: 0; } ' + + '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixedPanel + + ' { position: absolute; top: 0; bottom: 0; z-index: 2; left: 0; right: 0; } ' + '</style>'; $( style ).appendTo( 'body' ); }); @@ -144,9 +146,18 @@ ts.scroller = { isFirefox : navigator.userAgent.toLowerCase().indexOf( 'firefox' ) > -1, // old IE needs a wrap to hide the fixed column scrollbar; http://stackoverflow.com/a/24408672/145346 isOldIE : document.all && !window.atob, - - hasScrollBar : function( $target ) { - return $target.get(0).scrollHeight > $target.height(); + isIE : ( document.all && !window.atob ) || navigator.appVersion.indexOf( 'Trident/' ) > 0, + // http://stackoverflow.com/questions/7944460/detect-safari-browser - needed to position scrolling body + // when the table is set up in RTL direction + isSafari : navigator.userAgent.toLowerCase().indexOf( 'safari' ) > -1 && + navigator.userAgent.toLowerCase().indexOf( 'chrome' ) === -1, + + hasScrollBar : function( $target, checkWidth ) { + if ( checkWidth ) { + return $target.get(0).scrollWidth > $target.width(); + } else { + return $target.get(0).scrollHeight > $target.height(); + } }, setWidth : function( $el, width ) { @@ -159,39 +170,56 @@ ts.scroller = { // modified from http://davidwalsh.name/detect-scrollbar-width getBarWidth : function() { - var $scrollDiv = $( '<div class="' + tscss.scrollerWrap + 'Measure">' ).appendTo( 'body' ), - div = $scrollDiv[ 0 ], - barWidth = div.offsetWidth - div.clientWidth; - $scrollDiv.remove(); + var $div = $( '<div>' ).css({ + 'position' : 'absolute', + 'top' : '-9999px', + 'left' : 0, + 'width' : '100px', + 'height' : '100px', + 'overflow' : 'scroll', + 'visibility' : 'hidden' + }).appendTo( 'body' ), + div = $div[0], + barWidth = div.offsetWidth - div.clientWidth; + $div.remove(); return barWidth; }, setup : function( c, wo ) { - var maxHt, tbHt, $hdr, $t, $hCells, $fCells, $tableWrap, + var maxHt, tbHt, $hdr, $t, $hCells, $fCells, $tableWrap, events, tmp, $win = $( window ), + tsScroller = ts.scroller, namespace = c.namespace + 'tsscroller', $foot = $(), // c.namespace contains a unique tablesorter ID, per table id = c.namespace.slice( 1 ) + 'tsscroller', $table = c.$table; - // force developer to set fixedWidth to maintain column widths + // force config.widthFixed option - this helps maintain proper alignment across cloned tables c.widthFixed = true; + + wo.scroller_calcWidths = []; + wo.scroller_saved = [ 0, 0 ]; + wo.scroller_isBusy = true; + + // set scrollbar width & allow setting width to zero + wo.scroller_barSetWidth = wo.scroller_barWidth !== null ? + wo.scroller_barWidth : + ( tsScroller.getBarWidth() || 15 ); + maxHt = wo.scroller_height || 300; - tbHt = $table.children( 'tbody' ).height(); - if ( tbHt !== 0 && maxHt > tbHt ) { maxHt = tbHt + 10; } // Table is less than h px - wo.scroller_$header = $hdr = $( '<table class="' + $table.attr( 'class' ) + '" cellpadding=0 cellspacing=0>' + - $table.children( 'thead' )[0].outerHTML + - '</table>' ) - .addClass( c.namespace.slice(1) + '_extra_table' ); + $hdr = $( '<table class="' + $table.attr( 'class' ) + '" cellpadding=0 cellspacing=0>' + + $table.children( 'thead' )[ 0 ].outerHTML + '</table>' ); + wo.scroller_$header = $hdr.addClass( c.namespace.slice( 1 ) + '_extra_table' ); $t = $table.children( 'tfoot' ); if ( $t.length ) { - $foot = $( '<table class="' + $table.attr('class') + '" cellpadding=0 cellspacing=0 style="margin-top:0"></table>' ) - .addClass( c.namespace.slice(1) + '_extra_table' ) - .append( $t.clone( true ) ) // maintain any bindings on the tfoot cells - .append( $table.children( 'thead' )[ 0 ].outerHTML ) + $foot = $( '<table class="' + $table.attr( 'class' ) + + '" cellpadding=0 cellspacing=0 style="margin-top:0"></table>' ) + .addClass( c.namespace.slice( 1 ) + '_extra_table' ) + // maintain any bindings on the tfoot cells + .append( $t.clone( true ) ) .wrap( '<div class="' + tscss.scrollerFooter + '"/>' ); $fCells = $foot.children( 'tfoot' ).eq( 0 ).children( 'tr' ).children(); } @@ -201,7 +229,8 @@ ts.scroller = { .wrap( '<div id="' + id + '" class="' + tscss.scrollerWrap + '" />' ) .before( $hdr ) // shrink filter row but don't completely hide it because the inputs/selectors may distort the columns - .find( '.' + tscss.filterRow ).addClass( tscss.filterRowHide ); + .find( '.' + tscss.filterRow ) + .addClass( tscss.filterRowHide ); wo.scroller_$container = $table.parent(); @@ -223,19 +252,12 @@ ts.scroller = { // look for filter widget if ( $table.hasClass( 'hasFilters' ) ) { - ts.filter.bindSearch( $table, $hdr.find('.' + tscss.filter) ); - } - - // remove any previous fixed columns ( in case we're updating ) - wo.scroller_$container.find( '.' + tscss.scrollerFixed ).remove(); - - if ( wo.scroller_fixedColumns > 0 ) { - ts.scroller.setupFixed( c, wo ); + ts.filter.bindSearch( $table, $hdr.find( '.' + tscss.filter ) ); } - ts.scroller.resize( c, wo ); - - $table.find( 'thead' ).css( 'visibility', 'hidden' ); + $table + .find( 'thead' ) + .addClass( tscss.scrollerHideElement ); tbHt = $tableWrap.parent().height(); @@ -249,33 +271,60 @@ ts.scroller = { $win.scrollTop( $hdr.offset().top ); } } - $hdr.parent().add( $foot.parent() ).scrollLeft( $( this ).scrollLeft() ); + $hdr + .parent() + .add( $foot.parent() ) + .scrollLeft( $( this ).scrollLeft() ); }); - // Sorting, so scroll to top + // resize/update events + events = ( ( ts.hasWidget( c.table, 'filter' ) ? 'filterEnd' : 'tablesorter-initialized' ) + + ' updateComplete pagerComplete columnUpdate ' ).split( ' ' ).join( namespace + ' ' ); + $table - .off( 'sortEnd setFixedColumnSize updateComplete '.split( ' ' ).join( namespace + ' ' ) ) - .on( 'sortEnd' + namespace, function() { - if ( wo.scroller_upAfterSort ) { - $table.parent().animate({ scrollTop: 0 }, 'fast' ); + .off( namespace ) + .on( 'sortEnd filterEnd'.split( ' ' ).join( namespace + ' ' ), function( event ) { + // Sorting, so scroll to top + if ( event.type === 'sortEnd' && wo.scroller_upAfterSort ) { + $tableWrap.animate({ + scrollTop : 0 + }, 'fast' ); + } else if ( wo.scroller_fixedColumns ) { + setTimeout( function() { + // restore previous scroll position + $tableWrap + .scrollTop( wo.scroller_saved[1] ) + .scrollLeft( wo.scroller_saved[0] ); + tsScroller.updateFixed( c, wo, false ); + }, 0 ); } }) .on( 'setFixedColumnSize' + namespace, function( event, size ) { + var $wrap = wo.scroller_$container; if ( typeof size !== 'undefined' && !isNaN( size ) ) { wo.scroller_fixedColumns = parseInt( size, 10 ); } // remove fixed columns - wo.scroller_$container.find( '.' + tscss.scrollerFixed ).remove(); + tsScroller.removeFixed( c, wo ); size = wo.scroller_fixedColumns; if ( size > 0 && size < c.columns - 1 ) { - ts.scroller.setupFixed( c, wo ); - } else { - wo.scroller_$container.removeClass( tscss.scrollerHasFix ); + tsScroller.updateFixed( c, wo ); + } else if ( $wrap.hasClass( tscss.scrollerHasFix ) ) { + $wrap.removeClass( tscss.scrollerHasFix ); + // resize needed to make tables full width + tsScroller.resize( c, wo ); } }) - .on( 'updateComplete' + namespace, function() { + .on( events, function( event ) { + // Stop from running twice with pager + if ( ts.hasWidget( 'pager' ) && event.type === 'updateComplete' ) { + return; + } + if ( wo.scroller_fixedColumns > 0 ) { + tsScroller.updateFixed( c, wo, false ); + } // adjust column sizes after an update - ts.scroller.resize( c, wo ); + tsScroller.resize( c, wo ); }); // Setup window.resizeEnd event @@ -286,110 +335,128 @@ ts.scroller = { // IE calls resize when you modify content, so we have to unbind the resize event // so we don't end up with an infinite loop. we can rebind after we're done. $win.off( 'resize' + namespace, ts.window_resize ); - ts.scroller.resize( c, wo ); + tsScroller.resize( c, wo ); $win.on( 'resize' + namespace, ts.window_resize ); + $tableWrap.trigger( 'scroll' + namespace ); }); // initialization flag c.isScrolling = true; + tsScroller.updateFixed( c, wo ); + }, resize : function( c, wo ) { - var index, borderWidth, setWidth, $hCells, $bCells, $fCells, $headers, $this, + if ( wo.scroller_isBusy ) { return; } + var index, borderWidth, setWidth, $hCells, $bCells, $fCells, $headers, $this, temp, + tsScroller = ts.scroller, + $container = wo.scroller_$container, $table = c.$table, $tableWrap = $table.parent(), $hdr = wo.scroller_$header, $foot = wo.scroller_$footer, id = c.namespace.slice( 1 ) + 'tsscroller', // Hide other scrollers so we can resize - $div = $( 'div.' + tscss.scrollerWrap + '[id != "' + id + '"]' ).hide(); + $div = $( 'div.' + tscss.scrollerWrap + '[id!="' + id + '"]' ) + .addClass( tscss.scrollerHideElement ), + row = '<tr class="' + tscss.scrollerSpacerRow + ' ' + c.selectorRemove.slice(1) + '">'; - $table.children( 'thead' ).show(); - // only remove colgroup if it was added by the plugin - // the $.tablesorter.fixColumnWidth() function already does this (v2.19.0) - // but we need to get "accurate" resized measurements here - see issue #680 - $table.add( $hdr ).add( $foot ).children( 'colgroup' ).remove(); + wo.scroller_calcWidths = []; - // Reset sizes so parent can resize. + // Remove fixed so we get proper widths and heights + tsScroller.removeFixed( c, wo ); + $container.find( '.' + tscss.scrollerSpacerRow ).remove(); + // remove ts added colgroups + $container.find( '.' + ts.css.colgroup ).remove(); + + // show original table elements to get proper alignment $table - .addClass( tscss.scrollerReset ) - .children( 'thead' ) - .find( '.' + tscss.headerIn ).addClass( tscss.scrollerReset ).end() - .find( '.' + tscss.filterRow ).show(); - $tableWrap.addClass( tscss.scrollerReset ); + .find( '.' + tscss.scrollerHideElement ) + .removeClass( tscss.scrollerHideElement ); // include left & right border widths borderWidth = parseInt( $table.css( 'border-left-width' ), 10 ); - // Shrink a bit to accommodate scrollbar - wo.scroller_barSetWidth = ( wo.scroller_barWidth || ts.scroller.getBarWidth() || 18 ) + borderWidth; + $headers = c.$headerIndexed; - $tableWrap.width( $tableWrap.parent().innerWidth() - ( ts.scroller.hasScrollBar( $tableWrap.parent() ) ? wo.scroller_barSetWidth : 0 ) ); - setWidth = $tableWrap.innerWidth() - ( ts.scroller.hasScrollBar( $tableWrap ) ? wo.scroller_barSetWidth : 0 ) + borderWidth; - $hdr.parent().add( $foot.parent() ).width( setWidth ); - - $hCells = $hdr.children( 'thead' ).children().children( 'th, td' ).filter( ':visible' ); - $bCells = $table.children('tbody').eq( 0 ).children().eq( 0 ).children( 'th, td' ).filter( ':visible' ); - $fCells = $foot.children( 'tfoot' ).children().children( 'th, td' ).filter( ':visible' ); - - ts.scroller.setWidth( $hCells.add( $bCells ).add( $fCells ), '' ); - $headers = $table.children( 'thead' ).children().eq( 0 ).children( 'th, td' ); - for ( index = 0; index < $headers.length; index++ ) { - $this = $headers.eq( index ); + for ( index = 0; index < c.columns; index++ ) { + $this = $headers[ index ]; // code from https://github.com/jmosbech/StickyTableHeaders if ( $this.css( 'box-sizing' ) === 'border-box' ) { setWidth = $this.outerWidth(); } else { if ( $hCells.eq( index ).css( 'border-collapse' ) === 'collapse' ) { if ( $this.length && window.getComputedStyle ) { - setWidth = parseFloat( window.getComputedStyle( $this[0], null ).width ); + setWidth = parseFloat( window.getComputedStyle( $this[ 0 ], null ).width ); } else { // ie8 only - borderWidth = parseFloat( $this.css( 'border-width' ) ) || 0; - setWidth = $this.outerWidth() - parseFloat( $this.css( 'padding-left' ) ) - parseFloat( $this.css( 'padding-right' ) ) - borderWidth; + setWidth = $this.outerWidth() - parseFloat( $this.css( 'padding-left' ) ) - + parseFloat( $this.css( 'padding-right' ) ) - + ( parseFloat( $this.css( 'border-width' ) ) || 0 ); } } else { setWidth = $this.width(); } } - ts.scroller.setWidth( $hCells.eq( index ).add( $bCells.eq( index ) ).add( $fCells.eq( index ) ), setWidth ); - } + row += '<td data-column="' + index + '" style="padding:0;margin:0;border:0;height:0;max-height:0;' + + 'min-height:0;width:' + setWidth + 'px;min-width:' + setWidth + 'px;max-width:' + setWidth + 'px"></td>'; - wo.scroller_$container - .find( '.' + tscss.scrollerReset ) - .removeClass( tscss.scrollerReset ); + // save current widths + wo.scroller_calcWidths[ index ] = setWidth; + } + row += '</tr>'; + c.$tbodies.eq(0).prepend( row ); // tbody + $hdr.children( 'thead' ).append( row ); + $foot.children( 'tfoot' ).append( row ); - // refresh colgroup & copy to cloned header + // include colgroup or alignment is off ts.fixColumnWidth( c.table ); + row = c.$table.children( 'colgroup' )[0].outerHTML; + $hdr.prepend( row ); + $foot.prepend( row ); - // add colgroup to all clones - $hCells = $table.children( 'colgroup' ); - if ( $hCells.length ) { - $bCells = $hCells[0].outerHTML; - $hdr.prepend( $bCells ); - if ( $foot.length ) { - $foot.prepend( $bCells ); - } - } + temp = $tableWrap.parent().innerWidth() - + ( tsScroller.hasScrollBar( $tableWrap ) ? wo.scroller_barSetWidth : 0 ); + $tableWrap.width( temp ); + + temp = ( tsScroller.hasScrollBar( $tableWrap ) ? wo.scroller_barSetWidth : 0 ) + borderWidth; + setWidth = $tableWrap.innerWidth() - temp; + + $hdr + .parent() + .add( $foot.parent() ) + .width( setWidth ); + + $tableWrap + .width( setWidth + temp ); + + // hide original table thead + $table.children( 'thead' ).addClass( tscss.scrollerHideElement ); // update fixed column sizes - if ( wo.scroller_fixedColumns > 0 ) { - ts.scroller.updateFixed( c, wo, true ); - } + tsScroller.updateFixed( c, wo ); - // hide filter row because filterEnd event fires - $table.children( 'thead' ).find( '.' + tscss.filterRow ).hide(); + $div.removeClass( tscss.scrollerHideElement ); + + // restore scrollTop - fixes #926 + $tableWrap.scrollTop( wo.scroller_saved[1] ); + wo.scroller_$container + .find( '.' + tscss.scrollerFixed ) + .find( '.' + tscss.scrollerTable ) + .scrollTop( wo.scroller_saved[1] ); - $div.show(); + // update resizable widget handles + setTimeout( function() { + c.$table.trigger( 'resizableUpdate' ); + }, 100 ); }, - // Add fixed (frozen) columns + // Add fixed (frozen) columns (Do not call directly, use updateFixed) setupFixed : function( c, wo ) { - var index, index2, $el, len, temp, $fixedColumn, $fixedTbody, $fixedContainer, + var index, index2, $el, len, temp, $fixedColumn, $fixedTbody, $table = c.$table, - namespace = c.namespace + 'tsscrollerFixed', $wrapper = wo.scroller_$container, fixedColumns = wo.scroller_fixedColumns; @@ -399,14 +466,18 @@ ts.scroller = { .addClass( tscss.scrollerFixed ) .removeClass( tscss.scrollerWrap ) .attr( 'id', '' ); + if ( wo.scroller_addFixedOverlay ) { $fixedColumn.append( '<div class="' + tscss.scrollerFixedPanel + '">' ); } + $fixedTbody = $fixedColumn.find( '.' + tscss.scrollerTable ); - $fixedTbody.find( 'table' ) - .addClass( c.namespace.slice(1) + '_extra_table' ) - .attr( 'id', '' ); - $fixedContainer = $fixedTbody.find( 'tbody' ); + $fixedTbody + .children( 'table' ) + .addClass( c.namespace.slice( 1 ) + '_extra_table' ) + .attr( 'id', '' ) + .children( 'thead, tfoot' ) + .remove(); wo.scroller_$fixedColumns = $fixedColumn; @@ -420,13 +491,22 @@ ts.scroller = { for ( index = 0; index < len; index++ ) { $el.eq( index ).children( ':gt(' + ( fixedColumns - 1 ) + ')' ).remove(); } - $fixedColumn.hide().prependTo( $wrapper ); + $fixedColumn + .addClass( tscss.scrollerHideElement ) + .prependTo( $wrapper ); // look for filter widget if ( c.$table.hasClass( 'hasFilters' ) ) { + // make sure fixed column filters aren't disabled + $el = $fixedColumn + .find( '.' + tscss.filter ) + .not( '.' + tscss.filterDisabled ) + .prop( 'disabled', false ); ts.filter.bindSearch( $table, $fixedColumn.find( '.' + tscss.filter ) ); // disable/enable filters behind fixed column - $el = $wrapper.children( '.' + tscss.scrollerHeader ).find( '.' + tscss.filter ); + $el = $wrapper + .children( '.' + tscss.scrollerHeader ) + .find( '.' + tscss.filter ); len = $el.length; for ( index = 0; index < len; index++ ) { // previously disabled filter; don't mess with it! filterDisabled class added by filter widget @@ -441,10 +521,14 @@ ts.scroller = { c.$table .add( '.' + tscss.scrollerFooter + ' table' ) .children( 'thead' ) - .children( 'tr.' + tscss.headerRow ).children().attr( 'tabindex', -1 ); + .children( 'tr.' + tscss.headerRow ) + .children() + .attr( 'tabindex', -1 ); + $el = wo.scroller_$header .add( $fixedColumn.find( '.' + tscss.scrollerTable + ' table' ) ) - .children( 'thead' ).children( 'tr.' + tscss.headerRow ); + .children( 'thead' ) + .children( 'tr.' + tscss.headerRow ); len = $el.length; for ( index = 0; index < len; index++ ) { temp = $el.eq( index ).children(); @@ -454,179 +538,363 @@ ts.scroller = { } ts.bindEvents( c.table, $fixedColumn.find( '.' + tscss.header ) ); + ts.scroller.bindFixedColumnEvents( c, wo ); + + /*** Scrollbar hack! Since we can't hide the scrollbar with css ***/ + if ( ts.scroller.isFirefox || ts.scroller.isOldIE ) { + $fixedTbody.wrap( '<div class="' + tscss.scrollerHack + '" style="overflow:hidden;">' ); + } + + }, + bindFixedColumnEvents : function( c, wo ) { // update thead & tbody in fixed column - temp = ( 'tablesorter-initialized sortEnd filterEnd ' ).split( ' ' ).join( namespace + ' ' ); + var tsScroller = ts.scroller, + namespace = c.namespace + 'tsscrollerFixed', + events = 'scroll' + namespace, + $fixedTbody = wo.scroller_$fixedColumns.find( '.' + tscss.scrollerTable ), + fixedScroll = true, + tableScroll = true; + c.$table - .off( temp ) - .on( temp, function( event, size ) { - ts.scroller.updateFixed( c, wo, false ); - }) .parent() // *** SCROLL *** scroll fixed column along with main - .off( 'scroll' + namespace ) - .on( 'scroll' + namespace, function() { - $fixedTbody.scrollTop( $( this ).scrollTop() ); + .off( events ) + .on( events, function() { + if ( wo.scroller_isBusy ) { return; } + // using flags to prevent firing the scroll event excessively leading to slow scrolling in Firefox + if ( !wo.scroller_isBusy && ( fixedScroll || !( tsScroller.isFirefox || tsScroller.isIE ) ) ) { + tableScroll = false; + var $this = $( this ); + $fixedTbody[0].scrollTop = wo.scroller_saved[1] = $this.scrollTop(); + wo.scroller_saved[0] = $this.scrollLeft(); + setTimeout( function() { + tableScroll = true; + }, 20 ); + } }); // scroll main along with fixed column $fixedTbody - .off( 'scroll' + namespace ) - .on( 'scroll' + namespace, function() { - c.$table.parent().scrollTop( $fixedTbody.scrollTop() ); + .off( events ) + .on( events, function() { + // using flags to prevent firing the scroll event excessively leading to slow scrolling in Firefox + if ( !wo.scroller_isBusy && ( tableScroll || !( tsScroller.isFirefox || tsScroller.isIE ) ) ) { + fixedScroll = false; + var $this = $( this ); + c.$table.parent()[0].scrollTop = wo.scroller_saved[1] = $this.scrollTop(); + setTimeout( function() { + fixedScroll = true; + }, 20 ); + } }) .scroll(); // *** ROW HIGHLIGHT *** if ( wo.scroller_rowHighlight !== '' ) { - temp = 'mouseover mouseleave '.split( ' ' ).join( namespace + ' ' ); + events = 'mouseover mouseleave '.split( ' ' ).join( namespace + ' ' ); + // can't use c.$tbodies because it doesn't include info-only tbodies c.$table - .off( temp, 'tr' ) - .on( temp, 'tr', function( event ) { - var indx = $( this ).index(); - $fixedContainer.children().eq( indx ) + .off( events, 'tbody > tr' ) + .on( events, 'tbody > tr', function( event ) { + var indx = c.$table.children( 'tbody' ).children( 'tr' ).index( this ); + $fixedTbody + .children( 'table' ) + .children( 'tbody' ) + .children( 'tr' ) + .eq( indx ) .add( this ) .toggleClass( wo.scroller_rowHighlight, event.type === 'mouseover' ); }); - $fixedTbody.find( 'table' ) - .off( temp, 'tr' ) - .on( temp, 'tr', function( event ) { - var indx = $( this ).index(); - c.$tbodies.children().eq( indx ) + $fixedTbody + .find( 'table' ) + .off( events, 'tbody > tr' ) + .on( events, 'tbody > tr', function( event ) { + var $fixed = $fixedTbody.children( 'table' ).children( 'tbody' ).children( 'tr' ), + indx = $fixed.index( this ); + c.$table + .children( 'tbody' ) + .children( 'tr' ) + .eq( indx ) .add( this ) .toggleClass( wo.scroller_rowHighlight, event.type === 'mouseover' ); }); } + }, - /*** Scrollbar hack! Since we can't hide the scrollbar with css ***/ - if ( ts.scroller.isFirefox || ts.scroller.isOldIE ) { - $fixedTbody.wrap( '<div class="scroller-scrollbar-hack" style="overflow:hidden;">' ); - } - - ts.scroller.updateFixed( c, wo, true ); + adjustWidth : function( c, wo, totalWidth, adj, dir ) { + var $wrapper = wo.scroller_$container; + // RTL support (fixes column on right) + $wrapper + .children( '.' + tscss.scrollerTable ) + .css( dir ? 'right' : 'left', totalWidth ); + $wrapper + .children( '.' + tscss.scrollerHeader + ', .' + tscss.scrollerFooter ) + // Safari needs a scrollbar width of extra adjusment to align the fixed & scrolling columns + .css( dir ? 'right' : 'left', totalWidth + ( dir && ts.scroller.isSafari ? adj : 0 ) ); }, updateFixed : function( c, wo ) { - if ( !c.isScrolling ) { return; } + var temp, adj, + $wrapper = wo.scroller_$container, + $hdr = wo.scroller_$header, + $foot = wo.scroller_$footer, + $table = c.$table, + $tableWrap = $table.parent(), + scrollBarWidth = wo.scroller_barSetWidth, + dir = $table.hasClass( tscss.scrollerRtl ); + + if ( wo.scroller_fixedColumns === 0 ) { + wo.scroller_isBusy = false; + ts.scroller.removeFixed( c, wo ); + temp = $wrapper.width(); + $tableWrap.width( temp ); + adj = ts.scroller.hasScrollBar( $tableWrap ) ? scrollBarWidth : 0; + $hdr + .parent() + .add( $foot.parent() ) + .width( temp - adj ); + return; + } + + if ( !c.isScrolling ) { + return; + } + + wo.scroller_isBusy = true; - // no idea why this happens, but sometimes the main table wrapper gets the scrollbar width - // subtracted from it on load and on theme change - it can be very sporatic; this fixes it. - c.$table.parent().width( wo.scroller_$container.width() ); + // Make sure the wo.scroller_$fixedColumns container exists, if not build it + if ( !$wrapper.find( '.' + tscss.scrollerFixed ).length ) { + ts.scroller.setupFixed( c, wo ); + } // scroller_fixedColumns - var index, tbodyIndex, rowIndex, $tbody, $adjCol, $fb, totalRows, widths, - $table = c.$table, - $wrapper = wo.scroller_$container, + var index, tbodyIndex, rowIndex, $tbody, $adjCol, $fb, $fixHead, $fixBody, $fixFoot, + totalRows, row, // source cells for measurement - $mainTbodies = wo.scroller_$container.children( '.' + tscss.scrollerTable ).children( 'table' ).children( 'tbody' ), - $rows = wo.scroller_$header.children( 'thead' ).children( '.' + tscss.headerRow ), // variable gets redefined + $mainTbodies = wo.scroller_$container + .children( '.' + tscss.scrollerTable ) + .children( 'table' ) + .children( 'tbody' ), + // variable gets redefined + $rows = wo.scroller_$header + .children( 'thead' ) + .children( '.' + tscss.headerRow ), // hide fixed column during resize, or we get a FOUC - $fixedColumn = wo.scroller_$fixedColumns.hide(), + $fixedColumn = wo.scroller_$fixedColumns + .addClass( tscss.scrollerHideElement ), // target cells - $fixedTbodiesTable = $fixedColumn.find( '.' + tscss.scrollerTable ).children( 'table' ), - $fixedTbodies = $fixedTbodiesTable.children( 'tbody' ), - $fixedHeader = $fixedColumn.find( '.' + tscss.scrollerHeader ).children( 'table' ).children( 'thead' ), + $fixedTbodiesTable = $fixedColumn + .find( '.' + tscss.scrollerTable ) + .children( 'table' ), + $fixedTbodies = $fixedTbodiesTable + .children( 'tbody' ), // variables tsScroller = ts.scroller, - scrollBarWidth = wo.scroller_barSetWidth, fixedColumns = wo.scroller_fixedColumns, // get dimensions $temp = $table.find( 'tbody td' ), borderRightWidth = parseInt( $temp.css( 'border-right-width' ), 10 ) || 1, - borderBottomWidth = parseInt( $temp.css( 'border-bottom-width' ), 10 ) || 1, - borderSpacing = parseInt( $temp.css( 'border-spacing' ).split( /\s/ )[ 0 ], 10 ) / 2 || 0, - totalWidth = parseInt( $table.css( 'padding-left' ), 10 ) + parseInt( $table.css( 'padding-right' ), 10 ) - borderRightWidth; - - // fixed header cell height - $temp = $fixedHeader.children( '.' + tscss.headerRow ); - for ( index = 0; index < $temp.length; index++ ) { - $temp.eq( index ).height( $rows.eq( index ).outerHeight() ); - } + borderSpacing = parseInt( ( $temp.css( 'border-spacing' ) || '' ).split( /\s/ )[ 0 ], 10 ) / 2 || 0, + totalWidth = parseInt( $table.css( 'padding-left' ), 10 ) + + parseInt( $table.css( 'padding-right' ), 10 ) - + borderRightWidth, + widths = wo.scroller_calcWidths; - // body cell dimensions seem to be more accurate *shrug* - $rows = ( c.filteredRows > 0 ? c.$tbodies : $table.children( 'thead' ) ).children( 'tr:visible' ); - // recalculate widths - widths = $rows.children( ':lt(' + fixedColumns + ')' ).map( function() { - totalWidth += $( this ).outerWidth() + borderSpacing; - return $( this ).outerWidth(); - }).get(); - - // set fixed column width - tsScroller.setWidth( $fixedColumn.add( $fixedColumn.children() ), totalWidth + borderRightWidth * 2 - borderSpacing ); - tsScroller.setWidth( $fixedColumn.find( 'table' ), totalWidth + borderRightWidth ); - - // set fixed column height ( changes with filtering ) - $fixedColumn.height( $wrapper.height() ); + ts.scroller.removeFixed( c, wo, false ); - if ( wo.scroller_$footer.length ) { - // adjust footer row heights (text could wrap on resize) - $temp = $wrapper.children( '.' + tscss.scrollerFooter ).find( 'tfoot tr' ); - $rows = $fixedColumn.find( '.' + tscss.scrollerFooter + ' tfoot tr' ); - for ( index = 0; index < $rows.length; index++ ) { - $rows.eq( index ).height( $temp.eq( index ).height() ); - } + // calculate fixed column width + for ( index = 0; index < fixedColumns; index++ ) { + totalWidth += widths[ index ] + borderSpacing; } - // leave a gap under the tbody for the horizontal scrollbar - $fixedColumn.find( '.' + tscss.scrollerTable ) - .height( $table.parent().height() - scrollBarWidth + borderBottomWidth ); - // update fixed column tbody content, set row height & set cell widths for first row + // set fixed column width + totalWidth = totalWidth + borderRightWidth * 2 - borderSpacing; + tsScroller.setWidth( $fixedColumn.add( $fixedColumn.children() ), totalWidth ); + tsScroller.setWidth( $fixedColumn.children().children( 'table' ), totalWidth ); + + // update fixed column tbody content, set cell widths on hidden row for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { $tbody = $mainTbodies.eq( tbodyIndex ); if ( $tbody.length ) { // get tbody $rows = $tbody.children(); totalRows = $rows.length; - $fb = ts.processTbody( $fixedTbodiesTable, $fixedTbodies.eq( tbodyIndex ), true); + $fb = ts.processTbody( $fixedTbodiesTable, $fixedTbodies.eq( tbodyIndex ), true ); $fb.empty(); // update tbody cells after sort/filtering for ( rowIndex = 0; rowIndex < totalRows; rowIndex++ ) { $adjCol = $( $rows[ rowIndex ].outerHTML ); - $adjCol.children( 'td, th' ).slice( fixedColumns ).remove(); - // set row height - $adjCol.children().eq( 0 ).height( $rows.eq( rowIndex ).outerHeight() - ( tsScroller.isFirefox ? borderBottomWidth * 2 : 0 ) ); - // still need to adjust tbody cell widths ( the previous row may now be filtered ) - if ( rowIndex === 0 ) { - tsScroller.setWidth( $adjCol.children().eq( 0 ), widths[ 0 ] ); - } + $adjCol + .children( 'td, th' ) + .slice( fixedColumns ) + .remove(); $fb.append( $adjCol ); } - // adjust fixed header cell widths - $temp = $fixedColumn.find( 'thead' ).children( 'tr.' + tscss.headerRow ); - for ( index = 0; index < fixedColumns; index++ ) { - tsScroller.setWidth( $temp.children( ':eq(' + index + ')' ), widths[ index ] ); - } - // restore tbody ts.processTbody( $fixedTbodiesTable, $fb, false ); } } + adj = ts.scroller.hasScrollBar( $tableWrap ) ? scrollBarWidth : 0; + /*** scrollbar HACK! Since we can't hide the scrollbar with css ***/ if ( tsScroller.isFirefox || tsScroller.isOldIE ) { - $fixedTbodiesTable.parent().css({ - 'width' : totalWidth + scrollBarWidth + borderRightWidth - }); + $fixedTbodiesTable + .css( 'width', totalWidth ) + .parent() + .css( 'width', totalWidth + adj ); } - $fixedColumn.show(); + $fixedColumn.removeClass( tscss.scrollerHideElement ); + for ( index = 0; index < fixedColumns; index++ ) { + $wrapper + .children( 'div' ) + .children( 'table' ) + .find( 'th:nth-child(' + ( index + 1 ) + '), td:nth-child(' + ( index + 1 ) + ')' ) + .addClass( tscss.scrollerHideColumn ); + } + + totalWidth = totalWidth - borderRightWidth; + temp = $tableWrap.parent().innerWidth() - totalWidth; + $tableWrap.width( temp ); + // RTL support (fixes column on right) + $wrapper + .children( '.' + tscss.scrollerTable ) + .css( dir ? 'right' : 'left', totalWidth ); + $wrapper + .children( '.' + tscss.scrollerHeader + ', .' + tscss.scrollerFooter ) + // Safari needs a scrollbar width of extra adjusment to align the fixed & scrolling columns + .css( dir ? 'right' : 'left', totalWidth + ( dir && ts.scroller.isSafari ? adj : 0 ) ); + + $hdr + .parent() + .add( $foot.parent() ) + .width( temp - adj ); + + // fix gap under the tbody for the horizontal scrollbar + temp = ts.scroller.hasScrollBar( $tableWrap, true ); + adj = temp ? scrollBarWidth : 0; + if ( !$fixedColumn.find( '.' + tscss.scrollerBarSpacer ).length && temp ) { + $temp = $( '<div class="' + tscss.scrollerBarSpacer + '">' ) + .css( 'height', adj + 'px' ); + $fixedColumn.find( '.' + tscss.scrollerTable ).append( $temp ); + } else if ( !temp ) { + $fixedColumn.find( '.' + tscss.scrollerBarSpacer ).remove(); + } + + ts.scroller.updateRowHeight( c, wo ); + // set fixed column height (changes with filtering) + $fixedColumn.height( $wrapper.height() ); + + $fixedColumn.removeClass( tscss.scrollerHideElement ); + + wo.scroller_isBusy = false; + }, + + fixHeight : function( $rows, $fixedRows ) { + var index, heightRow, heightFixed, $r, $f, + len = $rows.length; + for ( index = 0; index < len; index++ ) { + $r = $rows.eq( index ); + $f = $fixedRows.eq( index ); + heightRow = $r.height(); + heightFixed = $f.height(); + if ( heightRow > heightFixed ) { + $f.addClass( tscss.scrollerAddedHeight ).height( heightRow ); + } else if ( heightRow < heightFixed ) { + $r.addClass( tscss.scrollerAddedHeight ).height( heightFixed ); + } + } + }, + + updateRowHeight : function( c, wo ) { + var $rows, $fixed, + $fixedColumns = wo.scroller_$fixedColumns; + + wo.scroller_$container + .find( '.' + tscss.scrollerAddedHeight ) + .removeClass( tscss.scrollerAddedHeight ) + .height( '' ); + + $rows = wo.scroller_$header + .children( 'thead' ) + .children( 'tr' ); + $fixed = $fixedColumns + .children( '.' + tscss.scrollerHeader ) + .children( 'table' ) + .children( 'thead' ) + .children( 'tr' ); + ts.scroller.fixHeight( $rows, $fixed ); + + $rows = wo.scroller_$footer + .children( 'tfoot' ) + .children( 'tr' ); + $fixed = $fixedColumns + .children( '.' + tscss.scrollerFooter ) + .children( 'table' ) + .children( 'tfoot' ) + .children( 'tr' ); + ts.scroller.fixHeight( $rows, $fixed ); + + if ( ts.scroller.isFirefox || ts.scroller.isOldIE ) { + // Firefox/Old IE scrollbar hack (wraps table to hide the scrollbar) + $fixedColumns = $fixedColumns.find( '.' + tscss.scrollerHack ); + } + $rows = c.$table + .children( 'tbody' ) + .children( 'tr' ); + $fixed = $fixedColumns + .children( '.' + tscss.scrollerTable ) + .children( 'table' ) + .children( 'tbody' ) + .children( 'tr' ); + ts.scroller.fixHeight( $rows, $fixed ); }, + removeFixed : function( c, wo, removeIt ) { + var $table = c.$table, + $wrapper = wo.scroller_$container, + dir = $table.hasClass( tscss.scrollerRtl ); + + // remove fixed columns + if ( removeIt || typeof removeIt === 'undefined' ) { + $wrapper.find( '.' + tscss.scrollerFixed ).remove(); + } + + $wrapper + .find( '.' + tscss.scrollerHideColumn ) + .removeClass( tscss.scrollerHideColumn ); + + // RTL support ( fixes column on right ) + $wrapper + .children( ':not(.' + tscss.scrollerFixed + ')' ) + .css( dir ? 'right' : 'left', 0 ); + }, + remove : function( c, wo ) { var $wrap = wo.scroller_$container, namespace = c.namespace + 'tsscroller'; c.$table .off( namespace ) .insertBefore( $wrap ) - .find( 'thead' ).show().css( 'visibility', 'visible' ) - .children( 'tr.' + tscss.headerRow ).children().attr( 'tabindex', 0 ) + .find( 'thead' ) + .removeClass( tscss.scrollerHideElement ) + .children( 'tr.' + tscss.headerRow ) + .children() + .attr( 'tabindex', 0 ) .end() - .find( '.' + tscss.filterRow ).show().removeClass( tscss.filterRowHide ); + .find( '.' + tscss.filterRow ) + .removeClass( tscss.scrollerHideElement + ' ' + tscss.filterRowHide ); + c.$table + .find( '.' + tscss.filter ) + .not( '.' + tscss.filterDisabled ) + .prop( 'disabled', false ); $wrap.remove(); $( window ).off( namespace ); c.isScrolling = false; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js new file mode 100644 index 0000000..9d49740 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js @@ -0,0 +1,228 @@ +/*! tablesorter tbody sorting widget (BETA) - 6/10/2015 (core v2.22.2) + * Requires tablesorter v2.22.2+ and jQuery 1.4+ + * by Rob Garrison + */ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;( function( $ ) { +'use strict'; +var ts = $.tablesorter; + +ts.sortTbodies = { + init: function( c, wo ) { + + var index, rows, txt, max, $rows, + namespace = c.namespace + 'sortTbody', + $tbodies = c.$table.children( 'tbody' ), + len = $tbodies.length; + + // save serverSideSorting value; use to toggle internal row sorting + wo.sortTbody_original_serverSideSorting = c.serverSideSorting; + + // include info-only tbodies - we need parsed data from *all* tbodies + wo.sortTbody_original_cssInfoBlock = c.cssInfoBlock; + c.cssInfoBlock = wo.sortTbody_noSort; + ts.sortTbodies.setTbodies( c, wo ); + + // add original order index for stable sort + for ( index = 0; index < len; index++ ) { + $tbodies.eq( index ).attr( 'data-ts-original-order', index ); + } + + c.$table + .unbind( 'sortBegin updateComplete '.split( ' ' ).join( namespace + ' ' ) ) + .bind( 'sortBegin' + namespace, function() { + ts.sortTbodies.sorter( c ); + }) + .bind( 'updateComplete' + namespace, function() { + // find parsers for each column + ts.sortTbodies.setTbodies( c, wo ); + c.$table.trigger( 'updateCache', [ null, c.$tbodies ] ); + }); + + // detect parsers - in case the table contains only info-only tbodies + if ( $.isEmptyObject( c.parsers ) || c.$tbodies.length !== $tbodies.length ) { + ts.sortTbodies.setTbodies( c, wo ); + c.$table.trigger( 'updateCache', [ null, c.$tbodies ] ); + } + + // find colMax; this only matter for numeric columns + $rows = $tbodies.children( 'tr' ); + len = $rows.length; + for ( index = 0; index < c.columns; index++ ) { + max = 0; + if ( c.parsers[ index ].type === 'numeric' ) { + for ( rows = 0; rows < len; rows++ ) { + // update column max value (ignore sign) + txt = ts.getParsedText( c, $rows.eq( rows ).children()[ index ], index ); + max = Math.max( Math.abs( txt ) || 0, max ); + } + } + c.$headerIndexed[ index ].attr( 'data-ts-col-max-value', max ); + } + + }, + + // make sure c.$tbodies is up-to-date (init & after updates) + setTbodies: function( c, wo ) { + c.$tbodies = c.$table.children( 'tbody' ).not( '.' + wo.sortTbody_noSort ); + }, + + sorter: function( c ) { + var $table = c.$table, + wo = c.widgetOptions; + + // prevent multiple calls while processing + if ( wo.sortTbody_busy !== true ) { + wo.sortTbody_busy = true; + var $tbodies = $table.children( 'tbody' ).not( '.' + wo.sortTbody_noSort ), + primary = wo.sortTbody_primaryRow || 'tr:eq(0)', + sortList = c.sortList || [], + len = sortList.length; + + if ( len ) { + + // toggle internal row sorting + c.serverSideSorting = !wo.sortTbody_sortRows; + + $tbodies.sort( function( a, b ) { + var sortListIndex, txt, dir, num, colMax, sort, col, order, colA, colB, x, y, + table = c.table, + parsers = c.parsers, + cts = c.textSorter || '', + $tbodyA = $( a ), + $tbodyB = $( b ), + $a = $tbodyA.find( primary ).children( 'td, th' ), + $b = $tbodyB.find( primary ).children( 'td, th' ); + for ( sortListIndex = 0; sortListIndex < len; sortListIndex++ ) { + col = sortList[ sortListIndex ][0]; + order = sortList[ sortListIndex ][1]; + // sort direction, true = asc, false = desc + dir = order === 0; + // column txt - tbody A + txt = ts.getElementText( c, $a.eq( col ), col ); + colA = parsers[ col ].format( txt, table, $a[ col ], col ); + // column txt - tbody B + txt = ts.getElementText( c, $b.eq( col ), col ); + colB = parsers[ col ].format( txt, table, $b[ col ], col ); + + if (c.sortStable && colA === colB && len === 1) { + return $tbodyA.attr( 'data-ts-original-order' ) - $tbodyB.attr( 'data-ts-original-order' ); + } + + // fallback to natural sort since it is more robust + num = /n/i.test( parsers && parsers[ col ] ? parsers[ col ].type || '' : '' ); + if ( num && c.strings[ col ] ) { + colMax = c.$headerIndexed[ col ].attr( 'data-ts-col-max-value' ) || + 1.79E+308; // close to Number.MAX_VALUE + // sort strings in numerical columns + if ( typeof ( c.string[ c.strings[ col ] ] ) === 'boolean' ) { + num = ( dir ? 1 : -1 ) * ( c.string[ c.strings[ col ] ] ? -1 : 1 ); + } else { + num = ( c.strings[ col ] ) ? c.string[ c.strings[ col ] ] || 0 : 0; + } + // fall back to built-in numeric sort + // var sort = $.tablesorter['sort' + s](a, b, dir, colMax, table); + sort = c.numberSorter ? c.numberSorter( colA, colB, dir, colMax, table ) : + ts[ 'sortNumeric' + ( dir ? 'Asc' : 'Desc' ) ]( colA, colB, num, colMax, col, table ); + } else { + // set a & b depending on sort direction + x = dir ? colA : colB; + y = dir ? colB : colA; + // text sort function + if ( typeof ( cts ) === 'function' ) { + // custom OVERALL text sorter + sort = cts( x, y, dir, col, table ); + } else if ( typeof ( cts ) === 'object' && cts.hasOwnProperty( col ) ) { + // custom text sorter for a SPECIFIC COLUMN + sort = cts[ col ]( x, y, dir, col, table ); + } else { + // fall back to natural sort + sort = ts[ 'sortNatural' + ( dir ? 'Asc' : 'Desc' ) ]( colA, colB, col, table, c ); + } + } + if ( sort ) { return sort; } + } + return $tbodyA.attr( 'data-ts-original-order' ) - $tbodyB.attr( 'data-ts-original-order' ); + }); + + ts.sortTbodies.restoreTbodies ( c, wo, $tbodies ); + wo.sortTbody_busy = false; + } + } + }, + + restoreTbodies : function ( c, wo, $sortedTbodies ) { + var $nosort, $tbodies, $thisTbody, tbLen, nsLen, index, targetIndex, + $table = c.$table, + hasShuffled = true, + indx = 0; + + // hide entire table to improve sort performance + $table.hide(); + $sortedTbodies.appendTo( $table ); + + // reposition no-sort tbodies + $tbodies = $table.children( 'tbody' ); + tbLen = $tbodies.length; + $nosort = $tbodies.filter( '.' + wo.sortTbody_noSort ).appendTo( $table ); + nsLen = $nosort.length; + + if ( nsLen ) { + // don't allow the while loop to cycle more times than the set number of no-sort tbodies + while ( hasShuffled && indx < nsLen ) { + hasShuffled = false; + for ( index = 0; index < nsLen; index++ ) { + targetIndex = parseInt( $nosort.eq( index ).attr( 'data-ts-original-order' ), 10 ); + // if target index > number of tbodies, make it last + targetIndex = targetIndex >= tbLen ? tbLen : targetIndex < 0 ? 0 : targetIndex; + + if ( targetIndex !== $nosort.eq( index ).index() ) { + hasShuffled = true; + $thisTbody = $nosort.eq( index ).detach(); + + if ( targetIndex >= tbLen ) { + // Are we trying to be the last tbody? + $thisTbody.appendTo( $table ); + } else if ( targetIndex === 0 ) { + // Are we trying to be the first tbody? + $thisTbody.prependTo( $table ); + } else { + // No, we want to be somewhere in the middle! + $thisTbody.insertBefore( $table.children( 'tbody:eq(' + targetIndex + ')' ) ); + } + + } + } + indx++; + } + } + + $table.show(); + } + +}; + +ts.addWidget({ + id: 'sortTbody', + // priority < 50 (filter widget), so c.$tbodies has the correct elements + priority: 40, + options: { + // point to a row within the tbody that matches the number of header columns + sortTbody_primaryRow : null, + // sort tbody internal rows + sortTbody_sortRows : false, + // static tbodies (like static rows) + sortTbody_noSort : 'tablesorter-no-sort-tbody' + }, + init : function( table, thisWidget, c, wo ) { + ts.sortTbodies.init( c, wo ); + }, + remove : function( table, c, wo, refreshing ) { + c.$table.unbind( 'sortBegin updateComplete '.split( ' ' ).join( c.namespace + 'sortTbody ' ) ); + c.serverSideSorting = wo.sortTbody_original_serverSideSorting; + c.cssInfoBlock = wo.sortTbody_original_cssInfoBlock; + } +}); + +})( jQuery ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js index 4c75186..f0fa8fc 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js @@ -16,32 +16,34 @@ $.extend(ts.css, { // Add a resize event to table headers ts.addHeaderResizeEvent = function(table, disable, settings) { table = $(table)[0]; // make sure we're using a dom element - var headers, - defaults = { + if ( !table.config ) { return; } + var defaults = { timer : 250 }, options = $.extend({}, defaults, settings), c = table.config, wo = c.widgetOptions, - checkSizes = function(triggerEvent) { + checkSizes = function( triggerEvent ) { + var index, headers, $header, sizes, width, height, + len = c.$headers.length; wo.resize_flag = true; headers = []; - c.$headers.each(function() { - var $header = $(this), - sizes = $header.data('savedSizes') || [0,0], // fixes #394 - width = this.offsetWidth, - height = this.offsetHeight; - if (width !== sizes[0] || height !== sizes[1]) { - $header.data('savedSizes', [ width, height ]); - headers.push(this); + for ( index = 0; index < len; index++ ) { + $header = c.$headers.eq( index ); + sizes = $header.data( 'savedSizes' ) || [ 0,0 ]; // fixes #394 + width = $header[0].offsetWidth; + height = $header[0].offsetHeight; + if ( width !== sizes[0] || height !== sizes[1] ) { + $header.data( 'savedSizes', [ width, height ] ); + headers.push( $header[0] ); } - }); - if (headers.length && triggerEvent !== false) { - c.$table.trigger('resize', [ headers ]); + } + if ( headers.length && triggerEvent !== false ) { + c.$table.trigger( 'resize', [ headers ] ); } wo.resize_flag = false; }; - checkSizes(false); + checkSizes( false ); clearInterval(wo.resize_timer); if (disable) { wo.resize_flag = false; @@ -77,7 +79,8 @@ ts.addWidget({ if ( c.$table.hasClass('hasStickyHeaders') || ($.inArray('filter', c.widgets) >= 0 && !c.$table.hasClass('hasFilters')) ) { return; } - var $table = c.$table, + var index, len, $t, + $table = c.$table, // add position: relative to attach element, hopefully it won't cause trouble. $attach = $(wo.stickyHeaders_attachTo), namespace = c.namespace + 'stickyheaders ', @@ -112,17 +115,19 @@ ts.addWidget({ laststate = '', spacing = 0, setWidth = function($orig, $clone){ - $orig.filter(':visible').each(function(i) { - var width, border, - $cell = $clone.filter(':visible').eq(i), - $this = $(this); + var index, width, border, $cell, $this, + $cells = $orig.filter(':visible'), + len = $cells.length; + for ( index = 0; index < len; index++ ) { + $cell = $clone.filter(':visible').eq(index); + $this = $cells.eq(index); // code from https://github.com/jmosbech/StickyTableHeaders if ($this.css('box-sizing') === 'border-box') { width = $this.outerWidth(); } else { if ($cell.css('border-collapse') === 'collapse') { if (window.getComputedStyle) { - width = parseFloat( window.getComputedStyle(this, null).width ); + width = parseFloat( window.getComputedStyle($this[0], null).width ); } else { // ie8 only border = parseFloat( $this.css('border-width') ); @@ -133,10 +138,11 @@ ts.addWidget({ } } $cell.css({ + 'width': width, 'min-width': width, 'max-width': width }); - }); + } }, resizeHeader = function() { stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0; @@ -148,6 +154,39 @@ ts.addWidget({ }); setWidth( $table, $stickyTable ); setWidth( $header, $stickyCells ); + }, + scrollSticky = function( resizing ) { + if (!$table.is(':visible')) { return; } // fixes #278 + // Detect nested tables - fixes #724 + nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; + var offset = $table.offset(), + yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 + xWindow = $.isWindow( $xScroll[0] ), + // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, + scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, + tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)), + isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', + cssSettings = { visibility : isVisible }; + + if ($attach.length) { + cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); + } + if (xWindow) { + // adjust when scrolling horizontally - fixes issue #143 + cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing; + } + if ($nestedSticky.length) { + cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; + } + $stickyWrap + .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) + .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) + .css(cssSettings); + if (isVisible !== laststate || resizing) { + // make sure the column widths match + resizeHeader(); + laststate = isVisible; + } }; // only add a position relative if a position isn't already defined if ($attach.length && !$attach.css('position')) { @@ -179,48 +218,26 @@ ts.addWidget({ // onRenderHeader is defined, we need to do something about it (fixes #641) if (c.onRenderHeader) { - $stickyThead.children('tr').children().each(function(index){ + $t = $stickyThead.children('tr').children(); + len = $t.length; + for ( index = 0; index < len; index++ ) { // send second parameter - c.onRenderHeader.apply( $(this), [ index, c, $stickyTable ] ); - }); + c.onRenderHeader.apply( $t.eq( index ), [ index, c, $stickyTable ] ); + } } // make it sticky! $xScroll.add($yScroll) - .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') ) - .bind('scroll resize '.split(' ').join( namespace ), function(event) { - if (!$table.is(':visible')) { return; } // fixes #278 - // Detect nested tables - fixes #724 - nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; - var offset = $table.offset(), - yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 - xWindow = $.isWindow( $xScroll[0] ), - // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, - scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, - tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)), - isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', - cssSettings = { visibility : isVisible }; + .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') ) + .bind('scroll resize '.split(' ').join( namespace ), function( event ) { + scrollSticky( event.type === 'resize' ); + }); + c.$table + .unbind('stickyHeadersUpdate' + namespace) + .bind('stickyHeadersUpdate' + namespace, function(){ + scrollSticky( true ); + }); - if ($attach.length) { - cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); - } - if (xWindow) { - // adjust when scrolling horizontally - fixes issue #143 - cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing; - } - if ($nestedSticky.length) { - cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; - } - $stickyWrap - .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) - .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) - .css(cssSettings); - if (isVisible !== laststate || event.type === 'resize') { - // make sure the column widths match - resizeHeader(); - laststate = isVisible; - } - }); if (wo.stickyHeaders_addResizeEvent) { ts.addHeaderResizeEvent(table); } @@ -256,7 +273,7 @@ ts.addWidget({ var namespace = c.namespace + 'stickyheaders '; c.$table .removeClass('hasStickyHeaders') - .unbind( ('pagerComplete filterEnd '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) + .unbind( ('pagerComplete filterEnd stickyHeadersUpdate '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) .next('.' + ts.css.stickyWrap).remove(); if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table $(window) From efd3755a973bc09099071c106f27230ef823274b Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Tue, 28 Jul 2015 22:47:36 +0200 Subject: [PATCH 060/138] * updated tablesorter to latest version (2.22.4) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 147 +- .../extras/jquery.dragtable.mod.js | 2 +- .../extras/jquery.quicksearch.js | 195 - .../jquery.tablesorter.combined.js | 5223 +++++++++-------- .../jquery-tablesorter/jquery.tablesorter.js | 293 +- .../jquery.tablesorter.widgets.js | 4958 ++++++++-------- .../parsers/parser-date-extract.js | 30 +- .../parsers/parser-date-iso8601.js | 2 +- .../parsers/parser-date-month.js | 8 +- .../parsers/parser-date-range.js | 2 +- .../parsers/parser-date-two-digit-year.js | 24 +- .../parsers/parser-date-weekday.js | 8 +- .../jquery-tablesorter/parsers/parser-date.js | 2 +- .../parsers/parser-duration.js | 2 +- .../parsers/parser-feet-inch-fraction.js | 12 +- .../parsers/parser-file-type.js | 44 +- .../parsers/parser-globalize.js | 2 +- .../parsers/parser-ignore-articles.js | 30 +- .../parsers/parser-image.js | 6 +- .../parsers/parser-input-select.js | 13 +- .../parsers/parser-metric.js | 4 +- .../parsers/parser-named-numbers.js | 6 +- .../parsers/parser-network.js | 2 +- .../parsers/parser-roman.js | 8 +- .../widgets/widget-alignChar.js | 243 +- .../widgets/widget-build-table.js | 26 +- .../widgets/widget-chart.js | 4 +- .../widgets/widget-columnSelector.js | 648 +- .../widgets/widget-columns.js | 120 +- .../widgets/widget-editable.js | 438 +- .../widgets/widget-filter-formatter-html5.js | 721 ++- .../widgets/widget-filter-formatter-jui.js | 1332 ++--- .../widget-filter-formatter-select2.js | 248 +- .../widgets/widget-filter-type-insideRange.js | 2 +- .../widgets/widget-filter.js | 2881 ++++----- .../widgets/widget-formatter.js | 2 +- .../widgets/widget-grouping.js | 426 +- .../widgets/widget-headerTitles.js | 6 +- .../jquery-tablesorter/widgets/widget-math.js | 487 +- .../widgets/widget-output.js | 659 ++- .../widgets/widget-pager.js | 2102 +++---- .../widgets/widget-print.js | 218 +- .../widgets/widget-reflow.js | 229 +- .../widgets/widget-resizable.js | 719 +-- .../widgets/widget-saveSort.js | 118 +- .../widgets/widget-scroller.js | 1624 ++--- .../widgets/widget-sort2Hash.js | 128 + .../widgets/widget-sortTbodies.js | 390 +- .../widgets/widget-staticRow.js | 180 +- .../widgets/widget-stickyHeaders.js | 514 +- .../widgets/widget-storage.js | 152 +- .../widgets/widget-uitheme.js | 340 +- 56 files changed, 13097 insertions(+), 12893 deletions(-) delete mode 100644 vendor/assets/javascripts/jquery-tablesorter/extras/jquery.quicksearch.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a19507..4319dad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.17.3 (2015-07-28) + +* Upgrade tablesorter to v2.22.4 + #### v1.17.2 (2015-07-01) * Upgrade tablesorter to v2.22.3 diff --git a/README.md b/README.md index 7699bd6..c611239 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.22.3 (6/30/2015), [documentation] +Current tablesorter version: 2.22.4 (7/28/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 4ac2c35..cd0bd6d 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.17.2' + VERSION = '1.17.3' end diff --git a/tablesorter b/tablesorter index 0e611bc..b3dd4b7 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 0e611bc6a89597795e16d2097949c7adf2b29e89 +Subproject commit b3dd4b7c7504451eb98d2a342314045534b07496 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index ed5c9aa..99badd5 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,10 +1,10 @@ /*! * tablesorter (FORK) pager plugin - * updated 5/17/2015 (v2.22.0) + * updated 7/28/2015 (v2.22.4) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { - "use strict"; + 'use strict'; /*jshint supernew:true */ var ts = $.tablesorter; @@ -159,6 +159,7 @@ if ( p.initializing ) { return; } var s, t, $out, indx, len, options, c = table.config, + namespace = c.namespace + 'pager', sz = p.size || p.settings.size || 10; // don't allow dividing by zero if (p.countChildRows) { t.push(c.cssChildRow); } p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method @@ -175,13 +176,13 @@ // form the output string (can now get a new output string from the server) s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || p.output : p.output ) // {page} = one-based index; {page+#} = zero based index +/- value - .replace(/\{page([\-+]\d+)?\}/gi, function(m,n){ + .replace(/\{page([\-+]\d+)?\}/gi, function(m, n){ return p.totalPages ? p.page + (n ? parseInt(n, 10) : 1) : 0; }) // {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object) .replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m){ var len, indx, - str = m.replace(/[{}\s]/g,''), + str = m.replace(/[{}\s]/g, ''), extra = str.split(':'), data = p.ajaxData, // return zero for default page/row numbers @@ -206,10 +207,10 @@ if ($out.length) { $out[ ($out[0].nodeName === 'INPUT') ? 'val' : 'html' ](s); // rebind startRow/page inputs - $out.find('.ts-startRow, .ts-page').unbind('change.pager').bind('change.pager', function(){ + $out.find('.ts-startRow, .ts-page').unbind('change' + namespace).bind('change' + namespace, function(){ var v = $(this).val(), - pg = $(this).hasClass('ts-startRow') ? Math.floor( v/p.size ) + 1 : v; - c.$table.trigger('pageSet.pager', [ pg ]); + pg = $(this).hasClass('ts-startRow') ? Math.floor( v / p.size ) + 1 : v; + c.$table.trigger('pageSet' + namespace, [ pg ]); }); } } @@ -217,7 +218,7 @@ fixHeight(table, p); if (p.initialized && completed !== false) { if (c.debug) { - ts.log('Pager: Triggering pagerComplete'); + console.log('Pager: Triggering pagerComplete'); } c.$table.trigger('pagerComplete', p); // save pager info to storage @@ -241,7 +242,7 @@ current_page = p.page + 1, start_page = skip_set_size, end_page = pg - skip_set_size, - option_pages = [1], + option_pages = [ 1 ], // construct default options pages array option_pages_start_page = (large_collection) ? skip_set_size : 1; @@ -285,7 +286,7 @@ option_pages = $.grep(option_pages, function(value, indx) { return $.inArray(value, option_pages) === indx; }) - .sort(function(a,b) { return a - b; }); + .sort(function(a, b) { return a - b; }); return option_pages; }, @@ -367,7 +368,7 @@ pagerArrows(p); if ( !p.removeRows ) { hideRows(table, p); - $(table).bind('sortEnd.pager filterEnd.pager', function(){ + $(table).bind('sortEnd filterEnd '.split(' ').join(table.config.namespace + 'pager '), function(){ hideRows(table, p); }); } @@ -375,7 +376,7 @@ renderAjax = function(data, table, p, xhr, exception){ // process data - if ( typeof(p.ajaxProcessing) === "function" ) { + if ( typeof p.ajaxProcessing === 'function' ) { // ajaxProcessing result: [ total, rows, headers ] var i, j, t, hsh, $f, $sh, $headers, $h, icon, th, d, l, rr_count, len, c = table.config, @@ -389,7 +390,7 @@ if ( exception ) { if (c.debug) { - ts.log('Pager: >> Ajax Error', xhr, exception); + console.error('Pager: >> Ajax Error', xhr, exception); } ts.showError(table, xhr.status === 0 ? 'Not connected, verify Network' : @@ -482,28 +483,28 @@ p.last.currentFilters = p.currentFilters; p.last.sortList = (c.sortList || []).join(','); updatePageDisplay(table, p, false); - $table.trigger('updateCache', [function(){ + $table.trigger('updateCache', [ function(){ if (p.initialized) { // apply widgets after table has rendered & after a delay to prevent // multiple applyWidget blocking code from blocking this trigger setTimeout(function(){ if (c.debug) { - ts.log('Pager: Triggering pagerChange'); + console.log('Pager: Triggering pagerChange'); } $table .trigger('applyWidgets') .trigger('pagerChange', p); updatePageDisplay(table, p, true); - }, 0); + }, 0); } - }]); + } ]); } if (!p.initialized) { p.initialized = true; p.initializing = false; if (table.config.debug) { - ts.log('Pager: Triggering pagerInitialized'); + console.log('Pager: Triggering pagerInitialized'); } $(table) .trigger('applyWidgets') @@ -512,18 +513,19 @@ } }, - getAjax = function(table, p){ + getAjax = function(table, p) { var url = getAjaxUrl(table, p), - $doc = $(document), - counter, - c = table.config; + $doc = $(document), + counter, + c = table.config, + namespace = c.namespace + 'pager'; if ( url !== '' ) { if (c.showProcessing) { ts.isProcessing(table, true); // show loading icon } - $doc.bind('ajaxError.pager', function(e, xhr, settings, exception) { + $doc.bind('ajaxError' + namespace, function(e, xhr, settings, exception) { renderAjax(null, table, p, xhr, exception); - $doc.unbind('ajaxError.pager'); + $doc.unbind('ajaxError' + namespace); }); counter = ++p.ajaxCounter; @@ -536,13 +538,13 @@ return; } renderAjax(data, table, p, jqxhr); - $doc.unbind('ajaxError.pager'); + $doc.unbind('ajaxError' + namespace); if (typeof p.oldAjaxSuccess === 'function') { p.oldAjaxSuccess(data); } }; if (c.debug) { - ts.log('Pager: Ajax initialized', p.ajaxObject); + console.log('Pager: Ajax initialized', p.ajaxObject); } $.ajax(p.ajaxObject); } @@ -553,7 +555,7 @@ c = table.config, url = (p.ajaxUrl) ? p.ajaxUrl // allow using "{page+1}" in the url string to switch to a non-zero based index - .replace(/\{page([\-+]\d+)?\}/, function(s,n){ return p.page + (n ? parseInt(n, 10) : 0); }) + .replace(/\{page([\-+]\d+)?\}/, function(s, n){ return p.page + (n ? parseInt(n, 10) : 0); }) .replace(/\{size\}/g, p.size) : '', sortList = c.sortList, filterList = p.currentFilters || $(table).data('lastSearch') || [], @@ -582,11 +584,11 @@ url = url.replace(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : filterCol ); p.currentFilters = filterList; } - if ( typeof(p.customAjaxUrl) === "function" ) { + if ( typeof p.customAjaxUrl === 'function' ) { url = p.customAjaxUrl(table, url); } if (c.debug) { - ts.log('Pager: Ajax url = ' + url); + console.log('Pager: Ajax url = ' + url); } return url; }, @@ -601,7 +603,7 @@ e = p.size; if ( l < 1 ) { if (c.debug) { - ts.log('Pager: >> No rows for pager to render'); + console.warn('Pager: >> No rows for pager to render'); } // empty table, abort! return; @@ -614,7 +616,7 @@ p.isDisabled = false; // needed because sorting will change the page and re-enable the pager if (p.initialized) { if (c.debug) { - ts.log('Pager: Triggering pagerChange'); + console.log('Pager: Triggering pagerChange'); } $t.trigger('pagerChange', p); } @@ -645,13 +647,13 @@ updatePageDisplay(table, p); if (table.isUpdating) { if (c.debug) { - ts.log('Pager: Triggering updateComplete'); + console.log('Pager: Triggering updateComplete'); } $t.trigger('updateComplete', [ table, true ]); } }, - showAllRows = function(table, p){ + showAllRows = function(table, p) { var index, $controls, len; if ( p.ajax ) { pagerArrows(p, true); @@ -669,7 +671,7 @@ renderTable(table, table.config.rowsCopy, p); $(table).trigger('applyWidgets'); if (table.config.debug) { - ts.log('Pager: Disabled'); + console.log('Pager: Disabled'); } } // disable size selector @@ -728,7 +730,7 @@ (l.optAjaxUrl || '') === (p.ajaxUrl || '') && l.sortList === (c.sortList || []).join(',') ) { return; } if (c.debug) { - ts.log('Pager: Changing to page ' + p.page); + console.log('Pager: Changing to page ' + p.page); } p.last = { page : p.page, @@ -748,14 +750,14 @@ $.data(table, 'pagerLastPage', p.page); if (p.initialized && pageMoved !== false) { if (c.debug) { - ts.log('Pager: Triggering pageMoved'); + console.log('Pager: Triggering pageMoved'); } $t .trigger('pageMoved', p) .trigger('applyWidgets'); if (table.isUpdating) { if (c.debug) { - ts.log('Pager: Triggering updateComplete'); + console.log('Pager: Triggering updateComplete'); } $t.trigger('updateComplete', [ table, true ]); } @@ -798,19 +800,20 @@ moveToPage(table, p); }, - destroyPager = function(table, p){ + destroyPager = function(table, p) { showAllRows(table, p); p.$container.hide(); // hide pager - table.config.appender = null; // remove pager appender function + var c = table.config; + c.appender = null; // remove pager appender function p.initialized = false; - delete table.config.rowsCopy; - $(table).unbind( pagerEvents.split(' ').join('.pager ').replace(/\s+/g, ' ') ); + delete c.rowsCopy; + $(table).unbind( pagerEvents.split(' ').join(c.namespace + 'pager ').replace(/\s+/g, ' ') ); if (ts.storage) { ts.storage(table, p.storageKey, ''); } }, - enablePager = function(table, p, triggered){ + enablePager = function(table, p, triggered) { var info, c = table.config; p.$size.add(p.$goto).add(p.$container.find('.ts-startRow, .ts-page')) @@ -834,7 +837,7 @@ setPageSize(table, p.size, p); hideRowsSetup(table, p); if (c.debug) { - ts.log('Pager: Enabled'); + console.log('Pager: Enabled'); } } }; @@ -863,12 +866,13 @@ wo = c.widgetOptions, p = c.pager = $.extend( true, {}, $.tablesorterPager.defaults, settings ), $t = c.$table, + namespace = c.namespace + 'pager', // added in case the pager is reinitialized after being destroyed. pager = p.$container = $(p.container).addClass('tablesorter-pager').show(); // save a copy of the original settings p.settings = $.extend( true, {}, $.tablesorterPager.defaults, settings ); if (c.debug) { - ts.log('Pager: Initializing'); + console.log('Pager: Initializing'); } p.oldAjaxSuccess = p.oldAjaxSuccess || p.ajaxObject.success; c.appender = $this.appender; @@ -884,8 +888,8 @@ p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.slice(1) + '|' + c.cssChildRow + ')'); $t - .unbind( pagerEvents.split(' ').join('.pager ').replace(/\s+/g, ' ') ) - .bind('filterInit.pager filterStart.pager', function(e, filters) { + .unbind( pagerEvents.split(' ').join(namespace + ' ').replace(/\s+/g, ' ') ) + .bind('filterInit filterStart '.split(' ').join(namespace + ' '), function(e, filters) { p.currentFilters = $.isArray(filters) ? filters : c.$table.data('lastSearch'); // don't change page if filters are the same (pager updating, etc) if (e.type === 'filterStart' && p.pageReset !== false && (c.lastCombinedFilter || '') !== (p.currentFilters || []).join('')) { @@ -893,7 +897,7 @@ } }) // update pager after filter widget completes - .bind('filterEnd.pager sortEnd.pager', function() { + .bind('filterEnd sortEnd '.split(' ').join(namespace + ' '), function() { p.currentFilters = c.$table.data('lastSearch'); if (p.initialized || p.initializing) { if (c.delayInit && c.rowsCopy && c.rowsCopy.length === 0) { @@ -905,19 +909,19 @@ c.$table.trigger('applyWidgets'); } }) - .bind('disable.pager', function(e){ + .bind('disable' + namespace, function(e){ e.stopPropagation(); showAllRows(table, p); }) - .bind('enable.pager', function(e){ + .bind('enable' + namespace, function(e){ e.stopPropagation(); enablePager(table, p, true); }) - .bind('destroy.pager', function(e){ + .bind('destroy' + namespace, function(e){ e.stopPropagation(); destroyPager(table, p); }) - .bind('updateComplete.pager', function(e, table, triggered){ + .bind('updateComplete' + namespace, function(e, table, triggered){ e.stopPropagation(); // table can be unintentionally undefined in tablesorter v2.17.7 and earlier // don't recalculate total rows/pages if using ajax @@ -936,13 +940,13 @@ changeHeight(table, p); updatePageDisplay(table, p, true); }) - .bind('pageSize.pager refreshComplete.pager', function(e,v){ + .bind('pageSize refreshComplete '.split(' ').join(namespace + ' '), function(e, v){ e.stopPropagation(); setPageSize(table, parseInt(v, 10) || p.settings.size || 10, p); hideRows(table, p); updatePageDisplay(table, p, false); }) - .bind('pageSet.pager pagerUpdate.pager', function(e,v){ + .bind('pageSet pagerUpdate '.split(' ').join(namespace + ' '), function(e, v){ e.stopPropagation(); // force pager refresh if (e.type === 'pagerUpdate') { @@ -953,7 +957,7 @@ moveToPage(table, p, true); updatePageDisplay(table, p, false); }) - .bind('pageAndSize.pager', function(e, page, size){ + .bind('pageAndSize' + namespace, function(e, page, size){ e.stopPropagation(); p.page = (parseInt(page, 10) || 1) - 1; setPageSize(table, parseInt(size, 10) || p.settings.size || 10, p); @@ -966,12 +970,12 @@ ctrls = [ p.cssFirst, p.cssPrev, p.cssNext, p.cssLast ]; fxn = [ moveToFirstPage, moveToPrevPage, moveToNextPage, moveToLastPage ]; if (c.debug && !pager.length) { - ts.log('Pager: >> Container not found'); + console.warn('Pager: >> Container not found'); } pager.find(ctrls.join(',')) - .attr("tabindex", 0) - .unbind('click.pager') - .bind('click.pager', function(e){ + .attr('tabindex', 0) + .unbind('click' + namespace) + .bind('click' + namespace, function(e){ e.stopPropagation(); var i, $t = $(this), l = ctrls.length; if ( !$t.hasClass(p.cssDisabled) ) { @@ -988,21 +992,21 @@ p.$goto = pager.find(p.cssGoto); if ( p.$goto.length ) { p.$goto - .unbind('change.pager') - .bind('change.pager', function(){ + .unbind('change' + namespace) + .bind('change' + namespace, function(){ p.page = $(this).val() - 1; moveToPage(table, p, true); updatePageDisplay(table, p, false); }); } else if (c.debug) { - ts.log('Pager: >> Goto selector not found'); + console.warn('Pager: >> Goto selector not found'); } // page size selector p.$size = pager.find(p.cssPageSize); if ( p.$size.length ) { // setting an option as selected appears to cause issues with initial page size p.$size.find('option').removeAttr('selected'); - p.$size.unbind('change.pager').bind('change.pager', function() { + p.$size.unbind('change' + namespace).bind('change' + namespace, function() { p.$size.val( $(this).val() ); // in case there are more than one pagers if ( !$(this).hasClass(p.cssDisabled) ) { setPageSize(table, parseInt( $(this).val(), 10 ), p); @@ -1011,7 +1015,7 @@ return false; }); } else if (c.debug) { - ts.log('Pager: >> Size selector not found'); + console.warn('Pager: >> Size selector not found'); } // clear initialized flag @@ -1020,17 +1024,18 @@ $t.trigger('pagerBeforeInitialized', p); enablePager(table, p, false); - if ( typeof(p.ajaxUrl) === 'string' ) { + if ( typeof p.ajaxUrl === 'string' ) { // ajax pager; interact with database p.ajax = true; - //When filtering with ajax, allow only custom filtering function, disable default filtering since it will be done server side. + // When filtering with ajax, allow only custom filtering function, disable default + // filtering since it will be done server side. c.widgetOptions.filter_serversideFiltering = true; c.serverSideSorting = true; moveToPage(table, p); } else { p.ajax = false; // Regular pager; all rows stored in memory - $(this).trigger("appendCache", true); + $(this).trigger('appendCache', true); hideRowsSetup(table, p); } @@ -1040,7 +1045,7 @@ p.initialized = true; moveToPage(table, p); if (c.debug) { - ts.log('Pager: Triggering pagerInitialized'); + console.log('Pager: Triggering pagerInitialized'); } c.$table.trigger('pagerInitialized', p); if ( !( c.widgetOptions.filter_initialized && ts.hasWidget(table, 'filter') ) ) { @@ -1083,9 +1088,9 @@ } }; -// extend plugin scope -$.fn.extend({ - tablesorterPager: $.tablesorterPager.construct -}); + // extend plugin scope + $.fn.extend({ + tablesorterPager: $.tablesorterPager.construct + }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.dragtable.mod.js b/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.dragtable.mod.js index debb642..04675cf 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.dragtable.mod.js +++ b/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.dragtable.mod.js @@ -129,7 +129,7 @@ pos = endIndex === cols ? false : endIndex <= startIndex, $rows = c.$table.children().children( 'tr' ); if ( c.debug ) { - ts.log( 'Inserting column ' + startIndex + ( pos ? ' before' : ' after' ) + ' column ' + endIndex ); + console.log( 'Inserting column ' + startIndex + ( pos ? ' before' : ' after' ) + ' column ' + endIndex ); } $rows.each( function() { $cols = $( this ).children(); diff --git a/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.quicksearch.js b/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.quicksearch.js deleted file mode 100644 index f1e311c..0000000 --- a/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.quicksearch.js +++ /dev/null @@ -1,195 +0,0 @@ -/* jQuery Quicksearch plugin - by riklomas https://github.com/riklomas/quicksearch - Modified to include childRows (for tablesorter) - - See http://stackoverflow.com/q/20342203/145346 for - more details -*/ -(function($, window, document, undefined) { - $.fn.quicksearch = function (target, opt) { - - var timeout, cache, rowcache, jq_results, val = '', e = this, options = $.extend({ - delay: 100, - selector: null, - stripeRows: null, - loader: null, - noResults: '', - childRow: 'tablesorter-childRow', // include child row with search results - matchedResultsCount: 0, - bind: 'keyup', - onBefore: function () { - return; - }, - onAfter: function () { - return; - }, - show: function () { - this.style.display = ""; - }, - hide: function () { - this.style.display = "none"; - }, - prepareQuery: function (val) { - return val.toLowerCase().split(' '); - }, - testQuery: function (query, txt) { - for (var i = 0; i < query.length; i += 1) { - if (txt.indexOf(query[i]) === -1) { - return false; - } - } - return true; - } - }, opt); - - this.go = function () { - - var len, i = 0, - numMatchedRows = 0, - noresults = true, - query = options.prepareQuery(val), - val_empty = (val.replace(' ', '').length === 0); - - for (i = 0, len = rowcache.length; i < len; i++) { - if (val_empty || options.testQuery(query, cache[i], rowcache[i]) || - ($(rowcache[i]).hasClass(options.childRow) && $(rowcache[i > 1 ? i - 1 : 0]).is(':visible'))) { - options.show.apply(rowcache[i]); - noresults = false; - numMatchedRows++; - } else { - options.hide.apply(rowcache[i]); - } - } - - if (noresults) { - this.results(false); - } else { - this.results(true); - this.stripe(); - } - - this.matchedResultsCount = numMatchedRows; - this.loader(false); - options.onAfter(); - - return this; - }; - - /* - * External API so that users can perform search programatically. - * */ - this.search = function (submittedVal) { - val = submittedVal; - e.trigger(); - }; - - /* - * External API to get the number of matched results as seen in - * https://github.com/ruiz107/quicksearch/commit/f78dc440b42d95ce9caed1d087174dd4359982d6 - * */ - this.currentMatchedResults = function() { - return this.matchedResultsCount; - }; - - this.stripe = function () { - - if (typeof options.stripeRows === "object" && options.stripeRows !== null) - { - var joined = options.stripeRows.join(' '); - var stripeRows_length = options.stripeRows.length; - - jq_results.not(':hidden').each(function (i) { - $(this).removeClass(joined).addClass(options.stripeRows[i % stripeRows_length]); - }); - } - - return this; - }; - - this.strip_html = function (input) { - var output = input.replace(new RegExp('<[^<]+\>', 'g'), ""); - output = $.trim(output.toLowerCase()); - return output; - }; - - this.results = function (bool) { - if (typeof options.noResults === "string" && options.noResults !== "") { - if (bool) { - $(options.noResults).hide(); - } else { - $(options.noResults).show(); - } - } - return this; - }; - - this.loader = function (bool) { - if (typeof options.loader === "string" && options.loader !== "") { - if (bool) { - $(options.loader).show(); - } else { - $(options.loader).hide(); - } - } - return this; - }; - - this.cache = function () { - - jq_results = $(target); - - if (typeof options.noResults === "string" && options.noResults !== "") { - jq_results = jq_results.not(options.noResults); - } - - var t = (typeof options.selector === "string") ? - jq_results.find(options.selector) : $(target).not(options.noResults); - cache = t.map(function () { - return e.strip_html(this.innerHTML); - }); - - rowcache = jq_results.map(function () { - return this; - }); - - /* - * Modified fix for sync-ing "val". - * Original fix https://github.com/michaellwest/quicksearch/commit/4ace4008d079298a01f97f885ba8fa956a9703d1 - * */ - val = val || this.val() || ""; - - return this.go(); - }; - - this.trigger = function () { - this.loader(true); - options.onBefore(); - - window.clearTimeout(timeout); - timeout = window.setTimeout(function () { - e.go(); - }, options.delay); - - return this; - }; - - this.cache(); - this.results(true); - this.stripe(); - this.loader(false); - - return this.each(function () { - - /* - * Changed from .bind to .on. - * */ - $(this).on(options.bind, function () { - - val = $(this).val(); - e.trigger(); - }); - }); - - }; - -}(jQuery, this, document)); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 1cfb15a..37ba337 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 06-30-2015 (v2.22.2)*/ +/*! tablesorter (FORK) - updated 07-28-2015 (v2.22.4)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.22.3 *//* +/*! TableSorter (FORK) v2.22.4 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -44,7 +44,7 @@ var ts = this; - ts.version = '2.22.3'; + ts.version = '2.22.4'; ts.parsers = []; ts.widgets = []; @@ -172,24 +172,6 @@ // These methods can be applied on table.config instance ts.instanceMethods = {}; - /* debuging utils */ - function log() { - var a = arguments[0], - s = arguments.length > 1 ? Array.prototype.slice.call(arguments) : a; - if (typeof console !== 'undefined' && typeof console.log !== 'undefined') { - console[ /error/i.test(a) ? 'error' : /warn/i.test(a) ? 'warn' : 'log' ](s); - } else { - alert(s); - } - } - - function benchmark(s, d) { - log(s + ' (' + (new Date().getTime() - d.getTime()) + 'ms)'); - } - - ts.log = log; - ts.benchmark = benchmark; - // $.isEmptyObject from jQuery v1.4 function isEmptyObject(obj) { /*jshint forin: false */ @@ -206,7 +188,7 @@ // node could be a jquery object // http://jsperf.com/jquery-vs-instanceof-jquery/2 $node = node.jquery ? node : $(node); - if (typeof(t) === 'string') { + if (typeof t === 'string') { // check data-attribute first when set to 'basic'; don't use node.innerText - it's really slow! // http://www.kellegous.com/j/2013/02/27/innertext-vs-textcontent/ if ( t === 'basic' && typeof ( te = $node.attr(c.textAttribute) ) !== 'undefined' ) { @@ -214,7 +196,7 @@ } return $.trim( node.textContent || $node.text() ); } else { - if (typeof(t) === 'function') { + if (typeof t === 'function') { return $.trim( t($node[0], c.table, cellIndex) ); } else if (typeof (te = ts.getColumnData( c.table, t, cellIndex )) === 'function') { return $.trim( te($node[0], c.table, cellIndex) ); @@ -237,7 +219,7 @@ nodeValue = ts.getElementText(c, node, cellIndex); $node = $(node); if (c.debug) { - log('Checking if value was empty on row ' + rowIndex + ', column: ' + cellIndex + ': "' + nodeValue + '"'); + console.log('Checking if value was empty on row ' + rowIndex + ', column: ' + cellIndex + ': "' + nodeValue + '"'); } } else { keepLooking = false; @@ -274,7 +256,7 @@ // make sure txt is a string (extractor may have converted it) parser.format( '' + txt, c.table, cell, colIndex ); if ( c.ignoreCase && typeof val === 'string' ) { - val = val.toLowerCase(); + val = val.toLowerCase(); } } return val; @@ -284,16 +266,16 @@ var rows, list, l, i, h, ch, np, p, e, time, tb, len, table = c.table, j = 0, - parsersDebug = ''; + debug = {}; // update table bodies in case we start with an empty table c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'); tb = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies; len = tb.length; if ( len === 0) { - return c.debug ? log('Warning: *Empty table!* Not building a parser cache') : ''; + return c.debug ? console.warn('Warning: *Empty table!* Not building a parser cache') : ''; } else if (c.debug) { time = new Date(); - log('Detecting parsers for each column'); + console[ console.group ? 'group' : 'log' ]('Detecting parsers for each column'); } list = { extractors: [], @@ -326,7 +308,12 @@ p = detectParserForColumn(c, rows, -1, i); } if (c.debug) { - parsersDebug += 'column:' + i + '; extractor:' + e.id + '; parser:' + p.id + '; string:' + c.strings[i] + '; empty: ' + c.empties[i] + '\n'; + debug[ '(' + i + ') ' + h.text() ] = { + parser : p.id, + extractor : e ? e.id : 'none', + string : c.strings[i], + empty : c.empties[i] + }; } list.parsers[i] = p; list.extractors[i] = e; @@ -334,9 +321,14 @@ } j += (list.parsers.length) ? len : 1; } - if (c.debug) { - log(parsersDebug ? parsersDebug : 'No parsers detected'); - benchmark('Completed detecting parsers', time); + if ( c.debug ) { + if ( !isEmptyObject( debug ) ) { + console[ console.table ? 'table' : 'log' ]( debug ); + } else { + console.warn( ' No parsers detected!' ); + } + console.log( 'Completed detecting parsers' + ts.benchmark( time ) ); + if ( console.groupEnd ) { console.groupEnd(); } } c.parsers = list.parsers; c.extractors = list.extractors; @@ -355,7 +347,7 @@ c.totalRows = 0; // if no parsers found, return - it's an empty table. if (!parsers) { - return c.debug ? log('Warning: *Empty table!* Not building a cache') : ''; + return c.debug ? console.warn('Warning: *Empty table!* Not building a cache') : ''; } if (c.debug) { cacheTime = new Date(); @@ -408,7 +400,7 @@ for ( j = 0; j < c.columns; ++j ) { if (typeof parsers[ j ] === 'undefined') { if ( c.debug ) { - log( 'No parser found for cell:', $row[ 0 ].cells[ j ], 'does it have a header?' ); + console.warn( 'No parser found for cell:', $row[ 0 ].cells[ j ], 'does it have a header?' ); } continue; } @@ -434,7 +426,7 @@ ts.isProcessing( table ); // remove processing icon } if ( c.debug ) { - benchmark( 'Building cache for ' + totalRows + ' rows', cacheTime ); + console.log( 'Building cache for ' + totalRows + ' rows' + ts.benchmark( cacheTime ) ); } } @@ -478,7 +470,7 @@ c.appender(table, rows); } if (c.debug) { - benchmark('Rebuilt table', appendTime); + console.log( 'Rebuilt table' + ts.benchmark(appendTime) ); } // apply table widgets; but not before ajax completes if (!init && !c.appender) { ts.applyWidget(table); } @@ -518,20 +510,22 @@ // set up header template t = c.headerTemplate.replace(/\{content\}/g, $t.html()).replace(/\{icon\}/g, $t.find('.' + ts.css.icon).length ? '' : i); if (c.onRenderTemplate) { - h = c.onRenderTemplate.apply($t, [index, t]); + h = c.onRenderTemplate.apply( $t, [ index, t ] ); if (h && typeof h === 'string') { t = h; } // only change t if something is returned } $t.html('<div class="' + ts.css.headerIn + '">' + t + '</div>'); // faster than wrapInner } - if (c.onRenderHeader) { c.onRenderHeader.apply($t, [index, c, c.$table]); } + if (c.onRenderHeader) { c.onRenderHeader.apply( $t, [ index, c, c.$table ] ); } // *** remove this.column value if no conflicts found elem.column = parseInt( $t.attr('data-column'), 10); - elem.order = formatSortingOrder( ts.getData($t, ch, 'sortInitialOrder') || c.sortInitialOrder ) ? [1,0,2] : [0,1,2]; + elem.order = formatSortingOrder( ts.getData( $t, ch, 'sortInitialOrder' ) || c.sortInitialOrder ) ? + [ 1, 0, 2 ] : // desc, asc, unsorted + [ 0, 1, 2 ]; // asc, desc, unsorted elem.count = -1; // set to -1 because clicking on the header automatically adds one elem.lockedOrder = false; lock = ts.getData($t, ch, 'lockedOrder') || false; if (typeof lock !== 'undefined' && lock !== false) { - elem.order = elem.lockedOrder = formatSortingOrder(lock) ? [1,1,1] : [0,0,0]; + elem.order = elem.lockedOrder = formatSortingOrder(lock) ? [ 1, 1, 1 ] : [ 0, 0, 0 ]; } $t.addClass(ts.css.header + ' ' + c.cssHeader); // add cell to headerList @@ -557,8 +551,8 @@ // enable/disable sorting updateHeader(table); if (c.debug) { - benchmark('Built headers:', time); - log(c.$headers); + console.log( 'Built headers:' + ts.benchmark( time ) ); + console.log( c.$headers ); } } @@ -601,9 +595,9 @@ list = c.sortList, len = list.length, none = ts.css.sortNone + ' ' + c.cssNone, - css = [ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc], + css = [ ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc ], cssIcon = [ c.cssIconAsc, c.cssIconDesc, c.cssIconNone ], - aria = ['ascending', 'descending'], + aria = [ 'ascending', 'descending' ], // find the footer $t = $(table).find('tfoot tr').children() .add( $( c.namespace + '_extra_headers' ) ) @@ -673,7 +667,7 @@ dir = ('' + val[1]).match(/^(1|d|s|o|n)/); dir = dir ? dir[0] : ''; // 0/(a)sc (default), 1/(d)esc, (s)ame, (o)pposite, (n)ext - switch(dir) { + switch (dir) { case '1': case 'd': // descending dir = 1; break; @@ -749,11 +743,11 @@ // add column to sort list order = cell.order[cell.count]; if (order < 2) { - c.sortList.push([indx, order]); + c.sortList.push([ indx, order ]); // add other columns if header spans across multiple if (cell.colSpan > 1) { for (col = 1; col < cell.colSpan; col++) { - c.sortList.push([indx + col, order]); + c.sortList.push([ indx + col, order ]); } } } @@ -764,7 +758,7 @@ for (col = 0; col < c.sortAppend.length; col++) { s = ts.isValueInArray(c.sortAppend[col][0], c.sortList); if (s >= 0) { - c.sortList.splice(s,1); + c.sortList.splice(s, 1); } } } @@ -778,7 +772,7 @@ // order.count seems to be incorrect when compared to cell.count s[1] = order.order[cell.count]; if (s[1] === 2) { - c.sortList.splice(col,1); + c.sortList.splice(col, 1); order.count = -1; } } @@ -787,11 +781,11 @@ // add column to sort list array order = cell.order[cell.count]; if (order < 2) { - c.sortList.push([indx, order]); + c.sortList.push([ indx, order ]); // add other columns if header spans across multiple if (cell.colSpan > 1) { for (col = 1; col < cell.colSpan; col++) { - c.sortList.push([indx + col, order]); + c.sortList.push([ indx + col, order ]); } } } @@ -865,10 +859,10 @@ x = dir ? a : b; y = dir ? b : a; // text sort function - if (typeof(cts) === 'function') { + if (typeof cts === 'function') { // custom OVERALL text sorter sort = cts(x[col], y[col], dir, col, table); - } else if (typeof(cts) === 'object' && cts.hasOwnProperty(col)) { + } else if (typeof cts === 'object' && cts.hasOwnProperty(col)) { // custom text sorter for a SPECIFIC COLUMN sort = cts[col](x[col], y[col], dir, col, table); } else { @@ -881,7 +875,9 @@ return a[c.columns].order - b[c.columns].order; }); } - if (c.debug) { benchmark('Sorting on ' + sortList.toString() + ' and dir ' + order + ' time', sortTime); } + if (c.debug) { + console.log( 'Sorting on ' + sortList.toString() + ' and dir ' + order + ' time' + ts.benchmark(sortTime) ); + } } function resortComplete(c, callback){ @@ -901,14 +897,14 @@ // this will catch spamming of the updateCell method if (resrt !== false && !c.serverSideSorting && !c.table.isProcessing) { if (sl.length) { - c.$table.trigger('sorton', [sl, function(){ + c.$table.trigger('sorton', [ sl, function(){ resortComplete(c, callback); - }, true]); + }, true ]); } else { - c.$table.trigger('sortReset', [function(){ + c.$table.trigger('sortReset', [ function(){ resortComplete(c, callback); ts.applyWidget(c.table, false); - }]); + } ]); } } else { resortComplete(c, callback); @@ -1108,10 +1104,10 @@ ts.construct = function(settings) { return this.each(function() { var table = this, - // merge & extend config options - c = $.extend(true, {}, ts.defaults, settings, ts.instanceMethods); - // save initial settings - c.originalSettings = settings; + // merge & extend config options + c = $.extend(true, {}, ts.defaults, settings, ts.instanceMethods); + // save initial settings + c.originalSettings = settings; // create a table from data (build table widget) if (!table.hasInitialized && ts.buildTable && this.nodeName !== 'TABLE') { // return the table (in case the original target is the table's container) @@ -1125,7 +1121,14 @@ ts.setup = function(table, c) { // if no thead or tbody, or tablesorter is already present, quit if (!table || !table.tHead || table.tBodies.length === 0 || table.hasInitialized === true) { - return c.debug ? log('ERROR: stopping initialization! No table, thead, tbody or tablesorter has already been initialized') : ''; + if ( c.debug ) { + if ( table.hasInitialized ) { + console.warn( 'Stopping initialization. Tablesorter has already been initialized' ); + } else { + console.error( 'Stopping initialization! No table, thead or tbody' ); + } + } + return; } var k = '', @@ -1139,7 +1142,10 @@ table.config = c; // save the settings where they read $.data(table, 'tablesorter', c); - if (c.debug) { $.data( table, 'startoveralltimer', new Date()); } + if (c.debug) { + console[ console.group ? 'group' : 'log' ]( 'Initializing tablesorter' ); + $.data( table, 'startoveralltimer', new Date()); + } // removing this in version 3 (only supports jQuery 1.7+) c.supportsDataObject = (function(version) { @@ -1166,7 +1172,7 @@ c.namespace = '.tablesorter' + Math.random().toString(16).slice(2); } else { // make sure namespace starts with a period & doesn't have weird characters - c.namespace = '.' + c.namespace.replace(/\W/g,''); + c.namespace = '.' + c.namespace.replace(/\W/g, ''); } c.$table.children().children('tr').attr('role', 'row'); @@ -1210,7 +1216,7 @@ ts.applyWidget(table, true); // if user has supplied a sort list to constructor if (c.sortList.length > 0) { - $table.trigger('sorton', [c.sortList, {}, !c.initWidgets, true]); + $table.trigger('sorton', [ c.sortList, {}, !c.initWidgets, true ]); } else { setHeadersCss(table); if (c.initWidgets) { @@ -1238,7 +1244,8 @@ table.hasInitialized = true; table.isProcessing = false; if (c.debug) { - ts.benchmark('Overall initialization time', $.data( table, 'startoveralltimer')); + console.log( 'Overall initialization time: ' + ts.benchmark( $.data( table, 'startoveralltimer') ) ); + if ( c.debug && console.groupEnd ) { console.groupEnd(); } } $table.trigger('tablesorter-initialized', table); if (typeof c.initialized === 'function') { c.initialized(table); } @@ -1249,22 +1256,22 @@ table = $(table)[0]; var overallWidth, percent, $tbodies, len, index, c = table.config, - colgroup = c.$table.children('colgroup'); + $colgroup = c.$table.children('colgroup'); // remove plugin-added colgroup, in case we need to refresh the widths - if (colgroup.length && colgroup.hasClass(ts.css.colgroup)) { - colgroup.remove(); + if ($colgroup.length && $colgroup.hasClass(ts.css.colgroup)) { + $colgroup.remove(); } if (c.widthFixed && c.$table.children('colgroup').length === 0) { - colgroup = $('<colgroup class="' + ts.css.colgroup + '">'); + $colgroup = $('<colgroup class="' + ts.css.colgroup + '">'); overallWidth = c.$table.width(); // only add col for visible columns - fixes #371 - $tbodies = c.$tbodies.find('tr:first').children(':visible'); //.each(function() + $tbodies = c.$tbodies.find('tr:first').children(':visible'); // .each(function() len = $tbodies.length; for ( index = 0; index < len; index++ ) { percent = parseInt( ( $tbodies.eq( index ).width() / overallWidth ) * 1000, 10 ) / 10 + '%'; - colgroup.append( $('<col>').css('width', percent) ); + $colgroup.append( $('<col>').css('width', percent) ); } - c.$table.prepend(colgroup); + c.$table.prepend($colgroup); } }; @@ -1311,12 +1318,12 @@ cellId = rowIndex + '-' + $cell.index(); rowSpan = cell.rowSpan || 1; colSpan = cell.colSpan || 1; - if (typeof(matrix[rowIndex]) === 'undefined') { + if (typeof matrix[rowIndex] === 'undefined') { matrix[rowIndex] = []; } // Find first available column in the first row for (k = 0; k < matrix[rowIndex].length + 1; k++) { - if (typeof(matrix[rowIndex][k]) === 'undefined') { + if (typeof matrix[rowIndex][k] === 'undefined') { firstAvailCol = k; break; } @@ -1325,7 +1332,7 @@ // add data-column $cell.attr({ 'data-column' : firstAvailCol }); // 'data-row' : rowIndex for (k = rowIndex; k < rowIndex + rowSpan; k++) { - if (typeof(matrix[k]) === 'undefined') { + if (typeof matrix[k] === 'undefined') { matrix[k] = []; } matrixrow = matrix[k]; @@ -1340,23 +1347,23 @@ // *** Process table *** // add processing indicator - ts.isProcessing = function(table, toggle, $ths) { - table = $(table); - var c = table[0].config, + ts.isProcessing = function( $table, toggle, $ths ) { + $table = $( $table ); + var c = $table[0].config, // default to all headers - $h = $ths || table.find('.' + ts.css.header); + $h = $ths || $table.find('.' + ts.css.header); if (toggle) { // don't use sortList if custom $ths used if (typeof $ths !== 'undefined' && c.sortList.length > 0) { // get headers from the sortList $h = $h.filter(function(){ - // get data-column from attr to keep compatibility with jQuery 1.2.6 + // get data-column from attr to keep compatibility with jQuery 1.2.6 return this.sortDisabled ? false : ts.isValueInArray( parseFloat($(this).attr('data-column')), c.sortList) >= 0; }); } - table.add($h).addClass(ts.css.processing + ' ' + c.cssProcessing); + $table.add($h).addClass(ts.css.processing + ' ' + c.cssProcessing); } else { - table.add($h).removeClass(ts.css.processing + ' ' + c.cssProcessing); + $table.add($h).removeClass(ts.css.processing + ' ' + c.cssProcessing); } }; @@ -1367,11 +1374,11 @@ var holdr; if (getIt) { table.isProcessing = true; - $tb.before('<span class="tablesorter-savemyplace"/>'); + $tb.before('<colgroup class="tablesorter-savemyplace"/>'); holdr = ($.fn.detach) ? $tb.detach() : $tb.remove(); return holdr; } - holdr = $(table).find('span.tablesorter-savemyplace'); + holdr = $(table).find('colgroup.tablesorter-savemyplace'); $tb.insertAfter( holdr ); holdr.remove(); table.isProcessing = false; @@ -1481,13 +1488,14 @@ var events, $t = $(table), c = table.config, + debug = c.debug, $h = $t.find('thead:first'), $r = $h.find('tr.' + ts.css.headerRow).removeClass(ts.css.headerRow + ' ' + c.cssHeaderRow), $f = $t.find('tfoot:first > tr').children('th, td'); if (removeClasses === false && $.inArray('uitheme', c.widgets) >= 0) { // reapply uitheme classes, in case we want to maintain appearance - $t.trigger('applyWidgetId', ['uitheme']); - $t.trigger('applyWidgetId', ['zebra']); + $t.trigger('applyWidgetId', [ 'uitheme' ]); + $t.trigger('applyWidgetId', [ 'zebra' ]); } // remove widget added rows, just in case $h.find('tr').not($r).remove(); @@ -1499,7 +1507,7 @@ .removeData('tablesorter') .unbind( events.replace(/\s+/g, ' ') ); c.$headers.add($f) - .removeClass( [ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone].join(' ') ) + .removeClass( [ ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone ].join(' ') ) .removeAttr('data-column') .removeAttr('aria-label') .attr('aria-disabled', 'true'); @@ -1512,6 +1520,9 @@ if (typeof callback === 'function') { callback(table); } + if (debug) { + console.log( 'tablesorter has been removed' ); + } }; // *** sort functions *** @@ -1660,10 +1671,11 @@ }; // *** utilities *** - ts.isValueInArray = function(column, arry) { - var indx, len = arry.length; - for (indx = 0; indx < len; indx++) { - if (arry[indx][0] === column) { + ts.isValueInArray = function( column, arry ) { + var indx, + len = arry && arry.length || 0; + for ( indx = 0; indx < len; indx++ ) { + if ( arry[ indx ][ 0 ] === column ) { return indx; } } @@ -1704,9 +1716,9 @@ ts.widgets.push(widget); }; - ts.hasWidget = function(table, name){ - table = $(table); - return table.length && table[0].config && table[0].config.widgetInit[name] || false; + ts.hasWidget = function( $table, name ) { + $table = $( $table ); + return $table.length && $table[0].config && $table[0].config.widgetInit[name] || false; }; ts.getWidgetById = function(name) { @@ -1735,7 +1747,7 @@ ts.applyWidget = function(table, init, callback) { table = $(table)[0]; // in case this is called externally - var indx, len, name, + var indx, len, names, widget, name, applied, c = table.config, wo = c.widgetOptions, tableClass = ' ' + c.table.className + ' ', @@ -1746,7 +1758,7 @@ if (c.debug) { time = new Date(); } // look for widgets to apply from in table class // stop using \b otherwise this matches 'ui-widget-content' & adds 'content' widget - wd = new RegExp( '\\s' + c.widgetClass.replace( /\{name\}/i, '([\\w-]+)' )+ '\\s', 'g' ); + wd = new RegExp( '\\s' + c.widgetClass.replace( /\{name\}/i, '([\\w-]+)' ) + '\\s', 'g' ); if ( tableClass.match( wd ) ) { // extract out the widget id from the table class (widget id's can include dashes) w = tableClass.match( wd ); @@ -1763,11 +1775,11 @@ c.widgets = $.grep(c.widgets, function(v, k){ return $.inArray(v, c.widgets) === k; }); - name = c.widgets || []; - len = name.length; + names = c.widgets || []; + len = names.length; // build widget array & add priority as needed for (indx = 0; indx < len; indx++) { - wd = ts.getWidgetById(name[indx]); + wd = ts.getWidgetById(names[indx]); if (wd && wd.id) { // set priority to 10 if not defined if (!wd.priority) { wd.priority = 10; } @@ -1780,28 +1792,47 @@ }); // add/update selected widgets len = widgets.length; + if (c.debug) { + console[ console.group ? 'group' : 'log' ]( 'Start ' + ( init ? 'initializing' : 'applying' ) + ' widgets' ); + } for (indx = 0; indx < len; indx++) { - if (widgets[indx]) { - if ( init || !( c.widgetInit[ widgets[indx].id ] ) ) { + widget = widgets[indx]; + if (widget) { + name = widget.id; + applied = false; + if (c.debug) { time2 = new Date(); } + + if ( init || !( c.widgetInit[ name ] ) ) { // set init flag first to prevent calling init more than once (e.g. pager) - c.widgetInit[ widgets[indx].id ] = true; + c.widgetInit[ name ] = true; if (table.hasInitialized) { // don't reapply widget options on tablesorter init ts.applyWidgetOptions( table, c ); } - if ( 'init' in widgets[indx] ) { - if (c.debug) { time2 = new Date(); } - widgets[indx].init(table, widgets[indx], c, wo); - if (c.debug) { ts.benchmark('Initializing ' + widgets[indx].id + ' widget', time2); } + if ( 'init' in widget ) { + applied = true; + if (c.debug) { + console[ console.group ? 'group' : 'log' ]( 'Initializing ' + name + ' widget' ); + } + widget.init(table, widget, c, wo); + } + } + if ( !init && 'format' in widget ) { + applied = true; + if (c.debug) { + console[ console.group ? 'group' : 'log' ]( 'Updating ' + name + ' widget' ); } + widget.format(table, c, wo, false); } - if ( !init && 'format' in widgets[indx] ) { - if (c.debug) { time2 = new Date(); } - widgets[indx].format(table, c, wo, false); - if (c.debug) { ts.benchmark( ( init ? 'Initializing ' : 'Applying ' ) + widgets[indx].id + ' widget', time2); } + if (c.debug) { + if (applied) { + console.log( 'Completed ' + ( init ? 'initializing ' : 'applying ' ) + name + ' widget' + ts.benchmark( time2 ) ); + if ( console.groupEnd ) { console.groupEnd(); } + } } } } + if ( c.debug && console.groupEnd ) { console.groupEnd(); } // callback executed on init only if (!init && typeof callback === 'function') { callback(table); @@ -1813,7 +1844,7 @@ }, 0); if (c.debug) { w = c.widgets.length; - benchmark('Completed ' + (init === true ? 'initializing ' : 'applying ') + w + ' widget' + (w !== 1 ? 's' : ''), time); + console.log( 'Completed ' + (init === true ? 'initializing ' : 'applying ') + w + ' widget' + (w !== 1 ? 's' : '') + ts.benchmark(time) ); } }; @@ -1841,7 +1872,10 @@ widget = ts.getWidgetById(name[i]); indx = $.inArray( name[i], c.widgets ); if ( widget && 'remove' in widget ) { - if (c.debug && indx >= 0) { log( 'Removing "' + name[i] + '" widget' ); } + if (c.debug && indx >= 0) { console.log( 'Removing "' + name[i] + '" widget' ); } + if ( c.debug ) { + console.log( ( refreshing ? 'Refreshing' : 'Removing' ) + ' "' + name[i] + '" widget' ); + } widget.remove(table, c, c.widgetOptions, refreshing); c.widgetInit[ name[i] ] = false; } @@ -1889,7 +1923,11 @@ allColumns = column === 'all', data = { raw : [], parsed: [], $cell: [] }, c = table.config; - if ( !isEmptyObject( c ) ) { + if ( isEmptyObject( c ) ) { + if ( c.debug ) { + console.warn( 'No cache found - aborting getColumnText function!' ); + } + } else { tbodyLen = c.$tbodies.length; for ( tbodyIndex = 0; tbodyIndex < tbodyLen; tbodyIndex++ ) { cache = c.cache[ tbodyIndex ].normalized; @@ -1954,13 +1992,13 @@ typeof table !== 'undefined' ? table : true; if (t) { // US Format - 1,234,567.89 -> 1234567.89 - s = s.replace(/,/g,''); + s = s.replace(/,/g, ''); } else { // German Format = 1.234.567,89 -> 1234567.89 // French Format = 1 234 567,89 -> 1234567.89 - s = s.replace(/[\s|\.]/g,'').replace(/,/g,'.'); + s = s.replace(/[\s|\.]/g, '').replace(/,/g, '.'); } - if(/^\s*\([.\d]+\)/.test(s)) { + if (/^\s*\([.\d]+\)/.test(s)) { // make (#) into a negative number -> (10) = -10 s = s.replace(/^\s*\(([.\d]+)\)/, '-$1'); } @@ -1985,6 +2023,23 @@ tablesorter: ts.construct }); + // set up debug logs + if ( !( console && console.log ) ) { + ts.logs = []; + /*jshint -W020 */ + console = {}; + console.log = console.warn = console.error = console.table = function() { + ts.logs.push( [ Date.now(), arguments ] ); + }; + } + + ts.log = function(){ + console.log( arguments ); + }; + ts.benchmark = function( diff ) { + return ( ' (' + ( new Date().getTime() - diff.getTime() ) + 'ms)' ); + }; + // add default parsers ts.addParser({ id: 'no-parser', @@ -2028,7 +2083,7 @@ ts.addParser({ id: 'currency', is: function(s) { - return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test((s || '').replace(/[+\-,. ]/g,'')); // £$€¤¥¢ + return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test((s || '').replace(/[+\-,. ]/g, '')); // £$€¤¥¢ }, format: function(s, table) { var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ''), table); @@ -2103,7 +2158,7 @@ id: 'shortDate', // 'mmddyyyy', 'ddmmyyyy' or 'yyyymmdd' is: function(s) { // testing for ##-##-#### or ####-##-##, so it's not perfect; time can be included - return (/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/).test((s || '').replace(/\s+/g,' ').replace(/[\-.,]/g, '/')); + return (/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/).test((s || '').replace(/\s+/g, ' ').replace(/[\-.,]/g, '/')); }, format: function(s, table, cell, cellIndex) { if (s) { @@ -2196,450 +2251,450 @@ /*! Widget: storage - updated 3/26/2015 (v2.21.3) */ ;(function ($, window, document) { -'use strict'; - -var ts = $.tablesorter || {}; -// *** Store data in local storage, with a cookie fallback *** -/* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json) - if you need it, then include https://github.com/douglascrockford/JSON-js - - $.parseJSON is not available is jQuery versions older than 1.4.1, using older - versions will only allow storing information for one page at a time - - // *** Save data (JSON format only) *** - // val must be valid JSON... use http://jsonlint.com/ to ensure it is valid - var val = { "mywidget" : "data1" }; // valid JSON uses double quotes - // $.tablesorter.storage(table, key, val); - $.tablesorter.storage(table, 'tablesorter-mywidget', val); - - // *** Get data: $.tablesorter.storage(table, key); *** - v = $.tablesorter.storage(table, 'tablesorter-mywidget'); - // val may be empty, so also check for your data - val = (v && v.hasOwnProperty('mywidget')) ? v.mywidget : ''; - alert(val); // "data1" if saved, or "" if not -*/ -ts.storage = function(table, key, value, options) { - table = $(table)[0]; - var cookieIndex, cookies, date, - hasStorage = false, - values = {}, - c = table.config, - wo = c && c.widgetOptions, - storageType = ( options && options.useSessionStorage ) || ( wo && wo.storage_useSessionStorage ) ? - 'sessionStorage' : 'localStorage', - $table = $(table), - // id from (1) options ID, (2) table "data-table-group" attribute, (3) widgetOptions.storage_tableId, - // (4) table ID, then (5) table index - id = options && options.id || - $table.attr( options && options.group || wo && wo.storage_group || 'data-table-group') || - wo && wo.storage_tableId || table.id || $('.tablesorter').index( $table ), - // url from (1) options url, (2) table "data-table-page" attribute, (3) widgetOptions.storage_fixedUrl, - // (4) table.config.fixedUrl (deprecated), then (5) window location path - url = options && options.url || - $table.attr(options && options.page || wo && wo.storage_page || 'data-table-page') || - wo && wo.storage_fixedUrl || c && c.fixedUrl || window.location.pathname; - // https://gist.github.com/paulirish/5558557 - if (storageType in window) { - try { - window[storageType].setItem('_tmptest', 'temp'); - hasStorage = true; - window[storageType].removeItem('_tmptest'); - } catch(error) { - if (c && c.debug) { - ts.log( storageType + ' is not supported in this browser' ); + 'use strict'; + + var ts = $.tablesorter || {}; + // *** Store data in local storage, with a cookie fallback *** + /* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json) + if you need it, then include https://github.com/douglascrockford/JSON-js + + $.parseJSON is not available is jQuery versions older than 1.4.1, using older + versions will only allow storing information for one page at a time + + // *** Save data (JSON format only) *** + // val must be valid JSON... use http://jsonlint.com/ to ensure it is valid + var val = { "mywidget" : "data1" }; // valid JSON uses double quotes + // $.tablesorter.storage(table, key, val); + $.tablesorter.storage(table, 'tablesorter-mywidget', val); + + // *** Get data: $.tablesorter.storage(table, key); *** + v = $.tablesorter.storage(table, 'tablesorter-mywidget'); + // val may be empty, so also check for your data + val = (v && v.hasOwnProperty('mywidget')) ? v.mywidget : ''; + alert(val); // 'data1' if saved, or '' if not + */ + ts.storage = function(table, key, value, options) { + table = $(table)[0]; + var cookieIndex, cookies, date, + hasStorage = false, + values = {}, + c = table.config, + wo = c && c.widgetOptions, + storageType = ( options && options.useSessionStorage ) || ( wo && wo.storage_useSessionStorage ) ? + 'sessionStorage' : 'localStorage', + $table = $(table), + // id from (1) options ID, (2) table 'data-table-group' attribute, (3) widgetOptions.storage_tableId, + // (4) table ID, then (5) table index + id = options && options.id || + $table.attr( options && options.group || wo && wo.storage_group || 'data-table-group') || + wo && wo.storage_tableId || table.id || $('.tablesorter').index( $table ), + // url from (1) options url, (2) table 'data-table-page' attribute, (3) widgetOptions.storage_fixedUrl, + // (4) table.config.fixedUrl (deprecated), then (5) window location path + url = options && options.url || + $table.attr(options && options.page || wo && wo.storage_page || 'data-table-page') || + wo && wo.storage_fixedUrl || c && c.fixedUrl || window.location.pathname; + // https://gist.github.com/paulirish/5558557 + if (storageType in window) { + try { + window[storageType].setItem('_tmptest', 'temp'); + hasStorage = true; + window[storageType].removeItem('_tmptest'); + } catch (error) { + if (c && c.debug) { + console.warn( storageType + ' is not supported in this browser' ); + } } } - } - // *** get value *** - if ($.parseJSON) { - if (hasStorage) { - values = $.parseJSON( window[storageType][key] || 'null' ) || {}; - } else { - // old browser, using cookies - cookies = document.cookie.split(/[;\s|=]/); - // add one to get from the key to the value - cookieIndex = $.inArray(key, cookies) + 1; - values = (cookieIndex !== 0) ? $.parseJSON(cookies[cookieIndex] || 'null') || {} : {}; - } - } - // allow value to be an empty string too - if ((value || value === '') && window.JSON && JSON.hasOwnProperty('stringify')) { - // add unique identifiers = url pathname > table ID/index on page > data - if (!values[url]) { - values[url] = {}; + // *** get value *** + if ($.parseJSON) { + if (hasStorage) { + values = $.parseJSON( window[storageType][key] || 'null' ) || {}; + } else { + // old browser, using cookies + cookies = document.cookie.split(/[;\s|=]/); + // add one to get from the key to the value + cookieIndex = $.inArray(key, cookies) + 1; + values = (cookieIndex !== 0) ? $.parseJSON(cookies[cookieIndex] || 'null') || {} : {}; + } } - values[url][id] = value; - // *** set value *** - if (hasStorage) { - window[storageType][key] = JSON.stringify(values); + // allow value to be an empty string too + if ((value || value === '') && window.JSON && JSON.hasOwnProperty('stringify')) { + // add unique identifiers = url pathname > table ID/index on page > data + if (!values[url]) { + values[url] = {}; + } + values[url][id] = value; + // *** set value *** + if (hasStorage) { + window[storageType][key] = JSON.stringify(values); + } else { + date = new Date(); + date.setTime(date.getTime() + (31536e+6)); // 365 days + document.cookie = key + '=' + (JSON.stringify(values)).replace(/\"/g, '\"') + '; expires=' + date.toGMTString() + '; path=/'; + } } else { - date = new Date(); - date.setTime(date.getTime() + (31536e+6)); // 365 days - document.cookie = key + '=' + (JSON.stringify(values)).replace(/\"/g,'\"') + '; expires=' + date.toGMTString() + '; path=/'; + return values && values[url] ? values[url][id] : ''; } - } else { - return values && values[url] ? values[url][id] : ''; - } -}; + }; })(jQuery, window, document); /*! Widget: uitheme - updated 3/26/2015 (v2.21.3) */ ;(function ($) { -'use strict'; -var ts = $.tablesorter || {}; - -ts.themes = { - 'bootstrap' : { - table : 'table table-bordered table-striped', - caption : 'caption', - // header class names - header : 'bootstrap-header', // give the header a gradient background (theme.bootstrap_2.css) - sortNone : '', - sortAsc : '', - sortDesc : '', - active : '', // applied when column is sorted - hover : '', // custom css required - a defined bootstrap style may not override other classes - // icon class names - icons : '', // add "icon-white" to make them white; this icon class is added to the <i> in the header - iconSortNone : 'bootstrap-icon-unsorted', // class name added to icon when column is not sorted - iconSortAsc : 'icon-chevron-up glyphicon glyphicon-chevron-up', // class name added to icon when column has ascending sort - iconSortDesc : 'icon-chevron-down glyphicon glyphicon-chevron-down', // class name added to icon when column has descending sort - filterRow : '', // filter row class - footerRow : '', - footerCells : '', - even : '', // even row zebra striping - odd : '' // odd row zebra striping - }, - 'jui' : { - table : 'ui-widget ui-widget-content ui-corner-all', // table classes - caption : 'ui-widget-content', - // header class names - header : 'ui-widget-header ui-corner-all ui-state-default', // header classes - sortNone : '', - sortAsc : '', - sortDesc : '', - active : 'ui-state-active', // applied when column is sorted - hover : 'ui-state-hover', // hover class - // icon class names - icons : 'ui-icon', // icon class added to the <i> in the header - iconSortNone : 'ui-icon-carat-2-n-s', // class name added to icon when column is not sorted - iconSortAsc : 'ui-icon-carat-1-n', // class name added to icon when column has ascending sort - iconSortDesc : 'ui-icon-carat-1-s', // class name added to icon when column has descending sort - filterRow : '', - footerRow : '', - footerCells : '', - even : 'ui-widget-content', // even row zebra striping - odd : 'ui-state-default' // odd row zebra striping - } -}; - -$.extend(ts.css, { - wrapper : 'tablesorter-wrapper' // ui theme & resizable -}); - -ts.addWidget({ - id: "uitheme", - priority: 10, - format: function(table, c, wo) { - var i, hdr, icon, time, $header, $icon, $tfoot, $h, oldtheme, oldremove, oldIconRmv, hasOldTheme, - themesAll = ts.themes, - $table = c.$table.add( $( c.namespace + '_extra_table' ) ), - $headers = c.$headers.add( $( c.namespace + '_extra_headers' ) ), - theme = c.theme || 'jui', - themes = themesAll[theme] || {}, - remove = $.trim( [ themes.sortNone, themes.sortDesc, themes.sortAsc, themes.active ].join( ' ' ) ), - iconRmv = $.trim( [ themes.iconSortNone, themes.iconSortDesc, themes.iconSortAsc ].join( ' ' ) ); - if (c.debug) { time = new Date(); } - // initialization code - run once - if (!$table.hasClass('tablesorter-' + theme) || c.theme !== c.appliedTheme || !wo.uitheme_applied) { - wo.uitheme_applied = true; - oldtheme = themesAll[c.appliedTheme] || {}; - hasOldTheme = !$.isEmptyObject(oldtheme); - oldremove = hasOldTheme ? [ oldtheme.sortNone, oldtheme.sortDesc, oldtheme.sortAsc, oldtheme.active ].join( ' ' ) : ''; - oldIconRmv = hasOldTheme ? [ oldtheme.iconSortNone, oldtheme.iconSortDesc, oldtheme.iconSortAsc ].join( ' ' ) : ''; - if (hasOldTheme) { - wo.zebra[0] = $.trim( ' ' + wo.zebra[0].replace(' ' + oldtheme.even, '') ); - wo.zebra[1] = $.trim( ' ' + wo.zebra[1].replace(' ' + oldtheme.odd, '') ); - c.$tbodies.children().removeClass( [oldtheme.even, oldtheme.odd].join(' ') ); - } - // update zebra stripes - if (themes.even) { wo.zebra[0] += ' ' + themes.even; } - if (themes.odd) { wo.zebra[1] += ' ' + themes.odd; } - // add caption style - $table.children('caption') - .removeClass(oldtheme.caption || '') - .addClass(themes.caption); - // add table/footer class names - $tfoot = $table - // remove other selected themes - .removeClass( (c.appliedTheme ? 'tablesorter-' + (c.appliedTheme || '') : '') + ' ' + (oldtheme.table || '') ) - .addClass('tablesorter-' + theme + ' ' + (themes.table || '')) // add theme widget class name - .children('tfoot'); - c.appliedTheme = c.theme; - - if ($tfoot.length) { - $tfoot - // if oldtheme.footerRow or oldtheme.footerCells are undefined, all class names are removed - .children('tr').removeClass(oldtheme.footerRow || '').addClass(themes.footerRow) - .children('th, td').removeClass(oldtheme.footerCells || '').addClass(themes.footerCells); - } - // update header classes - $headers - .removeClass( (hasOldTheme ? [oldtheme.header, oldtheme.hover, oldremove].join(' ') : '') || '' ) - .addClass(themes.header) - .not('.sorter-false') - .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') - .bind('mouseenter.tsuitheme mouseleave.tsuitheme', function(event) { - // toggleClass with switch added in jQuery 1.3 - $(this)[ event.type === 'mouseenter' ? 'addClass' : 'removeClass' ](themes.hover || ''); - }); + 'use strict'; + var ts = $.tablesorter || {}; + + ts.themes = { + 'bootstrap' : { + table : 'table table-bordered table-striped', + caption : 'caption', + // header class names + header : 'bootstrap-header', // give the header a gradient background (theme.bootstrap_2.css) + sortNone : '', + sortAsc : '', + sortDesc : '', + active : '', // applied when column is sorted + hover : '', // custom css required - a defined bootstrap style may not override other classes + // icon class names + icons : '', // add 'icon-white' to make them white; this icon class is added to the <i> in the header + iconSortNone : 'bootstrap-icon-unsorted', // class name added to icon when column is not sorted + iconSortAsc : 'icon-chevron-up glyphicon glyphicon-chevron-up', // class name added to icon when column has ascending sort + iconSortDesc : 'icon-chevron-down glyphicon glyphicon-chevron-down', // class name added to icon when column has descending sort + filterRow : '', // filter row class + footerRow : '', + footerCells : '', + even : '', // even row zebra striping + odd : '' // odd row zebra striping + }, + 'jui' : { + table : 'ui-widget ui-widget-content ui-corner-all', // table classes + caption : 'ui-widget-content', + // header class names + header : 'ui-widget-header ui-corner-all ui-state-default', // header classes + sortNone : '', + sortAsc : '', + sortDesc : '', + active : 'ui-state-active', // applied when column is sorted + hover : 'ui-state-hover', // hover class + // icon class names + icons : 'ui-icon', // icon class added to the <i> in the header + iconSortNone : 'ui-icon-carat-2-n-s', // class name added to icon when column is not sorted + iconSortAsc : 'ui-icon-carat-1-n', // class name added to icon when column has ascending sort + iconSortDesc : 'ui-icon-carat-1-s', // class name added to icon when column has descending sort + filterRow : '', + footerRow : '', + footerCells : '', + even : 'ui-widget-content', // even row zebra striping + odd : 'ui-state-default' // odd row zebra striping + } + }; - $headers.each(function(){ - var $this = $(this); - if (!$this.find('.' + ts.css.wrapper).length) { - // Firefox needs this inner div to position the icon & resizer correctly - $this.wrapInner('<div class="' + ts.css.wrapper + '" style="position:relative;height:100%;width:100%"></div>'); - } - }); - if (c.cssIcon) { - // if c.cssIcon is '', then no <i> is added to the header + $.extend(ts.css, { + wrapper : 'tablesorter-wrapper' // ui theme & resizable + }); + + ts.addWidget({ + id: 'uitheme', + priority: 10, + format: function(table, c, wo) { + var i, hdr, icon, time, $header, $icon, $tfoot, $h, oldtheme, oldremove, oldIconRmv, hasOldTheme, + themesAll = ts.themes, + $table = c.$table.add( $( c.namespace + '_extra_table' ) ), + $headers = c.$headers.add( $( c.namespace + '_extra_headers' ) ), + theme = c.theme || 'jui', + themes = themesAll[theme] || {}, + remove = $.trim( [ themes.sortNone, themes.sortDesc, themes.sortAsc, themes.active ].join( ' ' ) ), + iconRmv = $.trim( [ themes.iconSortNone, themes.iconSortDesc, themes.iconSortAsc ].join( ' ' ) ); + if (c.debug) { time = new Date(); } + // initialization code - run once + if (!$table.hasClass('tablesorter-' + theme) || c.theme !== c.appliedTheme || !wo.uitheme_applied) { + wo.uitheme_applied = true; + oldtheme = themesAll[c.appliedTheme] || {}; + hasOldTheme = !$.isEmptyObject(oldtheme); + oldremove = hasOldTheme ? [ oldtheme.sortNone, oldtheme.sortDesc, oldtheme.sortAsc, oldtheme.active ].join( ' ' ) : ''; + oldIconRmv = hasOldTheme ? [ oldtheme.iconSortNone, oldtheme.iconSortDesc, oldtheme.iconSortAsc ].join( ' ' ) : ''; + if (hasOldTheme) { + wo.zebra[0] = $.trim( ' ' + wo.zebra[0].replace(' ' + oldtheme.even, '') ); + wo.zebra[1] = $.trim( ' ' + wo.zebra[1].replace(' ' + oldtheme.odd, '') ); + c.$tbodies.children().removeClass( [ oldtheme.even, oldtheme.odd ].join(' ') ); + } + // update zebra stripes + if (themes.even) { wo.zebra[0] += ' ' + themes.even; } + if (themes.odd) { wo.zebra[1] += ' ' + themes.odd; } + // add caption style + $table.children('caption') + .removeClass(oldtheme.caption || '') + .addClass(themes.caption); + // add table/footer class names + $tfoot = $table + // remove other selected themes + .removeClass( (c.appliedTheme ? 'tablesorter-' + (c.appliedTheme || '') : '') + ' ' + (oldtheme.table || '') ) + .addClass('tablesorter-' + theme + ' ' + (themes.table || '')) // add theme widget class name + .children('tfoot'); + c.appliedTheme = c.theme; + + if ($tfoot.length) { + $tfoot + // if oldtheme.footerRow or oldtheme.footerCells are undefined, all class names are removed + .children('tr').removeClass(oldtheme.footerRow || '').addClass(themes.footerRow) + .children('th, td').removeClass(oldtheme.footerCells || '').addClass(themes.footerCells); + } + // update header classes $headers - .find('.' + ts.css.icon) - .removeClass(hasOldTheme ? [oldtheme.icons, oldIconRmv].join(' ') : '') - .addClass(themes.icons || ''); - } - if ($table.hasClass('hasFilters')) { - $table.children('thead').children('.' + ts.css.filterRow) - .removeClass(hasOldTheme ? oldtheme.filterRow || '' : '') - .addClass(themes.filterRow || ''); + .removeClass( (hasOldTheme ? [ oldtheme.header, oldtheme.hover, oldremove ].join(' ') : '') || '' ) + .addClass(themes.header) + .not('.sorter-false') + .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') + .bind('mouseenter.tsuitheme mouseleave.tsuitheme', function(event) { + // toggleClass with switch added in jQuery 1.3 + $(this)[ event.type === 'mouseenter' ? 'addClass' : 'removeClass' ](themes.hover || ''); + }); + + $headers.each(function(){ + var $this = $(this); + if (!$this.find('.' + ts.css.wrapper).length) { + // Firefox needs this inner div to position the icon & resizer correctly + $this.wrapInner('<div class="' + ts.css.wrapper + '" style="position:relative;height:100%;width:100%"></div>'); + } + }); + if (c.cssIcon) { + // if c.cssIcon is '', then no <i> is added to the header + $headers + .find('.' + ts.css.icon) + .removeClass(hasOldTheme ? [ oldtheme.icons, oldIconRmv ].join(' ') : '') + .addClass(themes.icons || ''); + } + if ($table.hasClass('hasFilters')) { + $table.children('thead').children('.' + ts.css.filterRow) + .removeClass(hasOldTheme ? oldtheme.filterRow || '' : '') + .addClass(themes.filterRow || ''); + } } - } - for (i = 0; i < c.columns; i++) { - $header = c.$headers - .add($(c.namespace + '_extra_headers')) - .not('.sorter-false') - .filter('[data-column="' + i + '"]'); - $icon = (ts.css.icon) ? $header.find('.' + ts.css.icon) : $(); - $h = $headers.not('.sorter-false').filter('[data-column="' + i + '"]:last'); - if ($h.length) { - $header.removeClass(remove); - $icon.removeClass(iconRmv); - if ($h[0].sortDisabled) { - // no sort arrows for disabled columns! - $icon.removeClass(themes.icons || ''); - } else { - hdr = themes.sortNone; - icon = themes.iconSortNone; - if ($h.hasClass(ts.css.sortAsc)) { - hdr = [themes.sortAsc, themes.active].join(' '); - icon = themes.iconSortAsc; - } else if ($h.hasClass(ts.css.sortDesc)) { - hdr = [themes.sortDesc, themes.active].join(' '); - icon = themes.iconSortDesc; + for (i = 0; i < c.columns; i++) { + $header = c.$headers + .add($(c.namespace + '_extra_headers')) + .not('.sorter-false') + .filter('[data-column="' + i + '"]'); + $icon = (ts.css.icon) ? $header.find('.' + ts.css.icon) : $(); + $h = $headers.not('.sorter-false').filter('[data-column="' + i + '"]:last'); + if ($h.length) { + $header.removeClass(remove); + $icon.removeClass(iconRmv); + if ($h[0].sortDisabled) { + // no sort arrows for disabled columns! + $icon.removeClass(themes.icons || ''); + } else { + hdr = themes.sortNone; + icon = themes.iconSortNone; + if ($h.hasClass(ts.css.sortAsc)) { + hdr = [ themes.sortAsc, themes.active ].join(' '); + icon = themes.iconSortAsc; + } else if ($h.hasClass(ts.css.sortDesc)) { + hdr = [ themes.sortDesc, themes.active ].join(' '); + icon = themes.iconSortDesc; + } + $header.addClass(hdr); + $icon.addClass(icon || ''); } - $header.addClass(hdr); - $icon.addClass(icon || ''); } } + if (c.debug) { + console.log('Applying ' + theme + ' theme' + ts.benchmark(time)); + } + }, + remove: function(table, c, wo, refreshing) { + if (!wo.uitheme_applied) { return; } + var $table = c.$table, + theme = c.appliedTheme || 'jui', + themes = ts.themes[ theme ] || ts.themes.jui, + $headers = $table.children('thead').children(), + remove = themes.sortNone + ' ' + themes.sortDesc + ' ' + themes.sortAsc, + iconRmv = themes.iconSortNone + ' ' + themes.iconSortDesc + ' ' + themes.iconSortAsc; + $table.removeClass('tablesorter-' + theme + ' ' + themes.table); + wo.uitheme_applied = false; + if (refreshing) { return; } + $table.find(ts.css.header).removeClass(themes.header); + $headers + .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') // remove hover + .removeClass(themes.hover + ' ' + remove + ' ' + themes.active) + .filter('.' + ts.css.filterRow) + .removeClass(themes.filterRow); + $headers.find('.' + ts.css.icon).removeClass(themes.icons + ' ' + iconRmv); } - if (c.debug) { - ts.benchmark("Applying " + theme + " theme", time); - } - }, - remove: function(table, c, wo, refreshing) { - if (!wo.uitheme_applied) { return; } - var $table = c.$table, - theme = c.appliedTheme || 'jui', - themes = ts.themes[ theme ] || ts.themes.jui, - $headers = $table.children('thead').children(), - remove = themes.sortNone + ' ' + themes.sortDesc + ' ' + themes.sortAsc, - iconRmv = themes.iconSortNone + ' ' + themes.iconSortDesc + ' ' + themes.iconSortAsc; - $table.removeClass('tablesorter-' + theme + ' ' + themes.table); - wo.uitheme_applied = false; - if (refreshing) { return; } - $table.find(ts.css.header).removeClass(themes.header); - $headers - .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') // remove hover - .removeClass(themes.hover + ' ' + remove + ' ' + themes.active) - .filter('.' + ts.css.filterRow) - .removeClass(themes.filterRow); - $headers.find('.' + ts.css.icon).removeClass(themes.icons + ' ' + iconRmv); - } -}); + }); })(jQuery); /*! Widget: columns */ ;(function ($) { -'use strict'; -var ts = $.tablesorter || {}; - -ts.addWidget({ - id: "columns", - priority: 30, - options : { - columns : [ "primary", "secondary", "tertiary" ] - }, - format: function(table, c, wo) { - var $tbody, tbodyIndex, $rows, rows, $row, $cells, remove, indx, + 'use strict'; + var ts = $.tablesorter || {}; + + ts.addWidget({ + id: 'columns', + priority: 30, + options : { + columns : [ 'primary', 'secondary', 'tertiary' ] + }, + format: function(table, c, wo) { + var $tbody, tbodyIndex, $rows, rows, $row, $cells, remove, indx, $table = c.$table, $tbodies = c.$tbodies, sortList = c.sortList, len = sortList.length, // removed c.widgetColumns support - css = wo && wo.columns || [ "primary", "secondary", "tertiary" ], + css = wo && wo.columns || [ 'primary', 'secondary', 'tertiary' ], last = css.length - 1; remove = css.join(' '); - // check if there is a sort (on initialization there may not be one) - for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { - $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // detach tbody - $rows = $tbody.children('tr'); - // loop through the visible rows - $rows.each(function() { - $row = $(this); - if (this.style.display !== 'none') { - // remove all columns class names - $cells = $row.children().removeClass(remove); - // add appropriate column class names - if (sortList && sortList[0]) { - // primary sort column class - $cells.eq(sortList[0][0]).addClass(css[0]); - if (len > 1) { - for (indx = 1; indx < len; indx++) { - // secondary, tertiary, etc sort column classes - $cells.eq(sortList[indx][0]).addClass( css[indx] || css[last] ); + // check if there is a sort (on initialization there may not be one) + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // detach tbody + $rows = $tbody.children('tr'); + // loop through the visible rows + $rows.each(function() { + $row = $(this); + if (this.style.display !== 'none') { + // remove all columns class names + $cells = $row.children().removeClass(remove); + // add appropriate column class names + if (sortList && sortList[0]) { + // primary sort column class + $cells.eq(sortList[0][0]).addClass(css[0]); + if (len > 1) { + for (indx = 1; indx < len; indx++) { + // secondary, tertiary, etc sort column classes + $cells.eq(sortList[indx][0]).addClass( css[indx] || css[last] ); + } } } } - } - }); - ts.processTbody(table, $tbody, false); - } - // add classes to thead and tfoot - rows = wo.columns_thead !== false ? ['thead tr'] : []; - if (wo.columns_tfoot !== false) { - rows.push('tfoot tr'); - } - if (rows.length) { - $rows = $table.find( rows.join(',') ).children().removeClass(remove); - if (len) { - for (indx = 0; indx < len; indx++) { - // add primary. secondary, tertiary, etc sort column classes - $rows.filter('[data-column="' + sortList[indx][0] + '"]').addClass(css[indx] || css[last]); + }); + ts.processTbody(table, $tbody, false); + } + // add classes to thead and tfoot + rows = wo.columns_thead !== false ? [ 'thead tr' ] : []; + if (wo.columns_tfoot !== false) { + rows.push('tfoot tr'); + } + if (rows.length) { + $rows = $table.find( rows.join(',') ).children().removeClass(remove); + if (len) { + for (indx = 0; indx < len; indx++) { + // add primary. secondary, tertiary, etc sort column classes + $rows.filter('[data-column="' + sortList[indx][0] + '"]').addClass(css[indx] || css[last]); + } } } + }, + remove: function(table, c, wo) { + var tbodyIndex, $tbody, + $tbodies = c.$tbodies, + remove = (wo.columns || [ 'primary', 'secondary', 'tertiary' ]).join(' '); + c.$headers.removeClass(remove); + c.$table.children('tfoot').children('tr').children('th, td').removeClass(remove); + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody + $tbody.children('tr').each(function() { + $(this).children().removeClass(remove); + }); + ts.processTbody(table, $tbody, false); // restore tbody + } } - }, - remove: function(table, c, wo) { - var tbodyIndex, $tbody, - $tbodies = c.$tbodies, - remove = (wo.columns || [ "primary", "secondary", "tertiary" ]).join(' '); - c.$headers.removeClass(remove); - c.$table.children('tfoot').children('tr').children('th, td').removeClass(remove); - for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { - $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody - $tbody.children('tr').each(function() { - $(this).children().removeClass(remove); - }); - ts.processTbody(table, $tbody, false); // restore tbody - } - } -}); + }); })(jQuery); -/*! Widget: filter - updated 5/17/2015 (v2.22.1) *//* +/*! Widget: filter - updated 7/28/2015 (v2.22.4) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ ;( function ( $ ) { -'use strict'; -var ts = $.tablesorter || {}, + 'use strict'; + var ts = $.tablesorter || {}, tscss = ts.css; -$.extend( tscss, { - filterRow : 'tablesorter-filter-row', - filter : 'tablesorter-filter', - filterDisabled : 'disabled', - filterRowHide : 'hideme' -}); - -ts.addWidget({ - id: 'filter', - priority: 50, - options : { - filter_childRows : false, // if true, filter includes child row content in the search - filter_childByColumn : false, // ( filter_childRows must be true ) if true = search child rows by column; false = search all child row text grouped - filter_columnFilters : true, // if true, a filter will be added to the top of each table column - filter_columnAnyMatch: true, // if true, allows using '#:{query}' in AnyMatch searches ( column:query ) - filter_cellFilter : '', // css class name added to the filter cell ( string or array ) - filter_cssFilter : '', // css class name added to the filter row & each input in the row ( tablesorter-filter is ALWAYS added ) - filter_defaultFilter : {}, // add a default column filter type '~{query}' to make fuzzy searches default; '{q1} AND {q2}' to make all searches use a logical AND. - filter_excludeFilter : {}, // filters to exclude, per column - filter_external : '', // jQuery selector string ( or jQuery object ) of external filters - filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin - filter_formatter : null, // add custom filter elements to the filter row - filter_functions : null, // add custom filter functions using this option - filter_hideEmpty : true, // hide filter row when table is empty - filter_hideFilters : false, // collapse filter row when mouse leaves the area - filter_ignoreCase : true, // if true, make all searches case-insensitive - filter_liveSearch : true, // if true, search column content while the user types ( with a delay ) - filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available ( visible ) options within the drop down - filter_placeholder : { search : '', select : '' }, // default placeholder text ( overridden by any header 'data-placeholder' setting ) - filter_reset : null, // jQuery selector string of an element used to reset the filters - filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters - filter_searchDelay : 300, // typing delay in milliseconds before starting a search - filter_searchFiltered: true, // allow searching through already filtered rows in special circumstances; will speed up searching in large tables if true - filter_selectSource : null, // include a function to return an array of values to be added to the column filter select - filter_startsWith : false, // if true, filter start from the beginning of the cell contents - filter_useParsedData : false, // filter all data using parsed content - filter_serversideFiltering : false, // if true, server-side filtering should be performed because client-side filtering will be disabled, but the ui and events will still be used. - filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value - filter_selectSourceSeparator : '|' // filter_selectSource array text left of the separator is added to the option value, right into the option text - }, - format: function( table, c, wo ) { - if ( !c.$table.hasClass( 'hasFilters' ) ) { - ts.filter.init( table, c, wo ); - } - }, - remove: function( table, c, wo, refreshing ) { - var tbodyIndex, $tbody, - $table = c.$table, - $tbodies = c.$tbodies, - events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' - .split( ' ' ).join( c.namespace + 'filter ' ); - $table - .removeClass( 'hasFilters' ) - // add .tsfilter namespace to all BUT search - .unbind( events.replace( /\s+/g, ' ' ) ) - // remove the filter row even if refreshing, because the column might have been moved - .find( '.' + tscss.filterRow ).remove(); - if ( refreshing ) { return; } - for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { - $tbody = ts.processTbody( table, $tbodies.eq( tbodyIndex ), true ); // remove tbody - $tbody.children().removeClass( wo.filter_filteredRow ).show(); - ts.processTbody( table, $tbody, false ); // restore tbody - } - if ( wo.filter_reset ) { - $( document ).undelegate( wo.filter_reset, 'click.tsfilter' ); + $.extend( tscss, { + filterRow : 'tablesorter-filter-row', + filter : 'tablesorter-filter', + filterDisabled : 'disabled', + filterRowHide : 'hideme' + }); + + ts.addWidget({ + id: 'filter', + priority: 50, + options : { + filter_childRows : false, // if true, filter includes child row content in the search + filter_childByColumn : false, // ( filter_childRows must be true ) if true = search child rows by column; false = search all child row text grouped + filter_columnFilters : true, // if true, a filter will be added to the top of each table column + filter_columnAnyMatch: true, // if true, allows using '#:{query}' in AnyMatch searches ( column:query ) + filter_cellFilter : '', // css class name added to the filter cell ( string or array ) + filter_cssFilter : '', // css class name added to the filter row & each input in the row ( tablesorter-filter is ALWAYS added ) + filter_defaultFilter : {}, // add a default column filter type '~{query}' to make fuzzy searches default; '{q1} AND {q2}' to make all searches use a logical AND. + filter_excludeFilter : {}, // filters to exclude, per column + filter_external : '', // jQuery selector string ( or jQuery object ) of external filters + filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin + filter_formatter : null, // add custom filter elements to the filter row + filter_functions : null, // add custom filter functions using this option + filter_hideEmpty : true, // hide filter row when table is empty + filter_hideFilters : false, // collapse filter row when mouse leaves the area + filter_ignoreCase : true, // if true, make all searches case-insensitive + filter_liveSearch : true, // if true, search column content while the user types ( with a delay ) + filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available ( visible ) options within the drop down + filter_placeholder : { search : '', select : '' }, // default placeholder text ( overridden by any header 'data-placeholder' setting ) + filter_reset : null, // jQuery selector string of an element used to reset the filters + filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters + filter_searchDelay : 300, // typing delay in milliseconds before starting a search + filter_searchFiltered: true, // allow searching through already filtered rows in special circumstances; will speed up searching in large tables if true + filter_selectSource : null, // include a function to return an array of values to be added to the column filter select + filter_startsWith : false, // if true, filter start from the beginning of the cell contents + filter_useParsedData : false, // filter all data using parsed content + filter_serversideFiltering : false, // if true, must perform server-side filtering b/c client-side filtering is disabled, but the ui and events will still be used. + filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value + filter_selectSourceSeparator : '|' // filter_selectSource array text left of the separator is added to the option value, right into the option text + }, + format: function( table, c, wo ) { + if ( !c.$table.hasClass( 'hasFilters' ) ) { + ts.filter.init( table, c, wo ); + } + }, + remove: function( table, c, wo, refreshing ) { + var tbodyIndex, $tbody, + $table = c.$table, + $tbodies = c.$tbodies, + events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' + .split( ' ' ).join( c.namespace + 'filter ' ); + $table + .removeClass( 'hasFilters' ) + // add .tsfilter namespace to all BUT search + .unbind( events.replace( /\s+/g, ' ' ) ) + // remove the filter row even if refreshing, because the column might have been moved + .find( '.' + tscss.filterRow ).remove(); + if ( refreshing ) { return; } + for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody( table, $tbodies.eq( tbodyIndex ), true ); // remove tbody + $tbody.children().removeClass( wo.filter_filteredRow ).show(); + ts.processTbody( table, $tbody, false ); // restore tbody + } + if ( wo.filter_reset ) { + $( document ).undelegate( wo.filter_reset, 'click.tsfilter' ); + } } - } -}); - -ts.filter = { - - // regex used in filter 'check' functions - not for general use and not documented - regex: { - regex : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // regex to test for regex - child : /tablesorter-childRow/, // child row class name; this gets updated in the script - filtered : /filtered/, // filtered (hidden) row class name; updated in the script - type : /undefined|number/, // check type - exact : /(^[\"\'=]+)|([\"\'=]+$)/g, // exact match (allow '==') - nondigit : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser) - operators : /[<>=]/g, // replace operators - query : '(q|query)' // replace filter queries - }, + }); + + ts.filter = { + + // regex used in filter 'check' functions - not for general use and not documented + regex: { + regex : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // regex to test for regex + child : /tablesorter-childRow/, // child row class name; this gets updated in the script + filtered : /filtered/, // filtered (hidden) row class name; updated in the script + type : /undefined|number/, // check type + exact : /(^[\"\'=]+)|([\"\'=]+$)/g, // exact match (allow '==') + nondigit : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser) + operators : /[<>=]/g, // replace operators + query : '(q|query)' // replace filter queries + }, // function( c, data ) { } // c = table.config // data.$row = jQuery object of the row currently being processed @@ -2653,1508 +2708,1523 @@ ts.filter = { // data.cacheArray = An array of parsed content from each table cell in the row being processed // data.index = column index; table = table element ( DOM ) // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class ) - types: { - or : function( c, data, vars ) { - if ( /\|/.test( data.iFilter ) || ts.filter.regex.orSplit.test( data.filter ) ) { - var indx, filterMatched, txt, query, regex, - // duplicate data but split filter - data2 = $.extend( {}, data ), - index = data.index, - parsed = data.parsed[ index ], - filter = data.filter.split( ts.filter.regex.orSplit ), - iFilter = data.iFilter.split( ts.filter.regex.orSplit ), - len = filter.length; - for ( indx = 0; indx < len; indx++ ) { - data2.nestedFilters = true; - data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); - data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); - query = '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')'; - regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); - // filterMatched = data2.filter === '' && indx > 0 ? true - // look for an exact match with the 'or' unless the 'filter-match' class is found - filterMatched = regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ); - if ( filterMatched ) { - return filterMatched; + types: { + or : function( c, data, vars ) { + if ( /\|/.test( data.iFilter ) || ts.filter.regex.orSplit.test( data.filter ) ) { + var indx, filterMatched, txt, query, regex, + // duplicate data but split filter + data2 = $.extend( {}, data ), + index = data.index, + parsed = data.parsed[ index ], + filter = data.filter.split( ts.filter.regex.orSplit ), + iFilter = data.iFilter.split( ts.filter.regex.orSplit ), + len = filter.length; + for ( indx = 0; indx < len; indx++ ) { + data2.nestedFilters = true; + data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); + data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); + query = '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')'; + try { + // use try/catch, because query may not be a valid regex if "|" is contained within a partial regex search, + // e.g "/(Alex|Aar" -> Uncaught SyntaxError: Invalid regular expression: /(/(Alex)/: Unterminated group + regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); + // filterMatched = data2.filter === '' && indx > 0 ? true + // look for an exact match with the 'or' unless the 'filter-match' class is found + filterMatched = regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ); + if ( filterMatched ) { + return filterMatched; + } + } catch ( error ) { + return null; + } } + // may be null from processing types + return filterMatched || false; } - // may be null from processing types - return filterMatched || false; - } - return null; - }, - // Look for an AND or && operator ( logical and ) - and : function( c, data, vars ) { - if ( ts.filter.regex.andTest.test( data.filter ) ) { - var indx, filterMatched, result, txt, query, regex, - // duplicate data but split filter - data2 = $.extend( {}, data ), - index = data.index, - parsed = data.parsed[ index ], - filter = data.filter.split( ts.filter.regex.andSplit ), - iFilter = data.iFilter.split( ts.filter.regex.andSplit ), - len = filter.length; - for ( indx = 0; indx < len; indx++ ) { - data2.nestedFilters = true; - data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); - data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); - query = ( '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' ) - // replace wild cards since /(a*)/i will match anything - .replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ); - regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); - // look for an exact match with the 'and' unless the 'filter-match' class is found - result = ( regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ) ); - if ( indx === 0 ) { - filterMatched = result; - } else { - filterMatched = filterMatched && result; + return null; + }, + // Look for an AND or && operator ( logical and ) + and : function( c, data, vars ) { + if ( ts.filter.regex.andTest.test( data.filter ) ) { + var indx, filterMatched, result, txt, query, regex, + // duplicate data but split filter + data2 = $.extend( {}, data ), + index = data.index, + parsed = data.parsed[ index ], + filter = data.filter.split( ts.filter.regex.andSplit ), + iFilter = data.iFilter.split( ts.filter.regex.andSplit ), + len = filter.length; + for ( indx = 0; indx < len; indx++ ) { + data2.nestedFilters = true; + data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); + data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); + query = ( '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' ) + // replace wild cards since /(a*)/i will match anything + .replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ); + try { + // use try/catch just in case RegExp is invalid + regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); + // look for an exact match with the 'and' unless the 'filter-match' class is found + result = ( regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ) ); + if ( indx === 0 ) { + filterMatched = result; + } else { + filterMatched = filterMatched && result; + } + } catch ( error ) { + return null; + } } + // may be null from processing types + return filterMatched || false; } - // may be null from processing types - return filterMatched || false; - } - return null; - }, - // Look for regex - regex: function( c, data ) { - if ( ts.filter.regex.regex.test( data.filter ) ) { - var matches, - // cache regex per column for optimal speed - regex = data.filter_regexCache[ data.index ] || ts.filter.regex.regex.exec( data.filter ), - isRegex = regex instanceof RegExp; - try { - if ( !isRegex ) { - // force case insensitive search if ignoreCase option set? - // if ( c.ignoreCase && !regex[2] ) { regex[2] = 'i'; } - data.filter_regexCache[ data.index ] = regex = new RegExp( regex[1], regex[2] ); + return null; + }, + // Look for regex + regex: function( c, data ) { + if ( ts.filter.regex.regex.test( data.filter ) ) { + var matches, + // cache regex per column for optimal speed + regex = data.filter_regexCache[ data.index ] || ts.filter.regex.regex.exec( data.filter ), + isRegex = regex instanceof RegExp; + try { + if ( !isRegex ) { + // force case insensitive search if ignoreCase option set? + // if ( c.ignoreCase && !regex[2] ) { regex[2] = 'i'; } + data.filter_regexCache[ data.index ] = regex = new RegExp( regex[1], regex[2] ); + } + matches = regex.test( data.exact ); + } catch ( error ) { + matches = false; } - matches = regex.test( data.exact ); - } catch ( error ) { - matches = false; + return matches; } - return matches; - } - return null; - }, - // Look for operators >, >=, < or <= - operators: function( c, data ) { - // ignore empty strings... because '' < 10 is true - if ( /^[<>]=?/.test( data.iFilter ) && data.iExact !== '' ) { - var cachedValue, result, txt, - table = c.table, - index = data.index, - parsed = data.parsed[index], - query = ts.formatFloat( data.iFilter.replace( ts.filter.regex.operators, '' ), table ), - parser = c.parsers[index], - savedSearch = query; - // parse filter value in case we're comparing numbers ( dates ) - if ( parsed || parser.type === 'numeric' ) { - txt = $.trim( '' + data.iFilter.replace( ts.filter.regex.operators, '' ) ); - result = ts.filter.parseFilter( c, txt, index, true ); - query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query; - } - // iExact may be numeric - see issue #149; - // check if cached is defined, because sometimes j goes out of range? ( numeric columns ) - if ( ( parsed || parser.type === 'numeric' ) && !isNaN( query ) && - typeof data.cache !== 'undefined' ) { - cachedValue = data.cache; - } else { - txt = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; - cachedValue = ts.formatFloat( txt, table ); + return null; + }, + // Look for operators >, >=, < or <= + operators: function( c, data ) { + // ignore empty strings... because '' < 10 is true + if ( /^[<>]=?/.test( data.iFilter ) && data.iExact !== '' ) { + var cachedValue, result, txt, + table = c.table, + index = data.index, + parsed = data.parsed[index], + query = ts.formatFloat( data.iFilter.replace( ts.filter.regex.operators, '' ), table ), + parser = c.parsers[index], + savedSearch = query; + // parse filter value in case we're comparing numbers ( dates ) + if ( parsed || parser.type === 'numeric' ) { + txt = $.trim( '' + data.iFilter.replace( ts.filter.regex.operators, '' ) ); + result = ts.filter.parseFilter( c, txt, index, true ); + query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query; + } + // iExact may be numeric - see issue #149; + // check if cached is defined, because sometimes j goes out of range? ( numeric columns ) + if ( ( parsed || parser.type === 'numeric' ) && !isNaN( query ) && + typeof data.cache !== 'undefined' ) { + cachedValue = data.cache; + } else { + txt = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; + cachedValue = ts.formatFloat( txt, table ); + } + if ( />/.test( data.iFilter ) ) { + result = />=/.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; + } else if ( /</.test( data.iFilter ) ) { + result = /<=/.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; + } + // keep showing all rows if nothing follows the operator + if ( !result && savedSearch === '' ) { + result = true; + } + return result; } - if ( />/.test( data.iFilter ) ) { - result = />=/.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; - } else if ( /</.test( data.iFilter ) ) { - result = /<=/.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; + return null; + }, + // Look for a not match + notMatch: function( c, data ) { + if ( /^\!/.test( data.iFilter ) ) { + var indx, + txt = data.iFilter.replace( '!', '' ), + filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + if ( ts.filter.regex.exact.test( filter ) ) { + // look for exact not matches - see #628 + filter = filter.replace( ts.filter.regex.exact, '' ); + return filter === '' ? true : $.trim( filter ) !== data.iExact; + } else { + indx = data.iExact.search( $.trim( filter ) ); + return filter === '' ? true : !( c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0 ); + } } - // keep showing all rows if nothing follows the operator - if ( !result && savedSearch === '' ) { - result = true; + return null; + }, + // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric + exact: function( c, data ) { + /*jshint eqeqeq:false */ + if ( ts.filter.regex.exact.test( data.iFilter ) ) { + var txt = data.iFilter.replace( ts.filter.regex.exact, '' ), + filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact; } - return result; - } - return null; - }, - // Look for a not match - notMatch: function( c, data ) { - if ( /^\!/.test( data.iFilter ) ) { - var indx, - txt = data.iFilter.replace( '!', '' ), - filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; - if ( ts.filter.regex.exact.test( filter ) ) { - // look for exact not matches - see #628 - filter = filter.replace( ts.filter.regex.exact, '' ); - return filter === '' ? true : $.trim( filter ) !== data.iExact; - } else { - indx = data.iExact.search( $.trim( filter ) ); - return filter === '' ? true : !( c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0 ); + return null; + }, + // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu! + range : function( c, data ) { + if ( ts.filter.regex.toTest.test( data.iFilter ) ) { + var result, tmp, range1, range2, + table = c.table, + index = data.index, + parsed = data.parsed[index], + // make sure the dash is for a range and not indicating a negative number + query = data.iFilter.split( ts.filter.regex.toSplit ); + + tmp = query[0].replace( ts.filter.regex.nondigit, '' ) || ''; + range1 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); + tmp = query[1].replace( ts.filter.regex.nondigit, '' ) || ''; + range2 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); + // parse filter value in case we're comparing numbers ( dates ) + if ( parsed || c.parsers[index].type === 'numeric' ) { + result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index ); + range1 = ( result !== '' && !isNaN( result ) ) ? result : range1; + result = c.parsers[ index ].format( '' + query[1], table, c.$headers.eq( index ), index ); + range2 = ( result !== '' && !isNaN( result ) ) ? result : range2; + } + if ( ( parsed || c.parsers[ index ].type === 'numeric' ) && !isNaN( range1 ) && !isNaN( range2 ) ) { + result = data.cache; + } else { + tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; + result = ts.formatFloat( tmp, table ); + } + if ( range1 > range2 ) { + tmp = range1; range1 = range2; range2 = tmp; // swap + } + return ( result >= range1 && result <= range2 ) || ( range1 === '' || range2 === '' ); } - } - return null; - }, - // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric - exact: function( c, data ) { - /*jshint eqeqeq:false */ - if ( ts.filter.regex.exact.test( data.iFilter ) ) { - var txt = data.iFilter.replace( ts.filter.regex.exact, '' ), - filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; - return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact; - } - return null; - }, - // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu! - range : function( c, data ) { - if ( ts.filter.regex.toTest.test( data.iFilter ) ) { - var result, tmp, range1, range2, - table = c.table, - index = data.index, - parsed = data.parsed[index], - // make sure the dash is for a range and not indicating a negative number - query = data.iFilter.split( ts.filter.regex.toSplit ); - - tmp = query[0].replace( ts.filter.regex.nondigit, '' ) || ''; - range1 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); - tmp = query[1].replace( ts.filter.regex.nondigit, '' ) || ''; - range2 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); - // parse filter value in case we're comparing numbers ( dates ) - if ( parsed || c.parsers[index].type === 'numeric' ) { - result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index ); - range1 = ( result !== '' && !isNaN( result ) ) ? result : range1; - result = c.parsers[ index ].format( '' + query[1], table, c.$headers.eq( index ), index ); - range2 = ( result !== '' && !isNaN( result ) ) ? result : range2; - } - if ( ( parsed || c.parsers[ index ].type === 'numeric' ) && !isNaN( range1 ) && !isNaN( range2 ) ) { - result = data.cache; - } else { - tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; - result = ts.formatFloat( tmp, table ); + return null; + }, + // Look for wild card: ? = single, * = multiple, or | = logical OR + wild : function( c, data ) { + if ( /[\?\*\|]/.test( data.iFilter ) ) { + var index = data.index, + parsed = data.parsed[ index ], + query = '' + ( ts.filter.parseFilter( c, data.iFilter, index, parsed ) || '' ); + // look for an exact match with the 'or' unless the 'filter-match' class is found + if ( !/\?\*/.test( query ) && data.nestedFilters ) { + query = data.isMatch ? query : '^(' + query + ')$'; + } + // parsing the filter may not work properly when using wildcards =/ + try { + return new RegExp( + query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ), + c.widgetOptions.filter_ignoreCase ? 'i' : '' + ) + .test( data.exact ); + } catch ( error ) { + return null; + } } - if ( range1 > range2 ) { - tmp = range1; range1 = range2; range2 = tmp; // swap + return null; + }, + // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license ) + fuzzy: function( c, data ) { + if ( /^~/.test( data.iFilter ) ) { + var indx, + patternIndx = 0, + len = data.iExact.length, + txt = data.iFilter.slice( 1 ), + pattern = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + for ( indx = 0; indx < len; indx++ ) { + if ( data.iExact[ indx ] === pattern[ patternIndx ] ) { + patternIndx += 1; + } + } + if ( patternIndx === pattern.length ) { + return true; + } + return false; } - return ( result >= range1 && result <= range2 ) || ( range1 === '' || range2 === '' ); + return null; } - return null; }, - // Look for wild card: ? = single, * = multiple, or | = logical OR - wild : function( c, data ) { - if ( /[\?\*\|]/.test( data.iFilter ) ) { - var index = data.index, - parsed = data.parsed[ index ], - query = '' + ( ts.filter.parseFilter( c, data.iFilter, index, parsed ) || '' ); - // look for an exact match with the 'or' unless the 'filter-match' class is found - if ( !/\?\*/.test( query ) && data.nestedFilters ) { - query = data.isMatch ? query : '^(' + query + ')$'; - } - // parsing the filter may not work properly when using wildcards =/ - return new RegExp( - query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ), - c.widgetOptions.filter_ignoreCase ? 'i' : '' - ) - .test( data.exact ); + init: function( table, c, wo ) { + // filter language options + ts.language = $.extend( true, {}, { + to : 'to', + or : 'or', + and : 'and' + }, ts.language ); + + var options, string, txt, $header, column, filters, val, fxn, noSelect, + regex = ts.filter.regex; + c.$table.addClass( 'hasFilters' ); + + // define timers so using clearTimeout won't cause an undefined error + wo.searchTimer = null; + wo.filter_initTimer = null; + wo.filter_formatterCount = 0; + wo.filter_formatterInit = []; + wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; + wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; + + val = '\\{' + ts.filter.regex.query + '\\}'; + $.extend( regex, { + child : new RegExp( c.cssChildRow ), + filtered : new RegExp( wo.filter_filteredRow ), + alreadyFiltered : new RegExp( '(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i' ), + toTest : new RegExp( '\\s+(-|' + ts.language.to + ')\\s+', 'i' ), + toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)', 'gi' ), + andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), + andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), + orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ), + iQuery : new RegExp( val, 'i' ), + igQuery : new RegExp( val, 'ig' ) + }); + + // don't build filter row if columnFilters is false or all columns are set to 'filter-false' + // see issue #156 + val = c.$headers.filter( '.filter-false, .parser-false' ).length; + if ( wo.filter_columnFilters !== false && val !== c.$headers.length ) { + // build filter row + ts.filter.buildRow( table, c, wo ); } - return null; - }, - // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license ) - fuzzy: function( c, data ) { - if ( /^~/.test( data.iFilter ) ) { - var indx, - patternIndx = 0, - len = data.iExact.length, - txt = data.iFilter.slice( 1 ), - pattern = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; - for ( indx = 0; indx < len; indx++ ) { - if ( data.iExact[ indx ] === pattern[ patternIndx ] ) { - patternIndx += 1; - } + + txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' + .split( ' ' ).join( c.namespace + 'filter ' ); + c.$table.bind( txt, function( event, filter ) { + val = wo.filter_hideEmpty && + $.isEmptyObject( c.cache ) && + !( c.delayInit && event.type === 'appendCache' ); + // hide filter row using the 'filtered' class name + c.$table.find( '.' + tscss.filterRow ).toggleClass( wo.filter_filteredRow, val ); // fixes #450 + if ( !/(search|filter)/.test( event.type ) ) { + event.stopPropagation(); + ts.filter.buildDefault( table, true ); } - if ( patternIndx === pattern.length ) { - return true; + if ( event.type === 'filterReset' ) { + c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' ); + ts.filter.searching( table, [] ); + } else if ( event.type === 'filterEnd' ) { + ts.filter.buildDefault( table, true ); + } else { + // send false argument to force a new search; otherwise if the filter hasn't changed, + // it will return + filter = event.type === 'search' ? filter : + event.type === 'updateComplete' ? c.$table.data( 'lastSearch' ) : ''; + if ( /(update|add)/.test( event.type ) && event.type !== 'updateComplete' ) { + // force a new search since content has changed + c.lastCombinedFilter = null; + c.lastSearch = []; + } + // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first + // input ensures all inputs are updated when a search is triggered on the table + // $( 'table' ).trigger( 'search', [...] ); + ts.filter.searching( table, filter, true ); } return false; - } - return null; - } - }, - init: function( table, c, wo ) { - // filter language options - ts.language = $.extend( true, {}, { - to : 'to', - or : 'or', - and : 'and' - }, ts.language ); - - var options, string, txt, $header, column, filters, val, fxn, noSelect, - regex = ts.filter.regex; - c.$table.addClass( 'hasFilters' ); - - // define timers so using clearTimeout won't cause an undefined error - wo.searchTimer = null; - wo.filter_initTimer = null; - wo.filter_formatterCount = 0; - wo.filter_formatterInit = []; - wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; - wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; - - val = '\\{' + ts.filter.regex.query + '\\}'; - $.extend( regex, { - child : new RegExp( c.cssChildRow ), - filtered : new RegExp( wo.filter_filteredRow ), - alreadyFiltered : new RegExp( '(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i' ), - toTest : new RegExp( '\\s+(-|' + ts.language.to + ')\\s+', 'i' ), - toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)' ,'gi' ), - andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), - andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), - orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ), - iQuery : new RegExp( val, 'i' ), - igQuery : new RegExp( val, 'ig' ) - }); - - // don't build filter row if columnFilters is false or all columns are set to 'filter-false' - // see issue #156 - val = c.$headers.filter( '.filter-false, .parser-false' ).length; - if ( wo.filter_columnFilters !== false && val !== c.$headers.length ) { - // build filter row - ts.filter.buildRow( table, c, wo ); - } - - txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' - .split( ' ' ).join( c.namespace + 'filter ' ); - c.$table.bind( txt, function( event, filter ) { - val = wo.filter_hideEmpty && - $.isEmptyObject( c.cache ) && - !( c.delayInit && event.type === 'appendCache' ); - // hide filter row using the 'filtered' class name - c.$table.find( '.' + tscss.filterRow ).toggleClass( wo.filter_filteredRow, val ); // fixes #450 - if ( !/(search|filter)/.test( event.type ) ) { - event.stopPropagation(); - ts.filter.buildDefault( table, true ); - } - if ( event.type === 'filterReset' ) { - c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' ); - ts.filter.searching( table, [] ); - } else if ( event.type === 'filterEnd' ) { - ts.filter.buildDefault( table, true ); - } else { - // send false argument to force a new search; otherwise if the filter hasn't changed, - // it will return - filter = event.type === 'search' ? filter : - event.type === 'updateComplete' ? c.$table.data( 'lastSearch' ) : ''; - if ( /(update|add)/.test( event.type ) && event.type !== 'updateComplete' ) { - // force a new search since content has changed - c.lastCombinedFilter = null; - c.lastSearch = []; - } - // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first - // input ensures all inputs are updated when a search is triggered on the table - // $( 'table' ).trigger( 'search', [...] ); - ts.filter.searching( table, filter, true ); - } - return false; - }); + }); - // reset button/link - if ( wo.filter_reset ) { - if ( wo.filter_reset instanceof $ ) { - // reset contains a jQuery object, bind to it - wo.filter_reset.click( function() { - c.$table.trigger( 'filterReset' ); - }); - } else if ( $( wo.filter_reset ).length ) { - // reset is a jQuery selector, use event delegation - $( document ) - .undelegate( wo.filter_reset, 'click.tsfilter' ) - .delegate( wo.filter_reset, 'click.tsfilter', function() { - // trigger a reset event, so other functions ( filter_formatter ) know when to reset + // reset button/link + if ( wo.filter_reset ) { + if ( wo.filter_reset instanceof $ ) { + // reset contains a jQuery object, bind to it + wo.filter_reset.click( function() { c.$table.trigger( 'filterReset' ); }); + } else if ( $( wo.filter_reset ).length ) { + // reset is a jQuery selector, use event delegation + $( document ) + .undelegate( wo.filter_reset, 'click.tsfilter' ) + .delegate( wo.filter_reset, 'click.tsfilter', function() { + // trigger a reset event, so other functions ( filter_formatter ) know when to reset + c.$table.trigger( 'filterReset' ); + }); + } } - } - if ( wo.filter_functions ) { - for ( column = 0; column < c.columns; column++ ) { - fxn = ts.getColumnData( table, wo.filter_functions, column ); - if ( fxn ) { - // remove 'filter-select' from header otherwise the options added here are replaced with - // all options - $header = c.$headerIndexed[ column ].removeClass( 'filter-select' ); - // don't build select if 'filter-false' or 'parser-false' set - noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); - options = ''; - if ( fxn === true && noSelect ) { - ts.filter.buildSelect( table, column ); - } else if ( typeof fxn === 'object' && noSelect ) { - // add custom drop down list - for ( string in fxn ) { - if ( typeof string === 'string' ) { - options += options === '' ? - '<option value="">' + - ( $header.data( 'placeholder' ) || - $header.attr( 'data-placeholder' ) || - wo.filter_placeholder.select || - '' - ) + - '</option>' : ''; - val = string; - txt = string; - if ( string.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { - val = string.split( wo.filter_selectSourceSeparator ); - txt = val[1]; - val = val[0]; + if ( wo.filter_functions ) { + for ( column = 0; column < c.columns; column++ ) { + fxn = ts.getColumnData( table, wo.filter_functions, column ); + if ( fxn ) { + // remove 'filter-select' from header otherwise the options added here are replaced with + // all options + $header = c.$headerIndexed[ column ].removeClass( 'filter-select' ); + // don't build select if 'filter-false' or 'parser-false' set + noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); + options = ''; + if ( fxn === true && noSelect ) { + ts.filter.buildSelect( table, column ); + } else if ( typeof fxn === 'object' && noSelect ) { + // add custom drop down list + for ( string in fxn ) { + if ( typeof string === 'string' ) { + options += options === '' ? + '<option value="">' + + ( $header.data( 'placeholder' ) || + $header.attr( 'data-placeholder' ) || + wo.filter_placeholder.select || + '' + ) + + '</option>' : ''; + val = string; + txt = string; + if ( string.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { + val = string.split( wo.filter_selectSourceSeparator ); + txt = val[1]; + val = val[0]; + } + options += '<option ' + + ( txt === val ? '' : 'data-function-name="' + string + '" ' ) + + 'value="' + val + '">' + txt + '</option>'; } - options += '<option ' + - ( txt === val ? '' : 'data-function-name="' + string + '" ' ) + - 'value="' + val + '">' + txt + '</option>'; } - } - c.$table - .find( 'thead' ) - .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) - .append( options ); - txt = wo.filter_selectSource; - fxn = $.isFunction( txt ) ? true : ts.getColumnData( table, txt, column ); - if ( fxn ) { - // updating so the extra options are appended - ts.filter.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) ); + c.$table + .find( 'thead' ) + .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) + .append( options ); + txt = wo.filter_selectSource; + fxn = $.isFunction( txt ) ? true : ts.getColumnData( table, txt, column ); + if ( fxn ) { + // updating so the extra options are appended + ts.filter.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) ); + } } } } } - } - // not really updating, but if the column has both the 'filter-select' class & - // filter_functions set to true, it would append the same options twice. - ts.filter.buildDefault( table, true ); + // not really updating, but if the column has both the 'filter-select' class & + // filter_functions set to true, it would append the same options twice. + ts.filter.buildDefault( table, true ); - ts.filter.bindSearch( table, c.$table.find( '.' + tscss.filter ), true ); - if ( wo.filter_external ) { - ts.filter.bindSearch( table, wo.filter_external ); - } + ts.filter.bindSearch( table, c.$table.find( '.' + tscss.filter ), true ); + if ( wo.filter_external ) { + ts.filter.bindSearch( table, wo.filter_external ); + } - if ( wo.filter_hideFilters ) { - ts.filter.hideFilters( table, c ); - } + if ( wo.filter_hideFilters ) { + ts.filter.hideFilters( table, c ); + } - // show processing icon - if ( c.showProcessing ) { - txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter ' ); - c.$table - .unbind( txt.replace( /\s+/g, ' ' ) ) - .bind( txt, function( event, columns ) { - // only add processing to certain columns to all columns - $header = ( columns ) ? - c.$table - .find( '.' + tscss.header ) - .filter( '[data-column]' ) - .filter( function() { - return columns[ $( this ).data( 'column' ) ] !== ''; - }) : ''; - ts.isProcessing( table, event.type === 'filterStart', columns ? $header : '' ); - }); - } + // show processing icon + if ( c.showProcessing ) { + txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter ' ); + c.$table + .unbind( txt.replace( /\s+/g, ' ' ) ) + .bind( txt, function( event, columns ) { + // only add processing to certain columns to all columns + $header = ( columns ) ? + c.$table + .find( '.' + tscss.header ) + .filter( '[data-column]' ) + .filter( function() { + return columns[ $( this ).data( 'column' ) ] !== ''; + }) : ''; + ts.isProcessing( table, event.type === 'filterStart', columns ? $header : '' ); + }); + } + + // set filtered rows count ( intially unfiltered ) + c.filteredRows = c.totalRows; - // set filtered rows count ( intially unfiltered ) - c.filteredRows = c.totalRows; - - // add default values - txt = 'tablesorter-initialized pagerBeforeInitialized '.split( ' ' ).join( c.namespace + 'filter ' ); - c.$table - .unbind( txt.replace( /\s+/g, ' ' ) ) - .bind( txt, function() { - // redefine 'wo' as it does not update properly inside this callback - var wo = this.config.widgetOptions; - filters = ts.filter.setDefaults( table, c, wo ) || []; - if ( filters.length ) { - // prevent delayInit from triggering a cache build if filters are empty - if ( !( c.delayInit && filters.join( '' ) === '' ) ) { - ts.setFilters( table, filters, true ); + // add default values + txt = 'tablesorter-initialized pagerBeforeInitialized '.split( ' ' ).join( c.namespace + 'filter ' ); + c.$table + .unbind( txt.replace( /\s+/g, ' ' ) ) + .bind( txt, function() { + // redefine 'wo' as it does not update properly inside this callback + var wo = this.config.widgetOptions; + filters = ts.filter.setDefaults( table, c, wo ) || []; + if ( filters.length ) { + // prevent delayInit from triggering a cache build if filters are empty + if ( !( c.delayInit && filters.join( '' ) === '' ) ) { + ts.setFilters( table, filters, true ); + } } - } - c.$table.trigger( 'filterFomatterUpdate' ); - // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers - setTimeout( function() { - if ( !wo.filter_initialized ) { + c.$table.trigger( 'filterFomatterUpdate' ); + // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers + setTimeout( function() { + if ( !wo.filter_initialized ) { + ts.filter.filterInitComplete( c ); + } + }, 100 ); + }); + // if filter widget is added after pager has initialized; then set filter init flag + if ( c.pager && c.pager.initialized && !wo.filter_initialized ) { + c.$table.trigger( 'filterFomatterUpdate' ); + setTimeout( function() { ts.filter.filterInitComplete( c ); - } - }, 100 ); - }); - // if filter widget is added after pager has initialized; then set filter init flag - if ( c.pager && c.pager.initialized && !wo.filter_initialized ) { - c.$table.trigger( 'filterFomatterUpdate' ); - setTimeout( function() { - ts.filter.filterInitComplete( c ); - }, 100 ); - } - }, - // $cell parameter, but not the config, is passed to the filter_formatters, - // so we have to work with it instead - formatterUpdated: function( $cell, column ) { - var wo = $cell.closest( 'table' )[0].config.widgetOptions; - if ( !wo.filter_initialized ) { - // add updates by column since this function - // may be called numerous times before initialization - wo.filter_formatterInit[ column ] = 1; - } - }, - filterInitComplete: function( c ) { - var indx, len, - wo = c.widgetOptions, - count = 0, - completed = function() { - wo.filter_initialized = true; - c.$table.trigger( 'filterInit', c ); - ts.filter.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); - }; - if ( $.isEmptyObject( wo.filter_formatter ) ) { - completed(); - } else { - len = wo.filter_formatterInit.length; - for ( indx = 0; indx < len; indx++ ) { - if ( wo.filter_formatterInit[ indx ] === 1 ) { - count++; - } - } - clearTimeout( wo.filter_initTimer ); - if ( !wo.filter_initialized && count === wo.filter_formatterCount ) { - // filter widget initialized - completed(); - } else if ( !wo.filter_initialized ) { - // fall back in case a filter_formatter doesn't call - // $.tablesorter.filter.formatterUpdated( $cell, column ), and the count is off - wo.filter_initTimer = setTimeout( function() { - completed(); - }, 500 ); + }, 100 ); } - } - }, - setDefaults: function( table, c, wo ) { - var isArray, saved, indx, col, $filters, - // get current ( default ) filters - filters = ts.getFilters( table ) || []; - if ( wo.filter_saveFilters && ts.storage ) { - saved = ts.storage( table, 'tablesorter-filters' ) || []; - isArray = $.isArray( saved ); - // make sure we're not just getting an empty array - if ( !( isArray && saved.join( '' ) === '' || !isArray ) ) { - filters = saved; - } - } - // if no filters saved, then check default settings - if ( filters.join( '' ) === '' ) { - // allow adding default setting to external filters - $filters = c.$headers.add( wo.filter_$externalFilters ) - .filter( '[' + wo.filter_defaultAttrib + ']' ); - for ( indx = 0; indx <= c.columns; indx++ ) { - // include data-column='all' external filters - col = indx === c.columns ? 'all' : indx; - filters[indx] = $filters - .filter( '[data-column="' + col + '"]' ) - .attr( wo.filter_defaultAttrib ) || filters[indx] || ''; + }, + // $cell parameter, but not the config, is passed to the filter_formatters, + // so we have to work with it instead + formatterUpdated: function( $cell, column ) { + var wo = $cell.closest( 'table' )[0].config.widgetOptions; + if ( !wo.filter_initialized ) { + // add updates by column since this function + // may be called numerous times before initialization + wo.filter_formatterInit[ column ] = 1; } - } - c.$table.data( 'lastSearch', filters ); - return filters; - }, - parseFilter: function( c, filter, column, parsed ) { - return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter; - }, - buildRow: function( table, c, wo ) { - var col, column, $header, buildSelect, disabled, name, ffxn, tmp, - // c.columns defined in computeThIndexes() - cellFilter = wo.filter_cellFilter, - columns = c.columns, - arry = $.isArray( cellFilter ), - buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; - for ( column = 0; column < columns; column++ ) { - buildFilter += '<td'; - if ( arry ) { - buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' ); + }, + filterInitComplete: function( c ) { + var indx, len, + wo = c.widgetOptions, + count = 0, + completed = function() { + wo.filter_initialized = true; + c.$table.trigger( 'filterInit', c ); + ts.filter.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); + }; + if ( $.isEmptyObject( wo.filter_formatter ) ) { + completed(); } else { - buildFilter += ( cellFilter !== '' ? ' class="' + cellFilter + '"' : '' ); + len = wo.filter_formatterInit.length; + for ( indx = 0; indx < len; indx++ ) { + if ( wo.filter_formatterInit[ indx ] === 1 ) { + count++; + } + } + clearTimeout( wo.filter_initTimer ); + if ( !wo.filter_initialized && count === wo.filter_formatterCount ) { + // filter widget initialized + completed(); + } else if ( !wo.filter_initialized ) { + // fall back in case a filter_formatter doesn't call + // $.tablesorter.filter.formatterUpdated( $cell, column ), and the count is off + wo.filter_initTimer = setTimeout( function() { + completed(); + }, 500 ); + } } - buildFilter += '></td>'; - } - c.$filters = $( buildFilter += '</tr>' ) - .appendTo( c.$table.children( 'thead' ).eq( 0 ) ) - .find( 'td' ); - // build each filter input - for ( column = 0; column < columns; column++ ) { - disabled = false; - // assuming last cell of a column is the main column - $header = c.$headerIndexed[ column ]; - ffxn = ts.getColumnData( table, wo.filter_functions, column ); - buildSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || - $header.hasClass( 'filter-select' ); - // get data from jQuery data, metadata, headers option or header class name - col = ts.getColumnData( table, c.headers, column ); - disabled = ts.getData( $header[0], col, 'filter' ) === 'false' || - ts.getData( $header[0], col, 'parser' ) === 'false'; - - if ( buildSelect ) { - buildFilter = $( '<select>' ).appendTo( c.$filters.eq( column ) ); - } else { - ffxn = ts.getColumnData( table, wo.filter_formatter, column ); - if ( ffxn ) { - wo.filter_formatterCount++; - buildFilter = ffxn( c.$filters.eq( column ), column ); - // no element returned, so lets go find it - if ( buildFilter && buildFilter.length === 0 ) { - buildFilter = c.$filters.eq( column ).children( 'input' ); + }, + setDefaults: function( table, c, wo ) { + var isArray, saved, indx, col, $filters, + // get current ( default ) filters + filters = ts.getFilters( table ) || []; + if ( wo.filter_saveFilters && ts.storage ) { + saved = ts.storage( table, 'tablesorter-filters' ) || []; + isArray = $.isArray( saved ); + // make sure we're not just getting an empty array + if ( !( isArray && saved.join( '' ) === '' || !isArray ) ) { + filters = saved; + } + } + // if no filters saved, then check default settings + if ( filters.join( '' ) === '' ) { + // allow adding default setting to external filters + $filters = c.$headers.add( wo.filter_$externalFilters ) + .filter( '[' + wo.filter_defaultAttrib + ']' ); + for ( indx = 0; indx <= c.columns; indx++ ) { + // include data-column='all' external filters + col = indx === c.columns ? 'all' : indx; + filters[indx] = $filters + .filter( '[data-column="' + col + '"]' ) + .attr( wo.filter_defaultAttrib ) || filters[indx] || ''; + } + } + c.$table.data( 'lastSearch', filters ); + return filters; + }, + parseFilter: function( c, filter, column, parsed ) { + return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter; + }, + buildRow: function( table, c, wo ) { + var col, column, $header, buildSelect, disabled, name, ffxn, tmp, + // c.columns defined in computeThIndexes() + cellFilter = wo.filter_cellFilter, + columns = c.columns, + arry = $.isArray( cellFilter ), + buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; + for ( column = 0; column < columns; column++ ) { + buildFilter += '<td'; + if ( arry ) { + buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' ); + } else { + buildFilter += ( cellFilter !== '' ? ' class="' + cellFilter + '"' : '' ); + } + buildFilter += '></td>'; + } + c.$filters = $( buildFilter += '</tr>' ) + .appendTo( c.$table.children( 'thead' ).eq( 0 ) ) + .find( 'td' ); + // build each filter input + for ( column = 0; column < columns; column++ ) { + disabled = false; + // assuming last cell of a column is the main column + $header = c.$headerIndexed[ column ]; + ffxn = ts.getColumnData( table, wo.filter_functions, column ); + buildSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || + $header.hasClass( 'filter-select' ); + // get data from jQuery data, metadata, headers option or header class name + col = ts.getColumnData( table, c.headers, column ); + disabled = ts.getData( $header[0], col, 'filter' ) === 'false' || + ts.getData( $header[0], col, 'parser' ) === 'false'; + + if ( buildSelect ) { + buildFilter = $( '<select>' ).appendTo( c.$filters.eq( column ) ); + } else { + ffxn = ts.getColumnData( table, wo.filter_formatter, column ); + if ( ffxn ) { + wo.filter_formatterCount++; + buildFilter = ffxn( c.$filters.eq( column ), column ); + // no element returned, so lets go find it + if ( buildFilter && buildFilter.length === 0 ) { + buildFilter = c.$filters.eq( column ).children( 'input' ); + } + // element not in DOM, so lets attach it + if ( buildFilter && ( buildFilter.parent().length === 0 || + ( buildFilter.parent().length && buildFilter.parent()[0] !== c.$filters[column] ) ) ) { + c.$filters.eq( column ).append( buildFilter ); + } + } else { + buildFilter = $( '<input type="search">' ).appendTo( c.$filters.eq( column ) ); } - // element not in DOM, so lets attach it - if ( buildFilter && ( buildFilter.parent().length === 0 || - ( buildFilter.parent().length && buildFilter.parent()[0] !== c.$filters[column] ) ) ) { - c.$filters.eq( column ).append( buildFilter ); + if ( buildFilter ) { + tmp = $header.data( 'placeholder' ) || + $header.attr( 'data-placeholder' ) || + wo.filter_placeholder.search || ''; + buildFilter.attr( 'placeholder', tmp ); } - } else { - buildFilter = $( '<input type="search">' ).appendTo( c.$filters.eq( column ) ); } if ( buildFilter ) { - tmp = $header.data( 'placeholder' ) || - $header.attr( 'data-placeholder' ) || - wo.filter_placeholder.search || ''; - buildFilter.attr( 'placeholder', tmp ); + // add filter class name + name = ( $.isArray( wo.filter_cssFilter ) ? + ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : + wo.filter_cssFilter ) || ''; + buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column ); + if ( disabled ) { + buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; + } } } - if ( buildFilter ) { - // add filter class name - name = ( $.isArray( wo.filter_cssFilter ) ? - ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : - wo.filter_cssFilter ) || ''; - buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column ); - if ( disabled ) { - buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; + }, + bindSearch: function( table, $el, internal ) { + table = $( table )[0]; + $el = $( $el ); // allow passing a selector string + if ( !$el.length ) { return; } + var tmp, + c = table.config, + wo = c.widgetOptions, + namespace = c.namespace + 'filter', + $ext = wo.filter_$externalFilters; + if ( internal !== true ) { + // save anyMatch element + tmp = wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector; + wo.filter_$anyMatch = $el.filter( tmp ); + if ( $ext && $ext.length ) { + wo.filter_$externalFilters = wo.filter_$externalFilters.add( $el ); + } else { + wo.filter_$externalFilters = $el; } + // update values ( external filters added after table initialization ) + ts.setFilters( table, c.$table.data( 'lastSearch' ) || [], internal === false ); } - } - }, - bindSearch: function( table, $el, internal ) { - table = $( table )[0]; - $el = $( $el ); // allow passing a selector string - if ( !$el.length ) { return; } - var tmp, - c = table.config, - wo = c.widgetOptions, - namespace = c.namespace + 'filter', - $ext = wo.filter_$externalFilters; - if ( internal !== true ) { - // save anyMatch element - tmp = wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector; - wo.filter_$anyMatch = $el.filter( tmp ); - if ( $ext && $ext.length ) { - wo.filter_$externalFilters = wo.filter_$externalFilters.add( $el ); + // unbind events + tmp = ( 'keypress keyup search change '.split( ' ' ).join( namespace + ' ' ) ); + $el + // use data attribute instead of jQuery data since the head is cloned without including + // the data/binding + .attr( 'data-lastSearchTime', new Date().getTime() ) + .unbind( tmp.replace( /\s+/g, ' ' ) ) + // include change for select - fixes #473 + .bind( 'keyup' + namespace, function( event ) { + $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); + // emulate what webkit does.... escape clears the filter + if ( event.which === 27 ) { + this.value = ''; + // live search + } else if ( wo.filter_liveSearch === false ) { + return; + // don't return if the search value is empty ( all rows need to be revealed ) + } else if ( this.value !== '' && ( + // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace + ( typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch ) || + // let return & backspace continue on, but ignore arrows & non-valid characters + ( event.which !== 13 && event.which !== 8 && + ( event.which < 32 || ( event.which >= 37 && event.which <= 40 ) ) ) ) ) { + return; + } + // change event = no delay; last true flag tells getFilters to skip newest timed input + ts.filter.searching( table, true, true ); + }) + .bind( 'search change keypress '.split( ' ' ).join( namespace + ' ' ), function( event ) { + var column = $( this ).data( 'column' ); + // don't allow 'change' event to process if the input value is the same - fixes #685 + if ( event.which === 13 || event.type === 'search' || + event.type === 'change' && this.value !== c.lastSearch[column] ) { + event.preventDefault(); + // init search with no delay + $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); + ts.filter.searching( table, false, true ); + } + }); + }, + searching: function( table, filter, skipFirst ) { + var wo = table.config.widgetOptions; + clearTimeout( wo.searchTimer ); + if ( typeof filter === 'undefined' || filter === true ) { + // delay filtering + wo.searchTimer = setTimeout( function() { + ts.filter.checkFilters( table, filter, skipFirst ); + }, wo.filter_liveSearch ? wo.filter_searchDelay : 10 ); } else { - wo.filter_$externalFilters = $el; + // skip delay + ts.filter.checkFilters( table, filter, skipFirst ); } - // update values ( external filters added after table initialization ) - ts.setFilters( table, c.$table.data( 'lastSearch' ) || [], internal === false ); - } - // unbind events - tmp = ( 'keypress keyup search change '.split( ' ' ).join( namespace + ' ' ) ); - $el - // use data attribute instead of jQuery data since the head is cloned without including - // the data/binding - .attr( 'data-lastSearchTime', new Date().getTime() ) - .unbind( tmp.replace( /\s+/g, ' ' ) ) - // include change for select - fixes #473 - .bind( 'keyup' + namespace, function( event ) { - $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - // emulate what webkit does.... escape clears the filter - if ( event.which === 27 ) { - this.value = ''; - // live search - } else if ( wo.filter_liveSearch === false ) { + }, + checkFilters: function( table, filter, skipFirst ) { + var c = table.config, + wo = c.widgetOptions, + filterArray = $.isArray( filter ), + filters = ( filterArray ) ? filter : ts.getFilters( table, true ), + combinedFilters = ( filters || [] ).join( '' ); // combined filter values + // prevent errors if delay init is set + if ( $.isEmptyObject( c.cache ) ) { + // update cache if delayInit set & pager has initialized ( after user initiates a search ) + if ( c.delayInit && c.pager && c.pager.initialized ) { + c.$table.trigger( 'updateCache', [ function() { + ts.filter.checkFilters( table, false, skipFirst ); + } ] ); + } return; - // don't return if the search value is empty ( all rows need to be revealed ) - } else if ( this.value !== '' && ( - // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace - ( typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch ) || - // let return & backspace continue on, but ignore arrows & non-valid characters - ( event.which !== 13 && event.which !== 8 && - ( event.which < 32 || ( event.which >= 37 && event.which <= 40 ) ) ) ) ) { + } + // add filter array back into inputs + if ( filterArray ) { + ts.setFilters( table, filters, false, skipFirst !== true ); + if ( !wo.filter_initialized ) { c.lastCombinedFilter = ''; } + } + if ( wo.filter_hideFilters ) { + // show/hide filter row as needed + c.$table + .find( '.' + tscss.filterRow ) + .trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); + } + // return if the last search is the same; but filter === false when updating the search + // see example-widget-filter.html filter toggle buttons + if ( c.lastCombinedFilter === combinedFilters && filter !== false ) { return; + } else if ( filter === false ) { + // force filter refresh + c.lastCombinedFilter = null; + c.lastSearch = []; } - // change event = no delay; last true flag tells getFilters to skip newest timed input - ts.filter.searching( table, true, true ); - }) - .bind( 'search change keypress '.split( ' ' ).join( namespace + ' ' ), function( event ) { - var column = $( this ).data( 'column' ); - // don't allow 'change' event to process if the input value is the same - fixes #685 - if ( event.which === 13 || event.type === 'search' || - event.type === 'change' && this.value !== c.lastSearch[column] ) { - event.preventDefault(); - // init search with no delay - $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - ts.filter.searching( table, false, true ); + if ( wo.filter_initialized ) { + c.$table.trigger( 'filterStart', [ filters ] ); } - }); - }, - searching: function( table, filter, skipFirst ) { - var wo = table.config.widgetOptions; - clearTimeout( wo.searchTimer ); - if ( typeof filter === 'undefined' || filter === true ) { - // delay filtering - wo.searchTimer = setTimeout( function() { - ts.filter.checkFilters( table, filter, skipFirst ); - }, wo.filter_liveSearch ? wo.filter_searchDelay : 10 ); - } else { - // skip delay - ts.filter.checkFilters( table, filter, skipFirst ); - } - }, - checkFilters: function( table, filter, skipFirst ) { - var c = table.config, - wo = c.widgetOptions, - filterArray = $.isArray( filter ), - filters = ( filterArray ) ? filter : ts.getFilters( table, true ), - combinedFilters = ( filters || [] ).join( '' ); // combined filter values - // prevent errors if delay init is set - if ( $.isEmptyObject( c.cache ) ) { - // update cache if delayInit set & pager has initialized ( after user initiates a search ) - if ( c.delayInit && c.pager && c.pager.initialized ) { - c.$table.trigger( 'updateCache', [ function() { - ts.filter.checkFilters( table, false, skipFirst ); - } ] ); + if ( c.showProcessing ) { + // give it time for the processing icon to kick in + setTimeout( function() { + ts.filter.findRows( table, filters, combinedFilters ); + return false; + }, 30 ); + } else { + ts.filter.findRows( table, filters, combinedFilters ); + return false; } - return; - } - // add filter array back into inputs - if ( filterArray ) { - ts.setFilters( table, filters, false, skipFirst !== true ); - if ( !wo.filter_initialized ) { c.lastCombinedFilter = ''; } - } - if ( wo.filter_hideFilters ) { - // show/hide filter row as needed + }, + hideFilters: function( table, c ) { + var timer; c.$table .find( '.' + tscss.filterRow ) - .trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); - } - // return if the last search is the same; but filter === false when updating the search - // see example-widget-filter.html filter toggle buttons - if ( c.lastCombinedFilter === combinedFilters && filter !== false ) { - return; - } else if ( filter === false ) { - // force filter refresh - c.lastCombinedFilter = null; - c.lastSearch = []; - } - if ( wo.filter_initialized ) { - c.$table.trigger( 'filterStart', [filters] ); - } - if ( c.showProcessing ) { - // give it time for the processing icon to kick in - setTimeout( function() { - ts.filter.findRows( table, filters, combinedFilters ); - return false; - }, 30 ); - } else { - ts.filter.findRows( table, filters, combinedFilters ); - return false; - } - }, - hideFilters: function( table, c ) { - var timer; - c.$table - .find( '.' + tscss.filterRow ) - .bind( 'mouseenter mouseleave', function( e ) { - // save event object - http://bugs.jquery.com/ticket/12140 - var event = e, - $filterRow = $( this ); - clearTimeout( timer ); - timer = setTimeout( function() { - if ( /enter|over/.test( event.type ) ) { - $filterRow.removeClass( tscss.filterRowHide ); - } else { - // don't hide if input has focus - // $( ':focus' ) needs jQuery 1.6+ - if ( $( document.activeElement ).closest( 'tr' )[0] !== $filterRow[0] ) { - // don't hide row if any filter has a value - if ( c.lastCombinedFilter === '' ) { - $filterRow.addClass( tscss.filterRowHide ); + .bind( 'mouseenter mouseleave', function( e ) { + // save event object - http://bugs.jquery.com/ticket/12140 + var event = e, + $filterRow = $( this ); + clearTimeout( timer ); + timer = setTimeout( function() { + if ( /enter|over/.test( event.type ) ) { + $filterRow.removeClass( tscss.filterRowHide ); + } else { + // don't hide if input has focus + // $( ':focus' ) needs jQuery 1.6+ + if ( $( document.activeElement ).closest( 'tr' )[0] !== $filterRow[0] ) { + // don't hide row if any filter has a value + if ( c.lastCombinedFilter === '' ) { + $filterRow.addClass( tscss.filterRowHide ); + } } } - } - }, 200 ); - }) - .find( 'input, select' ).bind( 'focus blur', function( e ) { - var event = e, - $row = $( this ).closest( 'tr' ); - clearTimeout( timer ); - timer = setTimeout( function() { + }, 200 ); + }) + .find( 'input, select' ).bind( 'focus blur', function( e ) { + var event = e, + $row = $( this ).closest( 'tr' ); clearTimeout( timer ); - // don't hide row if any filter has a value - if ( ts.getFilters( c.$table ).join( '' ) === '' ) { - $row.toggleClass( tscss.filterRowHide, event.type !== 'focus' ); - } - }, 200 ); - }); - }, - defaultFilter: function( filter, mask ) { - if ( filter === '' ) { return filter; } - var regex = ts.filter.regex.iQuery, - maskLen = mask.match( ts.filter.regex.igQuery ).length, - query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ], - len = query.length - 1, - indx = 0, - val = mask; - if ( len < 1 && maskLen > 1 ) { - // only one 'word' in query but mask has >1 slots - query[1] = query[0]; - } - // replace all {query} with query words... - // if query = 'Bob', then convert mask from '!{query}' to '!Bob' - // if query = 'Bob Joe Frank', then convert mask '{q} OR {q}' to 'Bob OR Joe OR Frank' - while ( regex.test( val ) ) { - val = val.replace( regex, query[indx++] || '' ); - if ( regex.test( val ) && indx < len && ( query[indx] || '' ) !== '' ) { - val = mask.replace( regex, val ); + timer = setTimeout( function() { + clearTimeout( timer ); + // don't hide row if any filter has a value + if ( ts.getFilters( c.$table ).join( '' ) === '' ) { + $row.toggleClass( tscss.filterRowHide, event.type !== 'focus' ); + } + }, 200 ); + }); + }, + defaultFilter: function( filter, mask ) { + if ( filter === '' ) { return filter; } + var regex = ts.filter.regex.iQuery, + maskLen = mask.match( ts.filter.regex.igQuery ).length, + query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ], + len = query.length - 1, + indx = 0, + val = mask; + if ( len < 1 && maskLen > 1 ) { + // only one 'word' in query but mask has >1 slots + query[1] = query[0]; } - } - return val; - }, - getLatestSearch: function( $input ) { - if ( $input ) { - return $input.sort( function( a, b ) { - return $( b ).attr( 'data-lastSearchTime' ) - $( a ).attr( 'data-lastSearchTime' ); - }); - } - return $input || $(); - }, - multipleColumns: function( c, $input ) { - // look for multiple columns '1-3,4-6,8' in data-column - var temp, ranges, range, start, end, singles, i, indx, len, - wo = c.widgetOptions, - // only target 'all' column inputs on initialization - // & don't target 'all' column inputs if they don't exist - targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, - columns = [], - val = $.trim( ts.filter.getLatestSearch( $input ).attr( 'data-column' ) || '' ); - // process column range - if ( targets && /-/.test( val ) ) { - ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); - len = ranges.length; - for ( indx = 0; indx < len; indx++ ) { - range = ranges[indx].split( /\s*-\s*/ ); - start = parseInt( range[0], 10 ) || 0; - end = parseInt( range[1], 10 ) || ( c.columns - 1 ); - if ( start > end ) { - temp = start; start = end; end = temp; // swap - } - if ( end >= c.columns ) { - end = c.columns - 1; - } - for ( ; start <= end; start++ ) { - columns.push( start ); - } - // remove processed range from val - val = val.replace( ranges[ indx ], '' ); + // replace all {query} with query words... + // if query = 'Bob', then convert mask from '!{query}' to '!Bob' + // if query = 'Bob Joe Frank', then convert mask '{q} OR {q}' to 'Bob OR Joe OR Frank' + while ( regex.test( val ) ) { + val = val.replace( regex, query[indx++] || '' ); + if ( regex.test( val ) && indx < len && ( query[indx] || '' ) !== '' ) { + val = mask.replace( regex, val ); + } } - } - // process single columns - if ( targets && /,/.test( val ) ) { - singles = val.split( /\s*,\s*/ ); - len = singles.length; - for ( i = 0; i < len; i++ ) { - if ( singles[ i ] !== '' ) { - indx = parseInt( singles[ i ], 10 ); - if ( indx < c.columns ) { - columns.push( indx ); + return val; + }, + getLatestSearch: function( $input ) { + if ( $input ) { + return $input.sort( function( a, b ) { + return $( b ).attr( 'data-lastSearchTime' ) - $( a ).attr( 'data-lastSearchTime' ); + }); + } + return $input || $(); + }, + multipleColumns: function( c, $input ) { + // look for multiple columns '1-3,4-6,8' in data-column + var temp, ranges, range, start, end, singles, i, indx, len, + wo = c.widgetOptions, + // only target 'all' column inputs on initialization + // & don't target 'all' column inputs if they don't exist + targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, + columns = [], + val = $.trim( ts.filter.getLatestSearch( $input ).attr( 'data-column' ) || '' ); + // process column range + if ( targets && /-/.test( val ) ) { + ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); + len = ranges.length; + for ( indx = 0; indx < len; indx++ ) { + range = ranges[indx].split( /\s*-\s*/ ); + start = parseInt( range[0], 10 ) || 0; + end = parseInt( range[1], 10 ) || ( c.columns - 1 ); + if ( start > end ) { + temp = start; start = end; end = temp; // swap + } + if ( end >= c.columns ) { + end = c.columns - 1; } + for ( ; start <= end; start++ ) { + columns.push( start ); + } + // remove processed range from val + val = val.replace( ranges[ indx ], '' ); } } - } - // return all columns - if ( !columns.length ) { - for ( indx = 0; indx < c.columns; indx++ ) { - columns.push( indx ); + // process single columns + if ( targets && /,/.test( val ) ) { + singles = val.split( /\s*,\s*/ ); + len = singles.length; + for ( i = 0; i < len; i++ ) { + if ( singles[ i ] !== '' ) { + indx = parseInt( singles[ i ], 10 ); + if ( indx < c.columns ) { + columns.push( indx ); + } + } + } } - } - return columns; - }, - processTypes: function( c, data, vars ) { - var ffxn, - filterMatched = null, - matches = null; - for ( ffxn in ts.filter.types ) { - if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) { - matches = ts.filter.types[ffxn]( c, data, vars ); - if ( matches !== null ) { - filterMatched = matches; + // return all columns + if ( !columns.length ) { + for ( indx = 0; indx < c.columns; indx++ ) { + columns.push( indx ); } } - } - return filterMatched; - }, - processRow: function( c, data, vars ) { - var columnIndex, hasSelect, result, val, filterMatched, - fxn, ffxn, txt, - regex = ts.filter.regex, - wo = c.widgetOptions, - showRow = true; - data.$cells = data.$row.children(); - - if ( data.anyMatchFlag ) { - // look for multiple columns '1-3,4-6,8' - columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); - data.anyMatch = true; - data.isMatch = true; - data.rowArray = data.$cells.map( function( i ) { - if ( $.inArray( i, columnIndex ) > -1 ) { - if ( data.parsed[ i ] ) { - txt = data.cacheArray[ i ]; - } else { - txt = data.rawArray[ i ]; - txt = $.trim( wo.filter_ignoreCase ? txt.toLowerCase() : txt ); - if ( c.sortLocaleCompare ) { - txt = ts.replaceAccents( txt ); - } + return columns; + }, + processTypes: function( c, data, vars ) { + var ffxn, + filterMatched = null, + matches = null; + for ( ffxn in ts.filter.types ) { + if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) { + matches = ts.filter.types[ffxn]( c, data, vars ); + if ( matches !== null ) { + filterMatched = matches; } - return txt; } - }).get(); - data.filter = data.anyMatchFilter; - data.iFilter = data.iAnyMatchFilter; - data.exact = data.rowArray.join( ' ' ); - data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; - data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' ); - - vars.excludeMatch = vars.noAnyMatch; - filterMatched = ts.filter.processTypes( c, data, vars ); - - if ( filterMatched !== null ) { - showRow = filterMatched; - } else { - if ( wo.filter_startsWith ) { - showRow = false; - columnIndex = c.columns; - while ( !showRow && columnIndex > 0 ) { - columnIndex--; - showRow = showRow || data.rowArray[ columnIndex ].indexOf( data.iFilter ) === 0; + } + return filterMatched; + }, + processRow: function( c, data, vars ) { + var columnIndex, hasSelect, result, val, filterMatched, + fxn, ffxn, txt, + regex = ts.filter.regex, + wo = c.widgetOptions, + showRow = true; + data.$cells = data.$row.children(); + + if ( data.anyMatchFlag ) { + // look for multiple columns '1-3,4-6,8' + columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); + data.anyMatch = true; + data.isMatch = true; + data.rowArray = data.$cells.map( function( i ) { + if ( $.inArray( i, columnIndex ) > -1 ) { + if ( data.parsed[ i ] ) { + txt = data.cacheArray[ i ]; + } else { + txt = data.rawArray[ i ]; + txt = $.trim( wo.filter_ignoreCase ? txt.toLowerCase() : txt ); + if ( c.sortLocaleCompare ) { + txt = ts.replaceAccents( txt ); + } + } + return txt; } + }).get(); + data.filter = data.anyMatchFilter; + data.iFilter = data.iAnyMatchFilter; + data.exact = data.rowArray.join( ' ' ); + data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; + data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' ); + + vars.excludeMatch = vars.noAnyMatch; + filterMatched = ts.filter.processTypes( c, data, vars ); + + if ( filterMatched !== null ) { + showRow = filterMatched; } else { - showRow = ( data.iExact + data.childRowText ).indexOf( data.iFilter ) >= 0; + if ( wo.filter_startsWith ) { + showRow = false; + columnIndex = c.columns; + while ( !showRow && columnIndex > 0 ) { + columnIndex--; + showRow = showRow || data.rowArray[ columnIndex ].indexOf( data.iFilter ) === 0; + } + } else { + showRow = ( data.iExact + data.childRowText ).indexOf( data.iFilter ) >= 0; + } + } + data.anyMatch = false; + // no other filters to process + if ( data.filters.join( '' ) === data.filter ) { + return showRow; } } - data.anyMatch = false; - // no other filters to process - if ( data.filters.join( '' ) === data.filter ) { - return showRow; - } - } - for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { - data.filter = data.filters[ columnIndex ]; - data.index = columnIndex; + for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { + data.filter = data.filters[ columnIndex ]; + data.index = columnIndex; - // filter types to exclude, per column - vars.excludeMatch = vars.excludeFilter[ columnIndex ]; + // filter types to exclude, per column + vars.excludeMatch = vars.excludeFilter[ columnIndex ]; - // ignore if filter is empty or disabled - if ( data.filter ) { - data.cache = data.cacheArray[ columnIndex ]; - // check if column data should be from the cell or from parsed data - if ( wo.filter_useParsedData || data.parsed[ columnIndex ] ) { - data.exact = data.cache; - } else { - result = data.rawArray[ columnIndex ] || ''; - data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 - } - data.iExact = !regex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? - data.exact.toLowerCase() : data.exact; - - data.isMatch = c.$headerIndexed[ data.index ].hasClass( 'filter-match' ); - - result = showRow; // if showRow is true, show that row - - // in case select filter option has a different value vs text 'a - z|A through Z' - ffxn = wo.filter_columnFilters ? - c.$filters.add( c.$externalFilters ) - .filter( '[data-column="'+ columnIndex + '"]' ) - .find( 'select option:selected' ) - .attr( 'data-function-name' ) || '' : ''; - // replace accents - see #357 - if ( c.sortLocaleCompare ) { - data.filter = ts.replaceAccents( data.filter ); - } - - val = true; - if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) { - data.filter = ts.filter.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); - // val is used to indicate that a filter select is using a default filter; - // so we override the exact & partial matches - val = false; - } - // data.iFilter = case insensitive ( if wo.filter_ignoreCase is true ), - // data.filter = case sensitive - data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter; - fxn = vars.functions[ columnIndex ]; - hasSelect = c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' ); - filterMatched = null; - if ( fxn || ( hasSelect && val ) ) { - if ( fxn === true || hasSelect ) { - // default selector uses exact match unless 'filter-match' class is found - filterMatched = data.isMatch ? - data.iExact.search( data.iFilter ) >= 0 : - data.filter === data.exact; - } else if ( typeof fxn === 'function' ) { - // filter callback( exact cell content, parser normalized content, - // filter input value, column index, jQuery row object ) - filterMatched = fxn( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); - } else if ( typeof fxn[ ffxn || data.filter ] === 'function' ) { - // selector option function - txt = ffxn || data.filter; - filterMatched = - fxn[ txt ]( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); + // ignore if filter is empty or disabled + if ( data.filter ) { + data.cache = data.cacheArray[ columnIndex ]; + // check if column data should be from the cell or from parsed data + if ( wo.filter_useParsedData || data.parsed[ columnIndex ] ) { + data.exact = data.cache; + } else { + result = data.rawArray[ columnIndex ] || ''; + data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 } - } - if ( filterMatched === null ) { - // cycle through the different filters - // filters return a boolean or null if nothing matches - filterMatched = ts.filter.processTypes( c, data, vars ); - if ( filterMatched !== null ) { - result = filterMatched; - // Look for match, and add child row data for matching + data.iExact = !regex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? + data.exact.toLowerCase() : data.exact; + + data.isMatch = c.$headerIndexed[ data.index ].hasClass( 'filter-match' ); + + result = showRow; // if showRow is true, show that row + + // in case select filter option has a different value vs text 'a - z|A through Z' + ffxn = wo.filter_columnFilters ? + c.$filters.add( c.$externalFilters ) + .filter( '[data-column="' + columnIndex + '"]' ) + .find( 'select option:selected' ) + .attr( 'data-function-name' ) || '' : ''; + // replace accents - see #357 + if ( c.sortLocaleCompare ) { + data.filter = ts.replaceAccents( data.filter ); + } + + val = true; + if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) { + data.filter = ts.filter.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); + // val is used to indicate that a filter select is using a default filter; + // so we override the exact & partial matches + val = false; + } + // data.iFilter = case insensitive ( if wo.filter_ignoreCase is true ), + // data.filter = case sensitive + data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter; + fxn = vars.functions[ columnIndex ]; + hasSelect = c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' ); + filterMatched = null; + if ( fxn || ( hasSelect && val ) ) { + if ( fxn === true || hasSelect ) { + // default selector uses exact match unless 'filter-match' class is found + filterMatched = data.isMatch ? + data.iExact.search( data.iFilter ) >= 0 : + data.filter === data.exact; + } else if ( typeof fxn === 'function' ) { + // filter callback( exact cell content, parser normalized content, + // filter input value, column index, jQuery row object ) + filterMatched = fxn( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); + } else if ( typeof fxn[ ffxn || data.filter ] === 'function' ) { + // selector option function + txt = ffxn || data.filter; + filterMatched = + fxn[ txt ]( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); + } + } + if ( filterMatched === null ) { + // cycle through the different filters + // filters return a boolean or null if nothing matches + filterMatched = ts.filter.processTypes( c, data, vars ); + if ( filterMatched !== null ) { + result = filterMatched; + // Look for match, and add child row data for matching + } else { + txt = ( data.iExact + data.childRowText ) + .indexOf( ts.filter.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) ); + result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); + } } else { - txt = ( data.iExact + data.childRowText ) - .indexOf( ts.filter.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) ); - result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); + result = filterMatched; } - } else { - result = filterMatched; + showRow = ( result ) ? showRow : false; } - showRow = ( result ) ? showRow : false; } - } - return showRow; - }, - findRows: function( table, filters, combinedFilters ) { - if ( table.config.lastCombinedFilter === combinedFilters || - !table.config.widgetOptions.filter_initialized ) { - return; - } - var len, norm_rows, rowData, $rows, rowIndex, tbodyIndex, $tbody, columnIndex, - isChild, childRow, lastSearch, showRow, time, val, indx, - notFiltered, searchFiltered, query, injected, res, id, txt, - storedFilters = $.extend( [], filters ), - regex = ts.filter.regex, - c = table.config, - wo = c.widgetOptions, - // data object passed to filters; anyMatch is a flag for the filters - data = { - anyMatch: false, - filters: filters, - // regex filter type cache - filter_regexCache : [] - }, - vars = { - // anyMatch really screws up with these types of filters - noAnyMatch: [ 'range', 'notMatch', 'operators' ], - // cache filter variables that use ts.getColumnData in the main loop - functions : [], - excludeFilter : [], - defaultColFilter : [], - defaultAnyFilter : ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '' - }; + return showRow; + }, + findRows: function( table, filters, combinedFilters ) { + if ( table.config.lastCombinedFilter === combinedFilters || + !table.config.widgetOptions.filter_initialized ) { + return; + } + var len, norm_rows, rowData, $rows, rowIndex, tbodyIndex, $tbody, columnIndex, + isChild, childRow, lastSearch, showRow, time, val, indx, + notFiltered, searchFiltered, query, injected, res, id, txt, + storedFilters = $.extend( [], filters ), + regex = ts.filter.regex, + c = table.config, + wo = c.widgetOptions, + // data object passed to filters; anyMatch is a flag for the filters + data = { + anyMatch: false, + filters: filters, + // regex filter type cache + filter_regexCache : [] + }, + vars = { + // anyMatch really screws up with these types of filters + noAnyMatch: [ 'range', 'notMatch', 'operators' ], + // cache filter variables that use ts.getColumnData in the main loop + functions : [], + excludeFilter : [], + defaultColFilter : [], + defaultAnyFilter : ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '' + }; - // parse columns after formatter, in case the class is added at that point - data.parsed = c.$headers.map( function( columnIndex ) { - return c.parsers && c.parsers[ columnIndex ] && - // force parsing if parser type is numeric - c.parsers[ columnIndex ].parsed || - // getData won't return 'parsed' if other 'filter-' class names exist - // ( e.g. <th class="filter-select filter-parsed"> ) - ts.getData && ts.getData( c.$headerIndexed[ columnIndex ], - ts.getColumnData( table, c.headers, columnIndex ), 'filter' ) === 'parsed' || - $( this ).hasClass( 'filter-parsed' ); - }).get(); - - for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { - vars.functions[ columnIndex ] = - ts.getColumnData( table, wo.filter_functions, columnIndex ); - vars.defaultColFilter[ columnIndex ] = - ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || ''; - vars.excludeFilter[ columnIndex ] = - ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split( /\s+/ ); - } + // parse columns after formatter, in case the class is added at that point + data.parsed = c.$headers.map( function( columnIndex ) { + return c.parsers && c.parsers[ columnIndex ] && + // force parsing if parser type is numeric + c.parsers[ columnIndex ].parsed || + // getData won't return 'parsed' if other 'filter-' class names exist + // ( e.g. <th class="filter-select filter-parsed"> ) + ts.getData && ts.getData( c.$headerIndexed[ columnIndex ], + ts.getColumnData( table, c.headers, columnIndex ), 'filter' ) === 'parsed' || + $( this ).hasClass( 'filter-parsed' ); + }).get(); - if ( c.debug ) { - ts.log( 'Filter: Starting filter widget search', filters ); - time = new Date(); - } - // filtered rows count - c.filteredRows = 0; - c.totalRows = 0; - // combindedFilters are undefined on init - combinedFilters = ( storedFilters || [] ).join( '' ); - - for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { - $tbody = ts.processTbody( table, c.$tbodies.eq( tbodyIndex ), true ); - // skip child rows & widget added ( removable ) rows - fixes #448 thanks to @hempel! - // $rows = $tbody.children( 'tr' ).not( c.selectorRemove ); - columnIndex = c.columns; - // convert stored rows into a jQuery object - norm_rows = c.cache[ tbodyIndex ].normalized; - $rows = $( $.map( norm_rows, function( el ) { - return el[ columnIndex ].$row.get(); - }) ); - - if ( combinedFilters === '' || wo.filter_serversideFiltering ) { - $rows - .removeClass( wo.filter_filteredRow ) - .not( '.' + c.cssChildRow ) - .css( 'display', '' ); - } else { - // filter out child rows - $rows = $rows.not( '.' + c.cssChildRow ); - len = $rows.length; - - if ( ( wo.filter_$anyMatch && wo.filter_$anyMatch.length ) || - typeof filters[c.columns] !== 'undefined' ) { - data.anyMatchFlag = true; - data.anyMatchFilter = '' + ( - filters[ c.columns ] || - wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || - '' - ); - if ( wo.filter_columnAnyMatch ) { - // specific columns search - query = data.anyMatchFilter.split( regex.andSplit ); - injected = false; - for ( indx = 0; indx < query.length; indx++ ) { - res = query[ indx ].split( ':' ); - if ( res.length > 1 ) { - // make the column a one-based index ( non-developers start counting from one :P ) - id = parseInt( res[0], 10 ) - 1; - if ( id >= 0 && id < c.columns ) { // if id is an integer - filters[ id ] = res[1]; - query.splice( indx, 1 ); - indx--; - injected = true; + for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { + vars.functions[ columnIndex ] = + ts.getColumnData( table, wo.filter_functions, columnIndex ); + vars.defaultColFilter[ columnIndex ] = + ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || ''; + vars.excludeFilter[ columnIndex ] = + ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split( /\s+/ ); + } + + if ( c.debug ) { + console.log( 'Filter: Starting filter widget search', filters ); + time = new Date(); + } + // filtered rows count + c.filteredRows = 0; + c.totalRows = 0; + // combindedFilters are undefined on init + combinedFilters = ( storedFilters || [] ).join( '' ); + + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody( table, c.$tbodies.eq( tbodyIndex ), true ); + // skip child rows & widget added ( removable ) rows - fixes #448 thanks to @hempel! + // $rows = $tbody.children( 'tr' ).not( c.selectorRemove ); + columnIndex = c.columns; + // convert stored rows into a jQuery object + norm_rows = c.cache[ tbodyIndex ].normalized; + $rows = $( $.map( norm_rows, function( el ) { + return el[ columnIndex ].$row.get(); + }) ); + + if ( combinedFilters === '' || wo.filter_serversideFiltering ) { + $rows + .removeClass( wo.filter_filteredRow ) + .not( '.' + c.cssChildRow ) + .css( 'display', '' ); + } else { + // filter out child rows + $rows = $rows.not( '.' + c.cssChildRow ); + len = $rows.length; + + if ( ( wo.filter_$anyMatch && wo.filter_$anyMatch.length ) || + typeof filters[c.columns] !== 'undefined' ) { + data.anyMatchFlag = true; + data.anyMatchFilter = '' + ( + filters[ c.columns ] || + wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || + '' + ); + if ( wo.filter_columnAnyMatch ) { + // specific columns search + query = data.anyMatchFilter.split( regex.andSplit ); + injected = false; + for ( indx = 0; indx < query.length; indx++ ) { + res = query[ indx ].split( ':' ); + if ( res.length > 1 ) { + // make the column a one-based index ( non-developers start counting from one :P ) + id = parseInt( res[0], 10 ) - 1; + if ( id >= 0 && id < c.columns ) { // if id is an integer + filters[ id ] = res[1]; + query.splice( indx, 1 ); + indx--; + injected = true; + } } } - } - if ( injected ) { - data.anyMatchFilter = query.join( ' && ' ); + if ( injected ) { + data.anyMatchFilter = query.join( ' && ' ); + } } } - } - // optimize searching only through already filtered rows - see #313 - searchFiltered = wo.filter_searchFiltered; - lastSearch = c.lastSearch || c.$table.data( 'lastSearch' ) || []; - if ( searchFiltered ) { - // cycle through all filters; include last ( columnIndex + 1 = match any column ). Fixes #669 - for ( indx = 0; indx < columnIndex + 1; indx++ ) { - val = filters[indx] || ''; - // break out of loop if we've already determined not to search filtered rows - if ( !searchFiltered ) { indx = columnIndex; } - // search already filtered rows if... - searchFiltered = searchFiltered && lastSearch.length && - // there are no changes from beginning of filter - val.indexOf( lastSearch[indx] || '' ) === 0 && - // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string - !regex.alreadyFiltered.test( val ) && - // if we are not doing exact matches, using '|' ( logical or ) or not '!' - !/[=\"\|!]/.test( val ) && - // don't search only filtered if the value is negative - // ( '> -10' => '> -100' will ignore hidden rows ) - !( /(>=?\s*-\d)/.test( val ) || /(<=?\s*\d)/.test( val ) ) && - // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 - !( val !== '' && c.$filters && c.$filters.eq( indx ).find( 'select' ).length && - !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); + // optimize searching only through already filtered rows - see #313 + searchFiltered = wo.filter_searchFiltered; + lastSearch = c.lastSearch || c.$table.data( 'lastSearch' ) || []; + if ( searchFiltered ) { + // cycle through all filters; include last ( columnIndex + 1 = match any column ). Fixes #669 + for ( indx = 0; indx < columnIndex + 1; indx++ ) { + val = filters[indx] || ''; + // break out of loop if we've already determined not to search filtered rows + if ( !searchFiltered ) { indx = columnIndex; } + // search already filtered rows if... + searchFiltered = searchFiltered && lastSearch.length && + // there are no changes from beginning of filter + val.indexOf( lastSearch[indx] || '' ) === 0 && + // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string + !regex.alreadyFiltered.test( val ) && + // if we are not doing exact matches, using '|' ( logical or ) or not '!' + !/[=\"\|!]/.test( val ) && + // don't search only filtered if the value is negative + // ( '> -10' => '> -100' will ignore hidden rows ) + !( /(>=?\s*-\d)/.test( val ) || /(<=?\s*\d)/.test( val ) ) && + // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 + !( val !== '' && c.$filters && c.$filters.eq( indx ).find( 'select' ).length && + !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); + } } - } - notFiltered = $rows.not( '.' + wo.filter_filteredRow ).length; - // can't search when all rows are hidden - this happens when looking for exact matches - if ( searchFiltered && notFiltered === 0 ) { searchFiltered = false; } - if ( c.debug ) { - ts.log( 'Filter: Searching through ' + - ( searchFiltered && notFiltered < len ? notFiltered : 'all' ) + ' rows' ); - } - if ( data.anyMatchFlag ) { - if ( c.sortLocaleCompare ) { - // replace accents - data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter ); + notFiltered = $rows.not( '.' + wo.filter_filteredRow ).length; + // can't search when all rows are hidden - this happens when looking for exact matches + if ( searchFiltered && notFiltered === 0 ) { searchFiltered = false; } + if ( c.debug ) { + console.log( 'Filter: Searching through ' + + ( searchFiltered && notFiltered < len ? notFiltered : 'all' ) + ' rows' ); } - if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultAnyFilter ) ) { - data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter ); - // clear search filtered flag because default filters are not saved to the last search - searchFiltered = false; + if ( data.anyMatchFlag ) { + if ( c.sortLocaleCompare ) { + // replace accents + data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter ); + } + if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultAnyFilter ) ) { + data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter ); + // clear search filtered flag because default filters are not saved to the last search + searchFiltered = false; + } + // make iAnyMatchFilter lowercase unless both filter widget & core ignoreCase options are true + // when c.ignoreCase is true, the cache contains all lower case data + data.iAnyMatchFilter = !( wo.filter_ignoreCase && c.ignoreCase ) ? + data.anyMatchFilter : + data.anyMatchFilter.toLowerCase(); } - // make iAnyMatchFilter lowercase unless both filter widget & core ignoreCase options are true - // when c.ignoreCase is true, the cache contains all lower case data - data.iAnyMatchFilter = !( wo.filter_ignoreCase && c.ignoreCase ) ? - data.anyMatchFilter : - data.anyMatchFilter.toLowerCase(); - } - // loop through the rows - for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { - - txt = $rows[ rowIndex ].className; - // the first row can never be a child row - isChild = rowIndex && regex.child.test( txt ); - // skip child rows & already filtered rows - if ( isChild || ( searchFiltered && regex.filtered.test( txt ) ) ) { - continue; - } + // loop through the rows + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { - data.$row = $rows.eq( rowIndex ); - data.cacheArray = norm_rows[ rowIndex ]; - rowData = data.cacheArray[ c.columns ]; - data.rawArray = rowData.raw; - data.childRowText = ''; - - if ( !wo.filter_childByColumn ) { - txt = ''; - // child row cached text - childRow = rowData.child; - // so, if 'table.config.widgetOptions.filter_childRows' is true and there is - // a match anywhere in the child row, then it will make the row visible - // checked here so the option can be changed dynamically - for ( indx = 0; indx < childRow.length; indx++ ) { - txt += ' ' + childRow[indx].join( '' ) || ''; + txt = $rows[ rowIndex ].className; + // the first row can never be a child row + isChild = rowIndex && regex.child.test( txt ); + // skip child rows & already filtered rows + if ( isChild || ( searchFiltered && regex.filtered.test( txt ) ) ) { + continue; } - data.childRowText = wo.filter_childRows ? - ( wo.filter_ignoreCase ? txt.toLowerCase() : txt ) : - ''; - } - showRow = ts.filter.processRow( c, data, vars ); - childRow = rowData.$row.filter( ':gt( 0 )' ); - - if ( wo.filter_childRows && childRow.length ) { - if ( wo.filter_childByColumn ) { - // cycle through each child row + data.$row = $rows.eq( rowIndex ); + data.cacheArray = norm_rows[ rowIndex ]; + rowData = data.cacheArray[ c.columns ]; + data.rawArray = rowData.raw; + data.childRowText = ''; + + if ( !wo.filter_childByColumn ) { + txt = ''; + // child row cached text + childRow = rowData.child; + // so, if 'table.config.widgetOptions.filter_childRows' is true and there is + // a match anywhere in the child row, then it will make the row visible + // checked here so the option can be changed dynamically for ( indx = 0; indx < childRow.length; indx++ ) { - data.$row = childRow.eq( indx ); - data.cacheArray = rowData.child[ indx ]; - data.rawArray = data.cacheArray; - // use OR comparison on child rows - showRow = showRow || ts.filter.processRow( c, data, vars ); + txt += ' ' + childRow[indx].join( '' ) || ''; } + data.childRowText = wo.filter_childRows ? + ( wo.filter_ignoreCase ? txt.toLowerCase() : txt ) : + ''; + } + + showRow = ts.filter.processRow( c, data, vars ); + childRow = rowData.$row.filter( ':gt( 0 )' ); + + if ( wo.filter_childRows && childRow.length ) { + if ( wo.filter_childByColumn ) { + // cycle through each child row + for ( indx = 0; indx < childRow.length; indx++ ) { + data.$row = childRow.eq( indx ); + data.cacheArray = rowData.child[ indx ]; + data.rawArray = data.cacheArray; + // use OR comparison on child rows + showRow = showRow || ts.filter.processRow( c, data, vars ); + } + } + childRow.toggleClass( wo.filter_filteredRow, !showRow ); } - childRow.toggleClass( wo.filter_filteredRow, !showRow ); - } - rowData.$row - .toggleClass( wo.filter_filteredRow, !showRow )[0] - .display = showRow ? '' : 'none'; + rowData.$row + .toggleClass( wo.filter_filteredRow, !showRow )[0] + .display = showRow ? '' : 'none'; + } } + c.filteredRows += $rows.not( '.' + wo.filter_filteredRow ).length; + c.totalRows += $rows.length; + ts.processTbody( table, $tbody, false ); + } + c.lastCombinedFilter = combinedFilters; // save last search + // don't save 'filters' directly since it may have altered ( AnyMatch column searches ) + c.lastSearch = storedFilters; + c.$table.data( 'lastSearch', storedFilters ); + if ( wo.filter_saveFilters && ts.storage ) { + ts.storage( table, 'tablesorter-filters', storedFilters ); + } + if ( c.debug ) { + console.log( 'Completed filter widget search' + ts.benchmark(time) ); + } + if ( wo.filter_initialized ) { + c.$table.trigger( 'filterEnd', c ); + } + setTimeout( function() { + c.$table.trigger( 'applyWidgets' ); // make sure zebra widget is applied + }, 0 ); + }, + getOptionSource: function( table, column, onlyAvail ) { + table = $( table )[0]; + var cts, txt, indx, len, + c = table.config, + wo = c.widgetOptions, + parsed = [], + arry = false, + source = wo.filter_selectSource, + last = c.$table.data( 'lastSearch' ) || [], + fxn = $.isFunction( source ) ? true : ts.getColumnData( table, source, column ); + + if ( onlyAvail && last[column] !== '' ) { + onlyAvail = false; } - c.filteredRows += $rows.not( '.' + wo.filter_filteredRow ).length; - c.totalRows += $rows.length; - ts.processTbody( table, $tbody, false ); - } - c.lastCombinedFilter = combinedFilters; // save last search - // don't save 'filters' directly since it may have altered ( AnyMatch column searches ) - c.lastSearch = storedFilters; - c.$table.data( 'lastSearch', storedFilters ); - if ( wo.filter_saveFilters && ts.storage ) { - ts.storage( table, 'tablesorter-filters', storedFilters ); - } - if ( c.debug ) { - ts.benchmark( 'Completed filter widget search', time ); - } - if ( wo.filter_initialized ) { - c.$table.trigger( 'filterEnd', c ); - } - setTimeout( function() { - c.$table.trigger( 'applyWidgets' ); // make sure zebra widget is applied - }, 0 ); - }, - getOptionSource: function( table, column, onlyAvail ) { - table = $( table )[0]; - var cts, txt, indx, len, - c = table.config, - wo = c.widgetOptions, - parsed = [], - arry = false, - source = wo.filter_selectSource, - last = c.$table.data( 'lastSearch' ) || [], - fxn = $.isFunction( source ) ? true : ts.getColumnData( table, source, column ); - - if ( onlyAvail && last[column] !== '' ) { - onlyAvail = false; - } - // filter select source option - if ( fxn === true ) { - // OVERALL source - arry = source( table, column, onlyAvail ); - } else if ( fxn instanceof $ || ( $.type( fxn ) === 'string' && fxn.indexOf( '</option>' ) >= 0 ) ) { - // selectSource is a jQuery object or string of options - return fxn; - } else if ( $.isArray( fxn ) ) { - arry = fxn; - } else if ( $.type( source ) === 'object' && fxn ) { - // custom select source function for a SPECIFIC COLUMN - arry = fxn( table, column, onlyAvail ); - } - if ( arry === false ) { - // fall back to original method - arry = ts.filter.getOptions( table, column, onlyAvail ); - } + // filter select source option + if ( fxn === true ) { + // OVERALL source + arry = source( table, column, onlyAvail ); + } else if ( fxn instanceof $ || ( $.type( fxn ) === 'string' && fxn.indexOf( '</option>' ) >= 0 ) ) { + // selectSource is a jQuery object or string of options + return fxn; + } else if ( $.isArray( fxn ) ) { + arry = fxn; + } else if ( $.type( source ) === 'object' && fxn ) { + // custom select source function for a SPECIFIC COLUMN + arry = fxn( table, column, onlyAvail ); + } + if ( arry === false ) { + // fall back to original method + arry = ts.filter.getOptions( table, column, onlyAvail ); + } - // get unique elements and sort the list - // if $.tablesorter.sortText exists ( not in the original tablesorter ), - // then natural sort the list otherwise use a basic sort - arry = $.grep( arry, function( value, indx ) { - return $.inArray( value, arry ) === indx; - }); + // get unique elements and sort the list + // if $.tablesorter.sortText exists ( not in the original tablesorter ), + // then natural sort the list otherwise use a basic sort + arry = $.grep( arry, function( value, indx ) { + return $.inArray( value, arry ) === indx; + }); - if ( c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) { - // unsorted select options - return arry; - } else { - len = arry.length; - // parse select option values - for ( indx = 0; indx < len; indx++ ) { - txt = arry[ indx ]; - // parse array data using set column parser; this DOES NOT pass the original - // table cell to the parser format function - parsed.push({ - t : txt, - // check parser length - fixes #934 - p : c.parsers && c.parsers.length && c.parsers[ column ].format( txt, table, [], column ) || txt + if ( c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) { + // unsorted select options + return arry; + } else { + len = arry.length; + // parse select option values + for ( indx = 0; indx < len; indx++ ) { + txt = arry[ indx ]; + // parse array data using set column parser; this DOES NOT pass the original + // table cell to the parser format function + parsed.push({ + t : txt, + // check parser length - fixes #934 + p : c.parsers && c.parsers.length && c.parsers[ column ].format( txt, table, [], column ) || txt + }); + } + + // sort parsed select options + cts = c.textSorter || ''; + parsed.sort( function( a, b ) { + // sortNatural breaks if you don't pass it strings + var x = a.p.toString(), + y = b.p.toString(); + if ( $.isFunction( cts ) ) { + // custom OVERALL text sorter + return cts( x, y, true, column, table ); + } else if ( typeof cts === 'object' && cts.hasOwnProperty( column ) ) { + // custom text sorter for a SPECIFIC COLUMN + return cts[column]( x, y, true, column, table ); + } else if ( ts.sortNatural ) { + // fall back to natural sort + return ts.sortNatural( x, y ); + } + // using an older version! do a basic sort + return true; }); + // rebuild arry from sorted parsed data + arry = []; + len = parsed.length; + for ( indx = 0; indx < len; indx++ ) { + arry.push( parsed[indx].t ); + } + return arry; } - - // sort parsed select options - cts = c.textSorter || ''; - parsed.sort( function( a, b ) { - // sortNatural breaks if you don't pass it strings - var x = a.p.toString(), - y = b.p.toString(); - if ( $.isFunction( cts ) ) { - // custom OVERALL text sorter - return cts( x, y, true, column, table ); - } else if ( typeof( cts ) === 'object' && cts.hasOwnProperty( column ) ) { - // custom text sorter for a SPECIFIC COLUMN - return cts[column]( x, y, true, column, table ); - } else if ( ts.sortNatural ) { - // fall back to natural sort - return ts.sortNatural( x, y ); - } - // using an older version! do a basic sort - return true; - }); - // rebuild arry from sorted parsed data - arry = []; - len = parsed.length; - for ( indx = 0; indx < len; indx++ ) { - arry.push( parsed[indx].t ); + }, + getOptions: function( table, column, onlyAvail ) { + table = $( table )[0]; + var rowIndex, tbodyIndex, len, row, cache, + c = table.config, + wo = c.widgetOptions, + arry = []; + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + cache = c.cache[tbodyIndex]; + len = c.cache[tbodyIndex].normalized.length; + // loop through the rows + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { + // get cached row from cache.row ( old ) or row data object + // ( new; last item in normalized array ) + row = cache.row ? + cache.row[ rowIndex ] : + cache.normalized[ rowIndex ][ c.columns ].$row[0]; + // check if has class filtered + if ( onlyAvail && row.className.match( wo.filter_filteredRow ) ) { + continue; + } + // get non-normalized cell content + if ( wo.filter_useParsedData || + c.parsers[column].parsed || + c.$headerIndexed[column].hasClass( 'filter-parsed' ) ) { + arry.push( '' + cache.normalized[ rowIndex ][ column ] ); + } else { + // get raw cached data instead of content directly from the cells + arry.push( cache.normalized[ rowIndex ][ c.columns ].raw[ column ] ); + } + } } return arry; - } - }, - getOptions: function( table, column, onlyAvail ) { - table = $( table )[0]; - var rowIndex, tbodyIndex, len, row, cache, - c = table.config, - wo = c.widgetOptions, - arry = []; - for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { - cache = c.cache[tbodyIndex]; - len = c.cache[tbodyIndex].normalized.length; - // loop through the rows - for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { - // get cached row from cache.row ( old ) or row data object - // ( new; last item in normalized array ) - row = cache.row ? - cache.row[ rowIndex ] : - cache.normalized[ rowIndex ][ c.columns ].$row[0]; - // check if has class filtered - if ( onlyAvail && row.className.match( wo.filter_filteredRow ) ) { - continue; - } - // get non-normalized cell content - if ( wo.filter_useParsedData || - c.parsers[column].parsed || - c.$headerIndexed[column].hasClass( 'filter-parsed' ) ) { - arry.push( '' + cache.normalized[ rowIndex ][ column ] ); - } else { - // get raw cached data instead of content directly from the cells - arry.push( cache.normalized[ rowIndex ][ c.columns ].raw[ column ] ); - } + }, + buildSelect: function( table, column, arry, updating, onlyAvail ) { + table = $( table )[0]; + column = parseInt( column, 10 ); + if ( !table.config.cache || $.isEmptyObject( table.config.cache ) ) { + return; + } + var indx, val, txt, t, $filters, $filter, + c = table.config, + wo = c.widgetOptions, + node = c.$headerIndexed[ column ], + // t.data( 'placeholder' ) won't work in jQuery older than 1.4.3 + options = '<option value="">' + + ( node.data( 'placeholder' ) || + node.attr( 'data-placeholder' ) || + wo.filter_placeholder.select || '' + ) + '</option>', + // Get curent filter value + currentValue = c.$table + .find( 'thead' ) + .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) + .val(); + // nothing included in arry ( external source ), so get the options from + // filter_selectSource or column data + if ( typeof arry === 'undefined' || arry === '' ) { + arry = ts.filter.getOptionSource( table, column, onlyAvail ); } - } - return arry; - }, - buildSelect: function( table, column, arry, updating, onlyAvail ) { - table = $( table )[0]; - column = parseInt( column, 10 ); - if ( !table.config.cache || $.isEmptyObject( table.config.cache ) ) { - return; - } - var indx, val, txt, t, $filters, $filter, - c = table.config, - wo = c.widgetOptions, - node = c.$headerIndexed[ column ], - // t.data( 'placeholder' ) won't work in jQuery older than 1.4.3 - options = '<option value="">' + - ( node.data( 'placeholder' ) || - node.attr( 'data-placeholder' ) || - wo.filter_placeholder.select || '' - ) + '</option>', - // Get curent filter value - currentValue = c.$table - .find( 'thead' ) - .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) - .val(); - // nothing included in arry ( external source ), so get the options from - // filter_selectSource or column data - if ( typeof arry === 'undefined' || arry === '' ) { - arry = ts.filter.getOptionSource( table, column, onlyAvail ); - } - if ( $.isArray( arry ) ) { - // build option list - for ( indx = 0; indx < arry.length; indx++ ) { - txt = arry[indx] = ( '' + arry[indx] ).replace( /\"/g, '"' ); - val = txt; - // allow including a symbol in the selectSource array - // 'a-z|A through Z' so that 'a-z' becomes the option value - // and 'A through Z' becomes the option text - if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { - t = txt.split( wo.filter_selectSourceSeparator ); - val = t[0]; - txt = t[1]; - } - // replace quotes - fixes #242 & ignore empty strings - // see http://stackoverflow.com/q/14990971/145346 - options += arry[indx] !== '' ? - '<option ' + - ( val === txt ? '' : 'data-function-name="' + arry[indx] + '" ' ) + - 'value="' + val + '">' + txt + - '</option>' : ''; + if ( $.isArray( arry ) ) { + // build option list + for ( indx = 0; indx < arry.length; indx++ ) { + txt = arry[indx] = ( '' + arry[indx] ).replace( /\"/g, '"' ); + val = txt; + // allow including a symbol in the selectSource array + // 'a-z|A through Z' so that 'a-z' becomes the option value + // and 'A through Z' becomes the option text + if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { + t = txt.split( wo.filter_selectSourceSeparator ); + val = t[0]; + txt = t[1]; + } + // replace quotes - fixes #242 & ignore empty strings + // see http://stackoverflow.com/q/14990971/145346 + options += arry[indx] !== '' ? + '<option ' + + ( val === txt ? '' : 'data-function-name="' + arry[indx] + '" ' ) + + 'value="' + val + '">' + txt + + '</option>' : ''; + } + // clear arry so it doesn't get appended twice + arry = []; } - // clear arry so it doesn't get appended twice - arry = []; - } - // update all selects in the same column ( clone thead in sticky headers & - // any external selects ) - fixes 473 - $filters = ( c.$filters ? c.$filters : c.$table.children( 'thead' ) ) - .find( '.' + tscss.filter ); - if ( wo.filter_$externalFilters ) { - $filters = $filters && $filters.length ? - $filters.add( wo.filter_$externalFilters ) : - wo.filter_$externalFilters; - } - $filter = $filters.filter( 'select[data-column="' + column + '"]' ); - - // make sure there is a select there! - if ( $filter.length ) { - $filter[ updating ? 'html' : 'append' ]( options ); - if ( !$.isArray( arry ) ) { - // append options if arry is provided externally as a string or jQuery object - // options ( default value ) was already added - $filter.append( arry ).val( currentValue ); + // update all selects in the same column ( clone thead in sticky headers & + // any external selects ) - fixes 473 + $filters = ( c.$filters ? c.$filters : c.$table.children( 'thead' ) ) + .find( '.' + tscss.filter ); + if ( wo.filter_$externalFilters ) { + $filters = $filters && $filters.length ? + $filters.add( wo.filter_$externalFilters ) : + wo.filter_$externalFilters; } - $filter.val( currentValue ); - } - }, - buildDefault: function( table, updating ) { - var columnIndex, $header, noSelect, - c = table.config, - wo = c.widgetOptions, - columns = c.columns; - // build default select dropdown - for ( columnIndex = 0; columnIndex < columns; columnIndex++ ) { - $header = c.$headerIndexed[columnIndex]; - noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); - // look for the filter-select class; build/update it if found - if ( ( $header.hasClass( 'filter-select' ) || - ts.getColumnData( table, wo.filter_functions, columnIndex ) === true ) && noSelect ) { - ts.filter.buildSelect( table, columnIndex, '', updating, $header.hasClass( wo.filter_onlyAvail ) ); + $filter = $filters.filter( 'select[data-column="' + column + '"]' ); + + // make sure there is a select there! + if ( $filter.length ) { + $filter[ updating ? 'html' : 'append' ]( options ); + if ( !$.isArray( arry ) ) { + // append options if arry is provided externally as a string or jQuery object + // options ( default value ) was already added + $filter.append( arry ).val( currentValue ); + } + $filter.val( currentValue ); + } + }, + buildDefault: function( table, updating ) { + var columnIndex, $header, noSelect, + c = table.config, + wo = c.widgetOptions, + columns = c.columns; + // build default select dropdown + for ( columnIndex = 0; columnIndex < columns; columnIndex++ ) { + $header = c.$headerIndexed[columnIndex]; + noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); + // look for the filter-select class; build/update it if found + if ( ( $header.hasClass( 'filter-select' ) || + ts.getColumnData( table, wo.filter_functions, columnIndex ) === true ) && noSelect ) { + ts.filter.buildSelect( table, columnIndex, '', updating, $header.hasClass( wo.filter_onlyAvail ) ); + } } } - } -}; - -ts.getFilters = function( table, getRaw, setFilters, skipFirst ) { - var i, $filters, $column, cols, - filters = false, - c = table ? $( table )[0].config : '', - wo = c ? c.widgetOptions : ''; - if ( ( getRaw !== true && wo && !wo.filter_columnFilters ) || - // setFilters called, but last search is exactly the same as the current - // fixes issue #733 & #903 where calling update causes the input values to reset - ( $.isArray(setFilters) && setFilters.join('') === c.lastCombinedFilter ) ) { - return $( table ).data( 'lastSearch' ); - } - if ( c ) { - if ( c.$filters ) { - $filters = c.$filters.find( '.' + tscss.filter ); - } - if ( wo.filter_$externalFilters ) { - $filters = $filters && $filters.length ? - $filters.add( wo.filter_$externalFilters ) : - wo.filter_$externalFilters; + }; + + ts.getFilters = function( table, getRaw, setFilters, skipFirst ) { + var i, $filters, $column, cols, + filters = false, + c = table ? $( table )[0].config : '', + wo = c ? c.widgetOptions : ''; + if ( ( getRaw !== true && wo && !wo.filter_columnFilters ) || + // setFilters called, but last search is exactly the same as the current + // fixes issue #733 & #903 where calling update causes the input values to reset + ( $.isArray(setFilters) && setFilters.join('') === c.lastCombinedFilter ) ) { + return $( table ).data( 'lastSearch' ); } - if ( $filters && $filters.length ) { - filters = setFilters || []; - for ( i = 0; i < c.columns + 1; i++ ) { - cols = ( i === c.columns ? - // 'all' columns can now include a range or set of columms ( data-column='0-2,4,6-7' ) - wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector : - '[data-column="' + i + '"]' ); - $column = $filters.filter( cols ); - if ( $column.length ) { - // move the latest search to the first slot in the array - $column = ts.filter.getLatestSearch( $column ); - if ( $.isArray( setFilters ) ) { - // skip first ( latest input ) to maintain cursor position while typing - if ( skipFirst && $column.length > 1 ) { - $column = $column.slice( 1 ); - } - if ( i === c.columns ) { - // prevent data-column='all' from filling data-column='0,1' ( etc ) - cols = $column.filter( wo.filter_anyColumnSelector ); - $column = cols.length ? cols : $column; - } - $column - .val( setFilters[ i ] ) - .trigger( 'change.tsfilter' ); - } else { - filters[i] = $column.val() || ''; - // don't change the first... it will move the cursor - if ( i === c.columns ) { - // don't update range columns from 'all' setting + if ( c ) { + if ( c.$filters ) { + $filters = c.$filters.find( '.' + tscss.filter ); + } + if ( wo.filter_$externalFilters ) { + $filters = $filters && $filters.length ? + $filters.add( wo.filter_$externalFilters ) : + wo.filter_$externalFilters; + } + if ( $filters && $filters.length ) { + filters = setFilters || []; + for ( i = 0; i < c.columns + 1; i++ ) { + cols = ( i === c.columns ? + // 'all' columns can now include a range or set of columms ( data-column='0-2,4,6-7' ) + wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector : + '[data-column="' + i + '"]' ); + $column = $filters.filter( cols ); + if ( $column.length ) { + // move the latest search to the first slot in the array + $column = ts.filter.getLatestSearch( $column ); + if ( $.isArray( setFilters ) ) { + // skip first ( latest input ) to maintain cursor position while typing + if ( skipFirst && $column.length > 1 ) { + $column = $column.slice( 1 ); + } + if ( i === c.columns ) { + // prevent data-column='all' from filling data-column='0,1' ( etc ) + cols = $column.filter( wo.filter_anyColumnSelector ); + $column = cols.length ? cols : $column; + } $column - .slice( 1 ) - .filter( '[data-column*="' + $column.attr( 'data-column' ) + '"]' ) - .val( filters[ i ] ); + .val( setFilters[ i ] ) + .trigger( 'change.tsfilter' ); } else { - $column - .slice( 1 ) - .val( filters[ i ] ); + filters[i] = $column.val() || ''; + // don't change the first... it will move the cursor + if ( i === c.columns ) { + // don't update range columns from 'all' setting + $column + .slice( 1 ) + .filter( '[data-column*="' + $column.attr( 'data-column' ) + '"]' ) + .val( filters[ i ] ); + } else { + $column + .slice( 1 ) + .val( filters[ i ] ); + } + } + // save any match input dynamically + if ( i === c.columns && $column.length ) { + wo.filter_$anyMatch = $column; } - } - // save any match input dynamically - if ( i === c.columns && $column.length ) { - wo.filter_$anyMatch = $column; } } } } - } - if ( filters.length === 0 ) { - filters = false; - } - return filters; -}; - -ts.setFilters = function( table, filter, apply, skipFirst ) { - var c = table ? $( table )[0].config : '', - valid = ts.getFilters( table, true, filter, skipFirst ); - if ( c && apply ) { - // ensure new set filters are applied, even if the search is the same - c.lastCombinedFilter = null; - c.lastSearch = []; - ts.filter.searching( c.table, filter, skipFirst ); - c.$table.trigger( 'filterFomatterUpdate' ); - } - return !!valid; -}; + if ( filters.length === 0 ) { + filters = false; + } + return filters; + }; + + ts.setFilters = function( table, filter, apply, skipFirst ) { + var c = table ? $( table )[0].config : '', + valid = ts.getFilters( table, true, filter, skipFirst ); + if ( c && apply ) { + // ensure new set filters are applied, even if the search is the same + c.lastCombinedFilter = null; + c.lastSearch = []; + ts.filter.searching( c.table, filter, skipFirst ); + c.$table.trigger( 'filterFomatterUpdate' ); + } + return !!valid; + }; })( jQuery ); @@ -4163,750 +4233,751 @@ ts.setFilters = function( table, filter, apply, skipFirst ) { * by Rob Garrison */ ;(function ($, window) { -'use strict'; -var ts = $.tablesorter || {}; - -$.extend(ts.css, { - sticky : 'tablesorter-stickyHeader', // stickyHeader - stickyVis : 'tablesorter-sticky-visible', - stickyHide: 'tablesorter-sticky-hidden', - stickyWrap: 'tablesorter-sticky-wrapper' -}); - -// Add a resize event to table headers -ts.addHeaderResizeEvent = function(table, disable, settings) { - table = $(table)[0]; // make sure we're using a dom element - if ( !table.config ) { return; } - var defaults = { - timer : 250 - }, - options = $.extend({}, defaults, settings), - c = table.config, - wo = c.widgetOptions, - checkSizes = function( triggerEvent ) { - var index, headers, $header, sizes, width, height, - len = c.$headers.length; - wo.resize_flag = true; - headers = []; - for ( index = 0; index < len; index++ ) { - $header = c.$headers.eq( index ); - sizes = $header.data( 'savedSizes' ) || [ 0,0 ]; // fixes #394 - width = $header[0].offsetWidth; - height = $header[0].offsetHeight; - if ( width !== sizes[0] || height !== sizes[1] ) { - $header.data( 'savedSizes', [ width, height ] ); - headers.push( $header[0] ); + 'use strict'; + var ts = $.tablesorter || {}; + + $.extend(ts.css, { + sticky : 'tablesorter-stickyHeader', // stickyHeader + stickyVis : 'tablesorter-sticky-visible', + stickyHide: 'tablesorter-sticky-hidden', + stickyWrap: 'tablesorter-sticky-wrapper' + }); + + // Add a resize event to table headers + ts.addHeaderResizeEvent = function(table, disable, settings) { + table = $(table)[0]; // make sure we're using a dom element + if ( !table.config ) { return; } + var defaults = { + timer : 250 + }, + options = $.extend({}, defaults, settings), + c = table.config, + wo = c.widgetOptions, + checkSizes = function( triggerEvent ) { + var index, headers, $header, sizes, width, height, + len = c.$headers.length; + wo.resize_flag = true; + headers = []; + for ( index = 0; index < len; index++ ) { + $header = c.$headers.eq( index ); + sizes = $header.data( 'savedSizes' ) || [ 0, 0 ]; // fixes #394 + width = $header[0].offsetWidth; + height = $header[0].offsetHeight; + if ( width !== sizes[0] || height !== sizes[1] ) { + $header.data( 'savedSizes', [ width, height ] ); + headers.push( $header[0] ); + } } - } - if ( headers.length && triggerEvent !== false ) { - c.$table.trigger( 'resize', [ headers ] ); - } + if ( headers.length && triggerEvent !== false ) { + c.$table.trigger( 'resize', [ headers ] ); + } + wo.resize_flag = false; + }; + checkSizes( false ); + clearInterval(wo.resize_timer); + if (disable) { wo.resize_flag = false; - }; - checkSizes( false ); - clearInterval(wo.resize_timer); - if (disable) { - wo.resize_flag = false; - return false; - } - wo.resize_timer = setInterval(function() { - if (wo.resize_flag) { return; } - checkSizes(); - }, options.timer); -}; - -// Sticky headers based on this awesome article: -// http://css-tricks.com/13465-persistent-headers/ -// and https://github.com/jmosbech/StickyTableHeaders by Jonas Mosbech -// ************************** -ts.addWidget({ - id: "stickyHeaders", - priority: 60, // sticky widget must be initialized after the filter widget! - options: { - stickyHeaders : '', // extra class name added to the sticky header row - stickyHeaders_attachTo : null, // jQuery selector or object to attach sticky header to - stickyHeaders_xScroll : null, // jQuery selector or object to monitor horizontal scroll position (defaults: xScroll > attachTo > window) - stickyHeaders_yScroll : null, // jQuery selector or object to monitor vertical scroll position (defaults: yScroll > attachTo > window) - stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element - stickyHeaders_filteredToTop: true, // scroll table top into view after filtering - stickyHeaders_cloneId : '-sticky', // added to table ID, if it exists - stickyHeaders_addResizeEvent : true, // trigger "resize" event on headers - stickyHeaders_includeCaption : true, // if false and a caption exist, it won't be included in the sticky header - stickyHeaders_zIndex : 2 // The zIndex of the stickyHeaders, allows the user to adjust this to their needs - }, - format: function(table, c, wo) { - // filter widget doesn't initialize on an empty table. Fixes #449 - if ( c.$table.hasClass('hasStickyHeaders') || ($.inArray('filter', c.widgets) >= 0 && !c.$table.hasClass('hasFilters')) ) { - return; + return false; } - var index, len, $t, - $table = c.$table, - // add position: relative to attach element, hopefully it won't cause trouble. - $attach = $(wo.stickyHeaders_attachTo), - namespace = c.namespace + 'stickyheaders ', - // element to watch for the scroll event - $yScroll = $(wo.stickyHeaders_yScroll || wo.stickyHeaders_attachTo || window), - $xScroll = $(wo.stickyHeaders_xScroll || wo.stickyHeaders_attachTo || window), - $thead = $table.children('thead:first'), - $header = $thead.children('tr').not('.sticky-false').children(), - $tfoot = $table.children('tfoot'), - $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '', - stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, - // is this table nested? If so, find parent sticky header wrapper (div, not table) - $nestedSticky = $table.parent().closest('.' + ts.css.table).hasClass('hasStickyHeaders') ? - $table.parent().closest('table.tablesorter')[0].config.widgetOptions.$sticky.parent() : [], - nestedStickyTop = $nestedSticky.length ? $nestedSticky.height() : 0, - // clone table, then wrap to make sticky header - $stickyTable = wo.$sticky = $table.clone() - .addClass('containsStickyHeaders ' + ts.css.sticky + ' ' + wo.stickyHeaders + ' ' + c.namespace.slice(1) + '_extra_table' ) - .wrap('<div class="' + ts.css.stickyWrap + '">'), - $stickyWrap = $stickyTable.parent() - .addClass(ts.css.stickyHide) - .css({ - position : $attach.length ? 'absolute' : 'fixed', - padding : parseInt( $stickyTable.parent().parent().css('padding-left'), 10 ), - top : stickyOffset + nestedStickyTop, - left : 0, - visibility : 'hidden', - zIndex : wo.stickyHeaders_zIndex || 2 - }), - $stickyThead = $stickyTable.children('thead:first'), - $stickyCells, - laststate = '', - spacing = 0, - setWidth = function($orig, $clone){ - var index, width, border, $cell, $this, - $cells = $orig.filter(':visible'), - len = $cells.length; - for ( index = 0; index < len; index++ ) { - $cell = $clone.filter(':visible').eq(index); - $this = $cells.eq(index); - // code from https://github.com/jmosbech/StickyTableHeaders - if ($this.css('box-sizing') === 'border-box') { - width = $this.outerWidth(); - } else { - if ($cell.css('border-collapse') === 'collapse') { - if (window.getComputedStyle) { - width = parseFloat( window.getComputedStyle($this[0], null).width ); + wo.resize_timer = setInterval(function() { + if (wo.resize_flag) { return; } + checkSizes(); + }, options.timer); + }; + + // Sticky headers based on this awesome article: + // http://css-tricks.com/13465-persistent-headers/ + // and https://github.com/jmosbech/StickyTableHeaders by Jonas Mosbech + // ************************** + ts.addWidget({ + id: 'stickyHeaders', + priority: 60, // sticky widget must be initialized after the filter widget! + options: { + stickyHeaders : '', // extra class name added to the sticky header row + stickyHeaders_attachTo : null, // jQuery selector or object to attach sticky header to + stickyHeaders_xScroll : null, // jQuery selector or object to monitor horizontal scroll position (defaults: xScroll > attachTo > window) + stickyHeaders_yScroll : null, // jQuery selector or object to monitor vertical scroll position (defaults: yScroll > attachTo > window) + stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element + stickyHeaders_filteredToTop: true, // scroll table top into view after filtering + stickyHeaders_cloneId : '-sticky', // added to table ID, if it exists + stickyHeaders_addResizeEvent : true, // trigger 'resize' event on headers + stickyHeaders_includeCaption : true, // if false and a caption exist, it won't be included in the sticky header + stickyHeaders_zIndex : 2 // The zIndex of the stickyHeaders, allows the user to adjust this to their needs + }, + format: function(table, c, wo) { + // filter widget doesn't initialize on an empty table. Fixes #449 + if ( c.$table.hasClass('hasStickyHeaders') || ($.inArray('filter', c.widgets) >= 0 && !c.$table.hasClass('hasFilters')) ) { + return; + } + var index, len, $t, + $table = c.$table, + // add position: relative to attach element, hopefully it won't cause trouble. + $attach = $(wo.stickyHeaders_attachTo), + namespace = c.namespace + 'stickyheaders ', + // element to watch for the scroll event + $yScroll = $(wo.stickyHeaders_yScroll || wo.stickyHeaders_attachTo || window), + $xScroll = $(wo.stickyHeaders_xScroll || wo.stickyHeaders_attachTo || window), + $thead = $table.children('thead:first'), + $header = $thead.children('tr').not('.sticky-false').children(), + $tfoot = $table.children('tfoot'), + $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '', + stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, + // is this table nested? If so, find parent sticky header wrapper (div, not table) + $nestedSticky = $table.parent().closest('.' + ts.css.table).hasClass('hasStickyHeaders') ? + $table.parent().closest('table.tablesorter')[0].config.widgetOptions.$sticky.parent() : [], + nestedStickyTop = $nestedSticky.length ? $nestedSticky.height() : 0, + // clone table, then wrap to make sticky header + $stickyTable = wo.$sticky = $table.clone() + .addClass('containsStickyHeaders ' + ts.css.sticky + ' ' + wo.stickyHeaders + ' ' + c.namespace.slice(1) + '_extra_table' ) + .wrap('<div class="' + ts.css.stickyWrap + '">'), + $stickyWrap = $stickyTable.parent() + .addClass(ts.css.stickyHide) + .css({ + position : $attach.length ? 'absolute' : 'fixed', + padding : parseInt( $stickyTable.parent().parent().css('padding-left'), 10 ), + top : stickyOffset + nestedStickyTop, + left : 0, + visibility : 'hidden', + zIndex : wo.stickyHeaders_zIndex || 2 + }), + $stickyThead = $stickyTable.children('thead:first'), + $stickyCells, + laststate = '', + spacing = 0, + setWidth = function($orig, $clone){ + var index, width, border, $cell, $this, + $cells = $orig.filter(':visible'), + len = $cells.length; + for ( index = 0; index < len; index++ ) { + $cell = $clone.filter(':visible').eq(index); + $this = $cells.eq(index); + // code from https://github.com/jmosbech/StickyTableHeaders + if ($this.css('box-sizing') === 'border-box') { + width = $this.outerWidth(); + } else { + if ($cell.css('border-collapse') === 'collapse') { + if (window.getComputedStyle) { + width = parseFloat( window.getComputedStyle($this[0], null).width ); + } else { + // ie8 only + border = parseFloat( $this.css('border-width') ); + width = $this.outerWidth() - parseFloat( $this.css('padding-left') ) - parseFloat( $this.css('padding-right') ) - border; + } } else { - // ie8 only - border = parseFloat( $this.css('border-width') ); - width = $this.outerWidth() - parseFloat( $this.css('padding-left') ) - parseFloat( $this.css('padding-right') ) - border; + width = $this.width(); } - } else { - width = $this.width(); } + $cell.css({ + 'width': width, + 'min-width': width, + 'max-width': width + }); } - $cell.css({ - 'width': width, - 'min-width': width, - 'max-width': width + }, + resizeHeader = function() { + stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0; + spacing = 0; + $stickyWrap.css({ + left : $attach.length ? parseInt($attach.css('padding-left'), 10) || 0 : + $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing, + width: $table.outerWidth() }); - } - }, - resizeHeader = function() { - stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0; - spacing = 0; - $stickyWrap.css({ - left : $attach.length ? parseInt($attach.css('padding-left'), 10) || 0 : - $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing, - width: $table.outerWidth() - }); - setWidth( $table, $stickyTable ); - setWidth( $header, $stickyCells ); - }, - scrollSticky = function( resizing ) { - if (!$table.is(':visible')) { return; } // fixes #278 - // Detect nested tables - fixes #724 - nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; - var offset = $table.offset(), - yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 - xWindow = $.isWindow( $xScroll[0] ), - // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, - scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, - tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)), - isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', - cssSettings = { visibility : isVisible }; - - if ($attach.length) { - cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); - } - if (xWindow) { - // adjust when scrolling horizontally - fixes issue #143 - cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing; - } - if ($nestedSticky.length) { - cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; - } - $stickyWrap - .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) - .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) - .css(cssSettings); - if (isVisible !== laststate || resizing) { - // make sure the column widths match + setWidth( $table, $stickyTable ); + setWidth( $header, $stickyCells ); + }, + scrollSticky = function( resizing ) { + if (!$table.is(':visible')) { return; } // fixes #278 + // Detect nested tables - fixes #724 + nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; + var offset = $table.offset(), + yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 + xWindow = $.isWindow( $xScroll[0] ), + // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, + scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, + tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)), + isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', + cssSettings = { visibility : isVisible }; + + if ($attach.length) { + cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); + } + if (xWindow) { + // adjust when scrolling horizontally - fixes issue #143 + cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing; + } + if ($nestedSticky.length) { + cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; + } + $stickyWrap + .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) + .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) + .css(cssSettings); + if (isVisible !== laststate || resizing) { + // make sure the column widths match + resizeHeader(); + laststate = isVisible; + } + }; + // only add a position relative if a position isn't already defined + if ($attach.length && !$attach.css('position')) { + $attach.css('position', 'relative'); + } + // fix clone ID, if it exists - fixes #271 + if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; } + // clear out cloned table, except for sticky header + // include caption & filter row (fixes #126 & #249) - don't remove cells to get correct cell indexing + $stickyTable.find('thead:gt(0), tr.sticky-false').hide(); + $stickyTable.find('tbody, tfoot').remove(); + $stickyTable.find('caption').toggle(wo.stickyHeaders_includeCaption); + // issue #172 - find td/th in sticky header + $stickyCells = $stickyThead.children().children(); + $stickyTable.css({ height:0, width:0, margin: 0 }); + // remove resizable block + $stickyCells.find('.' + ts.css.resizer).remove(); + // update sticky header class names to match real header after sorting + $table + .addClass('hasStickyHeaders') + .bind('pagerComplete' + namespace, function() { resizeHeader(); - laststate = isVisible; - } - }; - // only add a position relative if a position isn't already defined - if ($attach.length && !$attach.css('position')) { - $attach.css('position', 'relative'); - } - // fix clone ID, if it exists - fixes #271 - if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; } - // clear out cloned table, except for sticky header - // include caption & filter row (fixes #126 & #249) - don't remove cells to get correct cell indexing - $stickyTable.find('thead:gt(0), tr.sticky-false').hide(); - $stickyTable.find('tbody, tfoot').remove(); - $stickyTable.find('caption').toggle(wo.stickyHeaders_includeCaption); - // issue #172 - find td/th in sticky header - $stickyCells = $stickyThead.children().children(); - $stickyTable.css({ height:0, width:0, margin: 0 }); - // remove resizable block - $stickyCells.find('.' + ts.css.resizer).remove(); - // update sticky header class names to match real header after sorting - $table - .addClass('hasStickyHeaders') - .bind('pagerComplete' + namespace, function() { - resizeHeader(); - }); + }); - ts.bindEvents(table, $stickyThead.children().children('.' + ts.css.header)); + ts.bindEvents(table, $stickyThead.children().children('.' + ts.css.header)); - // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. - $table.after( $stickyWrap ); + // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. + $table.after( $stickyWrap ); - // onRenderHeader is defined, we need to do something about it (fixes #641) - if (c.onRenderHeader) { - $t = $stickyThead.children('tr').children(); - len = $t.length; - for ( index = 0; index < len; index++ ) { - // send second parameter - c.onRenderHeader.apply( $t.eq( index ), [ index, c, $stickyTable ] ); + // onRenderHeader is defined, we need to do something about it (fixes #641) + if (c.onRenderHeader) { + $t = $stickyThead.children('tr').children(); + len = $t.length; + for ( index = 0; index < len; index++ ) { + // send second parameter + c.onRenderHeader.apply( $t.eq( index ), [ index, c, $stickyTable ] ); + } } - } - // make it sticky! - $xScroll.add($yScroll) - .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') ) - .bind('scroll resize '.split(' ').join( namespace ), function( event ) { - scrollSticky( event.type === 'resize' ); - }); - c.$table - .unbind('stickyHeadersUpdate' + namespace) - .bind('stickyHeadersUpdate' + namespace, function(){ - scrollSticky( true ); - }); + // make it sticky! + $xScroll.add($yScroll) + .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') ) + .bind('scroll resize '.split(' ').join( namespace ), function( event ) { + scrollSticky( event.type === 'resize' ); + }); + c.$table + .unbind('stickyHeadersUpdate' + namespace) + .bind('stickyHeadersUpdate' + namespace, function(){ + scrollSticky( true ); + }); - if (wo.stickyHeaders_addResizeEvent) { - ts.addHeaderResizeEvent(table); - } + if (wo.stickyHeaders_addResizeEvent) { + ts.addHeaderResizeEvent(table); + } - // look for filter widget - if ($table.hasClass('hasFilters') && wo.filter_columnFilters) { - // scroll table into view after filtering, if sticky header is active - #482 - $table.bind('filterEnd' + namespace, function() { - // $(':focus') needs jQuery 1.6+ - var $td = $(document.activeElement).closest('td'), - column = $td.parent().children().index($td); - // only scroll if sticky header is active - if ($stickyWrap.hasClass(ts.css.stickyVis) && wo.stickyHeaders_filteredToTop) { - // scroll to original table (not sticky clone) - window.scrollTo(0, $table.position().top); - // give same input/select focus; check if c.$filters exists; fixes #594 - if (column >= 0 && c.$filters) { - c.$filters.eq(column).find('a, select, input').filter(':visible').focus(); + // look for filter widget + if ($table.hasClass('hasFilters') && wo.filter_columnFilters) { + // scroll table into view after filtering, if sticky header is active - #482 + $table.bind('filterEnd' + namespace, function() { + // $(':focus') needs jQuery 1.6+ + var $td = $(document.activeElement).closest('td'), + column = $td.parent().children().index($td); + // only scroll if sticky header is active + if ($stickyWrap.hasClass(ts.css.stickyVis) && wo.stickyHeaders_filteredToTop) { + // scroll to original table (not sticky clone) + window.scrollTo(0, $table.position().top); + // give same input/select focus; check if c.$filters exists; fixes #594 + if (column >= 0 && c.$filters) { + c.$filters.eq(column).find('a, select, input').filter(':visible').focus(); + } } + }); + ts.filter.bindSearch( $table, $stickyCells.find('.' + ts.css.filter) ); + // support hideFilters + if (wo.filter_hideFilters) { + ts.filter.hideFilters($stickyTable, c); } - }); - ts.filter.bindSearch( $table, $stickyCells.find('.' + ts.css.filter) ); - // support hideFilters - if (wo.filter_hideFilters) { - ts.filter.hideFilters($stickyTable, c); } - } - $table.trigger('stickyHeadersInit'); - - }, - remove: function(table, c, wo) { - var namespace = c.namespace + 'stickyheaders '; - c.$table - .removeClass('hasStickyHeaders') - .unbind( ('pagerComplete filterEnd stickyHeadersUpdate '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) - .next('.' + ts.css.stickyWrap).remove(); - if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table - $(window) - .add(wo.stickyHeaders_xScroll) - .add(wo.stickyHeaders_yScroll) - .add(wo.stickyHeaders_attachTo) - .unbind( ('scroll resize '.split(' ').join(namespace)).replace(/\s+/g, ' ') ); - ts.addHeaderResizeEvent(table, false); - } -}); + $table.trigger('stickyHeadersInit'); + + }, + remove: function(table, c, wo) { + var namespace = c.namespace + 'stickyheaders '; + c.$table + .removeClass('hasStickyHeaders') + .unbind( ('pagerComplete filterEnd stickyHeadersUpdate '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) + .next('.' + ts.css.stickyWrap).remove(); + if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table + $(window) + .add(wo.stickyHeaders_xScroll) + .add(wo.stickyHeaders_yScroll) + .add(wo.stickyHeaders_attachTo) + .unbind( ('scroll resize '.split(' ').join(namespace)).replace(/\s+/g, ' ') ); + ts.addHeaderResizeEvent(table, false); + } + }); })(jQuery, window); /*! Widget: resizable - updated 6/26/2015 (v2.22.2) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { -'use strict'; -var ts = $.tablesorter || {}; - -$.extend(ts.css, { - resizableContainer : 'tablesorter-resizable-container', - resizableHandle : 'tablesorter-resizable-handle', - resizableNoSelect : 'tablesorter-disableSelection', - resizableStorage : 'tablesorter-resizable' -}); - -// Add extra scroller css -$(function(){ - var s = '<style>' + - 'body.' + ts.css.resizableNoSelect + ' { -ms-user-select: none; -moz-user-select: -moz-none;' + - '-khtml-user-select: none; -webkit-user-select: none; user-select: none; }' + - '.' + ts.css.resizableContainer + ' { position: relative; height: 1px; }' + - // make handle z-index > than stickyHeader z-index, so the handle stays above sticky header - '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px;' + - 'top: 1px; cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' + - '</style>'; - $(s).appendTo('body'); -}); - -ts.resizable = { - init : function( c, wo ) { - if ( c.$table.hasClass( 'hasResizable' ) ) { return; } - c.$table.addClass( 'hasResizable' ); - - var noResize, $header, column, storedSizes, tmp, - $table = c.$table, - $parent = $table.parent(), - marginTop = parseInt( $table.css( 'margin-top' ), 10 ), - - // internal variables - vars = wo.resizable_ = { - useStorage : ts.storage && wo.resizable !== false, - $wrap : $parent, - mouseXPosition : 0, - $target : null, - $next : null, - overflow : $parent.css('overflow') === 'auto' || - $parent.css('overflow') === 'scroll' || - $parent.css('overflow-x') === 'auto' || - $parent.css('overflow-x') === 'scroll', - storedSizes : [] - }; + 'use strict'; + var ts = $.tablesorter || {}; - // set default widths - ts.resizableReset( c.table, true ); - - // now get measurements! - vars.tableWidth = $table.width(); - // attempt to autodetect - vars.fullWidth = Math.abs( $parent.width() - vars.tableWidth ) < 20; - - /* - // Hacky method to determine if table width is set to "auto" - // http://stackoverflow.com/a/20892048/145346 - if ( !vars.fullWidth ) { - tmp = $table.width(); - $header = $table.wrap('<span>').parent(); // temp variable - storedSizes = parseInt( $table.css( 'margin-left' ), 10 ) || 0; - $table.css( 'margin-left', storedSizes + 50 ); - vars.tableWidth = $header.width() > tmp ? 'auto' : tmp; - $table.css( 'margin-left', storedSizes ? storedSizes : '' ); - $header = null; - $table.unwrap('<span>'); - } - */ + $.extend(ts.css, { + resizableContainer : 'tablesorter-resizable-container', + resizableHandle : 'tablesorter-resizable-handle', + resizableNoSelect : 'tablesorter-disableSelection', + resizableStorage : 'tablesorter-resizable' + }); - if ( vars.useStorage && vars.overflow ) { - // save table width - ts.storage( c.table, 'tablesorter-table-original-css-width', vars.tableWidth ); - tmp = ts.storage( c.table, 'tablesorter-table-resized-width' ) || 'auto'; - ts.resizable.setWidth( $table, tmp, true ); - } - wo.resizable_.storedSizes = storedSizes = ( vars.useStorage ? - ts.storage( c.table, ts.css.resizableStorage ) : - [] ) || []; - ts.resizable.setWidths( c, wo, storedSizes ); - ts.resizable.updateStoredSizes( c, wo ); - - wo.$resizable_container = $( '<div class="' + ts.css.resizableContainer + '">' ) - .css({ top : marginTop }) - .insertBefore( $table ); - // add container - for ( column = 0; column < c.columns; column++ ) { - $header = c.$headerIndexed[ column ]; - tmp = ts.getColumnData( c.table, c.headers, column ); - noResize = ts.getData( $header, tmp, 'resizable' ) === 'false'; - if ( !noResize ) { - $( '<div class="' + ts.css.resizableHandle + '">' ) - .appendTo( wo.$resizable_container ) - .attr({ - 'data-column' : column, - 'unselectable' : 'on' - }) - .data( 'header', $header ) - .bind( 'selectstart', false ); + // Add extra scroller css + $(function(){ + var s = '<style>' + + 'body.' + ts.css.resizableNoSelect + ' { -ms-user-select: none; -moz-user-select: -moz-none;' + + '-khtml-user-select: none; -webkit-user-select: none; user-select: none; }' + + '.' + ts.css.resizableContainer + ' { position: relative; height: 1px; }' + + // make handle z-index > than stickyHeader z-index, so the handle stays above sticky header + '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px;' + + 'top: 1px; cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' + + '</style>'; + $(s).appendTo('body'); + }); + + ts.resizable = { + init : function( c, wo ) { + if ( c.$table.hasClass( 'hasResizable' ) ) { return; } + c.$table.addClass( 'hasResizable' ); + + var noResize, $header, column, storedSizes, tmp, + $table = c.$table, + $parent = $table.parent(), + marginTop = parseInt( $table.css( 'margin-top' ), 10 ), + + // internal variables + vars = wo.resizable_vars = { + useStorage : ts.storage && wo.resizable !== false, + $wrap : $parent, + mouseXPosition : 0, + $target : null, + $next : null, + overflow : $parent.css('overflow') === 'auto' || + $parent.css('overflow') === 'scroll' || + $parent.css('overflow-x') === 'auto' || + $parent.css('overflow-x') === 'scroll', + storedSizes : [] + }; + + // set default widths + ts.resizableReset( c.table, true ); + + // now get measurements! + vars.tableWidth = $table.width(); + // attempt to autodetect + vars.fullWidth = Math.abs( $parent.width() - vars.tableWidth ) < 20; + + /* + // Hacky method to determine if table width is set to 'auto' + // http://stackoverflow.com/a/20892048/145346 + if ( !vars.fullWidth ) { + tmp = $table.width(); + $header = $table.wrap('<span>').parent(); // temp variable + storedSizes = parseInt( $table.css( 'margin-left' ), 10 ) || 0; + $table.css( 'margin-left', storedSizes + 50 ); + vars.tableWidth = $header.width() > tmp ? 'auto' : tmp; + $table.css( 'margin-left', storedSizes ? storedSizes : '' ); + $header = null; + $table.unwrap('<span>'); } - } - $table.one('tablesorter-initialized', function() { - ts.resizable.setHandlePosition( c, wo ); - ts.resizable.bindings( this.config, this.config.widgetOptions ); - }); - }, - - updateStoredSizes : function( c, wo ) { - var column, $header, - len = c.columns, - vars = wo.resizable_; - vars.storedSizes = []; - for ( column = 0; column < len; column++ ) { - $header = c.$headerIndexed[ column ]; - vars.storedSizes[ column ] = $header.is(':visible') ? $header.width() : 0; - } - }, - - setWidth : function( $el, width, overflow ) { - // overflow tables need min & max width set as well - $el.css({ - 'width' : width, - 'min-width' : overflow ? width : '', - 'max-width' : overflow ? width : '' - }); - }, - - setWidths : function( c, wo, storedSizes ) { - var column, $temp, - vars = wo.resizable_, - $extra = $( c.namespace + '_extra_headers' ), - $col = c.$table.children( 'colgroup' ).children( 'col' ); - storedSizes = storedSizes || vars.storedSizes || []; - // process only if table ID or url match - if ( storedSizes.length ) { + */ + + if ( vars.useStorage && vars.overflow ) { + // save table width + ts.storage( c.table, 'tablesorter-table-original-css-width', vars.tableWidth ); + tmp = ts.storage( c.table, 'tablesorter-table-resized-width' ) || 'auto'; + ts.resizable.setWidth( $table, tmp, true ); + } + wo.resizable_vars.storedSizes = storedSizes = ( vars.useStorage ? + ts.storage( c.table, ts.css.resizableStorage ) : + [] ) || []; + ts.resizable.setWidths( c, wo, storedSizes ); + ts.resizable.updateStoredSizes( c, wo ); + + wo.$resizable_container = $( '<div class="' + ts.css.resizableContainer + '">' ) + .css({ top : marginTop }) + .insertBefore( $table ); + // add container for ( column = 0; column < c.columns; column++ ) { - // set saved resizable widths - ts.resizable.setWidth( c.$headerIndexed[ column ], storedSizes[ column ], vars.overflow ); - if ( $extra.length ) { - // stickyHeaders needs to modify min & max width as well - $temp = $extra.eq( column ).add( $col.eq( column ) ); - ts.resizable.setWidth( $temp, storedSizes[ column ], vars.overflow ); + $header = c.$headerIndexed[ column ]; + tmp = ts.getColumnData( c.table, c.headers, column ); + noResize = ts.getData( $header, tmp, 'resizable' ) === 'false'; + if ( !noResize ) { + $( '<div class="' + ts.css.resizableHandle + '">' ) + .appendTo( wo.$resizable_container ) + .attr({ + 'data-column' : column, + 'unselectable' : 'on' + }) + .data( 'header', $header ) + .bind( 'selectstart', false ); } } - $temp = $( c.namespace + '_extra_table' ); - if ( $temp.length && !ts.hasWidget( c.table, 'scroller' ) ) { - ts.resizable.setWidth( $temp, c.$table.outerWidth(), vars.overflow ); - } - } - }, - - setHandlePosition : function( c, wo ) { - var startPosition, - hasScroller = ts.hasWidget( c.table, 'scroller' ), - tableHeight = c.$table.height(), - $handles = wo.$resizable_container.children(), - handleCenter = Math.floor( $handles.width() / 2 ); - - if ( hasScroller ) { - tableHeight = 0; - c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function(){ - var $this = $(this); - // center table has a max-height set - tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); + $table.one('tablesorter-initialized', function() { + ts.resizable.setHandlePosition( c, wo ); + ts.resizable.bindings( this.config, this.config.widgetOptions ); }); - } - // subtract out table left position from resizable handles. Fixes #864 - startPosition = c.$table.position().left; - $handles.each( function() { - var $this = $(this), - column = parseInt( $this.attr( 'data-column' ), 10 ), - columns = c.columns - 1, - $header = $this.data( 'header' ); - if ( !$header ) { return; } // see #859 - if ( !$header.is(':visible') ) { - $this.hide(); - } else if ( column < columns || column === columns && wo.resizable_addLastColumn ) { - $this.css({ - display: 'inline-block', - height : tableHeight, - left : $header.position().left - startPosition + $header.outerWidth() - handleCenter - }); + }, + + updateStoredSizes : function( c, wo ) { + var column, $header, + len = c.columns, + vars = wo.resizable_vars; + vars.storedSizes = []; + for ( column = 0; column < len; column++ ) { + $header = c.$headerIndexed[ column ]; + vars.storedSizes[ column ] = $header.is(':visible') ? $header.width() : 0; } - }); - }, - - // prevent text selection while dragging resize bar - toggleTextSelection : function( c, toggle ) { - var namespace = c.namespace + 'tsresize'; - c.widgetOptions.resizable_.disabled = toggle; - $( 'body' ).toggleClass( ts.css.resizableNoSelect, toggle ); - if ( toggle ) { - $( 'body' ) - .attr( 'unselectable', 'on' ) - .bind( 'selectstart' + namespace, false ); - } else { - $( 'body' ) - .removeAttr( 'unselectable' ) - .unbind( 'selectstart' + namespace ); - } - }, + }, - bindings : function( c, wo ) { - var namespace = c.namespace + 'tsresize'; - wo.$resizable_container.children().bind( 'mousedown', function( event ) { - // save header cell and mouse position - var column, - vars = wo.resizable_, - $extras = $( c.namespace + '_extra_headers' ), - $header = $( event.target ).data( 'header' ); + setWidth : function( $el, width, overflow ) { + // overflow tables need min & max width set as well + $el.css({ + 'width' : width, + 'min-width' : overflow ? width : '', + 'max-width' : overflow ? width : '' + }); + }, - column = parseInt( $header.attr( 'data-column' ), 10 ); - vars.$target = $header = $header.add( $extras.filter('[data-column="' + column + '"]') ); - vars.target = column; + setWidths : function( c, wo, storedSizes ) { + var column, $temp, + vars = wo.resizable_vars, + $extra = $( c.namespace + '_extra_headers' ), + $col = c.$table.children( 'colgroup' ).children( 'col' ); + storedSizes = storedSizes || vars.storedSizes || []; + // process only if table ID or url match + if ( storedSizes.length ) { + for ( column = 0; column < c.columns; column++ ) { + // set saved resizable widths + ts.resizable.setWidth( c.$headerIndexed[ column ], storedSizes[ column ], vars.overflow ); + if ( $extra.length ) { + // stickyHeaders needs to modify min & max width as well + $temp = $extra.eq( column ).add( $col.eq( column ) ); + ts.resizable.setWidth( $temp, storedSizes[ column ], vars.overflow ); + } + } + $temp = $( c.namespace + '_extra_table' ); + if ( $temp.length && !ts.hasWidget( c.table, 'scroller' ) ) { + ts.resizable.setWidth( $temp, c.$table.outerWidth(), vars.overflow ); + } + } + }, - // if table is not as wide as it's parent, then resize the table - vars.$next = event.shiftKey || wo.resizable_targetLast ? - $header.parent().children().not( '.resizable-false' ).filter( ':last' ) : - $header.nextAll( ':not(.resizable-false)' ).eq( 0 ); + setHandlePosition : function( c, wo ) { + var startPosition, + hasScroller = ts.hasWidget( c.table, 'scroller' ), + tableHeight = c.$table.height(), + $handles = wo.$resizable_container.children(), + handleCenter = Math.floor( $handles.width() / 2 ); + + if ( hasScroller ) { + tableHeight = 0; + c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function(){ + var $this = $(this); + // center table has a max-height set + tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); + }); + } + // subtract out table left position from resizable handles. Fixes #864 + startPosition = c.$table.position().left; + $handles.each( function() { + var $this = $(this), + column = parseInt( $this.attr( 'data-column' ), 10 ), + columns = c.columns - 1, + $header = $this.data( 'header' ); + if ( !$header ) { return; } // see #859 + if ( !$header.is(':visible') ) { + $this.hide(); + } else if ( column < columns || column === columns && wo.resizable_addLastColumn ) { + $this.css({ + display: 'inline-block', + height : tableHeight, + left : $header.position().left - startPosition + $header.outerWidth() - handleCenter + }); + } + }); + }, - column = parseInt( vars.$next.attr( 'data-column' ), 10 ); - vars.$next = vars.$next.add( $extras.filter('[data-column="' + column + '"]') ); - vars.next = column; + // prevent text selection while dragging resize bar + toggleTextSelection : function( c, toggle ) { + var namespace = c.namespace + 'tsresize'; + c.widgetOptions.resizable_vars.disabled = toggle; + $( 'body' ).toggleClass( ts.css.resizableNoSelect, toggle ); + if ( toggle ) { + $( 'body' ) + .attr( 'unselectable', 'on' ) + .bind( 'selectstart' + namespace, false ); + } else { + $( 'body' ) + .removeAttr( 'unselectable' ) + .unbind( 'selectstart' + namespace ); + } + }, - vars.mouseXPosition = event.pageX; - ts.resizable.updateStoredSizes( c, wo ); - ts.resizable.toggleTextSelection( c, true ); - }); + bindings : function( c, wo ) { + var namespace = c.namespace + 'tsresize'; + wo.$resizable_container.children().bind( 'mousedown', function( event ) { + // save header cell and mouse position + var column, + vars = wo.resizable_vars, + $extras = $( c.namespace + '_extra_headers' ), + $header = $( event.target ).data( 'header' ); + + column = parseInt( $header.attr( 'data-column' ), 10 ); + vars.$target = $header = $header.add( $extras.filter('[data-column="' + column + '"]') ); + vars.target = column; + + // if table is not as wide as it's parent, then resize the table + vars.$next = event.shiftKey || wo.resizable_targetLast ? + $header.parent().children().not( '.resizable-false' ).filter( ':last' ) : + $header.nextAll( ':not(.resizable-false)' ).eq( 0 ); + + column = parseInt( vars.$next.attr( 'data-column' ), 10 ); + vars.$next = vars.$next.add( $extras.filter('[data-column="' + column + '"]') ); + vars.next = column; + + vars.mouseXPosition = event.pageX; + ts.resizable.updateStoredSizes( c, wo ); + ts.resizable.toggleTextSelection( c, true ); + }); - $( document ) - .bind( 'mousemove' + namespace, function( event ) { - var vars = wo.resizable_; - // ignore mousemove if no mousedown - if ( !vars.disabled || vars.mouseXPosition === 0 || !vars.$target ) { return; } - if ( wo.resizable_throttle ) { - clearTimeout( vars.timer ); - vars.timer = setTimeout( function() { + $( document ) + .bind( 'mousemove' + namespace, function( event ) { + var vars = wo.resizable_vars; + // ignore mousemove if no mousedown + if ( !vars.disabled || vars.mouseXPosition === 0 || !vars.$target ) { return; } + if ( wo.resizable_throttle ) { + clearTimeout( vars.timer ); + vars.timer = setTimeout( function() { + ts.resizable.mouseMove( c, wo, event ); + }, isNaN( wo.resizable_throttle ) ? 5 : wo.resizable_throttle ); + } else { ts.resizable.mouseMove( c, wo, event ); - }, isNaN( wo.resizable_throttle ) ? 5 : wo.resizable_throttle ); - } else { - ts.resizable.mouseMove( c, wo, event ); - } - }) - .bind( 'mouseup' + namespace, function() { - if (!wo.resizable_.disabled) { return; } - ts.resizable.toggleTextSelection( c, false ); - ts.resizable.stopResize( c, wo ); + } + }) + .bind( 'mouseup' + namespace, function() { + if (!wo.resizable_vars.disabled) { return; } + ts.resizable.toggleTextSelection( c, false ); + ts.resizable.stopResize( c, wo ); + ts.resizable.setHandlePosition( c, wo ); + }); + + // resizeEnd event triggered by scroller widget + $( window ).bind( 'resize' + namespace + ' resizeEnd' + namespace, function() { ts.resizable.setHandlePosition( c, wo ); }); - // resizeEnd event triggered by scroller widget - $( window ).bind( 'resize' + namespace + ' resizeEnd' + namespace, function() { - ts.resizable.setHandlePosition( c, wo ); - }); + // right click to reset columns to default widths + c.$table + .bind( 'columnUpdate' + namespace, function() { + ts.resizable.setHandlePosition( c, wo ); + }) + .find( 'thead:first' ) + .add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) + .bind( 'contextmenu' + namespace, function() { + // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset + var allowClick = wo.resizable_vars.storedSizes.length === 0; + ts.resizableReset( c.table ); + ts.resizable.setHandlePosition( c, wo ); + wo.resizable_vars.storedSizes = []; + return allowClick; + }); - // right click to reset columns to default widths - c.$table - .bind( 'columnUpdate' + namespace, function() { - ts.resizable.setHandlePosition( c, wo ); - }) - .find( 'thead:first' ) - .add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) - .bind( 'contextmenu' + namespace, function() { - // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset - var allowClick = wo.resizable_.storedSizes.length === 0; - ts.resizableReset( c.table ); - ts.resizable.setHandlePosition( c, wo ); - wo.resizable_.storedSizes = []; - return allowClick; - }); + }, - }, - - mouseMove : function( c, wo, event ) { - if ( wo.resizable_.mouseXPosition === 0 || !wo.resizable_.$target ) { return; } - // resize columns - var column, - total = 0, - vars = wo.resizable_, - $next = vars.$next, - tar = vars.storedSizes[ vars.target ], - leftEdge = event.pageX - vars.mouseXPosition; - if ( vars.overflow ) { - if ( tar + leftEdge > 0 ) { - vars.storedSizes[ vars.target ] += leftEdge; - ts.resizable.setWidth( vars.$target, vars.storedSizes[ vars.target ], true ); - // update the entire table width - for ( column = 0; column < c.columns; column++ ) { - total += vars.storedSizes[ column ]; + mouseMove : function( c, wo, event ) { + if ( wo.resizable_vars.mouseXPosition === 0 || !wo.resizable_vars.$target ) { return; } + // resize columns + var column, + total = 0, + vars = wo.resizable_vars, + $next = vars.$next, + tar = vars.storedSizes[ vars.target ], + leftEdge = event.pageX - vars.mouseXPosition; + if ( vars.overflow ) { + if ( tar + leftEdge > 0 ) { + vars.storedSizes[ vars.target ] += leftEdge; + ts.resizable.setWidth( vars.$target, vars.storedSizes[ vars.target ], true ); + // update the entire table width + for ( column = 0; column < c.columns; column++ ) { + total += vars.storedSizes[ column ]; + } + ts.resizable.setWidth( c.$table.add( $( c.namespace + '_extra_table' ) ), total ); + } + if ( !$next.length ) { + // if expanding right-most column, scroll the wrapper + vars.$wrap[0].scrollLeft = c.$table.width(); } - ts.resizable.setWidth( c.$table.add( $( c.namespace + '_extra_table' ) ), total ); + } else if ( vars.fullWidth ) { + vars.storedSizes[ vars.target ] += leftEdge; + vars.storedSizes[ vars.next ] -= leftEdge; + ts.resizable.setWidths( c, wo ); + } else { + vars.storedSizes[ vars.target ] += leftEdge; + ts.resizable.setWidths( c, wo ); } - if ( !$next.length ) { - // if expanding right-most column, scroll the wrapper - vars.$wrap[0].scrollLeft = c.$table.width(); + vars.mouseXPosition = event.pageX; + // dynamically update sticky header widths + c.$table.trigger('stickyHeadersUpdate'); + }, + + stopResize : function( c, wo ) { + var vars = wo.resizable_vars; + ts.resizable.updateStoredSizes( c, wo ); + if ( vars.useStorage ) { + // save all column widths + ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); + ts.storage( c.table, 'tablesorter-table-resized-width', c.$table.width() ); } - } else if ( vars.fullWidth ) { - vars.storedSizes[ vars.target ] += leftEdge; - vars.storedSizes[ vars.next ] -= leftEdge; - ts.resizable.setWidths( c, wo ); - } else { - vars.storedSizes[ vars.target ] += leftEdge; - ts.resizable.setWidths( c, wo ); - } - vars.mouseXPosition = event.pageX; - // dynamically update sticky header widths - c.$table.trigger('stickyHeadersUpdate'); - }, - - stopResize : function( c, wo ) { - var vars = wo.resizable_; - ts.resizable.updateStoredSizes( c, wo ); - if ( vars.useStorage ) { - // save all column widths - ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); - ts.storage( c.table, 'tablesorter-table-resized-width', c.$table.width() ); + vars.mouseXPosition = 0; + vars.$target = vars.$next = null; + // will update stickyHeaders, just in case, see #912 + c.$table.trigger('stickyHeadersUpdate'); } - vars.mouseXPosition = 0; - vars.$target = vars.$next = null; - // will update stickyHeaders, just in case, see #912 - c.$table.trigger('stickyHeadersUpdate'); - } -}; - -// this widget saves the column widths if -// $.tablesorter.storage function is included -// ************************** -ts.addWidget({ - id: "resizable", - priority: 40, - options: { - resizable : true, // save column widths to storage - resizable_addLastColumn : false, - resizable_widths : [], - resizable_throttle : false, // set to true (5ms) or any number 0-10 range - resizable_targetLast : false, - resizable_fullWidth : null - }, - init: function(table, thisWidget, c, wo) { - ts.resizable.init( c, wo ); - }, - remove: function( table, c, wo, refreshing ) { - if (wo.$resizable_container) { - var namespace = c.namespace + 'tsresize'; - c.$table.add( $( c.namespace + '_extra_table' ) ) - .removeClass('hasResizable') - .children( 'thead' ).unbind( 'contextmenu' + namespace ); + }; + + // this widget saves the column widths if + // $.tablesorter.storage function is included + // ************************** + ts.addWidget({ + id: 'resizable', + priority: 40, + options: { + resizable : true, // save column widths to storage + resizable_addLastColumn : false, + resizable_widths : [], + resizable_throttle : false, // set to true (5ms) or any number 0-10 range + resizable_targetLast : false, + resizable_fullWidth : null + }, + init: function(table, thisWidget, c, wo) { + ts.resizable.init( c, wo ); + }, + remove: function( table, c, wo, refreshing ) { + if (wo.$resizable_container) { + var namespace = c.namespace + 'tsresize'; + c.$table.add( $( c.namespace + '_extra_table' ) ) + .removeClass('hasResizable') + .children( 'thead' ) + .unbind( 'contextmenu' + namespace ); wo.$resizable_container.remove(); - ts.resizable.toggleTextSelection( c, false ); - ts.resizableReset( table, refreshing ); - $( document ).unbind( 'mousemove' + namespace + ' mouseup' + namespace ); + ts.resizable.toggleTextSelection( c, false ); + ts.resizableReset( table, refreshing ); + $( document ).unbind( 'mousemove' + namespace + ' mouseup' + namespace ); + } } - } -}); + }); -ts.resizableReset = function( table, refreshing ) { - $( table ).each(function(){ - var index, $t, - c = this.config, - wo = c && c.widgetOptions, - vars = wo.resizable_; - if ( table && c && c.$headerIndexed.length ) { - // restore the initial table width - if ( vars.overflow && vars.tableWidth ) { - ts.resizable.setWidth( c.$table, vars.tableWidth, true ); - if ( vars.useStorage ) { - ts.storage( table, 'tablesorter-table-resized-width', 'auto' ); + ts.resizableReset = function( table, refreshing ) { + $( table ).each(function(){ + var index, $t, + c = this.config, + wo = c && c.widgetOptions, + vars = wo.resizable_vars; + if ( table && c && c.$headerIndexed.length ) { + // restore the initial table width + if ( vars.overflow && vars.tableWidth ) { + ts.resizable.setWidth( c.$table, vars.tableWidth, true ); + if ( vars.useStorage ) { + ts.storage( table, 'tablesorter-table-resized-width', 'auto' ); + } } - } - for ( index = 0; index < c.columns; index++ ) { - $t = c.$headerIndexed[ index ]; - if ( wo.resizable_widths && wo.resizable_widths[ index ] ) { - ts.resizable.setWidth( $t, wo.resizable_widths[ index ], vars.overflow ); - } else if ( !$t.hasClass( 'resizable-false' ) ) { - // don't clear the width of any column that is not resizable - ts.resizable.setWidth( $t, '', vars.overflow ); + for ( index = 0; index < c.columns; index++ ) { + $t = c.$headerIndexed[ index ]; + if ( wo.resizable_widths && wo.resizable_widths[ index ] ) { + ts.resizable.setWidth( $t, wo.resizable_widths[ index ], vars.overflow ); + } else if ( !$t.hasClass( 'resizable-false' ) ) { + // don't clear the width of any column that is not resizable + ts.resizable.setWidth( $t, '', vars.overflow ); + } } - } - // reset stickyHeader widths - c.$table.trigger( 'stickyHeadersUpdate' ); - if ( ts.storage && !refreshing ) { - ts.storage( this, ts.css.resizableStorage, {} ); + // reset stickyHeader widths + c.$table.trigger( 'stickyHeadersUpdate' ); + if ( ts.storage && !refreshing ) { + ts.storage( this, ts.css.resizableStorage, {} ); + } } - } - }); -}; + }); + }; })( jQuery, window ); /*! Widget: saveSort */ ;(function ($) { -'use strict'; -var ts = $.tablesorter || {}; - -// this widget saves the last sort only if the -// saveSort widget option is true AND the -// $.tablesorter.storage function is included -// ************************** -ts.addWidget({ - id: 'saveSort', - priority: 20, - options: { - saveSort : true - }, - init: function(table, thisWidget, c, wo) { - // run widget format before all other widgets are applied to the table - thisWidget.format(table, c, wo, true); - }, - format: function(table, c, wo, init) { - var stored, time, - $table = c.$table, - saveSort = wo.saveSort !== false, // make saveSort active/inactive; default to true - sortList = { "sortList" : c.sortList }; - if (c.debug) { - time = new Date(); - } - if ($table.hasClass('hasSaveSort')) { - if (saveSort && table.hasInitialized && ts.storage) { - ts.storage( table, 'tablesorter-savesort', sortList ); - if (c.debug) { - ts.benchmark('saveSort widget: Saving last sort: ' + c.sortList, time); - } + 'use strict'; + var ts = $.tablesorter || {}; + + // this widget saves the last sort only if the + // saveSort widget option is true AND the + // $.tablesorter.storage function is included + // ************************** + ts.addWidget({ + id: 'saveSort', + priority: 20, + options: { + saveSort : true + }, + init: function(table, thisWidget, c, wo) { + // run widget format before all other widgets are applied to the table + thisWidget.format(table, c, wo, true); + }, + format: function(table, c, wo, init) { + var stored, time, + $table = c.$table, + saveSort = wo.saveSort !== false, // make saveSort active/inactive; default to true + sortList = { 'sortList' : c.sortList }; + if (c.debug) { + time = new Date(); } - } else { - // set table sort on initial run of the widget - $table.addClass('hasSaveSort'); - sortList = ''; - // get data - if (ts.storage) { - stored = ts.storage( table, 'tablesorter-savesort' ); - sortList = (stored && stored.hasOwnProperty('sortList') && $.isArray(stored.sortList)) ? stored.sortList : ''; - if (c.debug) { - ts.benchmark('saveSort: Last sort loaded: "' + sortList + '"', time); + if ($table.hasClass('hasSaveSort')) { + if (saveSort && table.hasInitialized && ts.storage) { + ts.storage( table, 'tablesorter-savesort', sortList ); + if (c.debug) { + console.log('saveSort widget: Saving last sort: ' + c.sortList + ts.benchmark(time)); + } + } + } else { + // set table sort on initial run of the widget + $table.addClass('hasSaveSort'); + sortList = ''; + // get data + if (ts.storage) { + stored = ts.storage( table, 'tablesorter-savesort' ); + sortList = (stored && stored.hasOwnProperty('sortList') && $.isArray(stored.sortList)) ? stored.sortList : ''; + if (c.debug) { + console.log('saveSort: Last sort loaded: "' + sortList + '"' + ts.benchmark(time)); + } + $table.bind('saveSortReset', function(event) { + event.stopPropagation(); + ts.storage( table, 'tablesorter-savesort', '' ); + }); + } + // init is true when widget init is run, this will run this widget before all other widgets have initialized + // this method allows using this widget in the original tablesorter plugin; but then it will run all widgets twice. + if (init && sortList && sortList.length > 0) { + c.sortList = sortList; + } else if (table.hasInitialized && sortList && sortList.length > 0) { + // update sort change + $table.trigger('sorton', [ sortList ]); } - $table.bind('saveSortReset', function(event) { - event.stopPropagation(); - ts.storage( table, 'tablesorter-savesort', '' ); - }); - } - // init is true when widget init is run, this will run this widget before all other widgets have initialized - // this method allows using this widget in the original tablesorter plugin; but then it will run all widgets twice. - if (init && sortList && sortList.length > 0) { - c.sortList = sortList; - } else if (table.hasInitialized && sortList && sortList.length > 0) { - // update sort change - $table.trigger('sorton', [sortList]); } + }, + remove: function(table, c) { + c.$table.removeClass('hasSaveSort'); + // clear storage + if (ts.storage) { ts.storage( table, 'tablesorter-savesort', '' ); } } - }, - remove: function(table, c) { - c.$table.removeClass('hasSaveSort'); - // clear storage - if (ts.storage) { ts.storage( table, 'tablesorter-savesort', '' ); } - } -}); + }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 1ce5443..a2eda6d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.22.3 *//* +/*! TableSorter (FORK) v2.22.4 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -26,7 +26,7 @@ var ts = this; - ts.version = '2.22.3'; + ts.version = '2.22.4'; ts.parsers = []; ts.widgets = []; @@ -154,24 +154,6 @@ // These methods can be applied on table.config instance ts.instanceMethods = {}; - /* debuging utils */ - function log() { - var a = arguments[0], - s = arguments.length > 1 ? Array.prototype.slice.call(arguments) : a; - if (typeof console !== 'undefined' && typeof console.log !== 'undefined') { - console[ /error/i.test(a) ? 'error' : /warn/i.test(a) ? 'warn' : 'log' ](s); - } else { - alert(s); - } - } - - function benchmark(s, d) { - log(s + ' (' + (new Date().getTime() - d.getTime()) + 'ms)'); - } - - ts.log = log; - ts.benchmark = benchmark; - // $.isEmptyObject from jQuery v1.4 function isEmptyObject(obj) { /*jshint forin: false */ @@ -188,7 +170,7 @@ // node could be a jquery object // http://jsperf.com/jquery-vs-instanceof-jquery/2 $node = node.jquery ? node : $(node); - if (typeof(t) === 'string') { + if (typeof t === 'string') { // check data-attribute first when set to 'basic'; don't use node.innerText - it's really slow! // http://www.kellegous.com/j/2013/02/27/innertext-vs-textcontent/ if ( t === 'basic' && typeof ( te = $node.attr(c.textAttribute) ) !== 'undefined' ) { @@ -196,7 +178,7 @@ } return $.trim( node.textContent || $node.text() ); } else { - if (typeof(t) === 'function') { + if (typeof t === 'function') { return $.trim( t($node[0], c.table, cellIndex) ); } else if (typeof (te = ts.getColumnData( c.table, t, cellIndex )) === 'function') { return $.trim( te($node[0], c.table, cellIndex) ); @@ -219,7 +201,7 @@ nodeValue = ts.getElementText(c, node, cellIndex); $node = $(node); if (c.debug) { - log('Checking if value was empty on row ' + rowIndex + ', column: ' + cellIndex + ': "' + nodeValue + '"'); + console.log('Checking if value was empty on row ' + rowIndex + ', column: ' + cellIndex + ': "' + nodeValue + '"'); } } else { keepLooking = false; @@ -256,7 +238,7 @@ // make sure txt is a string (extractor may have converted it) parser.format( '' + txt, c.table, cell, colIndex ); if ( c.ignoreCase && typeof val === 'string' ) { - val = val.toLowerCase(); + val = val.toLowerCase(); } } return val; @@ -266,16 +248,16 @@ var rows, list, l, i, h, ch, np, p, e, time, tb, len, table = c.table, j = 0, - parsersDebug = ''; + debug = {}; // update table bodies in case we start with an empty table c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'); tb = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies; len = tb.length; if ( len === 0) { - return c.debug ? log('Warning: *Empty table!* Not building a parser cache') : ''; + return c.debug ? console.warn('Warning: *Empty table!* Not building a parser cache') : ''; } else if (c.debug) { time = new Date(); - log('Detecting parsers for each column'); + console[ console.group ? 'group' : 'log' ]('Detecting parsers for each column'); } list = { extractors: [], @@ -308,7 +290,12 @@ p = detectParserForColumn(c, rows, -1, i); } if (c.debug) { - parsersDebug += 'column:' + i + '; extractor:' + e.id + '; parser:' + p.id + '; string:' + c.strings[i] + '; empty: ' + c.empties[i] + '\n'; + debug[ '(' + i + ') ' + h.text() ] = { + parser : p.id, + extractor : e ? e.id : 'none', + string : c.strings[i], + empty : c.empties[i] + }; } list.parsers[i] = p; list.extractors[i] = e; @@ -316,9 +303,14 @@ } j += (list.parsers.length) ? len : 1; } - if (c.debug) { - log(parsersDebug ? parsersDebug : 'No parsers detected'); - benchmark('Completed detecting parsers', time); + if ( c.debug ) { + if ( !isEmptyObject( debug ) ) { + console[ console.table ? 'table' : 'log' ]( debug ); + } else { + console.warn( ' No parsers detected!' ); + } + console.log( 'Completed detecting parsers' + ts.benchmark( time ) ); + if ( console.groupEnd ) { console.groupEnd(); } } c.parsers = list.parsers; c.extractors = list.extractors; @@ -337,7 +329,7 @@ c.totalRows = 0; // if no parsers found, return - it's an empty table. if (!parsers) { - return c.debug ? log('Warning: *Empty table!* Not building a cache') : ''; + return c.debug ? console.warn('Warning: *Empty table!* Not building a cache') : ''; } if (c.debug) { cacheTime = new Date(); @@ -390,7 +382,7 @@ for ( j = 0; j < c.columns; ++j ) { if (typeof parsers[ j ] === 'undefined') { if ( c.debug ) { - log( 'No parser found for cell:', $row[ 0 ].cells[ j ], 'does it have a header?' ); + console.warn( 'No parser found for cell:', $row[ 0 ].cells[ j ], 'does it have a header?' ); } continue; } @@ -416,7 +408,7 @@ ts.isProcessing( table ); // remove processing icon } if ( c.debug ) { - benchmark( 'Building cache for ' + totalRows + ' rows', cacheTime ); + console.log( 'Building cache for ' + totalRows + ' rows' + ts.benchmark( cacheTime ) ); } } @@ -460,7 +452,7 @@ c.appender(table, rows); } if (c.debug) { - benchmark('Rebuilt table', appendTime); + console.log( 'Rebuilt table' + ts.benchmark(appendTime) ); } // apply table widgets; but not before ajax completes if (!init && !c.appender) { ts.applyWidget(table); } @@ -500,20 +492,22 @@ // set up header template t = c.headerTemplate.replace(/\{content\}/g, $t.html()).replace(/\{icon\}/g, $t.find('.' + ts.css.icon).length ? '' : i); if (c.onRenderTemplate) { - h = c.onRenderTemplate.apply($t, [index, t]); + h = c.onRenderTemplate.apply( $t, [ index, t ] ); if (h && typeof h === 'string') { t = h; } // only change t if something is returned } $t.html('<div class="' + ts.css.headerIn + '">' + t + '</div>'); // faster than wrapInner } - if (c.onRenderHeader) { c.onRenderHeader.apply($t, [index, c, c.$table]); } + if (c.onRenderHeader) { c.onRenderHeader.apply( $t, [ index, c, c.$table ] ); } // *** remove this.column value if no conflicts found elem.column = parseInt( $t.attr('data-column'), 10); - elem.order = formatSortingOrder( ts.getData($t, ch, 'sortInitialOrder') || c.sortInitialOrder ) ? [1,0,2] : [0,1,2]; + elem.order = formatSortingOrder( ts.getData( $t, ch, 'sortInitialOrder' ) || c.sortInitialOrder ) ? + [ 1, 0, 2 ] : // desc, asc, unsorted + [ 0, 1, 2 ]; // asc, desc, unsorted elem.count = -1; // set to -1 because clicking on the header automatically adds one elem.lockedOrder = false; lock = ts.getData($t, ch, 'lockedOrder') || false; if (typeof lock !== 'undefined' && lock !== false) { - elem.order = elem.lockedOrder = formatSortingOrder(lock) ? [1,1,1] : [0,0,0]; + elem.order = elem.lockedOrder = formatSortingOrder(lock) ? [ 1, 1, 1 ] : [ 0, 0, 0 ]; } $t.addClass(ts.css.header + ' ' + c.cssHeader); // add cell to headerList @@ -539,8 +533,8 @@ // enable/disable sorting updateHeader(table); if (c.debug) { - benchmark('Built headers:', time); - log(c.$headers); + console.log( 'Built headers:' + ts.benchmark( time ) ); + console.log( c.$headers ); } } @@ -583,9 +577,9 @@ list = c.sortList, len = list.length, none = ts.css.sortNone + ' ' + c.cssNone, - css = [ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc], + css = [ ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc ], cssIcon = [ c.cssIconAsc, c.cssIconDesc, c.cssIconNone ], - aria = ['ascending', 'descending'], + aria = [ 'ascending', 'descending' ], // find the footer $t = $(table).find('tfoot tr').children() .add( $( c.namespace + '_extra_headers' ) ) @@ -655,7 +649,7 @@ dir = ('' + val[1]).match(/^(1|d|s|o|n)/); dir = dir ? dir[0] : ''; // 0/(a)sc (default), 1/(d)esc, (s)ame, (o)pposite, (n)ext - switch(dir) { + switch (dir) { case '1': case 'd': // descending dir = 1; break; @@ -731,11 +725,11 @@ // add column to sort list order = cell.order[cell.count]; if (order < 2) { - c.sortList.push([indx, order]); + c.sortList.push([ indx, order ]); // add other columns if header spans across multiple if (cell.colSpan > 1) { for (col = 1; col < cell.colSpan; col++) { - c.sortList.push([indx + col, order]); + c.sortList.push([ indx + col, order ]); } } } @@ -746,7 +740,7 @@ for (col = 0; col < c.sortAppend.length; col++) { s = ts.isValueInArray(c.sortAppend[col][0], c.sortList); if (s >= 0) { - c.sortList.splice(s,1); + c.sortList.splice(s, 1); } } } @@ -760,7 +754,7 @@ // order.count seems to be incorrect when compared to cell.count s[1] = order.order[cell.count]; if (s[1] === 2) { - c.sortList.splice(col,1); + c.sortList.splice(col, 1); order.count = -1; } } @@ -769,11 +763,11 @@ // add column to sort list array order = cell.order[cell.count]; if (order < 2) { - c.sortList.push([indx, order]); + c.sortList.push([ indx, order ]); // add other columns if header spans across multiple if (cell.colSpan > 1) { for (col = 1; col < cell.colSpan; col++) { - c.sortList.push([indx + col, order]); + c.sortList.push([ indx + col, order ]); } } } @@ -847,10 +841,10 @@ x = dir ? a : b; y = dir ? b : a; // text sort function - if (typeof(cts) === 'function') { + if (typeof cts === 'function') { // custom OVERALL text sorter sort = cts(x[col], y[col], dir, col, table); - } else if (typeof(cts) === 'object' && cts.hasOwnProperty(col)) { + } else if (typeof cts === 'object' && cts.hasOwnProperty(col)) { // custom text sorter for a SPECIFIC COLUMN sort = cts[col](x[col], y[col], dir, col, table); } else { @@ -863,7 +857,9 @@ return a[c.columns].order - b[c.columns].order; }); } - if (c.debug) { benchmark('Sorting on ' + sortList.toString() + ' and dir ' + order + ' time', sortTime); } + if (c.debug) { + console.log( 'Sorting on ' + sortList.toString() + ' and dir ' + order + ' time' + ts.benchmark(sortTime) ); + } } function resortComplete(c, callback){ @@ -883,14 +879,14 @@ // this will catch spamming of the updateCell method if (resrt !== false && !c.serverSideSorting && !c.table.isProcessing) { if (sl.length) { - c.$table.trigger('sorton', [sl, function(){ + c.$table.trigger('sorton', [ sl, function(){ resortComplete(c, callback); - }, true]); + }, true ]); } else { - c.$table.trigger('sortReset', [function(){ + c.$table.trigger('sortReset', [ function(){ resortComplete(c, callback); ts.applyWidget(c.table, false); - }]); + } ]); } } else { resortComplete(c, callback); @@ -1090,10 +1086,10 @@ ts.construct = function(settings) { return this.each(function() { var table = this, - // merge & extend config options - c = $.extend(true, {}, ts.defaults, settings, ts.instanceMethods); - // save initial settings - c.originalSettings = settings; + // merge & extend config options + c = $.extend(true, {}, ts.defaults, settings, ts.instanceMethods); + // save initial settings + c.originalSettings = settings; // create a table from data (build table widget) if (!table.hasInitialized && ts.buildTable && this.nodeName !== 'TABLE') { // return the table (in case the original target is the table's container) @@ -1107,7 +1103,14 @@ ts.setup = function(table, c) { // if no thead or tbody, or tablesorter is already present, quit if (!table || !table.tHead || table.tBodies.length === 0 || table.hasInitialized === true) { - return c.debug ? log('ERROR: stopping initialization! No table, thead, tbody or tablesorter has already been initialized') : ''; + if ( c.debug ) { + if ( table.hasInitialized ) { + console.warn( 'Stopping initialization. Tablesorter has already been initialized' ); + } else { + console.error( 'Stopping initialization! No table, thead or tbody' ); + } + } + return; } var k = '', @@ -1121,7 +1124,10 @@ table.config = c; // save the settings where they read $.data(table, 'tablesorter', c); - if (c.debug) { $.data( table, 'startoveralltimer', new Date()); } + if (c.debug) { + console[ console.group ? 'group' : 'log' ]( 'Initializing tablesorter' ); + $.data( table, 'startoveralltimer', new Date()); + } // removing this in version 3 (only supports jQuery 1.7+) c.supportsDataObject = (function(version) { @@ -1148,7 +1154,7 @@ c.namespace = '.tablesorter' + Math.random().toString(16).slice(2); } else { // make sure namespace starts with a period & doesn't have weird characters - c.namespace = '.' + c.namespace.replace(/\W/g,''); + c.namespace = '.' + c.namespace.replace(/\W/g, ''); } c.$table.children().children('tr').attr('role', 'row'); @@ -1192,7 +1198,7 @@ ts.applyWidget(table, true); // if user has supplied a sort list to constructor if (c.sortList.length > 0) { - $table.trigger('sorton', [c.sortList, {}, !c.initWidgets, true]); + $table.trigger('sorton', [ c.sortList, {}, !c.initWidgets, true ]); } else { setHeadersCss(table); if (c.initWidgets) { @@ -1220,7 +1226,8 @@ table.hasInitialized = true; table.isProcessing = false; if (c.debug) { - ts.benchmark('Overall initialization time', $.data( table, 'startoveralltimer')); + console.log( 'Overall initialization time: ' + ts.benchmark( $.data( table, 'startoveralltimer') ) ); + if ( c.debug && console.groupEnd ) { console.groupEnd(); } } $table.trigger('tablesorter-initialized', table); if (typeof c.initialized === 'function') { c.initialized(table); } @@ -1231,22 +1238,22 @@ table = $(table)[0]; var overallWidth, percent, $tbodies, len, index, c = table.config, - colgroup = c.$table.children('colgroup'); + $colgroup = c.$table.children('colgroup'); // remove plugin-added colgroup, in case we need to refresh the widths - if (colgroup.length && colgroup.hasClass(ts.css.colgroup)) { - colgroup.remove(); + if ($colgroup.length && $colgroup.hasClass(ts.css.colgroup)) { + $colgroup.remove(); } if (c.widthFixed && c.$table.children('colgroup').length === 0) { - colgroup = $('<colgroup class="' + ts.css.colgroup + '">'); + $colgroup = $('<colgroup class="' + ts.css.colgroup + '">'); overallWidth = c.$table.width(); // only add col for visible columns - fixes #371 - $tbodies = c.$tbodies.find('tr:first').children(':visible'); //.each(function() + $tbodies = c.$tbodies.find('tr:first').children(':visible'); // .each(function() len = $tbodies.length; for ( index = 0; index < len; index++ ) { percent = parseInt( ( $tbodies.eq( index ).width() / overallWidth ) * 1000, 10 ) / 10 + '%'; - colgroup.append( $('<col>').css('width', percent) ); + $colgroup.append( $('<col>').css('width', percent) ); } - c.$table.prepend(colgroup); + c.$table.prepend($colgroup); } }; @@ -1293,12 +1300,12 @@ cellId = rowIndex + '-' + $cell.index(); rowSpan = cell.rowSpan || 1; colSpan = cell.colSpan || 1; - if (typeof(matrix[rowIndex]) === 'undefined') { + if (typeof matrix[rowIndex] === 'undefined') { matrix[rowIndex] = []; } // Find first available column in the first row for (k = 0; k < matrix[rowIndex].length + 1; k++) { - if (typeof(matrix[rowIndex][k]) === 'undefined') { + if (typeof matrix[rowIndex][k] === 'undefined') { firstAvailCol = k; break; } @@ -1307,7 +1314,7 @@ // add data-column $cell.attr({ 'data-column' : firstAvailCol }); // 'data-row' : rowIndex for (k = rowIndex; k < rowIndex + rowSpan; k++) { - if (typeof(matrix[k]) === 'undefined') { + if (typeof matrix[k] === 'undefined') { matrix[k] = []; } matrixrow = matrix[k]; @@ -1322,23 +1329,23 @@ // *** Process table *** // add processing indicator - ts.isProcessing = function(table, toggle, $ths) { - table = $(table); - var c = table[0].config, + ts.isProcessing = function( $table, toggle, $ths ) { + $table = $( $table ); + var c = $table[0].config, // default to all headers - $h = $ths || table.find('.' + ts.css.header); + $h = $ths || $table.find('.' + ts.css.header); if (toggle) { // don't use sortList if custom $ths used if (typeof $ths !== 'undefined' && c.sortList.length > 0) { // get headers from the sortList $h = $h.filter(function(){ - // get data-column from attr to keep compatibility with jQuery 1.2.6 + // get data-column from attr to keep compatibility with jQuery 1.2.6 return this.sortDisabled ? false : ts.isValueInArray( parseFloat($(this).attr('data-column')), c.sortList) >= 0; }); } - table.add($h).addClass(ts.css.processing + ' ' + c.cssProcessing); + $table.add($h).addClass(ts.css.processing + ' ' + c.cssProcessing); } else { - table.add($h).removeClass(ts.css.processing + ' ' + c.cssProcessing); + $table.add($h).removeClass(ts.css.processing + ' ' + c.cssProcessing); } }; @@ -1349,11 +1356,11 @@ var holdr; if (getIt) { table.isProcessing = true; - $tb.before('<span class="tablesorter-savemyplace"/>'); + $tb.before('<colgroup class="tablesorter-savemyplace"/>'); holdr = ($.fn.detach) ? $tb.detach() : $tb.remove(); return holdr; } - holdr = $(table).find('span.tablesorter-savemyplace'); + holdr = $(table).find('colgroup.tablesorter-savemyplace'); $tb.insertAfter( holdr ); holdr.remove(); table.isProcessing = false; @@ -1463,13 +1470,14 @@ var events, $t = $(table), c = table.config, + debug = c.debug, $h = $t.find('thead:first'), $r = $h.find('tr.' + ts.css.headerRow).removeClass(ts.css.headerRow + ' ' + c.cssHeaderRow), $f = $t.find('tfoot:first > tr').children('th, td'); if (removeClasses === false && $.inArray('uitheme', c.widgets) >= 0) { // reapply uitheme classes, in case we want to maintain appearance - $t.trigger('applyWidgetId', ['uitheme']); - $t.trigger('applyWidgetId', ['zebra']); + $t.trigger('applyWidgetId', [ 'uitheme' ]); + $t.trigger('applyWidgetId', [ 'zebra' ]); } // remove widget added rows, just in case $h.find('tr').not($r).remove(); @@ -1481,7 +1489,7 @@ .removeData('tablesorter') .unbind( events.replace(/\s+/g, ' ') ); c.$headers.add($f) - .removeClass( [ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone].join(' ') ) + .removeClass( [ ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone ].join(' ') ) .removeAttr('data-column') .removeAttr('aria-label') .attr('aria-disabled', 'true'); @@ -1494,6 +1502,9 @@ if (typeof callback === 'function') { callback(table); } + if (debug) { + console.log( 'tablesorter has been removed' ); + } }; // *** sort functions *** @@ -1642,10 +1653,11 @@ }; // *** utilities *** - ts.isValueInArray = function(column, arry) { - var indx, len = arry.length; - for (indx = 0; indx < len; indx++) { - if (arry[indx][0] === column) { + ts.isValueInArray = function( column, arry ) { + var indx, + len = arry && arry.length || 0; + for ( indx = 0; indx < len; indx++ ) { + if ( arry[ indx ][ 0 ] === column ) { return indx; } } @@ -1686,9 +1698,9 @@ ts.widgets.push(widget); }; - ts.hasWidget = function(table, name){ - table = $(table); - return table.length && table[0].config && table[0].config.widgetInit[name] || false; + ts.hasWidget = function( $table, name ) { + $table = $( $table ); + return $table.length && $table[0].config && $table[0].config.widgetInit[name] || false; }; ts.getWidgetById = function(name) { @@ -1717,7 +1729,7 @@ ts.applyWidget = function(table, init, callback) { table = $(table)[0]; // in case this is called externally - var indx, len, name, + var indx, len, names, widget, name, applied, c = table.config, wo = c.widgetOptions, tableClass = ' ' + c.table.className + ' ', @@ -1728,7 +1740,7 @@ if (c.debug) { time = new Date(); } // look for widgets to apply from in table class // stop using \b otherwise this matches 'ui-widget-content' & adds 'content' widget - wd = new RegExp( '\\s' + c.widgetClass.replace( /\{name\}/i, '([\\w-]+)' )+ '\\s', 'g' ); + wd = new RegExp( '\\s' + c.widgetClass.replace( /\{name\}/i, '([\\w-]+)' ) + '\\s', 'g' ); if ( tableClass.match( wd ) ) { // extract out the widget id from the table class (widget id's can include dashes) w = tableClass.match( wd ); @@ -1745,11 +1757,11 @@ c.widgets = $.grep(c.widgets, function(v, k){ return $.inArray(v, c.widgets) === k; }); - name = c.widgets || []; - len = name.length; + names = c.widgets || []; + len = names.length; // build widget array & add priority as needed for (indx = 0; indx < len; indx++) { - wd = ts.getWidgetById(name[indx]); + wd = ts.getWidgetById(names[indx]); if (wd && wd.id) { // set priority to 10 if not defined if (!wd.priority) { wd.priority = 10; } @@ -1762,28 +1774,47 @@ }); // add/update selected widgets len = widgets.length; + if (c.debug) { + console[ console.group ? 'group' : 'log' ]( 'Start ' + ( init ? 'initializing' : 'applying' ) + ' widgets' ); + } for (indx = 0; indx < len; indx++) { - if (widgets[indx]) { - if ( init || !( c.widgetInit[ widgets[indx].id ] ) ) { + widget = widgets[indx]; + if (widget) { + name = widget.id; + applied = false; + if (c.debug) { time2 = new Date(); } + + if ( init || !( c.widgetInit[ name ] ) ) { // set init flag first to prevent calling init more than once (e.g. pager) - c.widgetInit[ widgets[indx].id ] = true; + c.widgetInit[ name ] = true; if (table.hasInitialized) { // don't reapply widget options on tablesorter init ts.applyWidgetOptions( table, c ); } - if ( 'init' in widgets[indx] ) { - if (c.debug) { time2 = new Date(); } - widgets[indx].init(table, widgets[indx], c, wo); - if (c.debug) { ts.benchmark('Initializing ' + widgets[indx].id + ' widget', time2); } + if ( 'init' in widget ) { + applied = true; + if (c.debug) { + console[ console.group ? 'group' : 'log' ]( 'Initializing ' + name + ' widget' ); + } + widget.init(table, widget, c, wo); } } - if ( !init && 'format' in widgets[indx] ) { - if (c.debug) { time2 = new Date(); } - widgets[indx].format(table, c, wo, false); - if (c.debug) { ts.benchmark( ( init ? 'Initializing ' : 'Applying ' ) + widgets[indx].id + ' widget', time2); } + if ( !init && 'format' in widget ) { + applied = true; + if (c.debug) { + console[ console.group ? 'group' : 'log' ]( 'Updating ' + name + ' widget' ); + } + widget.format(table, c, wo, false); + } + if (c.debug) { + if (applied) { + console.log( 'Completed ' + ( init ? 'initializing ' : 'applying ' ) + name + ' widget' + ts.benchmark( time2 ) ); + if ( console.groupEnd ) { console.groupEnd(); } + } } } } + if ( c.debug && console.groupEnd ) { console.groupEnd(); } // callback executed on init only if (!init && typeof callback === 'function') { callback(table); @@ -1795,7 +1826,7 @@ }, 0); if (c.debug) { w = c.widgets.length; - benchmark('Completed ' + (init === true ? 'initializing ' : 'applying ') + w + ' widget' + (w !== 1 ? 's' : ''), time); + console.log( 'Completed ' + (init === true ? 'initializing ' : 'applying ') + w + ' widget' + (w !== 1 ? 's' : '') + ts.benchmark(time) ); } }; @@ -1823,7 +1854,10 @@ widget = ts.getWidgetById(name[i]); indx = $.inArray( name[i], c.widgets ); if ( widget && 'remove' in widget ) { - if (c.debug && indx >= 0) { log( 'Removing "' + name[i] + '" widget' ); } + if (c.debug && indx >= 0) { console.log( 'Removing "' + name[i] + '" widget' ); } + if ( c.debug ) { + console.log( ( refreshing ? 'Refreshing' : 'Removing' ) + ' "' + name[i] + '" widget' ); + } widget.remove(table, c, c.widgetOptions, refreshing); c.widgetInit[ name[i] ] = false; } @@ -1871,7 +1905,11 @@ allColumns = column === 'all', data = { raw : [], parsed: [], $cell: [] }, c = table.config; - if ( !isEmptyObject( c ) ) { + if ( isEmptyObject( c ) ) { + if ( c.debug ) { + console.warn( 'No cache found - aborting getColumnText function!' ); + } + } else { tbodyLen = c.$tbodies.length; for ( tbodyIndex = 0; tbodyIndex < tbodyLen; tbodyIndex++ ) { cache = c.cache[ tbodyIndex ].normalized; @@ -1936,13 +1974,13 @@ typeof table !== 'undefined' ? table : true; if (t) { // US Format - 1,234,567.89 -> 1234567.89 - s = s.replace(/,/g,''); + s = s.replace(/,/g, ''); } else { // German Format = 1.234.567,89 -> 1234567.89 // French Format = 1 234 567,89 -> 1234567.89 - s = s.replace(/[\s|\.]/g,'').replace(/,/g,'.'); + s = s.replace(/[\s|\.]/g, '').replace(/,/g, '.'); } - if(/^\s*\([.\d]+\)/.test(s)) { + if (/^\s*\([.\d]+\)/.test(s)) { // make (#) into a negative number -> (10) = -10 s = s.replace(/^\s*\(([.\d]+)\)/, '-$1'); } @@ -1967,6 +2005,23 @@ tablesorter: ts.construct }); + // set up debug logs + if ( !( console && console.log ) ) { + ts.logs = []; + /*jshint -W020 */ + console = {}; + console.log = console.warn = console.error = console.table = function() { + ts.logs.push( [ Date.now(), arguments ] ); + }; + } + + ts.log = function(){ + console.log( arguments ); + }; + ts.benchmark = function( diff ) { + return ( ' (' + ( new Date().getTime() - diff.getTime() ) + 'ms)' ); + }; + // add default parsers ts.addParser({ id: 'no-parser', @@ -2010,7 +2065,7 @@ ts.addParser({ id: 'currency', is: function(s) { - return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test((s || '').replace(/[+\-,. ]/g,'')); // £$€¤¥¢ + return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test((s || '').replace(/[+\-,. ]/g, '')); // £$€¤¥¢ }, format: function(s, table) { var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ''), table); @@ -2085,7 +2140,7 @@ id: 'shortDate', // 'mmddyyyy', 'ddmmyyyy' or 'yyyymmdd' is: function(s) { // testing for ##-##-#### or ####-##-##, so it's not perfect; time can be included - return (/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/).test((s || '').replace(/\s+/g,' ').replace(/[\-.,]/g, '/')); + return (/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/).test((s || '').replace(/\s+/g, ' ').replace(/[\-.,]/g, '/')); }, format: function(s, table, cell, cellIndex) { if (s) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 9290ecc..27e7752 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 06-30-2015 (v2.22.2)*/ +/*! tablesorter (FORK) - updated 07-28-2015 (v2.22.4)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -18,450 +18,450 @@ /*! Widget: storage - updated 3/26/2015 (v2.21.3) */ ;(function ($, window, document) { -'use strict'; - -var ts = $.tablesorter || {}; -// *** Store data in local storage, with a cookie fallback *** -/* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json) - if you need it, then include https://github.com/douglascrockford/JSON-js - - $.parseJSON is not available is jQuery versions older than 1.4.1, using older - versions will only allow storing information for one page at a time - - // *** Save data (JSON format only) *** - // val must be valid JSON... use http://jsonlint.com/ to ensure it is valid - var val = { "mywidget" : "data1" }; // valid JSON uses double quotes - // $.tablesorter.storage(table, key, val); - $.tablesorter.storage(table, 'tablesorter-mywidget', val); - - // *** Get data: $.tablesorter.storage(table, key); *** - v = $.tablesorter.storage(table, 'tablesorter-mywidget'); - // val may be empty, so also check for your data - val = (v && v.hasOwnProperty('mywidget')) ? v.mywidget : ''; - alert(val); // "data1" if saved, or "" if not -*/ -ts.storage = function(table, key, value, options) { - table = $(table)[0]; - var cookieIndex, cookies, date, - hasStorage = false, - values = {}, - c = table.config, - wo = c && c.widgetOptions, - storageType = ( options && options.useSessionStorage ) || ( wo && wo.storage_useSessionStorage ) ? - 'sessionStorage' : 'localStorage', - $table = $(table), - // id from (1) options ID, (2) table "data-table-group" attribute, (3) widgetOptions.storage_tableId, - // (4) table ID, then (5) table index - id = options && options.id || - $table.attr( options && options.group || wo && wo.storage_group || 'data-table-group') || - wo && wo.storage_tableId || table.id || $('.tablesorter').index( $table ), - // url from (1) options url, (2) table "data-table-page" attribute, (3) widgetOptions.storage_fixedUrl, - // (4) table.config.fixedUrl (deprecated), then (5) window location path - url = options && options.url || - $table.attr(options && options.page || wo && wo.storage_page || 'data-table-page') || - wo && wo.storage_fixedUrl || c && c.fixedUrl || window.location.pathname; - // https://gist.github.com/paulirish/5558557 - if (storageType in window) { - try { - window[storageType].setItem('_tmptest', 'temp'); - hasStorage = true; - window[storageType].removeItem('_tmptest'); - } catch(error) { - if (c && c.debug) { - ts.log( storageType + ' is not supported in this browser' ); + 'use strict'; + + var ts = $.tablesorter || {}; + // *** Store data in local storage, with a cookie fallback *** + /* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json) + if you need it, then include https://github.com/douglascrockford/JSON-js + + $.parseJSON is not available is jQuery versions older than 1.4.1, using older + versions will only allow storing information for one page at a time + + // *** Save data (JSON format only) *** + // val must be valid JSON... use http://jsonlint.com/ to ensure it is valid + var val = { "mywidget" : "data1" }; // valid JSON uses double quotes + // $.tablesorter.storage(table, key, val); + $.tablesorter.storage(table, 'tablesorter-mywidget', val); + + // *** Get data: $.tablesorter.storage(table, key); *** + v = $.tablesorter.storage(table, 'tablesorter-mywidget'); + // val may be empty, so also check for your data + val = (v && v.hasOwnProperty('mywidget')) ? v.mywidget : ''; + alert(val); // 'data1' if saved, or '' if not + */ + ts.storage = function(table, key, value, options) { + table = $(table)[0]; + var cookieIndex, cookies, date, + hasStorage = false, + values = {}, + c = table.config, + wo = c && c.widgetOptions, + storageType = ( options && options.useSessionStorage ) || ( wo && wo.storage_useSessionStorage ) ? + 'sessionStorage' : 'localStorage', + $table = $(table), + // id from (1) options ID, (2) table 'data-table-group' attribute, (3) widgetOptions.storage_tableId, + // (4) table ID, then (5) table index + id = options && options.id || + $table.attr( options && options.group || wo && wo.storage_group || 'data-table-group') || + wo && wo.storage_tableId || table.id || $('.tablesorter').index( $table ), + // url from (1) options url, (2) table 'data-table-page' attribute, (3) widgetOptions.storage_fixedUrl, + // (4) table.config.fixedUrl (deprecated), then (5) window location path + url = options && options.url || + $table.attr(options && options.page || wo && wo.storage_page || 'data-table-page') || + wo && wo.storage_fixedUrl || c && c.fixedUrl || window.location.pathname; + // https://gist.github.com/paulirish/5558557 + if (storageType in window) { + try { + window[storageType].setItem('_tmptest', 'temp'); + hasStorage = true; + window[storageType].removeItem('_tmptest'); + } catch (error) { + if (c && c.debug) { + console.warn( storageType + ' is not supported in this browser' ); + } } } - } - // *** get value *** - if ($.parseJSON) { - if (hasStorage) { - values = $.parseJSON( window[storageType][key] || 'null' ) || {}; - } else { - // old browser, using cookies - cookies = document.cookie.split(/[;\s|=]/); - // add one to get from the key to the value - cookieIndex = $.inArray(key, cookies) + 1; - values = (cookieIndex !== 0) ? $.parseJSON(cookies[cookieIndex] || 'null') || {} : {}; - } - } - // allow value to be an empty string too - if ((value || value === '') && window.JSON && JSON.hasOwnProperty('stringify')) { - // add unique identifiers = url pathname > table ID/index on page > data - if (!values[url]) { - values[url] = {}; - } - values[url][id] = value; - // *** set value *** - if (hasStorage) { - window[storageType][key] = JSON.stringify(values); + // *** get value *** + if ($.parseJSON) { + if (hasStorage) { + values = $.parseJSON( window[storageType][key] || 'null' ) || {}; + } else { + // old browser, using cookies + cookies = document.cookie.split(/[;\s|=]/); + // add one to get from the key to the value + cookieIndex = $.inArray(key, cookies) + 1; + values = (cookieIndex !== 0) ? $.parseJSON(cookies[cookieIndex] || 'null') || {} : {}; + } + } + // allow value to be an empty string too + if ((value || value === '') && window.JSON && JSON.hasOwnProperty('stringify')) { + // add unique identifiers = url pathname > table ID/index on page > data + if (!values[url]) { + values[url] = {}; + } + values[url][id] = value; + // *** set value *** + if (hasStorage) { + window[storageType][key] = JSON.stringify(values); + } else { + date = new Date(); + date.setTime(date.getTime() + (31536e+6)); // 365 days + document.cookie = key + '=' + (JSON.stringify(values)).replace(/\"/g, '\"') + '; expires=' + date.toGMTString() + '; path=/'; + } } else { - date = new Date(); - date.setTime(date.getTime() + (31536e+6)); // 365 days - document.cookie = key + '=' + (JSON.stringify(values)).replace(/\"/g,'\"') + '; expires=' + date.toGMTString() + '; path=/'; + return values && values[url] ? values[url][id] : ''; } - } else { - return values && values[url] ? values[url][id] : ''; - } -}; + }; })(jQuery, window, document); /*! Widget: uitheme - updated 3/26/2015 (v2.21.3) */ ;(function ($) { -'use strict'; -var ts = $.tablesorter || {}; - -ts.themes = { - 'bootstrap' : { - table : 'table table-bordered table-striped', - caption : 'caption', - // header class names - header : 'bootstrap-header', // give the header a gradient background (theme.bootstrap_2.css) - sortNone : '', - sortAsc : '', - sortDesc : '', - active : '', // applied when column is sorted - hover : '', // custom css required - a defined bootstrap style may not override other classes - // icon class names - icons : '', // add "icon-white" to make them white; this icon class is added to the <i> in the header - iconSortNone : 'bootstrap-icon-unsorted', // class name added to icon when column is not sorted - iconSortAsc : 'icon-chevron-up glyphicon glyphicon-chevron-up', // class name added to icon when column has ascending sort - iconSortDesc : 'icon-chevron-down glyphicon glyphicon-chevron-down', // class name added to icon when column has descending sort - filterRow : '', // filter row class - footerRow : '', - footerCells : '', - even : '', // even row zebra striping - odd : '' // odd row zebra striping - }, - 'jui' : { - table : 'ui-widget ui-widget-content ui-corner-all', // table classes - caption : 'ui-widget-content', - // header class names - header : 'ui-widget-header ui-corner-all ui-state-default', // header classes - sortNone : '', - sortAsc : '', - sortDesc : '', - active : 'ui-state-active', // applied when column is sorted - hover : 'ui-state-hover', // hover class - // icon class names - icons : 'ui-icon', // icon class added to the <i> in the header - iconSortNone : 'ui-icon-carat-2-n-s', // class name added to icon when column is not sorted - iconSortAsc : 'ui-icon-carat-1-n', // class name added to icon when column has ascending sort - iconSortDesc : 'ui-icon-carat-1-s', // class name added to icon when column has descending sort - filterRow : '', - footerRow : '', - footerCells : '', - even : 'ui-widget-content', // even row zebra striping - odd : 'ui-state-default' // odd row zebra striping - } -}; - -$.extend(ts.css, { - wrapper : 'tablesorter-wrapper' // ui theme & resizable -}); - -ts.addWidget({ - id: "uitheme", - priority: 10, - format: function(table, c, wo) { - var i, hdr, icon, time, $header, $icon, $tfoot, $h, oldtheme, oldremove, oldIconRmv, hasOldTheme, - themesAll = ts.themes, - $table = c.$table.add( $( c.namespace + '_extra_table' ) ), - $headers = c.$headers.add( $( c.namespace + '_extra_headers' ) ), - theme = c.theme || 'jui', - themes = themesAll[theme] || {}, - remove = $.trim( [ themes.sortNone, themes.sortDesc, themes.sortAsc, themes.active ].join( ' ' ) ), - iconRmv = $.trim( [ themes.iconSortNone, themes.iconSortDesc, themes.iconSortAsc ].join( ' ' ) ); - if (c.debug) { time = new Date(); } - // initialization code - run once - if (!$table.hasClass('tablesorter-' + theme) || c.theme !== c.appliedTheme || !wo.uitheme_applied) { - wo.uitheme_applied = true; - oldtheme = themesAll[c.appliedTheme] || {}; - hasOldTheme = !$.isEmptyObject(oldtheme); - oldremove = hasOldTheme ? [ oldtheme.sortNone, oldtheme.sortDesc, oldtheme.sortAsc, oldtheme.active ].join( ' ' ) : ''; - oldIconRmv = hasOldTheme ? [ oldtheme.iconSortNone, oldtheme.iconSortDesc, oldtheme.iconSortAsc ].join( ' ' ) : ''; - if (hasOldTheme) { - wo.zebra[0] = $.trim( ' ' + wo.zebra[0].replace(' ' + oldtheme.even, '') ); - wo.zebra[1] = $.trim( ' ' + wo.zebra[1].replace(' ' + oldtheme.odd, '') ); - c.$tbodies.children().removeClass( [oldtheme.even, oldtheme.odd].join(' ') ); - } - // update zebra stripes - if (themes.even) { wo.zebra[0] += ' ' + themes.even; } - if (themes.odd) { wo.zebra[1] += ' ' + themes.odd; } - // add caption style - $table.children('caption') - .removeClass(oldtheme.caption || '') - .addClass(themes.caption); - // add table/footer class names - $tfoot = $table - // remove other selected themes - .removeClass( (c.appliedTheme ? 'tablesorter-' + (c.appliedTheme || '') : '') + ' ' + (oldtheme.table || '') ) - .addClass('tablesorter-' + theme + ' ' + (themes.table || '')) // add theme widget class name - .children('tfoot'); - c.appliedTheme = c.theme; - - if ($tfoot.length) { - $tfoot - // if oldtheme.footerRow or oldtheme.footerCells are undefined, all class names are removed - .children('tr').removeClass(oldtheme.footerRow || '').addClass(themes.footerRow) - .children('th, td').removeClass(oldtheme.footerCells || '').addClass(themes.footerCells); - } - // update header classes - $headers - .removeClass( (hasOldTheme ? [oldtheme.header, oldtheme.hover, oldremove].join(' ') : '') || '' ) - .addClass(themes.header) - .not('.sorter-false') - .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') - .bind('mouseenter.tsuitheme mouseleave.tsuitheme', function(event) { - // toggleClass with switch added in jQuery 1.3 - $(this)[ event.type === 'mouseenter' ? 'addClass' : 'removeClass' ](themes.hover || ''); - }); + 'use strict'; + var ts = $.tablesorter || {}; + + ts.themes = { + 'bootstrap' : { + table : 'table table-bordered table-striped', + caption : 'caption', + // header class names + header : 'bootstrap-header', // give the header a gradient background (theme.bootstrap_2.css) + sortNone : '', + sortAsc : '', + sortDesc : '', + active : '', // applied when column is sorted + hover : '', // custom css required - a defined bootstrap style may not override other classes + // icon class names + icons : '', // add 'icon-white' to make them white; this icon class is added to the <i> in the header + iconSortNone : 'bootstrap-icon-unsorted', // class name added to icon when column is not sorted + iconSortAsc : 'icon-chevron-up glyphicon glyphicon-chevron-up', // class name added to icon when column has ascending sort + iconSortDesc : 'icon-chevron-down glyphicon glyphicon-chevron-down', // class name added to icon when column has descending sort + filterRow : '', // filter row class + footerRow : '', + footerCells : '', + even : '', // even row zebra striping + odd : '' // odd row zebra striping + }, + 'jui' : { + table : 'ui-widget ui-widget-content ui-corner-all', // table classes + caption : 'ui-widget-content', + // header class names + header : 'ui-widget-header ui-corner-all ui-state-default', // header classes + sortNone : '', + sortAsc : '', + sortDesc : '', + active : 'ui-state-active', // applied when column is sorted + hover : 'ui-state-hover', // hover class + // icon class names + icons : 'ui-icon', // icon class added to the <i> in the header + iconSortNone : 'ui-icon-carat-2-n-s', // class name added to icon when column is not sorted + iconSortAsc : 'ui-icon-carat-1-n', // class name added to icon when column has ascending sort + iconSortDesc : 'ui-icon-carat-1-s', // class name added to icon when column has descending sort + filterRow : '', + footerRow : '', + footerCells : '', + even : 'ui-widget-content', // even row zebra striping + odd : 'ui-state-default' // odd row zebra striping + } + }; + + $.extend(ts.css, { + wrapper : 'tablesorter-wrapper' // ui theme & resizable + }); - $headers.each(function(){ - var $this = $(this); - if (!$this.find('.' + ts.css.wrapper).length) { - // Firefox needs this inner div to position the icon & resizer correctly - $this.wrapInner('<div class="' + ts.css.wrapper + '" style="position:relative;height:100%;width:100%"></div>'); + ts.addWidget({ + id: 'uitheme', + priority: 10, + format: function(table, c, wo) { + var i, hdr, icon, time, $header, $icon, $tfoot, $h, oldtheme, oldremove, oldIconRmv, hasOldTheme, + themesAll = ts.themes, + $table = c.$table.add( $( c.namespace + '_extra_table' ) ), + $headers = c.$headers.add( $( c.namespace + '_extra_headers' ) ), + theme = c.theme || 'jui', + themes = themesAll[theme] || {}, + remove = $.trim( [ themes.sortNone, themes.sortDesc, themes.sortAsc, themes.active ].join( ' ' ) ), + iconRmv = $.trim( [ themes.iconSortNone, themes.iconSortDesc, themes.iconSortAsc ].join( ' ' ) ); + if (c.debug) { time = new Date(); } + // initialization code - run once + if (!$table.hasClass('tablesorter-' + theme) || c.theme !== c.appliedTheme || !wo.uitheme_applied) { + wo.uitheme_applied = true; + oldtheme = themesAll[c.appliedTheme] || {}; + hasOldTheme = !$.isEmptyObject(oldtheme); + oldremove = hasOldTheme ? [ oldtheme.sortNone, oldtheme.sortDesc, oldtheme.sortAsc, oldtheme.active ].join( ' ' ) : ''; + oldIconRmv = hasOldTheme ? [ oldtheme.iconSortNone, oldtheme.iconSortDesc, oldtheme.iconSortAsc ].join( ' ' ) : ''; + if (hasOldTheme) { + wo.zebra[0] = $.trim( ' ' + wo.zebra[0].replace(' ' + oldtheme.even, '') ); + wo.zebra[1] = $.trim( ' ' + wo.zebra[1].replace(' ' + oldtheme.odd, '') ); + c.$tbodies.children().removeClass( [ oldtheme.even, oldtheme.odd ].join(' ') ); } - }); - if (c.cssIcon) { - // if c.cssIcon is '', then no <i> is added to the header + // update zebra stripes + if (themes.even) { wo.zebra[0] += ' ' + themes.even; } + if (themes.odd) { wo.zebra[1] += ' ' + themes.odd; } + // add caption style + $table.children('caption') + .removeClass(oldtheme.caption || '') + .addClass(themes.caption); + // add table/footer class names + $tfoot = $table + // remove other selected themes + .removeClass( (c.appliedTheme ? 'tablesorter-' + (c.appliedTheme || '') : '') + ' ' + (oldtheme.table || '') ) + .addClass('tablesorter-' + theme + ' ' + (themes.table || '')) // add theme widget class name + .children('tfoot'); + c.appliedTheme = c.theme; + + if ($tfoot.length) { + $tfoot + // if oldtheme.footerRow or oldtheme.footerCells are undefined, all class names are removed + .children('tr').removeClass(oldtheme.footerRow || '').addClass(themes.footerRow) + .children('th, td').removeClass(oldtheme.footerCells || '').addClass(themes.footerCells); + } + // update header classes $headers - .find('.' + ts.css.icon) - .removeClass(hasOldTheme ? [oldtheme.icons, oldIconRmv].join(' ') : '') - .addClass(themes.icons || ''); - } - if ($table.hasClass('hasFilters')) { - $table.children('thead').children('.' + ts.css.filterRow) - .removeClass(hasOldTheme ? oldtheme.filterRow || '' : '') - .addClass(themes.filterRow || ''); + .removeClass( (hasOldTheme ? [ oldtheme.header, oldtheme.hover, oldremove ].join(' ') : '') || '' ) + .addClass(themes.header) + .not('.sorter-false') + .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') + .bind('mouseenter.tsuitheme mouseleave.tsuitheme', function(event) { + // toggleClass with switch added in jQuery 1.3 + $(this)[ event.type === 'mouseenter' ? 'addClass' : 'removeClass' ](themes.hover || ''); + }); + + $headers.each(function(){ + var $this = $(this); + if (!$this.find('.' + ts.css.wrapper).length) { + // Firefox needs this inner div to position the icon & resizer correctly + $this.wrapInner('<div class="' + ts.css.wrapper + '" style="position:relative;height:100%;width:100%"></div>'); + } + }); + if (c.cssIcon) { + // if c.cssIcon is '', then no <i> is added to the header + $headers + .find('.' + ts.css.icon) + .removeClass(hasOldTheme ? [ oldtheme.icons, oldIconRmv ].join(' ') : '') + .addClass(themes.icons || ''); + } + if ($table.hasClass('hasFilters')) { + $table.children('thead').children('.' + ts.css.filterRow) + .removeClass(hasOldTheme ? oldtheme.filterRow || '' : '') + .addClass(themes.filterRow || ''); + } } - } - for (i = 0; i < c.columns; i++) { - $header = c.$headers - .add($(c.namespace + '_extra_headers')) - .not('.sorter-false') - .filter('[data-column="' + i + '"]'); - $icon = (ts.css.icon) ? $header.find('.' + ts.css.icon) : $(); - $h = $headers.not('.sorter-false').filter('[data-column="' + i + '"]:last'); - if ($h.length) { - $header.removeClass(remove); - $icon.removeClass(iconRmv); - if ($h[0].sortDisabled) { - // no sort arrows for disabled columns! - $icon.removeClass(themes.icons || ''); - } else { - hdr = themes.sortNone; - icon = themes.iconSortNone; - if ($h.hasClass(ts.css.sortAsc)) { - hdr = [themes.sortAsc, themes.active].join(' '); - icon = themes.iconSortAsc; - } else if ($h.hasClass(ts.css.sortDesc)) { - hdr = [themes.sortDesc, themes.active].join(' '); - icon = themes.iconSortDesc; + for (i = 0; i < c.columns; i++) { + $header = c.$headers + .add($(c.namespace + '_extra_headers')) + .not('.sorter-false') + .filter('[data-column="' + i + '"]'); + $icon = (ts.css.icon) ? $header.find('.' + ts.css.icon) : $(); + $h = $headers.not('.sorter-false').filter('[data-column="' + i + '"]:last'); + if ($h.length) { + $header.removeClass(remove); + $icon.removeClass(iconRmv); + if ($h[0].sortDisabled) { + // no sort arrows for disabled columns! + $icon.removeClass(themes.icons || ''); + } else { + hdr = themes.sortNone; + icon = themes.iconSortNone; + if ($h.hasClass(ts.css.sortAsc)) { + hdr = [ themes.sortAsc, themes.active ].join(' '); + icon = themes.iconSortAsc; + } else if ($h.hasClass(ts.css.sortDesc)) { + hdr = [ themes.sortDesc, themes.active ].join(' '); + icon = themes.iconSortDesc; + } + $header.addClass(hdr); + $icon.addClass(icon || ''); } - $header.addClass(hdr); - $icon.addClass(icon || ''); } } + if (c.debug) { + console.log('Applying ' + theme + ' theme' + ts.benchmark(time)); + } + }, + remove: function(table, c, wo, refreshing) { + if (!wo.uitheme_applied) { return; } + var $table = c.$table, + theme = c.appliedTheme || 'jui', + themes = ts.themes[ theme ] || ts.themes.jui, + $headers = $table.children('thead').children(), + remove = themes.sortNone + ' ' + themes.sortDesc + ' ' + themes.sortAsc, + iconRmv = themes.iconSortNone + ' ' + themes.iconSortDesc + ' ' + themes.iconSortAsc; + $table.removeClass('tablesorter-' + theme + ' ' + themes.table); + wo.uitheme_applied = false; + if (refreshing) { return; } + $table.find(ts.css.header).removeClass(themes.header); + $headers + .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') // remove hover + .removeClass(themes.hover + ' ' + remove + ' ' + themes.active) + .filter('.' + ts.css.filterRow) + .removeClass(themes.filterRow); + $headers.find('.' + ts.css.icon).removeClass(themes.icons + ' ' + iconRmv); } - if (c.debug) { - ts.benchmark("Applying " + theme + " theme", time); - } - }, - remove: function(table, c, wo, refreshing) { - if (!wo.uitheme_applied) { return; } - var $table = c.$table, - theme = c.appliedTheme || 'jui', - themes = ts.themes[ theme ] || ts.themes.jui, - $headers = $table.children('thead').children(), - remove = themes.sortNone + ' ' + themes.sortDesc + ' ' + themes.sortAsc, - iconRmv = themes.iconSortNone + ' ' + themes.iconSortDesc + ' ' + themes.iconSortAsc; - $table.removeClass('tablesorter-' + theme + ' ' + themes.table); - wo.uitheme_applied = false; - if (refreshing) { return; } - $table.find(ts.css.header).removeClass(themes.header); - $headers - .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') // remove hover - .removeClass(themes.hover + ' ' + remove + ' ' + themes.active) - .filter('.' + ts.css.filterRow) - .removeClass(themes.filterRow); - $headers.find('.' + ts.css.icon).removeClass(themes.icons + ' ' + iconRmv); - } -}); + }); })(jQuery); /*! Widget: columns */ ;(function ($) { -'use strict'; -var ts = $.tablesorter || {}; - -ts.addWidget({ - id: "columns", - priority: 30, - options : { - columns : [ "primary", "secondary", "tertiary" ] - }, - format: function(table, c, wo) { - var $tbody, tbodyIndex, $rows, rows, $row, $cells, remove, indx, + 'use strict'; + var ts = $.tablesorter || {}; + + ts.addWidget({ + id: 'columns', + priority: 30, + options : { + columns : [ 'primary', 'secondary', 'tertiary' ] + }, + format: function(table, c, wo) { + var $tbody, tbodyIndex, $rows, rows, $row, $cells, remove, indx, $table = c.$table, $tbodies = c.$tbodies, sortList = c.sortList, len = sortList.length, // removed c.widgetColumns support - css = wo && wo.columns || [ "primary", "secondary", "tertiary" ], + css = wo && wo.columns || [ 'primary', 'secondary', 'tertiary' ], last = css.length - 1; remove = css.join(' '); - // check if there is a sort (on initialization there may not be one) - for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { - $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // detach tbody - $rows = $tbody.children('tr'); - // loop through the visible rows - $rows.each(function() { - $row = $(this); - if (this.style.display !== 'none') { - // remove all columns class names - $cells = $row.children().removeClass(remove); - // add appropriate column class names - if (sortList && sortList[0]) { - // primary sort column class - $cells.eq(sortList[0][0]).addClass(css[0]); - if (len > 1) { - for (indx = 1; indx < len; indx++) { - // secondary, tertiary, etc sort column classes - $cells.eq(sortList[indx][0]).addClass( css[indx] || css[last] ); + // check if there is a sort (on initialization there may not be one) + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // detach tbody + $rows = $tbody.children('tr'); + // loop through the visible rows + $rows.each(function() { + $row = $(this); + if (this.style.display !== 'none') { + // remove all columns class names + $cells = $row.children().removeClass(remove); + // add appropriate column class names + if (sortList && sortList[0]) { + // primary sort column class + $cells.eq(sortList[0][0]).addClass(css[0]); + if (len > 1) { + for (indx = 1; indx < len; indx++) { + // secondary, tertiary, etc sort column classes + $cells.eq(sortList[indx][0]).addClass( css[indx] || css[last] ); + } } } } - } - }); - ts.processTbody(table, $tbody, false); - } - // add classes to thead and tfoot - rows = wo.columns_thead !== false ? ['thead tr'] : []; - if (wo.columns_tfoot !== false) { - rows.push('tfoot tr'); - } - if (rows.length) { - $rows = $table.find( rows.join(',') ).children().removeClass(remove); - if (len) { - for (indx = 0; indx < len; indx++) { - // add primary. secondary, tertiary, etc sort column classes - $rows.filter('[data-column="' + sortList[indx][0] + '"]').addClass(css[indx] || css[last]); + }); + ts.processTbody(table, $tbody, false); + } + // add classes to thead and tfoot + rows = wo.columns_thead !== false ? [ 'thead tr' ] : []; + if (wo.columns_tfoot !== false) { + rows.push('tfoot tr'); + } + if (rows.length) { + $rows = $table.find( rows.join(',') ).children().removeClass(remove); + if (len) { + for (indx = 0; indx < len; indx++) { + // add primary. secondary, tertiary, etc sort column classes + $rows.filter('[data-column="' + sortList[indx][0] + '"]').addClass(css[indx] || css[last]); + } } } + }, + remove: function(table, c, wo) { + var tbodyIndex, $tbody, + $tbodies = c.$tbodies, + remove = (wo.columns || [ 'primary', 'secondary', 'tertiary' ]).join(' '); + c.$headers.removeClass(remove); + c.$table.children('tfoot').children('tr').children('th, td').removeClass(remove); + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody + $tbody.children('tr').each(function() { + $(this).children().removeClass(remove); + }); + ts.processTbody(table, $tbody, false); // restore tbody + } } - }, - remove: function(table, c, wo) { - var tbodyIndex, $tbody, - $tbodies = c.$tbodies, - remove = (wo.columns || [ "primary", "secondary", "tertiary" ]).join(' '); - c.$headers.removeClass(remove); - c.$table.children('tfoot').children('tr').children('th, td').removeClass(remove); - for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { - $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody - $tbody.children('tr').each(function() { - $(this).children().removeClass(remove); - }); - ts.processTbody(table, $tbody, false); // restore tbody - } - } -}); + }); })(jQuery); -/*! Widget: filter - updated 5/17/2015 (v2.22.1) *//* +/*! Widget: filter - updated 7/28/2015 (v2.22.4) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ ;( function ( $ ) { -'use strict'; -var ts = $.tablesorter || {}, + 'use strict'; + var ts = $.tablesorter || {}, tscss = ts.css; -$.extend( tscss, { - filterRow : 'tablesorter-filter-row', - filter : 'tablesorter-filter', - filterDisabled : 'disabled', - filterRowHide : 'hideme' -}); - -ts.addWidget({ - id: 'filter', - priority: 50, - options : { - filter_childRows : false, // if true, filter includes child row content in the search - filter_childByColumn : false, // ( filter_childRows must be true ) if true = search child rows by column; false = search all child row text grouped - filter_columnFilters : true, // if true, a filter will be added to the top of each table column - filter_columnAnyMatch: true, // if true, allows using '#:{query}' in AnyMatch searches ( column:query ) - filter_cellFilter : '', // css class name added to the filter cell ( string or array ) - filter_cssFilter : '', // css class name added to the filter row & each input in the row ( tablesorter-filter is ALWAYS added ) - filter_defaultFilter : {}, // add a default column filter type '~{query}' to make fuzzy searches default; '{q1} AND {q2}' to make all searches use a logical AND. - filter_excludeFilter : {}, // filters to exclude, per column - filter_external : '', // jQuery selector string ( or jQuery object ) of external filters - filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin - filter_formatter : null, // add custom filter elements to the filter row - filter_functions : null, // add custom filter functions using this option - filter_hideEmpty : true, // hide filter row when table is empty - filter_hideFilters : false, // collapse filter row when mouse leaves the area - filter_ignoreCase : true, // if true, make all searches case-insensitive - filter_liveSearch : true, // if true, search column content while the user types ( with a delay ) - filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available ( visible ) options within the drop down - filter_placeholder : { search : '', select : '' }, // default placeholder text ( overridden by any header 'data-placeholder' setting ) - filter_reset : null, // jQuery selector string of an element used to reset the filters - filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters - filter_searchDelay : 300, // typing delay in milliseconds before starting a search - filter_searchFiltered: true, // allow searching through already filtered rows in special circumstances; will speed up searching in large tables if true - filter_selectSource : null, // include a function to return an array of values to be added to the column filter select - filter_startsWith : false, // if true, filter start from the beginning of the cell contents - filter_useParsedData : false, // filter all data using parsed content - filter_serversideFiltering : false, // if true, server-side filtering should be performed because client-side filtering will be disabled, but the ui and events will still be used. - filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value - filter_selectSourceSeparator : '|' // filter_selectSource array text left of the separator is added to the option value, right into the option text - }, - format: function( table, c, wo ) { - if ( !c.$table.hasClass( 'hasFilters' ) ) { - ts.filter.init( table, c, wo ); - } - }, - remove: function( table, c, wo, refreshing ) { - var tbodyIndex, $tbody, - $table = c.$table, - $tbodies = c.$tbodies, - events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' - .split( ' ' ).join( c.namespace + 'filter ' ); - $table - .removeClass( 'hasFilters' ) - // add .tsfilter namespace to all BUT search - .unbind( events.replace( /\s+/g, ' ' ) ) - // remove the filter row even if refreshing, because the column might have been moved - .find( '.' + tscss.filterRow ).remove(); - if ( refreshing ) { return; } - for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { - $tbody = ts.processTbody( table, $tbodies.eq( tbodyIndex ), true ); // remove tbody - $tbody.children().removeClass( wo.filter_filteredRow ).show(); - ts.processTbody( table, $tbody, false ); // restore tbody - } - if ( wo.filter_reset ) { - $( document ).undelegate( wo.filter_reset, 'click.tsfilter' ); + $.extend( tscss, { + filterRow : 'tablesorter-filter-row', + filter : 'tablesorter-filter', + filterDisabled : 'disabled', + filterRowHide : 'hideme' + }); + + ts.addWidget({ + id: 'filter', + priority: 50, + options : { + filter_childRows : false, // if true, filter includes child row content in the search + filter_childByColumn : false, // ( filter_childRows must be true ) if true = search child rows by column; false = search all child row text grouped + filter_columnFilters : true, // if true, a filter will be added to the top of each table column + filter_columnAnyMatch: true, // if true, allows using '#:{query}' in AnyMatch searches ( column:query ) + filter_cellFilter : '', // css class name added to the filter cell ( string or array ) + filter_cssFilter : '', // css class name added to the filter row & each input in the row ( tablesorter-filter is ALWAYS added ) + filter_defaultFilter : {}, // add a default column filter type '~{query}' to make fuzzy searches default; '{q1} AND {q2}' to make all searches use a logical AND. + filter_excludeFilter : {}, // filters to exclude, per column + filter_external : '', // jQuery selector string ( or jQuery object ) of external filters + filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin + filter_formatter : null, // add custom filter elements to the filter row + filter_functions : null, // add custom filter functions using this option + filter_hideEmpty : true, // hide filter row when table is empty + filter_hideFilters : false, // collapse filter row when mouse leaves the area + filter_ignoreCase : true, // if true, make all searches case-insensitive + filter_liveSearch : true, // if true, search column content while the user types ( with a delay ) + filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available ( visible ) options within the drop down + filter_placeholder : { search : '', select : '' }, // default placeholder text ( overridden by any header 'data-placeholder' setting ) + filter_reset : null, // jQuery selector string of an element used to reset the filters + filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters + filter_searchDelay : 300, // typing delay in milliseconds before starting a search + filter_searchFiltered: true, // allow searching through already filtered rows in special circumstances; will speed up searching in large tables if true + filter_selectSource : null, // include a function to return an array of values to be added to the column filter select + filter_startsWith : false, // if true, filter start from the beginning of the cell contents + filter_useParsedData : false, // filter all data using parsed content + filter_serversideFiltering : false, // if true, must perform server-side filtering b/c client-side filtering is disabled, but the ui and events will still be used. + filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value + filter_selectSourceSeparator : '|' // filter_selectSource array text left of the separator is added to the option value, right into the option text + }, + format: function( table, c, wo ) { + if ( !c.$table.hasClass( 'hasFilters' ) ) { + ts.filter.init( table, c, wo ); + } + }, + remove: function( table, c, wo, refreshing ) { + var tbodyIndex, $tbody, + $table = c.$table, + $tbodies = c.$tbodies, + events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' + .split( ' ' ).join( c.namespace + 'filter ' ); + $table + .removeClass( 'hasFilters' ) + // add .tsfilter namespace to all BUT search + .unbind( events.replace( /\s+/g, ' ' ) ) + // remove the filter row even if refreshing, because the column might have been moved + .find( '.' + tscss.filterRow ).remove(); + if ( refreshing ) { return; } + for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody( table, $tbodies.eq( tbodyIndex ), true ); // remove tbody + $tbody.children().removeClass( wo.filter_filteredRow ).show(); + ts.processTbody( table, $tbody, false ); // restore tbody + } + if ( wo.filter_reset ) { + $( document ).undelegate( wo.filter_reset, 'click.tsfilter' ); + } } - } -}); - -ts.filter = { - - // regex used in filter 'check' functions - not for general use and not documented - regex: { - regex : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // regex to test for regex - child : /tablesorter-childRow/, // child row class name; this gets updated in the script - filtered : /filtered/, // filtered (hidden) row class name; updated in the script - type : /undefined|number/, // check type - exact : /(^[\"\'=]+)|([\"\'=]+$)/g, // exact match (allow '==') - nondigit : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser) - operators : /[<>=]/g, // replace operators - query : '(q|query)' // replace filter queries - }, + }); + + ts.filter = { + + // regex used in filter 'check' functions - not for general use and not documented + regex: { + regex : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // regex to test for regex + child : /tablesorter-childRow/, // child row class name; this gets updated in the script + filtered : /filtered/, // filtered (hidden) row class name; updated in the script + type : /undefined|number/, // check type + exact : /(^[\"\'=]+)|([\"\'=]+$)/g, // exact match (allow '==') + nondigit : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser) + operators : /[<>=]/g, // replace operators + query : '(q|query)' // replace filter queries + }, // function( c, data ) { } // c = table.config // data.$row = jQuery object of the row currently being processed @@ -475,1508 +475,1523 @@ ts.filter = { // data.cacheArray = An array of parsed content from each table cell in the row being processed // data.index = column index; table = table element ( DOM ) // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class ) - types: { - or : function( c, data, vars ) { - if ( /\|/.test( data.iFilter ) || ts.filter.regex.orSplit.test( data.filter ) ) { - var indx, filterMatched, txt, query, regex, - // duplicate data but split filter - data2 = $.extend( {}, data ), - index = data.index, - parsed = data.parsed[ index ], - filter = data.filter.split( ts.filter.regex.orSplit ), - iFilter = data.iFilter.split( ts.filter.regex.orSplit ), - len = filter.length; - for ( indx = 0; indx < len; indx++ ) { - data2.nestedFilters = true; - data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); - data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); - query = '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')'; - regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); - // filterMatched = data2.filter === '' && indx > 0 ? true - // look for an exact match with the 'or' unless the 'filter-match' class is found - filterMatched = regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ); - if ( filterMatched ) { - return filterMatched; + types: { + or : function( c, data, vars ) { + if ( /\|/.test( data.iFilter ) || ts.filter.regex.orSplit.test( data.filter ) ) { + var indx, filterMatched, txt, query, regex, + // duplicate data but split filter + data2 = $.extend( {}, data ), + index = data.index, + parsed = data.parsed[ index ], + filter = data.filter.split( ts.filter.regex.orSplit ), + iFilter = data.iFilter.split( ts.filter.regex.orSplit ), + len = filter.length; + for ( indx = 0; indx < len; indx++ ) { + data2.nestedFilters = true; + data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); + data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); + query = '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')'; + try { + // use try/catch, because query may not be a valid regex if "|" is contained within a partial regex search, + // e.g "/(Alex|Aar" -> Uncaught SyntaxError: Invalid regular expression: /(/(Alex)/: Unterminated group + regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); + // filterMatched = data2.filter === '' && indx > 0 ? true + // look for an exact match with the 'or' unless the 'filter-match' class is found + filterMatched = regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ); + if ( filterMatched ) { + return filterMatched; + } + } catch ( error ) { + return null; + } } + // may be null from processing types + return filterMatched || false; } - // may be null from processing types - return filterMatched || false; - } - return null; - }, - // Look for an AND or && operator ( logical and ) - and : function( c, data, vars ) { - if ( ts.filter.regex.andTest.test( data.filter ) ) { - var indx, filterMatched, result, txt, query, regex, - // duplicate data but split filter - data2 = $.extend( {}, data ), - index = data.index, - parsed = data.parsed[ index ], - filter = data.filter.split( ts.filter.regex.andSplit ), - iFilter = data.iFilter.split( ts.filter.regex.andSplit ), - len = filter.length; - for ( indx = 0; indx < len; indx++ ) { - data2.nestedFilters = true; - data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); - data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); - query = ( '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' ) - // replace wild cards since /(a*)/i will match anything - .replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ); - regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); - // look for an exact match with the 'and' unless the 'filter-match' class is found - result = ( regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ) ); - if ( indx === 0 ) { - filterMatched = result; - } else { - filterMatched = filterMatched && result; + return null; + }, + // Look for an AND or && operator ( logical and ) + and : function( c, data, vars ) { + if ( ts.filter.regex.andTest.test( data.filter ) ) { + var indx, filterMatched, result, txt, query, regex, + // duplicate data but split filter + data2 = $.extend( {}, data ), + index = data.index, + parsed = data.parsed[ index ], + filter = data.filter.split( ts.filter.regex.andSplit ), + iFilter = data.iFilter.split( ts.filter.regex.andSplit ), + len = filter.length; + for ( indx = 0; indx < len; indx++ ) { + data2.nestedFilters = true; + data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); + data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); + query = ( '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' ) + // replace wild cards since /(a*)/i will match anything + .replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ); + try { + // use try/catch just in case RegExp is invalid + regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); + // look for an exact match with the 'and' unless the 'filter-match' class is found + result = ( regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ) ); + if ( indx === 0 ) { + filterMatched = result; + } else { + filterMatched = filterMatched && result; + } + } catch ( error ) { + return null; + } } + // may be null from processing types + return filterMatched || false; } - // may be null from processing types - return filterMatched || false; - } - return null; - }, - // Look for regex - regex: function( c, data ) { - if ( ts.filter.regex.regex.test( data.filter ) ) { - var matches, - // cache regex per column for optimal speed - regex = data.filter_regexCache[ data.index ] || ts.filter.regex.regex.exec( data.filter ), - isRegex = regex instanceof RegExp; - try { - if ( !isRegex ) { - // force case insensitive search if ignoreCase option set? - // if ( c.ignoreCase && !regex[2] ) { regex[2] = 'i'; } - data.filter_regexCache[ data.index ] = regex = new RegExp( regex[1], regex[2] ); + return null; + }, + // Look for regex + regex: function( c, data ) { + if ( ts.filter.regex.regex.test( data.filter ) ) { + var matches, + // cache regex per column for optimal speed + regex = data.filter_regexCache[ data.index ] || ts.filter.regex.regex.exec( data.filter ), + isRegex = regex instanceof RegExp; + try { + if ( !isRegex ) { + // force case insensitive search if ignoreCase option set? + // if ( c.ignoreCase && !regex[2] ) { regex[2] = 'i'; } + data.filter_regexCache[ data.index ] = regex = new RegExp( regex[1], regex[2] ); + } + matches = regex.test( data.exact ); + } catch ( error ) { + matches = false; } - matches = regex.test( data.exact ); - } catch ( error ) { - matches = false; + return matches; } - return matches; - } - return null; - }, - // Look for operators >, >=, < or <= - operators: function( c, data ) { - // ignore empty strings... because '' < 10 is true - if ( /^[<>]=?/.test( data.iFilter ) && data.iExact !== '' ) { - var cachedValue, result, txt, - table = c.table, - index = data.index, - parsed = data.parsed[index], - query = ts.formatFloat( data.iFilter.replace( ts.filter.regex.operators, '' ), table ), - parser = c.parsers[index], - savedSearch = query; - // parse filter value in case we're comparing numbers ( dates ) - if ( parsed || parser.type === 'numeric' ) { - txt = $.trim( '' + data.iFilter.replace( ts.filter.regex.operators, '' ) ); - result = ts.filter.parseFilter( c, txt, index, true ); - query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query; - } - // iExact may be numeric - see issue #149; - // check if cached is defined, because sometimes j goes out of range? ( numeric columns ) - if ( ( parsed || parser.type === 'numeric' ) && !isNaN( query ) && - typeof data.cache !== 'undefined' ) { - cachedValue = data.cache; - } else { - txt = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; - cachedValue = ts.formatFloat( txt, table ); + return null; + }, + // Look for operators >, >=, < or <= + operators: function( c, data ) { + // ignore empty strings... because '' < 10 is true + if ( /^[<>]=?/.test( data.iFilter ) && data.iExact !== '' ) { + var cachedValue, result, txt, + table = c.table, + index = data.index, + parsed = data.parsed[index], + query = ts.formatFloat( data.iFilter.replace( ts.filter.regex.operators, '' ), table ), + parser = c.parsers[index], + savedSearch = query; + // parse filter value in case we're comparing numbers ( dates ) + if ( parsed || parser.type === 'numeric' ) { + txt = $.trim( '' + data.iFilter.replace( ts.filter.regex.operators, '' ) ); + result = ts.filter.parseFilter( c, txt, index, true ); + query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query; + } + // iExact may be numeric - see issue #149; + // check if cached is defined, because sometimes j goes out of range? ( numeric columns ) + if ( ( parsed || parser.type === 'numeric' ) && !isNaN( query ) && + typeof data.cache !== 'undefined' ) { + cachedValue = data.cache; + } else { + txt = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; + cachedValue = ts.formatFloat( txt, table ); + } + if ( />/.test( data.iFilter ) ) { + result = />=/.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; + } else if ( /</.test( data.iFilter ) ) { + result = /<=/.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; + } + // keep showing all rows if nothing follows the operator + if ( !result && savedSearch === '' ) { + result = true; + } + return result; } - if ( />/.test( data.iFilter ) ) { - result = />=/.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; - } else if ( /</.test( data.iFilter ) ) { - result = /<=/.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; + return null; + }, + // Look for a not match + notMatch: function( c, data ) { + if ( /^\!/.test( data.iFilter ) ) { + var indx, + txt = data.iFilter.replace( '!', '' ), + filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + if ( ts.filter.regex.exact.test( filter ) ) { + // look for exact not matches - see #628 + filter = filter.replace( ts.filter.regex.exact, '' ); + return filter === '' ? true : $.trim( filter ) !== data.iExact; + } else { + indx = data.iExact.search( $.trim( filter ) ); + return filter === '' ? true : !( c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0 ); + } } - // keep showing all rows if nothing follows the operator - if ( !result && savedSearch === '' ) { - result = true; + return null; + }, + // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric + exact: function( c, data ) { + /*jshint eqeqeq:false */ + if ( ts.filter.regex.exact.test( data.iFilter ) ) { + var txt = data.iFilter.replace( ts.filter.regex.exact, '' ), + filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact; } - return result; - } - return null; - }, - // Look for a not match - notMatch: function( c, data ) { - if ( /^\!/.test( data.iFilter ) ) { - var indx, - txt = data.iFilter.replace( '!', '' ), - filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; - if ( ts.filter.regex.exact.test( filter ) ) { - // look for exact not matches - see #628 - filter = filter.replace( ts.filter.regex.exact, '' ); - return filter === '' ? true : $.trim( filter ) !== data.iExact; - } else { - indx = data.iExact.search( $.trim( filter ) ); - return filter === '' ? true : !( c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0 ); + return null; + }, + // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu! + range : function( c, data ) { + if ( ts.filter.regex.toTest.test( data.iFilter ) ) { + var result, tmp, range1, range2, + table = c.table, + index = data.index, + parsed = data.parsed[index], + // make sure the dash is for a range and not indicating a negative number + query = data.iFilter.split( ts.filter.regex.toSplit ); + + tmp = query[0].replace( ts.filter.regex.nondigit, '' ) || ''; + range1 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); + tmp = query[1].replace( ts.filter.regex.nondigit, '' ) || ''; + range2 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); + // parse filter value in case we're comparing numbers ( dates ) + if ( parsed || c.parsers[index].type === 'numeric' ) { + result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index ); + range1 = ( result !== '' && !isNaN( result ) ) ? result : range1; + result = c.parsers[ index ].format( '' + query[1], table, c.$headers.eq( index ), index ); + range2 = ( result !== '' && !isNaN( result ) ) ? result : range2; + } + if ( ( parsed || c.parsers[ index ].type === 'numeric' ) && !isNaN( range1 ) && !isNaN( range2 ) ) { + result = data.cache; + } else { + tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; + result = ts.formatFloat( tmp, table ); + } + if ( range1 > range2 ) { + tmp = range1; range1 = range2; range2 = tmp; // swap + } + return ( result >= range1 && result <= range2 ) || ( range1 === '' || range2 === '' ); } - } - return null; - }, - // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric - exact: function( c, data ) { - /*jshint eqeqeq:false */ - if ( ts.filter.regex.exact.test( data.iFilter ) ) { - var txt = data.iFilter.replace( ts.filter.regex.exact, '' ), - filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; - return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact; - } - return null; - }, - // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu! - range : function( c, data ) { - if ( ts.filter.regex.toTest.test( data.iFilter ) ) { - var result, tmp, range1, range2, - table = c.table, - index = data.index, - parsed = data.parsed[index], - // make sure the dash is for a range and not indicating a negative number - query = data.iFilter.split( ts.filter.regex.toSplit ); - - tmp = query[0].replace( ts.filter.regex.nondigit, '' ) || ''; - range1 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); - tmp = query[1].replace( ts.filter.regex.nondigit, '' ) || ''; - range2 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); - // parse filter value in case we're comparing numbers ( dates ) - if ( parsed || c.parsers[index].type === 'numeric' ) { - result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index ); - range1 = ( result !== '' && !isNaN( result ) ) ? result : range1; - result = c.parsers[ index ].format( '' + query[1], table, c.$headers.eq( index ), index ); - range2 = ( result !== '' && !isNaN( result ) ) ? result : range2; - } - if ( ( parsed || c.parsers[ index ].type === 'numeric' ) && !isNaN( range1 ) && !isNaN( range2 ) ) { - result = data.cache; - } else { - tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; - result = ts.formatFloat( tmp, table ); + return null; + }, + // Look for wild card: ? = single, * = multiple, or | = logical OR + wild : function( c, data ) { + if ( /[\?\*\|]/.test( data.iFilter ) ) { + var index = data.index, + parsed = data.parsed[ index ], + query = '' + ( ts.filter.parseFilter( c, data.iFilter, index, parsed ) || '' ); + // look for an exact match with the 'or' unless the 'filter-match' class is found + if ( !/\?\*/.test( query ) && data.nestedFilters ) { + query = data.isMatch ? query : '^(' + query + ')$'; + } + // parsing the filter may not work properly when using wildcards =/ + try { + return new RegExp( + query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ), + c.widgetOptions.filter_ignoreCase ? 'i' : '' + ) + .test( data.exact ); + } catch ( error ) { + return null; + } } - if ( range1 > range2 ) { - tmp = range1; range1 = range2; range2 = tmp; // swap + return null; + }, + // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license ) + fuzzy: function( c, data ) { + if ( /^~/.test( data.iFilter ) ) { + var indx, + patternIndx = 0, + len = data.iExact.length, + txt = data.iFilter.slice( 1 ), + pattern = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + for ( indx = 0; indx < len; indx++ ) { + if ( data.iExact[ indx ] === pattern[ patternIndx ] ) { + patternIndx += 1; + } + } + if ( patternIndx === pattern.length ) { + return true; + } + return false; } - return ( result >= range1 && result <= range2 ) || ( range1 === '' || range2 === '' ); + return null; } - return null; }, - // Look for wild card: ? = single, * = multiple, or | = logical OR - wild : function( c, data ) { - if ( /[\?\*\|]/.test( data.iFilter ) ) { - var index = data.index, - parsed = data.parsed[ index ], - query = '' + ( ts.filter.parseFilter( c, data.iFilter, index, parsed ) || '' ); - // look for an exact match with the 'or' unless the 'filter-match' class is found - if ( !/\?\*/.test( query ) && data.nestedFilters ) { - query = data.isMatch ? query : '^(' + query + ')$'; - } - // parsing the filter may not work properly when using wildcards =/ - return new RegExp( - query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ), - c.widgetOptions.filter_ignoreCase ? 'i' : '' - ) - .test( data.exact ); - } - return null; - }, - // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license ) - fuzzy: function( c, data ) { - if ( /^~/.test( data.iFilter ) ) { - var indx, - patternIndx = 0, - len = data.iExact.length, - txt = data.iFilter.slice( 1 ), - pattern = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; - for ( indx = 0; indx < len; indx++ ) { - if ( data.iExact[ indx ] === pattern[ patternIndx ] ) { - patternIndx += 1; - } + init: function( table, c, wo ) { + // filter language options + ts.language = $.extend( true, {}, { + to : 'to', + or : 'or', + and : 'and' + }, ts.language ); + + var options, string, txt, $header, column, filters, val, fxn, noSelect, + regex = ts.filter.regex; + c.$table.addClass( 'hasFilters' ); + + // define timers so using clearTimeout won't cause an undefined error + wo.searchTimer = null; + wo.filter_initTimer = null; + wo.filter_formatterCount = 0; + wo.filter_formatterInit = []; + wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; + wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; + + val = '\\{' + ts.filter.regex.query + '\\}'; + $.extend( regex, { + child : new RegExp( c.cssChildRow ), + filtered : new RegExp( wo.filter_filteredRow ), + alreadyFiltered : new RegExp( '(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i' ), + toTest : new RegExp( '\\s+(-|' + ts.language.to + ')\\s+', 'i' ), + toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)', 'gi' ), + andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), + andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), + orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ), + iQuery : new RegExp( val, 'i' ), + igQuery : new RegExp( val, 'ig' ) + }); + + // don't build filter row if columnFilters is false or all columns are set to 'filter-false' + // see issue #156 + val = c.$headers.filter( '.filter-false, .parser-false' ).length; + if ( wo.filter_columnFilters !== false && val !== c.$headers.length ) { + // build filter row + ts.filter.buildRow( table, c, wo ); + } + + txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' + .split( ' ' ).join( c.namespace + 'filter ' ); + c.$table.bind( txt, function( event, filter ) { + val = wo.filter_hideEmpty && + $.isEmptyObject( c.cache ) && + !( c.delayInit && event.type === 'appendCache' ); + // hide filter row using the 'filtered' class name + c.$table.find( '.' + tscss.filterRow ).toggleClass( wo.filter_filteredRow, val ); // fixes #450 + if ( !/(search|filter)/.test( event.type ) ) { + event.stopPropagation(); + ts.filter.buildDefault( table, true ); } - if ( patternIndx === pattern.length ) { - return true; + if ( event.type === 'filterReset' ) { + c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' ); + ts.filter.searching( table, [] ); + } else if ( event.type === 'filterEnd' ) { + ts.filter.buildDefault( table, true ); + } else { + // send false argument to force a new search; otherwise if the filter hasn't changed, + // it will return + filter = event.type === 'search' ? filter : + event.type === 'updateComplete' ? c.$table.data( 'lastSearch' ) : ''; + if ( /(update|add)/.test( event.type ) && event.type !== 'updateComplete' ) { + // force a new search since content has changed + c.lastCombinedFilter = null; + c.lastSearch = []; + } + // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first + // input ensures all inputs are updated when a search is triggered on the table + // $( 'table' ).trigger( 'search', [...] ); + ts.filter.searching( table, filter, true ); } return false; - } - return null; - } - }, - init: function( table, c, wo ) { - // filter language options - ts.language = $.extend( true, {}, { - to : 'to', - or : 'or', - and : 'and' - }, ts.language ); - - var options, string, txt, $header, column, filters, val, fxn, noSelect, - regex = ts.filter.regex; - c.$table.addClass( 'hasFilters' ); - - // define timers so using clearTimeout won't cause an undefined error - wo.searchTimer = null; - wo.filter_initTimer = null; - wo.filter_formatterCount = 0; - wo.filter_formatterInit = []; - wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; - wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; - - val = '\\{' + ts.filter.regex.query + '\\}'; - $.extend( regex, { - child : new RegExp( c.cssChildRow ), - filtered : new RegExp( wo.filter_filteredRow ), - alreadyFiltered : new RegExp( '(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i' ), - toTest : new RegExp( '\\s+(-|' + ts.language.to + ')\\s+', 'i' ), - toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)' ,'gi' ), - andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), - andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), - orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ), - iQuery : new RegExp( val, 'i' ), - igQuery : new RegExp( val, 'ig' ) - }); - - // don't build filter row if columnFilters is false or all columns are set to 'filter-false' - // see issue #156 - val = c.$headers.filter( '.filter-false, .parser-false' ).length; - if ( wo.filter_columnFilters !== false && val !== c.$headers.length ) { - // build filter row - ts.filter.buildRow( table, c, wo ); - } - - txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' - .split( ' ' ).join( c.namespace + 'filter ' ); - c.$table.bind( txt, function( event, filter ) { - val = wo.filter_hideEmpty && - $.isEmptyObject( c.cache ) && - !( c.delayInit && event.type === 'appendCache' ); - // hide filter row using the 'filtered' class name - c.$table.find( '.' + tscss.filterRow ).toggleClass( wo.filter_filteredRow, val ); // fixes #450 - if ( !/(search|filter)/.test( event.type ) ) { - event.stopPropagation(); - ts.filter.buildDefault( table, true ); - } - if ( event.type === 'filterReset' ) { - c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' ); - ts.filter.searching( table, [] ); - } else if ( event.type === 'filterEnd' ) { - ts.filter.buildDefault( table, true ); - } else { - // send false argument to force a new search; otherwise if the filter hasn't changed, - // it will return - filter = event.type === 'search' ? filter : - event.type === 'updateComplete' ? c.$table.data( 'lastSearch' ) : ''; - if ( /(update|add)/.test( event.type ) && event.type !== 'updateComplete' ) { - // force a new search since content has changed - c.lastCombinedFilter = null; - c.lastSearch = []; - } - // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first - // input ensures all inputs are updated when a search is triggered on the table - // $( 'table' ).trigger( 'search', [...] ); - ts.filter.searching( table, filter, true ); - } - return false; - }); + }); - // reset button/link - if ( wo.filter_reset ) { - if ( wo.filter_reset instanceof $ ) { - // reset contains a jQuery object, bind to it - wo.filter_reset.click( function() { - c.$table.trigger( 'filterReset' ); - }); - } else if ( $( wo.filter_reset ).length ) { - // reset is a jQuery selector, use event delegation - $( document ) - .undelegate( wo.filter_reset, 'click.tsfilter' ) - .delegate( wo.filter_reset, 'click.tsfilter', function() { - // trigger a reset event, so other functions ( filter_formatter ) know when to reset + // reset button/link + if ( wo.filter_reset ) { + if ( wo.filter_reset instanceof $ ) { + // reset contains a jQuery object, bind to it + wo.filter_reset.click( function() { c.$table.trigger( 'filterReset' ); }); + } else if ( $( wo.filter_reset ).length ) { + // reset is a jQuery selector, use event delegation + $( document ) + .undelegate( wo.filter_reset, 'click.tsfilter' ) + .delegate( wo.filter_reset, 'click.tsfilter', function() { + // trigger a reset event, so other functions ( filter_formatter ) know when to reset + c.$table.trigger( 'filterReset' ); + }); + } } - } - if ( wo.filter_functions ) { - for ( column = 0; column < c.columns; column++ ) { - fxn = ts.getColumnData( table, wo.filter_functions, column ); - if ( fxn ) { - // remove 'filter-select' from header otherwise the options added here are replaced with - // all options - $header = c.$headerIndexed[ column ].removeClass( 'filter-select' ); - // don't build select if 'filter-false' or 'parser-false' set - noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); - options = ''; - if ( fxn === true && noSelect ) { - ts.filter.buildSelect( table, column ); - } else if ( typeof fxn === 'object' && noSelect ) { - // add custom drop down list - for ( string in fxn ) { - if ( typeof string === 'string' ) { - options += options === '' ? - '<option value="">' + - ( $header.data( 'placeholder' ) || - $header.attr( 'data-placeholder' ) || - wo.filter_placeholder.select || - '' - ) + - '</option>' : ''; - val = string; - txt = string; - if ( string.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { - val = string.split( wo.filter_selectSourceSeparator ); - txt = val[1]; - val = val[0]; + if ( wo.filter_functions ) { + for ( column = 0; column < c.columns; column++ ) { + fxn = ts.getColumnData( table, wo.filter_functions, column ); + if ( fxn ) { + // remove 'filter-select' from header otherwise the options added here are replaced with + // all options + $header = c.$headerIndexed[ column ].removeClass( 'filter-select' ); + // don't build select if 'filter-false' or 'parser-false' set + noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); + options = ''; + if ( fxn === true && noSelect ) { + ts.filter.buildSelect( table, column ); + } else if ( typeof fxn === 'object' && noSelect ) { + // add custom drop down list + for ( string in fxn ) { + if ( typeof string === 'string' ) { + options += options === '' ? + '<option value="">' + + ( $header.data( 'placeholder' ) || + $header.attr( 'data-placeholder' ) || + wo.filter_placeholder.select || + '' + ) + + '</option>' : ''; + val = string; + txt = string; + if ( string.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { + val = string.split( wo.filter_selectSourceSeparator ); + txt = val[1]; + val = val[0]; + } + options += '<option ' + + ( txt === val ? '' : 'data-function-name="' + string + '" ' ) + + 'value="' + val + '">' + txt + '</option>'; } - options += '<option ' + - ( txt === val ? '' : 'data-function-name="' + string + '" ' ) + - 'value="' + val + '">' + txt + '</option>'; } - } - c.$table - .find( 'thead' ) - .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) - .append( options ); - txt = wo.filter_selectSource; - fxn = $.isFunction( txt ) ? true : ts.getColumnData( table, txt, column ); - if ( fxn ) { - // updating so the extra options are appended - ts.filter.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) ); + c.$table + .find( 'thead' ) + .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) + .append( options ); + txt = wo.filter_selectSource; + fxn = $.isFunction( txt ) ? true : ts.getColumnData( table, txt, column ); + if ( fxn ) { + // updating so the extra options are appended + ts.filter.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) ); + } } } } } - } - // not really updating, but if the column has both the 'filter-select' class & - // filter_functions set to true, it would append the same options twice. - ts.filter.buildDefault( table, true ); + // not really updating, but if the column has both the 'filter-select' class & + // filter_functions set to true, it would append the same options twice. + ts.filter.buildDefault( table, true ); - ts.filter.bindSearch( table, c.$table.find( '.' + tscss.filter ), true ); - if ( wo.filter_external ) { - ts.filter.bindSearch( table, wo.filter_external ); - } + ts.filter.bindSearch( table, c.$table.find( '.' + tscss.filter ), true ); + if ( wo.filter_external ) { + ts.filter.bindSearch( table, wo.filter_external ); + } - if ( wo.filter_hideFilters ) { - ts.filter.hideFilters( table, c ); - } + if ( wo.filter_hideFilters ) { + ts.filter.hideFilters( table, c ); + } - // show processing icon - if ( c.showProcessing ) { - txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter ' ); - c.$table - .unbind( txt.replace( /\s+/g, ' ' ) ) - .bind( txt, function( event, columns ) { - // only add processing to certain columns to all columns - $header = ( columns ) ? - c.$table - .find( '.' + tscss.header ) - .filter( '[data-column]' ) - .filter( function() { - return columns[ $( this ).data( 'column' ) ] !== ''; - }) : ''; - ts.isProcessing( table, event.type === 'filterStart', columns ? $header : '' ); - }); - } + // show processing icon + if ( c.showProcessing ) { + txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter ' ); + c.$table + .unbind( txt.replace( /\s+/g, ' ' ) ) + .bind( txt, function( event, columns ) { + // only add processing to certain columns to all columns + $header = ( columns ) ? + c.$table + .find( '.' + tscss.header ) + .filter( '[data-column]' ) + .filter( function() { + return columns[ $( this ).data( 'column' ) ] !== ''; + }) : ''; + ts.isProcessing( table, event.type === 'filterStart', columns ? $header : '' ); + }); + } - // set filtered rows count ( intially unfiltered ) - c.filteredRows = c.totalRows; + // set filtered rows count ( intially unfiltered ) + c.filteredRows = c.totalRows; - // add default values - txt = 'tablesorter-initialized pagerBeforeInitialized '.split( ' ' ).join( c.namespace + 'filter ' ); - c.$table - .unbind( txt.replace( /\s+/g, ' ' ) ) - .bind( txt, function() { - // redefine 'wo' as it does not update properly inside this callback - var wo = this.config.widgetOptions; - filters = ts.filter.setDefaults( table, c, wo ) || []; - if ( filters.length ) { - // prevent delayInit from triggering a cache build if filters are empty - if ( !( c.delayInit && filters.join( '' ) === '' ) ) { - ts.setFilters( table, filters, true ); + // add default values + txt = 'tablesorter-initialized pagerBeforeInitialized '.split( ' ' ).join( c.namespace + 'filter ' ); + c.$table + .unbind( txt.replace( /\s+/g, ' ' ) ) + .bind( txt, function() { + // redefine 'wo' as it does not update properly inside this callback + var wo = this.config.widgetOptions; + filters = ts.filter.setDefaults( table, c, wo ) || []; + if ( filters.length ) { + // prevent delayInit from triggering a cache build if filters are empty + if ( !( c.delayInit && filters.join( '' ) === '' ) ) { + ts.setFilters( table, filters, true ); + } } - } - c.$table.trigger( 'filterFomatterUpdate' ); - // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers - setTimeout( function() { - if ( !wo.filter_initialized ) { + c.$table.trigger( 'filterFomatterUpdate' ); + // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers + setTimeout( function() { + if ( !wo.filter_initialized ) { + ts.filter.filterInitComplete( c ); + } + }, 100 ); + }); + // if filter widget is added after pager has initialized; then set filter init flag + if ( c.pager && c.pager.initialized && !wo.filter_initialized ) { + c.$table.trigger( 'filterFomatterUpdate' ); + setTimeout( function() { ts.filter.filterInitComplete( c ); - } - }, 100 ); - }); - // if filter widget is added after pager has initialized; then set filter init flag - if ( c.pager && c.pager.initialized && !wo.filter_initialized ) { - c.$table.trigger( 'filterFomatterUpdate' ); - setTimeout( function() { - ts.filter.filterInitComplete( c ); - }, 100 ); - } - }, - // $cell parameter, but not the config, is passed to the filter_formatters, - // so we have to work with it instead - formatterUpdated: function( $cell, column ) { - var wo = $cell.closest( 'table' )[0].config.widgetOptions; - if ( !wo.filter_initialized ) { - // add updates by column since this function - // may be called numerous times before initialization - wo.filter_formatterInit[ column ] = 1; - } - }, - filterInitComplete: function( c ) { - var indx, len, - wo = c.widgetOptions, - count = 0, - completed = function() { - wo.filter_initialized = true; - c.$table.trigger( 'filterInit', c ); - ts.filter.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); - }; - if ( $.isEmptyObject( wo.filter_formatter ) ) { - completed(); - } else { - len = wo.filter_formatterInit.length; - for ( indx = 0; indx < len; indx++ ) { - if ( wo.filter_formatterInit[ indx ] === 1 ) { - count++; - } + }, 100 ); + } + }, + // $cell parameter, but not the config, is passed to the filter_formatters, + // so we have to work with it instead + formatterUpdated: function( $cell, column ) { + var wo = $cell.closest( 'table' )[0].config.widgetOptions; + if ( !wo.filter_initialized ) { + // add updates by column since this function + // may be called numerous times before initialization + wo.filter_formatterInit[ column ] = 1; } - clearTimeout( wo.filter_initTimer ); - if ( !wo.filter_initialized && count === wo.filter_formatterCount ) { - // filter widget initialized + }, + filterInitComplete: function( c ) { + var indx, len, + wo = c.widgetOptions, + count = 0, + completed = function() { + wo.filter_initialized = true; + c.$table.trigger( 'filterInit', c ); + ts.filter.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); + }; + if ( $.isEmptyObject( wo.filter_formatter ) ) { completed(); - } else if ( !wo.filter_initialized ) { - // fall back in case a filter_formatter doesn't call - // $.tablesorter.filter.formatterUpdated( $cell, column ), and the count is off - wo.filter_initTimer = setTimeout( function() { + } else { + len = wo.filter_formatterInit.length; + for ( indx = 0; indx < len; indx++ ) { + if ( wo.filter_formatterInit[ indx ] === 1 ) { + count++; + } + } + clearTimeout( wo.filter_initTimer ); + if ( !wo.filter_initialized && count === wo.filter_formatterCount ) { + // filter widget initialized completed(); - }, 500 ); - } - } - }, - setDefaults: function( table, c, wo ) { - var isArray, saved, indx, col, $filters, - // get current ( default ) filters - filters = ts.getFilters( table ) || []; - if ( wo.filter_saveFilters && ts.storage ) { - saved = ts.storage( table, 'tablesorter-filters' ) || []; - isArray = $.isArray( saved ); - // make sure we're not just getting an empty array - if ( !( isArray && saved.join( '' ) === '' || !isArray ) ) { - filters = saved; + } else if ( !wo.filter_initialized ) { + // fall back in case a filter_formatter doesn't call + // $.tablesorter.filter.formatterUpdated( $cell, column ), and the count is off + wo.filter_initTimer = setTimeout( function() { + completed(); + }, 500 ); + } } - } - // if no filters saved, then check default settings - if ( filters.join( '' ) === '' ) { - // allow adding default setting to external filters - $filters = c.$headers.add( wo.filter_$externalFilters ) - .filter( '[' + wo.filter_defaultAttrib + ']' ); - for ( indx = 0; indx <= c.columns; indx++ ) { - // include data-column='all' external filters - col = indx === c.columns ? 'all' : indx; - filters[indx] = $filters - .filter( '[data-column="' + col + '"]' ) - .attr( wo.filter_defaultAttrib ) || filters[indx] || ''; + }, + setDefaults: function( table, c, wo ) { + var isArray, saved, indx, col, $filters, + // get current ( default ) filters + filters = ts.getFilters( table ) || []; + if ( wo.filter_saveFilters && ts.storage ) { + saved = ts.storage( table, 'tablesorter-filters' ) || []; + isArray = $.isArray( saved ); + // make sure we're not just getting an empty array + if ( !( isArray && saved.join( '' ) === '' || !isArray ) ) { + filters = saved; + } } - } - c.$table.data( 'lastSearch', filters ); - return filters; - }, - parseFilter: function( c, filter, column, parsed ) { - return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter; - }, - buildRow: function( table, c, wo ) { - var col, column, $header, buildSelect, disabled, name, ffxn, tmp, - // c.columns defined in computeThIndexes() - cellFilter = wo.filter_cellFilter, - columns = c.columns, - arry = $.isArray( cellFilter ), - buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; - for ( column = 0; column < columns; column++ ) { - buildFilter += '<td'; - if ( arry ) { - buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' ); - } else { - buildFilter += ( cellFilter !== '' ? ' class="' + cellFilter + '"' : '' ); + // if no filters saved, then check default settings + if ( filters.join( '' ) === '' ) { + // allow adding default setting to external filters + $filters = c.$headers.add( wo.filter_$externalFilters ) + .filter( '[' + wo.filter_defaultAttrib + ']' ); + for ( indx = 0; indx <= c.columns; indx++ ) { + // include data-column='all' external filters + col = indx === c.columns ? 'all' : indx; + filters[indx] = $filters + .filter( '[data-column="' + col + '"]' ) + .attr( wo.filter_defaultAttrib ) || filters[indx] || ''; + } } - buildFilter += '></td>'; - } - c.$filters = $( buildFilter += '</tr>' ) - .appendTo( c.$table.children( 'thead' ).eq( 0 ) ) - .find( 'td' ); - // build each filter input - for ( column = 0; column < columns; column++ ) { - disabled = false; - // assuming last cell of a column is the main column - $header = c.$headerIndexed[ column ]; - ffxn = ts.getColumnData( table, wo.filter_functions, column ); - buildSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || - $header.hasClass( 'filter-select' ); - // get data from jQuery data, metadata, headers option or header class name - col = ts.getColumnData( table, c.headers, column ); - disabled = ts.getData( $header[0], col, 'filter' ) === 'false' || - ts.getData( $header[0], col, 'parser' ) === 'false'; - - if ( buildSelect ) { - buildFilter = $( '<select>' ).appendTo( c.$filters.eq( column ) ); - } else { - ffxn = ts.getColumnData( table, wo.filter_formatter, column ); - if ( ffxn ) { - wo.filter_formatterCount++; - buildFilter = ffxn( c.$filters.eq( column ), column ); - // no element returned, so lets go find it - if ( buildFilter && buildFilter.length === 0 ) { - buildFilter = c.$filters.eq( column ).children( 'input' ); + c.$table.data( 'lastSearch', filters ); + return filters; + }, + parseFilter: function( c, filter, column, parsed ) { + return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter; + }, + buildRow: function( table, c, wo ) { + var col, column, $header, buildSelect, disabled, name, ffxn, tmp, + // c.columns defined in computeThIndexes() + cellFilter = wo.filter_cellFilter, + columns = c.columns, + arry = $.isArray( cellFilter ), + buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; + for ( column = 0; column < columns; column++ ) { + buildFilter += '<td'; + if ( arry ) { + buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' ); + } else { + buildFilter += ( cellFilter !== '' ? ' class="' + cellFilter + '"' : '' ); + } + buildFilter += '></td>'; + } + c.$filters = $( buildFilter += '</tr>' ) + .appendTo( c.$table.children( 'thead' ).eq( 0 ) ) + .find( 'td' ); + // build each filter input + for ( column = 0; column < columns; column++ ) { + disabled = false; + // assuming last cell of a column is the main column + $header = c.$headerIndexed[ column ]; + ffxn = ts.getColumnData( table, wo.filter_functions, column ); + buildSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || + $header.hasClass( 'filter-select' ); + // get data from jQuery data, metadata, headers option or header class name + col = ts.getColumnData( table, c.headers, column ); + disabled = ts.getData( $header[0], col, 'filter' ) === 'false' || + ts.getData( $header[0], col, 'parser' ) === 'false'; + + if ( buildSelect ) { + buildFilter = $( '<select>' ).appendTo( c.$filters.eq( column ) ); + } else { + ffxn = ts.getColumnData( table, wo.filter_formatter, column ); + if ( ffxn ) { + wo.filter_formatterCount++; + buildFilter = ffxn( c.$filters.eq( column ), column ); + // no element returned, so lets go find it + if ( buildFilter && buildFilter.length === 0 ) { + buildFilter = c.$filters.eq( column ).children( 'input' ); + } + // element not in DOM, so lets attach it + if ( buildFilter && ( buildFilter.parent().length === 0 || + ( buildFilter.parent().length && buildFilter.parent()[0] !== c.$filters[column] ) ) ) { + c.$filters.eq( column ).append( buildFilter ); + } + } else { + buildFilter = $( '<input type="search">' ).appendTo( c.$filters.eq( column ) ); } - // element not in DOM, so lets attach it - if ( buildFilter && ( buildFilter.parent().length === 0 || - ( buildFilter.parent().length && buildFilter.parent()[0] !== c.$filters[column] ) ) ) { - c.$filters.eq( column ).append( buildFilter ); + if ( buildFilter ) { + tmp = $header.data( 'placeholder' ) || + $header.attr( 'data-placeholder' ) || + wo.filter_placeholder.search || ''; + buildFilter.attr( 'placeholder', tmp ); } - } else { - buildFilter = $( '<input type="search">' ).appendTo( c.$filters.eq( column ) ); } if ( buildFilter ) { - tmp = $header.data( 'placeholder' ) || - $header.attr( 'data-placeholder' ) || - wo.filter_placeholder.search || ''; - buildFilter.attr( 'placeholder', tmp ); + // add filter class name + name = ( $.isArray( wo.filter_cssFilter ) ? + ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : + wo.filter_cssFilter ) || ''; + buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column ); + if ( disabled ) { + buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; + } } } - if ( buildFilter ) { - // add filter class name - name = ( $.isArray( wo.filter_cssFilter ) ? - ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : - wo.filter_cssFilter ) || ''; - buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column ); - if ( disabled ) { - buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; + }, + bindSearch: function( table, $el, internal ) { + table = $( table )[0]; + $el = $( $el ); // allow passing a selector string + if ( !$el.length ) { return; } + var tmp, + c = table.config, + wo = c.widgetOptions, + namespace = c.namespace + 'filter', + $ext = wo.filter_$externalFilters; + if ( internal !== true ) { + // save anyMatch element + tmp = wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector; + wo.filter_$anyMatch = $el.filter( tmp ); + if ( $ext && $ext.length ) { + wo.filter_$externalFilters = wo.filter_$externalFilters.add( $el ); + } else { + wo.filter_$externalFilters = $el; } - } - } - }, - bindSearch: function( table, $el, internal ) { - table = $( table )[0]; - $el = $( $el ); // allow passing a selector string - if ( !$el.length ) { return; } - var tmp, - c = table.config, - wo = c.widgetOptions, - namespace = c.namespace + 'filter', - $ext = wo.filter_$externalFilters; - if ( internal !== true ) { - // save anyMatch element - tmp = wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector; - wo.filter_$anyMatch = $el.filter( tmp ); - if ( $ext && $ext.length ) { - wo.filter_$externalFilters = wo.filter_$externalFilters.add( $el ); + // update values ( external filters added after table initialization ) + ts.setFilters( table, c.$table.data( 'lastSearch' ) || [], internal === false ); + } + // unbind events + tmp = ( 'keypress keyup search change '.split( ' ' ).join( namespace + ' ' ) ); + $el + // use data attribute instead of jQuery data since the head is cloned without including + // the data/binding + .attr( 'data-lastSearchTime', new Date().getTime() ) + .unbind( tmp.replace( /\s+/g, ' ' ) ) + // include change for select - fixes #473 + .bind( 'keyup' + namespace, function( event ) { + $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); + // emulate what webkit does.... escape clears the filter + if ( event.which === 27 ) { + this.value = ''; + // live search + } else if ( wo.filter_liveSearch === false ) { + return; + // don't return if the search value is empty ( all rows need to be revealed ) + } else if ( this.value !== '' && ( + // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace + ( typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch ) || + // let return & backspace continue on, but ignore arrows & non-valid characters + ( event.which !== 13 && event.which !== 8 && + ( event.which < 32 || ( event.which >= 37 && event.which <= 40 ) ) ) ) ) { + return; + } + // change event = no delay; last true flag tells getFilters to skip newest timed input + ts.filter.searching( table, true, true ); + }) + .bind( 'search change keypress '.split( ' ' ).join( namespace + ' ' ), function( event ) { + var column = $( this ).data( 'column' ); + // don't allow 'change' event to process if the input value is the same - fixes #685 + if ( event.which === 13 || event.type === 'search' || + event.type === 'change' && this.value !== c.lastSearch[column] ) { + event.preventDefault(); + // init search with no delay + $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); + ts.filter.searching( table, false, true ); + } + }); + }, + searching: function( table, filter, skipFirst ) { + var wo = table.config.widgetOptions; + clearTimeout( wo.searchTimer ); + if ( typeof filter === 'undefined' || filter === true ) { + // delay filtering + wo.searchTimer = setTimeout( function() { + ts.filter.checkFilters( table, filter, skipFirst ); + }, wo.filter_liveSearch ? wo.filter_searchDelay : 10 ); } else { - wo.filter_$externalFilters = $el; + // skip delay + ts.filter.checkFilters( table, filter, skipFirst ); } - // update values ( external filters added after table initialization ) - ts.setFilters( table, c.$table.data( 'lastSearch' ) || [], internal === false ); - } - // unbind events - tmp = ( 'keypress keyup search change '.split( ' ' ).join( namespace + ' ' ) ); - $el - // use data attribute instead of jQuery data since the head is cloned without including - // the data/binding - .attr( 'data-lastSearchTime', new Date().getTime() ) - .unbind( tmp.replace( /\s+/g, ' ' ) ) - // include change for select - fixes #473 - .bind( 'keyup' + namespace, function( event ) { - $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - // emulate what webkit does.... escape clears the filter - if ( event.which === 27 ) { - this.value = ''; - // live search - } else if ( wo.filter_liveSearch === false ) { - return; - // don't return if the search value is empty ( all rows need to be revealed ) - } else if ( this.value !== '' && ( - // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace - ( typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch ) || - // let return & backspace continue on, but ignore arrows & non-valid characters - ( event.which !== 13 && event.which !== 8 && - ( event.which < 32 || ( event.which >= 37 && event.which <= 40 ) ) ) ) ) { + }, + checkFilters: function( table, filter, skipFirst ) { + var c = table.config, + wo = c.widgetOptions, + filterArray = $.isArray( filter ), + filters = ( filterArray ) ? filter : ts.getFilters( table, true ), + combinedFilters = ( filters || [] ).join( '' ); // combined filter values + // prevent errors if delay init is set + if ( $.isEmptyObject( c.cache ) ) { + // update cache if delayInit set & pager has initialized ( after user initiates a search ) + if ( c.delayInit && c.pager && c.pager.initialized ) { + c.$table.trigger( 'updateCache', [ function() { + ts.filter.checkFilters( table, false, skipFirst ); + } ] ); + } return; } - // change event = no delay; last true flag tells getFilters to skip newest timed input - ts.filter.searching( table, true, true ); - }) - .bind( 'search change keypress '.split( ' ' ).join( namespace + ' ' ), function( event ) { - var column = $( this ).data( 'column' ); - // don't allow 'change' event to process if the input value is the same - fixes #685 - if ( event.which === 13 || event.type === 'search' || - event.type === 'change' && this.value !== c.lastSearch[column] ) { - event.preventDefault(); - // init search with no delay - $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - ts.filter.searching( table, false, true ); + // add filter array back into inputs + if ( filterArray ) { + ts.setFilters( table, filters, false, skipFirst !== true ); + if ( !wo.filter_initialized ) { c.lastCombinedFilter = ''; } } - }); - }, - searching: function( table, filter, skipFirst ) { - var wo = table.config.widgetOptions; - clearTimeout( wo.searchTimer ); - if ( typeof filter === 'undefined' || filter === true ) { - // delay filtering - wo.searchTimer = setTimeout( function() { - ts.filter.checkFilters( table, filter, skipFirst ); - }, wo.filter_liveSearch ? wo.filter_searchDelay : 10 ); - } else { - // skip delay - ts.filter.checkFilters( table, filter, skipFirst ); - } - }, - checkFilters: function( table, filter, skipFirst ) { - var c = table.config, - wo = c.widgetOptions, - filterArray = $.isArray( filter ), - filters = ( filterArray ) ? filter : ts.getFilters( table, true ), - combinedFilters = ( filters || [] ).join( '' ); // combined filter values - // prevent errors if delay init is set - if ( $.isEmptyObject( c.cache ) ) { - // update cache if delayInit set & pager has initialized ( after user initiates a search ) - if ( c.delayInit && c.pager && c.pager.initialized ) { - c.$table.trigger( 'updateCache', [ function() { - ts.filter.checkFilters( table, false, skipFirst ); - } ] ); - } - return; - } - // add filter array back into inputs - if ( filterArray ) { - ts.setFilters( table, filters, false, skipFirst !== true ); - if ( !wo.filter_initialized ) { c.lastCombinedFilter = ''; } - } - if ( wo.filter_hideFilters ) { - // show/hide filter row as needed - c.$table - .find( '.' + tscss.filterRow ) - .trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); - } - // return if the last search is the same; but filter === false when updating the search - // see example-widget-filter.html filter toggle buttons - if ( c.lastCombinedFilter === combinedFilters && filter !== false ) { - return; - } else if ( filter === false ) { - // force filter refresh - c.lastCombinedFilter = null; - c.lastSearch = []; - } - if ( wo.filter_initialized ) { - c.$table.trigger( 'filterStart', [filters] ); - } - if ( c.showProcessing ) { - // give it time for the processing icon to kick in - setTimeout( function() { + if ( wo.filter_hideFilters ) { + // show/hide filter row as needed + c.$table + .find( '.' + tscss.filterRow ) + .trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); + } + // return if the last search is the same; but filter === false when updating the search + // see example-widget-filter.html filter toggle buttons + if ( c.lastCombinedFilter === combinedFilters && filter !== false ) { + return; + } else if ( filter === false ) { + // force filter refresh + c.lastCombinedFilter = null; + c.lastSearch = []; + } + if ( wo.filter_initialized ) { + c.$table.trigger( 'filterStart', [ filters ] ); + } + if ( c.showProcessing ) { + // give it time for the processing icon to kick in + setTimeout( function() { + ts.filter.findRows( table, filters, combinedFilters ); + return false; + }, 30 ); + } else { ts.filter.findRows( table, filters, combinedFilters ); return false; - }, 30 ); - } else { - ts.filter.findRows( table, filters, combinedFilters ); - return false; - } - }, - hideFilters: function( table, c ) { - var timer; - c.$table - .find( '.' + tscss.filterRow ) - .bind( 'mouseenter mouseleave', function( e ) { - // save event object - http://bugs.jquery.com/ticket/12140 - var event = e, - $filterRow = $( this ); - clearTimeout( timer ); - timer = setTimeout( function() { - if ( /enter|over/.test( event.type ) ) { - $filterRow.removeClass( tscss.filterRowHide ); - } else { - // don't hide if input has focus - // $( ':focus' ) needs jQuery 1.6+ - if ( $( document.activeElement ).closest( 'tr' )[0] !== $filterRow[0] ) { - // don't hide row if any filter has a value - if ( c.lastCombinedFilter === '' ) { - $filterRow.addClass( tscss.filterRowHide ); + } + }, + hideFilters: function( table, c ) { + var timer; + c.$table + .find( '.' + tscss.filterRow ) + .bind( 'mouseenter mouseleave', function( e ) { + // save event object - http://bugs.jquery.com/ticket/12140 + var event = e, + $filterRow = $( this ); + clearTimeout( timer ); + timer = setTimeout( function() { + if ( /enter|over/.test( event.type ) ) { + $filterRow.removeClass( tscss.filterRowHide ); + } else { + // don't hide if input has focus + // $( ':focus' ) needs jQuery 1.6+ + if ( $( document.activeElement ).closest( 'tr' )[0] !== $filterRow[0] ) { + // don't hide row if any filter has a value + if ( c.lastCombinedFilter === '' ) { + $filterRow.addClass( tscss.filterRowHide ); + } } } - } - }, 200 ); - }) - .find( 'input, select' ).bind( 'focus blur', function( e ) { - var event = e, - $row = $( this ).closest( 'tr' ); - clearTimeout( timer ); - timer = setTimeout( function() { + }, 200 ); + }) + .find( 'input, select' ).bind( 'focus blur', function( e ) { + var event = e, + $row = $( this ).closest( 'tr' ); clearTimeout( timer ); - // don't hide row if any filter has a value - if ( ts.getFilters( c.$table ).join( '' ) === '' ) { - $row.toggleClass( tscss.filterRowHide, event.type !== 'focus' ); - } - }, 200 ); - }); - }, - defaultFilter: function( filter, mask ) { - if ( filter === '' ) { return filter; } - var regex = ts.filter.regex.iQuery, - maskLen = mask.match( ts.filter.regex.igQuery ).length, - query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ], - len = query.length - 1, - indx = 0, - val = mask; - if ( len < 1 && maskLen > 1 ) { - // only one 'word' in query but mask has >1 slots - query[1] = query[0]; - } - // replace all {query} with query words... - // if query = 'Bob', then convert mask from '!{query}' to '!Bob' - // if query = 'Bob Joe Frank', then convert mask '{q} OR {q}' to 'Bob OR Joe OR Frank' - while ( regex.test( val ) ) { - val = val.replace( regex, query[indx++] || '' ); - if ( regex.test( val ) && indx < len && ( query[indx] || '' ) !== '' ) { - val = mask.replace( regex, val ); + timer = setTimeout( function() { + clearTimeout( timer ); + // don't hide row if any filter has a value + if ( ts.getFilters( c.$table ).join( '' ) === '' ) { + $row.toggleClass( tscss.filterRowHide, event.type !== 'focus' ); + } + }, 200 ); + }); + }, + defaultFilter: function( filter, mask ) { + if ( filter === '' ) { return filter; } + var regex = ts.filter.regex.iQuery, + maskLen = mask.match( ts.filter.regex.igQuery ).length, + query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ], + len = query.length - 1, + indx = 0, + val = mask; + if ( len < 1 && maskLen > 1 ) { + // only one 'word' in query but mask has >1 slots + query[1] = query[0]; + } + // replace all {query} with query words... + // if query = 'Bob', then convert mask from '!{query}' to '!Bob' + // if query = 'Bob Joe Frank', then convert mask '{q} OR {q}' to 'Bob OR Joe OR Frank' + while ( regex.test( val ) ) { + val = val.replace( regex, query[indx++] || '' ); + if ( regex.test( val ) && indx < len && ( query[indx] || '' ) !== '' ) { + val = mask.replace( regex, val ); + } } - } - return val; - }, - getLatestSearch: function( $input ) { - if ( $input ) { - return $input.sort( function( a, b ) { - return $( b ).attr( 'data-lastSearchTime' ) - $( a ).attr( 'data-lastSearchTime' ); - }); - } - return $input || $(); - }, - multipleColumns: function( c, $input ) { - // look for multiple columns '1-3,4-6,8' in data-column - var temp, ranges, range, start, end, singles, i, indx, len, - wo = c.widgetOptions, - // only target 'all' column inputs on initialization - // & don't target 'all' column inputs if they don't exist - targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, - columns = [], - val = $.trim( ts.filter.getLatestSearch( $input ).attr( 'data-column' ) || '' ); - // process column range - if ( targets && /-/.test( val ) ) { - ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); - len = ranges.length; - for ( indx = 0; indx < len; indx++ ) { - range = ranges[indx].split( /\s*-\s*/ ); - start = parseInt( range[0], 10 ) || 0; - end = parseInt( range[1], 10 ) || ( c.columns - 1 ); - if ( start > end ) { - temp = start; start = end; end = temp; // swap - } - if ( end >= c.columns ) { - end = c.columns - 1; - } - for ( ; start <= end; start++ ) { - columns.push( start ); - } - // remove processed range from val - val = val.replace( ranges[ indx ], '' ); + return val; + }, + getLatestSearch: function( $input ) { + if ( $input ) { + return $input.sort( function( a, b ) { + return $( b ).attr( 'data-lastSearchTime' ) - $( a ).attr( 'data-lastSearchTime' ); + }); } - } - // process single columns - if ( targets && /,/.test( val ) ) { - singles = val.split( /\s*,\s*/ ); - len = singles.length; - for ( i = 0; i < len; i++ ) { - if ( singles[ i ] !== '' ) { - indx = parseInt( singles[ i ], 10 ); - if ( indx < c.columns ) { - columns.push( indx ); + return $input || $(); + }, + multipleColumns: function( c, $input ) { + // look for multiple columns '1-3,4-6,8' in data-column + var temp, ranges, range, start, end, singles, i, indx, len, + wo = c.widgetOptions, + // only target 'all' column inputs on initialization + // & don't target 'all' column inputs if they don't exist + targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, + columns = [], + val = $.trim( ts.filter.getLatestSearch( $input ).attr( 'data-column' ) || '' ); + // process column range + if ( targets && /-/.test( val ) ) { + ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); + len = ranges.length; + for ( indx = 0; indx < len; indx++ ) { + range = ranges[indx].split( /\s*-\s*/ ); + start = parseInt( range[0], 10 ) || 0; + end = parseInt( range[1], 10 ) || ( c.columns - 1 ); + if ( start > end ) { + temp = start; start = end; end = temp; // swap } + if ( end >= c.columns ) { + end = c.columns - 1; + } + for ( ; start <= end; start++ ) { + columns.push( start ); + } + // remove processed range from val + val = val.replace( ranges[ indx ], '' ); } } - } - // return all columns - if ( !columns.length ) { - for ( indx = 0; indx < c.columns; indx++ ) { - columns.push( indx ); + // process single columns + if ( targets && /,/.test( val ) ) { + singles = val.split( /\s*,\s*/ ); + len = singles.length; + for ( i = 0; i < len; i++ ) { + if ( singles[ i ] !== '' ) { + indx = parseInt( singles[ i ], 10 ); + if ( indx < c.columns ) { + columns.push( indx ); + } + } + } } - } - return columns; - }, - processTypes: function( c, data, vars ) { - var ffxn, - filterMatched = null, - matches = null; - for ( ffxn in ts.filter.types ) { - if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) { - matches = ts.filter.types[ffxn]( c, data, vars ); - if ( matches !== null ) { - filterMatched = matches; + // return all columns + if ( !columns.length ) { + for ( indx = 0; indx < c.columns; indx++ ) { + columns.push( indx ); } } - } - return filterMatched; - }, - processRow: function( c, data, vars ) { - var columnIndex, hasSelect, result, val, filterMatched, - fxn, ffxn, txt, - regex = ts.filter.regex, - wo = c.widgetOptions, - showRow = true; - data.$cells = data.$row.children(); - - if ( data.anyMatchFlag ) { - // look for multiple columns '1-3,4-6,8' - columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); - data.anyMatch = true; - data.isMatch = true; - data.rowArray = data.$cells.map( function( i ) { - if ( $.inArray( i, columnIndex ) > -1 ) { - if ( data.parsed[ i ] ) { - txt = data.cacheArray[ i ]; - } else { - txt = data.rawArray[ i ]; - txt = $.trim( wo.filter_ignoreCase ? txt.toLowerCase() : txt ); - if ( c.sortLocaleCompare ) { - txt = ts.replaceAccents( txt ); - } + return columns; + }, + processTypes: function( c, data, vars ) { + var ffxn, + filterMatched = null, + matches = null; + for ( ffxn in ts.filter.types ) { + if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) { + matches = ts.filter.types[ffxn]( c, data, vars ); + if ( matches !== null ) { + filterMatched = matches; } - return txt; } - }).get(); - data.filter = data.anyMatchFilter; - data.iFilter = data.iAnyMatchFilter; - data.exact = data.rowArray.join( ' ' ); - data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; - data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' ); - - vars.excludeMatch = vars.noAnyMatch; - filterMatched = ts.filter.processTypes( c, data, vars ); - - if ( filterMatched !== null ) { - showRow = filterMatched; - } else { - if ( wo.filter_startsWith ) { - showRow = false; - columnIndex = c.columns; - while ( !showRow && columnIndex > 0 ) { - columnIndex--; - showRow = showRow || data.rowArray[ columnIndex ].indexOf( data.iFilter ) === 0; + } + return filterMatched; + }, + processRow: function( c, data, vars ) { + var columnIndex, hasSelect, result, val, filterMatched, + fxn, ffxn, txt, + regex = ts.filter.regex, + wo = c.widgetOptions, + showRow = true; + data.$cells = data.$row.children(); + + if ( data.anyMatchFlag ) { + // look for multiple columns '1-3,4-6,8' + columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); + data.anyMatch = true; + data.isMatch = true; + data.rowArray = data.$cells.map( function( i ) { + if ( $.inArray( i, columnIndex ) > -1 ) { + if ( data.parsed[ i ] ) { + txt = data.cacheArray[ i ]; + } else { + txt = data.rawArray[ i ]; + txt = $.trim( wo.filter_ignoreCase ? txt.toLowerCase() : txt ); + if ( c.sortLocaleCompare ) { + txt = ts.replaceAccents( txt ); + } + } + return txt; } + }).get(); + data.filter = data.anyMatchFilter; + data.iFilter = data.iAnyMatchFilter; + data.exact = data.rowArray.join( ' ' ); + data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; + data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' ); + + vars.excludeMatch = vars.noAnyMatch; + filterMatched = ts.filter.processTypes( c, data, vars ); + + if ( filterMatched !== null ) { + showRow = filterMatched; } else { - showRow = ( data.iExact + data.childRowText ).indexOf( data.iFilter ) >= 0; + if ( wo.filter_startsWith ) { + showRow = false; + columnIndex = c.columns; + while ( !showRow && columnIndex > 0 ) { + columnIndex--; + showRow = showRow || data.rowArray[ columnIndex ].indexOf( data.iFilter ) === 0; + } + } else { + showRow = ( data.iExact + data.childRowText ).indexOf( data.iFilter ) >= 0; + } + } + data.anyMatch = false; + // no other filters to process + if ( data.filters.join( '' ) === data.filter ) { + return showRow; } } - data.anyMatch = false; - // no other filters to process - if ( data.filters.join( '' ) === data.filter ) { - return showRow; - } - } - for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { - data.filter = data.filters[ columnIndex ]; - data.index = columnIndex; + for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { + data.filter = data.filters[ columnIndex ]; + data.index = columnIndex; - // filter types to exclude, per column - vars.excludeMatch = vars.excludeFilter[ columnIndex ]; + // filter types to exclude, per column + vars.excludeMatch = vars.excludeFilter[ columnIndex ]; - // ignore if filter is empty or disabled - if ( data.filter ) { - data.cache = data.cacheArray[ columnIndex ]; - // check if column data should be from the cell or from parsed data - if ( wo.filter_useParsedData || data.parsed[ columnIndex ] ) { - data.exact = data.cache; - } else { - result = data.rawArray[ columnIndex ] || ''; - data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 - } - data.iExact = !regex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? - data.exact.toLowerCase() : data.exact; - - data.isMatch = c.$headerIndexed[ data.index ].hasClass( 'filter-match' ); - - result = showRow; // if showRow is true, show that row - - // in case select filter option has a different value vs text 'a - z|A through Z' - ffxn = wo.filter_columnFilters ? - c.$filters.add( c.$externalFilters ) - .filter( '[data-column="'+ columnIndex + '"]' ) - .find( 'select option:selected' ) - .attr( 'data-function-name' ) || '' : ''; - // replace accents - see #357 - if ( c.sortLocaleCompare ) { - data.filter = ts.replaceAccents( data.filter ); - } - - val = true; - if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) { - data.filter = ts.filter.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); - // val is used to indicate that a filter select is using a default filter; - // so we override the exact & partial matches - val = false; - } - // data.iFilter = case insensitive ( if wo.filter_ignoreCase is true ), - // data.filter = case sensitive - data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter; - fxn = vars.functions[ columnIndex ]; - hasSelect = c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' ); - filterMatched = null; - if ( fxn || ( hasSelect && val ) ) { - if ( fxn === true || hasSelect ) { - // default selector uses exact match unless 'filter-match' class is found - filterMatched = data.isMatch ? - data.iExact.search( data.iFilter ) >= 0 : - data.filter === data.exact; - } else if ( typeof fxn === 'function' ) { - // filter callback( exact cell content, parser normalized content, - // filter input value, column index, jQuery row object ) - filterMatched = fxn( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); - } else if ( typeof fxn[ ffxn || data.filter ] === 'function' ) { - // selector option function - txt = ffxn || data.filter; - filterMatched = - fxn[ txt ]( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); + // ignore if filter is empty or disabled + if ( data.filter ) { + data.cache = data.cacheArray[ columnIndex ]; + // check if column data should be from the cell or from parsed data + if ( wo.filter_useParsedData || data.parsed[ columnIndex ] ) { + data.exact = data.cache; + } else { + result = data.rawArray[ columnIndex ] || ''; + data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 } - } - if ( filterMatched === null ) { - // cycle through the different filters - // filters return a boolean or null if nothing matches - filterMatched = ts.filter.processTypes( c, data, vars ); - if ( filterMatched !== null ) { - result = filterMatched; - // Look for match, and add child row data for matching + data.iExact = !regex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? + data.exact.toLowerCase() : data.exact; + + data.isMatch = c.$headerIndexed[ data.index ].hasClass( 'filter-match' ); + + result = showRow; // if showRow is true, show that row + + // in case select filter option has a different value vs text 'a - z|A through Z' + ffxn = wo.filter_columnFilters ? + c.$filters.add( c.$externalFilters ) + .filter( '[data-column="' + columnIndex + '"]' ) + .find( 'select option:selected' ) + .attr( 'data-function-name' ) || '' : ''; + // replace accents - see #357 + if ( c.sortLocaleCompare ) { + data.filter = ts.replaceAccents( data.filter ); + } + + val = true; + if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) { + data.filter = ts.filter.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); + // val is used to indicate that a filter select is using a default filter; + // so we override the exact & partial matches + val = false; + } + // data.iFilter = case insensitive ( if wo.filter_ignoreCase is true ), + // data.filter = case sensitive + data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter; + fxn = vars.functions[ columnIndex ]; + hasSelect = c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' ); + filterMatched = null; + if ( fxn || ( hasSelect && val ) ) { + if ( fxn === true || hasSelect ) { + // default selector uses exact match unless 'filter-match' class is found + filterMatched = data.isMatch ? + data.iExact.search( data.iFilter ) >= 0 : + data.filter === data.exact; + } else if ( typeof fxn === 'function' ) { + // filter callback( exact cell content, parser normalized content, + // filter input value, column index, jQuery row object ) + filterMatched = fxn( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); + } else if ( typeof fxn[ ffxn || data.filter ] === 'function' ) { + // selector option function + txt = ffxn || data.filter; + filterMatched = + fxn[ txt ]( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); + } + } + if ( filterMatched === null ) { + // cycle through the different filters + // filters return a boolean or null if nothing matches + filterMatched = ts.filter.processTypes( c, data, vars ); + if ( filterMatched !== null ) { + result = filterMatched; + // Look for match, and add child row data for matching + } else { + txt = ( data.iExact + data.childRowText ) + .indexOf( ts.filter.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) ); + result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); + } } else { - txt = ( data.iExact + data.childRowText ) - .indexOf( ts.filter.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) ); - result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); + result = filterMatched; } - } else { - result = filterMatched; + showRow = ( result ) ? showRow : false; } - showRow = ( result ) ? showRow : false; } - } - return showRow; - }, - findRows: function( table, filters, combinedFilters ) { - if ( table.config.lastCombinedFilter === combinedFilters || - !table.config.widgetOptions.filter_initialized ) { - return; - } - var len, norm_rows, rowData, $rows, rowIndex, tbodyIndex, $tbody, columnIndex, - isChild, childRow, lastSearch, showRow, time, val, indx, - notFiltered, searchFiltered, query, injected, res, id, txt, - storedFilters = $.extend( [], filters ), - regex = ts.filter.regex, - c = table.config, - wo = c.widgetOptions, - // data object passed to filters; anyMatch is a flag for the filters - data = { - anyMatch: false, - filters: filters, - // regex filter type cache - filter_regexCache : [] - }, - vars = { - // anyMatch really screws up with these types of filters - noAnyMatch: [ 'range', 'notMatch', 'operators' ], - // cache filter variables that use ts.getColumnData in the main loop - functions : [], - excludeFilter : [], - defaultColFilter : [], - defaultAnyFilter : ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '' - }; - - // parse columns after formatter, in case the class is added at that point - data.parsed = c.$headers.map( function( columnIndex ) { - return c.parsers && c.parsers[ columnIndex ] && - // force parsing if parser type is numeric - c.parsers[ columnIndex ].parsed || - // getData won't return 'parsed' if other 'filter-' class names exist - // ( e.g. <th class="filter-select filter-parsed"> ) - ts.getData && ts.getData( c.$headerIndexed[ columnIndex ], - ts.getColumnData( table, c.headers, columnIndex ), 'filter' ) === 'parsed' || - $( this ).hasClass( 'filter-parsed' ); - }).get(); - - for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { - vars.functions[ columnIndex ] = - ts.getColumnData( table, wo.filter_functions, columnIndex ); - vars.defaultColFilter[ columnIndex ] = - ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || ''; - vars.excludeFilter[ columnIndex ] = - ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split( /\s+/ ); - } + return showRow; + }, + findRows: function( table, filters, combinedFilters ) { + if ( table.config.lastCombinedFilter === combinedFilters || + !table.config.widgetOptions.filter_initialized ) { + return; + } + var len, norm_rows, rowData, $rows, rowIndex, tbodyIndex, $tbody, columnIndex, + isChild, childRow, lastSearch, showRow, time, val, indx, + notFiltered, searchFiltered, query, injected, res, id, txt, + storedFilters = $.extend( [], filters ), + regex = ts.filter.regex, + c = table.config, + wo = c.widgetOptions, + // data object passed to filters; anyMatch is a flag for the filters + data = { + anyMatch: false, + filters: filters, + // regex filter type cache + filter_regexCache : [] + }, + vars = { + // anyMatch really screws up with these types of filters + noAnyMatch: [ 'range', 'notMatch', 'operators' ], + // cache filter variables that use ts.getColumnData in the main loop + functions : [], + excludeFilter : [], + defaultColFilter : [], + defaultAnyFilter : ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '' + }; + + // parse columns after formatter, in case the class is added at that point + data.parsed = c.$headers.map( function( columnIndex ) { + return c.parsers && c.parsers[ columnIndex ] && + // force parsing if parser type is numeric + c.parsers[ columnIndex ].parsed || + // getData won't return 'parsed' if other 'filter-' class names exist + // ( e.g. <th class="filter-select filter-parsed"> ) + ts.getData && ts.getData( c.$headerIndexed[ columnIndex ], + ts.getColumnData( table, c.headers, columnIndex ), 'filter' ) === 'parsed' || + $( this ).hasClass( 'filter-parsed' ); + }).get(); - if ( c.debug ) { - ts.log( 'Filter: Starting filter widget search', filters ); - time = new Date(); - } - // filtered rows count - c.filteredRows = 0; - c.totalRows = 0; - // combindedFilters are undefined on init - combinedFilters = ( storedFilters || [] ).join( '' ); - - for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { - $tbody = ts.processTbody( table, c.$tbodies.eq( tbodyIndex ), true ); - // skip child rows & widget added ( removable ) rows - fixes #448 thanks to @hempel! - // $rows = $tbody.children( 'tr' ).not( c.selectorRemove ); - columnIndex = c.columns; - // convert stored rows into a jQuery object - norm_rows = c.cache[ tbodyIndex ].normalized; - $rows = $( $.map( norm_rows, function( el ) { - return el[ columnIndex ].$row.get(); - }) ); - - if ( combinedFilters === '' || wo.filter_serversideFiltering ) { - $rows - .removeClass( wo.filter_filteredRow ) - .not( '.' + c.cssChildRow ) - .css( 'display', '' ); - } else { - // filter out child rows - $rows = $rows.not( '.' + c.cssChildRow ); - len = $rows.length; - - if ( ( wo.filter_$anyMatch && wo.filter_$anyMatch.length ) || - typeof filters[c.columns] !== 'undefined' ) { - data.anyMatchFlag = true; - data.anyMatchFilter = '' + ( - filters[ c.columns ] || - wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || - '' - ); - if ( wo.filter_columnAnyMatch ) { - // specific columns search - query = data.anyMatchFilter.split( regex.andSplit ); - injected = false; - for ( indx = 0; indx < query.length; indx++ ) { - res = query[ indx ].split( ':' ); - if ( res.length > 1 ) { - // make the column a one-based index ( non-developers start counting from one :P ) - id = parseInt( res[0], 10 ) - 1; - if ( id >= 0 && id < c.columns ) { // if id is an integer - filters[ id ] = res[1]; - query.splice( indx, 1 ); - indx--; - injected = true; + for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { + vars.functions[ columnIndex ] = + ts.getColumnData( table, wo.filter_functions, columnIndex ); + vars.defaultColFilter[ columnIndex ] = + ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || ''; + vars.excludeFilter[ columnIndex ] = + ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split( /\s+/ ); + } + + if ( c.debug ) { + console.log( 'Filter: Starting filter widget search', filters ); + time = new Date(); + } + // filtered rows count + c.filteredRows = 0; + c.totalRows = 0; + // combindedFilters are undefined on init + combinedFilters = ( storedFilters || [] ).join( '' ); + + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody( table, c.$tbodies.eq( tbodyIndex ), true ); + // skip child rows & widget added ( removable ) rows - fixes #448 thanks to @hempel! + // $rows = $tbody.children( 'tr' ).not( c.selectorRemove ); + columnIndex = c.columns; + // convert stored rows into a jQuery object + norm_rows = c.cache[ tbodyIndex ].normalized; + $rows = $( $.map( norm_rows, function( el ) { + return el[ columnIndex ].$row.get(); + }) ); + + if ( combinedFilters === '' || wo.filter_serversideFiltering ) { + $rows + .removeClass( wo.filter_filteredRow ) + .not( '.' + c.cssChildRow ) + .css( 'display', '' ); + } else { + // filter out child rows + $rows = $rows.not( '.' + c.cssChildRow ); + len = $rows.length; + + if ( ( wo.filter_$anyMatch && wo.filter_$anyMatch.length ) || + typeof filters[c.columns] !== 'undefined' ) { + data.anyMatchFlag = true; + data.anyMatchFilter = '' + ( + filters[ c.columns ] || + wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || + '' + ); + if ( wo.filter_columnAnyMatch ) { + // specific columns search + query = data.anyMatchFilter.split( regex.andSplit ); + injected = false; + for ( indx = 0; indx < query.length; indx++ ) { + res = query[ indx ].split( ':' ); + if ( res.length > 1 ) { + // make the column a one-based index ( non-developers start counting from one :P ) + id = parseInt( res[0], 10 ) - 1; + if ( id >= 0 && id < c.columns ) { // if id is an integer + filters[ id ] = res[1]; + query.splice( indx, 1 ); + indx--; + injected = true; + } } } - } - if ( injected ) { - data.anyMatchFilter = query.join( ' && ' ); + if ( injected ) { + data.anyMatchFilter = query.join( ' && ' ); + } } } - } - // optimize searching only through already filtered rows - see #313 - searchFiltered = wo.filter_searchFiltered; - lastSearch = c.lastSearch || c.$table.data( 'lastSearch' ) || []; - if ( searchFiltered ) { - // cycle through all filters; include last ( columnIndex + 1 = match any column ). Fixes #669 - for ( indx = 0; indx < columnIndex + 1; indx++ ) { - val = filters[indx] || ''; - // break out of loop if we've already determined not to search filtered rows - if ( !searchFiltered ) { indx = columnIndex; } - // search already filtered rows if... - searchFiltered = searchFiltered && lastSearch.length && - // there are no changes from beginning of filter - val.indexOf( lastSearch[indx] || '' ) === 0 && - // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string - !regex.alreadyFiltered.test( val ) && - // if we are not doing exact matches, using '|' ( logical or ) or not '!' - !/[=\"\|!]/.test( val ) && - // don't search only filtered if the value is negative - // ( '> -10' => '> -100' will ignore hidden rows ) - !( /(>=?\s*-\d)/.test( val ) || /(<=?\s*\d)/.test( val ) ) && - // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 - !( val !== '' && c.$filters && c.$filters.eq( indx ).find( 'select' ).length && - !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); + // optimize searching only through already filtered rows - see #313 + searchFiltered = wo.filter_searchFiltered; + lastSearch = c.lastSearch || c.$table.data( 'lastSearch' ) || []; + if ( searchFiltered ) { + // cycle through all filters; include last ( columnIndex + 1 = match any column ). Fixes #669 + for ( indx = 0; indx < columnIndex + 1; indx++ ) { + val = filters[indx] || ''; + // break out of loop if we've already determined not to search filtered rows + if ( !searchFiltered ) { indx = columnIndex; } + // search already filtered rows if... + searchFiltered = searchFiltered && lastSearch.length && + // there are no changes from beginning of filter + val.indexOf( lastSearch[indx] || '' ) === 0 && + // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string + !regex.alreadyFiltered.test( val ) && + // if we are not doing exact matches, using '|' ( logical or ) or not '!' + !/[=\"\|!]/.test( val ) && + // don't search only filtered if the value is negative + // ( '> -10' => '> -100' will ignore hidden rows ) + !( /(>=?\s*-\d)/.test( val ) || /(<=?\s*\d)/.test( val ) ) && + // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 + !( val !== '' && c.$filters && c.$filters.eq( indx ).find( 'select' ).length && + !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); + } } - } - notFiltered = $rows.not( '.' + wo.filter_filteredRow ).length; - // can't search when all rows are hidden - this happens when looking for exact matches - if ( searchFiltered && notFiltered === 0 ) { searchFiltered = false; } - if ( c.debug ) { - ts.log( 'Filter: Searching through ' + - ( searchFiltered && notFiltered < len ? notFiltered : 'all' ) + ' rows' ); - } - if ( data.anyMatchFlag ) { - if ( c.sortLocaleCompare ) { - // replace accents - data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter ); + notFiltered = $rows.not( '.' + wo.filter_filteredRow ).length; + // can't search when all rows are hidden - this happens when looking for exact matches + if ( searchFiltered && notFiltered === 0 ) { searchFiltered = false; } + if ( c.debug ) { + console.log( 'Filter: Searching through ' + + ( searchFiltered && notFiltered < len ? notFiltered : 'all' ) + ' rows' ); } - if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultAnyFilter ) ) { - data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter ); - // clear search filtered flag because default filters are not saved to the last search - searchFiltered = false; + if ( data.anyMatchFlag ) { + if ( c.sortLocaleCompare ) { + // replace accents + data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter ); + } + if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultAnyFilter ) ) { + data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter ); + // clear search filtered flag because default filters are not saved to the last search + searchFiltered = false; + } + // make iAnyMatchFilter lowercase unless both filter widget & core ignoreCase options are true + // when c.ignoreCase is true, the cache contains all lower case data + data.iAnyMatchFilter = !( wo.filter_ignoreCase && c.ignoreCase ) ? + data.anyMatchFilter : + data.anyMatchFilter.toLowerCase(); } - // make iAnyMatchFilter lowercase unless both filter widget & core ignoreCase options are true - // when c.ignoreCase is true, the cache contains all lower case data - data.iAnyMatchFilter = !( wo.filter_ignoreCase && c.ignoreCase ) ? - data.anyMatchFilter : - data.anyMatchFilter.toLowerCase(); - } - // loop through the rows - for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { + // loop through the rows + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { - txt = $rows[ rowIndex ].className; - // the first row can never be a child row - isChild = rowIndex && regex.child.test( txt ); - // skip child rows & already filtered rows - if ( isChild || ( searchFiltered && regex.filtered.test( txt ) ) ) { - continue; - } - - data.$row = $rows.eq( rowIndex ); - data.cacheArray = norm_rows[ rowIndex ]; - rowData = data.cacheArray[ c.columns ]; - data.rawArray = rowData.raw; - data.childRowText = ''; - - if ( !wo.filter_childByColumn ) { - txt = ''; - // child row cached text - childRow = rowData.child; - // so, if 'table.config.widgetOptions.filter_childRows' is true and there is - // a match anywhere in the child row, then it will make the row visible - // checked here so the option can be changed dynamically - for ( indx = 0; indx < childRow.length; indx++ ) { - txt += ' ' + childRow[indx].join( '' ) || ''; + txt = $rows[ rowIndex ].className; + // the first row can never be a child row + isChild = rowIndex && regex.child.test( txt ); + // skip child rows & already filtered rows + if ( isChild || ( searchFiltered && regex.filtered.test( txt ) ) ) { + continue; } - data.childRowText = wo.filter_childRows ? - ( wo.filter_ignoreCase ? txt.toLowerCase() : txt ) : - ''; - } - - showRow = ts.filter.processRow( c, data, vars ); - childRow = rowData.$row.filter( ':gt( 0 )' ); - if ( wo.filter_childRows && childRow.length ) { - if ( wo.filter_childByColumn ) { - // cycle through each child row + data.$row = $rows.eq( rowIndex ); + data.cacheArray = norm_rows[ rowIndex ]; + rowData = data.cacheArray[ c.columns ]; + data.rawArray = rowData.raw; + data.childRowText = ''; + + if ( !wo.filter_childByColumn ) { + txt = ''; + // child row cached text + childRow = rowData.child; + // so, if 'table.config.widgetOptions.filter_childRows' is true and there is + // a match anywhere in the child row, then it will make the row visible + // checked here so the option can be changed dynamically for ( indx = 0; indx < childRow.length; indx++ ) { - data.$row = childRow.eq( indx ); - data.cacheArray = rowData.child[ indx ]; - data.rawArray = data.cacheArray; - // use OR comparison on child rows - showRow = showRow || ts.filter.processRow( c, data, vars ); + txt += ' ' + childRow[indx].join( '' ) || ''; } + data.childRowText = wo.filter_childRows ? + ( wo.filter_ignoreCase ? txt.toLowerCase() : txt ) : + ''; + } + + showRow = ts.filter.processRow( c, data, vars ); + childRow = rowData.$row.filter( ':gt( 0 )' ); + + if ( wo.filter_childRows && childRow.length ) { + if ( wo.filter_childByColumn ) { + // cycle through each child row + for ( indx = 0; indx < childRow.length; indx++ ) { + data.$row = childRow.eq( indx ); + data.cacheArray = rowData.child[ indx ]; + data.rawArray = data.cacheArray; + // use OR comparison on child rows + showRow = showRow || ts.filter.processRow( c, data, vars ); + } + } + childRow.toggleClass( wo.filter_filteredRow, !showRow ); } - childRow.toggleClass( wo.filter_filteredRow, !showRow ); - } - rowData.$row - .toggleClass( wo.filter_filteredRow, !showRow )[0] - .display = showRow ? '' : 'none'; + rowData.$row + .toggleClass( wo.filter_filteredRow, !showRow )[0] + .display = showRow ? '' : 'none'; + } } + c.filteredRows += $rows.not( '.' + wo.filter_filteredRow ).length; + c.totalRows += $rows.length; + ts.processTbody( table, $tbody, false ); } - c.filteredRows += $rows.not( '.' + wo.filter_filteredRow ).length; - c.totalRows += $rows.length; - ts.processTbody( table, $tbody, false ); - } - c.lastCombinedFilter = combinedFilters; // save last search - // don't save 'filters' directly since it may have altered ( AnyMatch column searches ) - c.lastSearch = storedFilters; - c.$table.data( 'lastSearch', storedFilters ); - if ( wo.filter_saveFilters && ts.storage ) { - ts.storage( table, 'tablesorter-filters', storedFilters ); - } - if ( c.debug ) { - ts.benchmark( 'Completed filter widget search', time ); - } - if ( wo.filter_initialized ) { - c.$table.trigger( 'filterEnd', c ); - } - setTimeout( function() { - c.$table.trigger( 'applyWidgets' ); // make sure zebra widget is applied - }, 0 ); - }, - getOptionSource: function( table, column, onlyAvail ) { - table = $( table )[0]; - var cts, txt, indx, len, - c = table.config, - wo = c.widgetOptions, - parsed = [], - arry = false, - source = wo.filter_selectSource, - last = c.$table.data( 'lastSearch' ) || [], - fxn = $.isFunction( source ) ? true : ts.getColumnData( table, source, column ); - - if ( onlyAvail && last[column] !== '' ) { - onlyAvail = false; - } - - // filter select source option - if ( fxn === true ) { - // OVERALL source - arry = source( table, column, onlyAvail ); - } else if ( fxn instanceof $ || ( $.type( fxn ) === 'string' && fxn.indexOf( '</option>' ) >= 0 ) ) { - // selectSource is a jQuery object or string of options - return fxn; - } else if ( $.isArray( fxn ) ) { - arry = fxn; - } else if ( $.type( source ) === 'object' && fxn ) { - // custom select source function for a SPECIFIC COLUMN - arry = fxn( table, column, onlyAvail ); - } - if ( arry === false ) { - // fall back to original method - arry = ts.filter.getOptions( table, column, onlyAvail ); - } + c.lastCombinedFilter = combinedFilters; // save last search + // don't save 'filters' directly since it may have altered ( AnyMatch column searches ) + c.lastSearch = storedFilters; + c.$table.data( 'lastSearch', storedFilters ); + if ( wo.filter_saveFilters && ts.storage ) { + ts.storage( table, 'tablesorter-filters', storedFilters ); + } + if ( c.debug ) { + console.log( 'Completed filter widget search' + ts.benchmark(time) ); + } + if ( wo.filter_initialized ) { + c.$table.trigger( 'filterEnd', c ); + } + setTimeout( function() { + c.$table.trigger( 'applyWidgets' ); // make sure zebra widget is applied + }, 0 ); + }, + getOptionSource: function( table, column, onlyAvail ) { + table = $( table )[0]; + var cts, txt, indx, len, + c = table.config, + wo = c.widgetOptions, + parsed = [], + arry = false, + source = wo.filter_selectSource, + last = c.$table.data( 'lastSearch' ) || [], + fxn = $.isFunction( source ) ? true : ts.getColumnData( table, source, column ); + + if ( onlyAvail && last[column] !== '' ) { + onlyAvail = false; + } + + // filter select source option + if ( fxn === true ) { + // OVERALL source + arry = source( table, column, onlyAvail ); + } else if ( fxn instanceof $ || ( $.type( fxn ) === 'string' && fxn.indexOf( '</option>' ) >= 0 ) ) { + // selectSource is a jQuery object or string of options + return fxn; + } else if ( $.isArray( fxn ) ) { + arry = fxn; + } else if ( $.type( source ) === 'object' && fxn ) { + // custom select source function for a SPECIFIC COLUMN + arry = fxn( table, column, onlyAvail ); + } + if ( arry === false ) { + // fall back to original method + arry = ts.filter.getOptions( table, column, onlyAvail ); + } + + // get unique elements and sort the list + // if $.tablesorter.sortText exists ( not in the original tablesorter ), + // then natural sort the list otherwise use a basic sort + arry = $.grep( arry, function( value, indx ) { + return $.inArray( value, arry ) === indx; + }); - // get unique elements and sort the list - // if $.tablesorter.sortText exists ( not in the original tablesorter ), - // then natural sort the list otherwise use a basic sort - arry = $.grep( arry, function( value, indx ) { - return $.inArray( value, arry ) === indx; - }); + if ( c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) { + // unsorted select options + return arry; + } else { + len = arry.length; + // parse select option values + for ( indx = 0; indx < len; indx++ ) { + txt = arry[ indx ]; + // parse array data using set column parser; this DOES NOT pass the original + // table cell to the parser format function + parsed.push({ + t : txt, + // check parser length - fixes #934 + p : c.parsers && c.parsers.length && c.parsers[ column ].format( txt, table, [], column ) || txt + }); + } - if ( c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) { - // unsorted select options - return arry; - } else { - len = arry.length; - // parse select option values - for ( indx = 0; indx < len; indx++ ) { - txt = arry[ indx ]; - // parse array data using set column parser; this DOES NOT pass the original - // table cell to the parser format function - parsed.push({ - t : txt, - // check parser length - fixes #934 - p : c.parsers && c.parsers.length && c.parsers[ column ].format( txt, table, [], column ) || txt + // sort parsed select options + cts = c.textSorter || ''; + parsed.sort( function( a, b ) { + // sortNatural breaks if you don't pass it strings + var x = a.p.toString(), + y = b.p.toString(); + if ( $.isFunction( cts ) ) { + // custom OVERALL text sorter + return cts( x, y, true, column, table ); + } else if ( typeof cts === 'object' && cts.hasOwnProperty( column ) ) { + // custom text sorter for a SPECIFIC COLUMN + return cts[column]( x, y, true, column, table ); + } else if ( ts.sortNatural ) { + // fall back to natural sort + return ts.sortNatural( x, y ); + } + // using an older version! do a basic sort + return true; }); + // rebuild arry from sorted parsed data + arry = []; + len = parsed.length; + for ( indx = 0; indx < len; indx++ ) { + arry.push( parsed[indx].t ); + } + return arry; } - - // sort parsed select options - cts = c.textSorter || ''; - parsed.sort( function( a, b ) { - // sortNatural breaks if you don't pass it strings - var x = a.p.toString(), - y = b.p.toString(); - if ( $.isFunction( cts ) ) { - // custom OVERALL text sorter - return cts( x, y, true, column, table ); - } else if ( typeof( cts ) === 'object' && cts.hasOwnProperty( column ) ) { - // custom text sorter for a SPECIFIC COLUMN - return cts[column]( x, y, true, column, table ); - } else if ( ts.sortNatural ) { - // fall back to natural sort - return ts.sortNatural( x, y ); - } - // using an older version! do a basic sort - return true; - }); - // rebuild arry from sorted parsed data - arry = []; - len = parsed.length; - for ( indx = 0; indx < len; indx++ ) { - arry.push( parsed[indx].t ); + }, + getOptions: function( table, column, onlyAvail ) { + table = $( table )[0]; + var rowIndex, tbodyIndex, len, row, cache, + c = table.config, + wo = c.widgetOptions, + arry = []; + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + cache = c.cache[tbodyIndex]; + len = c.cache[tbodyIndex].normalized.length; + // loop through the rows + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { + // get cached row from cache.row ( old ) or row data object + // ( new; last item in normalized array ) + row = cache.row ? + cache.row[ rowIndex ] : + cache.normalized[ rowIndex ][ c.columns ].$row[0]; + // check if has class filtered + if ( onlyAvail && row.className.match( wo.filter_filteredRow ) ) { + continue; + } + // get non-normalized cell content + if ( wo.filter_useParsedData || + c.parsers[column].parsed || + c.$headerIndexed[column].hasClass( 'filter-parsed' ) ) { + arry.push( '' + cache.normalized[ rowIndex ][ column ] ); + } else { + // get raw cached data instead of content directly from the cells + arry.push( cache.normalized[ rowIndex ][ c.columns ].raw[ column ] ); + } + } } return arry; - } - }, - getOptions: function( table, column, onlyAvail ) { - table = $( table )[0]; - var rowIndex, tbodyIndex, len, row, cache, - c = table.config, - wo = c.widgetOptions, - arry = []; - for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { - cache = c.cache[tbodyIndex]; - len = c.cache[tbodyIndex].normalized.length; - // loop through the rows - for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { - // get cached row from cache.row ( old ) or row data object - // ( new; last item in normalized array ) - row = cache.row ? - cache.row[ rowIndex ] : - cache.normalized[ rowIndex ][ c.columns ].$row[0]; - // check if has class filtered - if ( onlyAvail && row.className.match( wo.filter_filteredRow ) ) { - continue; - } - // get non-normalized cell content - if ( wo.filter_useParsedData || - c.parsers[column].parsed || - c.$headerIndexed[column].hasClass( 'filter-parsed' ) ) { - arry.push( '' + cache.normalized[ rowIndex ][ column ] ); - } else { - // get raw cached data instead of content directly from the cells - arry.push( cache.normalized[ rowIndex ][ c.columns ].raw[ column ] ); - } + }, + buildSelect: function( table, column, arry, updating, onlyAvail ) { + table = $( table )[0]; + column = parseInt( column, 10 ); + if ( !table.config.cache || $.isEmptyObject( table.config.cache ) ) { + return; } - } - return arry; - }, - buildSelect: function( table, column, arry, updating, onlyAvail ) { - table = $( table )[0]; - column = parseInt( column, 10 ); - if ( !table.config.cache || $.isEmptyObject( table.config.cache ) ) { - return; - } - var indx, val, txt, t, $filters, $filter, - c = table.config, - wo = c.widgetOptions, - node = c.$headerIndexed[ column ], - // t.data( 'placeholder' ) won't work in jQuery older than 1.4.3 - options = '<option value="">' + - ( node.data( 'placeholder' ) || - node.attr( 'data-placeholder' ) || - wo.filter_placeholder.select || '' - ) + '</option>', - // Get curent filter value - currentValue = c.$table - .find( 'thead' ) - .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) - .val(); - // nothing included in arry ( external source ), so get the options from - // filter_selectSource or column data - if ( typeof arry === 'undefined' || arry === '' ) { - arry = ts.filter.getOptionSource( table, column, onlyAvail ); - } - - if ( $.isArray( arry ) ) { - // build option list - for ( indx = 0; indx < arry.length; indx++ ) { - txt = arry[indx] = ( '' + arry[indx] ).replace( /\"/g, '"' ); - val = txt; - // allow including a symbol in the selectSource array - // 'a-z|A through Z' so that 'a-z' becomes the option value - // and 'A through Z' becomes the option text - if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { - t = txt.split( wo.filter_selectSourceSeparator ); - val = t[0]; - txt = t[1]; - } - // replace quotes - fixes #242 & ignore empty strings - // see http://stackoverflow.com/q/14990971/145346 - options += arry[indx] !== '' ? - '<option ' + - ( val === txt ? '' : 'data-function-name="' + arry[indx] + '" ' ) + - 'value="' + val + '">' + txt + - '</option>' : ''; - } - // clear arry so it doesn't get appended twice - arry = []; - } - - // update all selects in the same column ( clone thead in sticky headers & - // any external selects ) - fixes 473 - $filters = ( c.$filters ? c.$filters : c.$table.children( 'thead' ) ) - .find( '.' + tscss.filter ); - if ( wo.filter_$externalFilters ) { - $filters = $filters && $filters.length ? - $filters.add( wo.filter_$externalFilters ) : - wo.filter_$externalFilters; - } - $filter = $filters.filter( 'select[data-column="' + column + '"]' ); - - // make sure there is a select there! - if ( $filter.length ) { - $filter[ updating ? 'html' : 'append' ]( options ); - if ( !$.isArray( arry ) ) { - // append options if arry is provided externally as a string or jQuery object - // options ( default value ) was already added - $filter.append( arry ).val( currentValue ); + var indx, val, txt, t, $filters, $filter, + c = table.config, + wo = c.widgetOptions, + node = c.$headerIndexed[ column ], + // t.data( 'placeholder' ) won't work in jQuery older than 1.4.3 + options = '<option value="">' + + ( node.data( 'placeholder' ) || + node.attr( 'data-placeholder' ) || + wo.filter_placeholder.select || '' + ) + '</option>', + // Get curent filter value + currentValue = c.$table + .find( 'thead' ) + .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) + .val(); + // nothing included in arry ( external source ), so get the options from + // filter_selectSource or column data + if ( typeof arry === 'undefined' || arry === '' ) { + arry = ts.filter.getOptionSource( table, column, onlyAvail ); + } + + if ( $.isArray( arry ) ) { + // build option list + for ( indx = 0; indx < arry.length; indx++ ) { + txt = arry[indx] = ( '' + arry[indx] ).replace( /\"/g, '"' ); + val = txt; + // allow including a symbol in the selectSource array + // 'a-z|A through Z' so that 'a-z' becomes the option value + // and 'A through Z' becomes the option text + if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { + t = txt.split( wo.filter_selectSourceSeparator ); + val = t[0]; + txt = t[1]; + } + // replace quotes - fixes #242 & ignore empty strings + // see http://stackoverflow.com/q/14990971/145346 + options += arry[indx] !== '' ? + '<option ' + + ( val === txt ? '' : 'data-function-name="' + arry[indx] + '" ' ) + + 'value="' + val + '">' + txt + + '</option>' : ''; + } + // clear arry so it doesn't get appended twice + arry = []; + } + + // update all selects in the same column ( clone thead in sticky headers & + // any external selects ) - fixes 473 + $filters = ( c.$filters ? c.$filters : c.$table.children( 'thead' ) ) + .find( '.' + tscss.filter ); + if ( wo.filter_$externalFilters ) { + $filters = $filters && $filters.length ? + $filters.add( wo.filter_$externalFilters ) : + wo.filter_$externalFilters; + } + $filter = $filters.filter( 'select[data-column="' + column + '"]' ); + + // make sure there is a select there! + if ( $filter.length ) { + $filter[ updating ? 'html' : 'append' ]( options ); + if ( !$.isArray( arry ) ) { + // append options if arry is provided externally as a string or jQuery object + // options ( default value ) was already added + $filter.append( arry ).val( currentValue ); + } + $filter.val( currentValue ); } - $filter.val( currentValue ); - } - }, - buildDefault: function( table, updating ) { - var columnIndex, $header, noSelect, - c = table.config, - wo = c.widgetOptions, - columns = c.columns; - // build default select dropdown - for ( columnIndex = 0; columnIndex < columns; columnIndex++ ) { - $header = c.$headerIndexed[columnIndex]; - noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); - // look for the filter-select class; build/update it if found - if ( ( $header.hasClass( 'filter-select' ) || - ts.getColumnData( table, wo.filter_functions, columnIndex ) === true ) && noSelect ) { - ts.filter.buildSelect( table, columnIndex, '', updating, $header.hasClass( wo.filter_onlyAvail ) ); + }, + buildDefault: function( table, updating ) { + var columnIndex, $header, noSelect, + c = table.config, + wo = c.widgetOptions, + columns = c.columns; + // build default select dropdown + for ( columnIndex = 0; columnIndex < columns; columnIndex++ ) { + $header = c.$headerIndexed[columnIndex]; + noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); + // look for the filter-select class; build/update it if found + if ( ( $header.hasClass( 'filter-select' ) || + ts.getColumnData( table, wo.filter_functions, columnIndex ) === true ) && noSelect ) { + ts.filter.buildSelect( table, columnIndex, '', updating, $header.hasClass( wo.filter_onlyAvail ) ); + } } } - } -}; - -ts.getFilters = function( table, getRaw, setFilters, skipFirst ) { - var i, $filters, $column, cols, - filters = false, - c = table ? $( table )[0].config : '', - wo = c ? c.widgetOptions : ''; - if ( ( getRaw !== true && wo && !wo.filter_columnFilters ) || - // setFilters called, but last search is exactly the same as the current - // fixes issue #733 & #903 where calling update causes the input values to reset - ( $.isArray(setFilters) && setFilters.join('') === c.lastCombinedFilter ) ) { - return $( table ).data( 'lastSearch' ); - } - if ( c ) { - if ( c.$filters ) { - $filters = c.$filters.find( '.' + tscss.filter ); - } - if ( wo.filter_$externalFilters ) { - $filters = $filters && $filters.length ? - $filters.add( wo.filter_$externalFilters ) : - wo.filter_$externalFilters; - } - if ( $filters && $filters.length ) { - filters = setFilters || []; - for ( i = 0; i < c.columns + 1; i++ ) { - cols = ( i === c.columns ? - // 'all' columns can now include a range or set of columms ( data-column='0-2,4,6-7' ) - wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector : - '[data-column="' + i + '"]' ); - $column = $filters.filter( cols ); - if ( $column.length ) { - // move the latest search to the first slot in the array - $column = ts.filter.getLatestSearch( $column ); - if ( $.isArray( setFilters ) ) { - // skip first ( latest input ) to maintain cursor position while typing - if ( skipFirst && $column.length > 1 ) { - $column = $column.slice( 1 ); - } - if ( i === c.columns ) { - // prevent data-column='all' from filling data-column='0,1' ( etc ) - cols = $column.filter( wo.filter_anyColumnSelector ); - $column = cols.length ? cols : $column; - } - $column - .val( setFilters[ i ] ) - .trigger( 'change.tsfilter' ); - } else { - filters[i] = $column.val() || ''; - // don't change the first... it will move the cursor - if ( i === c.columns ) { - // don't update range columns from 'all' setting + }; + + ts.getFilters = function( table, getRaw, setFilters, skipFirst ) { + var i, $filters, $column, cols, + filters = false, + c = table ? $( table )[0].config : '', + wo = c ? c.widgetOptions : ''; + if ( ( getRaw !== true && wo && !wo.filter_columnFilters ) || + // setFilters called, but last search is exactly the same as the current + // fixes issue #733 & #903 where calling update causes the input values to reset + ( $.isArray(setFilters) && setFilters.join('') === c.lastCombinedFilter ) ) { + return $( table ).data( 'lastSearch' ); + } + if ( c ) { + if ( c.$filters ) { + $filters = c.$filters.find( '.' + tscss.filter ); + } + if ( wo.filter_$externalFilters ) { + $filters = $filters && $filters.length ? + $filters.add( wo.filter_$externalFilters ) : + wo.filter_$externalFilters; + } + if ( $filters && $filters.length ) { + filters = setFilters || []; + for ( i = 0; i < c.columns + 1; i++ ) { + cols = ( i === c.columns ? + // 'all' columns can now include a range or set of columms ( data-column='0-2,4,6-7' ) + wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector : + '[data-column="' + i + '"]' ); + $column = $filters.filter( cols ); + if ( $column.length ) { + // move the latest search to the first slot in the array + $column = ts.filter.getLatestSearch( $column ); + if ( $.isArray( setFilters ) ) { + // skip first ( latest input ) to maintain cursor position while typing + if ( skipFirst && $column.length > 1 ) { + $column = $column.slice( 1 ); + } + if ( i === c.columns ) { + // prevent data-column='all' from filling data-column='0,1' ( etc ) + cols = $column.filter( wo.filter_anyColumnSelector ); + $column = cols.length ? cols : $column; + } $column - .slice( 1 ) - .filter( '[data-column*="' + $column.attr( 'data-column' ) + '"]' ) - .val( filters[ i ] ); + .val( setFilters[ i ] ) + .trigger( 'change.tsfilter' ); } else { - $column - .slice( 1 ) - .val( filters[ i ] ); + filters[i] = $column.val() || ''; + // don't change the first... it will move the cursor + if ( i === c.columns ) { + // don't update range columns from 'all' setting + $column + .slice( 1 ) + .filter( '[data-column*="' + $column.attr( 'data-column' ) + '"]' ) + .val( filters[ i ] ); + } else { + $column + .slice( 1 ) + .val( filters[ i ] ); + } + } + // save any match input dynamically + if ( i === c.columns && $column.length ) { + wo.filter_$anyMatch = $column; } - } - // save any match input dynamically - if ( i === c.columns && $column.length ) { - wo.filter_$anyMatch = $column; } } } } - } - if ( filters.length === 0 ) { - filters = false; - } - return filters; -}; - -ts.setFilters = function( table, filter, apply, skipFirst ) { - var c = table ? $( table )[0].config : '', - valid = ts.getFilters( table, true, filter, skipFirst ); - if ( c && apply ) { - // ensure new set filters are applied, even if the search is the same - c.lastCombinedFilter = null; - c.lastSearch = []; - ts.filter.searching( c.table, filter, skipFirst ); - c.$table.trigger( 'filterFomatterUpdate' ); - } - return !!valid; -}; + if ( filters.length === 0 ) { + filters = false; + } + return filters; + }; + + ts.setFilters = function( table, filter, apply, skipFirst ) { + var c = table ? $( table )[0].config : '', + valid = ts.getFilters( table, true, filter, skipFirst ); + if ( c && apply ) { + // ensure new set filters are applied, even if the search is the same + c.lastCombinedFilter = null; + c.lastSearch = []; + ts.filter.searching( c.table, filter, skipFirst ); + c.$table.trigger( 'filterFomatterUpdate' ); + } + return !!valid; + }; })( jQuery ); @@ -1985,750 +2000,751 @@ ts.setFilters = function( table, filter, apply, skipFirst ) { * by Rob Garrison */ ;(function ($, window) { -'use strict'; -var ts = $.tablesorter || {}; - -$.extend(ts.css, { - sticky : 'tablesorter-stickyHeader', // stickyHeader - stickyVis : 'tablesorter-sticky-visible', - stickyHide: 'tablesorter-sticky-hidden', - stickyWrap: 'tablesorter-sticky-wrapper' -}); - -// Add a resize event to table headers -ts.addHeaderResizeEvent = function(table, disable, settings) { - table = $(table)[0]; // make sure we're using a dom element - if ( !table.config ) { return; } - var defaults = { - timer : 250 - }, - options = $.extend({}, defaults, settings), - c = table.config, - wo = c.widgetOptions, - checkSizes = function( triggerEvent ) { - var index, headers, $header, sizes, width, height, - len = c.$headers.length; - wo.resize_flag = true; - headers = []; - for ( index = 0; index < len; index++ ) { - $header = c.$headers.eq( index ); - sizes = $header.data( 'savedSizes' ) || [ 0,0 ]; // fixes #394 - width = $header[0].offsetWidth; - height = $header[0].offsetHeight; - if ( width !== sizes[0] || height !== sizes[1] ) { - $header.data( 'savedSizes', [ width, height ] ); - headers.push( $header[0] ); - } - } - if ( headers.length && triggerEvent !== false ) { - c.$table.trigger( 'resize', [ headers ] ); - } + 'use strict'; + var ts = $.tablesorter || {}; + + $.extend(ts.css, { + sticky : 'tablesorter-stickyHeader', // stickyHeader + stickyVis : 'tablesorter-sticky-visible', + stickyHide: 'tablesorter-sticky-hidden', + stickyWrap: 'tablesorter-sticky-wrapper' + }); + + // Add a resize event to table headers + ts.addHeaderResizeEvent = function(table, disable, settings) { + table = $(table)[0]; // make sure we're using a dom element + if ( !table.config ) { return; } + var defaults = { + timer : 250 + }, + options = $.extend({}, defaults, settings), + c = table.config, + wo = c.widgetOptions, + checkSizes = function( triggerEvent ) { + var index, headers, $header, sizes, width, height, + len = c.$headers.length; + wo.resize_flag = true; + headers = []; + for ( index = 0; index < len; index++ ) { + $header = c.$headers.eq( index ); + sizes = $header.data( 'savedSizes' ) || [ 0, 0 ]; // fixes #394 + width = $header[0].offsetWidth; + height = $header[0].offsetHeight; + if ( width !== sizes[0] || height !== sizes[1] ) { + $header.data( 'savedSizes', [ width, height ] ); + headers.push( $header[0] ); + } + } + if ( headers.length && triggerEvent !== false ) { + c.$table.trigger( 'resize', [ headers ] ); + } + wo.resize_flag = false; + }; + checkSizes( false ); + clearInterval(wo.resize_timer); + if (disable) { wo.resize_flag = false; - }; - checkSizes( false ); - clearInterval(wo.resize_timer); - if (disable) { - wo.resize_flag = false; - return false; - } - wo.resize_timer = setInterval(function() { - if (wo.resize_flag) { return; } - checkSizes(); - }, options.timer); -}; - -// Sticky headers based on this awesome article: -// http://css-tricks.com/13465-persistent-headers/ -// and https://github.com/jmosbech/StickyTableHeaders by Jonas Mosbech -// ************************** -ts.addWidget({ - id: "stickyHeaders", - priority: 60, // sticky widget must be initialized after the filter widget! - options: { - stickyHeaders : '', // extra class name added to the sticky header row - stickyHeaders_attachTo : null, // jQuery selector or object to attach sticky header to - stickyHeaders_xScroll : null, // jQuery selector or object to monitor horizontal scroll position (defaults: xScroll > attachTo > window) - stickyHeaders_yScroll : null, // jQuery selector or object to monitor vertical scroll position (defaults: yScroll > attachTo > window) - stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element - stickyHeaders_filteredToTop: true, // scroll table top into view after filtering - stickyHeaders_cloneId : '-sticky', // added to table ID, if it exists - stickyHeaders_addResizeEvent : true, // trigger "resize" event on headers - stickyHeaders_includeCaption : true, // if false and a caption exist, it won't be included in the sticky header - stickyHeaders_zIndex : 2 // The zIndex of the stickyHeaders, allows the user to adjust this to their needs - }, - format: function(table, c, wo) { - // filter widget doesn't initialize on an empty table. Fixes #449 - if ( c.$table.hasClass('hasStickyHeaders') || ($.inArray('filter', c.widgets) >= 0 && !c.$table.hasClass('hasFilters')) ) { - return; + return false; } - var index, len, $t, - $table = c.$table, - // add position: relative to attach element, hopefully it won't cause trouble. - $attach = $(wo.stickyHeaders_attachTo), - namespace = c.namespace + 'stickyheaders ', - // element to watch for the scroll event - $yScroll = $(wo.stickyHeaders_yScroll || wo.stickyHeaders_attachTo || window), - $xScroll = $(wo.stickyHeaders_xScroll || wo.stickyHeaders_attachTo || window), - $thead = $table.children('thead:first'), - $header = $thead.children('tr').not('.sticky-false').children(), - $tfoot = $table.children('tfoot'), - $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '', - stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, - // is this table nested? If so, find parent sticky header wrapper (div, not table) - $nestedSticky = $table.parent().closest('.' + ts.css.table).hasClass('hasStickyHeaders') ? - $table.parent().closest('table.tablesorter')[0].config.widgetOptions.$sticky.parent() : [], - nestedStickyTop = $nestedSticky.length ? $nestedSticky.height() : 0, - // clone table, then wrap to make sticky header - $stickyTable = wo.$sticky = $table.clone() - .addClass('containsStickyHeaders ' + ts.css.sticky + ' ' + wo.stickyHeaders + ' ' + c.namespace.slice(1) + '_extra_table' ) - .wrap('<div class="' + ts.css.stickyWrap + '">'), - $stickyWrap = $stickyTable.parent() - .addClass(ts.css.stickyHide) - .css({ - position : $attach.length ? 'absolute' : 'fixed', - padding : parseInt( $stickyTable.parent().parent().css('padding-left'), 10 ), - top : stickyOffset + nestedStickyTop, - left : 0, - visibility : 'hidden', - zIndex : wo.stickyHeaders_zIndex || 2 - }), - $stickyThead = $stickyTable.children('thead:first'), - $stickyCells, - laststate = '', - spacing = 0, - setWidth = function($orig, $clone){ - var index, width, border, $cell, $this, - $cells = $orig.filter(':visible'), - len = $cells.length; - for ( index = 0; index < len; index++ ) { - $cell = $clone.filter(':visible').eq(index); - $this = $cells.eq(index); - // code from https://github.com/jmosbech/StickyTableHeaders - if ($this.css('box-sizing') === 'border-box') { - width = $this.outerWidth(); - } else { - if ($cell.css('border-collapse') === 'collapse') { - if (window.getComputedStyle) { - width = parseFloat( window.getComputedStyle($this[0], null).width ); + wo.resize_timer = setInterval(function() { + if (wo.resize_flag) { return; } + checkSizes(); + }, options.timer); + }; + + // Sticky headers based on this awesome article: + // http://css-tricks.com/13465-persistent-headers/ + // and https://github.com/jmosbech/StickyTableHeaders by Jonas Mosbech + // ************************** + ts.addWidget({ + id: 'stickyHeaders', + priority: 60, // sticky widget must be initialized after the filter widget! + options: { + stickyHeaders : '', // extra class name added to the sticky header row + stickyHeaders_attachTo : null, // jQuery selector or object to attach sticky header to + stickyHeaders_xScroll : null, // jQuery selector or object to monitor horizontal scroll position (defaults: xScroll > attachTo > window) + stickyHeaders_yScroll : null, // jQuery selector or object to monitor vertical scroll position (defaults: yScroll > attachTo > window) + stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element + stickyHeaders_filteredToTop: true, // scroll table top into view after filtering + stickyHeaders_cloneId : '-sticky', // added to table ID, if it exists + stickyHeaders_addResizeEvent : true, // trigger 'resize' event on headers + stickyHeaders_includeCaption : true, // if false and a caption exist, it won't be included in the sticky header + stickyHeaders_zIndex : 2 // The zIndex of the stickyHeaders, allows the user to adjust this to their needs + }, + format: function(table, c, wo) { + // filter widget doesn't initialize on an empty table. Fixes #449 + if ( c.$table.hasClass('hasStickyHeaders') || ($.inArray('filter', c.widgets) >= 0 && !c.$table.hasClass('hasFilters')) ) { + return; + } + var index, len, $t, + $table = c.$table, + // add position: relative to attach element, hopefully it won't cause trouble. + $attach = $(wo.stickyHeaders_attachTo), + namespace = c.namespace + 'stickyheaders ', + // element to watch for the scroll event + $yScroll = $(wo.stickyHeaders_yScroll || wo.stickyHeaders_attachTo || window), + $xScroll = $(wo.stickyHeaders_xScroll || wo.stickyHeaders_attachTo || window), + $thead = $table.children('thead:first'), + $header = $thead.children('tr').not('.sticky-false').children(), + $tfoot = $table.children('tfoot'), + $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '', + stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, + // is this table nested? If so, find parent sticky header wrapper (div, not table) + $nestedSticky = $table.parent().closest('.' + ts.css.table).hasClass('hasStickyHeaders') ? + $table.parent().closest('table.tablesorter')[0].config.widgetOptions.$sticky.parent() : [], + nestedStickyTop = $nestedSticky.length ? $nestedSticky.height() : 0, + // clone table, then wrap to make sticky header + $stickyTable = wo.$sticky = $table.clone() + .addClass('containsStickyHeaders ' + ts.css.sticky + ' ' + wo.stickyHeaders + ' ' + c.namespace.slice(1) + '_extra_table' ) + .wrap('<div class="' + ts.css.stickyWrap + '">'), + $stickyWrap = $stickyTable.parent() + .addClass(ts.css.stickyHide) + .css({ + position : $attach.length ? 'absolute' : 'fixed', + padding : parseInt( $stickyTable.parent().parent().css('padding-left'), 10 ), + top : stickyOffset + nestedStickyTop, + left : 0, + visibility : 'hidden', + zIndex : wo.stickyHeaders_zIndex || 2 + }), + $stickyThead = $stickyTable.children('thead:first'), + $stickyCells, + laststate = '', + spacing = 0, + setWidth = function($orig, $clone){ + var index, width, border, $cell, $this, + $cells = $orig.filter(':visible'), + len = $cells.length; + for ( index = 0; index < len; index++ ) { + $cell = $clone.filter(':visible').eq(index); + $this = $cells.eq(index); + // code from https://github.com/jmosbech/StickyTableHeaders + if ($this.css('box-sizing') === 'border-box') { + width = $this.outerWidth(); + } else { + if ($cell.css('border-collapse') === 'collapse') { + if (window.getComputedStyle) { + width = parseFloat( window.getComputedStyle($this[0], null).width ); + } else { + // ie8 only + border = parseFloat( $this.css('border-width') ); + width = $this.outerWidth() - parseFloat( $this.css('padding-left') ) - parseFloat( $this.css('padding-right') ) - border; + } } else { - // ie8 only - border = parseFloat( $this.css('border-width') ); - width = $this.outerWidth() - parseFloat( $this.css('padding-left') ) - parseFloat( $this.css('padding-right') ) - border; + width = $this.width(); } - } else { - width = $this.width(); } + $cell.css({ + 'width': width, + 'min-width': width, + 'max-width': width + }); } - $cell.css({ - 'width': width, - 'min-width': width, - 'max-width': width + }, + resizeHeader = function() { + stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0; + spacing = 0; + $stickyWrap.css({ + left : $attach.length ? parseInt($attach.css('padding-left'), 10) || 0 : + $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing, + width: $table.outerWidth() }); - } - }, - resizeHeader = function() { - stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0; - spacing = 0; - $stickyWrap.css({ - left : $attach.length ? parseInt($attach.css('padding-left'), 10) || 0 : - $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing, - width: $table.outerWidth() - }); - setWidth( $table, $stickyTable ); - setWidth( $header, $stickyCells ); - }, - scrollSticky = function( resizing ) { - if (!$table.is(':visible')) { return; } // fixes #278 - // Detect nested tables - fixes #724 - nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; - var offset = $table.offset(), - yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 - xWindow = $.isWindow( $xScroll[0] ), - // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, - scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, - tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)), - isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', - cssSettings = { visibility : isVisible }; - - if ($attach.length) { - cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); - } - if (xWindow) { - // adjust when scrolling horizontally - fixes issue #143 - cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing; - } - if ($nestedSticky.length) { - cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; - } - $stickyWrap - .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) - .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) - .css(cssSettings); - if (isVisible !== laststate || resizing) { - // make sure the column widths match + setWidth( $table, $stickyTable ); + setWidth( $header, $stickyCells ); + }, + scrollSticky = function( resizing ) { + if (!$table.is(':visible')) { return; } // fixes #278 + // Detect nested tables - fixes #724 + nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; + var offset = $table.offset(), + yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 + xWindow = $.isWindow( $xScroll[0] ), + // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, + scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, + tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)), + isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', + cssSettings = { visibility : isVisible }; + + if ($attach.length) { + cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); + } + if (xWindow) { + // adjust when scrolling horizontally - fixes issue #143 + cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing; + } + if ($nestedSticky.length) { + cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; + } + $stickyWrap + .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) + .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) + .css(cssSettings); + if (isVisible !== laststate || resizing) { + // make sure the column widths match + resizeHeader(); + laststate = isVisible; + } + }; + // only add a position relative if a position isn't already defined + if ($attach.length && !$attach.css('position')) { + $attach.css('position', 'relative'); + } + // fix clone ID, if it exists - fixes #271 + if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; } + // clear out cloned table, except for sticky header + // include caption & filter row (fixes #126 & #249) - don't remove cells to get correct cell indexing + $stickyTable.find('thead:gt(0), tr.sticky-false').hide(); + $stickyTable.find('tbody, tfoot').remove(); + $stickyTable.find('caption').toggle(wo.stickyHeaders_includeCaption); + // issue #172 - find td/th in sticky header + $stickyCells = $stickyThead.children().children(); + $stickyTable.css({ height:0, width:0, margin: 0 }); + // remove resizable block + $stickyCells.find('.' + ts.css.resizer).remove(); + // update sticky header class names to match real header after sorting + $table + .addClass('hasStickyHeaders') + .bind('pagerComplete' + namespace, function() { resizeHeader(); - laststate = isVisible; - } - }; - // only add a position relative if a position isn't already defined - if ($attach.length && !$attach.css('position')) { - $attach.css('position', 'relative'); - } - // fix clone ID, if it exists - fixes #271 - if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; } - // clear out cloned table, except for sticky header - // include caption & filter row (fixes #126 & #249) - don't remove cells to get correct cell indexing - $stickyTable.find('thead:gt(0), tr.sticky-false').hide(); - $stickyTable.find('tbody, tfoot').remove(); - $stickyTable.find('caption').toggle(wo.stickyHeaders_includeCaption); - // issue #172 - find td/th in sticky header - $stickyCells = $stickyThead.children().children(); - $stickyTable.css({ height:0, width:0, margin: 0 }); - // remove resizable block - $stickyCells.find('.' + ts.css.resizer).remove(); - // update sticky header class names to match real header after sorting - $table - .addClass('hasStickyHeaders') - .bind('pagerComplete' + namespace, function() { - resizeHeader(); - }); + }); - ts.bindEvents(table, $stickyThead.children().children('.' + ts.css.header)); + ts.bindEvents(table, $stickyThead.children().children('.' + ts.css.header)); - // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. - $table.after( $stickyWrap ); + // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. + $table.after( $stickyWrap ); - // onRenderHeader is defined, we need to do something about it (fixes #641) - if (c.onRenderHeader) { - $t = $stickyThead.children('tr').children(); - len = $t.length; - for ( index = 0; index < len; index++ ) { - // send second parameter - c.onRenderHeader.apply( $t.eq( index ), [ index, c, $stickyTable ] ); + // onRenderHeader is defined, we need to do something about it (fixes #641) + if (c.onRenderHeader) { + $t = $stickyThead.children('tr').children(); + len = $t.length; + for ( index = 0; index < len; index++ ) { + // send second parameter + c.onRenderHeader.apply( $t.eq( index ), [ index, c, $stickyTable ] ); + } } - } - // make it sticky! - $xScroll.add($yScroll) - .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') ) - .bind('scroll resize '.split(' ').join( namespace ), function( event ) { - scrollSticky( event.type === 'resize' ); - }); - c.$table - .unbind('stickyHeadersUpdate' + namespace) - .bind('stickyHeadersUpdate' + namespace, function(){ - scrollSticky( true ); - }); - - if (wo.stickyHeaders_addResizeEvent) { - ts.addHeaderResizeEvent(table); - } + // make it sticky! + $xScroll.add($yScroll) + .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') ) + .bind('scroll resize '.split(' ').join( namespace ), function( event ) { + scrollSticky( event.type === 'resize' ); + }); + c.$table + .unbind('stickyHeadersUpdate' + namespace) + .bind('stickyHeadersUpdate' + namespace, function(){ + scrollSticky( true ); + }); - // look for filter widget - if ($table.hasClass('hasFilters') && wo.filter_columnFilters) { - // scroll table into view after filtering, if sticky header is active - #482 - $table.bind('filterEnd' + namespace, function() { - // $(':focus') needs jQuery 1.6+ - var $td = $(document.activeElement).closest('td'), - column = $td.parent().children().index($td); - // only scroll if sticky header is active - if ($stickyWrap.hasClass(ts.css.stickyVis) && wo.stickyHeaders_filteredToTop) { - // scroll to original table (not sticky clone) - window.scrollTo(0, $table.position().top); - // give same input/select focus; check if c.$filters exists; fixes #594 - if (column >= 0 && c.$filters) { - c.$filters.eq(column).find('a, select, input').filter(':visible').focus(); + if (wo.stickyHeaders_addResizeEvent) { + ts.addHeaderResizeEvent(table); + } + + // look for filter widget + if ($table.hasClass('hasFilters') && wo.filter_columnFilters) { + // scroll table into view after filtering, if sticky header is active - #482 + $table.bind('filterEnd' + namespace, function() { + // $(':focus') needs jQuery 1.6+ + var $td = $(document.activeElement).closest('td'), + column = $td.parent().children().index($td); + // only scroll if sticky header is active + if ($stickyWrap.hasClass(ts.css.stickyVis) && wo.stickyHeaders_filteredToTop) { + // scroll to original table (not sticky clone) + window.scrollTo(0, $table.position().top); + // give same input/select focus; check if c.$filters exists; fixes #594 + if (column >= 0 && c.$filters) { + c.$filters.eq(column).find('a, select, input').filter(':visible').focus(); + } } + }); + ts.filter.bindSearch( $table, $stickyCells.find('.' + ts.css.filter) ); + // support hideFilters + if (wo.filter_hideFilters) { + ts.filter.hideFilters($stickyTable, c); } - }); - ts.filter.bindSearch( $table, $stickyCells.find('.' + ts.css.filter) ); - // support hideFilters - if (wo.filter_hideFilters) { - ts.filter.hideFilters($stickyTable, c); } - } - $table.trigger('stickyHeadersInit'); - - }, - remove: function(table, c, wo) { - var namespace = c.namespace + 'stickyheaders '; - c.$table - .removeClass('hasStickyHeaders') - .unbind( ('pagerComplete filterEnd stickyHeadersUpdate '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) - .next('.' + ts.css.stickyWrap).remove(); - if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table - $(window) - .add(wo.stickyHeaders_xScroll) - .add(wo.stickyHeaders_yScroll) - .add(wo.stickyHeaders_attachTo) - .unbind( ('scroll resize '.split(' ').join(namespace)).replace(/\s+/g, ' ') ); - ts.addHeaderResizeEvent(table, false); - } -}); + $table.trigger('stickyHeadersInit'); + + }, + remove: function(table, c, wo) { + var namespace = c.namespace + 'stickyheaders '; + c.$table + .removeClass('hasStickyHeaders') + .unbind( ('pagerComplete filterEnd stickyHeadersUpdate '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) + .next('.' + ts.css.stickyWrap).remove(); + if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table + $(window) + .add(wo.stickyHeaders_xScroll) + .add(wo.stickyHeaders_yScroll) + .add(wo.stickyHeaders_attachTo) + .unbind( ('scroll resize '.split(' ').join(namespace)).replace(/\s+/g, ' ') ); + ts.addHeaderResizeEvent(table, false); + } + }); })(jQuery, window); /*! Widget: resizable - updated 6/26/2015 (v2.22.2) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { -'use strict'; -var ts = $.tablesorter || {}; - -$.extend(ts.css, { - resizableContainer : 'tablesorter-resizable-container', - resizableHandle : 'tablesorter-resizable-handle', - resizableNoSelect : 'tablesorter-disableSelection', - resizableStorage : 'tablesorter-resizable' -}); - -// Add extra scroller css -$(function(){ - var s = '<style>' + - 'body.' + ts.css.resizableNoSelect + ' { -ms-user-select: none; -moz-user-select: -moz-none;' + - '-khtml-user-select: none; -webkit-user-select: none; user-select: none; }' + - '.' + ts.css.resizableContainer + ' { position: relative; height: 1px; }' + - // make handle z-index > than stickyHeader z-index, so the handle stays above sticky header - '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px;' + - 'top: 1px; cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' + - '</style>'; - $(s).appendTo('body'); -}); - -ts.resizable = { - init : function( c, wo ) { - if ( c.$table.hasClass( 'hasResizable' ) ) { return; } - c.$table.addClass( 'hasResizable' ); - - var noResize, $header, column, storedSizes, tmp, - $table = c.$table, - $parent = $table.parent(), - marginTop = parseInt( $table.css( 'margin-top' ), 10 ), - - // internal variables - vars = wo.resizable_ = { - useStorage : ts.storage && wo.resizable !== false, - $wrap : $parent, - mouseXPosition : 0, - $target : null, - $next : null, - overflow : $parent.css('overflow') === 'auto' || - $parent.css('overflow') === 'scroll' || - $parent.css('overflow-x') === 'auto' || - $parent.css('overflow-x') === 'scroll', - storedSizes : [] - }; - - // set default widths - ts.resizableReset( c.table, true ); - - // now get measurements! - vars.tableWidth = $table.width(); - // attempt to autodetect - vars.fullWidth = Math.abs( $parent.width() - vars.tableWidth ) < 20; - - /* - // Hacky method to determine if table width is set to "auto" - // http://stackoverflow.com/a/20892048/145346 - if ( !vars.fullWidth ) { - tmp = $table.width(); - $header = $table.wrap('<span>').parent(); // temp variable - storedSizes = parseInt( $table.css( 'margin-left' ), 10 ) || 0; - $table.css( 'margin-left', storedSizes + 50 ); - vars.tableWidth = $header.width() > tmp ? 'auto' : tmp; - $table.css( 'margin-left', storedSizes ? storedSizes : '' ); - $header = null; - $table.unwrap('<span>'); - } - */ + 'use strict'; + var ts = $.tablesorter || {}; + + $.extend(ts.css, { + resizableContainer : 'tablesorter-resizable-container', + resizableHandle : 'tablesorter-resizable-handle', + resizableNoSelect : 'tablesorter-disableSelection', + resizableStorage : 'tablesorter-resizable' + }); - if ( vars.useStorage && vars.overflow ) { - // save table width - ts.storage( c.table, 'tablesorter-table-original-css-width', vars.tableWidth ); - tmp = ts.storage( c.table, 'tablesorter-table-resized-width' ) || 'auto'; - ts.resizable.setWidth( $table, tmp, true ); - } - wo.resizable_.storedSizes = storedSizes = ( vars.useStorage ? - ts.storage( c.table, ts.css.resizableStorage ) : - [] ) || []; - ts.resizable.setWidths( c, wo, storedSizes ); - ts.resizable.updateStoredSizes( c, wo ); - - wo.$resizable_container = $( '<div class="' + ts.css.resizableContainer + '">' ) - .css({ top : marginTop }) - .insertBefore( $table ); - // add container - for ( column = 0; column < c.columns; column++ ) { - $header = c.$headerIndexed[ column ]; - tmp = ts.getColumnData( c.table, c.headers, column ); - noResize = ts.getData( $header, tmp, 'resizable' ) === 'false'; - if ( !noResize ) { - $( '<div class="' + ts.css.resizableHandle + '">' ) - .appendTo( wo.$resizable_container ) - .attr({ - 'data-column' : column, - 'unselectable' : 'on' - }) - .data( 'header', $header ) - .bind( 'selectstart', false ); - } - } - $table.one('tablesorter-initialized', function() { - ts.resizable.setHandlePosition( c, wo ); - ts.resizable.bindings( this.config, this.config.widgetOptions ); - }); - }, - - updateStoredSizes : function( c, wo ) { - var column, $header, - len = c.columns, - vars = wo.resizable_; - vars.storedSizes = []; - for ( column = 0; column < len; column++ ) { - $header = c.$headerIndexed[ column ]; - vars.storedSizes[ column ] = $header.is(':visible') ? $header.width() : 0; - } - }, - - setWidth : function( $el, width, overflow ) { - // overflow tables need min & max width set as well - $el.css({ - 'width' : width, - 'min-width' : overflow ? width : '', - 'max-width' : overflow ? width : '' - }); - }, - - setWidths : function( c, wo, storedSizes ) { - var column, $temp, - vars = wo.resizable_, - $extra = $( c.namespace + '_extra_headers' ), - $col = c.$table.children( 'colgroup' ).children( 'col' ); - storedSizes = storedSizes || vars.storedSizes || []; - // process only if table ID or url match - if ( storedSizes.length ) { + // Add extra scroller css + $(function(){ + var s = '<style>' + + 'body.' + ts.css.resizableNoSelect + ' { -ms-user-select: none; -moz-user-select: -moz-none;' + + '-khtml-user-select: none; -webkit-user-select: none; user-select: none; }' + + '.' + ts.css.resizableContainer + ' { position: relative; height: 1px; }' + + // make handle z-index > than stickyHeader z-index, so the handle stays above sticky header + '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px;' + + 'top: 1px; cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' + + '</style>'; + $(s).appendTo('body'); + }); + + ts.resizable = { + init : function( c, wo ) { + if ( c.$table.hasClass( 'hasResizable' ) ) { return; } + c.$table.addClass( 'hasResizable' ); + + var noResize, $header, column, storedSizes, tmp, + $table = c.$table, + $parent = $table.parent(), + marginTop = parseInt( $table.css( 'margin-top' ), 10 ), + + // internal variables + vars = wo.resizable_vars = { + useStorage : ts.storage && wo.resizable !== false, + $wrap : $parent, + mouseXPosition : 0, + $target : null, + $next : null, + overflow : $parent.css('overflow') === 'auto' || + $parent.css('overflow') === 'scroll' || + $parent.css('overflow-x') === 'auto' || + $parent.css('overflow-x') === 'scroll', + storedSizes : [] + }; + + // set default widths + ts.resizableReset( c.table, true ); + + // now get measurements! + vars.tableWidth = $table.width(); + // attempt to autodetect + vars.fullWidth = Math.abs( $parent.width() - vars.tableWidth ) < 20; + + /* + // Hacky method to determine if table width is set to 'auto' + // http://stackoverflow.com/a/20892048/145346 + if ( !vars.fullWidth ) { + tmp = $table.width(); + $header = $table.wrap('<span>').parent(); // temp variable + storedSizes = parseInt( $table.css( 'margin-left' ), 10 ) || 0; + $table.css( 'margin-left', storedSizes + 50 ); + vars.tableWidth = $header.width() > tmp ? 'auto' : tmp; + $table.css( 'margin-left', storedSizes ? storedSizes : '' ); + $header = null; + $table.unwrap('<span>'); + } + */ + + if ( vars.useStorage && vars.overflow ) { + // save table width + ts.storage( c.table, 'tablesorter-table-original-css-width', vars.tableWidth ); + tmp = ts.storage( c.table, 'tablesorter-table-resized-width' ) || 'auto'; + ts.resizable.setWidth( $table, tmp, true ); + } + wo.resizable_vars.storedSizes = storedSizes = ( vars.useStorage ? + ts.storage( c.table, ts.css.resizableStorage ) : + [] ) || []; + ts.resizable.setWidths( c, wo, storedSizes ); + ts.resizable.updateStoredSizes( c, wo ); + + wo.$resizable_container = $( '<div class="' + ts.css.resizableContainer + '">' ) + .css({ top : marginTop }) + .insertBefore( $table ); + // add container for ( column = 0; column < c.columns; column++ ) { - // set saved resizable widths - ts.resizable.setWidth( c.$headerIndexed[ column ], storedSizes[ column ], vars.overflow ); - if ( $extra.length ) { - // stickyHeaders needs to modify min & max width as well - $temp = $extra.eq( column ).add( $col.eq( column ) ); - ts.resizable.setWidth( $temp, storedSizes[ column ], vars.overflow ); + $header = c.$headerIndexed[ column ]; + tmp = ts.getColumnData( c.table, c.headers, column ); + noResize = ts.getData( $header, tmp, 'resizable' ) === 'false'; + if ( !noResize ) { + $( '<div class="' + ts.css.resizableHandle + '">' ) + .appendTo( wo.$resizable_container ) + .attr({ + 'data-column' : column, + 'unselectable' : 'on' + }) + .data( 'header', $header ) + .bind( 'selectstart', false ); } } - $temp = $( c.namespace + '_extra_table' ); - if ( $temp.length && !ts.hasWidget( c.table, 'scroller' ) ) { - ts.resizable.setWidth( $temp, c.$table.outerWidth(), vars.overflow ); - } - } - }, - - setHandlePosition : function( c, wo ) { - var startPosition, - hasScroller = ts.hasWidget( c.table, 'scroller' ), - tableHeight = c.$table.height(), - $handles = wo.$resizable_container.children(), - handleCenter = Math.floor( $handles.width() / 2 ); - - if ( hasScroller ) { - tableHeight = 0; - c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function(){ - var $this = $(this); - // center table has a max-height set - tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); + $table.one('tablesorter-initialized', function() { + ts.resizable.setHandlePosition( c, wo ); + ts.resizable.bindings( this.config, this.config.widgetOptions ); }); - } - // subtract out table left position from resizable handles. Fixes #864 - startPosition = c.$table.position().left; - $handles.each( function() { - var $this = $(this), - column = parseInt( $this.attr( 'data-column' ), 10 ), - columns = c.columns - 1, - $header = $this.data( 'header' ); - if ( !$header ) { return; } // see #859 - if ( !$header.is(':visible') ) { - $this.hide(); - } else if ( column < columns || column === columns && wo.resizable_addLastColumn ) { - $this.css({ - display: 'inline-block', - height : tableHeight, - left : $header.position().left - startPosition + $header.outerWidth() - handleCenter - }); + }, + + updateStoredSizes : function( c, wo ) { + var column, $header, + len = c.columns, + vars = wo.resizable_vars; + vars.storedSizes = []; + for ( column = 0; column < len; column++ ) { + $header = c.$headerIndexed[ column ]; + vars.storedSizes[ column ] = $header.is(':visible') ? $header.width() : 0; } - }); - }, - - // prevent text selection while dragging resize bar - toggleTextSelection : function( c, toggle ) { - var namespace = c.namespace + 'tsresize'; - c.widgetOptions.resizable_.disabled = toggle; - $( 'body' ).toggleClass( ts.css.resizableNoSelect, toggle ); - if ( toggle ) { - $( 'body' ) - .attr( 'unselectable', 'on' ) - .bind( 'selectstart' + namespace, false ); - } else { - $( 'body' ) - .removeAttr( 'unselectable' ) - .unbind( 'selectstart' + namespace ); - } - }, + }, - bindings : function( c, wo ) { - var namespace = c.namespace + 'tsresize'; - wo.$resizable_container.children().bind( 'mousedown', function( event ) { - // save header cell and mouse position - var column, - vars = wo.resizable_, - $extras = $( c.namespace + '_extra_headers' ), - $header = $( event.target ).data( 'header' ); + setWidth : function( $el, width, overflow ) { + // overflow tables need min & max width set as well + $el.css({ + 'width' : width, + 'min-width' : overflow ? width : '', + 'max-width' : overflow ? width : '' + }); + }, - column = parseInt( $header.attr( 'data-column' ), 10 ); - vars.$target = $header = $header.add( $extras.filter('[data-column="' + column + '"]') ); - vars.target = column; + setWidths : function( c, wo, storedSizes ) { + var column, $temp, + vars = wo.resizable_vars, + $extra = $( c.namespace + '_extra_headers' ), + $col = c.$table.children( 'colgroup' ).children( 'col' ); + storedSizes = storedSizes || vars.storedSizes || []; + // process only if table ID or url match + if ( storedSizes.length ) { + for ( column = 0; column < c.columns; column++ ) { + // set saved resizable widths + ts.resizable.setWidth( c.$headerIndexed[ column ], storedSizes[ column ], vars.overflow ); + if ( $extra.length ) { + // stickyHeaders needs to modify min & max width as well + $temp = $extra.eq( column ).add( $col.eq( column ) ); + ts.resizable.setWidth( $temp, storedSizes[ column ], vars.overflow ); + } + } + $temp = $( c.namespace + '_extra_table' ); + if ( $temp.length && !ts.hasWidget( c.table, 'scroller' ) ) { + ts.resizable.setWidth( $temp, c.$table.outerWidth(), vars.overflow ); + } + } + }, - // if table is not as wide as it's parent, then resize the table - vars.$next = event.shiftKey || wo.resizable_targetLast ? - $header.parent().children().not( '.resizable-false' ).filter( ':last' ) : - $header.nextAll( ':not(.resizable-false)' ).eq( 0 ); + setHandlePosition : function( c, wo ) { + var startPosition, + hasScroller = ts.hasWidget( c.table, 'scroller' ), + tableHeight = c.$table.height(), + $handles = wo.$resizable_container.children(), + handleCenter = Math.floor( $handles.width() / 2 ); + + if ( hasScroller ) { + tableHeight = 0; + c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function(){ + var $this = $(this); + // center table has a max-height set + tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); + }); + } + // subtract out table left position from resizable handles. Fixes #864 + startPosition = c.$table.position().left; + $handles.each( function() { + var $this = $(this), + column = parseInt( $this.attr( 'data-column' ), 10 ), + columns = c.columns - 1, + $header = $this.data( 'header' ); + if ( !$header ) { return; } // see #859 + if ( !$header.is(':visible') ) { + $this.hide(); + } else if ( column < columns || column === columns && wo.resizable_addLastColumn ) { + $this.css({ + display: 'inline-block', + height : tableHeight, + left : $header.position().left - startPosition + $header.outerWidth() - handleCenter + }); + } + }); + }, - column = parseInt( vars.$next.attr( 'data-column' ), 10 ); - vars.$next = vars.$next.add( $extras.filter('[data-column="' + column + '"]') ); - vars.next = column; + // prevent text selection while dragging resize bar + toggleTextSelection : function( c, toggle ) { + var namespace = c.namespace + 'tsresize'; + c.widgetOptions.resizable_vars.disabled = toggle; + $( 'body' ).toggleClass( ts.css.resizableNoSelect, toggle ); + if ( toggle ) { + $( 'body' ) + .attr( 'unselectable', 'on' ) + .bind( 'selectstart' + namespace, false ); + } else { + $( 'body' ) + .removeAttr( 'unselectable' ) + .unbind( 'selectstart' + namespace ); + } + }, - vars.mouseXPosition = event.pageX; - ts.resizable.updateStoredSizes( c, wo ); - ts.resizable.toggleTextSelection( c, true ); - }); + bindings : function( c, wo ) { + var namespace = c.namespace + 'tsresize'; + wo.$resizable_container.children().bind( 'mousedown', function( event ) { + // save header cell and mouse position + var column, + vars = wo.resizable_vars, + $extras = $( c.namespace + '_extra_headers' ), + $header = $( event.target ).data( 'header' ); + + column = parseInt( $header.attr( 'data-column' ), 10 ); + vars.$target = $header = $header.add( $extras.filter('[data-column="' + column + '"]') ); + vars.target = column; + + // if table is not as wide as it's parent, then resize the table + vars.$next = event.shiftKey || wo.resizable_targetLast ? + $header.parent().children().not( '.resizable-false' ).filter( ':last' ) : + $header.nextAll( ':not(.resizable-false)' ).eq( 0 ); + + column = parseInt( vars.$next.attr( 'data-column' ), 10 ); + vars.$next = vars.$next.add( $extras.filter('[data-column="' + column + '"]') ); + vars.next = column; + + vars.mouseXPosition = event.pageX; + ts.resizable.updateStoredSizes( c, wo ); + ts.resizable.toggleTextSelection( c, true ); + }); - $( document ) - .bind( 'mousemove' + namespace, function( event ) { - var vars = wo.resizable_; - // ignore mousemove if no mousedown - if ( !vars.disabled || vars.mouseXPosition === 0 || !vars.$target ) { return; } - if ( wo.resizable_throttle ) { - clearTimeout( vars.timer ); - vars.timer = setTimeout( function() { + $( document ) + .bind( 'mousemove' + namespace, function( event ) { + var vars = wo.resizable_vars; + // ignore mousemove if no mousedown + if ( !vars.disabled || vars.mouseXPosition === 0 || !vars.$target ) { return; } + if ( wo.resizable_throttle ) { + clearTimeout( vars.timer ); + vars.timer = setTimeout( function() { + ts.resizable.mouseMove( c, wo, event ); + }, isNaN( wo.resizable_throttle ) ? 5 : wo.resizable_throttle ); + } else { ts.resizable.mouseMove( c, wo, event ); - }, isNaN( wo.resizable_throttle ) ? 5 : wo.resizable_throttle ); - } else { - ts.resizable.mouseMove( c, wo, event ); - } - }) - .bind( 'mouseup' + namespace, function() { - if (!wo.resizable_.disabled) { return; } - ts.resizable.toggleTextSelection( c, false ); - ts.resizable.stopResize( c, wo ); + } + }) + .bind( 'mouseup' + namespace, function() { + if (!wo.resizable_vars.disabled) { return; } + ts.resizable.toggleTextSelection( c, false ); + ts.resizable.stopResize( c, wo ); + ts.resizable.setHandlePosition( c, wo ); + }); + + // resizeEnd event triggered by scroller widget + $( window ).bind( 'resize' + namespace + ' resizeEnd' + namespace, function() { ts.resizable.setHandlePosition( c, wo ); }); - // resizeEnd event triggered by scroller widget - $( window ).bind( 'resize' + namespace + ' resizeEnd' + namespace, function() { - ts.resizable.setHandlePosition( c, wo ); - }); + // right click to reset columns to default widths + c.$table + .bind( 'columnUpdate' + namespace, function() { + ts.resizable.setHandlePosition( c, wo ); + }) + .find( 'thead:first' ) + .add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) + .bind( 'contextmenu' + namespace, function() { + // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset + var allowClick = wo.resizable_vars.storedSizes.length === 0; + ts.resizableReset( c.table ); + ts.resizable.setHandlePosition( c, wo ); + wo.resizable_vars.storedSizes = []; + return allowClick; + }); - // right click to reset columns to default widths - c.$table - .bind( 'columnUpdate' + namespace, function() { - ts.resizable.setHandlePosition( c, wo ); - }) - .find( 'thead:first' ) - .add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) - .bind( 'contextmenu' + namespace, function() { - // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset - var allowClick = wo.resizable_.storedSizes.length === 0; - ts.resizableReset( c.table ); - ts.resizable.setHandlePosition( c, wo ); - wo.resizable_.storedSizes = []; - return allowClick; - }); + }, - }, - - mouseMove : function( c, wo, event ) { - if ( wo.resizable_.mouseXPosition === 0 || !wo.resizable_.$target ) { return; } - // resize columns - var column, - total = 0, - vars = wo.resizable_, - $next = vars.$next, - tar = vars.storedSizes[ vars.target ], - leftEdge = event.pageX - vars.mouseXPosition; - if ( vars.overflow ) { - if ( tar + leftEdge > 0 ) { - vars.storedSizes[ vars.target ] += leftEdge; - ts.resizable.setWidth( vars.$target, vars.storedSizes[ vars.target ], true ); - // update the entire table width - for ( column = 0; column < c.columns; column++ ) { - total += vars.storedSizes[ column ]; + mouseMove : function( c, wo, event ) { + if ( wo.resizable_vars.mouseXPosition === 0 || !wo.resizable_vars.$target ) { return; } + // resize columns + var column, + total = 0, + vars = wo.resizable_vars, + $next = vars.$next, + tar = vars.storedSizes[ vars.target ], + leftEdge = event.pageX - vars.mouseXPosition; + if ( vars.overflow ) { + if ( tar + leftEdge > 0 ) { + vars.storedSizes[ vars.target ] += leftEdge; + ts.resizable.setWidth( vars.$target, vars.storedSizes[ vars.target ], true ); + // update the entire table width + for ( column = 0; column < c.columns; column++ ) { + total += vars.storedSizes[ column ]; + } + ts.resizable.setWidth( c.$table.add( $( c.namespace + '_extra_table' ) ), total ); } - ts.resizable.setWidth( c.$table.add( $( c.namespace + '_extra_table' ) ), total ); - } - if ( !$next.length ) { - // if expanding right-most column, scroll the wrapper - vars.$wrap[0].scrollLeft = c.$table.width(); + if ( !$next.length ) { + // if expanding right-most column, scroll the wrapper + vars.$wrap[0].scrollLeft = c.$table.width(); + } + } else if ( vars.fullWidth ) { + vars.storedSizes[ vars.target ] += leftEdge; + vars.storedSizes[ vars.next ] -= leftEdge; + ts.resizable.setWidths( c, wo ); + } else { + vars.storedSizes[ vars.target ] += leftEdge; + ts.resizable.setWidths( c, wo ); } - } else if ( vars.fullWidth ) { - vars.storedSizes[ vars.target ] += leftEdge; - vars.storedSizes[ vars.next ] -= leftEdge; - ts.resizable.setWidths( c, wo ); - } else { - vars.storedSizes[ vars.target ] += leftEdge; - ts.resizable.setWidths( c, wo ); - } - vars.mouseXPosition = event.pageX; - // dynamically update sticky header widths - c.$table.trigger('stickyHeadersUpdate'); - }, - - stopResize : function( c, wo ) { - var vars = wo.resizable_; - ts.resizable.updateStoredSizes( c, wo ); - if ( vars.useStorage ) { - // save all column widths - ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); - ts.storage( c.table, 'tablesorter-table-resized-width', c.$table.width() ); - } - vars.mouseXPosition = 0; - vars.$target = vars.$next = null; - // will update stickyHeaders, just in case, see #912 - c.$table.trigger('stickyHeadersUpdate'); - } -}; - -// this widget saves the column widths if -// $.tablesorter.storage function is included -// ************************** -ts.addWidget({ - id: "resizable", - priority: 40, - options: { - resizable : true, // save column widths to storage - resizable_addLastColumn : false, - resizable_widths : [], - resizable_throttle : false, // set to true (5ms) or any number 0-10 range - resizable_targetLast : false, - resizable_fullWidth : null - }, - init: function(table, thisWidget, c, wo) { - ts.resizable.init( c, wo ); - }, - remove: function( table, c, wo, refreshing ) { - if (wo.$resizable_container) { - var namespace = c.namespace + 'tsresize'; - c.$table.add( $( c.namespace + '_extra_table' ) ) - .removeClass('hasResizable') - .children( 'thead' ).unbind( 'contextmenu' + namespace ); + vars.mouseXPosition = event.pageX; + // dynamically update sticky header widths + c.$table.trigger('stickyHeadersUpdate'); + }, + + stopResize : function( c, wo ) { + var vars = wo.resizable_vars; + ts.resizable.updateStoredSizes( c, wo ); + if ( vars.useStorage ) { + // save all column widths + ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); + ts.storage( c.table, 'tablesorter-table-resized-width', c.$table.width() ); + } + vars.mouseXPosition = 0; + vars.$target = vars.$next = null; + // will update stickyHeaders, just in case, see #912 + c.$table.trigger('stickyHeadersUpdate'); + } + }; + + // this widget saves the column widths if + // $.tablesorter.storage function is included + // ************************** + ts.addWidget({ + id: 'resizable', + priority: 40, + options: { + resizable : true, // save column widths to storage + resizable_addLastColumn : false, + resizable_widths : [], + resizable_throttle : false, // set to true (5ms) or any number 0-10 range + resizable_targetLast : false, + resizable_fullWidth : null + }, + init: function(table, thisWidget, c, wo) { + ts.resizable.init( c, wo ); + }, + remove: function( table, c, wo, refreshing ) { + if (wo.$resizable_container) { + var namespace = c.namespace + 'tsresize'; + c.$table.add( $( c.namespace + '_extra_table' ) ) + .removeClass('hasResizable') + .children( 'thead' ) + .unbind( 'contextmenu' + namespace ); wo.$resizable_container.remove(); - ts.resizable.toggleTextSelection( c, false ); - ts.resizableReset( table, refreshing ); - $( document ).unbind( 'mousemove' + namespace + ' mouseup' + namespace ); + ts.resizable.toggleTextSelection( c, false ); + ts.resizableReset( table, refreshing ); + $( document ).unbind( 'mousemove' + namespace + ' mouseup' + namespace ); + } } - } -}); + }); -ts.resizableReset = function( table, refreshing ) { - $( table ).each(function(){ - var index, $t, - c = this.config, - wo = c && c.widgetOptions, - vars = wo.resizable_; - if ( table && c && c.$headerIndexed.length ) { - // restore the initial table width - if ( vars.overflow && vars.tableWidth ) { - ts.resizable.setWidth( c.$table, vars.tableWidth, true ); - if ( vars.useStorage ) { - ts.storage( table, 'tablesorter-table-resized-width', 'auto' ); + ts.resizableReset = function( table, refreshing ) { + $( table ).each(function(){ + var index, $t, + c = this.config, + wo = c && c.widgetOptions, + vars = wo.resizable_vars; + if ( table && c && c.$headerIndexed.length ) { + // restore the initial table width + if ( vars.overflow && vars.tableWidth ) { + ts.resizable.setWidth( c.$table, vars.tableWidth, true ); + if ( vars.useStorage ) { + ts.storage( table, 'tablesorter-table-resized-width', 'auto' ); + } } - } - for ( index = 0; index < c.columns; index++ ) { - $t = c.$headerIndexed[ index ]; - if ( wo.resizable_widths && wo.resizable_widths[ index ] ) { - ts.resizable.setWidth( $t, wo.resizable_widths[ index ], vars.overflow ); - } else if ( !$t.hasClass( 'resizable-false' ) ) { - // don't clear the width of any column that is not resizable - ts.resizable.setWidth( $t, '', vars.overflow ); + for ( index = 0; index < c.columns; index++ ) { + $t = c.$headerIndexed[ index ]; + if ( wo.resizable_widths && wo.resizable_widths[ index ] ) { + ts.resizable.setWidth( $t, wo.resizable_widths[ index ], vars.overflow ); + } else if ( !$t.hasClass( 'resizable-false' ) ) { + // don't clear the width of any column that is not resizable + ts.resizable.setWidth( $t, '', vars.overflow ); + } } - } - // reset stickyHeader widths - c.$table.trigger( 'stickyHeadersUpdate' ); - if ( ts.storage && !refreshing ) { - ts.storage( this, ts.css.resizableStorage, {} ); + // reset stickyHeader widths + c.$table.trigger( 'stickyHeadersUpdate' ); + if ( ts.storage && !refreshing ) { + ts.storage( this, ts.css.resizableStorage, {} ); + } } - } - }); -}; + }); + }; })( jQuery, window ); /*! Widget: saveSort */ ;(function ($) { -'use strict'; -var ts = $.tablesorter || {}; - -// this widget saves the last sort only if the -// saveSort widget option is true AND the -// $.tablesorter.storage function is included -// ************************** -ts.addWidget({ - id: 'saveSort', - priority: 20, - options: { - saveSort : true - }, - init: function(table, thisWidget, c, wo) { - // run widget format before all other widgets are applied to the table - thisWidget.format(table, c, wo, true); - }, - format: function(table, c, wo, init) { - var stored, time, - $table = c.$table, - saveSort = wo.saveSort !== false, // make saveSort active/inactive; default to true - sortList = { "sortList" : c.sortList }; - if (c.debug) { - time = new Date(); - } - if ($table.hasClass('hasSaveSort')) { - if (saveSort && table.hasInitialized && ts.storage) { - ts.storage( table, 'tablesorter-savesort', sortList ); - if (c.debug) { - ts.benchmark('saveSort widget: Saving last sort: ' + c.sortList, time); + 'use strict'; + var ts = $.tablesorter || {}; + + // this widget saves the last sort only if the + // saveSort widget option is true AND the + // $.tablesorter.storage function is included + // ************************** + ts.addWidget({ + id: 'saveSort', + priority: 20, + options: { + saveSort : true + }, + init: function(table, thisWidget, c, wo) { + // run widget format before all other widgets are applied to the table + thisWidget.format(table, c, wo, true); + }, + format: function(table, c, wo, init) { + var stored, time, + $table = c.$table, + saveSort = wo.saveSort !== false, // make saveSort active/inactive; default to true + sortList = { 'sortList' : c.sortList }; + if (c.debug) { + time = new Date(); + } + if ($table.hasClass('hasSaveSort')) { + if (saveSort && table.hasInitialized && ts.storage) { + ts.storage( table, 'tablesorter-savesort', sortList ); + if (c.debug) { + console.log('saveSort widget: Saving last sort: ' + c.sortList + ts.benchmark(time)); + } + } + } else { + // set table sort on initial run of the widget + $table.addClass('hasSaveSort'); + sortList = ''; + // get data + if (ts.storage) { + stored = ts.storage( table, 'tablesorter-savesort' ); + sortList = (stored && stored.hasOwnProperty('sortList') && $.isArray(stored.sortList)) ? stored.sortList : ''; + if (c.debug) { + console.log('saveSort: Last sort loaded: "' + sortList + '"' + ts.benchmark(time)); + } + $table.bind('saveSortReset', function(event) { + event.stopPropagation(); + ts.storage( table, 'tablesorter-savesort', '' ); + }); + } + // init is true when widget init is run, this will run this widget before all other widgets have initialized + // this method allows using this widget in the original tablesorter plugin; but then it will run all widgets twice. + if (init && sortList && sortList.length > 0) { + c.sortList = sortList; + } else if (table.hasInitialized && sortList && sortList.length > 0) { + // update sort change + $table.trigger('sorton', [ sortList ]); } } - } else { - // set table sort on initial run of the widget - $table.addClass('hasSaveSort'); - sortList = ''; - // get data - if (ts.storage) { - stored = ts.storage( table, 'tablesorter-savesort' ); - sortList = (stored && stored.hasOwnProperty('sortList') && $.isArray(stored.sortList)) ? stored.sortList : ''; - if (c.debug) { - ts.benchmark('saveSort: Last sort loaded: "' + sortList + '"', time); - } - $table.bind('saveSortReset', function(event) { - event.stopPropagation(); - ts.storage( table, 'tablesorter-savesort', '' ); - }); - } - // init is true when widget init is run, this will run this widget before all other widgets have initialized - // this method allows using this widget in the original tablesorter plugin; but then it will run all widgets twice. - if (init && sortList && sortList.length > 0) { - c.sortList = sortList; - } else if (table.hasInitialized && sortList && sortList.length > 0) { - // update sort change - $table.trigger('sorton', [sortList]); - } + }, + remove: function(table, c) { + c.$table.removeClass('hasSaveSort'); + // clear storage + if (ts.storage) { ts.storage( table, 'tablesorter-savesort', '' ); } } - }, - remove: function(table, c) { - c.$table.removeClass('hasSaveSort'); - // clear storage - if (ts.storage) { ts.storage( table, 'tablesorter-savesort', '' ); } - } -}); + }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js index 8e4a49f..7af9585 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js @@ -1,7 +1,7 @@ /*! Parser: Extract out date - updated 10/26/2014 (v2.18.0) */ /*jshint jquery:true */ ;(function($){ -"use strict"; + 'use strict'; var regex = { usLong : /[A-Z]{3,10}\.?\s+\d{1,2},?\s+(?:\d{4})(?:\s+\d{1,2}:\d{2}(?::\d{2})?(?:\s+[AP]M)?)?/i, @@ -15,10 +15,10 @@ }; /*! extract US Long Date *//* (ignore any other text) - * e.g. "Sue's Birthday! Jun 26, 2004 7:22 AM (8# 2oz)" + * e.g. 'Sue's Birthday! Jun 26, 2004 7:22 AM (8# 2oz)' * demo: http://jsfiddle.net/Mottie/abkNM/4165/ */ $.tablesorter.addParser({ - id: "extractUSLongDate", + id: 'extractUSLongDate', is: function () { // don't auto detect this parser return false; @@ -32,67 +32,67 @@ } return s; }, - type: "numeric" + type: 'numeric' }); /*! extract MMDDYYYY *//* (ignore any other text) * demo: http://jsfiddle.net/Mottie/abkNM/4166/ */ $.tablesorter.addParser({ - id: "extractMMDDYYYY", + id: 'extractMMDDYYYY', is: function () { // don't auto detect this parser return false; }, format: function (s) { var date, - str = s ? s.replace(/\s+/g," ").replace(/[\-.,]/g, "/").match(regex.mdy) : s; + str = s ? s.replace(/\s+/g, ' ').replace(/[\-.,]/g, '/').match(regex.mdy) : s; if (str) { date = new Date( str[0] ); return date instanceof Date && isFinite(date) ? date.getTime() : s; } return s; }, - type: "numeric" + type: 'numeric' }); /*! extract DDMMYYYY *//* (ignore any other text) * demo: http://jsfiddle.net/Mottie/abkNM/4167/ */ $.tablesorter.addParser({ - id: "extractDDMMYYYY", + id: 'extractDDMMYYYY', is: function () { // don't auto detect this parser return false; }, format: function (s) { var date, - str = s ? s.replace(/\s+/g," ").replace(/[\-.,]/g, "/").match(regex.dmy) : s; + str = s ? s.replace(/\s+/g, ' ').replace(/[\-.,]/g, '/').match(regex.dmy) : s; if (str) { - date = new Date( str[0].replace(regex.dmyreplace, "$2/$1/$3") ); + date = new Date( str[0].replace(regex.dmyreplace, '$2/$1/$3') ); return date instanceof Date && isFinite(date) ? date.getTime() : s; } return s; }, - type: "numeric" + type: 'numeric' }); /*! extract YYYYMMDD *//* (ignore any other text) * demo: http://jsfiddle.net/Mottie/abkNM/4168/ */ $.tablesorter.addParser({ - id: "extractYYYYMMDD", + id: 'extractYYYYMMDD', is: function () { // don't auto detect this parser return false; }, format: function (s) { var date, - str = s ? s.replace(/\s+/g," ").replace(/[\-.,]/g, "/").match(regex.ymd) : s; + str = s ? s.replace(/\s+/g, ' ').replace(/[\-.,]/g, '/').match(regex.ymd) : s; if (str) { - date = new Date( str[0].replace(regex.ymdreplace, "$2/$3/$1") ); + date = new Date( str[0].replace(regex.ymdreplace, '$2/$3/$1') ); return date instanceof Date && isFinite(date) ? date.getTime() : s; } return s; }, - type: "numeric" + type: 'numeric' }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js index 3483d35..5a1e3e6 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js @@ -6,7 +6,7 @@ */ /*global jQuery: false */ ;(function($){ -"use strict"; + 'use strict'; var iso8601date = /^([0-9]{4})(-([0-9]{2})(-([0-9]{2})(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?$/; diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js index 7caaae0..6eb8679 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js @@ -2,7 +2,7 @@ /* Demo: http://jsfiddle.net/Mottie/abkNM/4169/ */ /*jshint jquery:true */ ;(function($){ -"use strict"; + 'use strict'; var ts = $.tablesorter; ts.dates = $.extend({}, ts.dates, { @@ -12,7 +12,7 @@ ts.dates.monthLower = ts.dates.monthCased.join(',').toLocaleLowerCase().split(','); ts.addParser({ - id: "month", + id: 'month', is: function(){ return false; }, @@ -20,7 +20,7 @@ if (s) { var j = -1, c = table.config, n = c.ignoreCase ? s.toLocaleLowerCase() : s; - $.each(ts.dates[ 'month' + (c.ignoreCase ? 'Lower' : 'Cased') ], function(i,v){ + $.each(ts.dates[ 'month' + (c.ignoreCase ? 'Lower' : 'Cased') ], function(i, v){ if (j < 0 && n.match(v)) { j = i; return false; @@ -32,7 +32,7 @@ } return s; }, - type: "numeric" + type: 'numeric' }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-range.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-range.js index eab7440..dd178be 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-range.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-range.js @@ -2,7 +2,7 @@ /* Include the 'widget-filter-type-insideRange.js' to filter ranges */ /*jshint jquery:true */ ;(function($){ -'use strict'; + 'use strict'; var regex = { mdy : /(\d{1,2}[-\s]\d{1,2}[-\s]\d{4}(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?)/gi, diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js index 43571bd..ab08afa 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js @@ -2,7 +2,7 @@ /* Demo: http://mottie.github.io/tablesorter/docs/example-parsers-dates.html */ /*jshint jquery:true */ ;(function($){ -"use strict"; + 'use strict'; // Make the date be within +/- range of the 2 digit year // so if the current year is 2020, and the 2 digit year is 80 (2080 - 2020 > 50), it becomes 1980 @@ -23,7 +23,7 @@ var y, rng, n = s // replace separators - .replace(/\s+/g," ").replace(/[-.,]/g, "/") + .replace(/\s+/g, ' ').replace(/[-.,]/g, '/') // reformat xx/xx/xx to mm/dd/19yy; .replace(regex, format), d = new Date(n); @@ -31,7 +31,7 @@ y = d.getFullYear(); rng = table && table.config.dateRange || range; // if date > 50 years old (set range), add 100 years - // this will work when people start using "50" and mean "2050" + // this will work when people start using '50' and mean '2050' while (now - y > rng) { y += 100; } @@ -42,39 +42,39 @@ }; $.tablesorter.addParser({ - id: "ddmmyy", + id: 'ddmmyy', is: function() { return false; }, format: function(s, table) { // reformat dd/mm/yy to mm/dd/19yy; - return ts.formatDate(s, ts.dates.regxxxxyy, "$2/$1/19$3", table); + return ts.formatDate(s, ts.dates.regxxxxyy, '$2/$1/19$3', table); }, - type: "numeric" + type: 'numeric' }); $.tablesorter.addParser({ - id: "mmddyy", + id: 'mmddyy', is: function() { return false; }, format: function(s, table) { // reformat mm/dd/yy to mm/dd/19yy - return ts.formatDate(s, ts.dates.regxxxxyy, "$1/$2/19$3", table); + return ts.formatDate(s, ts.dates.regxxxxyy, '$1/$2/19$3', table); }, - type: "numeric" + type: 'numeric' }); $.tablesorter.addParser({ - id: "yymmdd", + id: 'yymmdd', is: function() { return false; }, format: function(s, table) { // reformat yy/mm/dd to mm/dd/19yy - return ts.formatDate(s, ts.dates.regyyxxxx, "$2/$3/19$1", table); + return ts.formatDate(s, ts.dates.regyyxxxx, '$2/$3/19$1', table); }, - type: "numeric" + type: 'numeric' }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js index 7a1b3fe..0a00cda 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js @@ -2,7 +2,7 @@ /* Demo: http://jsfiddle.net/Mottie/abkNM/4169/ */ /*jshint jquery:true */ ;(function($){ -"use strict"; + 'use strict'; var ts = $.tablesorter; ts.dates = $.extend({}, ts.dates, { @@ -12,7 +12,7 @@ ts.dates.weekdayLower = ts.dates.weekdayCased.join(',').toLocaleLowerCase().split(','); ts.addParser({ - id: "weekday", + id: 'weekday', is: function(){ return false; }, @@ -20,7 +20,7 @@ if (s) { var j = -1, c = table.config; s = c.ignoreCase ? s.toLocaleLowerCase() : s; - $.each(ts.dates[ 'weekday' + (c.ignoreCase ? 'Lower' : 'Cased') ], function(i,v){ + $.each(ts.dates[ 'weekday' + (c.ignoreCase ? 'Lower' : 'Cased') ], function(i, v){ if (j < 0 && s.match(v)) { j = i; return false; @@ -32,7 +32,7 @@ } return s; }, - type: "numeric" + type: 'numeric' }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js index dbc30ad..a2acef5 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js @@ -2,7 +2,7 @@ /* Extract dates using popular natural language date parsers */ /*jshint jquery:true */ ;(function($){ -'use strict'; + 'use strict'; /*! Sugar (http://sugarjs.com/dates#comparing_dates) */ /* demo: http://jsfiddle.net/Mottie/abkNM/4163/ */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-duration.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-duration.js index da9d4ba..2ad1fd0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-duration.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-duration.js @@ -1,7 +1,7 @@ /*! Parser: duration & countdown - updated 2/7/2015 (v2.19.0) */ /*jshint jquery:true, unused:false */ ;(function($){ -'use strict'; + 'use strict'; // If any number > 9999, then set table.config.durationLength = 5 // The below regex matches this duration example: 1y 23d 12h 44m 9s diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-feet-inch-fraction.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-feet-inch-fraction.js index eb7f259..3db96cb 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-feet-inch-fraction.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-feet-inch-fraction.js @@ -5,21 +5,21 @@ */ /*global jQuery: false */ ;(function($){ - "use strict"; + 'use strict'; var ts = $.tablesorter; ts.symbolRegex = /[\u215b\u215c\u215d\u215e\u00bc\u00bd\u00be]/g; ts.processFractions = function(n, table) { if (n) { var t, p = 0; - n = $.trim(n.replace(/\"/,'')); - // look for a space in the first part of the number: "10 3/4" and save the "10" + n = $.trim(n.replace(/\"/, '')); + // look for a space in the first part of the number: '10 3/4' and save the '10' if (/\s/.test(n)) { p = ts.formatFloat(n.split(' ')[0], table); // remove stuff to the left of the space n = $.trim(n.substring(n.indexOf(' '), n.length)); } - // look for a "/" to calculate fractions + // look for a '/' to calculate fractions if (/\//g.test(n)) { t = n.split('/'); // turn 3/4 into .75; make sure we don't divide by zero @@ -52,10 +52,10 @@ if (s === '') { return ''; } // look for feet symbol = ' // very generic test to catch 1.1', 1 1/2' and 1½' - var d = (/^\s*\S*(\s+\S+)?\s*\'/.test(s)) ? s.split("'") : [0,s], + var d = (/^\s*\S*(\s+\S+)?\s*\'/.test(s)) ? s.split(/\'/) : [ 0, s ], f = ts.processFractions(d[0], table), // feet i = ts.processFractions(d[1], table); // inches - return (/[\'\"]/).test(s) ? parseFloat(f) + (parseFloat(i)/12 || 0) : parseFloat(f) + parseFloat(i); + return (/[\'\"]/).test(s) ? parseFloat(f) + (parseFloat(i) / 12 || 0) : parseFloat(f) + parseFloat(i); }, type: 'numeric' }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js index e60abce..4180e65 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js @@ -4,33 +4,33 @@ */ /*global jQuery: false */ ;(function($){ -"use strict"; + 'use strict'; // basic list from http://en.wikipedia.org/wiki/List_of_file_formats // To add a custom equivalent, define: - // $.tablesorter.fileTypes.equivalents['xx'] = "A|B|C"; + // $.tablesorter.fileTypes.equivalents['xx'] = 'A|B|C'; $.tablesorter.fileTypes = { // divides filetype extensions in the equivalent list below separator : '|', equivalents : { - "3D Image" : "3dm|3ds|dwg|max|obj", - "Audio" : "aif|aac|ape|flac|la|m4a|mid|midi|mp2|mp3|ogg|ra|raw|rm|wav|wma", - "Compressed" : "7z|bin|cab|cbr|gz|gzip|iso|lha|lz|rar|tar|tgz|zip|zipx|zoo", - "Database" : "csv|dat|db|dbf|json|ldb|mdb|myd|pdb|sql|tsv|wdb|wmdb|xlr|xls|xlsx|xml", - "Development" : "asm|c|class|cls|cpp|cc|cs|cxx|cbp|cs|dba|fla|h|java|lua|pl|py|pyc|pyo|sh|sln|r|rb|vb", - "Document" : "doc|docx|odt|ott|pages|pdf|rtf|tex|wpd|wps|wrd|wri", - "Executable" : "apk|app|com|exe|gadget|lnk|msi", - "Fonts" : "eot|fnt|fon|otf|ttf|woff", - "Icons" : "ani|cur|icns|ico", - "Images" : "bmp|gif|jpg|jpeg|jpe|jp2|pic|png|psd|tga|tif|tiff|wmf|webp", - "Presentation" : "pps|ppt", - "Published" : "chp|epub|lit|pub|ppp|fm|mobi", - "Script" : "as|bat|cgi|cmd|jar|js|lua|scpt|scptd|sh|vbs|vb|wsf", - "Styles" : "css|less|sass", - "Text" : "info|log|md|markdown|nfo|tex|text|txt", - "Vectors" : "awg|ai|eps|cdr|ps|svg", - "Video" : "asf|avi|flv|m4v|mkv|mov|mp4|mpe|mpeg|mpg|ogg|rm|rv|swf|vob|wmv", - "Web" : "asp|aspx|cer|cfm|htm|html|php|url|xhtml" + '3D Image' : '3dm|3ds|dwg|max|obj', + 'Audio' : 'aif|aac|ape|flac|la|m4a|mid|midi|mp2|mp3|ogg|ra|raw|rm|wav|wma', + 'Compressed' : '7z|bin|cab|cbr|gz|gzip|iso|lha|lz|rar|tar|tgz|zip|zipx|zoo', + 'Database' : 'csv|dat|db|dbf|json|ldb|mdb|myd|pdb|sql|tsv|wdb|wmdb|xlr|xls|xlsx|xml', + 'Development' : 'asm|c|class|cls|cpp|cc|cs|cxx|cbp|cs|dba|fla|h|java|lua|pl|py|pyc|pyo|sh|sln|r|rb|vb', + 'Document' : 'doc|docx|odt|ott|pages|pdf|rtf|tex|wpd|wps|wrd|wri', + 'Executable' : 'apk|app|com|exe|gadget|lnk|msi', + 'Fonts' : 'eot|fnt|fon|otf|ttf|woff', + 'Icons' : 'ani|cur|icns|ico', + 'Images' : 'bmp|gif|jpg|jpeg|jpe|jp2|pic|png|psd|tga|tif|tiff|wmf|webp', + 'Presentation' : 'pps|ppt', + 'Published' : 'chp|epub|lit|pub|ppp|fm|mobi', + 'Script' : 'as|bat|cgi|cmd|jar|js|lua|scpt|scptd|sh|vbs|vb|wsf', + 'Styles' : 'css|less|sass', + 'Text' : 'info|log|md|markdown|nfo|tex|text|txt', + 'Vectors' : 'awg|ai|eps|cdr|ps|svg', + 'Video' : 'asf|avi|flv|m4v|mkv|mov|mp4|mpe|mpeg|mpg|ogg|rm|rv|swf|vob|wmv', + 'Web' : 'asp|aspx|cer|cfm|htm|html|php|url|xhtml' } }; @@ -48,9 +48,9 @@ m = $.tablesorter.fileTypes.matching, types = $.tablesorter.fileTypes.equivalents; if (!m) { - // make a string to "quick" match the existing equivalents + // make a string to 'quick' match the existing equivalents t = []; - $.each(types, function(i,v){ + $.each(types, function(i, v){ t.push(v); }); m = $.tablesorter.fileTypes.matching = sep + t.join(sep) + sep; diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-globalize.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-globalize.js index af69a1f..24a08bd 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-globalize.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-globalize.js @@ -3,7 +3,7 @@ Globalize.locale( 'xx' ) prior to initializing tablesorter! */ /*jshint jquery:true */ ;( function( $ ) { -'use strict'; + 'use strict'; /*! jQuery Globalize date parser (https://github.com/jquery/globalize#date-module) */ /* demo: http://jsfiddle.net/Mottie/0j18Lw8r/ */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js index 3424f98..4b6795a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js @@ -1,28 +1,28 @@ /*! Parser: ignoreArticles - updated 9/15/2014 (v2.17.8) *//* - * This parser will remove "The", "A" and "An" from the beginning of a book + * This parser will remove 'The', 'A' and 'An' from the beginning of a book * or movie title, so it sorts by the second word or number * Demo: http://jsfiddle.net/Mottie/abkNM/5/ */ /*jshint browser: true, jquery:true, unused:false */ ;(function($){ -"use strict"; + 'use strict'; -var ts = $.tablesorter; + var ts = $.tablesorter; // basic list from http://en.wikipedia.org/wiki/Article_%28grammar%29 ts.ignoreArticles = { - "en" : "the, a, an", - "de" : "der, die, das, des, dem, den, ein, eine, einer, eines, einem, einen", - "nl" : "de, het, de, een", - "es" : "el, la, lo, los, las, un, una, unos, unas", - "pt" : "o, a, os, as, um, uma, uns, umas", - "fr" : "le, la, l'_, les, un, une, des", - "it" : "il, lo, la, l'_, i, gli, le, un', uno, una, un", - "hu" : "a, az, egy" + 'en' : 'the, a, an', + 'de' : 'der, die, das, des, dem, den, ein, eine, einer, eines, einem, einen', + 'nl' : 'de, het, de, een', + 'es' : 'el, la, lo, los, las, un, una, unos, unas', + 'pt' : 'o, a, os, as, um, uma, uns, umas', + 'fr' : 'le, la, l\'_, les, un, une, des', + 'it' : 'il, lo, la, l\'_, i, gli, le, un\', uno, una, un', + 'hu' : 'a, az, egy' }; // To add a custom parser, define: - // $.tablesorter.ignoreArticles['xx'] = "A, B, C"; + // $.tablesorter.ignoreArticles['xx'] = 'A, B, C'; // and then set the language id 'xx' in the headers option // ignoreArticles : 'xx' @@ -40,11 +40,11 @@ var ts = $.tablesorter; if (!c.headers) { c.headers = {}; } if (!c.headers[cellIndex]) { c.headers[cellIndex] = {}; } lang = ts.getData( c.$headers.eq(cellIndex), ts.getColumnData( table, c.headers, cellIndex ), 'ignoreArticles' ); - art = (ts.ignoreArticles[lang] || "the, a, an" ) + ""; - c.headers[cellIndex].ignoreArticlesRegex = new RegExp('^(' + $.trim( art.split(/\s*\,\s*/).join('\\s|') + "\\s" ).replace("_\\s","") + ')', 'i'); + art = (ts.ignoreArticles[lang] || 'the, a, an' ) + ''; + c.headers[cellIndex].ignoreArticlesRegex = new RegExp('^(' + $.trim( art.split(/\s*\,\s*/).join('\\s|') + '\\s' ).replace('_\\s', '') + ')', 'i'); // exception regex stored in c.headers[cellIndex].ignoreArticlesRegex2 ignore = ts.getData( c.$headers.eq(cellIndex), ts.getColumnData( table, c.headers, cellIndex ), 'ignoreArticlesExcept' ); - c.headers[cellIndex].ignoreArticlesRegex2 = ignore !== '' ? new RegExp('^(' + ignore.replace(/\s/g, "\\s") + ')', 'i') : ''; + c.headers[cellIndex].ignoreArticlesRegex2 = ignore !== '' ? new RegExp('^(' + ignore.replace(/\s/g, '\\s') + ')', 'i') : ''; } art = c.headers[cellIndex].ignoreArticlesRegex; if (art.test(str)) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-image.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-image.js index a8b8723..9157eb0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-image.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-image.js @@ -2,10 +2,10 @@ /* alt attribute parser for jQuery 1.7+ & tablesorter 2.7.11+ */ /*jshint jquery:true, unused:false */ ;(function($){ -"use strict"; + 'use strict'; $.tablesorter.addParser({ - id: "image", + id: 'image', is: function(){ return false; }, @@ -13,7 +13,7 @@ return $(cell).find('img').attr(table.config.imgAttr || 'alt') || s; }, parsed : true, // filter widget flag - type: "text" + type: 'text' }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index e4016ec..2db3ee2 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,10 +1,10 @@ -/*! Parser: input & select - updated 5/17/2015 (v2.22.0) *//* +/*! Parser: input & select - updated 7/28/2015 (v2.22.4) *//* * for jQuery 1.7+ & tablesorter 2.7.11+ * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ /*jshint browser: true, jquery:true, unused:false */ ;( function( $ ) { -'use strict'; + 'use strict'; var updateServer = function( event, $table, $input ) { // do something here to update your server, if needed @@ -53,6 +53,7 @@ }, format : function( txt, table, cell, cellIndex ) { var $cell = $( cell ), + $row = $cell.closest( 'tr' ), wo = table.config.widgetOptions, checkedClass = table.config.checkboxClass || 'checked', // returning plain language here because this is what is shown in the @@ -62,7 +63,13 @@ isChecked = $input.length ? $input[ 0 ].checked : ''; // adding class to row, indicating that a checkbox is checked; includes // a column index in case more than one checkbox happens to be in a row - $cell.closest( 'tr' ).toggleClass( checkedClass + ' ' + checkedClass + '-' + cellIndex, isChecked ); + $row.toggleClass( checkedClass + '-' + cellIndex, isChecked ); + if ( isChecked ) { + $row.addClass( checkedClass ); + } else if ( !( $row[0].className || '' ).match( checkedClass + '-' ) ) { + // don't remove checked class if other columns have a check + $row.removeClass( checkedClass ); + } return $input.length ? status[ isChecked ? 0 : 1 ] : txt; }, parsed : true, // filter widget flag diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js index c216bf7..2fff9d9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js @@ -6,7 +6,7 @@ */ /*jshint jquery:true */ ;( function( $ ) { -'use strict'; + 'use strict'; var prefixes = { // 'prefix' : [ base 10, base 2 ] @@ -23,7 +23,7 @@ 'h|hecto' : [ 1e2, 1e2 ], 'da|deka' : [ 1e1, 1e1 ], 'd|deci' : [ 1e-1, 1e-1 ], - 'c|centi' : [ 1e-2, 1e-2], + 'c|centi' : [ 1e-2, 1e-2 ], 'm|milli' : [ 1e-3, 1e-3 ], 'µ|micro' : [ 1e-6, 1e-6 ], 'n|nano' : [ 1e-9, 1e-9 ], diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js index 53bfd8f..28d2dc8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js @@ -3,7 +3,7 @@ */ /*jshint jquery:true */ ;(function($){ -"use strict"; + 'use strict'; // Change language of the named numbers as needed var named = { @@ -99,7 +99,7 @@ }; $.tablesorter.addParser({ - id: "namedNumbers", + id: 'namedNumbers', is: function () { return false; }, @@ -116,7 +116,7 @@ // make sure to let zero get parsed, so check hasOwnProperty return result || named.numbers.hasOwnProperty( str ) ? result : $.tablesorter.formatFloat( str || '', table ); }, - type: "numeric" + type: 'numeric' }); })( jQuery ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js index 7175578..53927e2 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js @@ -69,7 +69,7 @@ // it's fastest & easiest for tablesorter to sort decimal values (vs hex) groups[i] = hex ? ('0000' + groups[i]).slice(-4) : ('00000' + (parseInt(groups[i], 16) || 0)).slice(-5); - expandedAddress += ( i != validGroupCount-1) ? groups[i] + ':' : groups[i]; + expandedAddress += ( i != validGroupCount - 1) ? groups[i] + ':' : groups[i]; } return hex ? expandedAddress : expandedAddress.replace(/:/g, ''); }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-roman.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-roman.js index 0032a74..0386bde 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-roman.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-roman.js @@ -5,7 +5,7 @@ */ /*jshint jquery:true, unused:false */ ;(function($){ -"use strict"; + 'use strict'; // allow lower case roman numerals, since lists use i, ii, iii, etc. var validator = /^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/i, @@ -34,7 +34,7 @@ return num; }, - type: "numeric" + type: 'numeric' }); $.tablesorter.addParser({ @@ -75,7 +75,7 @@ return num ? s.replace(orig, num) : s; }, - type: "text" + type: 'text' }); $.tablesorter.addParser({ @@ -111,7 +111,7 @@ return num ? num : s; }, - type: "numeric" + type: 'numeric' }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js index fddd039..1894481 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js @@ -5,142 +5,143 @@ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ ;(function($){ -"use strict"; -var ts = $.tablesorter; + 'use strict'; + var ts = $.tablesorter; -ts.alignChar = { + ts.alignChar = { - init : function(table, c, wo) { - c.$headers.filter('[' + wo.alignChar_charAttrib + ']').each(function(){ - var $this = $(this), - vars = { - column : this.column, - align : $this.attr(wo.alignChar_charAttrib), - alignIndex : parseInt( $this.attr(wo.alignChar_indexAttrib) || 0, 10), - adjust : parseFloat($this.attr(wo.alignChar_adjustAttrib)) || 0 - }; - vars.regex = new RegExp('\\' + vars.align, 'g'); - if (typeof vars.align !== 'undefined') { - wo.alignChar_savedVars[this.column] = vars; - ts.alignChar.setup(table, c, wo, vars); - } - }); - }, + init : function(table, c, wo) { + c.$headers.filter('[' + wo.alignChar_charAttrib + ']').each(function(){ + var $this = $(this), + vars = { + column : this.column, + align : $this.attr(wo.alignChar_charAttrib), + alignIndex : parseInt( $this.attr(wo.alignChar_indexAttrib) || 0, 10), + adjust : parseFloat($this.attr(wo.alignChar_adjustAttrib)) || 0 + }; + vars.regex = new RegExp('\\' + vars.align, 'g'); + if (typeof vars.align !== 'undefined') { + wo.alignChar_savedVars[this.column] = vars; + ts.alignChar.setup(table, c, wo, vars); + } + }); + }, - setup: function(table, c, wo, v){ - // do nothing for empty tables - if ($.isEmptyObject(c.cache)) { return; } - var tbodyIndex, rowIndex, start, end, last, index, rows, val, count, - len, wLeft, wRight, alignChar, $row, - left = [], - right = []; - for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++){ - rows = c.cache[tbodyIndex]; - len = rows.normalized.length; - for (rowIndex = 0; rowIndex < len; rowIndex++) { - // set up to work with modified cache v2.16.0+ - $row = rows.row ? rows.row[rowIndex] : rows.normalized[rowIndex][c.columns].$row; - val = $row.find('td').eq(v.column).text().replace(/[ ]/g, "\u00a0"); - // count how many "align" characters are in the string - count = (val.match( v.regex ) || []).length; - // set alignment @ alignIndex (one-based index) - if (count > 0 && v.alignIndex > 0) { - end = Math.min(v.alignIndex, count); - start = 0; - index = 0; - last = 0; - // find index of nth align character based on alignIndex (data-align-index) - while (start++ < end) { - last = val.indexOf(v.align, last + 1); - index = last < 0 ? index : last; + setup: function(table, c, wo, v){ + // do nothing for empty tables + if ($.isEmptyObject(c.cache)) { return; } + var tbodyIndex, rowIndex, start, end, last, index, rows, val, count, + len, wLeft, wRight, alignChar, $row, + left = [], + right = []; + for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++){ + rows = c.cache[tbodyIndex]; + len = rows.normalized.length; + for (rowIndex = 0; rowIndex < len; rowIndex++) { + // set up to work with modified cache v2.16.0+ + $row = rows.row ? rows.row[rowIndex] : rows.normalized[rowIndex][c.columns].$row; + val = $row.find('td').eq(v.column).text().replace(/[ ]/g, '\u00a0'); + // count how many 'align' characters are in the string + count = (val.match( v.regex ) || []).length; + // set alignment @ alignIndex (one-based index) + if (count > 0 && v.alignIndex > 0) { + end = Math.min(v.alignIndex, count); + start = 0; + index = 0; + last = 0; + // find index of nth align character based on alignIndex (data-align-index) + while (start++ < end) { + last = val.indexOf(v.align, last + 1); + index = last < 0 ? index : last; + } + } else { + index = val.indexOf(v.align); + } + if ( index >= 0 ) { + left.push( val.substring(0, index) || '' ); + right.push( val.substring(index, val.length) || '' ); + } else { + // no align character found! + // put val in right or left based on the align index + left.push( (count >= 1 && v.alignIndex >= count) ? '' : val || '' ); + right.push( (count >= 1 && v.alignIndex >= count) ? val || '' : '' ); } - } else { - index = val.indexOf(v.align); - } - if ( index >= 0 ) { - left.push( val.substring(0, index) || '' ); - right.push( val.substring(index, val.length) || '' ); - } else { - // no align character found! - // put val in right or left based on the align index - left.push( (count >= 1 && v.alignIndex >= count) ? '' : val || '' ); - right.push( (count >= 1 && v.alignIndex >= count) ? val || '' : '' ); } } - } - // find widest segments - wLeft = ($.extend([], left)).sort(function(a,b){ return b.length - a.length; })[0]; - wRight = ($.extend([], right)).sort(function(a,b){ return b.length - a.length; })[0]; - // calculate percentage widths - v.width = v.width || ( Math.floor(wLeft.length / (wLeft.length + wRight.length) * 100) + v.adjust ); - wLeft = 'min-width:' + v.width + '%'; - wRight = 'min-width:' + (100 - v.width) + '%'; + // find widest segments + wLeft = ($.extend([], left)).sort(function(a, b){ return b.length - a.length; })[0]; + wRight = ($.extend([], right)).sort(function(a, b){ return b.length - a.length; })[0]; + // calculate percentage widths + v.width = v.width || ( Math.floor(wLeft.length / (wLeft.length + wRight.length) * 100) + v.adjust ); + wLeft = 'min-width:' + v.width + '%'; + wRight = 'min-width:' + (100 - v.width) + '%'; - for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++){ - rows = c.cache[tbodyIndex]; - len = rows.normalized.length; - for (rowIndex = 0; rowIndex < len; rowIndex++) { - alignChar = $(wo.alignChar_wrap).length ? $(wo.alignChar_wrap).html(v.align)[0].outerHTML : v.align; - $row = rows.row ? rows.row[rowIndex] : rows.normalized[rowIndex][c.columns].$row; - last = right[rowIndex].slice(v.align.length); - $row.find('td').eq(v.column).html( - '<span class="ts-align-wrap"><span class="ts-align-left" style="' + wLeft + '">' + left[rowIndex] + '</span>' + - '<span class="ts-align-right" style="' + wRight + '">' + ( last.length ? alignChar + last : '' ) + '</span></span>' - ); + for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++){ + rows = c.cache[tbodyIndex]; + len = rows.normalized.length; + for (rowIndex = 0; rowIndex < len; rowIndex++) { + alignChar = $(wo.alignChar_wrap).length ? $(wo.alignChar_wrap).html(v.align)[0].outerHTML : v.align; + $row = rows.row ? rows.row[rowIndex] : rows.normalized[rowIndex][c.columns].$row; + last = right[rowIndex].slice(v.align.length); + $row.find('td').eq(v.column).html( + '<span class="ts-align-wrap"><span class="ts-align-left" style="' + wLeft + '">' + left[rowIndex] + '</span>' + + '<span class="ts-align-right" style="' + wRight + '">' + ( last.length ? alignChar + last : '' ) + '</span></span>' + ); + } } - } - wo.alignChar_initialized = true; + wo.alignChar_initialized = true; + + }, - }, - remove: function(table, c, column){ - if ($.isEmptyObject(c.cache)) { return; } - var tbodyIndex, rowIndex, len, rows, $row, $cell; - for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++){ - rows = c.cache[tbodyIndex]; - len = rows.normalized.length; - for (rowIndex = 0; rowIndex < len; rowIndex++) { - $row = rows.row ? rows.row[rowIndex] : rows.normalized[rowIndex][c.columns].$row; - $cell = $row.find('td').eq(column); - $cell.html( $cell.text().replace(/\s/g, ' ') ); + remove: function(table, c, column){ + if ($.isEmptyObject(c.cache)) { return; } + var tbodyIndex, rowIndex, len, rows, $row, $cell; + for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++){ + rows = c.cache[tbodyIndex]; + len = rows.normalized.length; + for (rowIndex = 0; rowIndex < len; rowIndex++) { + $row = rows.row ? rows.row[rowIndex] : rows.normalized[rowIndex][c.columns].$row; + $cell = $row.find('td').eq(column); + $cell.html( $cell.text().replace(/\s/g, ' ') ); + } } } - } -}; + }; -ts.addWidget({ - id: 'alignChar', - priority: 100, - options: { - alignChar_wrap : '', - alignChar_charAttrib : 'data-align-char', - alignChar_indexAttrib : 'data-align-index', - alignChar_adjustAttrib : 'data-align-adjust' // percentage width adjustments - }, - init: function(table, thisWidget, c, wo){ - wo.alignChar_initialized = false; - wo.alignChar_savedVars = []; - ts.alignChar.init(table, c, wo); - c.$table.on('pagerEnd refreshAlign', function(){ + ts.addWidget({ + id: 'alignChar', + priority: 100, + options: { + alignChar_wrap : '', + alignChar_charAttrib : 'data-align-char', + alignChar_indexAttrib : 'data-align-index', + alignChar_adjustAttrib : 'data-align-adjust' // percentage width adjustments + }, + init: function(table, thisWidget, c, wo){ + wo.alignChar_initialized = false; + wo.alignChar_savedVars = []; + ts.alignChar.init(table, c, wo); + c.$table.on('pagerEnd refreshAlign', function(){ + c.$headers.filter('[' + wo.alignChar_charAttrib + ']').each(function(){ + ts.alignChar.remove(table, c, this.column); + }); + ts.alignChar.init(table, c, wo); + }); + }, + format : function(table, c, wo){ + // reinitialize in case table is empty when first initialized + if (!wo.alignChar_initialized) { + c.$table.trigger('refreshAlign'); + } + }, + remove : function(table, c, wo, refreshing){ + if (refreshing) { return; } c.$headers.filter('[' + wo.alignChar_charAttrib + ']').each(function(){ ts.alignChar.remove(table, c, this.column); }); - ts.alignChar.init(table, c, wo); - }); - }, - format : function(table, c, wo){ - // reinitialize in case table is empty when first initialized - if (!wo.alignChar_initialized) { - c.$table.trigger('refreshAlign'); + wo.alignChar_initialized = false; } - }, - remove : function(table, c, wo, refreshing){ - if (refreshing) { return; } - c.$headers.filter('[' + wo.alignChar_charAttrib + ']').each(function(){ - ts.alignChar.remove(table, c, this.column); - }); - wo.alignChar_initialized = false; - } -}); + }); -})(jQuery); \ No newline at end of file +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js index 04fa1cf..03dd95d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js @@ -5,8 +5,8 @@ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ ;(function($){ -"use strict"; -var ts = $.tablesorter = $.tablesorter || {}, + 'use strict'; + var ts = $.tablesorter = $.tablesorter || {}, // build a table from data (requires existing <table> tag) // data.header contains an array of header titles @@ -40,7 +40,7 @@ var ts = $.tablesorter = $.tablesorter || {}, // valid JSON! return bt.object( table, d, wo ); } - } catch(ignore) {} + } catch (ignore) {} // fall through in case it's a csv string } // Array @@ -57,7 +57,7 @@ var ts = $.tablesorter = $.tablesorter || {}, // even if wo.build_type is undefined, we can try to figure out the type if ( !ts.buildTable.hasOwnProperty(typ) && typ !== '' ) { - if (c.debug) { ts.log('aborting build table widget, incorrect build type'); } + if (c.debug) { console.error('aborting build table widget, incorrect build type'); } return false; } @@ -71,7 +71,7 @@ var ts = $.tablesorter = $.tablesorter || {}, runType(data); }) .fail(function( jqXHR, textStatus, errorThrown) { - if (c.debug) { ts.log('aborting build table widget, failed ajax load'); } + if (c.debug) { console.error('aborting build table widget, failed ajax load'); } $tbl.html('<tr><td class="error">' + jqXHR.status + ' ' + textStatus + '</td></tr>'); }); } else { @@ -105,7 +105,7 @@ var ts = $.tablesorter = $.tablesorter || {}, // *** CSV only options *** build_csvStartLine : 0, // line within the csv to start adding to table - build_csvSeparator : ",", // csv separator + build_csvSeparator : ',', // csv separator // *** build object options *** build_objectRowKey : 'rows', // object key containing table rows @@ -225,7 +225,7 @@ var ts = $.tablesorter = $.tablesorter || {}, var c, h, csv = wo.build_type === 'csv' || typeof data === 'string', $t = $(table), - lines = csv ? data.replace('\r','').split('\n') : data, + lines = csv ? data.replace('\r', '').split('\n') : data, len = lines.length, printedLines = 0, infooter = false, @@ -276,13 +276,13 @@ var ts = $.tablesorter = $.tablesorter || {}, // CSV Parser by Brian Huisman (http://www.greywyvern.com/?post=258) bt.splitCSV = function(str, sep) { var x, tl, - thisCSV = $.trim(str).split(sep = sep || ","); + thisCSV = $.trim(str).split(sep = sep || ','); for ( x = thisCSV.length - 1; x >= 0; x-- ) { if ( thisCSV[x].replace(/\"\s+$/, '"').charAt(thisCSV[x].length - 1) === '"' ) { if ( (tl = thisCSV[x].replace(/^\s+\"/, '"')).length > 1 && tl.charAt(0) === '"' ) { thisCSV[x] = thisCSV[x].replace(/^\s*"|"\s*$/g, '').replace(/""/g, '"'); } else if (x) { - thisCSV.splice(x - 1, 2, [thisCSV[x - 1], thisCSV[x]].join(sep)); + thisCSV.splice(x - 1, 2, [ thisCSV[x - 1], thisCSV[x] ].join(sep)); } else { thisCSV = thisCSV.shift().split(sep).concat(thisCSV); } @@ -304,7 +304,7 @@ var ts = $.tablesorter = $.tablesorter || {}, bt.buildComplete(table, wo); }; -/* ==== Object example ==== + /* ==== Object example ==== data : { headers : [ [ @@ -345,9 +345,9 @@ var ts = $.tablesorter = $.tablesorter || {}, } ] } -*/ + */ bt.object = function(table, data, wo) { - // "rows" + // 'rows' var j, l, t, $c, $t, $tb, $tr, c = table.config, kh = wo.build_objectHeaderKey, @@ -356,7 +356,7 @@ var ts = $.tablesorter = $.tablesorter || {}, r = data.hasOwnProperty(kr) && !$.isEmptyObject(data.kr) ? data.kr : data.hasOwnProperty('rows') ? data.rows : false; if (!h || !r || h.length === 0 || r.length === 0) { - if (c.debug) { ts.log('aborting build table widget, missing data for object build'); } + if (c.debug) { console.error('aborting build table widget, missing data for object build'); } return false; } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js index 6589fb5..b85fa96 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js @@ -32,7 +32,7 @@ .off(wo.chart_event) .on(wo.chart_event, function() { if (this.hasInitialized) { - // refresh "c" variable in case options are updated dynamically + // refresh 'c' variable in case options are updated dynamically var c = this.config; chart.getCols(c, c.widgetOptions); chart.getData(c, c.widgetOptions); @@ -258,7 +258,7 @@ // Set the label column chart_labelCol: 0, // data sort, should always be first row, might want [[0,1]] - chart_sort: [[0,0]], + chart_sort: [ [ 0, 0 ] ], // event to trigger get updated data chart_event: 'chartData' }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index 2c519fc..3d031f0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -5,362 +5,362 @@ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ ;(function($){ -"use strict"; + 'use strict'; -var ts = $.tablesorter, -namespace = '.tscolsel', -tsColSel = ts.columnSelector = { + var ts = $.tablesorter, + namespace = '.tscolsel', + tsColSel = ts.columnSelector = { - queryAll : '@media only all { [columns] { display: none; } } ', - queryBreak : '@media all and (min-width: [size]) { [columns] { display: table-cell; } } ', + queryAll : '@media only all { [columns] { display: none; } } ', + queryBreak : '@media all and (min-width: [size]) { [columns] { display: table-cell; } } ', - init: function(table, c, wo) { - var $t, colSel; + init: function(table, c, wo) { + var $t, colSel; - // abort if no input is contained within the layout - $t = $(wo.columnSelector_layout); - if (!$t.find('input').add( $t.filter('input') ).length) { - if (c.debug) { - ts.log('ColumnSelector: >> ERROR: Column Selector aborting, no input found in the layout! ***'); + // abort if no input is contained within the layout + $t = $(wo.columnSelector_layout); + if (!$t.find('input').add( $t.filter('input') ).length) { + if (c.debug) { + console.error('ColumnSelector: >> ERROR: Column Selector aborting, no input found in the layout! ***'); + } + return; } - return; - } - // unique table class name - c.$table.addClass( c.namespace.slice(1) + 'columnselector' ); + // unique table class name + c.$table.addClass( c.namespace.slice(1) + 'columnselector' ); - // build column selector/state array - colSel = c.selector = { $container : $(wo.columnSelector_container || '<div>') }; - colSel.$style = $('<style></style>').prop('disabled', true).appendTo('head'); - colSel.$breakpoints = $('<style></style>').prop('disabled', true).appendTo('head'); + // build column selector/state array + colSel = c.selector = { $container : $(wo.columnSelector_container || '<div>') }; + colSel.$style = $('<style></style>').prop('disabled', true).appendTo('head'); + colSel.$breakpoints = $('<style></style>').prop('disabled', true).appendTo('head'); - colSel.isInitializing = true; - tsColSel.setupSelector(table, c, wo); + colSel.isInitializing = true; + tsColSel.setupSelector(table, c, wo); - if (wo.columnSelector_mediaquery) { - tsColSel.setupBreakpoints(c, wo); - } + if (wo.columnSelector_mediaquery) { + tsColSel.setupBreakpoints(c, wo); + } - colSel.isInitializing = false; - if (colSel.$container.length) { - tsColSel.updateCols(c, wo); - } else if (c.debug) { - ts.log('ColumnSelector: >> container not found'); - } + colSel.isInitializing = false; + if (colSel.$container.length) { + tsColSel.updateCols(c, wo); + } else if (c.debug) { + console.warn('ColumnSelector: >> container not found'); + } - c.$table - .off('refreshColumnSelector' + namespace) - .on('refreshColumnSelector' + namespace, function(e, opt){ - // make sure we're using current config settings - var i, - isArry = $.isArray(opt), - c = this.config, - wo = c.widgetOptions; - // see #798 - if (opt && c.selector.$container.length) { - if (isArry) { - // make sure array contains numbers - $.each(opt, function(i,v){ - opt[i] = parseInt(v, 10); - }); - for (i = 0; i < c.columns; i++) { - c.selector.$container - .find('input[data-column=' + i + ']') - .prop('checked', $.inArray( i, opt ) >= 0 ); + c.$table + .off('refreshColumnSelector' + namespace) + .on('refreshColumnSelector' + namespace, function(e, opt){ + // make sure we're using current config settings + var i, + isArry = $.isArray(opt), + c = this.config, + wo = c.widgetOptions; + // see #798 + if (opt && c.selector.$container.length) { + if (isArry) { + // make sure array contains numbers + $.each(opt, function(i, v){ + opt[i] = parseInt(v, 10); + }); + for (i = 0; i < c.columns; i++) { + c.selector.$container + .find('input[data-column=' + i + ']') + .prop('checked', $.inArray( i, opt ) >= 0 ); + } } + // if passing an array, set auto to false to allow manual column selection & update columns + tsColSel.updateAuto( c, wo, colSel.$container.find('input[data-column="auto"]').prop('checked', !isArry) ); + } else { + tsColSel.updateBreakpoints(c, wo); + tsColSel.updateCols(c, wo); } - // if passing an array, set auto to false to allow manual column selection & update columns - tsColSel.updateAuto( c, wo, colSel.$container.find('input[data-column="auto"]').prop('checked', !isArry) ); - } else { - tsColSel.updateBreakpoints(c, wo); - tsColSel.updateCols(c, wo); + }); + + }, + + setupSelector: function(table, c, wo) { + var name, + colSel = c.selector, + $container = colSel.$container, + useStorage = wo.columnSelector_saveColumns && ts.storage, + // get stored column states + saved = useStorage ? ts.storage( table, 'tablesorter-columnSelector' ) : [], + state = useStorage ? ts.storage( table, 'tablesorter-columnSelector-auto') : {}; + + // initial states + colSel.auto = $.isEmptyObject(state) || $.type(state.auto) !== 'boolean' ? wo.columnSelector_mediaqueryState : state.auto; + colSel.states = []; + colSel.$column = []; + colSel.$wrapper = []; + colSel.$checkbox = []; + // populate the selector container + c.$table.children('thead').find('tr:first th', table).each(function() { + var $this = $(this), + // if no data-priority is assigned, default to 1, but don't remove it from the selector list + priority = $this.attr(wo.columnSelector_priority) || 1, + colId = $this.attr('data-column'), + state = ts.getData(this, c.headers[colId], 'columnSelector'); + + // if this column not hidable at all + // include getData check (includes 'columnSelector-false' class, data attribute, etc) + if ( isNaN(priority) && priority.length > 0 || state === 'disable' || + ( wo.columnSelector_columns[colId] && wo.columnSelector_columns[colId] === 'disable') ) { + return true; // goto next + } + + // set default state; storage takes priority + colSel.states[colId] = saved && typeof saved[colId] !== 'undefined' ? + saved[colId] : typeof wo.columnSelector_columns[colId] !== 'undefined' ? + wo.columnSelector_columns[colId] : (state === 'true' || state !== 'false'); + colSel.$column[colId] = $(this); + + // set default col title + name = $this.attr(wo.columnSelector_name) || $this.text(); + + if ($container.length) { + colSel.$wrapper[colId] = $(wo.columnSelector_layout.replace(/\{name\}/g, name)).appendTo($container); + colSel.$checkbox[colId] = colSel.$wrapper[colId] + // input may not be wrapped within the layout template + .find('input').add( colSel.$wrapper[colId].filter('input') ) + .attr('data-column', colId) + .toggleClass( wo.columnSelector_cssChecked, colSel.states[colId] ) + .prop('checked', colSel.states[colId]) + .on('change', function(){ + colSel.states[colId] = this.checked; + tsColSel.updateCols(c, wo); + }).change(); } }); - }, - - setupSelector: function(table, c, wo) { - var name, - colSel = c.selector, - $container = colSel.$container, - useStorage = wo.columnSelector_saveColumns && ts.storage, - // get stored column states - saved = useStorage ? ts.storage( table, 'tablesorter-columnSelector' ) : [], - state = useStorage ? ts.storage( table, 'tablesorter-columnSelector-auto') : {}; - - // initial states - colSel.auto = $.isEmptyObject(state) || $.type(state.auto) !== "boolean" ? wo.columnSelector_mediaqueryState : state.auto; - colSel.states = []; - colSel.$column = []; - colSel.$wrapper = []; - colSel.$checkbox = []; - // populate the selector container - c.$table.children('thead').find('tr:first th', table).each(function() { - var $this = $(this), - // if no data-priority is assigned, default to 1, but don't remove it from the selector list - priority = $this.attr(wo.columnSelector_priority) || 1, - colId = $this.attr('data-column'), - state = ts.getData(this, c.headers[colId], 'columnSelector'); - - // if this column not hidable at all - // include getData check (includes "columnSelector-false" class, data attribute, etc) - if ( isNaN(priority) && priority.length > 0 || state === 'disable' || - ( wo.columnSelector_columns[colId] && wo.columnSelector_columns[colId] === 'disable') ) { - return true; // goto next - } + }, + + setupBreakpoints: function(c, wo){ + var colSel = c.selector; - // set default state; storage takes priority - colSel.states[colId] = saved && typeof(saved[colId]) !== 'undefined' ? - saved[colId] : typeof(wo.columnSelector_columns[colId]) !== 'undefined' ? - wo.columnSelector_columns[colId] : (state === 'true' || state !== 'false'); - colSel.$column[colId] = $(this); - - // set default col title - name = $this.attr(wo.columnSelector_name) || $this.text(); - - if ($container.length) { - colSel.$wrapper[colId] = $(wo.columnSelector_layout.replace(/\{name\}/g, name)).appendTo($container); - colSel.$checkbox[colId] = colSel.$wrapper[colId] - // input may not be wrapped within the layout template - .find('input').add( colSel.$wrapper[colId].filter('input') ) - .attr('data-column', colId) - .toggleClass( wo.columnSelector_cssChecked, colSel.states[colId] ) - .prop('checked', colSel.states[colId]) - .on('change', function(){ - colSel.states[colId] = this.checked; + // add responsive breakpoints + if (wo.columnSelector_mediaquery) { + // used by window resize function + colSel.lastIndex = -1; + tsColSel.updateBreakpoints(c, wo); + c.$table + .off('updateAll' + namespace) + .on('updateAll' + namespace, function(){ + tsColSel.updateBreakpoints(c, wo); tsColSel.updateCols(c, wo); - }).change(); + }); } - }); - - }, - setupBreakpoints: function(c, wo){ - var colSel = c.selector; - - // add responsive breakpoints - if (wo.columnSelector_mediaquery) { - // used by window resize function - colSel.lastIndex = -1; - tsColSel.updateBreakpoints(c, wo); - c.$table - .off('updateAll' + namespace) - .on('updateAll' + namespace, function(){ - tsColSel.updateBreakpoints(c, wo); + if (colSel.$container.length) { + // Add media queries toggle + if (wo.columnSelector_mediaquery) { + colSel.$auto = $( wo.columnSelector_layout.replace(/\{name\}/g, wo.columnSelector_mediaqueryName) ).prependTo(colSel.$container); + colSel.$auto + // needed in case the input in the layout is not wrapped + .find('input').add( colSel.$auto.filter('input') ) + .attr('data-column', 'auto') + .prop('checked', colSel.auto) + .toggleClass( wo.columnSelector_cssChecked, colSel.auto ) + .on('change', function(){ + tsColSel.updateAuto(c, wo, $(this)); + }).change(); + } + // Add a bind on update to re-run col setup + c.$table.off('update' + namespace).on('update' + namespace, function() { tsColSel.updateCols(c, wo); }); - } - - if (colSel.$container.length) { - // Add media queries toggle - if (wo.columnSelector_mediaquery) { - colSel.$auto = $( wo.columnSelector_layout.replace(/\{name\}/g, wo.columnSelector_mediaqueryName) ).prependTo(colSel.$container); - colSel.$auto - // needed in case the input in the layout is not wrapped - .find('input').add( colSel.$auto.filter('input') ) - .attr('data-column', 'auto') - .prop('checked', colSel.auto) - .toggleClass( wo.columnSelector_cssChecked, colSel.auto ) - .on('change', function(){ - tsColSel.updateAuto(c, wo, $(this)); - }).change(); } - // Add a bind on update to re-run col setup - c.$table.off('update' + namespace).on('update' + namespace, function() { - tsColSel.updateCols(c, wo); + }, + + updateAuto: function(c, wo, $el) { + var colSel = c.selector; + colSel.auto = $el.prop('checked') || false; + $.each( colSel.$checkbox, function(i, $cb){ + if ($cb) { + $cb[0].disabled = colSel.auto; + colSel.$wrapper[i].toggleClass('disabled', colSel.auto); + } }); - } - }, - - updateAuto: function(c, wo, $el) { - var colSel = c.selector; - colSel.auto = $el.prop('checked') || false; - $.each( colSel.$checkbox, function(i, $cb){ - if ($cb) { - $cb[0].disabled = colSel.auto; - colSel.$wrapper[i].toggleClass('disabled', colSel.auto); + if (wo.columnSelector_mediaquery) { + tsColSel.updateBreakpoints(c, wo); } - }); - if (wo.columnSelector_mediaquery) { - tsColSel.updateBreakpoints(c, wo); - } - tsColSel.updateCols(c, wo); - // copy the column selector to a popup/tooltip - if (c.selector.$popup) { - c.selector.$popup.find('.tablesorter-column-selector') - .html( colSel.$container.html() ) - .find('input').each(function(){ - var indx = $(this).attr('data-column'); - $(this).prop( 'checked', indx === 'auto' ? colSel.auto : colSel.states[indx] ); + tsColSel.updateCols(c, wo); + // copy the column selector to a popup/tooltip + if (c.selector.$popup) { + c.selector.$popup.find('.tablesorter-column-selector') + .html( colSel.$container.html() ) + .find('input').each(function(){ + var indx = $(this).attr('data-column'); + $(this).prop( 'checked', indx === 'auto' ? colSel.auto : colSel.states[indx] ); + }); + } + if (wo.columnSelector_saveColumns && ts.storage) { + ts.storage( c.$table[0], 'tablesorter-columnSelector-auto', { auto : colSel.auto } ); + } + // trigger columnUpdate if auto is true (it gets skipped in updateCols() + if (colSel.auto) { + c.$table.trigger('columnUpdate'); + } + }, + + updateBreakpoints: function(c, wo) { + var priority, column, breaks, temp, + colSel = c.selector, + prefix = c.namespace + 'columnselector', + mediaAll = [], + breakpts = ''; + if (wo.columnSelector_mediaquery && !colSel.auto) { + colSel.$breakpoints.prop('disabled', true); + colSel.$style.prop('disabled', false); + return; + } + + // only 6 breakpoints (same as jQuery Mobile) + for (priority = 0; priority < 6; priority++){ + /*jshint loopfunc:true */ + breaks = []; + c.$headers.filter('[' + wo.columnSelector_priority + '=' + (priority + 1) + ']').each(function(){ + column = parseInt($(this).attr('data-column'), 10) + 1; + temp = ' col:nth-child(' + column + ')'; + breaks.push(prefix + temp + ',' + prefix + '_extra_table' + temp); + temp = ' tr th:nth-child(' + column + ')'; + breaks.push(prefix + temp + ',' + prefix + '_extra_table' + temp); + temp = ' tr td:nth-child(' + column + ')'; + breaks.push(prefix + temp + ',' + prefix + '_extra_table' + temp); }); - } - if (wo.columnSelector_saveColumns && ts.storage) { - ts.storage( c.$table[0], 'tablesorter-columnSelector-auto', { auto : colSel.auto } ); - } - // trigger columnUpdate if auto is true (it gets skipped in updateCols() - if (colSel.auto) { - c.$table.trigger('columnUpdate'); - } - }, - - updateBreakpoints: function(c, wo) { - var priority, column, breaks, temp, - colSel = c.selector, - prefix = c.namespace + 'columnselector', - mediaAll = [], - breakpts = ''; - if (wo.columnSelector_mediaquery && !colSel.auto) { - colSel.$breakpoints.prop('disabled', true); - colSel.$style.prop('disabled', false); - return; - } + if (breaks.length) { + mediaAll = mediaAll.concat( breaks ); + breakpts += tsColSel.queryBreak + .replace(/\[size\]/g, wo.columnSelector_breakpoints[priority]) + .replace(/\[columns\]/g, breaks.join(',')); + } + } + if (colSel.$style) { + colSel.$style.prop('disabled', true); + } + if (mediaAll.length) { + colSel.$breakpoints + .prop('disabled', false) + .html( tsColSel.queryAll.replace(/\[columns\]/g, mediaAll.join(',')) + breakpts ); + } + }, - // only 6 breakpoints (same as jQuery Mobile) - for (priority = 0; priority < 6; priority++){ - /*jshint loopfunc:true */ - breaks = []; - c.$headers.filter('[' + wo.columnSelector_priority + '=' + (priority + 1) + ']').each(function(){ - column = parseInt($(this).attr('data-column'), 10) + 1; - temp = ' col:nth-child(' + column + ')'; - breaks.push(prefix + temp + ',' + prefix + '_extra_table' + temp); - temp = ' tr th:nth-child(' + column + ')'; - breaks.push(prefix + temp + ',' + prefix + '_extra_table' + temp); - temp = ' tr td:nth-child(' + column + ')'; - breaks.push(prefix + temp + ',' + prefix + '_extra_table' + temp); + updateCols: function(c, wo) { + if (wo.columnSelector_mediaquery && c.selector.auto || c.selector.isInitializing) { + return; + } + var column, temp, + colSel = c.selector, + styles = [], + prefix = c.namespace + 'columnselector'; + colSel.$container.find('input[data-column]').filter('[data-column!="auto"]').each(function(){ + if (!this.checked) { + column = parseInt( $(this).attr('data-column'), 10 ) + 1; + temp = ' col:nth-child(' + column + ')'; + styles.push(prefix + temp + ',' + prefix + '_extra_table' + temp); + temp = ' tr th:nth-child(' + column + ')'; + styles.push(prefix + temp + ',' + prefix + '_extra_table' + temp); + temp = ' tr td:nth-child(' + column + ')'; + styles.push(prefix + temp + ',' + prefix + '_extra_table' + temp); + } + $(this).toggleClass( wo.columnSelector_cssChecked, this.checked ); }); - if (breaks.length) { - mediaAll = mediaAll.concat( breaks ); - breakpts += tsColSel.queryBreak - .replace(/\[size\]/g, wo.columnSelector_breakpoints[priority]) - .replace(/\[columns\]/g, breaks.join(',')); + if (wo.columnSelector_mediaquery){ + colSel.$breakpoints.prop('disabled', true); } - } - if (colSel.$style) { - colSel.$style.prop('disabled', true); - } - if (mediaAll.length) { - colSel.$breakpoints - .prop('disabled', false) - .html( tsColSel.queryAll.replace(/\[columns\]/g, mediaAll.join(',')) + breakpts ); - } - }, - - updateCols: function(c, wo) { - if (wo.columnSelector_mediaquery && c.selector.auto || c.selector.isInitializing) { - return; - } - var column, temp, - colSel = c.selector, - styles = [], - prefix = c.namespace + 'columnselector'; - colSel.$container.find('input[data-column]').filter('[data-column!="auto"]').each(function(){ - if (!this.checked) { - column = parseInt( $(this).attr('data-column'), 10 ) + 1; - temp = ' col:nth-child(' + column + ')'; - styles.push(prefix + temp + ',' + prefix + '_extra_table' + temp); - temp = ' tr th:nth-child(' + column + ')'; - styles.push(prefix + temp + ',' + prefix + '_extra_table' + temp); - temp = ' tr td:nth-child(' + column + ')'; - styles.push(prefix + temp + ',' + prefix + '_extra_table' + temp); + if (colSel.$style) { + colSel.$style.prop('disabled', false).html( styles.length ? styles.join(',') + ' { display: none; }' : '' ); } - $(this).toggleClass( wo.columnSelector_cssChecked, this.checked ); - }); - if (wo.columnSelector_mediaquery){ - colSel.$breakpoints.prop('disabled', true); - } - if (colSel.$style) { - colSel.$style.prop('disabled', false).html( styles.length ? styles.join(',') + ' { display: none; }' : '' ); - } - if (wo.columnSelector_saveColumns && ts.storage) { - ts.storage( c.$table[0], 'tablesorter-columnSelector', colSel.states ); - } - c.$table.trigger('columnUpdate'); - }, - - attachTo : function(table, elm) { - table = $(table)[0]; - var colSel, wo, indx, - c = table.config, - $popup = $(elm); - if ($popup.length && c) { - if (!$popup.find('.tablesorter-column-selector').length) { - // add a wrapper to add the selector into, in case the popup has other content - $popup.append('<span class="tablesorter-column-selector"></span>'); + if (wo.columnSelector_saveColumns && ts.storage) { + ts.storage( c.$table[0], 'tablesorter-columnSelector', colSel.states ); } - colSel = c.selector; - wo = c.widgetOptions; - $popup.find('.tablesorter-column-selector') - .html( colSel.$container.html() ) - .find('input').each(function(){ - var indx = $(this).attr('data-column'), - isChecked = indx === 'auto' ? colSel.auto : colSel.states[indx]; - $(this) - .toggleClass( wo.columnSelector_cssChecked, isChecked ) - .prop( 'checked', isChecked ); + c.$table.trigger('columnUpdate'); + }, + + attachTo : function(table, elm) { + table = $(table)[0]; + var colSel, wo, indx, + c = table.config, + $popup = $(elm); + if ($popup.length && c) { + if (!$popup.find('.tablesorter-column-selector').length) { + // add a wrapper to add the selector into, in case the popup has other content + $popup.append('<span class="tablesorter-column-selector"></span>'); + } + colSel = c.selector; + wo = c.widgetOptions; + $popup.find('.tablesorter-column-selector') + .html( colSel.$container.html() ) + .find('input').each(function(){ + var indx = $(this).attr('data-column'), + isChecked = indx === 'auto' ? colSel.auto : colSel.states[indx]; + $(this) + .toggleClass( wo.columnSelector_cssChecked, isChecked ) + .prop( 'checked', isChecked ); + }); + colSel.$popup = $popup.on('change', 'input', function(){ + // data input + indx = $(this).toggleClass( wo.columnSelector_cssChecked, this.checked ).attr('data-column'); + // update original popup + colSel.$container.find('input[data-column="' + indx + '"]') + .prop('checked', this.checked) + .trigger('change'); }); - colSel.$popup = $popup.on('change', 'input', function(){ - // data input - indx = $(this).toggleClass( wo.columnSelector_cssChecked, this.checked ).attr('data-column'); - // update original popup - colSel.$container.find('input[data-column="' + indx + '"]') - .prop('checked', this.checked) - .trigger('change'); - }); + } } - } - -}; - -ts.addWidget({ - id: "columnSelector", - priority: 10, - options: { - // target the column selector markup - columnSelector_container : null, - // column status, true = display, false = hide - // disable = do not display on list - columnSelector_columns : {}, - // remember selected columns - columnSelector_saveColumns: true, - - // container layout - columnSelector_layout : '<label><input type="checkbox">{name}</label>', - // data attribute containing column name to use in the selector container - columnSelector_name : 'data-selector-name', - - /* Responsive Media Query settings */ - // enable/disable mediaquery breakpoints - columnSelector_mediaquery: true, - // toggle checkbox name - columnSelector_mediaqueryName: 'Auto: ', - // breakpoints checkbox initial setting - columnSelector_mediaqueryState: true, - // responsive table hides columns with priority 1-6 at these breakpoints - // see http://view.jquerymobile.com/1.3.2/dist/demos/widgets/table-column-toggle/#Applyingapresetbreakpoint - // *** set to false to disable *** - columnSelector_breakpoints : [ '20em', '30em', '40em', '50em', '60em', '70em' ], - // data attribute containing column priority - // duplicates how jQuery mobile uses priorities: - // http://view.jquerymobile.com/1.3.2/dist/demos/widgets/table-column-toggle/ - columnSelector_priority : 'data-priority', - // class name added to checked checkboxes - this fixes an issue with Chrome not updating FontAwesome - // applied icons; use this class name (input.checked) instead of input:checked - columnSelector_cssChecked : 'checked' - - }, - init: function(table, thisWidget, c, wo) { - tsColSel.init(table, c, wo); - }, - remove: function(table, c, wo, refreshing) { - if (refreshing) { return; } - var csel = c.selector; - csel.$container.empty(); - if (csel.$popup) { csel.$popup.empty(); } - csel.$style.remove(); - csel.$breakpoints.remove(); - c.$table.off('updateAll' + namespace + ' update' + namespace); - } - -}); + + }; + + ts.addWidget({ + id: 'columnSelector', + priority: 10, + options: { + // target the column selector markup + columnSelector_container : null, + // column status, true = display, false = hide + // disable = do not display on list + columnSelector_columns : {}, + // remember selected columns + columnSelector_saveColumns: true, + + // container layout + columnSelector_layout : '<label><input type="checkbox">{name}</label>', + // data attribute containing column name to use in the selector container + columnSelector_name : 'data-selector-name', + + /* Responsive Media Query settings */ + // enable/disable mediaquery breakpoints + columnSelector_mediaquery: true, + // toggle checkbox name + columnSelector_mediaqueryName: 'Auto: ', + // breakpoints checkbox initial setting + columnSelector_mediaqueryState: true, + // responsive table hides columns with priority 1-6 at these breakpoints + // see http://view.jquerymobile.com/1.3.2/dist/demos/widgets/table-column-toggle/#Applyingapresetbreakpoint + // *** set to false to disable *** + columnSelector_breakpoints : [ '20em', '30em', '40em', '50em', '60em', '70em' ], + // data attribute containing column priority + // duplicates how jQuery mobile uses priorities: + // http://view.jquerymobile.com/1.3.2/dist/demos/widgets/table-column-toggle/ + columnSelector_priority : 'data-priority', + // class name added to checked checkboxes - this fixes an issue with Chrome not updating FontAwesome + // applied icons; use this class name (input.checked) instead of input:checked + columnSelector_cssChecked : 'checked' + + }, + init: function(table, thisWidget, c, wo) { + tsColSel.init(table, c, wo); + }, + remove: function(table, c, wo, refreshing) { + if (refreshing) { return; } + var csel = c.selector; + csel.$container.empty(); + if (csel.$popup) { csel.$popup.empty(); } + csel.$style.remove(); + csel.$breakpoints.remove(); + c.$table.off('updateAll' + namespace + ' update' + namespace); + } + + }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columns.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columns.js index 30a2b1f..774443d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columns.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columns.js @@ -1,78 +1,78 @@ /*! Widget: columns */ ;(function ($) { -'use strict'; -var ts = $.tablesorter || {}; + 'use strict'; + var ts = $.tablesorter || {}; -ts.addWidget({ - id: "columns", - priority: 30, - options : { - columns : [ "primary", "secondary", "tertiary" ] - }, - format: function(table, c, wo) { - var $tbody, tbodyIndex, $rows, rows, $row, $cells, remove, indx, + ts.addWidget({ + id: 'columns', + priority: 30, + options : { + columns : [ 'primary', 'secondary', 'tertiary' ] + }, + format: function(table, c, wo) { + var $tbody, tbodyIndex, $rows, rows, $row, $cells, remove, indx, $table = c.$table, $tbodies = c.$tbodies, sortList = c.sortList, len = sortList.length, // removed c.widgetColumns support - css = wo && wo.columns || [ "primary", "secondary", "tertiary" ], + css = wo && wo.columns || [ 'primary', 'secondary', 'tertiary' ], last = css.length - 1; remove = css.join(' '); - // check if there is a sort (on initialization there may not be one) - for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { - $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // detach tbody - $rows = $tbody.children('tr'); - // loop through the visible rows - $rows.each(function() { - $row = $(this); - if (this.style.display !== 'none') { - // remove all columns class names - $cells = $row.children().removeClass(remove); - // add appropriate column class names - if (sortList && sortList[0]) { - // primary sort column class - $cells.eq(sortList[0][0]).addClass(css[0]); - if (len > 1) { - for (indx = 1; indx < len; indx++) { - // secondary, tertiary, etc sort column classes - $cells.eq(sortList[indx][0]).addClass( css[indx] || css[last] ); + // check if there is a sort (on initialization there may not be one) + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // detach tbody + $rows = $tbody.children('tr'); + // loop through the visible rows + $rows.each(function() { + $row = $(this); + if (this.style.display !== 'none') { + // remove all columns class names + $cells = $row.children().removeClass(remove); + // add appropriate column class names + if (sortList && sortList[0]) { + // primary sort column class + $cells.eq(sortList[0][0]).addClass(css[0]); + if (len > 1) { + for (indx = 1; indx < len; indx++) { + // secondary, tertiary, etc sort column classes + $cells.eq(sortList[indx][0]).addClass( css[indx] || css[last] ); + } } } } - } - }); - ts.processTbody(table, $tbody, false); - } - // add classes to thead and tfoot - rows = wo.columns_thead !== false ? ['thead tr'] : []; - if (wo.columns_tfoot !== false) { - rows.push('tfoot tr'); - } - if (rows.length) { - $rows = $table.find( rows.join(',') ).children().removeClass(remove); - if (len) { - for (indx = 0; indx < len; indx++) { - // add primary. secondary, tertiary, etc sort column classes - $rows.filter('[data-column="' + sortList[indx][0] + '"]').addClass(css[indx] || css[last]); + }); + ts.processTbody(table, $tbody, false); + } + // add classes to thead and tfoot + rows = wo.columns_thead !== false ? [ 'thead tr' ] : []; + if (wo.columns_tfoot !== false) { + rows.push('tfoot tr'); + } + if (rows.length) { + $rows = $table.find( rows.join(',') ).children().removeClass(remove); + if (len) { + for (indx = 0; indx < len; indx++) { + // add primary. secondary, tertiary, etc sort column classes + $rows.filter('[data-column="' + sortList[indx][0] + '"]').addClass(css[indx] || css[last]); + } } } + }, + remove: function(table, c, wo) { + var tbodyIndex, $tbody, + $tbodies = c.$tbodies, + remove = (wo.columns || [ 'primary', 'secondary', 'tertiary' ]).join(' '); + c.$headers.removeClass(remove); + c.$table.children('tfoot').children('tr').children('th, td').removeClass(remove); + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody + $tbody.children('tr').each(function() { + $(this).children().removeClass(remove); + }); + ts.processTbody(table, $tbody, false); // restore tbody + } } - }, - remove: function(table, c, wo) { - var tbodyIndex, $tbody, - $tbodies = c.$tbodies, - remove = (wo.columns || [ "primary", "secondary", "tertiary" ]).join(' '); - c.$headers.removeClass(remove); - c.$table.children('tfoot').children('tr').children('th, td').removeClass(remove); - for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { - $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody - $tbody.children('tr').each(function() { - $(this).children().removeClass(remove); - }); - ts.processTbody(table, $tbody, false); // restore tbody - } - } -}); + }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js index a493c44..596177a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js @@ -7,254 +7,254 @@ ;( function( $ ){ 'use strict'; -var tse = $.tablesorter.editable = { - namespace : '.tseditable', - // last edited class name - lastEdited: 'tseditable-last-edited-cell', + var tse = $.tablesorter.editable = { + namespace : '.tseditable', + // last edited class name + lastEdited: 'tseditable-last-edited-cell', - editComplete: function( c, wo, $cell, refocus ) { - c.$table - .find( '.' + tse.lastEdited ) - .removeClass( tse.lastEdited ) - .trigger( wo.editable_editComplete, [ c ] ); - // restore focus last cell after updating - if ( refocus ) { - setTimeout( function() { - $cell.focus(); - }, 50 ); - } - }, - - selectAll: function( cell ) { - setTimeout( function() { - // select all text in contenteditable - // see http://stackoverflow.com/a/6150060/145346 - var range, selection; - if ( document.body.createTextRange ) { - range = document.body.createTextRange(); - range.moveToElementText( cell ); - range.select(); - } else if ( window.getSelection ) { - selection = window.getSelection(); - range = document.createRange(); - range.selectNodeContents( cell ); - selection.removeAllRanges(); - selection.addRange( range ); + editComplete: function( c, wo, $cell, refocus ) { + c.$table + .find( '.' + tse.lastEdited ) + .removeClass( tse.lastEdited ) + .trigger( wo.editable_editComplete, [ c ] ); + // restore focus last cell after updating + if ( refocus ) { + setTimeout( function() { + $cell.focus(); + }, 50 ); } - }, 100 ); - }, + }, + + selectAll: function( cell ) { + setTimeout( function() { + // select all text in contenteditable + // see http://stackoverflow.com/a/6150060/145346 + var range, selection; + if ( document.body.createTextRange ) { + range = document.body.createTextRange(); + range.moveToElementText( cell ); + range.select(); + } else if ( window.getSelection ) { + selection = window.getSelection(); + range = document.createRange(); + range.selectNodeContents( cell ); + selection.removeAllRanges(); + selection.addRange( range ); + } + }, 100 ); + }, - getColumns : function( c, wo ) { - var indx, tmp, - colIndex = [], - cols = []; - if ( !wo.editable_columnsArray && $.type( wo.editable_columns ) === 'string' && wo.editable_columns.indexOf( '-' ) >= 0 ) { - // editable_columns can contain a range string ( i.e. '2-4' ) - tmp = wo.editable_columns.split( /\s*-\s*/ ); - indx = parseInt( tmp[ 0 ], 10 ) || 0; - tmp = parseInt( tmp[ 1 ], 10 ) || ( c.columns - 1 ); - if ( tmp > c.columns ) { - tmp = c.columns - 1; + getColumns : function( c, wo ) { + var indx, tmp, + colIndex = [], + cols = []; + if ( !wo.editable_columnsArray && $.type( wo.editable_columns ) === 'string' && wo.editable_columns.indexOf( '-' ) >= 0 ) { + // editable_columns can contain a range string ( i.e. '2-4' ) + tmp = wo.editable_columns.split( /\s*-\s*/ ); + indx = parseInt( tmp[ 0 ], 10 ) || 0; + tmp = parseInt( tmp[ 1 ], 10 ) || ( c.columns - 1 ); + if ( tmp > c.columns ) { + tmp = c.columns - 1; + } + for ( ; indx <= tmp; indx++ ) { + colIndex.push( indx ); + cols.push( 'td:nth-child(' + ( indx + 1 ) + ')' ); + } + } else if ( $.isArray( wo.editable_columns ) ) { + $.each( wo.editable_columnsArray || wo.editable_columns, function( i, col ) { + if ( col < c.columns ) { + colIndex.push( col ); + cols.push( 'td:nth-child(' + ( col + 1 ) + ')' ); + } + }); } - for ( ; indx <= tmp; indx++ ) { - colIndex.push( indx ); - cols.push( 'td:nth-child(' + ( indx + 1 ) + ')' ); + if ( !wo.editable_columnsArray ) { + wo.editable_columnsArray = colIndex; + wo.editable_columnsArray.sort(function(a, b){ return a - b; }); } - } else if ( $.isArray( wo.editable_columns ) ) { - $.each( wo.editable_columnsArray || wo.editable_columns, function( i, col ) { - if ( col < c.columns ) { - colIndex.push( col ); - cols.push( 'td:nth-child(' + ( col + 1 ) + ')' ); - } - }); - } - if ( !wo.editable_columnsArray ) { - wo.editable_columnsArray = colIndex; - wo.editable_columnsArray.sort(function(a,b){ return a - b; }); - } - return cols; - }, + return cols; + }, - update: function( c, wo ) { - var $t, - tmp = $( '<div>' ).wrapInner( wo.editable_wrapContent ).children().length || $.isFunction( wo.editable_wrapContent ), - cols = tse.getColumns( c, wo ).join( ',' ); + update: function( c, wo ) { + var $t, + tmp = $( '<div>' ).wrapInner( wo.editable_wrapContent ).children().length || $.isFunction( wo.editable_wrapContent ), + cols = tse.getColumns( c, wo ).join( ',' ); - // turn off contenteditable to allow dynamically setting the wo.editable_noEdit - // class on table cells - see issue #900 - c.$tbodies.find( cols ).find( '[contenteditable]' ).prop( 'contenteditable', false ); + // turn off contenteditable to allow dynamically setting the wo.editable_noEdit + // class on table cells - see issue #900 + c.$tbodies.find( cols ).find( '[contenteditable]' ).prop( 'contenteditable', false ); - // IE does not allow making TR/TH/TD cells directly editable ( issue #404 ) - // so add a div or span inside ( it's faster than using wrapInner() ) - c.$tbodies.find( cols ).not( '.' + wo.editable_noEdit ).each( function() { - // test for children, if they exist, then make the children editable - $t = $( this ); + // IE does not allow making TR/TH/TD cells directly editable ( issue #404 ) + // so add a div or span inside ( it's faster than using wrapInner() ) + c.$tbodies.find( cols ).not( '.' + wo.editable_noEdit ).each( function() { + // test for children, if they exist, then make the children editable + $t = $( this ); - if ( tmp && $t.children( 'div, span' ).length === 0 ) { - $t.wrapInner( wo.editable_wrapContent ); - } - if ( $t.children( 'div, span' ).length ) { - // make div/span children content editable - $t.children( 'div, span' ).not( '.' + wo.editable_noEdit ).each( function() { - var $this = $( this ); + if ( tmp && $t.children( 'div, span' ).length === 0 ) { + $t.wrapInner( wo.editable_wrapContent ); + } + if ( $t.children( 'div, span' ).length ) { + // make div/span children content editable + $t.children( 'div, span' ).not( '.' + wo.editable_noEdit ).each( function() { + var $this = $( this ); + if ( wo.editable_trimContent ) { + $this.html( function( i, txt ) { + return $.trim( txt ); + }); + } + $this.prop( 'contenteditable', true ); + }); + } else { if ( wo.editable_trimContent ) { - $this.html( function( i, txt ) { + $t.html( function( i, txt ) { return $.trim( txt ); }); } - $this.prop( 'contenteditable', true ); - }); - } else { - if ( wo.editable_trimContent ) { - $t.html( function( i, txt ) { - return $.trim( txt ); - }); - } - $t.prop( 'contenteditable', true ); - } - }); - }, - - bindEvents: function( c, wo ) { - var namespace = tse.namespace; - c.$table - .off( ( 'updateComplete pagerComplete '.split( ' ' ).join( namespace + ' ' ) ).replace( /\s+/g, ' ' ) ) - .on( 'updateComplete pagerComplete '.split( ' ' ).join( namespace + ' ' ), function() { - tse.update( c, c.widgetOptions ); - }) - // prevent sort initialized by user click on the header from changing the row indexing before - // updateCell can finish processing the change - .children( 'thead' ) - .add( $( c.namespace + '_extra_table' ).children( 'thead' ) ) - .off( 'mouseenter' + namespace ) - .on( 'mouseenter' + namespace, function() { - if ( c.$table.data( 'contentFocused' ) ) { - // change to 'true' instead of element to allow focusout to process - c.$table.data( 'contentFocused', true ); - $( ':focus' ).trigger( 'focusout' ); + $t.prop( 'contenteditable', true ); } }); + }, - c.$tbodies - .off( ( 'focus blur focusout keydown '.split( ' ' ).join( namespace + ' ' ) ).replace( /\s+/g, ' ' ) ) - .on( 'focus' + namespace, '[contenteditable]', function( e ) { - clearTimeout( $( this ).data( 'timer' ) ); - c.$table.data( 'contentFocused', e.target ); - c.table.isUpdating = true; // prevent sorting while editing - var $this = $( this ), - selAll = wo.editable_selectAll, - column = $this.closest( 'td' ).index(), - txt = $this.html(); - if ( wo.editable_trimContent ) { - txt = $.trim( txt ); - } - // prevent enter from adding into the content - $this - .off( 'keydown' + namespace ) - .on( 'keydown' + namespace, function( e ){ - if ( wo.editable_enterToAccept && e.which === 13 && !e.shiftKey ) { - e.preventDefault(); - } - }); - $this.data({ before : txt, original: txt }); - - if ( typeof wo.editable_focused === 'function' ) { - wo.editable_focused( txt, column, $this ); - } - - if ( selAll ) { - if ( typeof selAll === 'function' ) { - if ( selAll( txt, column, $this ) ) { - tse.selectAll( $this[0] ); - } - } else { - tse.selectAll( $this[0] ); + bindEvents: function( c, wo ) { + var namespace = tse.namespace; + c.$table + .off( ( 'updateComplete pagerComplete '.split( ' ' ).join( namespace + ' ' ) ).replace( /\s+/g, ' ' ) ) + .on( 'updateComplete pagerComplete '.split( ' ' ).join( namespace + ' ' ), function() { + tse.update( c, c.widgetOptions ); + }) + // prevent sort initialized by user click on the header from changing the row indexing before + // updateCell can finish processing the change + .children( 'thead' ) + .add( $( c.namespace + '_extra_table' ).children( 'thead' ) ) + .off( 'mouseenter' + namespace ) + .on( 'mouseenter' + namespace, function() { + if ( c.$table.data( 'contentFocused' ) ) { + // change to 'true' instead of element to allow focusout to process + c.$table.data( 'contentFocused', true ); + $( ':focus' ).trigger( 'focusout' ); } - } - }) - .on( 'blur focusout keydown '.split( ' ' ).join( namespace + ' ' ), '[contenteditable]', function( e ) { - if ( !c.$table.data( 'contentFocused' ) ) { return; } - var t, validate, - valid = false, - $this = $( e.target ), - txt = $this.html(), - column = $this.closest( 'td' ).index(); - if ( wo.editable_trimContent ) { - txt = $.trim( txt ); - } - if ( e.which === 27 ) { - // user cancelled - $this.html( $this.data( 'original' ) ).trigger( 'blur' + namespace ); - c.$table.data( 'contentFocused', false ); - c.table.isUpdating = false; - return false; - } - // accept on enter ( if set ), alt-enter ( always ) or if autoAccept is set and element is blurred or unfocused - t = e.which === 13 && !e.shiftKey && ( wo.editable_enterToAccept || e.altKey ) || wo.editable_autoAccept && e.type !== 'keydown'; - // change if new or user hits enter ( if option set ) - if ( t && $this.data( 'before' ) !== txt ) { + }); - validate = wo.editable_validate; - valid = txt; + c.$tbodies + .off( ( 'focus blur focusout keydown '.split( ' ' ).join( namespace + ' ' ) ).replace( /\s+/g, ' ' ) ) + .on( 'focus' + namespace, '[contenteditable]', function( e ) { + clearTimeout( $( this ).data( 'timer' ) ); + c.$table.data( 'contentFocused', e.target ); + c.table.isUpdating = true; // prevent sorting while editing + var $this = $( this ), + selAll = wo.editable_selectAll, + column = $this.closest( 'td' ).index(), + txt = $this.html(); + if ( wo.editable_trimContent ) { + txt = $.trim( txt ); + } + // prevent enter from adding into the content + $this + .off( 'keydown' + namespace ) + .on( 'keydown' + namespace, function( e ){ + if ( wo.editable_enterToAccept && e.which === 13 && !e.shiftKey ) { + e.preventDefault(); + } + }); + $this.data({ before : txt, original: txt }); - if ( typeof( validate ) === 'function' ) { - valid = validate( txt, $this.data( 'original' ), column, $this ); - } else if ( typeof ( validate = $.tablesorter.getColumnData( c.table, validate, column ) ) === 'function' ) { - valid = validate( txt, $this.data( 'original' ), column, $this ); + if ( typeof wo.editable_focused === 'function' ) { + wo.editable_focused( txt, column, $this ); } - if ( t && valid !== false ) { - c.$table.find( '.' + tse.lastEdited ).removeClass( tse.lastEdited ); - $this - .addClass( tse.lastEdited ) - .html( valid ) - .data( 'before', valid ) - .data( 'original', valid ) - .trigger( 'change' ); - c.$table.trigger( 'updateCell', [ $this.closest( 'td' ), false, function() { - if ( wo.editable_autoResort ) { - setTimeout( function() { - c.$table.trigger( 'sorton', [ c.sortList, function() { - tse.editComplete( c, wo, c.$table.data( 'contentFocused' ), true ); - }, true ] ); - }, 10 ); - } else { - tse.editComplete( c, wo, c.$table.data( 'contentFocused' ) ); + if ( selAll ) { + if ( typeof selAll === 'function' ) { + if ( selAll( txt, column, $this ) ) { + tse.selectAll( $this[0] ); } - } ] ); + } else { + tse.selectAll( $this[0] ); + } + } + }) + .on( 'blur focusout keydown '.split( ' ' ).join( namespace + ' ' ), '[contenteditable]', function( e ) { + if ( !c.$table.data( 'contentFocused' ) ) { return; } + var t, validate, + valid = false, + $this = $( e.target ), + txt = $this.html(), + column = $this.closest( 'td' ).index(); + if ( wo.editable_trimContent ) { + txt = $.trim( txt ); + } + if ( e.which === 27 ) { + // user cancelled + $this.html( $this.data( 'original' ) ).trigger( 'blur' + namespace ); + c.$table.data( 'contentFocused', false ); + c.table.isUpdating = false; return false; } - } else if ( !valid && e.type !== 'keydown' ) { - clearTimeout( $this.data( 'timer' ) ); - $this.data( 'timer', setTimeout( function() { - c.table.isUpdating = false; // clear flag or sorting will be disabled + // accept on enter ( if set ), alt-enter ( always ) or if autoAccept is set and element is blurred or unfocused + t = e.which === 13 && !e.shiftKey && ( wo.editable_enterToAccept || e.altKey ) || wo.editable_autoAccept && e.type !== 'keydown'; + // change if new or user hits enter ( if option set ) + if ( t && $this.data( 'before' ) !== txt ) { + + validate = wo.editable_validate; + valid = txt; - if ( $.isFunction( wo.editable_blur ) ) { - txt = $this.html(); - wo.editable_blur( wo.editable_trimContent ? $.trim( txt ) : txt, column, $this ); + if ( typeof validate === 'function' ) { + valid = validate( txt, $this.data( 'original' ), column, $this ); + } else if ( typeof ( validate = $.tablesorter.getColumnData( c.table, validate, column ) ) === 'function' ) { + valid = validate( txt, $this.data( 'original' ), column, $this ); } - }, 100 ) ); - // restore original content on blur - $this.html( $this.data( 'original' ) ); - } - }); - }, - destroy : function( c, wo ) { - var namespace = tse.namespace, - cols = tse.getColumns( c, wo ), - tmp = ( 'updateComplete pagerComplete '.split( ' ' ).join( namespace + ' ' ) ).replace( /\s+/g, ' ' ); - c.$table.off( tmp ); + if ( t && valid !== false ) { + c.$table.find( '.' + tse.lastEdited ).removeClass( tse.lastEdited ); + $this + .addClass( tse.lastEdited ) + .html( valid ) + .data( 'before', valid ) + .data( 'original', valid ) + .trigger( 'change' ); + c.$table.trigger( 'updateCell', [ $this.closest( 'td' ), false, function() { + if ( wo.editable_autoResort ) { + setTimeout( function() { + c.$table.trigger( 'sorton', [ c.sortList, function() { + tse.editComplete( c, wo, c.$table.data( 'contentFocused' ), true ); + }, true ] ); + }, 10 ); + } else { + tse.editComplete( c, wo, c.$table.data( 'contentFocused' ) ); + } + } ] ); + return false; + } + } else if ( !valid && e.type !== 'keydown' ) { + clearTimeout( $this.data( 'timer' ) ); + $this.data( 'timer', setTimeout( function() { + c.table.isUpdating = false; // clear flag or sorting will be disabled + + if ( $.isFunction( wo.editable_blur ) ) { + txt = $this.html(); + wo.editable_blur( wo.editable_trimContent ? $.trim( txt ) : txt, column, $this ); + } + }, 100 ) ); + // restore original content on blur + $this.html( $this.data( 'original' ) ); + } + }); + }, + destroy : function( c, wo ) { + var namespace = tse.namespace, + cols = tse.getColumns( c, wo ), + + tmp = ( 'updateComplete pagerComplete '.split( ' ' ).join( namespace + ' ' ) ).replace( /\s+/g, ' ' ); + c.$table.off( tmp ); - tmp = ( 'focus blur focusout keydown '.split( ' ' ).join( namespace + ' ' ) ).replace( /\s+/g, ' ' ); - c.$tbodies - .off( tmp ) - .find( cols.join( ',' ) ) - .find( '[contenteditable]' ) - .prop( 'contenteditable', false ); - } + tmp = ( 'focus blur focusout keydown '.split( ' ' ).join( namespace + ' ' ) ).replace( /\s+/g, ' ' ); + c.$tbodies + .off( tmp ) + .find( cols.join( ',' ) ) + .find( '[contenteditable]' ) + .prop( 'contenteditable', false ); + } -}; + }; $.tablesorter.addWidget({ id: 'editable', diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js index 1c5e303..67e6735 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js @@ -8,422 +8,421 @@ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ ;(function($){ -"use strict"; + 'use strict'; -var ts = $.tablesorter || {}, + var ts = $.tablesorter || {}, -// compare option selector class name (jQuery selector) -compareSelect = '.compare-select', + // compare option selector class name (jQuery selector) + compareSelect = '.compare-select', + tsff = ts.filterFormatter = $.extend( {}, ts.filterFormatter, { -tsff = ts.filterFormatter = $.extend( {}, ts.filterFormatter, { - - addCompare: function($cell, indx, options){ - if (options.compare && $.isArray(options.compare) && options.compare.length > 1) { - var opt = '', - compareSelectClass = [ compareSelect.slice(1), ' ' + compareSelect.slice(1), '' ], - txt = options.cellText ? '<label class="' + compareSelectClass.join('-label') + indx + '">' + options.cellText + '</label>' : ''; - $.each(options.compare, function(i, c){ - opt += '<option ' + (options.selected === i ? 'selected' : '') + '>' + c + '</option>'; - }); - $cell - .wrapInner('<div class="' + compareSelectClass.join('-wrapper') + indx + '" />') - .prepend( txt + '<select class="' + compareSelectClass.join('') + indx + '" />' ) - .find('select') - .append(opt); - } - }, - - updateCompare : function($cell, $input, o) { - var val = $input.val() || '', - num = val.replace(/\s*?[><=]\s*?/g, ''), - compare = val.match(/[><=]/g) || ''; - if (o.compare) { - if ($.isArray(o.compare)){ - compare = (compare || []).join('') || o.compare[o.selected || 0]; - } - $cell.find(compareSelect).val( compare ); - } - return [ val, num ]; - }, - - /**********************\ - HTML5 Number (spinner) - \**********************/ - html5Number : function($cell, indx, def5Num) { - var t, o = $.extend({ - value : 0, - min : 0, - max : 100, - step : 1, - delayed : true, - disabled : false, - addToggle : false, - exactMatch : false, - cellText : '', - compare : '', - skipTest: false - }, def5Num), - - $input, - // test browser for HTML5 range support - $number = $('<input type="number" style="visibility:hidden;" value="test">').appendTo($cell), - // test if HTML5 number is supported - from Modernizr - numberSupported = o.skipTest || $number.attr('type') === 'number' && $number.val() !== 'test', - $shcell = [], - c = $cell.closest('table')[0].config, - - updateNumber = function(delayed, notrigger){ - var chkd = o.addToggle ? $cell.find('.toggle').is(':checked') : true, - v = $cell.find('.number').val(), - compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', - searchType = c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) || '' : true; - $input - // add equal to the beginning, so we filter exact numbers - .val( !o.addToggle || chkd ? (compare ? compare : o.exactMatch ? '=' : '') + v : '' ) - .trigger( notrigger ? '' : 'search', searchType ).end() - .find('.number').val(v); - if ($cell.find('.number').length) { - $cell.find('.number')[0].disabled = (o.disabled || !chkd); + addCompare: function($cell, indx, options){ + if (options.compare && $.isArray(options.compare) && options.compare.length > 1) { + var opt = '', + compareSelectClass = [ compareSelect.slice(1), ' ' + compareSelect.slice(1), '' ], + txt = options.cellText ? '<label class="' + compareSelectClass.join('-label') + indx + '">' + options.cellText + '</label>' : ''; + $.each(options.compare, function(i, c){ + opt += '<option ' + (options.selected === i ? 'selected' : '') + '>' + c + '</option>'; + }); + $cell + .wrapInner('<div class="' + compareSelectClass.join('-wrapper') + indx + '" />') + .prepend( txt + '<select class="' + compareSelectClass.join('') + indx + '" />' ) + .find('select') + .append(opt); } - // update sticky header cell - if ($shcell.length) { - $shcell.find('.number').val(v)[0].disabled = (o.disabled || !chkd); - $shcell.find(compareSelect).val(compare); - if (o.addToggle) { - $shcell.find('.toggle')[0].checked = chkd; + }, + + updateCompare : function($cell, $input, o) { + var val = $input.val() || '', + num = val.replace(/\s*?[><=]\s*?/g, ''), + compare = val.match(/[><=]/g) || ''; + if (o.compare) { + if ($.isArray(o.compare)){ + compare = (compare || []).join('') || o.compare[o.selected || 0]; } + $cell.find(compareSelect).val( compare ); } - }; - $number.remove(); - - if (numberSupported) { - t = o.addToggle ? '<div class="button"><input id="html5button' + indx + '" type="checkbox" class="toggle" />' + - '<label for="html5button' + indx + '"></label></div>' : ''; - t += '<input class="number" type="number" min="' + o.min + '" max="' + o.max + '" value="' + - o.value + '" step="' + o.step + '" />'; - // add HTML5 number (spinner) - $cell - .append(t + '<input type="hidden" />') - .find('.toggle, .number').bind('change', function(){ - updateNumber(); - }) - .closest('thead').find('th[data-column=' + indx + ']') - .addClass('filter-parsed') // get exact numbers from column - // on reset - .closest('table').bind('filterReset', function(){ - if ($.isArray(o.compare)) { - $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); - } - // turn off the toggle checkbox + return [ val, num ]; + }, + + /**********************\ + HTML5 Number (spinner) + \**********************/ + html5Number : function($cell, indx, def5Num) { + var t, o = $.extend({ + value : 0, + min : 0, + max : 100, + step : 1, + delayed : true, + disabled : false, + addToggle : false, + exactMatch : false, + cellText : '', + compare : '', + skipTest: false + }, def5Num), + + $input, + // test browser for HTML5 range support + $number = $('<input type="number" style="visibility:hidden;" value="test">').appendTo($cell), + // test if HTML5 number is supported - from Modernizr + numberSupported = o.skipTest || $number.attr('type') === 'number' && $number.val() !== 'test', + $shcell = [], + c = $cell.closest('table')[0].config, + + updateNumber = function(delayed, notrigger){ + var chkd = o.addToggle ? $cell.find('.toggle').is(':checked') : true, + v = $cell.find('.number').val(), + compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', + searchType = c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) || '' : true; + $input + // add equal to the beginning, so we filter exact numbers + .val( !o.addToggle || chkd ? (compare ? compare : o.exactMatch ? '=' : '') + v : '' ) + .trigger( notrigger ? '' : 'search', searchType ).end() + .find('.number').val(v); + if ($cell.find('.number').length) { + $cell.find('.number')[0].disabled = (o.disabled || !chkd); + } + // update sticky header cell + if ($shcell.length) { + $shcell.find('.number').val(v)[0].disabled = (o.disabled || !chkd); + $shcell.find(compareSelect).val(compare); if (o.addToggle) { - $cell.find('.toggle')[0].checked = false; - if ($shcell.length) { - $shcell.find('.toggle')[0].checked = false; - } + $shcell.find('.toggle')[0].checked = chkd; } - $cell.find('.number').val( o.value ); - setTimeout(function(){ + } + }; + $number.remove(); + + if (numberSupported) { + t = o.addToggle ? '<div class="button"><input id="html5button' + indx + '" type="checkbox" class="toggle" />' + + '<label for="html5button' + indx + '"></label></div>' : ''; + t += '<input class="number" type="number" min="' + o.min + '" max="' + o.max + '" value="' + + o.value + '" step="' + o.step + '" />'; + // add HTML5 number (spinner) + $cell + .append(t + '<input type="hidden" />') + .find('.toggle, .number').bind('change', function(){ updateNumber(); - }, 0); - }); - $input = $cell.find('input[type=hidden]').bind('change', function(){ - $cell.find('.number').val( this.value ); - updateNumber(); - }); - - // update slider from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ - var val = tsff.updateCompare($cell, $input, o)[0] || o.value; - $cell.find('.number').val( ((val || '') + '').replace(/[><=]/g,'') ); - updateNumber(false, true); - ts.filter.formatterUpdated($cell, indx); - }); - - if (o.compare) { - // add compare select - tsff.addCompare($cell, indx, o); - $cell.find(compareSelect).bind('change', function(){ + }) + .closest('thead').find('th[data-column=' + indx + ']') + .addClass('filter-parsed') // get exact numbers from column + // on reset + .closest('table').bind('filterReset', function(){ + if ($.isArray(o.compare)) { + $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); + } + // turn off the toggle checkbox + if (o.addToggle) { + $cell.find('.toggle')[0].checked = false; + if ($shcell.length) { + $shcell.find('.toggle')[0].checked = false; + } + } + $cell.find('.number').val( o.value ); + setTimeout(function(){ + updateNumber(); + }, 0); + }); + $input = $cell.find('input[type=hidden]').bind('change', function(){ + $cell.find('.number').val( this.value ); updateNumber(); }); - } - // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ - $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); - $shcell - .append(t) - .find('.toggle, .number').bind('change', function(){ - $cell.find('.number').val( $(this).val() ); - updateNumber(); - }); + // update slider from hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + var val = tsff.updateCompare($cell, $input, o)[0] || o.value; + $cell.find('.number').val( ((val || '') + '').replace(/[><=]/g, '') ); + updateNumber(false, true); + ts.filter.formatterUpdated($cell, indx); + }); if (o.compare) { // add compare select - tsff.addCompare($shcell, indx, o); - $shcell.find(compareSelect).bind('change', function(){ - $cell.find(compareSelect).val( $(this).val() ); + tsff.addCompare($cell, indx, o); + $cell.find(compareSelect).bind('change', function(){ updateNumber(); }); } - updateNumber(); - }); + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + $shcell + .append(t) + .find('.toggle, .number').bind('change', function(){ + $cell.find('.number').val( $(this).val() ); + updateNumber(); + }); + + if (o.compare) { + // add compare select + tsff.addCompare($shcell, indx, o); + $shcell.find(compareSelect).bind('change', function(){ + $cell.find(compareSelect).val( $(this).val() ); + updateNumber(); + }); + } - updateNumber(); + updateNumber(); + }); - } + updateNumber(); - return numberSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">'); - }, - - /**********************\ - HTML5 range slider - \**********************/ - html5Range : function($cell, indx, def5Range) { - var o = $.extend({ - value : 0, - min : 0, - max : 100, - step : 1, - delayed : true, - valueToHeader : true, - exactMatch : true, - cellText : '', - compare : '', - allText : 'all', - skipTest : false - }, def5Range), - - $input, - // test browser for HTML5 range support - $range = $('<input type="range" style="visibility:hidden;" value="test">').appendTo($cell), - // test if HTML5 range is supported - from Modernizr (but I left out the method to detect in Safari 2-4) - // see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/inputtypes.js - rangeSupported = o.skipTest || $range.attr('type') === 'range' && $range.val() !== 'test', - $shcell = [], - c = $cell.closest('table')[0].config, - - updateRange = function(v, delayed, notrigger){ - /*jshint eqeqeq:false */ - // hidden input changes may include compare symbols - v = ( typeof v === "undefined" ? $input.val() : v ).toString().replace(/[<>=]/g,'') || o.value; - var compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', - t = ' (' + (compare ? compare + v : v == o.min ? o.allText : v) + ')', - searchType = c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) || '' : true; - $cell.find('input[type=hidden]') - // add equal to the beginning, so we filter exact numbers - .val( ( compare ? compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ) ) - //( val == o.min ? '' : val + (o.exactMatch ? '=' : '')) - .trigger( notrigger ? '' : 'search', searchType ).end() - .find('.range').val(v); - // or add current value to the header cell, if desired - $cell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(t); - // update sticky header cell - if ($shcell.length) { - $shcell - .find('.range').val(v).end() - .find(compareSelect).val( compare ); - $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(t); } - }; - $range.remove(); - - if (rangeSupported) { - // add HTML5 range - $cell - .html('<input type="hidden"><input class="range" type="range" min="' + o.min + '" max="' + o.max + '" value="' + o.value + '" />') - .closest('thead').find('th[data-column=' + indx + ']') - .addClass('filter-parsed') // get exact numbers from column - // add span to header for the current slider value - .find('.tablesorter-header-inner').append('<span class="curvalue" />'); - // hidden filter update namespace trigger by filter widget - $input = $cell.find('input[type=hidden]').bind('change' + c.namespace + 'filter', function(){ + + return numberSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">'); + }, + + /**********************\ + HTML5 range slider + \**********************/ + html5Range : function($cell, indx, def5Range) { + var o = $.extend({ + value : 0, + min : 0, + max : 100, + step : 1, + delayed : true, + valueToHeader : true, + exactMatch : true, + cellText : '', + compare : '', + allText : 'all', + skipTest : false + }, def5Range), + + $input, + // test browser for HTML5 range support + $range = $('<input type="range" style="visibility:hidden;" value="test">').appendTo($cell), + // test if HTML5 range is supported - from Modernizr (but I left out the method to detect in Safari 2-4) + // see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/inputtypes.js + rangeSupported = o.skipTest || $range.attr('type') === 'range' && $range.val() !== 'test', + $shcell = [], + c = $cell.closest('table')[0].config, + + updateRange = function(v, delayed, notrigger){ /*jshint eqeqeq:false */ - var v = this.value, - compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || ''; - if (v !== this.lastValue) { - this.lastValue = ( compare ? compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ); - this.value = this.lastValue; - updateRange( v ); + // hidden input changes may include compare symbols + v = ( typeof v === 'undefined' ? $input.val() : v ).toString().replace(/[<>=]/g, '') || o.value; + var compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', + t = ' (' + (compare ? compare + v : v == o.min ? o.allText : v) + ')', + searchType = c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) || '' : true; + $cell.find('input[type=hidden]') + // add equal to the beginning, so we filter exact numbers + .val( ( compare ? compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ) ) + // ( val == o.min ? '' : val + (o.exactMatch ? '=' : '')) + .trigger( notrigger ? '' : 'search', searchType ).end() + .find('.range').val(v); + // or add current value to the header cell, if desired + $cell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(t); + // update sticky header cell + if ($shcell.length) { + $shcell + .find('.range').val(v).end() + .find(compareSelect).val( compare ); + $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(t); } - }); - - $cell.find('.range').bind('change', function(){ - updateRange( this.value ); - }); - - // update spinner from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ - var val = tsff.updateCompare($cell, $input, o)[0]; - $cell.find('.range').val( val ); - updateRange(val, false, true); - ts.filter.formatterUpdated($cell, indx); - }); + }; + $range.remove(); + + if (rangeSupported) { + // add HTML5 range + $cell + .html('<input type="hidden"><input class="range" type="range" min="' + o.min + '" max="' + o.max + '" value="' + o.value + '" />') + .closest('thead').find('th[data-column=' + indx + ']') + .addClass('filter-parsed') // get exact numbers from column + // add span to header for the current slider value + .find('.tablesorter-header-inner').append('<span class="curvalue" />'); + // hidden filter update namespace trigger by filter widget + $input = $cell.find('input[type=hidden]').bind('change' + c.namespace + 'filter', function(){ + /*jshint eqeqeq:false */ + var v = this.value, + compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || ''; + if (v !== this.lastValue) { + this.lastValue = ( compare ? compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ); + this.value = this.lastValue; + updateRange( v ); + } + }); - if (o.compare) { - // add compare select - tsff.addCompare($cell, indx, o); - $cell.find(compareSelect).bind('change', function(){ - updateRange(); + $cell.find('.range').bind('change', function(){ + updateRange( this.value ); }); - } - // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ - $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); - $shcell - .html('<input class="range" type="range" min="' + o.min + '" max="' + o.max + '" value="' + o.value + '" />') - .find('.range').bind('change', function(){ - updateRange( $shcell.find('.range').val() ); - }); - updateRange(); + // update spinner from hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + var val = tsff.updateCompare($cell, $input, o)[0]; + $cell.find('.range').val( val ); + updateRange(val, false, true); + ts.filter.formatterUpdated($cell, indx); + }); if (o.compare) { // add compare select - tsff.addCompare($shcell, indx, o); - $shcell.find(compareSelect).bind('change', function(){ - $cell.find(compareSelect).val( $(this).val() ); + tsff.addCompare($cell, indx, o); + $cell.find(compareSelect).bind('change', function(){ updateRange(); }); } - }); + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + $shcell + .html('<input class="range" type="range" min="' + o.min + '" max="' + o.max + '" value="' + o.value + '" />') + .find('.range').bind('change', function(){ + updateRange( $shcell.find('.range').val() ); + }); + updateRange(); - // on reset - $cell.closest('table').bind('filterReset', function(){ - if ($.isArray(o.compare)) { - $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); - } - setTimeout(function(){ - updateRange(o.value, false, true); - }, 0); - }); - updateRange(); + if (o.compare) { + // add compare select + tsff.addCompare($shcell, indx, o); + $shcell.find(compareSelect).bind('change', function(){ + $cell.find(compareSelect).val( $(this).val() ); + updateRange(); + }); + } - } + }); - return rangeSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">'); - }, - - /**********************\ - HTML5 Color picker - \**********************/ - html5Color: function($cell, indx, defColor) { - var t, o = $.extend({ - value : '#000000', - disabled : false, - addToggle : true, - exactMatch : true, - valueToHeader : false, - skipTest : false - }, defColor), - $input, - // Add a hidden input to hold the range values - $color = $('<input type="color" style="visibility:hidden;" value="test">').appendTo($cell), - // test if HTML5 color is supported - from Modernizr - colorSupported = o.skipTest || $color.attr('type') === 'color' && $color.val() !== 'test', - $shcell = [], - c = $cell.closest('table')[0].config, - - updateColor = function(v, notrigger){ - v = ( typeof v === "undefined" ? $input.val() : v ).toString().replace('=','') || o.value; - var chkd = true, - t = ' (' + v + ')'; - if (o.addToggle) { - chkd = $cell.find('.toggle').is(':checked'); - } - if ($cell.find('.colorpicker').length) { - $cell.find('.colorpicker').val(v)[0].disabled = (o.disabled || !chkd); - } + // on reset + $cell.closest('table').bind('filterReset', function(){ + if ($.isArray(o.compare)) { + $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); + } + setTimeout(function(){ + updateRange(o.value, false, true); + }, 0); + }); + updateRange(); - $input - .val( chkd ? v + (o.exactMatch ? '=' : '') : '' ) - .trigger( !c.$table[0].hasInitialized || notrigger ? '' : 'search' ); - if (o.valueToHeader) { - // add current color to the header cell - $cell.closest('thead').find('th[data-column=' + indx + ']').find('.curcolor').html(t); - } else { - // current color to span in cell - $cell.find('.currentColor').html(t); } - // update sticky header cell - if ($shcell.length) { - $shcell.find('.colorpicker').val(v)[0].disabled = (o.disabled || !chkd); + return rangeSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">'); + }, + + /**********************\ + HTML5 Color picker + \**********************/ + html5Color: function($cell, indx, defColor) { + var t, o = $.extend({ + value : '#000000', + disabled : false, + addToggle : true, + exactMatch : true, + valueToHeader : false, + skipTest : false + }, defColor), + $input, + // Add a hidden input to hold the range values + $color = $('<input type="color" style="visibility:hidden;" value="test">').appendTo($cell), + // test if HTML5 color is supported - from Modernizr + colorSupported = o.skipTest || $color.attr('type') === 'color' && $color.val() !== 'test', + $shcell = [], + c = $cell.closest('table')[0].config, + + updateColor = function(v, notrigger) { + v = ( typeof v === 'undefined' ? $input.val() : v ).toString().replace('=', '') || o.value; + var chkd = true, + t = ' (' + v + ')'; if (o.addToggle) { - $shcell.find('.toggle')[0].checked = chkd; + chkd = $cell.find('.toggle').is(':checked'); + } + if ($cell.find('.colorpicker').length) { + $cell.find('.colorpicker').val(v)[0].disabled = (o.disabled || !chkd); } + + $input + .val( chkd ? v + (o.exactMatch ? '=' : '') : '' ) + .trigger( !c.$table[0].hasInitialized || notrigger ? '' : 'search' ); if (o.valueToHeader) { // add current color to the header cell - $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curcolor').html(t); + $cell.closest('thead').find('th[data-column=' + indx + ']').find('.curcolor').html(t); } else { // current color to span in cell - $shcell.find('.currentColor').html(t); + $cell.find('.currentColor').html(t); } - } - }; - $color.remove(); - - if (colorSupported) { - t = '' + indx + Math.round(Math.random() * 100); - // add HTML5 color picker - t = '<div class="color-controls-wrapper">' + - (o.addToggle ? '<div class="button"><input id="colorbutton' + t + '" type="checkbox" class="toggle" /><label for="colorbutton' + - t + '"></label></div>' : '') + - '<input type="hidden"><input class="colorpicker" type="color" />' + - (o.valueToHeader ? '' : '<span class="currentColor">(#000000)</span>') + '</div>'; - $cell.html(t); - // add span to header for the current color value - only works if the line in the updateColor() function is also un-commented out - if (o.valueToHeader) { - $cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="curcolor" />'); - } - $cell.find('.toggle, .colorpicker').bind('change', function(){ - updateColor( $cell.find('.colorpicker').val() ); - }); + // update sticky header cell + if ($shcell.length) { + $shcell.find('.colorpicker').val(v)[0].disabled = (o.disabled || !chkd); + if (o.addToggle) { + $shcell.find('.toggle')[0].checked = chkd; + } + if (o.valueToHeader) { + // add current color to the header cell + $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curcolor').html(t); + } else { + // current color to span in cell + $shcell.find('.currentColor').html(t); + } + } + }; + $color.remove(); + + if (colorSupported) { + t = '' + indx + Math.round(Math.random() * 100); + // add HTML5 color picker + t = '<div class="color-controls-wrapper">' + + (o.addToggle ? '<div class="button"><input id="colorbutton' + t + '" type="checkbox" class="toggle" /><label for="colorbutton' + + t + '"></label></div>' : '') + + '<input type="hidden"><input class="colorpicker" type="color" />' + + (o.valueToHeader ? '' : '<span class="currentColor">(#000000)</span>') + '</div>'; + $cell.html(t); + // add span to header for the current color value - only works if the line in the updateColor() function is also un-commented out + if (o.valueToHeader) { + $cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="curcolor" />'); + } - // hidden filter update namespace trigger by filter widget - $input = $cell.find('input[type=hidden]').bind('change' + c.namespace + 'filter', function(){ - updateColor( this.value ); - }); + $cell.find('.toggle, .colorpicker').bind('change', function(){ + updateColor( $cell.find('.colorpicker').val() ); + }); - // update slider from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ - updateColor( $input.val(), true ); - ts.filter.formatterUpdated($cell, indx); - }); + // hidden filter update namespace trigger by filter widget + $input = $cell.find('input[type=hidden]').bind('change' + c.namespace + 'filter', function(){ + updateColor( this.value ); + }); - // on reset - $cell.closest('table').bind('filterReset', function(){ - // just turn off the colorpicker - if (o.addToggle) { - $cell.find('.toggle')[0].checked = false; - } - // delay needed because default color needs to be set in the filter - // there is no compare option here, so if addToggle = false, - // default color is #000000 (even with no value set) - setTimeout(function(){ - updateColor(); - }, 0); - }); - - // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ - $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx); - $shcell - .html(t) - .find('.toggle, .colorpicker').bind('change', function(){ - updateColor( $shcell.find('.colorpicker').val() ); - }); - updateColor( $shcell.find('.colorpicker').val() ); - }); + // update slider from hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + updateColor( $input.val(), true ); + ts.filter.formatterUpdated($cell, indx); + }); - updateColor( o.value ); + // on reset + $cell.closest('table').bind('filterReset', function(){ + // just turn off the colorpicker + if (o.addToggle) { + $cell.find('.toggle')[0].checked = false; + } + // delay needed because default color needs to be set in the filter + // there is no compare option here, so if addToggle = false, + // default color is #000000 (even with no value set) + setTimeout(function(){ + updateColor(); + }, 0); + }); + + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx); + $shcell + .html(t) + .find('.toggle, .colorpicker').bind('change', function(){ + updateColor( $shcell.find('.colorpicker').val() ); + }); + updateColor( $shcell.find('.colorpicker').val() ); + }); + + updateColor( o.value ); + } + return colorSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">'); } - return colorSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">'); - } -}); + }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js index 00ee461..6ba2a65 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js @@ -10,755 +10,755 @@ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ ;(function($){ -"use strict"; + 'use strict'; -var ts = $.tablesorter || {}, + var ts = $.tablesorter || {}, -// compare option selector class name (jQuery selector) -compareSelect = '.compare-select', + // compare option selector class name (jQuery selector) + compareSelect = '.compare-select', -tsff = ts.filterFormatter = $.extend( {}, ts.filterFormatter, { + tsff = ts.filterFormatter = $.extend( {}, ts.filterFormatter, { - addCompare: function($cell, indx, options){ - if (options.compare && $.isArray(options.compare) && options.compare.length > 1) { - var opt = '', - compareSelectClass = [ compareSelect.slice(1), ' ' + compareSelect.slice(1), '' ], - txt = options.cellText ? '<label class="' + compareSelectClass.join('-label') + indx + '">' + options.cellText + '</label>' : ''; - $.each(options.compare, function(i, c){ - opt += '<option ' + (options.selected === i ? 'selected' : '') + '>' + c + '</option>'; - }); - $cell - .wrapInner('<div class="' + compareSelectClass.join('-wrapper') + indx + '" />') - .prepend( txt + '<select class="' + compareSelectClass.join('') + indx + '" />' ) - .find('select') - .append(opt); - } - }, - - updateCompare : function($cell, $input, o) { - var val = $input.val() || '', - num = val.replace(/\s*?[><=]\s*?/g, ''), - compare = val.match(/[><=]/g) || ''; - if (o.compare) { - if ($.isArray(o.compare)){ - compare = (compare || []).join('') || o.compare[o.selected || 0]; - } - $cell.find(compareSelect).val( compare ); - } - return [ val, num ]; - }, - - /**********************\ - jQuery UI Spinner - \**********************/ - uiSpinner: function($cell, indx, spinnerDef) { - var o = $.extend({ - // filter formatter options - delayed : true, - addToggle : true, - exactMatch : true, - value : 1, - cellText : '', - compare : '', - // include ANY jQuery UI spinner options below - min : 0, - max : 100, - step : 1, - disabled : false - - }, spinnerDef ), - c = $cell.closest('table')[0].config, - // Add a hidden input to hold the range values - $input = $('<input class="filter" type="hidden">') - .appendTo($cell) - // hidden filter update namespace trigger by filter widget - .bind('change' + c.namespace + 'filter', function(){ - updateSpinner({ value: this.value, delayed: false }); - }), - $shcell = [], - - // this function updates the hidden input and adds the current values to the header cell text - updateSpinner = function(ui, notrigger) { - var chkd = true, state, - // ui is not undefined on create - v = ui && ui.value && ts.formatFloat((ui.value + '').replace(/[><=]/g,'')) || - $cell.find('.spinner').val() || o.value, - compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', - searchType = ui && typeof ui.delayed === 'boolean' ? ui.delayed : c.$table[0].hasInitialized ? o.delayed || '' : true; - if (o.addToggle) { - chkd = $cell.find('.toggle').is(':checked'); + addCompare: function($cell, indx, options){ + if (options.compare && $.isArray(options.compare) && options.compare.length > 1) { + var opt = '', + compareSelectClass = [ compareSelect.slice(1), ' ' + compareSelect.slice(1), '' ], + txt = options.cellText ? '<label class="' + compareSelectClass.join('-label') + indx + '">' + options.cellText + '</label>' : ''; + $.each(options.compare, function(i, c){ + opt += '<option ' + (options.selected === i ? 'selected' : '') + '>' + c + '</option>'; + }); + $cell + .wrapInner('<div class="' + compareSelectClass.join('-wrapper') + indx + '" />') + .prepend( txt + '<select class="' + compareSelectClass.join('') + indx + '" />' ) + .find('select') + .append(opt); } - state = o.disabled || !chkd ? 'disable' : 'enable'; - $cell.find('.filter') - // add equal to the beginning, so we filter exact numbers - .val( chkd ? (compare ? compare : o.exactMatch ? '=' : '') + v : '' ) - .trigger( notrigger ? '' : 'search', searchType ).end() - .find('.spinner').spinner(state).val(v); - // update sticky header cell - if ($shcell.length) { - $shcell - .find('.spinner').spinner(state).val(v).end() - .find(compareSelect).val( compare ); - if (o.addToggle) { - $shcell.find('.toggle')[0].checked = chkd; + }, + + updateCompare : function($cell, $input, o) { + var val = $input.val() || '', + num = val.replace(/\s*?[><=]\s*?/g, ''), + compare = val.match(/[><=]/g) || ''; + if (o.compare) { + if ($.isArray(o.compare)){ + compare = (compare || []).join('') || o.compare[o.selected || 0]; } + $cell.find(compareSelect).val( compare ); } - }; - - // add callbacks; preserve added callbacks - o.oldcreate = o.create; - o.oldspin = o.spin; - o.create = function(event, ui) { - updateSpinner(); // ui is an empty object on create - if (typeof o.oldcreate === 'function') { o.oldcreate(event, ui); } - }; - o.spin = function(event, ui) { - updateSpinner(ui); - if (typeof o.oldspin === 'function') { o.oldspin(event, ui); } - }; - if (o.addToggle) { - $('<div class="button"><input id="uispinnerbutton' + indx + '" type="checkbox" class="toggle" />' + - '<label for="uispinnerbutton' + indx + '"></label></div>') - .appendTo($cell) - .find('.toggle') - .bind('change', function(){ - updateSpinner(); - }); - } - // make sure we use parsed data - $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed'); - // add a jQuery UI spinner! - $('<input class="spinner spinner' + indx + '" />') - .val(o.value) - .appendTo($cell) - .spinner(o) - .bind('change keyup', function(){ - updateSpinner(); - }); - - // update spinner from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ - var val = tsff.updateCompare($cell, $input, o)[0]; - $cell.find('.spinner').val( val ); - updateSpinner({ value: val }, true); - ts.filter.formatterUpdated($cell, indx); - }); - - if (o.compare) { - // add compare select - tsff.addCompare($cell, indx, o); - $cell.find(compareSelect).bind('change', function(){ - updateSpinner(); - }); - } + return [ val, num ]; + }, - // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ - $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + /**********************\ + jQuery UI Spinner + \**********************/ + uiSpinner: function($cell, indx, spinnerDef) { + var o = $.extend({ + // filter formatter options + delayed : true, + addToggle : true, + exactMatch : true, + value : 1, + cellText : '', + compare : '', + // include ANY jQuery UI spinner options below + min : 0, + max : 100, + step : 1, + disabled : false + + }, spinnerDef ), + c = $cell.closest('table')[0].config, + // Add a hidden input to hold the range values + $input = $('<input class="filter" type="hidden">') + .appendTo($cell) + // hidden filter update namespace trigger by filter widget + .bind('change' + c.namespace + 'filter', function(){ + updateSpinner({ value: this.value, delayed: false }); + }), + $shcell = [], + + // this function updates the hidden input and adds the current values to the header cell text + updateSpinner = function(ui, notrigger) { + var chkd = true, state, + // ui is not undefined on create + v = ui && ui.value && ts.formatFloat((ui.value + '').replace(/[><=]/g, '')) || + $cell.find('.spinner').val() || o.value, + compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', + searchType = ui && typeof ui.delayed === 'boolean' ? ui.delayed : c.$table[0].hasInitialized ? o.delayed || '' : true; + if (o.addToggle) { + chkd = $cell.find('.toggle').is(':checked'); + } + state = o.disabled || !chkd ? 'disable' : 'enable'; + $cell.find('.filter') + // add equal to the beginning, so we filter exact numbers + .val( chkd ? (compare ? compare : o.exactMatch ? '=' : '') + v : '' ) + .trigger( notrigger ? '' : 'search', searchType ).end() + .find('.spinner').spinner(state).val(v); + // update sticky header cell + if ($shcell.length) { + $shcell + .find('.spinner').spinner(state).val(v).end() + .find(compareSelect).val( compare ); + if (o.addToggle) { + $shcell.find('.toggle')[0].checked = chkd; + } + } + }; + + // add callbacks; preserve added callbacks + o.oldcreate = o.create; + o.oldspin = o.spin; + o.create = function(event, ui) { + updateSpinner(); // ui is an empty object on create + if (typeof o.oldcreate === 'function') { o.oldcreate(event, ui); } + }; + o.spin = function(event, ui) { + updateSpinner(ui); + if (typeof o.oldspin === 'function') { o.oldspin(event, ui); } + }; if (o.addToggle) { - $('<div class="button"><input id="stickyuispinnerbutton' + indx + '" type="checkbox" class="toggle" />' + - '<label for="stickyuispinnerbutton' + indx + '"></label></div>') - .appendTo($shcell) + $('<div class="button"><input id="uispinnerbutton' + indx + '" type="checkbox" class="toggle" />' + + '<label for="uispinnerbutton' + indx + '"></label></div>') + .appendTo($cell) .find('.toggle') .bind('change', function(){ - $cell.find('.toggle')[0].checked = this.checked; updateSpinner(); }); } + // make sure we use parsed data + $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed'); // add a jQuery UI spinner! $('<input class="spinner spinner' + indx + '" />') .val(o.value) - .appendTo($shcell) + .appendTo($cell) .spinner(o) .bind('change keyup', function(){ - $cell.find('.spinner').val( this.value ); updateSpinner(); }); + // update spinner from hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + var val = tsff.updateCompare($cell, $input, o)[0]; + $cell.find('.spinner').val( val ); + updateSpinner({ value: val }, true); + ts.filter.formatterUpdated($cell, indx); + }); + if (o.compare) { // add compare select - tsff.addCompare($shcell, indx, o); - $shcell.find(compareSelect).bind('change', function(){ - $cell.find(compareSelect).val( $(this).val() ); + tsff.addCompare($cell, indx, o); + $cell.find(compareSelect).bind('change', function(){ updateSpinner(); }); } - }); + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + if (o.addToggle) { + $('<div class="button"><input id="stickyuispinnerbutton' + indx + '" type="checkbox" class="toggle" />' + + '<label for="stickyuispinnerbutton' + indx + '"></label></div>') + .appendTo($shcell) + .find('.toggle') + .bind('change', function(){ + $cell.find('.toggle')[0].checked = this.checked; + updateSpinner(); + }); + } + // add a jQuery UI spinner! + $('<input class="spinner spinner' + indx + '" />') + .val(o.value) + .appendTo($shcell) + .spinner(o) + .bind('change keyup', function(){ + $cell.find('.spinner').val( this.value ); + updateSpinner(); + }); - // on reset - c.$table.bind('filterReset', function(){ - if ($.isArray(o.compare)) { - $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); - } - // turn off the toggle checkbox - if (o.addToggle) { - $cell.find('.toggle')[0].checked = false; - } - $cell.find('.spinner').spinner('value', o.value); - setTimeout(function(){ - updateSpinner(); - }, 0); - }); - - updateSpinner(); - return $input; - }, - - /**********************\ - jQuery UI Slider - \**********************/ - uiSlider: function($cell, indx, sliderDef) { - var o = $.extend({ - // filter formatter options - delayed : true, - valueToHeader : false, - exactMatch : true, - cellText : '', - compare : '', - allText : 'all', - // include ANY jQuery UI spinner options below - // except values, since this is a non-range setup - value : 0, - min : 0, - max : 100, - step : 1, - range : "min" - }, sliderDef ), - c = $cell.closest('table')[0].config, - // Add a hidden input to hold the range values - $input = $('<input class="filter" type="hidden">') - .appendTo($cell) - // hidden filter update namespace trigger by filter widget - .bind('change' + c.namespace + 'filter', function(){ - updateSlider({ value: this.value }); - }), - $shcell = [], - - // this function updates the hidden input and adds the current values to the header cell text - updateSlider = function(ui, notrigger) { - // ui is not undefined on create - var v = typeof ui !== "undefined" ? ts.formatFloat((ui.value + '').replace(/[><=]/g,'')) || o.value : o.value, - val = o.compare ? v : v === o.min ? o.allText : v, - compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', - result = compare + val, - searchType = ui && typeof ui.delayed === 'boolean' ? ui.delayed : c.$table[0].hasInitialized ? o.delayed || '' : true; - if (o.valueToHeader) { - // add range indication to the header cell above! - $cell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(' (' + result + ')'); - } else { - // add values to the handle data-value attribute so the css tooltip will work properly - $cell.find('.ui-slider-handle').addClass('value-popup').attr('data-value', result); - } - // update the hidden input; - // ****** ADD AN EQUAL SIGN TO THE BEGINNING! <- this makes the slide exactly match the number ****** - // when the value is at the minimum, clear the hidden input so all rows will be seen + if (o.compare) { + // add compare select + tsff.addCompare($shcell, indx, o); + $shcell.find(compareSelect).bind('change', function(){ + $cell.find(compareSelect).val( $(this).val() ); + updateSpinner(); + }); + } - $cell.find('.filter') - .val( ( compare ? compare + v : v === o.min ? '' : (o.exactMatch ? '=' : '') + v ) ) - .trigger( notrigger ? '' : 'search', searchType ).end() - .find('.slider').slider('value', v); + }); - // update sticky header cell - if ($shcell.length) { - $shcell - .find(compareSelect).val( compare ).end() - .find('.slider').slider('value', v); + // on reset + c.$table.bind('filterReset', function(){ + if ($.isArray(o.compare)) { + $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); + } + // turn off the toggle checkbox + if (o.addToggle) { + $cell.find('.toggle')[0].checked = false; + } + $cell.find('.spinner').spinner('value', o.value); + setTimeout(function(){ + updateSpinner(); + }, 0); + }); + + updateSpinner(); + return $input; + }, + + /**********************\ + jQuery UI Slider + \**********************/ + uiSlider: function($cell, indx, sliderDef) { + var o = $.extend({ + // filter formatter options + delayed : true, + valueToHeader : false, + exactMatch : true, + cellText : '', + compare : '', + allText : 'all', + // include ANY jQuery UI spinner options below + // except values, since this is a non-range setup + value : 0, + min : 0, + max : 100, + step : 1, + range : 'min' + }, sliderDef ), + c = $cell.closest('table')[0].config, + // Add a hidden input to hold the range values + $input = $('<input class="filter" type="hidden">') + .appendTo($cell) + // hidden filter update namespace trigger by filter widget + .bind('change' + c.namespace + 'filter', function(){ + updateSlider({ value: this.value }); + }), + $shcell = [], + + // this function updates the hidden input and adds the current values to the header cell text + updateSlider = function(ui, notrigger) { + // ui is not undefined on create + var v = typeof ui !== 'undefined' ? ts.formatFloat((ui.value + '').replace(/[><=]/g, '')) || o.value : o.value, + val = o.compare ? v : v === o.min ? o.allText : v, + compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', + result = compare + val, + searchType = ui && typeof ui.delayed === 'boolean' ? ui.delayed : c.$table[0].hasInitialized ? o.delayed || '' : true; if (o.valueToHeader) { - $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(' (' + result + ')'); + // add range indication to the header cell above! + $cell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(' (' + result + ')'); } else { - $shcell.find('.ui-slider-handle').addClass('value-popup').attr('data-value', result); + // add values to the handle data-value attribute so the css tooltip will work properly + $cell.find('.ui-slider-handle').addClass('value-popup').attr('data-value', result); } - } + // update the hidden input; + // ****** ADD AN EQUAL SIGN TO THE BEGINNING! <- this makes the slide exactly match the number ****** + // when the value is at the minimum, clear the hidden input so all rows will be seen - }; - $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed'); + $cell.find('.filter') + .val( ( compare ? compare + v : v === o.min ? '' : (o.exactMatch ? '=' : '') + v ) ) + .trigger( notrigger ? '' : 'search', searchType ).end() + .find('.slider').slider('value', v); - // add span to header for value - only works if the line in the updateSlider() function is also un-commented out - if (o.valueToHeader) { - $cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="curvalue" />'); - } + // update sticky header cell + if ($shcell.length) { + $shcell + .find(compareSelect).val( compare ).end() + .find('.slider').slider('value', v); + if (o.valueToHeader) { + $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(' (' + result + ')'); + } else { + $shcell.find('.ui-slider-handle').addClass('value-popup').attr('data-value', result); + } + } - // add callbacks; preserve added callbacks - o.oldcreate = o.create; - o.oldslide = o.slide; - o.create = function(event, ui) { - updateSlider(); // ui is an empty object on create - if (typeof o.oldcreate === 'function') { o.oldcreate(event, ui); } - }; - o.slide = function(event, ui) { - updateSlider(ui); - if (typeof o.oldslide === 'function') { o.oldslide(event, ui); } - }; - // add a jQuery UI slider! - $('<div class="slider slider' + indx + '"/>') - .appendTo($cell) - .slider(o); - - // update slider from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ - var val = tsff.updateCompare($cell, $input, o)[0]; - $cell.find('.slider').slider('value', val ); - updateSlider({ value: val }, false); - ts.filter.formatterUpdated($cell, indx); - }); - - if (o.compare) { - // add compare select - tsff.addCompare($cell, indx, o); - $cell.find(compareSelect).bind('change', function(){ - updateSlider({ value: $cell.find('.slider').slider('value') }); - }); - } + }; + $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed'); - // on reset - c.$table.bind('filterReset', function(){ - if ($.isArray(o.compare)) { - $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); + // add span to header for value - only works if the line in the updateSlider() function is also un-commented out + if (o.valueToHeader) { + $cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="curvalue" />'); } - setTimeout(function(){ - updateSlider({ value: o.value }); - }, 0); - }); - - // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ - $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + // add callbacks; preserve added callbacks + o.oldcreate = o.create; + o.oldslide = o.slide; + o.create = function(event, ui) { + updateSlider(); // ui is an empty object on create + if (typeof o.oldcreate === 'function') { o.oldcreate(event, ui); } + }; + o.slide = function(event, ui) { + updateSlider(ui); + if (typeof o.oldslide === 'function') { o.oldslide(event, ui); } + }; // add a jQuery UI slider! $('<div class="slider slider' + indx + '"/>') - .val(o.value) - .appendTo($shcell) - .slider(o) - .bind('change keyup', function(){ - $cell.find('.slider').slider('value', this.value ); - updateSlider(); - }); + .appendTo($cell) + .slider(o); + + // update slider from hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + var val = tsff.updateCompare($cell, $input, o)[0]; + $cell.find('.slider').slider('value', val ); + updateSlider({ value: val }, false); + ts.filter.formatterUpdated($cell, indx); + }); if (o.compare) { // add compare select - tsff.addCompare($shcell, indx, o); - $shcell.find(compareSelect).bind('change', function(){ - $cell.find(compareSelect).val( $(this).val() ); - updateSlider(); + tsff.addCompare($cell, indx, o); + $cell.find(compareSelect).bind('change', function(){ + updateSlider({ value: $cell.find('.slider').slider('value') }); }); } - }); - - return $input; - }, - - /*************************\ - jQuery UI Range Slider (2 handles) - \*************************/ - uiRange: function($cell, indx, rangeDef) { - var o = $.extend({ - // filter formatter options - delayed : true, - valueToHeader : false, - // include ANY jQuery UI spinner options below - // except value, since this one is range specific) - values : [0, 100], - min : 0, - max : 100, - range : true - }, rangeDef ), - c = $cell.closest('table')[0].config, - // Add a hidden input to hold the range values - $input = $('<input class="filter" type="hidden">') - .appendTo($cell) - // hidden filter update namespace trigger by filter widget - .bind('change' + c.namespace + 'filter', function(){ - getRange(); - }), - $shcell = [], - - getRange = function(){ - var val = $input.val(), - v = val.split(' - '); - if (val === '') { v = [ o.min, o.max ]; } - if (v && v[1]) { - updateUiRange({ values: v, delay: false }, true); - } + // on reset + c.$table.bind('filterReset', function(){ + if ($.isArray(o.compare)) { + $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); + } + setTimeout(function(){ + updateSlider({ value: o.value }); + }, 0); + }); + + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + + // add a jQuery UI slider! + $('<div class="slider slider' + indx + '"/>') + .val(o.value) + .appendTo($shcell) + .slider(o) + .bind('change keyup', function(){ + $cell.find('.slider').slider('value', this.value ); + updateSlider(); + }); + + if (o.compare) { + // add compare select + tsff.addCompare($shcell, indx, o); + $shcell.find(compareSelect).bind('change', function(){ + $cell.find(compareSelect).val( $(this).val() ); + updateSlider(); + }); + } + + }); + + return $input; }, - // this function updates the hidden input and adds the current values to the header cell text - updateUiRange = function(ui, notrigger) { - // ui.values are undefined for some reason on create - var val = ui && ui.values || o.values, - result = val[0] + ' - ' + val[1], - // make range an empty string if entire range is covered so the filter row will hide (if set) - range = val[0] === o.min && val[1] === o.max ? '' : result, - searchType = ui && typeof ui.delayed === 'boolean' ? ui.delayed : c.$table[0].hasInitialized ? o.delayed || '': true; - if (o.valueToHeader) { - // add range indication to the header cell above (if not using the css method)! - $cell.closest('thead').find('th[data-column=' + indx + ']').find('.currange').html(' (' + result + ')'); - } else { - // add values to the handle data-value attribute so the css tooltip will work properly - $cell.find('.ui-slider-handle') - .addClass('value-popup') - .eq(0).attr('data-value', val[0]).end() // adding value to data attribute - .eq(1).attr('data-value', val[1]); // value popup shown via css - } - // update the hidden input - $cell.find('.filter').val(range) - .trigger(notrigger ? '' : 'search', searchType).end() - .find('.range').slider('values', val); - // update sticky header cell - if ($shcell.length) { - $shcell.find('.range').slider('values', val); + /*************************\ + jQuery UI Range Slider (2 handles) + \*************************/ + uiRange: function($cell, indx, rangeDef) { + var o = $.extend({ + // filter formatter options + delayed : true, + valueToHeader : false, + // include ANY jQuery UI spinner options below + // except value, since this one is range specific) + values : [ 0, 100 ], + min : 0, + max : 100, + range : true + }, rangeDef ), + c = $cell.closest('table')[0].config, + // Add a hidden input to hold the range values + $input = $('<input class="filter" type="hidden">') + .appendTo($cell) + // hidden filter update namespace trigger by filter widget + .bind('change' + c.namespace + 'filter', function(){ + getRange(); + }), + $shcell = [], + + getRange = function(){ + var val = $input.val(), + v = val.split(' - '); + if (val === '') { v = [ o.min, o.max ]; } + if (v && v[1]) { + updateUiRange({ values: v, delay: false }, true); + } + }, + + // this function updates the hidden input and adds the current values to the header cell text + updateUiRange = function(ui, notrigger) { + // ui.values are undefined for some reason on create + var val = ui && ui.values || o.values, + result = val[0] + ' - ' + val[1], + // make range an empty string if entire range is covered so the filter row will hide (if set) + range = val[0] === o.min && val[1] === o.max ? '' : result, + searchType = ui && typeof ui.delayed === 'boolean' ? ui.delayed : c.$table[0].hasInitialized ? o.delayed || '' : true; if (o.valueToHeader) { - $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.currange').html(' (' + result + ')'); + // add range indication to the header cell above (if not using the css method)! + $cell.closest('thead').find('th[data-column=' + indx + ']').find('.currange').html(' (' + result + ')'); } else { - $shcell.find('.ui-slider-handle') - .addClass('value-popup') - .eq(0).attr('data-value', val[0]).end() // adding value to data attribute - .eq(1).attr('data-value', val[1]); // value popup shown via css + // add values to the handle data-value attribute so the css tooltip will work properly + $cell.find('.ui-slider-handle') + .addClass('value-popup') + .eq(0).attr('data-value', val[0]).end() // adding value to data attribute + .eq(1).attr('data-value', val[1]); // value popup shown via css + } + // update the hidden input + $cell.find('.filter').val(range) + .trigger(notrigger ? '' : 'search', searchType).end() + .find('.range').slider('values', val); + // update sticky header cell + if ($shcell.length) { + $shcell.find('.range').slider('values', val); + if (o.valueToHeader) { + $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.currange').html(' (' + result + ')'); + } else { + $shcell.find('.ui-slider-handle') + .addClass('value-popup') + .eq(0).attr('data-value', val[0]).end() // adding value to data attribute + .eq(1).attr('data-value', val[1]); // value popup shown via css + } } + + }; + $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed'); + + // add span to header for value - only works if the line in the updateUiRange() function is also un-commented out + if (o.valueToHeader) { + $cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="currange"/>'); } - }; - $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed'); + // add callbacks; preserve added callbacks + o.oldcreate = o.create; + o.oldslide = o.slide; + // add a jQuery UI range slider! + o.create = function(event, ui) { + updateUiRange(); // ui is an empty object on create + if (typeof o.oldcreate === 'function') { o.oldcreate(event, ui); } + }; + o.slide = function(event, ui) { + updateUiRange(ui); + if (typeof o.oldslide === 'function') { o.oldslide(event, ui); } + }; + $('<div class="range range' + indx + '"/>') + .appendTo($cell) + .slider(o); - // add span to header for value - only works if the line in the updateUiRange() function is also un-commented out - if (o.valueToHeader) { - $cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="currange"/>'); - } + // update slider from hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + getRange(); + ts.filter.formatterUpdated($cell, indx); + }); + + // on reset + c.$table.bind('filterReset', function(){ + $cell.find('.range').slider('values', o.values); + setTimeout(function(){ + updateUiRange(); + }, 0); + }); + + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + + // add a jQuery UI slider! + $('<div class="range range' + indx + '"/>') + .val(o.value) + .appendTo($shcell) + .slider(o) + .bind('change keyup', function(){ + $cell.find('.range').val( this.value ); + updateUiRange(); + }); - // add callbacks; preserve added callbacks - o.oldcreate = o.create; - o.oldslide = o.slide; - // add a jQuery UI range slider! - o.create = function(event, ui) { - updateUiRange(); // ui is an empty object on create - if (typeof o.oldcreate === 'function') { o.oldcreate(event, ui); } - }; - o.slide = function(event, ui) { - updateUiRange(ui); - if (typeof o.oldslide === 'function') { o.oldslide(event, ui); } - }; - $('<div class="range range' + indx +'"/>') - .appendTo($cell) - .slider(o); - - // update slider from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ - getRange(); - ts.filter.formatterUpdated($cell, indx); - }); - - // on reset - c.$table.bind('filterReset', function(){ - $cell.find('.range').slider('values', o.values); - setTimeout(function(){ - updateUiRange(); - }, 0); - }); - - // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ - $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); - - // add a jQuery UI slider! - $('<div class="range range' + indx + '"/>') - .val(o.value) - .appendTo($shcell) - .slider(o) - .bind('change keyup', function(){ - $cell.find('.range').val( this.value ); - updateUiRange(); }); - }); - - // return the hidden input so the filter widget has a reference to it - return $input; - }, - - /*************************\ - jQuery UI Datepicker compare (1 input) - \*************************/ - uiDateCompare: function($cell, indx, defDate) { - var o = $.extend({ - // filter formatter options - cellText : '', - compare : '', - endOfDay : true, - // include ANY jQuery UI spinner options below - - defaultDate : '', - - changeMonth : true, - changeYear : true, - numberOfMonths : 1 - }, defDate), - - $date, - c = $cell.closest('table')[0].config, - // make sure we're using parsed dates in the search - $hdr = $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed'), - // Add a hidden input to hold the range values - $input = $('<input class="dateCompare" type="hidden">') - .appendTo($cell) - // hidden filter update namespace trigger by filter widget - .bind('change' + c.namespace + 'filter', function(){ - var v = this.value; - if (v) { - o.onClose(v); + // return the hidden input so the filter widget has a reference to it + return $input; + }, + + /*************************\ + jQuery UI Datepicker compare (1 input) + \*************************/ + uiDateCompare: function($cell, indx, defDate) { + var o = $.extend({ + // filter formatter options + cellText : '', + compare : '', + endOfDay : true, + // include ANY jQuery UI spinner options below + + defaultDate : '', + + changeMonth : true, + changeYear : true, + numberOfMonths : 1 + }, defDate), + + $date, + c = $cell.closest('table')[0].config, + // make sure we're using parsed dates in the search + $hdr = $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed'), + // Add a hidden input to hold the range values + $input = $('<input class="dateCompare" type="hidden">') + .appendTo($cell) + // hidden filter update namespace trigger by filter widget + .bind('change' + c.namespace + 'filter', function(){ + var v = this.value; + if (v) { + o.onClose(v); + } + }), + t, $shcell = [], + + // this function updates the hidden input + date1Compare = function(notrigger) { + var date, query, + getdate = $date.datepicker('getDate') || '', + compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', + searchType = c.$table[0].hasInitialized ? o.delayed || '' : true; + $date.datepicker('setDate', (getdate === '' ? '' : getdate) || null); + if (getdate === '') { notrigger = false; } + date = $date.datepicker('getDate'); + query = date ? ( o.endOfDay && /<=/.test(compare) ? date.setHours(23, 59, 59) : date.getTime() ) || '' : ''; + if (date && o.endOfDay && compare === '=') { + compare = ''; + query += ' - ' + date.setHours(23, 59, 59); + notrigger = false; } - }), - t, $shcell = [], - - // this function updates the hidden input - date1Compare = function(notrigger) { - var date, query, - getdate = $date.datepicker('getDate') || '', - compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', - searchType = c.$table[0].hasInitialized ? o.delayed || '': true; - $date.datepicker('setDate', (getdate === '' ? '' : getdate) || null); - if (getdate === '') { notrigger = false; } - date = $date.datepicker('getDate'); - query = date ? ( o.endOfDay && /<=/.test(compare) ? date.setHours(23, 59, 59) : date.getTime() ) || '' : ''; - if (date && o.endOfDay && compare === '=') { - compare = ''; - query += ' - ' + date.setHours(23, 59, 59); - notrigger = false; - } - $cell.find('.dateCompare') - // add equal to the beginning, so we filter exact numbers - .val(compare + query) - .trigger( notrigger ? '' : 'search', searchType ).end(); - // update sticky header cell - if ($shcell.length) { - $shcell - .find('.dateCompare').val(compare + query).end() - .find(compareSelect).val(compare); - } - }; - - // Add date range picker - t = '<input type="text" class="date date' + indx + '" placeholder="' + - ($hdr.data('placeholder') || $hdr.attr('data-placeholder') || c.widgetOptions.filter_placeholder.search || '') + '" />'; - $date = $(t).appendTo($cell); - - // add callbacks; preserve added callbacks - o.oldonClose = o.onClose; - - o.onClose = function( selectedDate, ui ) { - date1Compare(); - if (typeof o.oldonClose === 'function') { o.oldonClose(selectedDate, ui); } - }; - $date.datepicker(o); - - // on reset - c.$table.bind('filterReset', function(){ - if ($.isArray(o.compare)) { - $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); - } - $cell.add($shcell).find('.date').val(o.defaultDate).datepicker('setDate', o.defaultDate || null); - setTimeout(function(){ - date1Compare(); - }, 0); - }); - - // update date compare from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ - var num, v = $input.val(); - if (/\s+-\s+/.test(v)) { - // date range found; assume an exact match on one day - $cell.find(compareSelect).val('='); - num = v.split(/\s+-\s+/)[0]; - $date.datepicker( 'setDate', num || null ); - } else { - num = (tsff.updateCompare($cell, $input, o)[1]).toString() || ''; - // differeniate 1388556000000 from 1/1/2014 using \d{5} regex - num = num !== '' ? /\d{5}/g.test(num) ? new Date(Number(num)) : num || '' : ''; - } - $cell.add($shcell).find('.date').datepicker( 'setDate', num || null ); - setTimeout(function(){ - date1Compare(true); - ts.filter.formatterUpdated($cell, indx); - }, 0); - }); + $cell.find('.dateCompare') + // add equal to the beginning, so we filter exact numbers + .val(compare + query) + .trigger( notrigger ? '' : 'search', searchType ).end(); + // update sticky header cell + if ($shcell.length) { + $shcell + .find('.dateCompare').val(compare + query).end() + .find(compareSelect).val(compare); + } + }; + + // Add date range picker + t = '<input type="text" class="date date' + indx + '" placeholder="' + + ($hdr.data('placeholder') || $hdr.attr('data-placeholder') || c.widgetOptions.filter_placeholder.search || '') + '" />'; + $date = $(t).appendTo($cell); + + // add callbacks; preserve added callbacks + o.oldonClose = o.onClose; - if (o.compare) { - // add compare select - tsff.addCompare($cell, indx, o); - $cell.find(compareSelect).bind('change', function(){ + o.onClose = function( selectedDate, ui ) { date1Compare(); + if (typeof o.oldonClose === 'function') { o.oldonClose(selectedDate, ui); } + }; + $date.datepicker(o); + + // on reset + c.$table.bind('filterReset', function(){ + if ($.isArray(o.compare)) { + $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); + } + $cell.add($shcell).find('.date').val(o.defaultDate).datepicker('setDate', o.defaultDate || null); + setTimeout(function(){ + date1Compare(); + }, 0); }); - } - // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ - $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); - - // add a jQuery datepicker! - $shcell - .append(t) - .find('.date') - .datepicker(o); + // update date compare from hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + var num, v = $input.val(); + if (/\s+-\s+/.test(v)) { + // date range found; assume an exact match on one day + $cell.find(compareSelect).val('='); + num = v.split(/\s+-\s+/)[0]; + $date.datepicker( 'setDate', num || null ); + } else { + num = (tsff.updateCompare($cell, $input, o)[1]).toString() || ''; + // differeniate 1388556000000 from 1/1/2014 using \d{5} regex + num = num !== '' ? /\d{5}/g.test(num) ? new Date(Number(num)) : num || '' : ''; + } + $cell.add($shcell).find('.date').datepicker( 'setDate', num || null ); + setTimeout(function(){ + date1Compare(true); + ts.filter.formatterUpdated($cell, indx); + }, 0); + }); if (o.compare) { // add compare select - tsff.addCompare($shcell, indx, o); - $shcell.find(compareSelect).bind('change', function(){ - $cell.find(compareSelect).val( $(this).val() ); + tsff.addCompare($cell, indx, o); + $cell.find(compareSelect).bind('change', function(){ date1Compare(); }); } - }); - - // return the hidden input so the filter widget has a reference to it - return $input.val( o.defaultDate ? o.defaultDate : '' ); - }, - - /*************************\ - jQuery UI Datepicker (2 inputs) - \*************************/ - uiDatepicker: function($cell, indx, defDate) { - var o = $.extend({ - // filter formatter options - endOfDay : true, - textFrom : 'from', - textTo : 'to', - from : '', // defaultDate "from" input - to : '', // defaultDate "to" input - // include ANY jQuery UI spinner options below - changeMonth : true, - changeYear : true, - numberOfMonths : 1 - }, defDate), - t, closeDate, $shcell = [], - c = $cell.closest('table')[0].config, - validDate = function(d){ - return d instanceof Date && isFinite(d); - }, - // Add a hidden input to hold the range values - $input = $('<input class="dateRange" type="hidden">') - .appendTo($cell) - // hidden filter update namespace trigger by filter widget - .bind('change' + c.namespace + 'filter', function(){ - var v = this.value; - if (v.match(' - ')) { - v = v.split(' - '); - $cell.find('.dateTo').val(v[1]); - closeDate(v[0]); - } else if (v.match('>=')) { - closeDate( v.replace('>=', '') ); - } else if (v.match('<=')) { - closeDate( v.replace('<=', '') ); + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + + // add a jQuery datepicker! + $shcell + .append(t) + .find('.date') + .datepicker(o); + + if (o.compare) { + // add compare select + tsff.addCompare($shcell, indx, o); + $shcell.find(compareSelect).bind('change', function(){ + $cell.find(compareSelect).val( $(this).val() ); + date1Compare(); + }); } - }), - - // make sure we're using parsed dates in the search - $hdr = $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed'); - // Add date range picker - t = '<label>' + o.textFrom + '</label><input type="text" class="dateFrom" placeholder="' + - ($hdr.data('placeholderFrom') || $hdr.attr('data-placeholder-from') || c.widgetOptions.filter_placeholder.from || '') + '" />' + - '<label>' + o.textTo + '</label><input type="text" class="dateTo" placeholder="' + - ($hdr.data('placeholderTo') || $hdr.attr('data-placeholder-to') || c.widgetOptions.filter_placeholder.to || '') + '" />'; - $(t).appendTo($cell); - - // add callbacks; preserve added callbacks - o.oldonClose = o.onClose; - - closeDate = o.onClose = function( selectedDate, ui ) { - var range, - from = $cell.find('.dateFrom').datepicker('getDate'), - to = $cell.find('.dateTo').datepicker('getDate'); - from = validDate(from) ? from.getTime() : ''; - to = validDate(to) ? ( o.endOfDay ? to.setHours(23, 59, 59) : to.getTime() ) || '' : ''; - range = from ? ( to ? from + ' - ' + to : '>=' + from ) : (to ? '<=' + to : ''); - $cell.add( $shcell ) - .find('.dateRange').val(range) - .trigger('search'); - // date picker needs date objects - from = from ? new Date(from) : ''; - to = to ? new Date(to) : ''; - - if (/<=/.test(range)) { - $cell.add( $shcell ) - .find('.dateFrom').datepicker('option', 'maxDate', to || null ).end() - .find('.dateTo').datepicker('option', 'minDate', null).datepicker('setDate', to || null); - } else if (/>=/.test(range)) { - $cell.add( $shcell ) - .find('.dateFrom').datepicker('option', 'maxDate', null).datepicker('setDate', from || null).end() - .find('.dateTo').datepicker('option', 'minDate', from || null ); - } else { - $cell.add( $shcell ) - .find('.dateFrom').datepicker('option', 'maxDate', null).datepicker('setDate', from || null ).end() - .find('.dateTo').datepicker('option', 'minDate', null).datepicker('setDate', to || null); - } - if (typeof o.oldonClose === 'function') { o.oldonClose(selectedDate, ui); } - }; + }); - o.defaultDate = o.from || ''; - $cell.find('.dateFrom').datepicker(o); - o.defaultDate = o.to || '+7d'; // set to date +7 days from today (if not defined) - $cell.find('.dateTo').datepicker(o); + // return the hidden input so the filter widget has a reference to it + return $input.val( o.defaultDate ? o.defaultDate : '' ); + }, - // update date compare from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ - var val = $input.val() || '', - from = '', - to = ''; - // date range - if (/\s+-\s+/.test(val)){ - val = val.split(/\s+-\s+/) || []; - from = val[0] || ''; - to = val[1] || ''; - } else if (/>=/.test(val)) { - // greater than date (to date empty) - from = val.replace(/>=/, '') || ''; - } else if (/<=/.test(val)) { - // less than date (from date empty) - to = val.replace(/<=/, '') || ''; - } + /*************************\ + jQuery UI Datepicker (2 inputs) + \*************************/ + uiDatepicker: function($cell, indx, defDate) { + var o = $.extend({ + // filter formatter options + endOfDay : true, + textFrom : 'from', + textTo : 'to', + from : '', // defaultDate 'from' input + to : '', // defaultDate 'to' input + // include ANY jQuery UI spinner options below + changeMonth : true, + changeYear : true, + numberOfMonths : 1 + }, defDate), + t, closeDate, $shcell = [], + c = $cell.closest('table')[0].config, + validDate = function(d){ + return d instanceof Date && isFinite(d); + }, + // Add a hidden input to hold the range values + $input = $('<input class="dateRange" type="hidden">') + .appendTo($cell) + // hidden filter update namespace trigger by filter widget + .bind('change' + c.namespace + 'filter', function(){ + var v = this.value; + if (v.match(' - ')) { + v = v.split(' - '); + $cell.find('.dateTo').val(v[1]); + closeDate(v[0]); + } else if (v.match('>=')) { + closeDate( v.replace('>=', '') ); + } else if (v.match('<=')) { + closeDate( v.replace('<=', '') ); + } + }), + + // make sure we're using parsed dates in the search + $hdr = $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed'); + // Add date range picker + t = '<label>' + o.textFrom + '</label><input type="text" class="dateFrom" placeholder="' + + ($hdr.data('placeholderFrom') || $hdr.attr('data-placeholder-from') || c.widgetOptions.filter_placeholder.from || '') + '" />' + + '<label>' + o.textTo + '</label><input type="text" class="dateTo" placeholder="' + + ($hdr.data('placeholderTo') || $hdr.attr('data-placeholder-to') || c.widgetOptions.filter_placeholder.to || '') + '" />'; + $(t).appendTo($cell); + + // add callbacks; preserve added callbacks + o.oldonClose = o.onClose; + + closeDate = o.onClose = function( selectedDate, ui ) { + var range, + from = $cell.find('.dateFrom').datepicker('getDate'), + to = $cell.find('.dateTo').datepicker('getDate'); + from = validDate(from) ? from.getTime() : ''; + to = validDate(to) ? ( o.endOfDay ? to.setHours(23, 59, 59) : to.getTime() ) || '' : ''; + range = from ? ( to ? from + ' - ' + to : '>=' + from ) : (to ? '<=' + to : ''); + $cell.add( $shcell ) + .find('.dateRange').val(range) + .trigger('search'); + // date picker needs date objects + from = from ? new Date(from) : ''; + to = to ? new Date(to) : ''; + + if (/<=/.test(range)) { + $cell.add( $shcell ) + .find('.dateFrom').datepicker('option', 'maxDate', to || null ).end() + .find('.dateTo').datepicker('option', 'minDate', null).datepicker('setDate', to || null); + } else if (/>=/.test(range)) { + $cell.add( $shcell ) + .find('.dateFrom').datepicker('option', 'maxDate', null).datepicker('setDate', from || null).end() + .find('.dateTo').datepicker('option', 'minDate', from || null ); + } else { + $cell.add( $shcell ) + .find('.dateFrom').datepicker('option', 'maxDate', null).datepicker('setDate', from || null ).end() + .find('.dateTo').datepicker('option', 'minDate', null).datepicker('setDate', to || null); + } - // differeniate 1388556000000 from 1/1/2014 using \d{5} regex - from = from !== '' ? /\d{5}/g.test(from) ? new Date(Number(from)) : from || '' : ''; - to = to !== '' ? /\d{5}/g.test(to) ? new Date(Number(to)) : to || '' : ''; + if (typeof o.oldonClose === 'function') { o.oldonClose(selectedDate, ui); } + }; - $cell.add($shcell).find('.dateFrom').datepicker('setDate', from || null); - $cell.add($shcell).find('.dateTo').datepicker('setDate', to || null); - // give datepicker time to process - setTimeout(function(){ - closeDate(); - ts.filter.formatterUpdated($cell, indx); - }, 0); - }); + o.defaultDate = o.from || ''; + $cell.find('.dateFrom').datepicker(o); + o.defaultDate = o.to || '+7d'; // set to date +7 days from today (if not defined) + $cell.find('.dateTo').datepicker(o); + + // update date compare from hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + var val = $input.val() || '', + from = '', + to = ''; + // date range + if (/\s+-\s+/.test(val)){ + val = val.split(/\s+-\s+/) || []; + from = val[0] || ''; + to = val[1] || ''; + } else if (/>=/.test(val)) { + // greater than date (to date empty) + from = val.replace(/>=/, '') || ''; + } else if (/<=/.test(val)) { + // less than date (from date empty) + to = val.replace(/<=/, '') || ''; + } - // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ - $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); - $shcell.append(t); + // differeniate 1388556000000 from 1/1/2014 using \d{5} regex + from = from !== '' ? /\d{5}/g.test(from) ? new Date(Number(from)) : from || '' : ''; + to = to !== '' ? /\d{5}/g.test(to) ? new Date(Number(to)) : to || '' : ''; + + $cell.add($shcell).find('.dateFrom').datepicker('setDate', from || null); + $cell.add($shcell).find('.dateTo').datepicker('setDate', to || null); + // give datepicker time to process + setTimeout(function(){ + closeDate(); + ts.filter.formatterUpdated($cell, indx); + }, 0); + }); - // add a jQuery datepicker! - o.defaultDate = o.from || ''; - $shcell.find('.dateFrom').datepicker(o); + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + $shcell.append(t); - o.defaultDate = o.to || '+7d'; - $shcell.find('.dateTo').datepicker(o); + // add a jQuery datepicker! + o.defaultDate = o.from || ''; + $shcell.find('.dateFrom').datepicker(o); - }); + o.defaultDate = o.to || '+7d'; + $shcell.find('.dateTo').datepicker(o); - // on reset - $cell.closest('table').bind('filterReset', function(){ - $cell.add($shcell).find('.dateFrom').val('').datepicker('setDate', o.from || null ); - $cell.add($shcell).find('.dateTo').val('').datepicker('setDate', o.to || null ); - setTimeout(function(){ - closeDate(); - }, 0); - }); + }); + + // on reset + $cell.closest('table').bind('filterReset', function(){ + $cell.add($shcell).find('.dateFrom').val('').datepicker('setDate', o.from || null ); + $cell.add($shcell).find('.dateTo').val('').datepicker('setDate', o.to || null ); + setTimeout(function(){ + closeDate(); + }, 0); + }); - // return the hidden input so the filter widget has a reference to it - return $input.val( o.from ? ( o.to ? o.from + ' - ' + o.to : '>=' + o.from ) : (o.to ? '<=' + o.to : '') ); - } + // return the hidden input so the filter widget has a reference to it + return $input.val( o.from ? ( o.to ? o.from + ' - ' + o.to : '>=' + o.from ) : (o.to ? '<=' + o.to : '') ); + } -}); + }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js index ec5d0d3..8139a58 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js @@ -4,146 +4,146 @@ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ ;(function($){ -"use strict"; + 'use strict'; -var ts = $.tablesorter || {}; -ts.filterFormatter = ts.filterFormatter || {}; + var ts = $.tablesorter || {}; + ts.filterFormatter = ts.filterFormatter || {}; -/************************\ - Select2 Filter Formatter -\************************/ -ts.filterFormatter.select2 = function($cell, indx, select2Def) { - var o = $.extend({ - // select2 filter formatter options - cellText : '', // Text (wrapped in a label element) - match : true, // adds "filter-match" to header - value : '', - // include ANY select2 options below - multiple : true, - width : '100%' + /************************\ + Select2 Filter Formatter + \************************/ + ts.filterFormatter.select2 = function($cell, indx, select2Def) { + var o = $.extend({ + // select2 filter formatter options + cellText : '', // Text (wrapped in a label element) + match : true, // adds 'filter-match' to header + value : '', + // include ANY select2 options below + multiple : true, + width : '100%' - }, select2Def ), - arry, data, - c = $cell.closest('table')[0].config, - wo = c.widgetOptions, - // Add a hidden input to hold the range values - $input = $('<input class="filter" type="hidden">') - .appendTo($cell) - // hidden filter update namespace trigger by filter widget - .bind('change' + c.namespace + 'filter', function(){ - var val = this.value; - val = val.replace(/[/()$^]/g, '').split('|'); - $cell.find('.select2').select2('val', val); - updateSelect2(); - }), - $header = c.$headerIndexed[indx], - onlyAvail = $header.hasClass(wo.filter_onlyAvail), - $shcell = [], - matchPrefix = o.match ? '' : '^', - matchSuffix = o.match ? '' : '$', - - // this function updates the hidden input and adds the current values to the header cell text - updateSelect2 = function() { - var arry = false, - v = $cell.find('.select2').select2('val') || o.value || ''; - // convert array to string - if ($.isArray(v)) { - arry = true; - v = v.join('\u0000'); - } - // escape special regex characters (http://stackoverflow.com/a/9310752/145346) - v = v.replace(/[-[\]{}()*+?.,/\\^$|#\s]/g, '\\$&'); - // convert string back into an array - if (arry) { - v = v.split('\u0000'); - } - $input - // add regex, so we filter exact numbers - .val( $.isArray(v) && v.length && v.join('') !== '' ? '/(' + matchPrefix + (v || []).join(matchSuffix + '|' + matchPrefix) + matchSuffix + ')/' : '' ) - .trigger('search').end() - .find('.select2').select2('val', v); - // update sticky header cell - if ($shcell.length) { - $shcell.find('.select2').select2('val', v); - } - }, - - // get options from table cell content or filter_selectSource (v2.16) - updateOptions = function(){ - data = []; - arry = ts.filter.getOptionSource(c.$table[0], indx, onlyAvail) || []; - // build select2 data option - $.each(arry, function(i,v){ - data.push({id: v, text: v}); - }); - o.data = data; - }; + }, select2Def ), + arry, data, + c = $cell.closest('table')[0].config, + wo = c.widgetOptions, + // Add a hidden input to hold the range values + $input = $('<input class="filter" type="hidden">') + .appendTo($cell) + // hidden filter update namespace trigger by filter widget + .bind('change' + c.namespace + 'filter', function(){ + var val = this.value; + val = val.replace(/[/()$^]/g, '').split('|'); + $cell.find('.select2').select2('val', val); + updateSelect2(); + }), + $header = c.$headerIndexed[indx], + onlyAvail = $header.hasClass(wo.filter_onlyAvail), + $shcell = [], + matchPrefix = o.match ? '' : '^', + matchSuffix = o.match ? '' : '$', - // get filter-match class from option - $header.toggleClass('filter-match', o.match); - if (o.cellText) { - $cell.prepend('<label>' + o.cellText + '</label>'); - } + // this function updates the hidden input and adds the current values to the header cell text + updateSelect2 = function() { + var arry = false, + v = $cell.find('.select2').select2('val') || o.value || ''; + // convert array to string + if ($.isArray(v)) { + arry = true; + v = v.join('\u0000'); + } + // escape special regex characters (http://stackoverflow.com/a/9310752/145346) + v = v.replace(/[-[\]{}()*+?.,/\\^$|#\s]/g, '\\$&'); + // convert string back into an array + if (arry) { + v = v.split('\u0000'); + } + $input + // add regex, so we filter exact numbers + .val( $.isArray(v) && v.length && v.join('') !== '' ? '/(' + matchPrefix + (v || []).join(matchSuffix + '|' + matchPrefix) + matchSuffix + ')/' : '' ) + .trigger('search').end() + .find('.select2').select2('val', v); + // update sticky header cell + if ($shcell.length) { + $shcell.find('.select2').select2('val', v); + } + }, - // don't add default in table options if either ajax or - // data options are already defined - if (!(o.ajax && !$.isEmptyObject(o.ajax)) && !o.data) { - updateOptions(); - if (onlyAvail) { - c.$table.bind('filterEnd', function(){ - updateOptions(); - $cell.add($shcell).find('.select2').select2(o); + // get options from table cell content or filter_selectSource (v2.16) + updateOptions = function(){ + data = []; + arry = ts.filter.getOptionSource(c.$table[0], indx, onlyAvail) || []; + // build select2 data option + $.each(arry, function(i, v){ + data.push({id: v, text: v}); }); - } - } + o.data = data; + }; - // add a select2 hidden input! - $('<input class="select2 select2-' + indx + '" type="hidden" />') - .val(o.value) - .appendTo($cell) - .select2(o) - .bind('change', function(){ - updateSelect2(); - }); + // get filter-match class from option + $header.toggleClass('filter-match', o.match); + if (o.cellText) { + $cell.prepend('<label>' + o.cellText + '</label>'); + } - // update select2 from filter hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ - // value = '/(^x$|^y$)/' => 'x,y' - var val = c.$table.data('lastSearch')[indx] || ''; - val = val.replace(/^\/\(\^?/,'').replace(/\$\|\^/g, '|').replace(/\$?\)\/$/g,'').split('|'); - $cell.find('.select2').select2('val', val); - updateSelect2(); - ts.filter.formatterUpdated($cell, indx); - }); + // don't add default in table options if either ajax or + // data options are already defined + if (!(o.ajax && !$.isEmptyObject(o.ajax)) && !o.data) { + updateOptions(); + if (onlyAvail) { + c.$table.bind('filterEnd', function(){ + updateOptions(); + $cell.add($shcell).find('.select2').select2(o); + }); + } + } - // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ - $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); - // add a select2! - $('<input class="select2 select2-' + indx + '" type="hidden">') + // add a select2 hidden input! + $('<input class="select2 select2-' + indx + '" type="hidden" />') .val(o.value) - .appendTo($shcell) + .appendTo($cell) .select2(o) .bind('change', function(){ - $cell.find('.select2').select2('val', $shcell.find('.select2').select2('val') ); updateSelect2(); }); - if (o.cellText) { - $shcell.prepend('<label>' + o.cellText + '</label>'); - } - }); - - // on reset - c.$table.bind('filterReset', function(){ - $cell.find('.select2').select2('val', o.value || ''); - setTimeout(function(){ + // update select2 from filter hidden input, in case of saved filters + c.$table.bind('filterFomatterUpdate', function(){ + // value = '/(^x$|^y$)/' => 'x,y' + var val = c.$table.data('lastSearch')[indx] || ''; + val = val.replace(/^\/\(\^?/, '').replace(/\$\|\^/g, '|').replace(/\$?\)\/$/g, '').split('|'); + $cell.find('.select2').select2('val', val); updateSelect2(); - }, 0); - }); + ts.filter.formatterUpdated($cell, indx); + }); + + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + // add a select2! + $('<input class="select2 select2-' + indx + '" type="hidden">') + .val(o.value) + .appendTo($shcell) + .select2(o) + .bind('change', function(){ + $cell.find('.select2').select2('val', $shcell.find('.select2').select2('val') ); + updateSelect2(); + }); + if (o.cellText) { + $shcell.prepend('<label>' + o.cellText + '</label>'); + } - updateSelect2(); - return $input; -}; + }); + + // on reset + c.$table.bind('filterReset', function(){ + $cell.find('.select2').select2('val', o.value || ''); + setTimeout(function(){ + updateSelect2(); + }, 0); + }); + + updateSelect2(); + return $input; + }; })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js index 0bdd37b..bb39f92 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js @@ -1,6 +1,6 @@ /*! Widget: filter, insideRange filter type - updated 2/23/2015 (v2.21.0) */ ;(function($){ -'use strict'; + 'use strict'; // Add insideRange filter type // ============================ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 3436e4c..d83d433 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,94 +1,94 @@ -/*! Widget: filter - updated 5/17/2015 (v2.22.1) *//* +/*! Widget: filter - updated 7/28/2015 (v2.22.4) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ ;( function ( $ ) { -'use strict'; -var ts = $.tablesorter || {}, + 'use strict'; + var ts = $.tablesorter || {}, tscss = ts.css; -$.extend( tscss, { - filterRow : 'tablesorter-filter-row', - filter : 'tablesorter-filter', - filterDisabled : 'disabled', - filterRowHide : 'hideme' -}); + $.extend( tscss, { + filterRow : 'tablesorter-filter-row', + filter : 'tablesorter-filter', + filterDisabled : 'disabled', + filterRowHide : 'hideme' + }); -ts.addWidget({ - id: 'filter', - priority: 50, - options : { - filter_childRows : false, // if true, filter includes child row content in the search - filter_childByColumn : false, // ( filter_childRows must be true ) if true = search child rows by column; false = search all child row text grouped - filter_columnFilters : true, // if true, a filter will be added to the top of each table column - filter_columnAnyMatch: true, // if true, allows using '#:{query}' in AnyMatch searches ( column:query ) - filter_cellFilter : '', // css class name added to the filter cell ( string or array ) - filter_cssFilter : '', // css class name added to the filter row & each input in the row ( tablesorter-filter is ALWAYS added ) - filter_defaultFilter : {}, // add a default column filter type '~{query}' to make fuzzy searches default; '{q1} AND {q2}' to make all searches use a logical AND. - filter_excludeFilter : {}, // filters to exclude, per column - filter_external : '', // jQuery selector string ( or jQuery object ) of external filters - filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin - filter_formatter : null, // add custom filter elements to the filter row - filter_functions : null, // add custom filter functions using this option - filter_hideEmpty : true, // hide filter row when table is empty - filter_hideFilters : false, // collapse filter row when mouse leaves the area - filter_ignoreCase : true, // if true, make all searches case-insensitive - filter_liveSearch : true, // if true, search column content while the user types ( with a delay ) - filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available ( visible ) options within the drop down - filter_placeholder : { search : '', select : '' }, // default placeholder text ( overridden by any header 'data-placeholder' setting ) - filter_reset : null, // jQuery selector string of an element used to reset the filters - filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters - filter_searchDelay : 300, // typing delay in milliseconds before starting a search - filter_searchFiltered: true, // allow searching through already filtered rows in special circumstances; will speed up searching in large tables if true - filter_selectSource : null, // include a function to return an array of values to be added to the column filter select - filter_startsWith : false, // if true, filter start from the beginning of the cell contents - filter_useParsedData : false, // filter all data using parsed content - filter_serversideFiltering : false, // if true, server-side filtering should be performed because client-side filtering will be disabled, but the ui and events will still be used. - filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value - filter_selectSourceSeparator : '|' // filter_selectSource array text left of the separator is added to the option value, right into the option text - }, - format: function( table, c, wo ) { - if ( !c.$table.hasClass( 'hasFilters' ) ) { - ts.filter.init( table, c, wo ); - } - }, - remove: function( table, c, wo, refreshing ) { - var tbodyIndex, $tbody, - $table = c.$table, - $tbodies = c.$tbodies, - events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' - .split( ' ' ).join( c.namespace + 'filter ' ); - $table - .removeClass( 'hasFilters' ) - // add .tsfilter namespace to all BUT search - .unbind( events.replace( /\s+/g, ' ' ) ) - // remove the filter row even if refreshing, because the column might have been moved - .find( '.' + tscss.filterRow ).remove(); - if ( refreshing ) { return; } - for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { - $tbody = ts.processTbody( table, $tbodies.eq( tbodyIndex ), true ); // remove tbody - $tbody.children().removeClass( wo.filter_filteredRow ).show(); - ts.processTbody( table, $tbody, false ); // restore tbody - } - if ( wo.filter_reset ) { - $( document ).undelegate( wo.filter_reset, 'click.tsfilter' ); + ts.addWidget({ + id: 'filter', + priority: 50, + options : { + filter_childRows : false, // if true, filter includes child row content in the search + filter_childByColumn : false, // ( filter_childRows must be true ) if true = search child rows by column; false = search all child row text grouped + filter_columnFilters : true, // if true, a filter will be added to the top of each table column + filter_columnAnyMatch: true, // if true, allows using '#:{query}' in AnyMatch searches ( column:query ) + filter_cellFilter : '', // css class name added to the filter cell ( string or array ) + filter_cssFilter : '', // css class name added to the filter row & each input in the row ( tablesorter-filter is ALWAYS added ) + filter_defaultFilter : {}, // add a default column filter type '~{query}' to make fuzzy searches default; '{q1} AND {q2}' to make all searches use a logical AND. + filter_excludeFilter : {}, // filters to exclude, per column + filter_external : '', // jQuery selector string ( or jQuery object ) of external filters + filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin + filter_formatter : null, // add custom filter elements to the filter row + filter_functions : null, // add custom filter functions using this option + filter_hideEmpty : true, // hide filter row when table is empty + filter_hideFilters : false, // collapse filter row when mouse leaves the area + filter_ignoreCase : true, // if true, make all searches case-insensitive + filter_liveSearch : true, // if true, search column content while the user types ( with a delay ) + filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available ( visible ) options within the drop down + filter_placeholder : { search : '', select : '' }, // default placeholder text ( overridden by any header 'data-placeholder' setting ) + filter_reset : null, // jQuery selector string of an element used to reset the filters + filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters + filter_searchDelay : 300, // typing delay in milliseconds before starting a search + filter_searchFiltered: true, // allow searching through already filtered rows in special circumstances; will speed up searching in large tables if true + filter_selectSource : null, // include a function to return an array of values to be added to the column filter select + filter_startsWith : false, // if true, filter start from the beginning of the cell contents + filter_useParsedData : false, // filter all data using parsed content + filter_serversideFiltering : false, // if true, must perform server-side filtering b/c client-side filtering is disabled, but the ui and events will still be used. + filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value + filter_selectSourceSeparator : '|' // filter_selectSource array text left of the separator is added to the option value, right into the option text + }, + format: function( table, c, wo ) { + if ( !c.$table.hasClass( 'hasFilters' ) ) { + ts.filter.init( table, c, wo ); + } + }, + remove: function( table, c, wo, refreshing ) { + var tbodyIndex, $tbody, + $table = c.$table, + $tbodies = c.$tbodies, + events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' + .split( ' ' ).join( c.namespace + 'filter ' ); + $table + .removeClass( 'hasFilters' ) + // add .tsfilter namespace to all BUT search + .unbind( events.replace( /\s+/g, ' ' ) ) + // remove the filter row even if refreshing, because the column might have been moved + .find( '.' + tscss.filterRow ).remove(); + if ( refreshing ) { return; } + for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody( table, $tbodies.eq( tbodyIndex ), true ); // remove tbody + $tbody.children().removeClass( wo.filter_filteredRow ).show(); + ts.processTbody( table, $tbody, false ); // restore tbody + } + if ( wo.filter_reset ) { + $( document ).undelegate( wo.filter_reset, 'click.tsfilter' ); + } } - } -}); + }); -ts.filter = { + ts.filter = { - // regex used in filter 'check' functions - not for general use and not documented - regex: { - regex : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // regex to test for regex - child : /tablesorter-childRow/, // child row class name; this gets updated in the script - filtered : /filtered/, // filtered (hidden) row class name; updated in the script - type : /undefined|number/, // check type - exact : /(^[\"\'=]+)|([\"\'=]+$)/g, // exact match (allow '==') - nondigit : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser) - operators : /[<>=]/g, // replace operators - query : '(q|query)' // replace filter queries - }, + // regex used in filter 'check' functions - not for general use and not documented + regex: { + regex : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // regex to test for regex + child : /tablesorter-childRow/, // child row class name; this gets updated in the script + filtered : /filtered/, // filtered (hidden) row class name; updated in the script + type : /undefined|number/, // check type + exact : /(^[\"\'=]+)|([\"\'=]+$)/g, // exact match (allow '==') + nondigit : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser) + operators : /[<>=]/g, // replace operators + query : '(q|query)' // replace filter queries + }, // function( c, data ) { } // c = table.config // data.$row = jQuery object of the row currently being processed @@ -102,1507 +102,1522 @@ ts.filter = { // data.cacheArray = An array of parsed content from each table cell in the row being processed // data.index = column index; table = table element ( DOM ) // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class ) - types: { - or : function( c, data, vars ) { - if ( /\|/.test( data.iFilter ) || ts.filter.regex.orSplit.test( data.filter ) ) { - var indx, filterMatched, txt, query, regex, - // duplicate data but split filter - data2 = $.extend( {}, data ), - index = data.index, - parsed = data.parsed[ index ], - filter = data.filter.split( ts.filter.regex.orSplit ), - iFilter = data.iFilter.split( ts.filter.regex.orSplit ), - len = filter.length; - for ( indx = 0; indx < len; indx++ ) { - data2.nestedFilters = true; - data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); - data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); - query = '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')'; - regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); - // filterMatched = data2.filter === '' && indx > 0 ? true - // look for an exact match with the 'or' unless the 'filter-match' class is found - filterMatched = regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ); - if ( filterMatched ) { - return filterMatched; + types: { + or : function( c, data, vars ) { + if ( /\|/.test( data.iFilter ) || ts.filter.regex.orSplit.test( data.filter ) ) { + var indx, filterMatched, txt, query, regex, + // duplicate data but split filter + data2 = $.extend( {}, data ), + index = data.index, + parsed = data.parsed[ index ], + filter = data.filter.split( ts.filter.regex.orSplit ), + iFilter = data.iFilter.split( ts.filter.regex.orSplit ), + len = filter.length; + for ( indx = 0; indx < len; indx++ ) { + data2.nestedFilters = true; + data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); + data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); + query = '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')'; + try { + // use try/catch, because query may not be a valid regex if "|" is contained within a partial regex search, + // e.g "/(Alex|Aar" -> Uncaught SyntaxError: Invalid regular expression: /(/(Alex)/: Unterminated group + regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); + // filterMatched = data2.filter === '' && indx > 0 ? true + // look for an exact match with the 'or' unless the 'filter-match' class is found + filterMatched = regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ); + if ( filterMatched ) { + return filterMatched; + } + } catch ( error ) { + return null; + } } + // may be null from processing types + return filterMatched || false; } - // may be null from processing types - return filterMatched || false; - } - return null; - }, - // Look for an AND or && operator ( logical and ) - and : function( c, data, vars ) { - if ( ts.filter.regex.andTest.test( data.filter ) ) { - var indx, filterMatched, result, txt, query, regex, - // duplicate data but split filter - data2 = $.extend( {}, data ), - index = data.index, - parsed = data.parsed[ index ], - filter = data.filter.split( ts.filter.regex.andSplit ), - iFilter = data.iFilter.split( ts.filter.regex.andSplit ), - len = filter.length; - for ( indx = 0; indx < len; indx++ ) { - data2.nestedFilters = true; - data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); - data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); - query = ( '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' ) - // replace wild cards since /(a*)/i will match anything - .replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ); - regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); - // look for an exact match with the 'and' unless the 'filter-match' class is found - result = ( regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ) ); - if ( indx === 0 ) { - filterMatched = result; - } else { - filterMatched = filterMatched && result; + return null; + }, + // Look for an AND or && operator ( logical and ) + and : function( c, data, vars ) { + if ( ts.filter.regex.andTest.test( data.filter ) ) { + var indx, filterMatched, result, txt, query, regex, + // duplicate data but split filter + data2 = $.extend( {}, data ), + index = data.index, + parsed = data.parsed[ index ], + filter = data.filter.split( ts.filter.regex.andSplit ), + iFilter = data.iFilter.split( ts.filter.regex.andSplit ), + len = filter.length; + for ( indx = 0; indx < len; indx++ ) { + data2.nestedFilters = true; + data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); + data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); + query = ( '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' ) + // replace wild cards since /(a*)/i will match anything + .replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ); + try { + // use try/catch just in case RegExp is invalid + regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); + // look for an exact match with the 'and' unless the 'filter-match' class is found + result = ( regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ) ); + if ( indx === 0 ) { + filterMatched = result; + } else { + filterMatched = filterMatched && result; + } + } catch ( error ) { + return null; + } } + // may be null from processing types + return filterMatched || false; } - // may be null from processing types - return filterMatched || false; - } - return null; - }, - // Look for regex - regex: function( c, data ) { - if ( ts.filter.regex.regex.test( data.filter ) ) { - var matches, - // cache regex per column for optimal speed - regex = data.filter_regexCache[ data.index ] || ts.filter.regex.regex.exec( data.filter ), - isRegex = regex instanceof RegExp; - try { - if ( !isRegex ) { - // force case insensitive search if ignoreCase option set? - // if ( c.ignoreCase && !regex[2] ) { regex[2] = 'i'; } - data.filter_regexCache[ data.index ] = regex = new RegExp( regex[1], regex[2] ); + return null; + }, + // Look for regex + regex: function( c, data ) { + if ( ts.filter.regex.regex.test( data.filter ) ) { + var matches, + // cache regex per column for optimal speed + regex = data.filter_regexCache[ data.index ] || ts.filter.regex.regex.exec( data.filter ), + isRegex = regex instanceof RegExp; + try { + if ( !isRegex ) { + // force case insensitive search if ignoreCase option set? + // if ( c.ignoreCase && !regex[2] ) { regex[2] = 'i'; } + data.filter_regexCache[ data.index ] = regex = new RegExp( regex[1], regex[2] ); + } + matches = regex.test( data.exact ); + } catch ( error ) { + matches = false; } - matches = regex.test( data.exact ); - } catch ( error ) { - matches = false; - } - return matches; - } - return null; - }, - // Look for operators >, >=, < or <= - operators: function( c, data ) { - // ignore empty strings... because '' < 10 is true - if ( /^[<>]=?/.test( data.iFilter ) && data.iExact !== '' ) { - var cachedValue, result, txt, - table = c.table, - index = data.index, - parsed = data.parsed[index], - query = ts.formatFloat( data.iFilter.replace( ts.filter.regex.operators, '' ), table ), - parser = c.parsers[index], - savedSearch = query; - // parse filter value in case we're comparing numbers ( dates ) - if ( parsed || parser.type === 'numeric' ) { - txt = $.trim( '' + data.iFilter.replace( ts.filter.regex.operators, '' ) ); - result = ts.filter.parseFilter( c, txt, index, true ); - query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query; + return matches; } - // iExact may be numeric - see issue #149; - // check if cached is defined, because sometimes j goes out of range? ( numeric columns ) - if ( ( parsed || parser.type === 'numeric' ) && !isNaN( query ) && - typeof data.cache !== 'undefined' ) { - cachedValue = data.cache; - } else { - txt = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; - cachedValue = ts.formatFloat( txt, table ); - } - if ( />/.test( data.iFilter ) ) { - result = />=/.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; - } else if ( /</.test( data.iFilter ) ) { - result = /<=/.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; + return null; + }, + // Look for operators >, >=, < or <= + operators: function( c, data ) { + // ignore empty strings... because '' < 10 is true + if ( /^[<>]=?/.test( data.iFilter ) && data.iExact !== '' ) { + var cachedValue, result, txt, + table = c.table, + index = data.index, + parsed = data.parsed[index], + query = ts.formatFloat( data.iFilter.replace( ts.filter.regex.operators, '' ), table ), + parser = c.parsers[index], + savedSearch = query; + // parse filter value in case we're comparing numbers ( dates ) + if ( parsed || parser.type === 'numeric' ) { + txt = $.trim( '' + data.iFilter.replace( ts.filter.regex.operators, '' ) ); + result = ts.filter.parseFilter( c, txt, index, true ); + query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query; + } + // iExact may be numeric - see issue #149; + // check if cached is defined, because sometimes j goes out of range? ( numeric columns ) + if ( ( parsed || parser.type === 'numeric' ) && !isNaN( query ) && + typeof data.cache !== 'undefined' ) { + cachedValue = data.cache; + } else { + txt = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; + cachedValue = ts.formatFloat( txt, table ); + } + if ( />/.test( data.iFilter ) ) { + result = />=/.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; + } else if ( /</.test( data.iFilter ) ) { + result = /<=/.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; + } + // keep showing all rows if nothing follows the operator + if ( !result && savedSearch === '' ) { + result = true; + } + return result; } - // keep showing all rows if nothing follows the operator - if ( !result && savedSearch === '' ) { - result = true; + return null; + }, + // Look for a not match + notMatch: function( c, data ) { + if ( /^\!/.test( data.iFilter ) ) { + var indx, + txt = data.iFilter.replace( '!', '' ), + filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + if ( ts.filter.regex.exact.test( filter ) ) { + // look for exact not matches - see #628 + filter = filter.replace( ts.filter.regex.exact, '' ); + return filter === '' ? true : $.trim( filter ) !== data.iExact; + } else { + indx = data.iExact.search( $.trim( filter ) ); + return filter === '' ? true : !( c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0 ); + } } - return result; - } - return null; - }, - // Look for a not match - notMatch: function( c, data ) { - if ( /^\!/.test( data.iFilter ) ) { - var indx, - txt = data.iFilter.replace( '!', '' ), - filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; - if ( ts.filter.regex.exact.test( filter ) ) { - // look for exact not matches - see #628 - filter = filter.replace( ts.filter.regex.exact, '' ); - return filter === '' ? true : $.trim( filter ) !== data.iExact; - } else { - indx = data.iExact.search( $.trim( filter ) ); - return filter === '' ? true : !( c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0 ); + return null; + }, + // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric + exact: function( c, data ) { + /*jshint eqeqeq:false */ + if ( ts.filter.regex.exact.test( data.iFilter ) ) { + var txt = data.iFilter.replace( ts.filter.regex.exact, '' ), + filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact; } - } - return null; - }, - // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric - exact: function( c, data ) { - /*jshint eqeqeq:false */ - if ( ts.filter.regex.exact.test( data.iFilter ) ) { - var txt = data.iFilter.replace( ts.filter.regex.exact, '' ), - filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; - return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact; - } - return null; - }, - // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu! - range : function( c, data ) { - if ( ts.filter.regex.toTest.test( data.iFilter ) ) { - var result, tmp, range1, range2, - table = c.table, - index = data.index, - parsed = data.parsed[index], - // make sure the dash is for a range and not indicating a negative number - query = data.iFilter.split( ts.filter.regex.toSplit ); + return null; + }, + // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu! + range : function( c, data ) { + if ( ts.filter.regex.toTest.test( data.iFilter ) ) { + var result, tmp, range1, range2, + table = c.table, + index = data.index, + parsed = data.parsed[index], + // make sure the dash is for a range and not indicating a negative number + query = data.iFilter.split( ts.filter.regex.toSplit ); - tmp = query[0].replace( ts.filter.regex.nondigit, '' ) || ''; - range1 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); - tmp = query[1].replace( ts.filter.regex.nondigit, '' ) || ''; - range2 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); - // parse filter value in case we're comparing numbers ( dates ) - if ( parsed || c.parsers[index].type === 'numeric' ) { - result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index ); - range1 = ( result !== '' && !isNaN( result ) ) ? result : range1; - result = c.parsers[ index ].format( '' + query[1], table, c.$headers.eq( index ), index ); - range2 = ( result !== '' && !isNaN( result ) ) ? result : range2; - } - if ( ( parsed || c.parsers[ index ].type === 'numeric' ) && !isNaN( range1 ) && !isNaN( range2 ) ) { - result = data.cache; - } else { - tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; - result = ts.formatFloat( tmp, table ); - } - if ( range1 > range2 ) { - tmp = range1; range1 = range2; range2 = tmp; // swap - } - return ( result >= range1 && result <= range2 ) || ( range1 === '' || range2 === '' ); - } - return null; - }, - // Look for wild card: ? = single, * = multiple, or | = logical OR - wild : function( c, data ) { - if ( /[\?\*\|]/.test( data.iFilter ) ) { - var index = data.index, - parsed = data.parsed[ index ], - query = '' + ( ts.filter.parseFilter( c, data.iFilter, index, parsed ) || '' ); - // look for an exact match with the 'or' unless the 'filter-match' class is found - if ( !/\?\*/.test( query ) && data.nestedFilters ) { - query = data.isMatch ? query : '^(' + query + ')$'; + tmp = query[0].replace( ts.filter.regex.nondigit, '' ) || ''; + range1 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); + tmp = query[1].replace( ts.filter.regex.nondigit, '' ) || ''; + range2 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); + // parse filter value in case we're comparing numbers ( dates ) + if ( parsed || c.parsers[index].type === 'numeric' ) { + result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index ); + range1 = ( result !== '' && !isNaN( result ) ) ? result : range1; + result = c.parsers[ index ].format( '' + query[1], table, c.$headers.eq( index ), index ); + range2 = ( result !== '' && !isNaN( result ) ) ? result : range2; + } + if ( ( parsed || c.parsers[ index ].type === 'numeric' ) && !isNaN( range1 ) && !isNaN( range2 ) ) { + result = data.cache; + } else { + tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; + result = ts.formatFloat( tmp, table ); + } + if ( range1 > range2 ) { + tmp = range1; range1 = range2; range2 = tmp; // swap + } + return ( result >= range1 && result <= range2 ) || ( range1 === '' || range2 === '' ); } - // parsing the filter may not work properly when using wildcards =/ - return new RegExp( - query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ), - c.widgetOptions.filter_ignoreCase ? 'i' : '' - ) - .test( data.exact ); - } - return null; - }, - // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license ) - fuzzy: function( c, data ) { - if ( /^~/.test( data.iFilter ) ) { - var indx, - patternIndx = 0, - len = data.iExact.length, - txt = data.iFilter.slice( 1 ), - pattern = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; - for ( indx = 0; indx < len; indx++ ) { - if ( data.iExact[ indx ] === pattern[ patternIndx ] ) { - patternIndx += 1; + return null; + }, + // Look for wild card: ? = single, * = multiple, or | = logical OR + wild : function( c, data ) { + if ( /[\?\*\|]/.test( data.iFilter ) ) { + var index = data.index, + parsed = data.parsed[ index ], + query = '' + ( ts.filter.parseFilter( c, data.iFilter, index, parsed ) || '' ); + // look for an exact match with the 'or' unless the 'filter-match' class is found + if ( !/\?\*/.test( query ) && data.nestedFilters ) { + query = data.isMatch ? query : '^(' + query + ')$'; + } + // parsing the filter may not work properly when using wildcards =/ + try { + return new RegExp( + query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ), + c.widgetOptions.filter_ignoreCase ? 'i' : '' + ) + .test( data.exact ); + } catch ( error ) { + return null; } } - if ( patternIndx === pattern.length ) { - return true; + return null; + }, + // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license ) + fuzzy: function( c, data ) { + if ( /^~/.test( data.iFilter ) ) { + var indx, + patternIndx = 0, + len = data.iExact.length, + txt = data.iFilter.slice( 1 ), + pattern = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + for ( indx = 0; indx < len; indx++ ) { + if ( data.iExact[ indx ] === pattern[ patternIndx ] ) { + patternIndx += 1; + } + } + if ( patternIndx === pattern.length ) { + return true; + } + return false; } - return false; + return null; } - return null; - } - }, - init: function( table, c, wo ) { - // filter language options - ts.language = $.extend( true, {}, { - to : 'to', - or : 'or', - and : 'and' - }, ts.language ); - - var options, string, txt, $header, column, filters, val, fxn, noSelect, - regex = ts.filter.regex; - c.$table.addClass( 'hasFilters' ); + }, + init: function( table, c, wo ) { + // filter language options + ts.language = $.extend( true, {}, { + to : 'to', + or : 'or', + and : 'and' + }, ts.language ); - // define timers so using clearTimeout won't cause an undefined error - wo.searchTimer = null; - wo.filter_initTimer = null; - wo.filter_formatterCount = 0; - wo.filter_formatterInit = []; - wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; - wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; + var options, string, txt, $header, column, filters, val, fxn, noSelect, + regex = ts.filter.regex; + c.$table.addClass( 'hasFilters' ); - val = '\\{' + ts.filter.regex.query + '\\}'; - $.extend( regex, { - child : new RegExp( c.cssChildRow ), - filtered : new RegExp( wo.filter_filteredRow ), - alreadyFiltered : new RegExp( '(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i' ), - toTest : new RegExp( '\\s+(-|' + ts.language.to + ')\\s+', 'i' ), - toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)' ,'gi' ), - andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), - andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), - orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ), - iQuery : new RegExp( val, 'i' ), - igQuery : new RegExp( val, 'ig' ) - }); + // define timers so using clearTimeout won't cause an undefined error + wo.searchTimer = null; + wo.filter_initTimer = null; + wo.filter_formatterCount = 0; + wo.filter_formatterInit = []; + wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; + wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; - // don't build filter row if columnFilters is false or all columns are set to 'filter-false' - // see issue #156 - val = c.$headers.filter( '.filter-false, .parser-false' ).length; - if ( wo.filter_columnFilters !== false && val !== c.$headers.length ) { - // build filter row - ts.filter.buildRow( table, c, wo ); - } + val = '\\{' + ts.filter.regex.query + '\\}'; + $.extend( regex, { + child : new RegExp( c.cssChildRow ), + filtered : new RegExp( wo.filter_filteredRow ), + alreadyFiltered : new RegExp( '(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i' ), + toTest : new RegExp( '\\s+(-|' + ts.language.to + ')\\s+', 'i' ), + toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)', 'gi' ), + andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), + andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), + orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ), + iQuery : new RegExp( val, 'i' ), + igQuery : new RegExp( val, 'ig' ) + }); - txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' - .split( ' ' ).join( c.namespace + 'filter ' ); - c.$table.bind( txt, function( event, filter ) { - val = wo.filter_hideEmpty && - $.isEmptyObject( c.cache ) && - !( c.delayInit && event.type === 'appendCache' ); - // hide filter row using the 'filtered' class name - c.$table.find( '.' + tscss.filterRow ).toggleClass( wo.filter_filteredRow, val ); // fixes #450 - if ( !/(search|filter)/.test( event.type ) ) { - event.stopPropagation(); - ts.filter.buildDefault( table, true ); + // don't build filter row if columnFilters is false or all columns are set to 'filter-false' + // see issue #156 + val = c.$headers.filter( '.filter-false, .parser-false' ).length; + if ( wo.filter_columnFilters !== false && val !== c.$headers.length ) { + // build filter row + ts.filter.buildRow( table, c, wo ); } - if ( event.type === 'filterReset' ) { - c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' ); - ts.filter.searching( table, [] ); - } else if ( event.type === 'filterEnd' ) { - ts.filter.buildDefault( table, true ); - } else { - // send false argument to force a new search; otherwise if the filter hasn't changed, - // it will return - filter = event.type === 'search' ? filter : - event.type === 'updateComplete' ? c.$table.data( 'lastSearch' ) : ''; - if ( /(update|add)/.test( event.type ) && event.type !== 'updateComplete' ) { - // force a new search since content has changed - c.lastCombinedFilter = null; - c.lastSearch = []; + + txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' + .split( ' ' ).join( c.namespace + 'filter ' ); + c.$table.bind( txt, function( event, filter ) { + val = wo.filter_hideEmpty && + $.isEmptyObject( c.cache ) && + !( c.delayInit && event.type === 'appendCache' ); + // hide filter row using the 'filtered' class name + c.$table.find( '.' + tscss.filterRow ).toggleClass( wo.filter_filteredRow, val ); // fixes #450 + if ( !/(search|filter)/.test( event.type ) ) { + event.stopPropagation(); + ts.filter.buildDefault( table, true ); } - // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first - // input ensures all inputs are updated when a search is triggered on the table - // $( 'table' ).trigger( 'search', [...] ); - ts.filter.searching( table, filter, true ); - } - return false; - }); + if ( event.type === 'filterReset' ) { + c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' ); + ts.filter.searching( table, [] ); + } else if ( event.type === 'filterEnd' ) { + ts.filter.buildDefault( table, true ); + } else { + // send false argument to force a new search; otherwise if the filter hasn't changed, + // it will return + filter = event.type === 'search' ? filter : + event.type === 'updateComplete' ? c.$table.data( 'lastSearch' ) : ''; + if ( /(update|add)/.test( event.type ) && event.type !== 'updateComplete' ) { + // force a new search since content has changed + c.lastCombinedFilter = null; + c.lastSearch = []; + } + // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first + // input ensures all inputs are updated when a search is triggered on the table + // $( 'table' ).trigger( 'search', [...] ); + ts.filter.searching( table, filter, true ); + } + return false; + }); - // reset button/link - if ( wo.filter_reset ) { - if ( wo.filter_reset instanceof $ ) { - // reset contains a jQuery object, bind to it - wo.filter_reset.click( function() { - c.$table.trigger( 'filterReset' ); - }); - } else if ( $( wo.filter_reset ).length ) { - // reset is a jQuery selector, use event delegation - $( document ) - .undelegate( wo.filter_reset, 'click.tsfilter' ) - .delegate( wo.filter_reset, 'click.tsfilter', function() { - // trigger a reset event, so other functions ( filter_formatter ) know when to reset + // reset button/link + if ( wo.filter_reset ) { + if ( wo.filter_reset instanceof $ ) { + // reset contains a jQuery object, bind to it + wo.filter_reset.click( function() { c.$table.trigger( 'filterReset' ); }); + } else if ( $( wo.filter_reset ).length ) { + // reset is a jQuery selector, use event delegation + $( document ) + .undelegate( wo.filter_reset, 'click.tsfilter' ) + .delegate( wo.filter_reset, 'click.tsfilter', function() { + // trigger a reset event, so other functions ( filter_formatter ) know when to reset + c.$table.trigger( 'filterReset' ); + }); + } } - } - if ( wo.filter_functions ) { - for ( column = 0; column < c.columns; column++ ) { - fxn = ts.getColumnData( table, wo.filter_functions, column ); - if ( fxn ) { - // remove 'filter-select' from header otherwise the options added here are replaced with - // all options - $header = c.$headerIndexed[ column ].removeClass( 'filter-select' ); - // don't build select if 'filter-false' or 'parser-false' set - noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); - options = ''; - if ( fxn === true && noSelect ) { - ts.filter.buildSelect( table, column ); - } else if ( typeof fxn === 'object' && noSelect ) { - // add custom drop down list - for ( string in fxn ) { - if ( typeof string === 'string' ) { - options += options === '' ? - '<option value="">' + - ( $header.data( 'placeholder' ) || - $header.attr( 'data-placeholder' ) || - wo.filter_placeholder.select || - '' - ) + - '</option>' : ''; - val = string; - txt = string; - if ( string.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { - val = string.split( wo.filter_selectSourceSeparator ); - txt = val[1]; - val = val[0]; + if ( wo.filter_functions ) { + for ( column = 0; column < c.columns; column++ ) { + fxn = ts.getColumnData( table, wo.filter_functions, column ); + if ( fxn ) { + // remove 'filter-select' from header otherwise the options added here are replaced with + // all options + $header = c.$headerIndexed[ column ].removeClass( 'filter-select' ); + // don't build select if 'filter-false' or 'parser-false' set + noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); + options = ''; + if ( fxn === true && noSelect ) { + ts.filter.buildSelect( table, column ); + } else if ( typeof fxn === 'object' && noSelect ) { + // add custom drop down list + for ( string in fxn ) { + if ( typeof string === 'string' ) { + options += options === '' ? + '<option value="">' + + ( $header.data( 'placeholder' ) || + $header.attr( 'data-placeholder' ) || + wo.filter_placeholder.select || + '' + ) + + '</option>' : ''; + val = string; + txt = string; + if ( string.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { + val = string.split( wo.filter_selectSourceSeparator ); + txt = val[1]; + val = val[0]; + } + options += '<option ' + + ( txt === val ? '' : 'data-function-name="' + string + '" ' ) + + 'value="' + val + '">' + txt + '</option>'; } - options += '<option ' + - ( txt === val ? '' : 'data-function-name="' + string + '" ' ) + - 'value="' + val + '">' + txt + '</option>'; } - } - c.$table - .find( 'thead' ) - .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) - .append( options ); - txt = wo.filter_selectSource; - fxn = $.isFunction( txt ) ? true : ts.getColumnData( table, txt, column ); - if ( fxn ) { - // updating so the extra options are appended - ts.filter.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) ); + c.$table + .find( 'thead' ) + .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) + .append( options ); + txt = wo.filter_selectSource; + fxn = $.isFunction( txt ) ? true : ts.getColumnData( table, txt, column ); + if ( fxn ) { + // updating so the extra options are appended + ts.filter.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) ); + } } } } } - } - // not really updating, but if the column has both the 'filter-select' class & - // filter_functions set to true, it would append the same options twice. - ts.filter.buildDefault( table, true ); + // not really updating, but if the column has both the 'filter-select' class & + // filter_functions set to true, it would append the same options twice. + ts.filter.buildDefault( table, true ); - ts.filter.bindSearch( table, c.$table.find( '.' + tscss.filter ), true ); - if ( wo.filter_external ) { - ts.filter.bindSearch( table, wo.filter_external ); - } + ts.filter.bindSearch( table, c.$table.find( '.' + tscss.filter ), true ); + if ( wo.filter_external ) { + ts.filter.bindSearch( table, wo.filter_external ); + } - if ( wo.filter_hideFilters ) { - ts.filter.hideFilters( table, c ); - } + if ( wo.filter_hideFilters ) { + ts.filter.hideFilters( table, c ); + } - // show processing icon - if ( c.showProcessing ) { - txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter ' ); - c.$table - .unbind( txt.replace( /\s+/g, ' ' ) ) - .bind( txt, function( event, columns ) { - // only add processing to certain columns to all columns - $header = ( columns ) ? - c.$table - .find( '.' + tscss.header ) - .filter( '[data-column]' ) - .filter( function() { - return columns[ $( this ).data( 'column' ) ] !== ''; - }) : ''; - ts.isProcessing( table, event.type === 'filterStart', columns ? $header : '' ); - }); - } + // show processing icon + if ( c.showProcessing ) { + txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter ' ); + c.$table + .unbind( txt.replace( /\s+/g, ' ' ) ) + .bind( txt, function( event, columns ) { + // only add processing to certain columns to all columns + $header = ( columns ) ? + c.$table + .find( '.' + tscss.header ) + .filter( '[data-column]' ) + .filter( function() { + return columns[ $( this ).data( 'column' ) ] !== ''; + }) : ''; + ts.isProcessing( table, event.type === 'filterStart', columns ? $header : '' ); + }); + } - // set filtered rows count ( intially unfiltered ) - c.filteredRows = c.totalRows; + // set filtered rows count ( intially unfiltered ) + c.filteredRows = c.totalRows; - // add default values - txt = 'tablesorter-initialized pagerBeforeInitialized '.split( ' ' ).join( c.namespace + 'filter ' ); - c.$table - .unbind( txt.replace( /\s+/g, ' ' ) ) - .bind( txt, function() { - // redefine 'wo' as it does not update properly inside this callback - var wo = this.config.widgetOptions; - filters = ts.filter.setDefaults( table, c, wo ) || []; - if ( filters.length ) { - // prevent delayInit from triggering a cache build if filters are empty - if ( !( c.delayInit && filters.join( '' ) === '' ) ) { - ts.setFilters( table, filters, true ); + // add default values + txt = 'tablesorter-initialized pagerBeforeInitialized '.split( ' ' ).join( c.namespace + 'filter ' ); + c.$table + .unbind( txt.replace( /\s+/g, ' ' ) ) + .bind( txt, function() { + // redefine 'wo' as it does not update properly inside this callback + var wo = this.config.widgetOptions; + filters = ts.filter.setDefaults( table, c, wo ) || []; + if ( filters.length ) { + // prevent delayInit from triggering a cache build if filters are empty + if ( !( c.delayInit && filters.join( '' ) === '' ) ) { + ts.setFilters( table, filters, true ); + } } - } - c.$table.trigger( 'filterFomatterUpdate' ); - // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers - setTimeout( function() { - if ( !wo.filter_initialized ) { + c.$table.trigger( 'filterFomatterUpdate' ); + // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers + setTimeout( function() { + if ( !wo.filter_initialized ) { + ts.filter.filterInitComplete( c ); + } + }, 100 ); + }); + // if filter widget is added after pager has initialized; then set filter init flag + if ( c.pager && c.pager.initialized && !wo.filter_initialized ) { + c.$table.trigger( 'filterFomatterUpdate' ); + setTimeout( function() { ts.filter.filterInitComplete( c ); - } - }, 100 ); - }); - // if filter widget is added after pager has initialized; then set filter init flag - if ( c.pager && c.pager.initialized && !wo.filter_initialized ) { - c.$table.trigger( 'filterFomatterUpdate' ); - setTimeout( function() { - ts.filter.filterInitComplete( c ); - }, 100 ); - } - }, - // $cell parameter, but not the config, is passed to the filter_formatters, - // so we have to work with it instead - formatterUpdated: function( $cell, column ) { - var wo = $cell.closest( 'table' )[0].config.widgetOptions; - if ( !wo.filter_initialized ) { - // add updates by column since this function - // may be called numerous times before initialization - wo.filter_formatterInit[ column ] = 1; - } - }, - filterInitComplete: function( c ) { - var indx, len, - wo = c.widgetOptions, - count = 0, - completed = function() { - wo.filter_initialized = true; - c.$table.trigger( 'filterInit', c ); - ts.filter.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); - }; - if ( $.isEmptyObject( wo.filter_formatter ) ) { - completed(); - } else { - len = wo.filter_formatterInit.length; - for ( indx = 0; indx < len; indx++ ) { - if ( wo.filter_formatterInit[ indx ] === 1 ) { - count++; - } + }, 100 ); + } + }, + // $cell parameter, but not the config, is passed to the filter_formatters, + // so we have to work with it instead + formatterUpdated: function( $cell, column ) { + var wo = $cell.closest( 'table' )[0].config.widgetOptions; + if ( !wo.filter_initialized ) { + // add updates by column since this function + // may be called numerous times before initialization + wo.filter_formatterInit[ column ] = 1; } - clearTimeout( wo.filter_initTimer ); - if ( !wo.filter_initialized && count === wo.filter_formatterCount ) { - // filter widget initialized + }, + filterInitComplete: function( c ) { + var indx, len, + wo = c.widgetOptions, + count = 0, + completed = function() { + wo.filter_initialized = true; + c.$table.trigger( 'filterInit', c ); + ts.filter.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); + }; + if ( $.isEmptyObject( wo.filter_formatter ) ) { completed(); - } else if ( !wo.filter_initialized ) { - // fall back in case a filter_formatter doesn't call - // $.tablesorter.filter.formatterUpdated( $cell, column ), and the count is off - wo.filter_initTimer = setTimeout( function() { + } else { + len = wo.filter_formatterInit.length; + for ( indx = 0; indx < len; indx++ ) { + if ( wo.filter_formatterInit[ indx ] === 1 ) { + count++; + } + } + clearTimeout( wo.filter_initTimer ); + if ( !wo.filter_initialized && count === wo.filter_formatterCount ) { + // filter widget initialized completed(); - }, 500 ); + } else if ( !wo.filter_initialized ) { + // fall back in case a filter_formatter doesn't call + // $.tablesorter.filter.formatterUpdated( $cell, column ), and the count is off + wo.filter_initTimer = setTimeout( function() { + completed(); + }, 500 ); + } } - } - }, - setDefaults: function( table, c, wo ) { - var isArray, saved, indx, col, $filters, - // get current ( default ) filters - filters = ts.getFilters( table ) || []; - if ( wo.filter_saveFilters && ts.storage ) { - saved = ts.storage( table, 'tablesorter-filters' ) || []; - isArray = $.isArray( saved ); - // make sure we're not just getting an empty array - if ( !( isArray && saved.join( '' ) === '' || !isArray ) ) { - filters = saved; + }, + setDefaults: function( table, c, wo ) { + var isArray, saved, indx, col, $filters, + // get current ( default ) filters + filters = ts.getFilters( table ) || []; + if ( wo.filter_saveFilters && ts.storage ) { + saved = ts.storage( table, 'tablesorter-filters' ) || []; + isArray = $.isArray( saved ); + // make sure we're not just getting an empty array + if ( !( isArray && saved.join( '' ) === '' || !isArray ) ) { + filters = saved; + } } - } - // if no filters saved, then check default settings - if ( filters.join( '' ) === '' ) { - // allow adding default setting to external filters - $filters = c.$headers.add( wo.filter_$externalFilters ) - .filter( '[' + wo.filter_defaultAttrib + ']' ); - for ( indx = 0; indx <= c.columns; indx++ ) { - // include data-column='all' external filters - col = indx === c.columns ? 'all' : indx; - filters[indx] = $filters - .filter( '[data-column="' + col + '"]' ) - .attr( wo.filter_defaultAttrib ) || filters[indx] || ''; + // if no filters saved, then check default settings + if ( filters.join( '' ) === '' ) { + // allow adding default setting to external filters + $filters = c.$headers.add( wo.filter_$externalFilters ) + .filter( '[' + wo.filter_defaultAttrib + ']' ); + for ( indx = 0; indx <= c.columns; indx++ ) { + // include data-column='all' external filters + col = indx === c.columns ? 'all' : indx; + filters[indx] = $filters + .filter( '[data-column="' + col + '"]' ) + .attr( wo.filter_defaultAttrib ) || filters[indx] || ''; + } } - } - c.$table.data( 'lastSearch', filters ); - return filters; - }, - parseFilter: function( c, filter, column, parsed ) { - return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter; - }, - buildRow: function( table, c, wo ) { - var col, column, $header, buildSelect, disabled, name, ffxn, tmp, - // c.columns defined in computeThIndexes() - cellFilter = wo.filter_cellFilter, - columns = c.columns, - arry = $.isArray( cellFilter ), - buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; - for ( column = 0; column < columns; column++ ) { - buildFilter += '<td'; - if ( arry ) { - buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' ); - } else { - buildFilter += ( cellFilter !== '' ? ' class="' + cellFilter + '"' : '' ); + c.$table.data( 'lastSearch', filters ); + return filters; + }, + parseFilter: function( c, filter, column, parsed ) { + return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter; + }, + buildRow: function( table, c, wo ) { + var col, column, $header, buildSelect, disabled, name, ffxn, tmp, + // c.columns defined in computeThIndexes() + cellFilter = wo.filter_cellFilter, + columns = c.columns, + arry = $.isArray( cellFilter ), + buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; + for ( column = 0; column < columns; column++ ) { + buildFilter += '<td'; + if ( arry ) { + buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' ); + } else { + buildFilter += ( cellFilter !== '' ? ' class="' + cellFilter + '"' : '' ); + } + buildFilter += '></td>'; } - buildFilter += '></td>'; - } - c.$filters = $( buildFilter += '</tr>' ) - .appendTo( c.$table.children( 'thead' ).eq( 0 ) ) - .find( 'td' ); - // build each filter input - for ( column = 0; column < columns; column++ ) { - disabled = false; - // assuming last cell of a column is the main column - $header = c.$headerIndexed[ column ]; - ffxn = ts.getColumnData( table, wo.filter_functions, column ); - buildSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || - $header.hasClass( 'filter-select' ); - // get data from jQuery data, metadata, headers option or header class name - col = ts.getColumnData( table, c.headers, column ); - disabled = ts.getData( $header[0], col, 'filter' ) === 'false' || - ts.getData( $header[0], col, 'parser' ) === 'false'; + c.$filters = $( buildFilter += '</tr>' ) + .appendTo( c.$table.children( 'thead' ).eq( 0 ) ) + .find( 'td' ); + // build each filter input + for ( column = 0; column < columns; column++ ) { + disabled = false; + // assuming last cell of a column is the main column + $header = c.$headerIndexed[ column ]; + ffxn = ts.getColumnData( table, wo.filter_functions, column ); + buildSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || + $header.hasClass( 'filter-select' ); + // get data from jQuery data, metadata, headers option or header class name + col = ts.getColumnData( table, c.headers, column ); + disabled = ts.getData( $header[0], col, 'filter' ) === 'false' || + ts.getData( $header[0], col, 'parser' ) === 'false'; - if ( buildSelect ) { - buildFilter = $( '<select>' ).appendTo( c.$filters.eq( column ) ); - } else { - ffxn = ts.getColumnData( table, wo.filter_formatter, column ); - if ( ffxn ) { - wo.filter_formatterCount++; - buildFilter = ffxn( c.$filters.eq( column ), column ); - // no element returned, so lets go find it - if ( buildFilter && buildFilter.length === 0 ) { - buildFilter = c.$filters.eq( column ).children( 'input' ); + if ( buildSelect ) { + buildFilter = $( '<select>' ).appendTo( c.$filters.eq( column ) ); + } else { + ffxn = ts.getColumnData( table, wo.filter_formatter, column ); + if ( ffxn ) { + wo.filter_formatterCount++; + buildFilter = ffxn( c.$filters.eq( column ), column ); + // no element returned, so lets go find it + if ( buildFilter && buildFilter.length === 0 ) { + buildFilter = c.$filters.eq( column ).children( 'input' ); + } + // element not in DOM, so lets attach it + if ( buildFilter && ( buildFilter.parent().length === 0 || + ( buildFilter.parent().length && buildFilter.parent()[0] !== c.$filters[column] ) ) ) { + c.$filters.eq( column ).append( buildFilter ); + } + } else { + buildFilter = $( '<input type="search">' ).appendTo( c.$filters.eq( column ) ); } - // element not in DOM, so lets attach it - if ( buildFilter && ( buildFilter.parent().length === 0 || - ( buildFilter.parent().length && buildFilter.parent()[0] !== c.$filters[column] ) ) ) { - c.$filters.eq( column ).append( buildFilter ); + if ( buildFilter ) { + tmp = $header.data( 'placeholder' ) || + $header.attr( 'data-placeholder' ) || + wo.filter_placeholder.search || ''; + buildFilter.attr( 'placeholder', tmp ); } - } else { - buildFilter = $( '<input type="search">' ).appendTo( c.$filters.eq( column ) ); } if ( buildFilter ) { - tmp = $header.data( 'placeholder' ) || - $header.attr( 'data-placeholder' ) || - wo.filter_placeholder.search || ''; - buildFilter.attr( 'placeholder', tmp ); + // add filter class name + name = ( $.isArray( wo.filter_cssFilter ) ? + ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : + wo.filter_cssFilter ) || ''; + buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column ); + if ( disabled ) { + buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; + } } } - if ( buildFilter ) { - // add filter class name - name = ( $.isArray( wo.filter_cssFilter ) ? - ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : - wo.filter_cssFilter ) || ''; - buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column ); - if ( disabled ) { - buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; + }, + bindSearch: function( table, $el, internal ) { + table = $( table )[0]; + $el = $( $el ); // allow passing a selector string + if ( !$el.length ) { return; } + var tmp, + c = table.config, + wo = c.widgetOptions, + namespace = c.namespace + 'filter', + $ext = wo.filter_$externalFilters; + if ( internal !== true ) { + // save anyMatch element + tmp = wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector; + wo.filter_$anyMatch = $el.filter( tmp ); + if ( $ext && $ext.length ) { + wo.filter_$externalFilters = wo.filter_$externalFilters.add( $el ); + } else { + wo.filter_$externalFilters = $el; } + // update values ( external filters added after table initialization ) + ts.setFilters( table, c.$table.data( 'lastSearch' ) || [], internal === false ); } - } - }, - bindSearch: function( table, $el, internal ) { - table = $( table )[0]; - $el = $( $el ); // allow passing a selector string - if ( !$el.length ) { return; } - var tmp, - c = table.config, - wo = c.widgetOptions, - namespace = c.namespace + 'filter', - $ext = wo.filter_$externalFilters; - if ( internal !== true ) { - // save anyMatch element - tmp = wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector; - wo.filter_$anyMatch = $el.filter( tmp ); - if ( $ext && $ext.length ) { - wo.filter_$externalFilters = wo.filter_$externalFilters.add( $el ); + // unbind events + tmp = ( 'keypress keyup search change '.split( ' ' ).join( namespace + ' ' ) ); + $el + // use data attribute instead of jQuery data since the head is cloned without including + // the data/binding + .attr( 'data-lastSearchTime', new Date().getTime() ) + .unbind( tmp.replace( /\s+/g, ' ' ) ) + // include change for select - fixes #473 + .bind( 'keyup' + namespace, function( event ) { + $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); + // emulate what webkit does.... escape clears the filter + if ( event.which === 27 ) { + this.value = ''; + // live search + } else if ( wo.filter_liveSearch === false ) { + return; + // don't return if the search value is empty ( all rows need to be revealed ) + } else if ( this.value !== '' && ( + // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace + ( typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch ) || + // let return & backspace continue on, but ignore arrows & non-valid characters + ( event.which !== 13 && event.which !== 8 && + ( event.which < 32 || ( event.which >= 37 && event.which <= 40 ) ) ) ) ) { + return; + } + // change event = no delay; last true flag tells getFilters to skip newest timed input + ts.filter.searching( table, true, true ); + }) + .bind( 'search change keypress '.split( ' ' ).join( namespace + ' ' ), function( event ) { + var column = $( this ).data( 'column' ); + // don't allow 'change' event to process if the input value is the same - fixes #685 + if ( event.which === 13 || event.type === 'search' || + event.type === 'change' && this.value !== c.lastSearch[column] ) { + event.preventDefault(); + // init search with no delay + $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); + ts.filter.searching( table, false, true ); + } + }); + }, + searching: function( table, filter, skipFirst ) { + var wo = table.config.widgetOptions; + clearTimeout( wo.searchTimer ); + if ( typeof filter === 'undefined' || filter === true ) { + // delay filtering + wo.searchTimer = setTimeout( function() { + ts.filter.checkFilters( table, filter, skipFirst ); + }, wo.filter_liveSearch ? wo.filter_searchDelay : 10 ); } else { - wo.filter_$externalFilters = $el; + // skip delay + ts.filter.checkFilters( table, filter, skipFirst ); } - // update values ( external filters added after table initialization ) - ts.setFilters( table, c.$table.data( 'lastSearch' ) || [], internal === false ); - } - // unbind events - tmp = ( 'keypress keyup search change '.split( ' ' ).join( namespace + ' ' ) ); - $el - // use data attribute instead of jQuery data since the head is cloned without including - // the data/binding - .attr( 'data-lastSearchTime', new Date().getTime() ) - .unbind( tmp.replace( /\s+/g, ' ' ) ) - // include change for select - fixes #473 - .bind( 'keyup' + namespace, function( event ) { - $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - // emulate what webkit does.... escape clears the filter - if ( event.which === 27 ) { - this.value = ''; - // live search - } else if ( wo.filter_liveSearch === false ) { + }, + checkFilters: function( table, filter, skipFirst ) { + var c = table.config, + wo = c.widgetOptions, + filterArray = $.isArray( filter ), + filters = ( filterArray ) ? filter : ts.getFilters( table, true ), + combinedFilters = ( filters || [] ).join( '' ); // combined filter values + // prevent errors if delay init is set + if ( $.isEmptyObject( c.cache ) ) { + // update cache if delayInit set & pager has initialized ( after user initiates a search ) + if ( c.delayInit && c.pager && c.pager.initialized ) { + c.$table.trigger( 'updateCache', [ function() { + ts.filter.checkFilters( table, false, skipFirst ); + } ] ); + } return; - // don't return if the search value is empty ( all rows need to be revealed ) - } else if ( this.value !== '' && ( - // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace - ( typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch ) || - // let return & backspace continue on, but ignore arrows & non-valid characters - ( event.which !== 13 && event.which !== 8 && - ( event.which < 32 || ( event.which >= 37 && event.which <= 40 ) ) ) ) ) { + } + // add filter array back into inputs + if ( filterArray ) { + ts.setFilters( table, filters, false, skipFirst !== true ); + if ( !wo.filter_initialized ) { c.lastCombinedFilter = ''; } + } + if ( wo.filter_hideFilters ) { + // show/hide filter row as needed + c.$table + .find( '.' + tscss.filterRow ) + .trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); + } + // return if the last search is the same; but filter === false when updating the search + // see example-widget-filter.html filter toggle buttons + if ( c.lastCombinedFilter === combinedFilters && filter !== false ) { return; + } else if ( filter === false ) { + // force filter refresh + c.lastCombinedFilter = null; + c.lastSearch = []; } - // change event = no delay; last true flag tells getFilters to skip newest timed input - ts.filter.searching( table, true, true ); - }) - .bind( 'search change keypress '.split( ' ' ).join( namespace + ' ' ), function( event ) { - var column = $( this ).data( 'column' ); - // don't allow 'change' event to process if the input value is the same - fixes #685 - if ( event.which === 13 || event.type === 'search' || - event.type === 'change' && this.value !== c.lastSearch[column] ) { - event.preventDefault(); - // init search with no delay - $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - ts.filter.searching( table, false, true ); + if ( wo.filter_initialized ) { + c.$table.trigger( 'filterStart', [ filters ] ); } - }); - }, - searching: function( table, filter, skipFirst ) { - var wo = table.config.widgetOptions; - clearTimeout( wo.searchTimer ); - if ( typeof filter === 'undefined' || filter === true ) { - // delay filtering - wo.searchTimer = setTimeout( function() { - ts.filter.checkFilters( table, filter, skipFirst ); - }, wo.filter_liveSearch ? wo.filter_searchDelay : 10 ); - } else { - // skip delay - ts.filter.checkFilters( table, filter, skipFirst ); - } - }, - checkFilters: function( table, filter, skipFirst ) { - var c = table.config, - wo = c.widgetOptions, - filterArray = $.isArray( filter ), - filters = ( filterArray ) ? filter : ts.getFilters( table, true ), - combinedFilters = ( filters || [] ).join( '' ); // combined filter values - // prevent errors if delay init is set - if ( $.isEmptyObject( c.cache ) ) { - // update cache if delayInit set & pager has initialized ( after user initiates a search ) - if ( c.delayInit && c.pager && c.pager.initialized ) { - c.$table.trigger( 'updateCache', [ function() { - ts.filter.checkFilters( table, false, skipFirst ); - } ] ); + if ( c.showProcessing ) { + // give it time for the processing icon to kick in + setTimeout( function() { + ts.filter.findRows( table, filters, combinedFilters ); + return false; + }, 30 ); + } else { + ts.filter.findRows( table, filters, combinedFilters ); + return false; } - return; - } - // add filter array back into inputs - if ( filterArray ) { - ts.setFilters( table, filters, false, skipFirst !== true ); - if ( !wo.filter_initialized ) { c.lastCombinedFilter = ''; } - } - if ( wo.filter_hideFilters ) { - // show/hide filter row as needed + }, + hideFilters: function( table, c ) { + var timer; c.$table .find( '.' + tscss.filterRow ) - .trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); - } - // return if the last search is the same; but filter === false when updating the search - // see example-widget-filter.html filter toggle buttons - if ( c.lastCombinedFilter === combinedFilters && filter !== false ) { - return; - } else if ( filter === false ) { - // force filter refresh - c.lastCombinedFilter = null; - c.lastSearch = []; - } - if ( wo.filter_initialized ) { - c.$table.trigger( 'filterStart', [filters] ); - } - if ( c.showProcessing ) { - // give it time for the processing icon to kick in - setTimeout( function() { - ts.filter.findRows( table, filters, combinedFilters ); - return false; - }, 30 ); - } else { - ts.filter.findRows( table, filters, combinedFilters ); - return false; - } - }, - hideFilters: function( table, c ) { - var timer; - c.$table - .find( '.' + tscss.filterRow ) - .bind( 'mouseenter mouseleave', function( e ) { - // save event object - http://bugs.jquery.com/ticket/12140 - var event = e, - $filterRow = $( this ); - clearTimeout( timer ); - timer = setTimeout( function() { - if ( /enter|over/.test( event.type ) ) { - $filterRow.removeClass( tscss.filterRowHide ); - } else { - // don't hide if input has focus - // $( ':focus' ) needs jQuery 1.6+ - if ( $( document.activeElement ).closest( 'tr' )[0] !== $filterRow[0] ) { - // don't hide row if any filter has a value - if ( c.lastCombinedFilter === '' ) { - $filterRow.addClass( tscss.filterRowHide ); + .bind( 'mouseenter mouseleave', function( e ) { + // save event object - http://bugs.jquery.com/ticket/12140 + var event = e, + $filterRow = $( this ); + clearTimeout( timer ); + timer = setTimeout( function() { + if ( /enter|over/.test( event.type ) ) { + $filterRow.removeClass( tscss.filterRowHide ); + } else { + // don't hide if input has focus + // $( ':focus' ) needs jQuery 1.6+ + if ( $( document.activeElement ).closest( 'tr' )[0] !== $filterRow[0] ) { + // don't hide row if any filter has a value + if ( c.lastCombinedFilter === '' ) { + $filterRow.addClass( tscss.filterRowHide ); + } } } - } - }, 200 ); - }) - .find( 'input, select' ).bind( 'focus blur', function( e ) { - var event = e, - $row = $( this ).closest( 'tr' ); - clearTimeout( timer ); - timer = setTimeout( function() { + }, 200 ); + }) + .find( 'input, select' ).bind( 'focus blur', function( e ) { + var event = e, + $row = $( this ).closest( 'tr' ); clearTimeout( timer ); - // don't hide row if any filter has a value - if ( ts.getFilters( c.$table ).join( '' ) === '' ) { - $row.toggleClass( tscss.filterRowHide, event.type !== 'focus' ); - } - }, 200 ); - }); - }, - defaultFilter: function( filter, mask ) { - if ( filter === '' ) { return filter; } - var regex = ts.filter.regex.iQuery, - maskLen = mask.match( ts.filter.regex.igQuery ).length, - query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ], - len = query.length - 1, - indx = 0, - val = mask; - if ( len < 1 && maskLen > 1 ) { - // only one 'word' in query but mask has >1 slots - query[1] = query[0]; - } - // replace all {query} with query words... - // if query = 'Bob', then convert mask from '!{query}' to '!Bob' - // if query = 'Bob Joe Frank', then convert mask '{q} OR {q}' to 'Bob OR Joe OR Frank' - while ( regex.test( val ) ) { - val = val.replace( regex, query[indx++] || '' ); - if ( regex.test( val ) && indx < len && ( query[indx] || '' ) !== '' ) { - val = mask.replace( regex, val ); + timer = setTimeout( function() { + clearTimeout( timer ); + // don't hide row if any filter has a value + if ( ts.getFilters( c.$table ).join( '' ) === '' ) { + $row.toggleClass( tscss.filterRowHide, event.type !== 'focus' ); + } + }, 200 ); + }); + }, + defaultFilter: function( filter, mask ) { + if ( filter === '' ) { return filter; } + var regex = ts.filter.regex.iQuery, + maskLen = mask.match( ts.filter.regex.igQuery ).length, + query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ], + len = query.length - 1, + indx = 0, + val = mask; + if ( len < 1 && maskLen > 1 ) { + // only one 'word' in query but mask has >1 slots + query[1] = query[0]; } - } - return val; - }, - getLatestSearch: function( $input ) { - if ( $input ) { - return $input.sort( function( a, b ) { - return $( b ).attr( 'data-lastSearchTime' ) - $( a ).attr( 'data-lastSearchTime' ); - }); - } - return $input || $(); - }, - multipleColumns: function( c, $input ) { - // look for multiple columns '1-3,4-6,8' in data-column - var temp, ranges, range, start, end, singles, i, indx, len, - wo = c.widgetOptions, - // only target 'all' column inputs on initialization - // & don't target 'all' column inputs if they don't exist - targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, - columns = [], - val = $.trim( ts.filter.getLatestSearch( $input ).attr( 'data-column' ) || '' ); - // process column range - if ( targets && /-/.test( val ) ) { - ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); - len = ranges.length; - for ( indx = 0; indx < len; indx++ ) { - range = ranges[indx].split( /\s*-\s*/ ); - start = parseInt( range[0], 10 ) || 0; - end = parseInt( range[1], 10 ) || ( c.columns - 1 ); - if ( start > end ) { - temp = start; start = end; end = temp; // swap + // replace all {query} with query words... + // if query = 'Bob', then convert mask from '!{query}' to '!Bob' + // if query = 'Bob Joe Frank', then convert mask '{q} OR {q}' to 'Bob OR Joe OR Frank' + while ( regex.test( val ) ) { + val = val.replace( regex, query[indx++] || '' ); + if ( regex.test( val ) && indx < len && ( query[indx] || '' ) !== '' ) { + val = mask.replace( regex, val ); } - if ( end >= c.columns ) { - end = c.columns - 1; - } - for ( ; start <= end; start++ ) { - columns.push( start ); + } + return val; + }, + getLatestSearch: function( $input ) { + if ( $input ) { + return $input.sort( function( a, b ) { + return $( b ).attr( 'data-lastSearchTime' ) - $( a ).attr( 'data-lastSearchTime' ); + }); + } + return $input || $(); + }, + multipleColumns: function( c, $input ) { + // look for multiple columns '1-3,4-6,8' in data-column + var temp, ranges, range, start, end, singles, i, indx, len, + wo = c.widgetOptions, + // only target 'all' column inputs on initialization + // & don't target 'all' column inputs if they don't exist + targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, + columns = [], + val = $.trim( ts.filter.getLatestSearch( $input ).attr( 'data-column' ) || '' ); + // process column range + if ( targets && /-/.test( val ) ) { + ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); + len = ranges.length; + for ( indx = 0; indx < len; indx++ ) { + range = ranges[indx].split( /\s*-\s*/ ); + start = parseInt( range[0], 10 ) || 0; + end = parseInt( range[1], 10 ) || ( c.columns - 1 ); + if ( start > end ) { + temp = start; start = end; end = temp; // swap + } + if ( end >= c.columns ) { + end = c.columns - 1; + } + for ( ; start <= end; start++ ) { + columns.push( start ); + } + // remove processed range from val + val = val.replace( ranges[ indx ], '' ); } - // remove processed range from val - val = val.replace( ranges[ indx ], '' ); } - } - // process single columns - if ( targets && /,/.test( val ) ) { - singles = val.split( /\s*,\s*/ ); - len = singles.length; - for ( i = 0; i < len; i++ ) { - if ( singles[ i ] !== '' ) { - indx = parseInt( singles[ i ], 10 ); - if ( indx < c.columns ) { - columns.push( indx ); + // process single columns + if ( targets && /,/.test( val ) ) { + singles = val.split( /\s*,\s*/ ); + len = singles.length; + for ( i = 0; i < len; i++ ) { + if ( singles[ i ] !== '' ) { + indx = parseInt( singles[ i ], 10 ); + if ( indx < c.columns ) { + columns.push( indx ); + } } } } - } - // return all columns - if ( !columns.length ) { - for ( indx = 0; indx < c.columns; indx++ ) { - columns.push( indx ); + // return all columns + if ( !columns.length ) { + for ( indx = 0; indx < c.columns; indx++ ) { + columns.push( indx ); + } } - } - return columns; - }, - processTypes: function( c, data, vars ) { - var ffxn, - filterMatched = null, - matches = null; - for ( ffxn in ts.filter.types ) { - if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) { - matches = ts.filter.types[ffxn]( c, data, vars ); - if ( matches !== null ) { - filterMatched = matches; + return columns; + }, + processTypes: function( c, data, vars ) { + var ffxn, + filterMatched = null, + matches = null; + for ( ffxn in ts.filter.types ) { + if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) { + matches = ts.filter.types[ffxn]( c, data, vars ); + if ( matches !== null ) { + filterMatched = matches; + } } } - } - return filterMatched; - }, - processRow: function( c, data, vars ) { - var columnIndex, hasSelect, result, val, filterMatched, - fxn, ffxn, txt, - regex = ts.filter.regex, - wo = c.widgetOptions, - showRow = true; - data.$cells = data.$row.children(); + return filterMatched; + }, + processRow: function( c, data, vars ) { + var columnIndex, hasSelect, result, val, filterMatched, + fxn, ffxn, txt, + regex = ts.filter.regex, + wo = c.widgetOptions, + showRow = true; + data.$cells = data.$row.children(); - if ( data.anyMatchFlag ) { - // look for multiple columns '1-3,4-6,8' - columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); - data.anyMatch = true; - data.isMatch = true; - data.rowArray = data.$cells.map( function( i ) { - if ( $.inArray( i, columnIndex ) > -1 ) { - if ( data.parsed[ i ] ) { - txt = data.cacheArray[ i ]; - } else { - txt = data.rawArray[ i ]; - txt = $.trim( wo.filter_ignoreCase ? txt.toLowerCase() : txt ); - if ( c.sortLocaleCompare ) { - txt = ts.replaceAccents( txt ); + if ( data.anyMatchFlag ) { + // look for multiple columns '1-3,4-6,8' + columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); + data.anyMatch = true; + data.isMatch = true; + data.rowArray = data.$cells.map( function( i ) { + if ( $.inArray( i, columnIndex ) > -1 ) { + if ( data.parsed[ i ] ) { + txt = data.cacheArray[ i ]; + } else { + txt = data.rawArray[ i ]; + txt = $.trim( wo.filter_ignoreCase ? txt.toLowerCase() : txt ); + if ( c.sortLocaleCompare ) { + txt = ts.replaceAccents( txt ); + } } + return txt; } - return txt; - } - }).get(); - data.filter = data.anyMatchFilter; - data.iFilter = data.iAnyMatchFilter; - data.exact = data.rowArray.join( ' ' ); - data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; - data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' ); + }).get(); + data.filter = data.anyMatchFilter; + data.iFilter = data.iAnyMatchFilter; + data.exact = data.rowArray.join( ' ' ); + data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; + data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' ); - vars.excludeMatch = vars.noAnyMatch; - filterMatched = ts.filter.processTypes( c, data, vars ); + vars.excludeMatch = vars.noAnyMatch; + filterMatched = ts.filter.processTypes( c, data, vars ); - if ( filterMatched !== null ) { - showRow = filterMatched; - } else { - if ( wo.filter_startsWith ) { - showRow = false; - columnIndex = c.columns; - while ( !showRow && columnIndex > 0 ) { - columnIndex--; - showRow = showRow || data.rowArray[ columnIndex ].indexOf( data.iFilter ) === 0; - } + if ( filterMatched !== null ) { + showRow = filterMatched; } else { - showRow = ( data.iExact + data.childRowText ).indexOf( data.iFilter ) >= 0; + if ( wo.filter_startsWith ) { + showRow = false; + columnIndex = c.columns; + while ( !showRow && columnIndex > 0 ) { + columnIndex--; + showRow = showRow || data.rowArray[ columnIndex ].indexOf( data.iFilter ) === 0; + } + } else { + showRow = ( data.iExact + data.childRowText ).indexOf( data.iFilter ) >= 0; + } + } + data.anyMatch = false; + // no other filters to process + if ( data.filters.join( '' ) === data.filter ) { + return showRow; } } - data.anyMatch = false; - // no other filters to process - if ( data.filters.join( '' ) === data.filter ) { - return showRow; - } - } - for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { - data.filter = data.filters[ columnIndex ]; - data.index = columnIndex; + for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { + data.filter = data.filters[ columnIndex ]; + data.index = columnIndex; - // filter types to exclude, per column - vars.excludeMatch = vars.excludeFilter[ columnIndex ]; + // filter types to exclude, per column + vars.excludeMatch = vars.excludeFilter[ columnIndex ]; - // ignore if filter is empty or disabled - if ( data.filter ) { - data.cache = data.cacheArray[ columnIndex ]; - // check if column data should be from the cell or from parsed data - if ( wo.filter_useParsedData || data.parsed[ columnIndex ] ) { - data.exact = data.cache; - } else { - result = data.rawArray[ columnIndex ] || ''; - data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 - } - data.iExact = !regex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? - data.exact.toLowerCase() : data.exact; + // ignore if filter is empty or disabled + if ( data.filter ) { + data.cache = data.cacheArray[ columnIndex ]; + // check if column data should be from the cell or from parsed data + if ( wo.filter_useParsedData || data.parsed[ columnIndex ] ) { + data.exact = data.cache; + } else { + result = data.rawArray[ columnIndex ] || ''; + data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 + } + data.iExact = !regex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? + data.exact.toLowerCase() : data.exact; - data.isMatch = c.$headerIndexed[ data.index ].hasClass( 'filter-match' ); + data.isMatch = c.$headerIndexed[ data.index ].hasClass( 'filter-match' ); - result = showRow; // if showRow is true, show that row + result = showRow; // if showRow is true, show that row - // in case select filter option has a different value vs text 'a - z|A through Z' - ffxn = wo.filter_columnFilters ? - c.$filters.add( c.$externalFilters ) - .filter( '[data-column="'+ columnIndex + '"]' ) - .find( 'select option:selected' ) - .attr( 'data-function-name' ) || '' : ''; - // replace accents - see #357 - if ( c.sortLocaleCompare ) { - data.filter = ts.replaceAccents( data.filter ); - } + // in case select filter option has a different value vs text 'a - z|A through Z' + ffxn = wo.filter_columnFilters ? + c.$filters.add( c.$externalFilters ) + .filter( '[data-column="' + columnIndex + '"]' ) + .find( 'select option:selected' ) + .attr( 'data-function-name' ) || '' : ''; + // replace accents - see #357 + if ( c.sortLocaleCompare ) { + data.filter = ts.replaceAccents( data.filter ); + } - val = true; - if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) { - data.filter = ts.filter.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); - // val is used to indicate that a filter select is using a default filter; - // so we override the exact & partial matches - val = false; - } - // data.iFilter = case insensitive ( if wo.filter_ignoreCase is true ), - // data.filter = case sensitive - data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter; - fxn = vars.functions[ columnIndex ]; - hasSelect = c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' ); - filterMatched = null; - if ( fxn || ( hasSelect && val ) ) { - if ( fxn === true || hasSelect ) { - // default selector uses exact match unless 'filter-match' class is found - filterMatched = data.isMatch ? - data.iExact.search( data.iFilter ) >= 0 : - data.filter === data.exact; - } else if ( typeof fxn === 'function' ) { - // filter callback( exact cell content, parser normalized content, - // filter input value, column index, jQuery row object ) - filterMatched = fxn( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); - } else if ( typeof fxn[ ffxn || data.filter ] === 'function' ) { - // selector option function - txt = ffxn || data.filter; - filterMatched = - fxn[ txt ]( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); + val = true; + if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) { + data.filter = ts.filter.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); + // val is used to indicate that a filter select is using a default filter; + // so we override the exact & partial matches + val = false; } - } - if ( filterMatched === null ) { - // cycle through the different filters - // filters return a boolean or null if nothing matches - filterMatched = ts.filter.processTypes( c, data, vars ); - if ( filterMatched !== null ) { - result = filterMatched; - // Look for match, and add child row data for matching + // data.iFilter = case insensitive ( if wo.filter_ignoreCase is true ), + // data.filter = case sensitive + data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter; + fxn = vars.functions[ columnIndex ]; + hasSelect = c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' ); + filterMatched = null; + if ( fxn || ( hasSelect && val ) ) { + if ( fxn === true || hasSelect ) { + // default selector uses exact match unless 'filter-match' class is found + filterMatched = data.isMatch ? + data.iExact.search( data.iFilter ) >= 0 : + data.filter === data.exact; + } else if ( typeof fxn === 'function' ) { + // filter callback( exact cell content, parser normalized content, + // filter input value, column index, jQuery row object ) + filterMatched = fxn( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); + } else if ( typeof fxn[ ffxn || data.filter ] === 'function' ) { + // selector option function + txt = ffxn || data.filter; + filterMatched = + fxn[ txt ]( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); + } + } + if ( filterMatched === null ) { + // cycle through the different filters + // filters return a boolean or null if nothing matches + filterMatched = ts.filter.processTypes( c, data, vars ); + if ( filterMatched !== null ) { + result = filterMatched; + // Look for match, and add child row data for matching + } else { + txt = ( data.iExact + data.childRowText ) + .indexOf( ts.filter.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) ); + result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); + } } else { - txt = ( data.iExact + data.childRowText ) - .indexOf( ts.filter.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) ); - result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); + result = filterMatched; } - } else { - result = filterMatched; + showRow = ( result ) ? showRow : false; } - showRow = ( result ) ? showRow : false; } - } - return showRow; - }, - findRows: function( table, filters, combinedFilters ) { - if ( table.config.lastCombinedFilter === combinedFilters || - !table.config.widgetOptions.filter_initialized ) { - return; - } - var len, norm_rows, rowData, $rows, rowIndex, tbodyIndex, $tbody, columnIndex, - isChild, childRow, lastSearch, showRow, time, val, indx, - notFiltered, searchFiltered, query, injected, res, id, txt, - storedFilters = $.extend( [], filters ), - regex = ts.filter.regex, - c = table.config, - wo = c.widgetOptions, - // data object passed to filters; anyMatch is a flag for the filters - data = { - anyMatch: false, - filters: filters, - // regex filter type cache - filter_regexCache : [] - }, - vars = { - // anyMatch really screws up with these types of filters - noAnyMatch: [ 'range', 'notMatch', 'operators' ], - // cache filter variables that use ts.getColumnData in the main loop - functions : [], - excludeFilter : [], - defaultColFilter : [], - defaultAnyFilter : ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '' - }; + return showRow; + }, + findRows: function( table, filters, combinedFilters ) { + if ( table.config.lastCombinedFilter === combinedFilters || + !table.config.widgetOptions.filter_initialized ) { + return; + } + var len, norm_rows, rowData, $rows, rowIndex, tbodyIndex, $tbody, columnIndex, + isChild, childRow, lastSearch, showRow, time, val, indx, + notFiltered, searchFiltered, query, injected, res, id, txt, + storedFilters = $.extend( [], filters ), + regex = ts.filter.regex, + c = table.config, + wo = c.widgetOptions, + // data object passed to filters; anyMatch is a flag for the filters + data = { + anyMatch: false, + filters: filters, + // regex filter type cache + filter_regexCache : [] + }, + vars = { + // anyMatch really screws up with these types of filters + noAnyMatch: [ 'range', 'notMatch', 'operators' ], + // cache filter variables that use ts.getColumnData in the main loop + functions : [], + excludeFilter : [], + defaultColFilter : [], + defaultAnyFilter : ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '' + }; - // parse columns after formatter, in case the class is added at that point - data.parsed = c.$headers.map( function( columnIndex ) { - return c.parsers && c.parsers[ columnIndex ] && - // force parsing if parser type is numeric - c.parsers[ columnIndex ].parsed || - // getData won't return 'parsed' if other 'filter-' class names exist - // ( e.g. <th class="filter-select filter-parsed"> ) - ts.getData && ts.getData( c.$headerIndexed[ columnIndex ], - ts.getColumnData( table, c.headers, columnIndex ), 'filter' ) === 'parsed' || - $( this ).hasClass( 'filter-parsed' ); - }).get(); + // parse columns after formatter, in case the class is added at that point + data.parsed = c.$headers.map( function( columnIndex ) { + return c.parsers && c.parsers[ columnIndex ] && + // force parsing if parser type is numeric + c.parsers[ columnIndex ].parsed || + // getData won't return 'parsed' if other 'filter-' class names exist + // ( e.g. <th class="filter-select filter-parsed"> ) + ts.getData && ts.getData( c.$headerIndexed[ columnIndex ], + ts.getColumnData( table, c.headers, columnIndex ), 'filter' ) === 'parsed' || + $( this ).hasClass( 'filter-parsed' ); + }).get(); - for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { - vars.functions[ columnIndex ] = - ts.getColumnData( table, wo.filter_functions, columnIndex ); - vars.defaultColFilter[ columnIndex ] = - ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || ''; - vars.excludeFilter[ columnIndex ] = - ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split( /\s+/ ); - } + for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { + vars.functions[ columnIndex ] = + ts.getColumnData( table, wo.filter_functions, columnIndex ); + vars.defaultColFilter[ columnIndex ] = + ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || ''; + vars.excludeFilter[ columnIndex ] = + ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split( /\s+/ ); + } - if ( c.debug ) { - ts.log( 'Filter: Starting filter widget search', filters ); - time = new Date(); - } - // filtered rows count - c.filteredRows = 0; - c.totalRows = 0; - // combindedFilters are undefined on init - combinedFilters = ( storedFilters || [] ).join( '' ); + if ( c.debug ) { + console.log( 'Filter: Starting filter widget search', filters ); + time = new Date(); + } + // filtered rows count + c.filteredRows = 0; + c.totalRows = 0; + // combindedFilters are undefined on init + combinedFilters = ( storedFilters || [] ).join( '' ); - for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { - $tbody = ts.processTbody( table, c.$tbodies.eq( tbodyIndex ), true ); - // skip child rows & widget added ( removable ) rows - fixes #448 thanks to @hempel! - // $rows = $tbody.children( 'tr' ).not( c.selectorRemove ); - columnIndex = c.columns; - // convert stored rows into a jQuery object - norm_rows = c.cache[ tbodyIndex ].normalized; - $rows = $( $.map( norm_rows, function( el ) { - return el[ columnIndex ].$row.get(); - }) ); + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody( table, c.$tbodies.eq( tbodyIndex ), true ); + // skip child rows & widget added ( removable ) rows - fixes #448 thanks to @hempel! + // $rows = $tbody.children( 'tr' ).not( c.selectorRemove ); + columnIndex = c.columns; + // convert stored rows into a jQuery object + norm_rows = c.cache[ tbodyIndex ].normalized; + $rows = $( $.map( norm_rows, function( el ) { + return el[ columnIndex ].$row.get(); + }) ); - if ( combinedFilters === '' || wo.filter_serversideFiltering ) { - $rows - .removeClass( wo.filter_filteredRow ) - .not( '.' + c.cssChildRow ) - .css( 'display', '' ); - } else { - // filter out child rows - $rows = $rows.not( '.' + c.cssChildRow ); - len = $rows.length; + if ( combinedFilters === '' || wo.filter_serversideFiltering ) { + $rows + .removeClass( wo.filter_filteredRow ) + .not( '.' + c.cssChildRow ) + .css( 'display', '' ); + } else { + // filter out child rows + $rows = $rows.not( '.' + c.cssChildRow ); + len = $rows.length; - if ( ( wo.filter_$anyMatch && wo.filter_$anyMatch.length ) || - typeof filters[c.columns] !== 'undefined' ) { - data.anyMatchFlag = true; - data.anyMatchFilter = '' + ( - filters[ c.columns ] || - wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || - '' - ); - if ( wo.filter_columnAnyMatch ) { - // specific columns search - query = data.anyMatchFilter.split( regex.andSplit ); - injected = false; - for ( indx = 0; indx < query.length; indx++ ) { - res = query[ indx ].split( ':' ); - if ( res.length > 1 ) { - // make the column a one-based index ( non-developers start counting from one :P ) - id = parseInt( res[0], 10 ) - 1; - if ( id >= 0 && id < c.columns ) { // if id is an integer - filters[ id ] = res[1]; - query.splice( indx, 1 ); - indx--; - injected = true; + if ( ( wo.filter_$anyMatch && wo.filter_$anyMatch.length ) || + typeof filters[c.columns] !== 'undefined' ) { + data.anyMatchFlag = true; + data.anyMatchFilter = '' + ( + filters[ c.columns ] || + wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || + '' + ); + if ( wo.filter_columnAnyMatch ) { + // specific columns search + query = data.anyMatchFilter.split( regex.andSplit ); + injected = false; + for ( indx = 0; indx < query.length; indx++ ) { + res = query[ indx ].split( ':' ); + if ( res.length > 1 ) { + // make the column a one-based index ( non-developers start counting from one :P ) + id = parseInt( res[0], 10 ) - 1; + if ( id >= 0 && id < c.columns ) { // if id is an integer + filters[ id ] = res[1]; + query.splice( indx, 1 ); + indx--; + injected = true; + } } } - } - if ( injected ) { - data.anyMatchFilter = query.join( ' && ' ); + if ( injected ) { + data.anyMatchFilter = query.join( ' && ' ); + } } } - } - // optimize searching only through already filtered rows - see #313 - searchFiltered = wo.filter_searchFiltered; - lastSearch = c.lastSearch || c.$table.data( 'lastSearch' ) || []; - if ( searchFiltered ) { - // cycle through all filters; include last ( columnIndex + 1 = match any column ). Fixes #669 - for ( indx = 0; indx < columnIndex + 1; indx++ ) { - val = filters[indx] || ''; - // break out of loop if we've already determined not to search filtered rows - if ( !searchFiltered ) { indx = columnIndex; } - // search already filtered rows if... - searchFiltered = searchFiltered && lastSearch.length && - // there are no changes from beginning of filter - val.indexOf( lastSearch[indx] || '' ) === 0 && - // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string - !regex.alreadyFiltered.test( val ) && - // if we are not doing exact matches, using '|' ( logical or ) or not '!' - !/[=\"\|!]/.test( val ) && - // don't search only filtered if the value is negative - // ( '> -10' => '> -100' will ignore hidden rows ) - !( /(>=?\s*-\d)/.test( val ) || /(<=?\s*\d)/.test( val ) ) && - // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 - !( val !== '' && c.$filters && c.$filters.eq( indx ).find( 'select' ).length && - !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); + // optimize searching only through already filtered rows - see #313 + searchFiltered = wo.filter_searchFiltered; + lastSearch = c.lastSearch || c.$table.data( 'lastSearch' ) || []; + if ( searchFiltered ) { + // cycle through all filters; include last ( columnIndex + 1 = match any column ). Fixes #669 + for ( indx = 0; indx < columnIndex + 1; indx++ ) { + val = filters[indx] || ''; + // break out of loop if we've already determined not to search filtered rows + if ( !searchFiltered ) { indx = columnIndex; } + // search already filtered rows if... + searchFiltered = searchFiltered && lastSearch.length && + // there are no changes from beginning of filter + val.indexOf( lastSearch[indx] || '' ) === 0 && + // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string + !regex.alreadyFiltered.test( val ) && + // if we are not doing exact matches, using '|' ( logical or ) or not '!' + !/[=\"\|!]/.test( val ) && + // don't search only filtered if the value is negative + // ( '> -10' => '> -100' will ignore hidden rows ) + !( /(>=?\s*-\d)/.test( val ) || /(<=?\s*\d)/.test( val ) ) && + // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 + !( val !== '' && c.$filters && c.$filters.eq( indx ).find( 'select' ).length && + !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); + } } - } - notFiltered = $rows.not( '.' + wo.filter_filteredRow ).length; - // can't search when all rows are hidden - this happens when looking for exact matches - if ( searchFiltered && notFiltered === 0 ) { searchFiltered = false; } - if ( c.debug ) { - ts.log( 'Filter: Searching through ' + - ( searchFiltered && notFiltered < len ? notFiltered : 'all' ) + ' rows' ); - } - if ( data.anyMatchFlag ) { - if ( c.sortLocaleCompare ) { - // replace accents - data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter ); + notFiltered = $rows.not( '.' + wo.filter_filteredRow ).length; + // can't search when all rows are hidden - this happens when looking for exact matches + if ( searchFiltered && notFiltered === 0 ) { searchFiltered = false; } + if ( c.debug ) { + console.log( 'Filter: Searching through ' + + ( searchFiltered && notFiltered < len ? notFiltered : 'all' ) + ' rows' ); } - if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultAnyFilter ) ) { - data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter ); - // clear search filtered flag because default filters are not saved to the last search - searchFiltered = false; + if ( data.anyMatchFlag ) { + if ( c.sortLocaleCompare ) { + // replace accents + data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter ); + } + if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultAnyFilter ) ) { + data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter ); + // clear search filtered flag because default filters are not saved to the last search + searchFiltered = false; + } + // make iAnyMatchFilter lowercase unless both filter widget & core ignoreCase options are true + // when c.ignoreCase is true, the cache contains all lower case data + data.iAnyMatchFilter = !( wo.filter_ignoreCase && c.ignoreCase ) ? + data.anyMatchFilter : + data.anyMatchFilter.toLowerCase(); } - // make iAnyMatchFilter lowercase unless both filter widget & core ignoreCase options are true - // when c.ignoreCase is true, the cache contains all lower case data - data.iAnyMatchFilter = !( wo.filter_ignoreCase && c.ignoreCase ) ? - data.anyMatchFilter : - data.anyMatchFilter.toLowerCase(); - } - // loop through the rows - for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { + // loop through the rows + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { - txt = $rows[ rowIndex ].className; - // the first row can never be a child row - isChild = rowIndex && regex.child.test( txt ); - // skip child rows & already filtered rows - if ( isChild || ( searchFiltered && regex.filtered.test( txt ) ) ) { - continue; - } + txt = $rows[ rowIndex ].className; + // the first row can never be a child row + isChild = rowIndex && regex.child.test( txt ); + // skip child rows & already filtered rows + if ( isChild || ( searchFiltered && regex.filtered.test( txt ) ) ) { + continue; + } - data.$row = $rows.eq( rowIndex ); - data.cacheArray = norm_rows[ rowIndex ]; - rowData = data.cacheArray[ c.columns ]; - data.rawArray = rowData.raw; - data.childRowText = ''; + data.$row = $rows.eq( rowIndex ); + data.cacheArray = norm_rows[ rowIndex ]; + rowData = data.cacheArray[ c.columns ]; + data.rawArray = rowData.raw; + data.childRowText = ''; - if ( !wo.filter_childByColumn ) { - txt = ''; - // child row cached text - childRow = rowData.child; - // so, if 'table.config.widgetOptions.filter_childRows' is true and there is - // a match anywhere in the child row, then it will make the row visible - // checked here so the option can be changed dynamically - for ( indx = 0; indx < childRow.length; indx++ ) { - txt += ' ' + childRow[indx].join( '' ) || ''; + if ( !wo.filter_childByColumn ) { + txt = ''; + // child row cached text + childRow = rowData.child; + // so, if 'table.config.widgetOptions.filter_childRows' is true and there is + // a match anywhere in the child row, then it will make the row visible + // checked here so the option can be changed dynamically + for ( indx = 0; indx < childRow.length; indx++ ) { + txt += ' ' + childRow[indx].join( '' ) || ''; + } + data.childRowText = wo.filter_childRows ? + ( wo.filter_ignoreCase ? txt.toLowerCase() : txt ) : + ''; } - data.childRowText = wo.filter_childRows ? - ( wo.filter_ignoreCase ? txt.toLowerCase() : txt ) : - ''; - } - showRow = ts.filter.processRow( c, data, vars ); - childRow = rowData.$row.filter( ':gt( 0 )' ); + showRow = ts.filter.processRow( c, data, vars ); + childRow = rowData.$row.filter( ':gt( 0 )' ); - if ( wo.filter_childRows && childRow.length ) { - if ( wo.filter_childByColumn ) { - // cycle through each child row - for ( indx = 0; indx < childRow.length; indx++ ) { - data.$row = childRow.eq( indx ); - data.cacheArray = rowData.child[ indx ]; - data.rawArray = data.cacheArray; - // use OR comparison on child rows - showRow = showRow || ts.filter.processRow( c, data, vars ); + if ( wo.filter_childRows && childRow.length ) { + if ( wo.filter_childByColumn ) { + // cycle through each child row + for ( indx = 0; indx < childRow.length; indx++ ) { + data.$row = childRow.eq( indx ); + data.cacheArray = rowData.child[ indx ]; + data.rawArray = data.cacheArray; + // use OR comparison on child rows + showRow = showRow || ts.filter.processRow( c, data, vars ); + } } + childRow.toggleClass( wo.filter_filteredRow, !showRow ); } - childRow.toggleClass( wo.filter_filteredRow, !showRow ); - } - rowData.$row - .toggleClass( wo.filter_filteredRow, !showRow )[0] - .display = showRow ? '' : 'none'; + rowData.$row + .toggleClass( wo.filter_filteredRow, !showRow )[0] + .display = showRow ? '' : 'none'; + } } + c.filteredRows += $rows.not( '.' + wo.filter_filteredRow ).length; + c.totalRows += $rows.length; + ts.processTbody( table, $tbody, false ); } - c.filteredRows += $rows.not( '.' + wo.filter_filteredRow ).length; - c.totalRows += $rows.length; - ts.processTbody( table, $tbody, false ); - } - c.lastCombinedFilter = combinedFilters; // save last search - // don't save 'filters' directly since it may have altered ( AnyMatch column searches ) - c.lastSearch = storedFilters; - c.$table.data( 'lastSearch', storedFilters ); - if ( wo.filter_saveFilters && ts.storage ) { - ts.storage( table, 'tablesorter-filters', storedFilters ); - } - if ( c.debug ) { - ts.benchmark( 'Completed filter widget search', time ); - } - if ( wo.filter_initialized ) { - c.$table.trigger( 'filterEnd', c ); - } - setTimeout( function() { - c.$table.trigger( 'applyWidgets' ); // make sure zebra widget is applied - }, 0 ); - }, - getOptionSource: function( table, column, onlyAvail ) { - table = $( table )[0]; - var cts, txt, indx, len, - c = table.config, - wo = c.widgetOptions, - parsed = [], - arry = false, - source = wo.filter_selectSource, - last = c.$table.data( 'lastSearch' ) || [], - fxn = $.isFunction( source ) ? true : ts.getColumnData( table, source, column ); + c.lastCombinedFilter = combinedFilters; // save last search + // don't save 'filters' directly since it may have altered ( AnyMatch column searches ) + c.lastSearch = storedFilters; + c.$table.data( 'lastSearch', storedFilters ); + if ( wo.filter_saveFilters && ts.storage ) { + ts.storage( table, 'tablesorter-filters', storedFilters ); + } + if ( c.debug ) { + console.log( 'Completed filter widget search' + ts.benchmark(time) ); + } + if ( wo.filter_initialized ) { + c.$table.trigger( 'filterEnd', c ); + } + setTimeout( function() { + c.$table.trigger( 'applyWidgets' ); // make sure zebra widget is applied + }, 0 ); + }, + getOptionSource: function( table, column, onlyAvail ) { + table = $( table )[0]; + var cts, txt, indx, len, + c = table.config, + wo = c.widgetOptions, + parsed = [], + arry = false, + source = wo.filter_selectSource, + last = c.$table.data( 'lastSearch' ) || [], + fxn = $.isFunction( source ) ? true : ts.getColumnData( table, source, column ); - if ( onlyAvail && last[column] !== '' ) { - onlyAvail = false; - } + if ( onlyAvail && last[column] !== '' ) { + onlyAvail = false; + } - // filter select source option - if ( fxn === true ) { - // OVERALL source - arry = source( table, column, onlyAvail ); - } else if ( fxn instanceof $ || ( $.type( fxn ) === 'string' && fxn.indexOf( '</option>' ) >= 0 ) ) { - // selectSource is a jQuery object or string of options - return fxn; - } else if ( $.isArray( fxn ) ) { - arry = fxn; - } else if ( $.type( source ) === 'object' && fxn ) { - // custom select source function for a SPECIFIC COLUMN - arry = fxn( table, column, onlyAvail ); - } - if ( arry === false ) { - // fall back to original method - arry = ts.filter.getOptions( table, column, onlyAvail ); - } + // filter select source option + if ( fxn === true ) { + // OVERALL source + arry = source( table, column, onlyAvail ); + } else if ( fxn instanceof $ || ( $.type( fxn ) === 'string' && fxn.indexOf( '</option>' ) >= 0 ) ) { + // selectSource is a jQuery object or string of options + return fxn; + } else if ( $.isArray( fxn ) ) { + arry = fxn; + } else if ( $.type( source ) === 'object' && fxn ) { + // custom select source function for a SPECIFIC COLUMN + arry = fxn( table, column, onlyAvail ); + } + if ( arry === false ) { + // fall back to original method + arry = ts.filter.getOptions( table, column, onlyAvail ); + } - // get unique elements and sort the list - // if $.tablesorter.sortText exists ( not in the original tablesorter ), - // then natural sort the list otherwise use a basic sort - arry = $.grep( arry, function( value, indx ) { - return $.inArray( value, arry ) === indx; - }); + // get unique elements and sort the list + // if $.tablesorter.sortText exists ( not in the original tablesorter ), + // then natural sort the list otherwise use a basic sort + arry = $.grep( arry, function( value, indx ) { + return $.inArray( value, arry ) === indx; + }); - if ( c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) { - // unsorted select options - return arry; - } else { - len = arry.length; - // parse select option values - for ( indx = 0; indx < len; indx++ ) { - txt = arry[ indx ]; - // parse array data using set column parser; this DOES NOT pass the original - // table cell to the parser format function - parsed.push({ - t : txt, - // check parser length - fixes #934 - p : c.parsers && c.parsers.length && c.parsers[ column ].format( txt, table, [], column ) || txt + if ( c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) { + // unsorted select options + return arry; + } else { + len = arry.length; + // parse select option values + for ( indx = 0; indx < len; indx++ ) { + txt = arry[ indx ]; + // parse array data using set column parser; this DOES NOT pass the original + // table cell to the parser format function + parsed.push({ + t : txt, + // check parser length - fixes #934 + p : c.parsers && c.parsers.length && c.parsers[ column ].format( txt, table, [], column ) || txt + }); + } + + // sort parsed select options + cts = c.textSorter || ''; + parsed.sort( function( a, b ) { + // sortNatural breaks if you don't pass it strings + var x = a.p.toString(), + y = b.p.toString(); + if ( $.isFunction( cts ) ) { + // custom OVERALL text sorter + return cts( x, y, true, column, table ); + } else if ( typeof cts === 'object' && cts.hasOwnProperty( column ) ) { + // custom text sorter for a SPECIFIC COLUMN + return cts[column]( x, y, true, column, table ); + } else if ( ts.sortNatural ) { + // fall back to natural sort + return ts.sortNatural( x, y ); + } + // using an older version! do a basic sort + return true; }); + // rebuild arry from sorted parsed data + arry = []; + len = parsed.length; + for ( indx = 0; indx < len; indx++ ) { + arry.push( parsed[indx].t ); + } + return arry; } - - // sort parsed select options - cts = c.textSorter || ''; - parsed.sort( function( a, b ) { - // sortNatural breaks if you don't pass it strings - var x = a.p.toString(), - y = b.p.toString(); - if ( $.isFunction( cts ) ) { - // custom OVERALL text sorter - return cts( x, y, true, column, table ); - } else if ( typeof( cts ) === 'object' && cts.hasOwnProperty( column ) ) { - // custom text sorter for a SPECIFIC COLUMN - return cts[column]( x, y, true, column, table ); - } else if ( ts.sortNatural ) { - // fall back to natural sort - return ts.sortNatural( x, y ); + }, + getOptions: function( table, column, onlyAvail ) { + table = $( table )[0]; + var rowIndex, tbodyIndex, len, row, cache, + c = table.config, + wo = c.widgetOptions, + arry = []; + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + cache = c.cache[tbodyIndex]; + len = c.cache[tbodyIndex].normalized.length; + // loop through the rows + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { + // get cached row from cache.row ( old ) or row data object + // ( new; last item in normalized array ) + row = cache.row ? + cache.row[ rowIndex ] : + cache.normalized[ rowIndex ][ c.columns ].$row[0]; + // check if has class filtered + if ( onlyAvail && row.className.match( wo.filter_filteredRow ) ) { + continue; + } + // get non-normalized cell content + if ( wo.filter_useParsedData || + c.parsers[column].parsed || + c.$headerIndexed[column].hasClass( 'filter-parsed' ) ) { + arry.push( '' + cache.normalized[ rowIndex ][ column ] ); + } else { + // get raw cached data instead of content directly from the cells + arry.push( cache.normalized[ rowIndex ][ c.columns ].raw[ column ] ); + } } - // using an older version! do a basic sort - return true; - }); - // rebuild arry from sorted parsed data - arry = []; - len = parsed.length; - for ( indx = 0; indx < len; indx++ ) { - arry.push( parsed[indx].t ); } return arry; - } - }, - getOptions: function( table, column, onlyAvail ) { - table = $( table )[0]; - var rowIndex, tbodyIndex, len, row, cache, - c = table.config, - wo = c.widgetOptions, - arry = []; - for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { - cache = c.cache[tbodyIndex]; - len = c.cache[tbodyIndex].normalized.length; - // loop through the rows - for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { - // get cached row from cache.row ( old ) or row data object - // ( new; last item in normalized array ) - row = cache.row ? - cache.row[ rowIndex ] : - cache.normalized[ rowIndex ][ c.columns ].$row[0]; - // check if has class filtered - if ( onlyAvail && row.className.match( wo.filter_filteredRow ) ) { - continue; - } - // get non-normalized cell content - if ( wo.filter_useParsedData || - c.parsers[column].parsed || - c.$headerIndexed[column].hasClass( 'filter-parsed' ) ) { - arry.push( '' + cache.normalized[ rowIndex ][ column ] ); - } else { - // get raw cached data instead of content directly from the cells - arry.push( cache.normalized[ rowIndex ][ c.columns ].raw[ column ] ); - } + }, + buildSelect: function( table, column, arry, updating, onlyAvail ) { + table = $( table )[0]; + column = parseInt( column, 10 ); + if ( !table.config.cache || $.isEmptyObject( table.config.cache ) ) { + return; + } + var indx, val, txt, t, $filters, $filter, + c = table.config, + wo = c.widgetOptions, + node = c.$headerIndexed[ column ], + // t.data( 'placeholder' ) won't work in jQuery older than 1.4.3 + options = '<option value="">' + + ( node.data( 'placeholder' ) || + node.attr( 'data-placeholder' ) || + wo.filter_placeholder.select || '' + ) + '</option>', + // Get curent filter value + currentValue = c.$table + .find( 'thead' ) + .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) + .val(); + // nothing included in arry ( external source ), so get the options from + // filter_selectSource or column data + if ( typeof arry === 'undefined' || arry === '' ) { + arry = ts.filter.getOptionSource( table, column, onlyAvail ); } - } - return arry; - }, - buildSelect: function( table, column, arry, updating, onlyAvail ) { - table = $( table )[0]; - column = parseInt( column, 10 ); - if ( !table.config.cache || $.isEmptyObject( table.config.cache ) ) { - return; - } - var indx, val, txt, t, $filters, $filter, - c = table.config, - wo = c.widgetOptions, - node = c.$headerIndexed[ column ], - // t.data( 'placeholder' ) won't work in jQuery older than 1.4.3 - options = '<option value="">' + - ( node.data( 'placeholder' ) || - node.attr( 'data-placeholder' ) || - wo.filter_placeholder.select || '' - ) + '</option>', - // Get curent filter value - currentValue = c.$table - .find( 'thead' ) - .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) - .val(); - // nothing included in arry ( external source ), so get the options from - // filter_selectSource or column data - if ( typeof arry === 'undefined' || arry === '' ) { - arry = ts.filter.getOptionSource( table, column, onlyAvail ); - } - if ( $.isArray( arry ) ) { - // build option list - for ( indx = 0; indx < arry.length; indx++ ) { - txt = arry[indx] = ( '' + arry[indx] ).replace( /\"/g, '"' ); - val = txt; - // allow including a symbol in the selectSource array - // 'a-z|A through Z' so that 'a-z' becomes the option value - // and 'A through Z' becomes the option text - if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { - t = txt.split( wo.filter_selectSourceSeparator ); - val = t[0]; - txt = t[1]; + if ( $.isArray( arry ) ) { + // build option list + for ( indx = 0; indx < arry.length; indx++ ) { + txt = arry[indx] = ( '' + arry[indx] ).replace( /\"/g, '"' ); + val = txt; + // allow including a symbol in the selectSource array + // 'a-z|A through Z' so that 'a-z' becomes the option value + // and 'A through Z' becomes the option text + if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { + t = txt.split( wo.filter_selectSourceSeparator ); + val = t[0]; + txt = t[1]; + } + // replace quotes - fixes #242 & ignore empty strings + // see http://stackoverflow.com/q/14990971/145346 + options += arry[indx] !== '' ? + '<option ' + + ( val === txt ? '' : 'data-function-name="' + arry[indx] + '" ' ) + + 'value="' + val + '">' + txt + + '</option>' : ''; } - // replace quotes - fixes #242 & ignore empty strings - // see http://stackoverflow.com/q/14990971/145346 - options += arry[indx] !== '' ? - '<option ' + - ( val === txt ? '' : 'data-function-name="' + arry[indx] + '" ' ) + - 'value="' + val + '">' + txt + - '</option>' : ''; + // clear arry so it doesn't get appended twice + arry = []; } - // clear arry so it doesn't get appended twice - arry = []; - } - // update all selects in the same column ( clone thead in sticky headers & - // any external selects ) - fixes 473 - $filters = ( c.$filters ? c.$filters : c.$table.children( 'thead' ) ) - .find( '.' + tscss.filter ); - if ( wo.filter_$externalFilters ) { - $filters = $filters && $filters.length ? - $filters.add( wo.filter_$externalFilters ) : - wo.filter_$externalFilters; - } - $filter = $filters.filter( 'select[data-column="' + column + '"]' ); + // update all selects in the same column ( clone thead in sticky headers & + // any external selects ) - fixes 473 + $filters = ( c.$filters ? c.$filters : c.$table.children( 'thead' ) ) + .find( '.' + tscss.filter ); + if ( wo.filter_$externalFilters ) { + $filters = $filters && $filters.length ? + $filters.add( wo.filter_$externalFilters ) : + wo.filter_$externalFilters; + } + $filter = $filters.filter( 'select[data-column="' + column + '"]' ); - // make sure there is a select there! - if ( $filter.length ) { - $filter[ updating ? 'html' : 'append' ]( options ); - if ( !$.isArray( arry ) ) { - // append options if arry is provided externally as a string or jQuery object - // options ( default value ) was already added - $filter.append( arry ).val( currentValue ); + // make sure there is a select there! + if ( $filter.length ) { + $filter[ updating ? 'html' : 'append' ]( options ); + if ( !$.isArray( arry ) ) { + // append options if arry is provided externally as a string or jQuery object + // options ( default value ) was already added + $filter.append( arry ).val( currentValue ); + } + $filter.val( currentValue ); } - $filter.val( currentValue ); - } - }, - buildDefault: function( table, updating ) { - var columnIndex, $header, noSelect, - c = table.config, - wo = c.widgetOptions, - columns = c.columns; - // build default select dropdown - for ( columnIndex = 0; columnIndex < columns; columnIndex++ ) { - $header = c.$headerIndexed[columnIndex]; - noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); - // look for the filter-select class; build/update it if found - if ( ( $header.hasClass( 'filter-select' ) || - ts.getColumnData( table, wo.filter_functions, columnIndex ) === true ) && noSelect ) { - ts.filter.buildSelect( table, columnIndex, '', updating, $header.hasClass( wo.filter_onlyAvail ) ); + }, + buildDefault: function( table, updating ) { + var columnIndex, $header, noSelect, + c = table.config, + wo = c.widgetOptions, + columns = c.columns; + // build default select dropdown + for ( columnIndex = 0; columnIndex < columns; columnIndex++ ) { + $header = c.$headerIndexed[columnIndex]; + noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); + // look for the filter-select class; build/update it if found + if ( ( $header.hasClass( 'filter-select' ) || + ts.getColumnData( table, wo.filter_functions, columnIndex ) === true ) && noSelect ) { + ts.filter.buildSelect( table, columnIndex, '', updating, $header.hasClass( wo.filter_onlyAvail ) ); + } } } - } -}; + }; -ts.getFilters = function( table, getRaw, setFilters, skipFirst ) { - var i, $filters, $column, cols, - filters = false, - c = table ? $( table )[0].config : '', - wo = c ? c.widgetOptions : ''; - if ( ( getRaw !== true && wo && !wo.filter_columnFilters ) || - // setFilters called, but last search is exactly the same as the current - // fixes issue #733 & #903 where calling update causes the input values to reset - ( $.isArray(setFilters) && setFilters.join('') === c.lastCombinedFilter ) ) { - return $( table ).data( 'lastSearch' ); - } - if ( c ) { - if ( c.$filters ) { - $filters = c.$filters.find( '.' + tscss.filter ); + ts.getFilters = function( table, getRaw, setFilters, skipFirst ) { + var i, $filters, $column, cols, + filters = false, + c = table ? $( table )[0].config : '', + wo = c ? c.widgetOptions : ''; + if ( ( getRaw !== true && wo && !wo.filter_columnFilters ) || + // setFilters called, but last search is exactly the same as the current + // fixes issue #733 & #903 where calling update causes the input values to reset + ( $.isArray(setFilters) && setFilters.join('') === c.lastCombinedFilter ) ) { + return $( table ).data( 'lastSearch' ); } - if ( wo.filter_$externalFilters ) { - $filters = $filters && $filters.length ? - $filters.add( wo.filter_$externalFilters ) : - wo.filter_$externalFilters; - } - if ( $filters && $filters.length ) { - filters = setFilters || []; - for ( i = 0; i < c.columns + 1; i++ ) { - cols = ( i === c.columns ? - // 'all' columns can now include a range or set of columms ( data-column='0-2,4,6-7' ) - wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector : - '[data-column="' + i + '"]' ); - $column = $filters.filter( cols ); - if ( $column.length ) { - // move the latest search to the first slot in the array - $column = ts.filter.getLatestSearch( $column ); - if ( $.isArray( setFilters ) ) { - // skip first ( latest input ) to maintain cursor position while typing - if ( skipFirst && $column.length > 1 ) { - $column = $column.slice( 1 ); - } - if ( i === c.columns ) { - // prevent data-column='all' from filling data-column='0,1' ( etc ) - cols = $column.filter( wo.filter_anyColumnSelector ); - $column = cols.length ? cols : $column; - } - $column - .val( setFilters[ i ] ) - .trigger( 'change.tsfilter' ); - } else { - filters[i] = $column.val() || ''; - // don't change the first... it will move the cursor - if ( i === c.columns ) { - // don't update range columns from 'all' setting + if ( c ) { + if ( c.$filters ) { + $filters = c.$filters.find( '.' + tscss.filter ); + } + if ( wo.filter_$externalFilters ) { + $filters = $filters && $filters.length ? + $filters.add( wo.filter_$externalFilters ) : + wo.filter_$externalFilters; + } + if ( $filters && $filters.length ) { + filters = setFilters || []; + for ( i = 0; i < c.columns + 1; i++ ) { + cols = ( i === c.columns ? + // 'all' columns can now include a range or set of columms ( data-column='0-2,4,6-7' ) + wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector : + '[data-column="' + i + '"]' ); + $column = $filters.filter( cols ); + if ( $column.length ) { + // move the latest search to the first slot in the array + $column = ts.filter.getLatestSearch( $column ); + if ( $.isArray( setFilters ) ) { + // skip first ( latest input ) to maintain cursor position while typing + if ( skipFirst && $column.length > 1 ) { + $column = $column.slice( 1 ); + } + if ( i === c.columns ) { + // prevent data-column='all' from filling data-column='0,1' ( etc ) + cols = $column.filter( wo.filter_anyColumnSelector ); + $column = cols.length ? cols : $column; + } $column - .slice( 1 ) - .filter( '[data-column*="' + $column.attr( 'data-column' ) + '"]' ) - .val( filters[ i ] ); + .val( setFilters[ i ] ) + .trigger( 'change.tsfilter' ); } else { - $column - .slice( 1 ) - .val( filters[ i ] ); + filters[i] = $column.val() || ''; + // don't change the first... it will move the cursor + if ( i === c.columns ) { + // don't update range columns from 'all' setting + $column + .slice( 1 ) + .filter( '[data-column*="' + $column.attr( 'data-column' ) + '"]' ) + .val( filters[ i ] ); + } else { + $column + .slice( 1 ) + .val( filters[ i ] ); + } + } + // save any match input dynamically + if ( i === c.columns && $column.length ) { + wo.filter_$anyMatch = $column; } - } - // save any match input dynamically - if ( i === c.columns && $column.length ) { - wo.filter_$anyMatch = $column; } } } } - } - if ( filters.length === 0 ) { - filters = false; - } - return filters; -}; + if ( filters.length === 0 ) { + filters = false; + } + return filters; + }; -ts.setFilters = function( table, filter, apply, skipFirst ) { - var c = table ? $( table )[0].config : '', - valid = ts.getFilters( table, true, filter, skipFirst ); - if ( c && apply ) { - // ensure new set filters are applied, even if the search is the same - c.lastCombinedFilter = null; - c.lastSearch = []; - ts.filter.searching( c.table, filter, skipFirst ); - c.$table.trigger( 'filterFomatterUpdate' ); - } - return !!valid; -}; + ts.setFilters = function( table, filter, apply, skipFirst ) { + var c = table ? $( table )[0].config : '', + valid = ts.getFilters( table, true, filter, skipFirst ); + if ( c && apply ) { + // ensure new set filters are applied, even if the search is the same + c.lastCombinedFilter = null; + c.lastSearch = []; + ts.filter.searching( c.table, filter, skipFirst ); + c.$table.trigger( 'filterFomatterUpdate' ); + } + return !!valid; + }; })( jQuery ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js index 61e1a03..c3608a5 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js @@ -5,7 +5,7 @@ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ ;(function($){ -'use strict'; + 'use strict'; var ts = $.tablesorter; ts.formatter = { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index 178d8cc..d0133da 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -5,246 +5,246 @@ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ ;(function($){ -"use strict"; -var ts = $.tablesorter; + 'use strict'; + var ts = $.tablesorter; -ts.grouping = { + ts.grouping = { - types : { - number : function(c, $column, txt, num, group){ - var value, word; - if (num > 1 && txt !== '') { - if ($column.hasClass(ts.css.sortAsc)) { - value = Math.floor(parseFloat(txt)/num) * num; - return value > parseFloat(group || 0) ? value : parseFloat(group || 0); + types : { + number : function(c, $column, txt, num, group){ + var value, word; + if (num > 1 && txt !== '') { + if ($column.hasClass(ts.css.sortAsc)) { + value = Math.floor(parseFloat(txt) / num) * num; + return value > parseFloat(group || 0) ? value : parseFloat(group || 0); + } else { + value = Math.ceil(parseFloat(txt) / num) * num; + return value < parseFloat(group || num) - value ? parseFloat(group || num) - value : value; + } } else { - value = Math.ceil(parseFloat(txt)/num) * num; - return value < parseFloat(group || num) - value ? parseFloat(group || num) - value : value; + word = (txt + '').match(/\d+/g); + return word && word.length >= num ? word[num - 1] : txt || ''; } - } else { - word = (txt + '').match(/\d+/g); + }, + separator : function(c, $column, txt, num){ + var word = (txt + '').split(c.widgetOptions.group_separator); + return $.trim(word && num > 0 && word.length >= num ? word[(num || 1) - 1] : ''); + }, + word : function(c, $column, txt, num){ + var word = (txt + ' ').match(/\w+/g); return word && word.length >= num ? word[num - 1] : txt || ''; + }, + letter : function(c, $column, txt, num){ + return txt ? (txt + ' ').substring(0, num) : ''; + }, + date : function(c, $column, txt, part, group){ + var wo = c.widgetOptions, + time = new Date(txt || ''), + hours = time.getHours(); + return part === 'year' ? time.getFullYear() : + part === 'month' ? wo.group_months[time.getMonth()] : + part === 'monthyear' ? wo.group_months[time.getMonth()] + ' ' + time.getFullYear() : + part === 'day' ? wo.group_months[time.getMonth()] + ' ' + time.getDate() : + part === 'week' ? wo.group_week[time.getDay()] : + part === 'time' ? ('00' + (hours > 12 ? hours - 12 : hours === 0 ? hours + 12 : hours)).slice(-2) + ':' + + ('00' + time.getMinutes()).slice(-2) + ' ' + ('00' + wo.group_time[hours >= 12 ? 1 : 0]).slice(-2) : + wo.group_dateString(time); } }, - separator : function(c, $column, txt, num){ - var word = (txt + '').split(c.widgetOptions.group_separator); - return $.trim(word && num > 0 && word.length >= num ? word[(num || 1) - 1] : ''); - }, - word : function(c, $column, txt, num){ - var word = (txt + ' ').match(/\w+/g); - return word && word.length >= num ? word[num - 1] : txt || ''; - }, - letter : function(c, $column, txt, num){ - return txt ? (txt + ' ').substring(0, num) : ''; - }, - date : function(c, $column, txt, part, group){ - var wo = c.widgetOptions, - time = new Date(txt || ''), - hours = time.getHours(); - return part === 'year' ? time.getFullYear() : - part === 'month' ? wo.group_months[time.getMonth()] : - part === 'monthyear' ? wo.group_months[time.getMonth()] + ' ' + time.getFullYear() : - part === 'day' ? wo.group_months[time.getMonth()] + ' ' + time.getDate() : - part === 'week' ? wo.group_week[time.getDay()] : - part === 'time' ? ('00' + (hours > 12 ? hours - 12 : hours === 0 ? hours + 12 : hours)).slice(-2) + ':' + - ('00' + time.getMinutes()).slice(-2) + ' ' + ('00' + wo.group_time[hours >= 12 ? 1 : 0]).slice(-2) : - wo.group_dateString(time); - } - }, - update : function(table, c, wo){ - if ($.isEmptyObject(c.cache)) { return; } - var rowIndex, tbodyIndex, currentGroup, $rows, groupClass, grouping, norm_rows, saveName, direction, - lang = wo.grouping_language, - group = '', - savedGroup = false, - column = c.sortList[0] ? c.sortList[0][0] : -1; - c.$table - .find('tr.group-hidden').removeClass('group-hidden').end() - .find('tr.group-header').remove(); - if (wo.group_collapsible) { - // clear pager saved spacer height (in case the rows are collapsed) - c.$table.data('pagerSavedHeight', 0); - } - if (column >= 0 && !c.$headerIndexed[column].hasClass('group-false')) { - wo.group_currentGroup = ''; // save current groups - wo.group_currentGroups = {}; + update : function(table, c, wo){ + if ($.isEmptyObject(c.cache)) { return; } + var rowIndex, tbodyIndex, currentGroup, $rows, groupClass, grouping, norm_rows, saveName, direction, + lang = wo.grouping_language, + group = '', + savedGroup = false, + column = c.sortList[0] ? c.sortList[0][0] : -1; + c.$table + .find('tr.group-hidden').removeClass('group-hidden').end() + .find('tr.group-header').remove(); + if (wo.group_collapsible) { + // clear pager saved spacer height (in case the rows are collapsed) + c.$table.data('pagerSavedHeight', 0); + } + if (column >= 0 && !c.$headerIndexed[column].hasClass('group-false')) { + wo.group_currentGroup = ''; // save current groups + wo.group_currentGroups = {}; - // group class finds "group-{word/separator/letter/number/date/false}-{optional:#/year/month/day/week/time}" - groupClass = (c.$headerIndexed[column].attr('class') || '').match(/(group-\w+(-\w+)?)/g); - // grouping = [ 'group', '{word/separator/letter/number/date/false}', '{#/year/month/day/week/time}' ] - grouping = groupClass ? groupClass[0].split('-') : ['group','letter',1]; // default to letter 1 + // group class finds 'group-{word/separator/letter/number/date/false}-{optional:#/year/month/day/week/time}' + groupClass = (c.$headerIndexed[column].attr('class') || '').match(/(group-\w+(-\w+)?)/g); + // grouping = [ 'group', '{word/separator/letter/number/date/false}', '{#/year/month/day/week/time}' ] + grouping = groupClass ? groupClass[0].split('-') : [ 'group', 'letter', 1 ]; // default to letter 1 - // save current grouping - if (wo.group_collapsible && wo.group_saveGroups && ts.storage) { - wo.group_currentGroups = ts.storage( table, 'tablesorter-groups' ) || {}; - // include direction when grouping numbers > 1 (reversed direction shows different range values) - direction = (grouping[1] === 'number' && grouping[2] > 1) ? 'dir' + c.sortList[0][1] : ''; - // combine column, sort direction & grouping as save key - saveName = wo.group_currentGroup = '' + column + direction + grouping.join(''); - if (!wo.group_currentGroups[saveName]) { - wo.group_currentGroups[saveName] = []; - } else { - savedGroup = true; + // save current grouping + if (wo.group_collapsible && wo.group_saveGroups && ts.storage) { + wo.group_currentGroups = ts.storage( table, 'tablesorter-groups' ) || {}; + // include direction when grouping numbers > 1 (reversed direction shows different range values) + direction = (grouping[1] === 'number' && grouping[2] > 1) ? 'dir' + c.sortList[0][1] : ''; + // combine column, sort direction & grouping as save key + saveName = wo.group_currentGroup = '' + column + direction + grouping.join(''); + if (!wo.group_currentGroups[saveName]) { + wo.group_currentGroups[saveName] = []; + } else { + savedGroup = true; + } } - } - for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++) { - norm_rows = c.cache[tbodyIndex].normalized; - group = ''; // clear grouping across tbodies - $rows = c.$tbodies.eq(tbodyIndex).children('tr').not('.' + c.cssChildRow); - for (rowIndex = 0; rowIndex < $rows.length; rowIndex++) { - if ( $rows.eq(rowIndex).is(':visible') ) { - // fixes #438 - if (ts.grouping.types[grouping[1]]) { - currentGroup = norm_rows[rowIndex] ? - ts.grouping.types[grouping[1]]( c, c.$headerIndexed[column], norm_rows[rowIndex][column], /date/.test(groupClass) ? - grouping[2] : parseInt(grouping[2] || 1, 10) || 1, group, lang ) : currentGroup; - if (group !== currentGroup) { - group = currentGroup; - // show range if number > 1 - if (grouping[1] === 'number' && grouping[2] > 1 && currentGroup !== '') { - currentGroup += ' - ' + (parseInt(currentGroup, 10) + - ((parseInt(grouping[2],10) - 1) * (c.$headerIndexed[column].hasClass(ts.css.sortAsc) ? 1 : -1))); - } - if ($.isFunction(wo.group_formatter)) { - currentGroup = wo.group_formatter((currentGroup || '').toString(), column, table, c, wo) || currentGroup; - } - $rows.eq(rowIndex).before('<tr class="group-header ' + c.selectorRemove.slice(1) + - '" unselectable="on"' + ( c.tabIndex ? ' tabindex="0"' : '' ) + '><td colspan="' + - c.columns + '">' + (wo.group_collapsible ? '<i/>' : '') + '<span class="group-name">' + - currentGroup + '</span><span class="group-count"></span></td></tr>'); - if (wo.group_saveGroups && !savedGroup && wo.group_collapsed && wo.group_collapsible) { - // all groups start collapsed - wo.group_currentGroups[wo.group_currentGroup].push(currentGroup); + for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++) { + norm_rows = c.cache[tbodyIndex].normalized; + group = ''; // clear grouping across tbodies + $rows = c.$tbodies.eq(tbodyIndex).children('tr').not('.' + c.cssChildRow); + for (rowIndex = 0; rowIndex < $rows.length; rowIndex++) { + if ( $rows.eq(rowIndex).is(':visible') ) { + // fixes #438 + if (ts.grouping.types[grouping[1]]) { + currentGroup = norm_rows[rowIndex] ? + ts.grouping.types[grouping[1]]( c, c.$headerIndexed[column], norm_rows[rowIndex][column], /date/.test(groupClass) ? + grouping[2] : parseInt(grouping[2] || 1, 10) || 1, group, lang ) : currentGroup; + if (group !== currentGroup) { + group = currentGroup; + // show range if number > 1 + if (grouping[1] === 'number' && grouping[2] > 1 && currentGroup !== '') { + currentGroup += ' - ' + (parseInt(currentGroup, 10) + + ((parseInt(grouping[2], 10) - 1) * (c.$headerIndexed[column].hasClass(ts.css.sortAsc) ? 1 : -1))); + } + if ($.isFunction(wo.group_formatter)) { + currentGroup = wo.group_formatter((currentGroup || '').toString(), column, table, c, wo) || currentGroup; + } + $rows.eq(rowIndex).before('<tr class="group-header ' + c.selectorRemove.slice(1) + + '" unselectable="on"' + ( c.tabIndex ? ' tabindex="0"' : '' ) + '><td colspan="' + + c.columns + '">' + (wo.group_collapsible ? '<i/>' : '') + '<span class="group-name">' + + currentGroup + '</span><span class="group-count"></span></td></tr>'); + if (wo.group_saveGroups && !savedGroup && wo.group_collapsed && wo.group_collapsible) { + // all groups start collapsed + wo.group_currentGroups[wo.group_currentGroup].push(currentGroup); + } } } } } } - } - c.$table.find('tr.group-header') - .bind('selectstart', false) - .each(function(){ - var isHidden, $label, name, - $row = $(this), - $rows = $row.nextUntil('tr.group-header').filter(':visible'); - if (wo.group_count || $.isFunction(wo.group_callback)) { - $label = $row.find('.group-count'); - if ($label.length) { - if (wo.group_count) { - $label.html( wo.group_count.replace(/\{num\}/g, $rows.length) ); - } - if ($.isFunction(wo.group_callback)) { - wo.group_callback($row.find('td'), $rows, column, table); + c.$table.find('tr.group-header') + .bind('selectstart', false) + .each(function(){ + var isHidden, $label, name, + $row = $(this), + $rows = $row.nextUntil('tr.group-header').filter(':visible'); + if (wo.group_count || $.isFunction(wo.group_callback)) { + $label = $row.find('.group-count'); + if ($label.length) { + if (wo.group_count) { + $label.html( wo.group_count.replace(/\{num\}/g, $rows.length) ); + } + if ($.isFunction(wo.group_callback)) { + wo.group_callback($row.find('td'), $rows, column, table); + } } } - } - if (wo.group_saveGroups && wo.group_currentGroups.length && wo.group_currentGroups[wo.group_currentGroup].length) { - name = $row.find('.group-name').text().toLowerCase(); - isHidden = $.inArray( name, wo.group_currentGroups[wo.group_currentGroup] ) > -1; - $row.toggleClass('collapsed', isHidden); - $rows.toggleClass('group-hidden', isHidden); - } else if (wo.group_collapsed && wo.group_collapsible) { - $row.addClass('collapsed'); - $rows.addClass('group-hidden'); - } - }); - c.$table.trigger(wo.group_complete); - } - }, + if (wo.group_saveGroups && wo.group_currentGroups.length && wo.group_currentGroups[wo.group_currentGroup].length) { + name = $row.find('.group-name').text().toLowerCase(); + isHidden = $.inArray( name, wo.group_currentGroups[wo.group_currentGroup] ) > -1; + $row.toggleClass('collapsed', isHidden); + $rows.toggleClass('group-hidden', isHidden); + } else if (wo.group_collapsed && wo.group_collapsible) { + $row.addClass('collapsed'); + $rows.addClass('group-hidden'); + } + }); + c.$table.trigger(wo.group_complete); + } + }, - bindEvents : function(table, c, wo){ - if (wo.group_collapsible) { - wo.group_currentGroups = []; - // .on() requires jQuery 1.7+ - c.$table.on('click toggleGroup keyup', 'tr.group-header', function(event){ - event.stopPropagation(); - // pressing enter will toggle the group - if (event.type === 'keyup' && event.which !== 13) { return; } - var isCollapsed, $groups, indx, - $this = $(this), - name = $this.find('.group-name').text().toLowerCase(); - // use shift-click to toggle ALL groups - if (event.shiftKey && (event.type === 'click' || event.type ==='keyup')) { - $this.siblings('.group-header').trigger('toggleGroup'); - } - $this.toggleClass('collapsed'); - // nextUntil requires jQuery 1.4+ - $this.nextUntil('tr.group-header').toggleClass('group-hidden', $this.hasClass('collapsed') ); - // save collapsed groups - if (wo.group_saveGroups && ts.storage) { - $groups = c.$table.find('.group-header'); - isCollapsed = $this.hasClass('collapsed'); - if (!wo.group_currentGroups[wo.group_currentGroup]) { - wo.group_currentGroups[wo.group_currentGroup] = []; + bindEvents : function(table, c, wo){ + if (wo.group_collapsible) { + wo.group_currentGroups = []; + // .on() requires jQuery 1.7+ + c.$table.on('click toggleGroup keyup', 'tr.group-header', function(event){ + event.stopPropagation(); + // pressing enter will toggle the group + if (event.type === 'keyup' && event.which !== 13) { return; } + var isCollapsed, $groups, indx, + $this = $(this), + name = $this.find('.group-name').text().toLowerCase(); + // use shift-click to toggle ALL groups + if (event.shiftKey && (event.type === 'click' || event.type === 'keyup')) { + $this.siblings('.group-header').trigger('toggleGroup'); } - if (isCollapsed && wo.group_currentGroup) { - wo.group_currentGroups[wo.group_currentGroup].push( name ); - } else if (wo.group_currentGroup) { - indx = $.inArray( name, wo.group_currentGroups[wo.group_currentGroup] ); - if (indx > -1) { - wo.group_currentGroups[wo.group_currentGroup].splice( indx, 1 ); + $this.toggleClass('collapsed'); + // nextUntil requires jQuery 1.4+ + $this.nextUntil('tr.group-header').toggleClass('group-hidden', $this.hasClass('collapsed') ); + // save collapsed groups + if (wo.group_saveGroups && ts.storage) { + $groups = c.$table.find('.group-header'); + isCollapsed = $this.hasClass('collapsed'); + if (!wo.group_currentGroups[wo.group_currentGroup]) { + wo.group_currentGroups[wo.group_currentGroup] = []; } + if (isCollapsed && wo.group_currentGroup) { + wo.group_currentGroups[wo.group_currentGroup].push( name ); + } else if (wo.group_currentGroup) { + indx = $.inArray( name, wo.group_currentGroups[wo.group_currentGroup] ); + if (indx > -1) { + wo.group_currentGroups[wo.group_currentGroup].splice( indx, 1 ); + } + } + ts.storage( table, 'tablesorter-groups', wo.group_currentGroups ); } - ts.storage( table, 'tablesorter-groups', wo.group_currentGroups ); - } + }); + } + $(wo.group_saveReset).on('click', function(){ + ts.grouping.clearSavedGroups(table); }); - } - $(wo.group_saveReset).on('click', function(){ - ts.grouping.clearSavedGroups(table); - }); - c.$table.on('pagerChange.tsgrouping', function(){ - ts.grouping.update(table, c, wo); - }); - }, + c.$table.on('pagerChange.tsgrouping', function(){ + ts.grouping.update(table, c, wo); + }); + }, - clearSavedGroups: function(table){ - if (table && ts.storage) { - ts.storage(table, 'tablesorter-groups', ''); - ts.grouping.update(table, table.config, table.config.widgetOptions); + clearSavedGroups: function(table){ + if (table && ts.storage) { + ts.storage(table, 'tablesorter-groups', ''); + ts.grouping.update(table, table.config, table.config.widgetOptions); + } } - } -}; + }; -ts.addWidget({ - id: 'group', - priority: 100, - options: { - group_collapsible : true, // make the group header clickable and collapse the rows below it. - group_collapsed : false, // start with all groups collapsed - group_saveGroups : true, // remember collapsed groups - group_saveReset : null, // element to clear saved collapsed groups - group_count : ' ({num})', // if not false, the "{num}" string is replaced with the number of rows in the group - group_separator : '-', // group name separator; used when group-separator-# class is used. - group_formatter : null, // function(txt, column, table, c, wo) { return txt; } - group_callback : null, // function($cell, $rows, column, table){}, callback allowing modification of the group header labels - group_complete : 'groupingComplete', // event triggered on the table when the grouping widget has finished work + ts.addWidget({ + id: 'group', + priority: 100, + options: { + group_collapsible : true, // make the group header clickable and collapse the rows below it. + group_collapsed : false, // start with all groups collapsed + group_saveGroups : true, // remember collapsed groups + group_saveReset : null, // element to clear saved collapsed groups + group_count : ' ({num})', // if not false, the '{num}' string is replaced with the number of rows in the group + group_separator : '-', // group name separator; used when group-separator-# class is used. + group_formatter : null, // function(txt, column, table, c, wo) { return txt; } + group_callback : null, // function($cell, $rows, column, table){}, callback allowing modification of the group header labels + group_complete : 'groupingComplete', // event triggered on the table when the grouping widget has finished work - // checkbox parser text used for checked/unchecked values - group_checkbox : [ 'checked', 'unchecked' ], - // change these default date names based on your language preferences - group_months : [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ], - group_week : [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ], - group_time : [ 'AM', 'PM' ], - // this function is used when "group-date" is set to create the date string - // you can just return date, date.toLocaleString(), date.toLocaleDateString() or d.toLocaleTimeString() - // reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#Conversion_getter - group_dateString : function(date) { return date.toLocaleString(); } - }, - init: function(table, thisWidget, c, wo){ - ts.grouping.bindEvents(table, c, wo); - }, - format: function(table, c, wo) { - ts.grouping.update(table, c, wo); - }, - remove : function(table, c, wo){ - c.$table - .off('click', 'tr.group-header') - .off('pagerChange.tsgrouping') - .find('.group-hidden').removeClass('group-hidden').end() - .find('tr.group-header').remove(); - } -}); + // checkbox parser text used for checked/unchecked values + group_checkbox : [ 'checked', 'unchecked' ], + // change these default date names based on your language preferences + group_months : [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ], + group_week : [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ], + group_time : [ 'AM', 'PM' ], + // this function is used when 'group-date' is set to create the date string + // you can just return date, date.toLocaleString(), date.toLocaleDateString() or d.toLocaleTimeString() + // reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#Conversion_getter + group_dateString : function(date) { return date.toLocaleString(); } + }, + init: function(table, thisWidget, c, wo){ + ts.grouping.bindEvents(table, c, wo); + }, + format: function(table, c, wo) { + ts.grouping.update(table, c, wo); + }, + remove : function(table, c, wo){ + c.$table + .off('click', 'tr.group-header') + .off('pagerChange.tsgrouping') + .find('.group-hidden').removeClass('group-hidden').end() + .find('tr.group-header').remove(); + } + }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js index 8f4f591..dfa738b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js @@ -5,14 +5,14 @@ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ ;(function($){ -"use strict"; -var ts = $.tablesorter; + 'use strict'; + var ts = $.tablesorter; ts.addWidget({ id: 'headerTitles', options: { // use aria-label text - // e.g. "First Name: Ascending sort applied, activate to apply a descending sort" + // e.g. 'First Name: Ascending sort applied, activate to apply a descending sort' headerTitle_useAria : false, // add tooltip class headerTitle_tooltip : '', diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index 8ac8b49..180188b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -1,180 +1,220 @@ -/*! Widget: math - updated 5/17/2015 (v2.22.0) *//* +/*! Widget: math - updated 7/28/2015 (v2.22.4) *//* * Requires tablesorter v2.16+ and jQuery 1.7+ * by Rob Garrison */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ -;(function($) { - "use strict"; +;( function( $ ) { + 'use strict'; var ts = $.tablesorter, math = { + error: { + 0 : 'Infinity result: Divide by zero', + 1 : 'Need more than one element to make this calculation', + 'undef' : 'No elements found' + }, + + // value returned when calculation is not possible, e.g. no values, dividing by zero, etc. + invalid : function( name, errorIndex ) { + // name = function returning invalid results + // errorIndex = math.error index with an explanation of the error + console.log( name, math.error[ errorIndex ] ); + return 'none'; // text for cell + }, + events : ( 'tablesorter-initialized update updateAll updateRows addRows updateCell ' + 'filterReset filterEnd ' ).split(' ').join('.tsmath '), + processText : function( c, $cell ) { + var txt = $cell.attr( c.textAttribute ); + if ( typeof txt === 'undefined' ) { + txt = $cell[0].textContent || $cell.text(); + } + txt = ts.formatFloat( txt.replace( /[^\w,. \-()]/g, '' ), c.table ) || 0; + // isNaN('') => false + return isNaN( txt ) ? 0 : txt; + }, + // get all of the row numerical values in an arry - getRow : function(table, wo, $el, dataAttrib) { + getRow : function( c, $el ) { var $t, txt, - c = table.config, + wo = c.widgetOptions, arry = [], - $row = $el.closest('tr'), - $cells = $row.children().not('[' + dataAttrib + '=ignore]'); - if (!$row.hasClass(wo.filter_filteredRow || 'filtered')) { - if (wo.math_ignore.length) { - $cells = $cells.not('[data-column=' + wo.math_ignore.join('],[data-column=') + ']'); + $row = $el.closest( 'tr' ), + $cells = $row.children().not( '[' + wo.math_dataAttrib + '=ignore]' ); + if ( !$row.hasClass( wo.filter_filteredRow || 'filtered' ) ) { + if ( wo.math_ignore.length ) { + $cells = $cells.not( '[data-column=' + wo.math_ignore.join( '],[data-column=' ) + ']' ); } - arry = $cells.not($el).map(function(){ - $t = $(this); - txt = $t.attr(c.textAttribute); - if (typeof txt === "undefined") { - txt = this.textContent || $t.text(); - } - txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table) || 0; - return isNaN(txt) ? 0 : txt; + arry = $cells.not( $el ).map( function() { + return math.processText( c, $( this ) ); }).get(); } return arry; }, // get all of the column numerical values in an arry - getColumn : function(table, wo, $el, type, dataAttrib) { - var i, txt, $t, len, mathAbove, + getColumn : function( c, $el, type ) { + var index, txt, $t, len, $mathRows, mathAbove, arry = [], - c = table.config, + wo = c.widgetOptions, filtered = wo.filter_filteredRow || 'filtered', - cIndex = parseInt( $el.attr('data-column'), 10 ), - $rows = c.$table.children('tbody').children(), - $row = $el.closest('tr'); + cIndex = parseInt( $el.attr( 'data-column' ), 10 ), + $rows = c.$table.children( 'tbody' ).children(), + $row = $el.closest( 'tr' ); // make sure tfoot rows are AFTER the tbody rows - // $rows.add( c.$table.children('tfoot').children() ); - if (type === 'above') { - len = $rows.index($row); - i = len; - while (i >= 0) { - $t = $rows.eq(i).children().filter('[data-column=' + cIndex + ']'); - mathAbove = $t.filter('[' + dataAttrib + '^=above]').length; + // $rows.add( c.$table.children( 'tfoot' ).children() ); + if ( type === 'above' ) { + len = $rows.index( $row ); + index = len; + while ( index >= 0 ) { + $t = $rows.eq( index ).children().filter( '[data-column=' + cIndex + ']' ); + mathAbove = $t.filter( '[' + wo.math_dataAttrib + '^=above]' ).length; // ignore filtered rows & rows with data-math="ignore" (and starting row) - if ( ( !$rows.eq(i).hasClass(filtered) && $rows.eq(i).not('[' + dataAttrib + '=ignore]').length && i !== len ) || mathAbove && i !== len ) { - // stop calculating "above", when encountering another "above" - if (mathAbove) { - i = 0; - } else if ($t.length) { - txt = $t.attr(c.textAttribute); - if (typeof txt === "undefined") { - txt = $t[0].textContent || $t.text(); - } - txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table) || 0; - arry.push(isNaN(txt) ? 0 : txt); + if ( ( !$rows.eq( index ).hasClass( filtered ) && + $rows.eq( index ).not( '[' + wo.math_dataAttrib + '=ignore]' ).length && + index !== len ) || + mathAbove && index !== len ) { + // stop calculating 'above', when encountering another 'above' + if ( mathAbove ) { + index = 0; + } else if ( $t.length ) { + arry.push( math.processText( c, $t ) ); } } - i--; + index--; } } else { - $rows.each(function(){ - $t = $(this).children().filter('[data-column=' + cIndex + ']'); - if (!$(this).hasClass(filtered) && $t.not('[' + dataAttrib + '^=above],[' + dataAttrib + '^=col]').length && !$t.is($el)) { - txt = $t.attr(c.textAttribute); - if (typeof txt === "undefined") { - txt = ($t[0] ? $t[0].textContent : '') || $t.text(); - } - // isNaN('') => false - txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table) || 0; - arry.push(isNaN(txt) ? 0 : txt); + $mathRows = $rows.not( '[' + wo.math_dataAttrib + '=ignore]' ); // .each(function(){ + len = $mathRows.length; + for ( index = 0; index < len; index++ ) { + $t = $mathRows.eq( index ).children().filter( '[data-column=' + cIndex + ']' ); + if ( !$mathRows.eq( index ).hasClass( filtered ) && + $t.not( '[' + wo.math_dataAttrib + '^=above],[' + wo.math_dataAttrib + '^=col]' ).length && + !$t.is( $el ) ) { + arry.push( math.processText( c, $t ) ); } - }); + } } return arry; }, // get all of the column numerical values in an arry - getAll : function(table, wo, dataAttrib) { - var txt, $t, col, + getAll : function( c ) { + var txt, $t, col, $row, rowIndex, rowLen, $cells, cellIndex, cellLen, arry = [], - c = table.config, + wo = c.widgetOptions, filtered = wo.filter_filteredRow || 'filtered', - $rows = c.$table.children('tbody').children(); - $rows.each(function(){ - if (!$(this).hasClass(filtered)) { - $(this).children().each(function(){ - $t = $(this); - col = parseInt( $t.attr('data-column'), 10); - if (!$t.filter('[' + dataAttrib + ']').length && $.inArray(col, wo.math_ignore) < 0) { - txt = $t.attr(c.textAttribute); - if (typeof txt === "undefined") { - txt = ($t[0] ? $t[0].textContent : '') || $t.text(); - } - txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table) || 0; - arry.push(isNaN(txt) ? 0 : txt); + $rows = c.$table.children( 'tbody' ).children().not( '[' + wo.math_dataAttrib + '=ignore]' ); + rowLen = $rows.length; + for ( rowIndex = 0; rowIndex < rowLen; rowIndex++ ) { + $row = $rows.eq( rowIndex ); + if ( !$row.hasClass( filtered ) ) { + $cells = $row.children().not( '[' + wo.math_dataAttrib + '=ignore]' ); + cellLen = $cells.length; + // $row.children().each(function(){ + for ( cellIndex = 0; cellIndex < cellLen; cellIndex++ ) { + $t = $cells.eq( cellIndex ); + col = parseInt( $t.attr( 'data-column' ), 10); + if ( !$t.filter( '[' + wo.math_dataAttrib + ']' ).length && $.inArray( col, wo.math_ignore ) < 0 ) { + arry.push( math.processText( c, $t ) ); } - }); + } } - }); + } return arry; }, recalculate : function(table, c, wo, init) { - if (c && (!wo.math_isUpdating || init)) { + if ( c && ( !wo.math_isUpdating || init ) ) { // add data-column attributes to all table cells - if (init) { - ts.computeColumnIndex( c.$table.children('tbody').children() ); + if ( init ) { + ts.computeColumnIndex( c.$table.children( 'tbody' ).children() ); } // data-attribute name (defaults to data-math) - var dataAttrib = 'data-' + (wo.math_data || 'math'), + wo.math_dataAttrib = 'data-' + (wo.math_data || 'math'); // all non-info tbody cells - $mathCells = c.$tbodies.find('[' + dataAttrib + ']'); - math.mathType( table, wo, $mathCells, wo.math_priority, dataAttrib ); + var $mathCells = c.$tbodies.find( '[' + wo.math_dataAttrib + ']' ); + math.mathType( c, $mathCells, wo.math_priority ); // only info tbody cells - $mathCells = c.$table.find('.' + c.cssInfoBlock + ', tfoot').find('[' + dataAttrib + ']'); - math.mathType( table, wo, $mathCells, wo.math_priority, dataAttrib ); + $mathCells = c.$table + .children( '.' + c.cssInfoBlock + ', tfoot' ) + .find( '[' + wo.math_dataAttrib + ']' ); + math.mathType( c, $mathCells, wo.math_priority ); - // find the "all" total - math.mathType( table, wo, c.$table.find('[' + dataAttrib + '^=all]'), ['all'], dataAttrib ); + // find the 'all' total + $mathCells = c.$table.find( '[' + wo.math_dataAttrib + '^=all]' ); + math.mathType( c, $mathCells, [ 'all' ] ); wo.math_isUpdating = true; - c.$table.trigger('update'); + if ( c.debug ) { + console[ console.group ? 'group' : 'log' ]( 'Math widget triggering an update after recalculation' ); + } + c.$table.trigger( 'update' ); } }, - mathType : function(table, wo, $cells, priority, dataAttrib) { - if ($cells.length) { - var formula, t, $t, arry, getAll, - eq = ts.equations; - if (priority[0] === 'all') { + mathType : function( c, $cells, priority ) { + if ( $cells.length ) { + var formula, result, $el, arry, getAll, $targetCells, index, len, + wo = c.widgetOptions, + equations = ts.equations; + if ( priority[0] === 'all' ) { // no need to get all cells more than once - getAll = math.getAll(table, wo, dataAttrib); + getAll = math.getAll( c ); } - $.each( priority, function(i, type) { - $cells.filter('[' + dataAttrib + '^=' + type + ']').each(function(){ - $t = $(this); - formula = ($t.attr(dataAttrib) || '').replace(type + '-', ''); - arry = (type === "row") ? math.getRow(table, wo, $t, dataAttrib) : - (type === "all") ? getAll : math.getColumn(table, wo, $t, type, dataAttrib); - if (eq[formula]) { - t = eq[formula](arry); - if (table.config.debug && console && console.log) { - console.log($t.attr(dataAttrib), arry, '=', t); + if (c.debug) { + console[ console.group ? 'group' : 'log' ]( 'Tablesorter Math widget recalculation' ); + } + // $.each is okay here... only 3 priorities + $.each( priority, function( i, type ) { + $targetCells = $cells.filter( '[' + wo.math_dataAttrib + '^=' + type + ']' ); + len = $targetCells.length; + if ( len ) { + if (c.debug) { + console[ console.group ? 'group' : 'log' ]( type ); + } + for ( index = 0; index < len; index++ ) { + $el = $targetCells.eq( index ); + formula = ( $el.attr( wo.math_dataAttrib ) || '' ).replace( type + '-', '' ); + arry = ( type === 'row' ) ? math.getRow( c, $el ) : + ( type === 'all' ) ? getAll : math.getColumn( c, $el, type ); + if ( equations[ formula ] ) { + if ( arry.length ) { + result = equations[ formula ]( arry ); + if ( c.debug ) { + console.log( $el.attr( wo.math_dataAttrib ), arry, '=', result ); + } + } else { + // mean will return a divide by zero error, everything else shows an undefined error + result = math.invalid( formula, formula === 'mean' ? 0 : 'undef' ); + } + math.output( $el, wo, result, arry ); } - math.output( $t, wo, t, arry ); } - }); + if ( c.debug && console.groupEnd ) { console.groupEnd(); } + } }); + if ( c.debug && console.groupEnd ) { console.groupEnd(); } } }, - output : function($cell, wo, value, arry) { + output : function( $cell, wo, value, arry ) { // get mask from cell data-attribute: data-math-mask="#,##0.00" - var result = ts.formatMask( $cell.attr('data-' + wo.math_data + '-mask') || wo.math_mask, value, wo.math_wrapPrefix, wo.math_wrapSuffix ); - if ($.isFunction(wo.math_complete)) { - result = wo.math_complete($cell, wo, result, value, arry); + var mask = $cell.attr( 'data-' + wo.math_data + '-mask' ) || wo.math_mask, + result = ts.formatMask( mask, value, wo.math_wrapPrefix, wo.math_wrapSuffix ); + if ( typeof wo.math_complete === 'function' ) { + result = wo.math_complete( $cell, wo, result, value, arry ); } - if (result !== false) { - $cell.html(result); + if ( result !== false ) { + $cell.html( result ); } } @@ -188,146 +228,153 @@ * (c)2011 ecava * Dual licensed under the MIT or GPL Version 2 licenses. */ - ts.formatMask = function(m, v, tmpPrefix, tmpSuffix) { - if ( !m || isNaN(+v) ) { - return v; // return as it is. + ts.formatMask = function( mask, val, tmpPrefix, tmpSuffix ) { + if ( !mask || isNaN( +val ) ) { + return val; // return as it is. } var isNegative, result, decimal, group, posLeadZero, posTrailZero, posSeparator, part, szSep, - integer, str, offset, i, l, len, start, tmp, end, inv, - prefix = '', - suffix = ''; - - // find prefix/suffix - len = m.length; - start = m.search( /[0-9\-\+#]/ ); - tmp = start > 0 ? m.substring(0, start) : ''; - prefix = tmp; + integer, str, offset, index, end, inv, + suffix = '', + + // find prefix/suffix + len = mask.length, + start = mask.search( /[0-9\-\+#]/ ), + tmp = start > 0 ? mask.substring( 0, start ) : '', + prefix = tmp; + if ( start > 0 && tmpPrefix ) { - if ( /\{content\}/.test(tmpPrefix || '') ) { - prefix = (tmpPrefix || '').replace(/\{content\}/g, tmp || ''); + if ( /\{content\}/.test( tmpPrefix || '' ) ) { + prefix = ( tmpPrefix || '' ).replace( /\{content\}/g, tmp || '' ); } else { - prefix = (tmpPrefix || '') + tmp; + prefix = ( tmpPrefix || '' ) + tmp; } } // reverse string: not an ideal method if there are surrogate pairs - inv = m.split('').reverse().join(''); + inv = mask.split( '' ).reverse().join( '' ); end = inv.search( /[0-9\-\+#]/ ); - i = len - end; - i += (m.substring( i, i + 1 ) === '.') ? 1 : 0; - tmp = end > 0 ? m.substring( i, len) : ''; + index = len - end; + index += ( mask.substring( index, index + 1 ) === '.' ) ? 1 : 0; + tmp = end > 0 ? mask.substring( index, len ) : ''; suffix = tmp; - if (tmp !== '' && tmpSuffix) { - if ( /\{content\}/.test(tmpSuffix || '') ) { - suffix = (tmpSuffix || '').replace(/\{content\}/g, tmp || ''); + if ( tmp !== '' && tmpSuffix ) { + if ( /\{content\}/.test( tmpSuffix || '' ) ) { + suffix = ( tmpSuffix || '' ).replace( /\{content\}/g, tmp || '' ); } else { - suffix = tmp + (tmpSuffix || ''); + suffix = tmp + ( tmpSuffix || '' ); } } - m = m.substring(start, i); + mask = mask.substring( start, index ); // convert any string to number according to formation sign. - v = m.charAt(0) == '-' ? -v : +v; - isNegative = v < 0 ? v = -v : 0; // process only abs(), and turn on flag. + val = mask.charAt( 0 ) == '-' ? -val : +val; + isNegative = val < 0 ? val = -val : 0; // process only abs(), and turn on flag. // search for separator for grp & decimal, anything not digit, not +/- sign, not #. - result = m.match( /[^\d\-\+#]/g ); - decimal = ( result && result[result.length-1] ) || '.'; // treat the right most symbol as decimal - group = ( result && result[1] && result[0] ) || ','; // treat the left most symbol as group separator + result = mask.match( /[^\d\-\+#]/g ); + decimal = ( result && result[ result.length - 1 ] ) || '.'; // treat the right most symbol as decimal + group = ( result && result[ 1 ] && result[ 0 ] ) || ','; // treat the left most symbol as group separator // split the decimal for the format string if any. - m = m.split( decimal ); + mask = mask.split( decimal ); // Fix the decimal first, toFixed will auto fill trailing zero. - v = v.toFixed( m[1] && m[1].length ); - v = +(v) + ''; // convert number to string to trim off *all* trailing decimal zero(es) + val = val.toFixed( mask[ 1 ] && mask[ 1 ].length ); + val = +( val ) + ''; // convert number to string to trim off *all* trailing decimal zero(es) // fill back any trailing zero according to format - posTrailZero = m[1] && m[1].lastIndexOf('0'); // look for last zero in format - part = v.split('.'); + posTrailZero = mask[ 1 ] && mask[ 1 ].lastIndexOf( '0' ); // look for last zero in format + part = val.split( '.' ); // integer will get !part[1] - if ( !part[1] || ( part[1] && part[1].length <= posTrailZero ) ) { - v = (+v).toFixed( posTrailZero + 1 ); + if ( !part[ 1 ] || ( part[ 1 ] && part[ 1 ].length <= posTrailZero ) ) { + val = ( +val ).toFixed( posTrailZero + 1 ); } - szSep = m[0].split( group ); // look for separator - m[0] = szSep.join(''); // join back without separator for counting the pos of any leading 0. + szSep = mask[ 0 ].split( group ); // look for separator + mask[ 0 ] = szSep.join( '' ); // join back without separator for counting the pos of any leading 0. - posLeadZero = m[0] && m[0].indexOf('0'); + posLeadZero = mask[ 0 ] && mask[ 0 ].indexOf( '0' ); if ( posLeadZero > -1 ) { - while ( part[0].length < ( m[0].length - posLeadZero ) ) { - part[0] = '0' + part[0]; + while ( part[ 0 ].length < ( mask[ 0 ].length - posLeadZero ) ) { + part[ 0 ] = '0' + part[ 0 ]; } - } else if ( +part[0] === 0 ) { - part[0] = ''; + } else if ( +part[ 0 ] === 0 ) { + part[ 0 ] = ''; } - v = v.split('.'); - v[0] = part[0]; + val = val.split( '.' ); + val[ 0 ] = part[ 0 ]; // process the first group separator from decimal (.) only, the rest ignore. // get the length of the last slice of split result. - posSeparator = ( szSep[1] && szSep[ szSep.length - 1 ].length ); + posSeparator = ( szSep[ 1 ] && szSep[ szSep.length - 1 ].length ); if ( posSeparator ) { - integer = v[0]; + integer = val[ 0 ]; str = ''; offset = integer.length % posSeparator; - l = integer.length; - for ( i = 0; i < l; i++ ) { - str += integer.charAt(i); // ie6 only support charAt for sz. + len = integer.length; + for ( index = 0; index < len; index++ ) { + str += integer.charAt( index ); // ie6 only support charAt for sz. // -posSeparator so that won't trail separator on full length /*jshint -W018 */ - if ( !( ( i - offset + 1 ) % posSeparator ) && i < l - posSeparator ) { + if ( !( ( index - offset + 1 ) % posSeparator ) && index < l - posSeparator ) { str += group; } } - v[0] = str; + val[ 0 ] = str; } - v[1] = ( m[1] && v[1] ) ? decimal + v[1] : ""; + val[ 1 ] = ( mask[ 1 ] && val[ 1 ] ) ? decimal + val[ 1 ] : ''; // put back any negation, combine integer and fraction, and add back prefix & suffix - return prefix + ( ( isNegative ? '-' : '' ) + v[0] + v[1] ) + suffix; + return prefix + ( ( isNegative ? '-' : '' ) + val[ 0 ] + val[ 1 ] ) + suffix; }; ts.equations = { - count : function(arry) { + count : function( arry ) { return arry.length; }, - sum : function(arry) { - var total = 0; - $.each( arry, function(i) { - total += arry[i]; - }); + sum : function( arry ) { + var index, + len = arry.length, + total = 0; + for ( index = 0; index < len; index++ ) { + total += arry[ index ]; + } return total; }, - mean : function(arry) { + mean : function( arry ) { var total = ts.equations.sum( arry ); return total / arry.length; }, - median : function(arry) { - // https://gist.github.com/caseyjustus/1166258 - arry.sort( function(a,b){ return a - b; } ); - var half = Math.floor( arry.length / 2 ); - return (arry.length % 2) ? arry[half] : ( arry[half - 1] + arry[half] ) / 2.0; + median : function( arry ) { + var half, + len = arry.length; + if ( len > 1 ) { + // https://gist.github.com/caseyjustus/1166258 + arry.sort( function( a, b ){ return a - b; } ); + half = Math.floor( len / 2 ); + return ( len % 2 ) ? arry[ half ] : ( arry[ half - 1 ] + arry[ half ] ) / 2; + } + return math.invalid( 'median', 1 ); }, - mode : function(arry) { + mode : function( arry ) { // http://stackoverflow.com/a/3451640/145346 - if ( arry.length === 0 ) { return 'none'; } - var i, el, + var index, el, m, modeMap = {}, maxCount = 1, - modes = [arry[0]]; - for (i = 0; i < arry.length; i++) { - el = arry[i]; - modeMap[el] = modeMap[el] ? modeMap[el] + 1 : 1; - if ( modeMap[el] > maxCount ) { - modes = [el]; - maxCount = modeMap[el]; - } else if (modeMap[el] === maxCount) { - modes.push(el); - maxCount = modeMap[el]; + modes = [ arry[ 0 ] ]; + for ( index = 0; index < arry.length; index++ ) { + el = arry[ index ]; + modeMap[ el ] = modeMap[ el ] ? modeMap[ el ] + 1 : 1; + m = modeMap[ el ]; + if ( m > maxCount ) { + modes = [ el ]; + maxCount = m; + } else if ( m === maxCount ) { + modes.push( el ); + maxCount = m; } } // returns arry of modes if there is a tie - return modes.sort( function(a,b){ return a - b; } ); + return modes.sort( function( a, b ){ return a - b; } ); }, max : function(arry) { return Math.max.apply( Math, arry ); @@ -336,37 +383,42 @@ return Math.min.apply( Math, arry ); }, range: function(arry) { - var v = arry.sort(function(a,b){ return a - b; }); - return v[ arry.length - 1 ] - v[0]; + var v = arry.sort( function( a, b ){ return a - b; } ); + return v[ arry.length - 1 ] - v[ 0 ]; }, // common variance equation // (not accessible via data-attribute setting) - variance: function(arry, population) { - var avg = ts.equations.mean( arry ), + variance: function( arry, population ) { + var divisor, + avg = ts.equations.mean( arry ), v = 0, i = arry.length; - while (i--) { - v += Math.pow( ( arry[i] - avg ), 2 ); + while ( i-- ) { + v += Math.pow( ( arry[ i ] - avg ), 2 ); + } + divisor = ( arry.length - ( population ? 0 : 1 ) ); + if ( divisor === 0 ) { + return math.invalid( 'variance', 0 ); } - v /= ( arry.length - (population ? 0 : 1) ); + v /= divisor; return v; }, // variance (population) - varp : function(arry) { - return ts.equations.variance(arry, true); + varp : function( arry ) { + return ts.equations.variance( arry, true ); }, // variance (sample) - vars : function(arry) { - return ts.equations.variance(arry); + vars : function( arry ) { + return ts.equations.variance( arry ); }, // standard deviation (sample) - stdevs : function(arry) { - var vars = ts.equations.variance(arry); + stdevs : function( arry ) { + var vars = ts.equations.variance( arry ); return Math.sqrt( vars ); }, // standard deviation (population) - stdevp : function(arry) { - var varp = ts.equations.variance(arry, true); + stdevp : function( arry ) { + var varp = ts.equations.variance( arry, true ); return Math.sqrt( varp ); } }; @@ -374,7 +426,7 @@ // add new widget called repeatHeaders // ************************************ ts.addWidget({ - id: "math", + id: 'math', priority: 100, options: { math_data : 'math', @@ -384,7 +436,7 @@ math_mask : '#,##0.00', // complete executed after each fucntion math_complete : null, // function($cell, wo, result, value, arry){ return result; }, - // order of calculation; "all" is last + // order of calculation; 'all' is last math_priority : [ 'row', 'above', 'col' ], // template for or just prepend the mask prefix & suffix with this HTML // e.g. '<span class="red">{content}</span>' @@ -392,33 +444,36 @@ math_suffix : '', math_event : 'recalculate' }, - init : function(table, thisWidget, c, wo) { + init : function( table, thisWidget, c, wo ) { + // filterEnd fires after updateComplete + var update = ts.hasWidget( table, 'filter' ) ? 'filterEnd' : 'updateComplete'; c.$table - .off( (math.events + ' updateComplete.tsmath ' + wo.math_event).replace(/\s+/g, ' ') ) - .on(math.events + ' ' + wo.math_event, function(e) { + .off( ( math.events + ' updateComplete.tsmath ' + wo.math_event ).replace( /\s+/g, ' ' ) ) + .on( math.events + ' ' + wo.math_event, function( e ) { var init = e.type === 'tablesorter-initialized'; if ( !wo.math_isUpdating || init ) { - if ( !/filter/.test(e.type) ) { + if ( !/filter/.test( e.type ) ) { // redo data-column indexes on update ts.computeColumnIndex( c.$table.children('tbody').children() ); } math.recalculate( table, c, wo, init ); } }) - .on('updateComplete.tsmath', function(){ - setTimeout(function(){ + .on( update + '.tsmath', function() { + setTimeout( function(){ + if ( wo.math_isUpdating && c.debug && console.groupEnd ) { console.groupEnd(); } wo.math_isUpdating = false; - }, 20); + }, 40 ); }); wo.math_isUpdating = false; }, // this remove function is called when using the refreshWidgets method or when destroying the tablesorter plugin // this function only applies to tablesorter v2.4+ - remove: function(table, c, wo, refreshing) { - if (refreshing) { return; } - $(table) - .off( (math.events + ' updateComplete.tsmath ' + wo.math_event).replace(/\s+/g, ' ') ) - .find('[data-' + wo.math_data + ']').empty(); + remove: function( table, c, wo, refreshing ) { + if ( refreshing ) { return; } + c.$table + .off( ( math.events + ' updateComplete.tsmath ' + wo.math_event ).replace( /\s+/g, ' ' ) ) + .find( '[data-' + wo.math_data + ']' ).empty(); } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index 9415afb..7fcd324 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -1,4 +1,4 @@ -/*! Widget: output - updated 5/17/2015 (v2.22.0) *//* +/*! Widget: output - updated 7/28/2015 (v2.22.4) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * Modified from: * HTML Table to CSV: http://www.kunalbabre.com/projects/table2CSV.php (License unknown?) @@ -7,352 +7,371 @@ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ ;(function($){ -"use strict"; - -var ts = $.tablesorter, - -output = ts.output = { - - event : 'outputTable', - - // wrap line breaks & tabs in quotes - regexQuote : /([\n\t\x09\x0d\x0a]|<[^<]+>)/, // test if cell needs wrapping quotes - regexBR : /(<br([\s\/])?>|\n)/g, // replace - regexIMG : /<img[^>]+alt\s*=\s*['"]([^'"]+)['"][^>]*>/i, // match - regexHTML : /<[^<]+>/g, // replace - - replaceCR : '\x0d\x0a', - replaceTab : '\x09', - - popupTitle : 'Output', - popupStyle : 'width:100%;height:100%;', // for textarea - message : 'Your device does not support downloading. Please try again in desktop browser.', - - init : function(c) { - c.$table - .off(output.event) - .on(output.event, function( e ) { - e.stopPropagation(); - // explicitly use table.config.widgetOptions because we want - // the most up-to-date values; not the "wo" from initialization - output.process(c, c.widgetOptions); - }); - }, - - processRow: function(c, $rows, isHeader, isJSON) { - var $cell, $cells, cellsLen, rowIndex, row, col, indx, rowspanLen, colspanLen, txt, - wo = c.widgetOptions, - tmpRow = [], - dupe = wo.output_duplicateSpans, - addSpanIndex = isHeader && isJSON && wo.output_headerRows && $.isFunction(wo.output_callbackJSON), - cellIndex = 0, - rowsLength = $rows.length; - - for ( rowIndex = 0; rowIndex < rowsLength; rowIndex++ ) { - if (!tmpRow[rowIndex]) { tmpRow[rowIndex] = []; } - cellIndex = 0; - $cells = $rows.eq( rowIndex ).children(); - cellsLen = $cells.length; - for ( indx = 0; indx < cellsLen; indx++ ) { - $cell = $cells.eq( indx ); - // process rowspans - if ($cell.filter('[rowspan]').length) { - rowspanLen = parseInt( $cell.attr('rowspan'), 10) - 1; - txt = output.formatData( wo, $cell, isHeader ); - for (row = 1; row <= rowspanLen; row++) { - if (!tmpRow[rowIndex + row]) { tmpRow[rowIndex + row] = []; } - tmpRow[rowIndex + row][cellIndex] = isHeader ? txt : dupe ? txt : ''; + 'use strict'; + + var ts = $.tablesorter, + + output = ts.output = { + + event : 'outputTable', + + // wrap line breaks & tabs in quotes + regexQuote : /([\n\t\x09\x0d\x0a]|<[^<]+>)/, // test if cell needs wrapping quotes + regexBR : /(<br([\s\/])?>|\n)/g, // replace + regexIMG : /<img[^>]+alt\s*=\s*['"]([^'"]+)['"][^>]*>/i, // match + regexHTML : /<[^<]+>/g, // replace + + replaceCR : '\x0d\x0a', + replaceTab : '\x09', + + popupTitle : 'Output', + popupStyle : 'width:100%;height:100%;', // for textarea + message : 'Your device does not support downloading. Please try again in desktop browser.', + + init : function(c) { + c.$table + .off(output.event) + .on(output.event, function( e ) { + e.stopPropagation(); + // explicitly use table.config.widgetOptions because we want + // the most up-to-date values; not the 'wo' from initialization + output.process(c, c.widgetOptions); + }); + }, + + processRow: function(c, $rows, isHeader, isJSON) { + var $cell, $cells, cellsLen, rowIndex, row, col, indx, rowspanLen, colspanLen, txt, + wo = c.widgetOptions, + tmpRow = [], + dupe = wo.output_duplicateSpans, + addSpanIndex = isHeader && isJSON && wo.output_headerRows && $.isFunction(wo.output_callbackJSON), + cellIndex = 0, + rowsLength = $rows.length; + + for ( rowIndex = 0; rowIndex < rowsLength; rowIndex++ ) { + if (!tmpRow[rowIndex]) { tmpRow[rowIndex] = []; } + cellIndex = 0; + $cells = $rows.eq( rowIndex ).children(); + cellsLen = $cells.length; + for ( indx = 0; indx < cellsLen; indx++ ) { + $cell = $cells.eq( indx ); + // process rowspans + if ($cell.filter('[rowspan]').length) { + rowspanLen = parseInt( $cell.attr('rowspan'), 10) - 1; + txt = output.formatData( c, wo, $cell, isHeader ); + for (row = 1; row <= rowspanLen; row++) { + if (!tmpRow[rowIndex + row]) { tmpRow[rowIndex + row] = []; } + tmpRow[rowIndex + row][cellIndex] = isHeader ? txt : dupe ? txt : ''; + } } - } - // process colspans - if ($cell.filter('[colspan]').length) { - colspanLen = parseInt( $cell.attr('colspan'), 10) - 1; - // allow data-attribute to be an empty string - txt = output.formatData( wo, $cell, isHeader ); - for (col = 1; col <= colspanLen; col++) { - // if we're processing the header & making JSON, the header names need to be unique - if ($cell.filter('[rowspan]').length) { - rowspanLen = parseInt( $cell.attr('rowspan'), 10); - for (row = 0; row < rowspanLen; row++) { - if (!tmpRow[rowIndex + row]) { tmpRow[rowIndex + row] = []; } - tmpRow[rowIndex + row][cellIndex + col] = addSpanIndex ? + // process colspans + if ($cell.filter('[colspan]').length) { + colspanLen = parseInt( $cell.attr('colspan'), 10) - 1; + // allow data-attribute to be an empty string + txt = output.formatData( c, wo, $cell, isHeader ); + for (col = 1; col <= colspanLen; col++) { + // if we're processing the header & making JSON, the header names need to be unique + if ($cell.filter('[rowspan]').length) { + rowspanLen = parseInt( $cell.attr('rowspan'), 10); + for (row = 0; row < rowspanLen; row++) { + if (!tmpRow[rowIndex + row]) { tmpRow[rowIndex + row] = []; } + tmpRow[rowIndex + row][cellIndex + col] = addSpanIndex ? + wo.output_callbackJSON($cell, txt, cellIndex + col) || + txt + '(' + (cellIndex + col) + ')' : isHeader ? txt : dupe ? txt : ''; + } + } else { + tmpRow[rowIndex][cellIndex + col] = addSpanIndex ? wo.output_callbackJSON($cell, txt, cellIndex + col) || txt + '(' + (cellIndex + col) + ')' : isHeader ? txt : dupe ? txt : ''; } - } else { - tmpRow[rowIndex][cellIndex + col] = addSpanIndex ? - wo.output_callbackJSON($cell, txt, cellIndex + col) || - txt + '(' + (cellIndex + col) + ')' : isHeader ? txt : dupe ? txt : ''; } } - } - // skip column if already defined - while (typeof tmpRow[rowIndex][cellIndex] !== 'undefined') { cellIndex++; } + // skip column if already defined + while (typeof tmpRow[rowIndex][cellIndex] !== 'undefined') { cellIndex++; } - tmpRow[rowIndex][cellIndex] = tmpRow[rowIndex][cellIndex] || - output.formatData( wo, $cell, isHeader ); - cellIndex++; + tmpRow[rowIndex][cellIndex] = tmpRow[rowIndex][cellIndex] || + output.formatData( c, wo, $cell, isHeader ); + cellIndex++; + } } - } - return ts.output.removeColumns( c, wo, tmpRow ); - }, - - // remove hidden/ignored columns - removeColumns : function( c, wo, arry ) { - var rowIndex, row, colIndex, - data = [], - len = arry.length; - for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { - row = arry[ rowIndex ]; - data[ rowIndex ] = []; - for ( colIndex = 0; colIndex < c.columns; colIndex++ ) { - if ( !wo.output_hiddenColumnArray[ colIndex ] ) { - data[ rowIndex ].push( row[ colIndex ] ); + return ts.output.removeColumns( c, wo, tmpRow ); + }, + + // remove hidden/ignored columns + removeColumns : function( c, wo, arry ) { + var rowIndex, row, colIndex, + data = [], + len = arry.length; + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { + row = arry[ rowIndex ]; + data[ rowIndex ] = []; + for ( colIndex = 0; colIndex < c.columns; colIndex++ ) { + if ( !wo.output_hiddenColumnArray[ colIndex ] ) { + data[ rowIndex ].push( row[ colIndex ] ); + } } } - } - return data; - }, - - process : function(c, wo) { - var mydata, $this, $rows, headers, csvData, len, rowsLen, tmp, - hasStringify = window.JSON && JSON.hasOwnProperty('stringify'), - indx = 0, - tmpData = (wo.output_separator || ',').toLowerCase(), - outputJSON = tmpData === 'json', - outputArray = tmpData === 'array', - separator = outputJSON || outputArray ? ',' : wo.output_separator, - saveRows = wo.output_saveRows, - $el = c.$table; - // regex to look for the set separator or HTML - wo.output_regex = new RegExp('(' + (/\\/.test(separator) ? '\\' : '' ) + separator + ')' ); - - // make a list of hidden columns - wo.output_hiddenColumnArray = []; - for ( indx = 0; indx < c.columns; indx++ ) { - wo.output_hiddenColumnArray[ indx ] = $.inArray( indx, wo.output_ignoreColumns ) > -1 || - c.$headerIndexed[ indx ].css( 'display' ) === 'none'; - } + return data; + }, + + process : function(c, wo) { + var mydata, $this, $rows, headers, csvData, len, rowsLen, tmp, + hasStringify = window.JSON && JSON.hasOwnProperty('stringify'), + indx = 0, + tmpData = (wo.output_separator || ',').toLowerCase(), + outputJSON = tmpData === 'json', + outputArray = tmpData === 'array', + separator = outputJSON || outputArray ? ',' : wo.output_separator, + saveRows = wo.output_saveRows, + $el = c.$table; + // regex to look for the set separator or HTML + wo.output_regex = new RegExp('(' + (/\\/.test(separator) ? '\\' : '' ) + separator + ')' ); + + // make a list of hidden columns + wo.output_hiddenColumnArray = []; + for ( indx = 0; indx < c.columns; indx++ ) { + wo.output_hiddenColumnArray[ indx ] = $.inArray( indx, wo.output_ignoreColumns ) > -1 || + ( !wo.output_hiddenColumns && c.$headerIndexed[ indx ].css( 'display' ) === 'none' && + !c.$headerIndexed[ indx ].hasClass( 'tablesorter-scroller-hidden-column' ) ); + } - // get header cells - $this = $el.find('thead tr:visible').not('.' + (ts.css.filterRow || 'tablesorter-filter-row') ); - headers = output.processRow(c, $this, true, outputJSON); - - // all tbody rows - $rows = $el.children('tbody').children('tr'); - - // get (f)iltered, (v)isible, all rows (look for the first letter only), or jQuery filter selector - $rows = /^f/.test(saveRows) ? $rows.not('.' + (wo.filter_filteredRow || 'filtered') ) : - /^v/.test(saveRows) ? $rows.filter(':visible') : - // look for '.' (class selector), '#' (id selector), - // ':' (basic filters, e.g. ':not()') or '[' (attribute selector start) - /^[.#:\[]/.test(saveRows) ? $rows.filter(saveRows) : - // default to all rows - $rows; - - // process to array of arrays - csvData = output.processRow(c, $rows); - - if (wo.output_includeFooter) { - // clone, to force the tfoot rows to the end of this selection of rows - // otherwise they appear after the thead (the order in the HTML) - csvData = csvData.concat( output.processRow( c, $el.children('tfoot').children('tr:visible') ) ); - } + // get header cells + $this = $el + .find('thead tr') + .not('.' + (ts.css.filterRow || 'tablesorter-filter-row') ) + .filter( function() { + return wo.output_hiddenColumns || $(this).css('display') !== 'none'; + }); + headers = output.processRow(c, $this, true, outputJSON); + + // all tbody rows + $rows = $el.children('tbody').children('tr'); + + // get (f)iltered, (v)isible, all rows (look for the first letter only), or jQuery filter selector + $rows = /^f/.test(saveRows) ? $rows.not('.' + (wo.filter_filteredRow || 'filtered') ) : + /^v/.test(saveRows) ? $rows.filter(':visible') : + // look for '.' (class selector), '#' (id selector), + // ':' (basic filters, e.g. ':not()') or '[' (attribute selector start) + /^[.#:\[]/.test(saveRows) ? $rows.filter(saveRows) : + // default to all rows + $rows; + + // process to array of arrays + csvData = output.processRow(c, $rows); + + if (wo.output_includeFooter) { + // clone, to force the tfoot rows to the end of this selection of rows + // otherwise they appear after the thead (the order in the HTML) + csvData = csvData.concat( output.processRow( c, $el.children('tfoot').children('tr:visible') ) ); + } - len = headers.length; + len = headers.length; - if (outputJSON) { - tmpData = []; - rowsLen = csvData.length; - for ( indx = 0; indx < rowsLen; indx++ ) { - // multiple header rows & output_headerRows = true, pick the last row... - tmp = headers[ ( len > 1 && wo.output_headerRows ) ? indx % len : len - 1 ]; - tmpData.push( output.row2Hash( tmp, csvData[ indx ] ) ); - } + if (outputJSON) { + tmpData = []; + rowsLen = csvData.length; + for ( indx = 0; indx < rowsLen; indx++ ) { + // multiple header rows & output_headerRows = true, pick the last row... + tmp = headers[ ( len > 1 && wo.output_headerRows ) ? indx % len : len - 1 ]; + tmpData.push( output.row2Hash( tmp, csvData[ indx ] ) ); + } - // requires JSON stringify; if it doesn't exist, the output will show [object Object],... in the output window - mydata = hasStringify ? JSON.stringify(tmpData) : tmpData; - } else { - tmp = [ headers[ ( len > 1 && wo.output_headerRows ) ? indx % len : len - 1 ] ]; - tmpData = output.row2CSV(wo, wo.output_headerRows ? headers : tmp, outputArray) - .concat( output.row2CSV(wo, csvData, outputArray) ); + // requires JSON stringify; if it doesn't exist, the output will show [object Object],... in the output window + mydata = hasStringify ? JSON.stringify(tmpData) : tmpData; + } else { + tmp = [ headers[ ( len > 1 && wo.output_headerRows ) ? indx % len : len - 1 ] ]; + tmpData = output.row2CSV(wo, wo.output_headerRows ? headers : tmp, outputArray) + .concat( output.row2CSV(wo, csvData, outputArray) ); - // stringify the array; if stringify doesn't exist the array will be flattened - mydata = outputArray && hasStringify ? JSON.stringify(tmpData) : tmpData.join('\n'); - } + // stringify the array; if stringify doesn't exist the array will be flattened + mydata = outputArray && hasStringify ? JSON.stringify(tmpData) : tmpData.join('\n'); + } - // callback; if true returned, continue processing - if ($.isFunction(wo.output_callback) && !wo.output_callback(c, mydata)) { return; } + // callback; if true returned, continue processing + if ($.isFunction(wo.output_callback) && !wo.output_callback(c, mydata)) { return; } - if ( /p/i.test( wo.output_delivery || '' ) ) { - output.popup(mydata, wo.output_popupStyle, outputJSON || outputArray); - } else { - output.download(wo, mydata); - } + if ( /p/i.test( wo.output_delivery || '' ) ) { + output.popup(mydata, wo.output_popupStyle, outputJSON || outputArray); + } else { + output.download(wo, mydata); + } - }, // end process - - row2CSV : function(wo, tmpRow, outputArray) { - var tmp, rowIndex, - csvData = [], - rowLen = tmpRow.length; - for (rowIndex = 0; rowIndex < rowLen; rowIndex++) { - // remove any blank rows - tmp = ( tmpRow[rowIndex] || [] ).join('').replace(/\"/g,''); - if ( ( tmpRow[rowIndex] || [] ).length > 0 && tmp !== '' ) { - csvData[csvData.length] = outputArray ? tmpRow[rowIndex] : tmpRow[rowIndex].join(wo.output_separator); + }, // end process + + row2CSV : function(wo, tmpRow, outputArray) { + var tmp, rowIndex, + csvData = [], + rowLen = tmpRow.length; + for (rowIndex = 0; rowIndex < rowLen; rowIndex++) { + // remove any blank rows + tmp = ( tmpRow[rowIndex] || [] ).join('').replace(/\"/g, ''); + if ( ( tmpRow[rowIndex] || [] ).length > 0 && tmp !== '' ) { + csvData[csvData.length] = outputArray ? tmpRow[rowIndex] : tmpRow[rowIndex].join(wo.output_separator); + } } - } - return csvData; - }, - - row2Hash : function( keys, values ) { - var indx, - json = {}, - len = values.length; - for ( indx = 0; indx < len; indx++ ) { - if ( indx < keys.length ) { - json[ keys[ indx ] ] = values[ indx ]; + return csvData; + }, + + row2Hash : function( keys, values ) { + var indx, + json = {}, + len = values.length; + for ( indx = 0; indx < len; indx++ ) { + if ( indx < keys.length ) { + json[ keys[ indx ] ] = values[ indx ]; + } + } + return json; + }, + + formatData : function(c, wo, $el, isHeader) { + var attr = $el.attr(wo.output_dataAttrib), + txt = typeof attr !== 'undefined' ? attr : $el.html(), + quotes = (wo.output_separator || ',').toLowerCase(), + separator = quotes === 'json' || quotes === 'array', + // replace " with “ if undefined + result = txt.replace(/\"/g, wo.output_replaceQuote || '\u201c'); + // replace line breaks with \\n & tabs with \\t + if (!wo.output_trimSpaces) { + result = result.replace(output.regexBR, output.replaceCR).replace(/\t/g, output.replaceTab); + } else { + result = result.replace(output.regexBR, ''); + } + // extract img alt text + txt = result.match(output.regexIMG); + if (!wo.output_includeHTML && txt !== null) { + result = txt[1]; + } + // replace/remove html + result = wo.output_includeHTML && !isHeader ? result : result.replace(output.regexHTML, ''); + result = wo.output_trimSpaces || isHeader ? $.trim(result) : result; + // JSON & array outputs don't need quotes + quotes = separator ? false : wo.output_wrapQuotes || wo.output_regex.test(result) || output.regexQuote.test(result); + result = quotes ? '"' + result + '"' : result; + + // formatting callback - added v2.22.4 + if ( typeof wo.output_formatContent === 'function' ) { + return wo.output_formatContent( c, wo, { + isHeader : isHeader, + $cell : $el, + content : result + }); } - } - return json; - }, - - formatData : function(wo, $el, isHeader) { - var attr = $el.attr(wo.output_dataAttrib), - txt = typeof attr !== 'undefined' ? attr : $el.html(), - quotes = (wo.output_separator || ',').toLowerCase(), - separator = quotes === 'json' || quotes === 'array', - // replace " with “ if undefined - result = txt.replace(/\"/g, wo.output_replaceQuote || '\u201c'); - // replace line breaks with \\n & tabs with \\t - if (!wo.output_trimSpaces) { - result = result.replace(output.regexBR, output.replaceCR).replace(/\t/g, output.replaceTab); - } else { - result = result.replace(output.regexBR, ''); - } - // extract img alt text - txt = result.match(output.regexIMG); - if (!wo.output_includeHTML && txt !== null) { - result = txt[1]; - } - // replace/remove html - result = wo.output_includeHTML && !isHeader ? result : result.replace(output.regexHTML, ''); - result = wo.output_trimSpaces || isHeader ? $.trim(result) : result; - // JSON & array outputs don't need quotes - quotes = separator ? false : wo.output_wrapQuotes || wo.output_regex.test(result) || output.regexQuote.test(result); - return quotes ? '"' + result + '"' : result; - }, - - popup : function(data, style, wrap) { - var generator = window.open('', output.popupTitle, style); - generator.document.write( - '<html><head><title>' + output.popupTitle + '</title></head><body>' + - '<textarea wrap="' + (wrap ? 'on' : 'off') + '" style="' + output.popupStyle + '">' + data + '\n</textarea>' + - '</body></html>' - ); - generator.document.close(); - generator.focus(); - // select all text and focus within the textarea in the popup - // $(generator.document).find('textarea').select().focus(); - return true; - }, - - // modified from https://github.com/PixelsCommander/Download-File-JS - // & http://html5-demos.appspot.com/static/a.download.html - download : function (wo, data){ - - var e, blob, gotBlob, - nav = window.navigator, - link = document.createElement('a'); - - // iOS devices do not support downloading. We have to inform user about this. - if (/(iP)/g.test(nav.userAgent)) { - alert(output.message); - return false; - } - - // test for blob support - try { - gotBlob = !!new Blob(); - } catch (err) { - gotBlob = false; - } - // Use HTML5 Blob if browser supports it - if ( gotBlob ) { + return result; + }, + + popup : function(data, style, wrap) { + var generator = window.open('', output.popupTitle, style); + generator.document.write( + '<html><head><title>' + output.popupTitle + '</title></head><body>' + + '<textarea wrap="' + (wrap ? 'on' : 'off') + '" style="' + output.popupStyle + '">' + data + '\n</textarea>' + + '</body></html>' + ); + generator.document.close(); + generator.focus(); + // select all text and focus within the textarea in the popup + // $(generator.document).find('textarea').select().focus(); + return true; + }, + + // modified from https://github.com/PixelsCommander/Download-File-JS + // & http://html5-demos.appspot.com/static/a.download.html + download : function (wo, data){ + + var e, blob, gotBlob, + nav = window.navigator, + link = document.createElement('a'); + + // iOS devices do not support downloading. We have to inform user about this. + if (/(iP)/g.test(nav.userAgent)) { + alert(output.message); + return false; + } - window.URL = window.URL || window.webkitURL; - // prepend BOM for utf-8 encoding - see https://github.com/eligrey/FileSaver.js/blob/master/FileSaver.js#L140 - blob = new Blob( [ '\ufeff', data ], { type: wo.output_encoding } ); + // test for blob support + try { + gotBlob = !!new Blob(); + } catch (err) { + gotBlob = false; + } - if (nav.msSaveBlob) { - // IE 10+ - nav.msSaveBlob(blob, wo.output_saveFileName); - } else { - // all other browsers - link.href = window.URL.createObjectURL(blob); - link.download = wo.output_saveFileName; - // Dispatching click event; using $(link).trigger() won't work - if (document.createEvent) { - e = document.createEvent('MouseEvents'); - // event.initMouseEvent(type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, - // ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget); - e.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); - link.dispatchEvent(e); + // Use HTML5 Blob if browser supports it + if ( gotBlob ) { + + window.URL = window.URL || window.webkitURL; + // prepend BOM for utf-8 encoding - see https://github.com/eligrey/FileSaver.js/blob/master/FileSaver.js#L140 + blob = new Blob( [ '\ufeff', data ], { type: wo.output_encoding } ); + + if (nav.msSaveBlob) { + // IE 10+ + nav.msSaveBlob(blob, wo.output_saveFileName); + } else { + // all other browsers + link.href = window.URL.createObjectURL(blob); + link.download = wo.output_saveFileName; + // Dispatching click event; using $(link).trigger() won't work + if (document.createEvent) { + e = document.createEvent('MouseEvents'); + // event.initMouseEvent(type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, + // ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget); + e.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); + link.dispatchEvent(e); + } } + return false; } - return false; + + // fallback to force file download (whether supported by server). + // not sure if this actually works in IE9 and older... + window.open( wo.output_encoding + encodeURIComponent(data) + '?download', '_self'); + return true; + + }, + + remove : function(c) { + c.$table.off(output.event); + } + + }; + + ts.addWidget({ + id: 'output', + options: { + output_separator : ',', // set to 'json', 'array' or any separator + output_ignoreColumns : [], // columns to ignore [0, 1,... ] (zero-based index) + output_hiddenColumns : false, // include hidden columns in the output + output_includeFooter : false, // include footer rows in the output + output_dataAttrib : 'data-name', // header attrib containing modified header name + output_headerRows : false, // if true, include multiple header rows (JSON only) + output_delivery : 'popup', // popup, download + output_saveRows : 'filtered', // (a)ll, (v)isible, (f)iltered or jQuery filter selector + output_duplicateSpans : true, // duplicate output data in tbody colspan/rowspan + output_replaceQuote : '\u201c;', // left double quote + output_includeHTML : false, + output_trimSpaces : true, + output_wrapQuotes : false, + output_popupStyle : 'width=500,height=300', + output_saveFileName : 'mytable.csv', + // format $cell content callback + output_formatContent : null, // function(config, data){ return data.content; } + // callback executed when processing completes + // return true to continue download/output + // return false to stop delivery & do something else with the data + output_callback : function(config, data){ return true; }, + // JSON callback executed when a colspan is encountered in the header + output_callbackJSON : function($cell, txt, cellIndex) { return txt + '(' + (cellIndex) + ')'; }, + // the need to modify this for Excel no longer exists + output_encoding : 'data:application/octet-stream;charset=utf8,' + }, + init: function(table, thisWidget, c) { + output.init(c); + }, + remove: function(table, c){ + output.remove(c); } - // fallback to force file download (whether supported by server). - // not sure if this actually works in IE9 and older... - window.open( wo.output_encoding + encodeURIComponent(data) + '?download' , '_self'); - return true; - - }, - - remove : function(c) { - c.$table.off(output.event); - } - -}; - -ts.addWidget({ - id: "output", - options: { - output_separator : ',', // set to "json", "array" or any separator - output_ignoreColumns : [], // columns to ignore [0, 1,... ] (zero-based index) - output_hiddenColumns : false, // include hidden columns in the output - output_includeFooter : false, // include footer rows in the output - output_dataAttrib : 'data-name', // header attrib containing modified header name - output_headerRows : false, // if true, include multiple header rows (JSON only) - output_delivery : 'popup', // popup, download - output_saveRows : 'filtered', // (a)ll, (v)isible, (f)iltered or jQuery filter selector - output_duplicateSpans: true, // duplicate output data in tbody colspan/rowspan - output_replaceQuote : '\u201c;', // left double quote - output_includeHTML : false, - output_trimSpaces : true, - output_wrapQuotes : false, - output_popupStyle : 'width=500,height=300', - output_saveFileName : 'mytable.csv', - // callback executed when processing completes - // return true to continue download/output - // return false to stop delivery & do something else with the data - output_callback : function(config, data){ return true; }, - // JSON callback executed when a colspan is encountered in the header - output_callbackJSON : function($cell, txt, cellIndex) { return txt + '(' + (cellIndex) + ')'; }, - // the need to modify this for Excel no longer exists - output_encoding : 'data:application/octet-stream;charset=utf8,' - }, - init: function(table, thisWidget, c) { - output.init(c); - }, - remove: function(table, c){ - output.remove(c); - } - -}); + }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index a7b1345..363cc21 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,1150 +1,1162 @@ -/*! Widget: Pager - updated 5/17/2015 (v2.22.0) */ +/*! Widget: Pager - updated 7/28/2015 (v2.22.4) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ /*jshint browser:true, jquery:true, unused:false */ ;(function($){ -"use strict"; -var tsp, + 'use strict'; + var tsp, ts = $.tablesorter; -ts.addWidget({ - id: "pager", - priority: 55, // load pager after filter widget - options : { - // output default: '{page}/{totalPages}' - // possible variables: {page}, {totalPages}, {filteredPages}, {startRow}, {endRow}, {filteredRows} and {totalRows} - pager_output: '{startRow} to {endRow} of {totalRows} rows', // '{page}/{totalPages}' - - // apply disabled classname to the pager arrows when the rows at either extreme is visible - pager_updateArrows: true, - - // starting page of the pager (zero based index) - pager_startPage: 0, - - // reset pager after filtering; set to desired page # - // set to false to not change page at filter start - pager_pageReset: 0, - - // Number of visible rows - pager_size: 10, - - // Number of options to include in the pager number selector - pager_maxOptionSize: 20, + ts.addWidget({ + id: 'pager', + priority: 55, // load pager after filter widget + options : { + // output default: '{page}/{totalPages}' + // possible variables: {page}, {totalPages}, {filteredPages}, {startRow}, {endRow}, {filteredRows} and {totalRows} + pager_output: '{startRow} to {endRow} of {totalRows} rows', // '{page}/{totalPages}' + + // apply disabled classname to the pager arrows when the rows at either extreme is visible + pager_updateArrows: true, + + // starting page of the pager (zero based index) + pager_startPage: 0, + + // reset pager after filtering; set to desired page # + // set to false to not change page at filter start + pager_pageReset: 0, + + // Number of visible rows + pager_size: 10, + + // Number of options to include in the pager number selector + pager_maxOptionSize: 20, + + // Save pager page & size if the storage script is loaded (requires $.tablesorter.storage in jquery.tablesorter.widgets.js) + pager_savePages: true, + + // defines custom storage key + pager_storageKey: 'tablesorter-pager', + + // if true, the table will remain the same height no matter how many records are displayed. The space is made up by an empty + // table row set to a height to compensate; default is false + pager_fixedHeight: false, + + // count child rows towards the set page size? (set true if it is a visible table row within the pager) + // if true, child row(s) may not appear to be attached to its parent row, may be split across pages or + // may distort the table if rowspan or cellspans are included. + pager_countChildRows: false, + + // remove rows from the table to speed up the sort of large tables. + // setting this to false, only hides the non-visible rows; needed if you plan to add/remove rows with the pager enabled. + pager_removeRows: false, // removing rows in larger tables speeds up the sort + + // use this format: 'http://mydatabase.com?page={page}&size={size}&{sortList:col}&{filterList:fcol}' + // where {page} is replaced by the page number, {size} is replaced by the number of records to show, + // {sortList:col} adds the sortList to the url into a 'col' array, and {filterList:fcol} adds + // the filterList to the url into an 'fcol' array. + // So a sortList = [[2,0],[3,0]] becomes '&col[2]=0&col[3]=0' in the url + // and a filterList = [[2,Blue],[3,13]] becomes '&fcol[2]=Blue&fcol[3]=13' in the url + pager_ajaxUrl: null, + + // modify the url after all processing has been applied + pager_customAjaxUrl: function(table, url) { return url; }, + + // modify the $.ajax object to allow complete control over your ajax requests + pager_ajaxObject: { + dataType: 'json' + }, + + // set this to false if you want to block ajax loading on init + pager_processAjaxOnInit: true, + + // process ajax so that the following information is returned: + // [ total_rows (number), rows (array of arrays), headers (array; optional) ] + // example: + // [ + // 100, // total rows + // [ + // [ "row1cell1", "row1cell2", ... "row1cellN" ], + // [ "row2cell1", "row2cell2", ... "row2cellN" ], + // ... + // [ "rowNcell1", "rowNcell2", ... "rowNcellN" ] + // ], + // [ "header1", "header2", ... "headerN" ] // optional + // ] + pager_ajaxProcessing: function(ajax){ return [ 0, [], null ]; }, + + // css class names of pager arrows + pager_css: { + container : 'tablesorter-pager', + errorRow : 'tablesorter-errorRow', // error information row (don't include period at beginning) + disabled : 'disabled' // class added to arrows @ extremes (i.e. prev/first arrows 'disabled' on first page) + }, + + // jQuery selectors + pager_selectors: { + container : '.pager', // target the pager markup + first : '.first', // go to first page arrow + prev : '.prev', // previous page arrow + next : '.next', // next page arrow + last : '.last', // go to last page arrow + gotoPage : '.gotoPage', // go to page selector - select dropdown that sets the current page + pageDisplay : '.pagedisplay', // location of where the 'output' is displayed + pageSize : '.pagesize' // page size selector - select dropdown that sets the 'size' option + } + }, + init: function(table){ + tsp.init(table); + }, + // only update to complete sorter initialization + format: function(table, c){ + if (!(c.pager && c.pager.initialized)){ + return tsp.initComplete(table, c); + } + tsp.moveToPage(table, c.pager, false); + }, + remove: function(table, c, wo, refreshing){ + tsp.destroyPager(table, c, refreshing); + } + }); - // Save pager page & size if the storage script is loaded (requires $.tablesorter.storage in jquery.tablesorter.widgets.js) - pager_savePages: true, + /* pager widget functions */ + tsp = ts.pager = { - //defines custom storage key - pager_storageKey: 'tablesorter-pager', + init: function(table) { + // check if tablesorter has initialized + if (table.hasInitialized && table.config.pager.initialized) { return; } + var t, + c = table.config, + wo = c.widgetOptions, + s = wo.pager_selectors, + + // save pager variables + p = c.pager = $.extend({ + totalPages: 0, + filteredRows: 0, + filteredPages: 0, + currentFilters: [], + page: wo.pager_startPage, + startRow: 0, + endRow: 0, + ajaxCounter: 0, + $size: null, + last: {}, + // save original pager size + setSize: wo.pager_size, + setPage: wo.pager_startPage, + events: 'filterInit filterStart filterEnd sortEnd disable enable destroy updateComplete ' + + 'pageSize pageSet pageAndSize pagerUpdate refreshComplete ' + }, c.pager); + + // pager initializes multiple times before table has completed initialization + if (p.isInitializing) { return; } + + p.isInitializing = true; + if (c.debug) { + console.log('Pager: Initializing'); + } - // if true, the table will remain the same height no matter how many records are displayed. The space is made up by an empty - // table row set to a height to compensate; default is false - pager_fixedHeight: false, + p.size = $.data(table, 'pagerLastSize') || wo.pager_size; + // added in case the pager is reinitialized after being destroyed. + p.$container = $(s.container).addClass(wo.pager_css.container).show(); + // goto selector + p.$goto = p.$container.find(s.gotoPage); // goto is a reserved word #657 + // page size selector + p.$size = p.$container.find(s.pageSize); + p.totalRows = c.$tbodies.eq(0).children('tr').not( wo.pager_countChildRows ? '' : '.' + c.cssChildRow ).length; + p.oldAjaxSuccess = p.oldAjaxSuccess || wo.pager_ajaxObject.success; + c.appender = tsp.appender; + p.initializing = true; + if (wo.pager_savePages && ts.storage) { + t = ts.storage(table, wo.pager_storageKey) || {}; // fixes #387 + p.page = ( isNaN(t.page) ? p.page : t.page ) || p.setPage || 0; + p.size = ( isNaN(t.size) ? p.size : t.size ) || p.setSize || 10; + $.data(table, 'pagerLastSize', p.size); + } - // count child rows towards the set page size? (set true if it is a visible table row within the pager) - // if true, child row(s) may not appear to be attached to its parent row, may be split across pages or - // may distort the table if rowspan or cellspans are included. - pager_countChildRows: false, + // skipped rows + p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.slice(1) + '|' + c.cssChildRow + ')'); - // remove rows from the table to speed up the sort of large tables. - // setting this to false, only hides the non-visible rows; needed if you plan to add/remove rows with the pager enabled. - pager_removeRows: false, // removing rows in larger tables speeds up the sort + // clear initialized flag + p.initialized = false; + // before initialization event + c.$table.trigger('pagerBeforeInitialized', c); - // use this format: "http://mydatabase.com?page={page}&size={size}&{sortList:col}&{filterList:fcol}" - // where {page} is replaced by the page number, {size} is replaced by the number of records to show, - // {sortList:col} adds the sortList to the url into a "col" array, and {filterList:fcol} adds - // the filterList to the url into an "fcol" array. - // So a sortList = [[2,0],[3,0]] becomes "&col[2]=0&col[3]=0" in the url - // and a filterList = [[2,Blue],[3,13]] becomes "&fcol[2]=Blue&fcol[3]=13" in the url - pager_ajaxUrl: null, + tsp.enablePager(table, c, false); - // modify the url after all processing has been applied - pager_customAjaxUrl: function(table, url) { return url; }, + // p must have ajaxObject + p.ajaxObject = wo.pager_ajaxObject; // $.extend({}, wo.pager_ajaxObject ); + p.ajaxObject.url = wo.pager_ajaxUrl; - // modify the $.ajax object to allow complete control over your ajax requests - pager_ajaxObject: { - dataType: 'json' - }, + if ( typeof wo.pager_ajaxUrl === 'string' ) { + // ajax pager; interact with database + p.ajax = true; + // When filtering with ajax, allow only custom filtering function, disable default filtering since it will be done server side. + wo.filter_serversideFiltering = true; + c.serverSideSorting = true; + tsp.moveToPage(table, p); + } else { + p.ajax = false; + // Regular pager; all rows stored in memory + c.$table.trigger('appendCache', [ {}, true ]); + } - // set this to false if you want to block ajax loading on init - pager_processAjaxOnInit: true, - - // process ajax so that the following information is returned: - // [ total_rows (number), rows (array of arrays), headers (array; optional) ] - // example: - // [ - // 100, // total rows - // [ - // [ "row1cell1", "row1cell2", ... "row1cellN" ], - // [ "row2cell1", "row2cell2", ... "row2cellN" ], - // ... - // [ "rowNcell1", "rowNcell2", ... "rowNcellN" ] - // ], - // [ "header1", "header2", ... "headerN" ] // optional - // ] - pager_ajaxProcessing: function(ajax){ return [ 0, [], null ]; }, - - // css class names of pager arrows - pager_css: { - container : 'tablesorter-pager', - errorRow : 'tablesorter-errorRow', // error information row (don't include period at beginning) - disabled : 'disabled' // class added to arrows @ extremes (i.e. prev/first arrows "disabled" on first page) }, - // jQuery selectors - pager_selectors: { - container : '.pager', // target the pager markup - first : '.first', // go to first page arrow - prev : '.prev', // previous page arrow - next : '.next', // next page arrow - last : '.last', // go to last page arrow - gotoPage : '.gotoPage', // go to page selector - select dropdown that sets the current page - pageDisplay : '.pagedisplay', // location of where the "output" is displayed - pageSize : '.pagesize' // page size selector - select dropdown that sets the "size" option - } - }, - init: function(table){ - tsp.init(table); - }, - // only update to complete sorter initialization - format: function(table, c){ - if (!(c.pager && c.pager.initialized)){ - return tsp.initComplete(table, c); - } - tsp.moveToPage(table, c.pager, false); - }, - remove: function(table, c, wo, refreshing){ - tsp.destroyPager(table, c, refreshing); - } -}); - -/* pager widget functions */ -tsp = ts.pager = { - - init: function(table) { - // check if tablesorter has initialized - if (table.hasInitialized && table.config.pager.initialized) { return; } - var t, - c = table.config, - wo = c.widgetOptions, - s = wo.pager_selectors, - - // save pager variables - p = c.pager = $.extend({ - totalPages: 0, - filteredRows: 0, - filteredPages: 0, - currentFilters: [], - page: wo.pager_startPage, - startRow: 0, - endRow: 0, - ajaxCounter: 0, - $size: null, - last: {}, - // save original pager size - setSize: wo.pager_size, - setPage: wo.pager_startPage, - events: 'filterInit filterStart filterEnd sortEnd disable enable destroy updateComplete ' + - 'pageSize pageSet pageAndSize pagerUpdate refreshComplete ' - }, c.pager); - - // pager initializes multiple times before table has completed initialization - if (p.isInitializing) { return; } - - p.isInitializing = true; - if (c.debug) { - ts.log('Pager: Initializing'); - } - - p.size = $.data(table, 'pagerLastSize') || wo.pager_size; - // added in case the pager is reinitialized after being destroyed. - p.$container = $(s.container).addClass(wo.pager_css.container).show(); - // goto selector - p.$goto = p.$container.find(s.gotoPage); // goto is a reserved word #657 - // page size selector - p.$size = p.$container.find(s.pageSize); - p.totalRows = c.$tbodies.eq(0).children('tr').not( wo.pager_countChildRows ? '' : '.' + c.cssChildRow ).length; - p.oldAjaxSuccess = p.oldAjaxSuccess || wo.pager_ajaxObject.success; - c.appender = tsp.appender; - p.initializing = true; - if (wo.pager_savePages && ts.storage) { - t = ts.storage(table, wo.pager_storageKey) || {}; // fixes #387 - p.page = ( isNaN(t.page) ? p.page : t.page ) || p.setPage || 0; - p.size = ( isNaN(t.size) ? p.size : t.size ) || p.setSize || 10; - $.data(table, 'pagerLastSize', p.size); - } - - // skipped rows - p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.slice(1) + '|' + c.cssChildRow + ')'); - - // clear initialized flag - p.initialized = false; - // before initialization event - c.$table.trigger('pagerBeforeInitialized', c); - - tsp.enablePager(table, c, false); - - // p must have ajaxObject - p.ajaxObject = wo.pager_ajaxObject; // $.extend({}, wo.pager_ajaxObject ); - p.ajaxObject.url = wo.pager_ajaxUrl; - - if ( typeof(wo.pager_ajaxUrl) === 'string' ) { - // ajax pager; interact with database - p.ajax = true; - // When filtering with ajax, allow only custom filtering function, disable default filtering since it will be done server side. - wo.filter_serversideFiltering = true; - c.serverSideSorting = true; - tsp.moveToPage(table, p); - } else { - p.ajax = false; - // Regular pager; all rows stored in memory - c.$table.trigger('appendCache', [{}, true]); - } + initComplete: function(table, c){ + var p = c.pager; + tsp.bindEvents(table, c); + tsp.setPageSize(table, 0, c); // page size 0 is ignored - }, + if (!p.ajax) { + tsp.hideRowsSetup(table, c); + } - initComplete: function(table, c){ - var p = c.pager; - tsp.bindEvents(table, c); - tsp.setPageSize(table, 0, c); // page size 0 is ignored + // pager initialized + p.initialized = true; + p.initializing = false; + p.isInitializing = false; + if (c.debug) { + console.log('Pager: Triggering pagerInitialized'); + } + c.$table.trigger('pagerInitialized', c); + // filter widget not initialized; it will update the output display & fire off the pagerComplete event + if ( !( c.widgetOptions.filter_initialized && ts.hasWidget(table, 'filter') ) ) { + // if ajax, then don't fire off pagerComplete + tsp.updatePageDisplay(table, c, !p.ajax); + } + }, - if (!p.ajax) { - tsp.hideRowsSetup(table, c); - } + bindEvents: function(table, c){ + var ctrls, fxn, + p = c.pager, + wo = c.widgetOptions, + namespace = c.namespace + 'pager', + s = wo.pager_selectors; - // pager initialized - p.initialized = true; - p.initializing = false; - p.isInitializing = false; - if (c.debug) { - ts.log('Pager: Triggering pagerInitialized'); - } - c.$table.trigger('pagerInitialized', c); - // filter widget not initialized; it will update the output display & fire off the pagerComplete event - if ( !( c.widgetOptions.filter_initialized && ts.hasWidget(table, 'filter') ) ) { - // if ajax, then don't fire off pagerComplete - tsp.updatePageDisplay(table, c, !p.ajax); - } - }, - - bindEvents: function(table, c){ - var ctrls, fxn, - p = c.pager, - wo = c.widgetOptions, - s = wo.pager_selectors; - - c.$table - .off( $.trim(p.events.split(' ').join('.pager ')) ) - .on('filterInit.pager filterStart.pager', function(e, filters) { - p.currentFilters = $.isArray(filters) ? filters : c.$table.data('lastSearch'); - // don't change page if filters are the same (pager updating, etc) - if (e.type === 'filterStart' && wo.pager_pageReset !== false && (c.lastCombinedFilter || '') !== (p.currentFilters || []).join('')) { - p.page = wo.pager_pageReset; // fixes #456 & #565 - } - }) - // update pager after filter widget completes - .on('filterEnd.pager sortEnd.pager', function() { - p.currentFilters = c.$table.data('lastSearch'); - if (p.initialized || p.initializing) { - if (c.delayInit && c.rowsCopy && c.rowsCopy.length === 0) { - // make sure we have a copy of all table rows once the cache has been built + c.$table + .off( $.trim(p.events.split(' ').join(namespace + ' ')) ) + .on('filterInit filterStart '.split(' ').join(namespace + ' '), function(e, filters) { + p.currentFilters = $.isArray(filters) ? filters : c.$table.data('lastSearch'); + // don't change page if filters are the same (pager updating, etc) + if (e.type === 'filterStart' && wo.pager_pageReset !== false && (c.lastCombinedFilter || '') !== (p.currentFilters || []).join('')) { + p.page = wo.pager_pageReset; // fixes #456 & #565 + } + }) + // update pager after filter widget completes + .on('filterEnd sortEnd '.split(' ').join(namespace + ' '), function() { + p.currentFilters = c.$table.data('lastSearch'); + if (p.initialized || p.initializing) { + if (c.delayInit && c.rowsCopy && c.rowsCopy.length === 0) { + // make sure we have a copy of all table rows once the cache has been built + tsp.updateCache(table); + } + tsp.updatePageDisplay(table, c, false); + // tsp.moveToPage(table, p, false); <-- called when applyWidgets is triggered + c.$table.trigger('applyWidgets'); + } + }) + .on('disable' + namespace, function(e){ + e.stopPropagation(); + tsp.showAllRows(table, c); + }) + .on('enable' + namespace, function(e){ + e.stopPropagation(); + tsp.enablePager(table, c, true); + }) + .on('destroy' + namespace, function(e, refreshing){ + e.stopPropagation(); + tsp.destroyPager(table, c, refreshing); + }) + .on('updateComplete' + namespace, function(e, table, triggered){ + e.stopPropagation(); + // table can be unintentionally undefined in tablesorter v2.17.7 and earlier + // don't recalculate total rows/pages if using ajax + if (!table || triggered || p.ajax) { return; } + var $rows = c.$tbodies.eq(0).children('tr').not(c.selectorRemove); + p.totalRows = $rows.length - ( wo.pager_countChildRows ? 0 : $rows.filter('.' + c.cssChildRow).length ); + p.totalPages = Math.ceil( p.totalRows / p.size ); + if ($rows.length && c.rowsCopy && c.rowsCopy.length === 0) { + // make a copy of all table rows once the cache has been built tsp.updateCache(table); } + if ( p.page >= p.totalPages ) { + tsp.moveToLastPage(table, p); + } + tsp.hideRows(table, c); + tsp.changeHeight(table, c); + // update without triggering pagerComplete tsp.updatePageDisplay(table, c, false); - // tsp.moveToPage(table, p, false); <-- called when applyWidgets is triggered + // make sure widgets are applied - fixes #450 c.$table.trigger('applyWidgets'); - } - }) - .on('disable.pager', function(e){ - e.stopPropagation(); - tsp.showAllRows(table, c); - }) - .on('enable.pager', function(e){ - e.stopPropagation(); - tsp.enablePager(table, c, true); - }) - .on('destroy.pager', function(e, refreshing){ - e.stopPropagation(); - tsp.destroyPager(table, c, refreshing); - }) - .on('updateComplete.pager', function(e, table, triggered){ - e.stopPropagation(); - // table can be unintentionally undefined in tablesorter v2.17.7 and earlier - // don't recalculate total rows/pages if using ajax - if (!table || triggered || p.ajax) { return; } - var $rows = c.$tbodies.eq(0).children('tr').not(c.selectorRemove); - p.totalRows = $rows.length - ( wo.pager_countChildRows ? 0 : $rows.filter('.' + c.cssChildRow).length ); - p.totalPages = Math.ceil( p.totalRows / p.size ); - if ($rows.length && c.rowsCopy && c.rowsCopy.length === 0) { - // make a copy of all table rows once the cache has been built - tsp.updateCache(table); - } - if ( p.page >= p.totalPages ) { - tsp.moveToLastPage(table, p); - } - tsp.hideRows(table, c); - tsp.changeHeight(table, c); - // update without triggering pagerComplete - tsp.updatePageDisplay(table, c, false); - // make sure widgets are applied - fixes #450 - c.$table.trigger('applyWidgets'); - tsp.updatePageDisplay(table, c); - }) - .on('pageSize.pager refreshComplete.pager', function(e,v){ - e.stopPropagation(); - tsp.setPageSize(table, parseInt(v, 10) || p.setSize || 10, c); - tsp.hideRows(table, c); - tsp.updatePageDisplay(table, c, false); - }) - .on('pageSet.pager pagerUpdate.pager', function(e,v){ - e.stopPropagation(); - // force pager refresh - if (e.type === 'pagerUpdate') { - v = typeof v === 'undefined' ? p.page + 1 : v; - p.last.page = true; - } - p.page = (parseInt(v, 10) || 1) - 1; - tsp.moveToPage(table, p, true); - tsp.updatePageDisplay(table, c, false); - }) - .on('pageAndSize.pager', function(e, page, size){ - e.stopPropagation(); - p.page = (parseInt(page, 10) || 1) - 1; - tsp.setPageSize(table, parseInt(size, 10) || p.setSize || 10, c); - tsp.moveToPage(table, p, true); - tsp.hideRows(table, c); - tsp.updatePageDisplay(table, c, false); - }); - - // clicked controls - ctrls = [ s.first, s.prev, s.next, s.last ]; - fxn = [ 'moveToFirstPage', 'moveToPrevPage', 'moveToNextPage', 'moveToLastPage' ]; - if (c.debug && !p.$container.length) { - ts.log('Pager: >> Container not found'); - } - p.$container.find(ctrls.join(',')) - .attr("tabindex", 0) - .off('click.pager') - .on('click.pager', function(e){ - e.stopPropagation(); - var i, - $c = $(this), - l = ctrls.length; - if ( !$c.hasClass(wo.pager_css.disabled) ) { - for (i = 0; i < l; i++) { - if ($c.is(ctrls[i])) { - tsp[fxn[i]](table, p); - break; - } + tsp.updatePageDisplay(table, c); + }) + .on('pageSize refreshComplete '.split(' ').join(namespace + ' '), function(e, v){ + e.stopPropagation(); + tsp.setPageSize(table, parseInt(v, 10) || p.setSize || 10, c); + tsp.hideRows(table, c); + tsp.updatePageDisplay(table, c, false); + }) + .on('pageSet pagerUpdate '.split(' ').join(namespace + ' '), function(e, v){ + e.stopPropagation(); + // force pager refresh + if (e.type === 'pagerUpdate') { + v = typeof v === 'undefined' ? p.page + 1 : v; + p.last.page = true; } - } - }); - - if ( p.$goto.length ) { - p.$goto - .off('change.pager') - .on('change.pager', function(){ - p.page = $(this).val() - 1; + p.page = (parseInt(v, 10) || 1) - 1; tsp.moveToPage(table, p, true); tsp.updatePageDisplay(table, c, false); + }) + .on('pageAndSize' + namespace, function(e, page, size){ + e.stopPropagation(); + p.page = (parseInt(page, 10) || 1) - 1; + tsp.setPageSize(table, parseInt(size, 10) || p.setSize || 10, c); + tsp.moveToPage(table, p, true); + tsp.hideRows(table, c); + tsp.updatePageDisplay(table, c, false); }); - } else if (c.debug) { - ts.log('Pager: >> Goto selector not found'); - } - if ( p.$size.length ) { - // setting an option as selected appears to cause issues with initial page size - p.$size.find('option').removeAttr('selected'); - p.$size - .off('change.pager') - .on('change.pager', function() { - p.$size.val( $(this).val() ); // in case there are more than one pagers - if ( !$(this).hasClass(wo.pager_css.disabled) ) { - tsp.setPageSize(table, parseInt( $(this).val(), 10 ), c); - tsp.changeHeight(table, c); + // clicked controls + ctrls = [ s.first, s.prev, s.next, s.last ]; + fxn = [ 'moveToFirstPage', 'moveToPrevPage', 'moveToNextPage', 'moveToLastPage' ]; + if (c.debug && !p.$container.length) { + console.warn('Pager: >> Container not found'); + } + p.$container.find(ctrls.join(',')) + .attr('tabindex', 0) + .off('click' + namespace) + .on('click' + namespace, function(e){ + e.stopPropagation(); + var i, + $c = $(this), + l = ctrls.length; + if ( !$c.hasClass(wo.pager_css.disabled) ) { + for (i = 0; i < l; i++) { + if ($c.is(ctrls[i])) { + tsp[fxn[i]](table, p); + break; + } + } } - return false; }); - } else if (c.debug) { - ts.log('Pager: >> Size selector not found'); - } - }, - - // hide arrows at extremes - pagerArrows: function(c, disable) { - var p = c.pager, - dis = !!disable, - first = dis || p.page === 0, - tp = Math.min( p.totalPages, p.filteredPages ), - last = dis || p.page === tp - 1 || tp === 0, - wo = c.widgetOptions, - s = wo.pager_selectors; - if ( wo.pager_updateArrows ) { - p.$container.find(s.first + ',' + s.prev).toggleClass(wo.pager_css.disabled, first).attr('aria-disabled', first); - p.$container.find(s.next + ',' + s.last).toggleClass(wo.pager_css.disabled, last).attr('aria-disabled', last); - } - }, - - calcFilters: function(table, c) { - var normalized, indx, len, - wo = c.widgetOptions, - p = c.pager, - hasFilters = c.$table.hasClass('hasFilters'); - if (hasFilters && !wo.pager_ajaxUrl) { - if ($.isEmptyObject(c.cache)) { - // delayInit: true so nothing is in the cache - p.filteredRows = p.totalRows = c.$tbodies.eq(0).children('tr').not( wo.pager_countChildRows ? '' : '.' + c.cssChildRow ).length; - } else { - p.filteredRows = 0; - normalized = c.cache[0].normalized; - len = normalized.length; - for (indx = 0; indx < len; indx++) { - p.filteredRows += p.regexRows.test(normalized[indx][c.columns].$row[0].className) ? 0 : 1; + if ( p.$goto.length ) { + p.$goto + .off('change' + namespace) + .on('change' + namespace, function(){ + p.page = $(this).val() - 1; + tsp.moveToPage(table, p, true); + tsp.updatePageDisplay(table, c, false); + }); + } else if (c.debug) { + console.warn('Pager: >> Goto selector not found'); + } + + if ( p.$size.length ) { + // setting an option as selected appears to cause issues with initial page size + p.$size.find('option').removeAttr('selected'); + p.$size + .off('change' + namespace) + .on('change' + namespace, function() { + p.$size.val( $(this).val() ); // in case there are more than one pagers + if ( !$(this).hasClass(wo.pager_css.disabled) ) { + tsp.setPageSize(table, parseInt( $(this).val(), 10 ), c); + tsp.changeHeight(table, c); + } + return false; + }); + } else if (c.debug) { + console.warn('Pager: >> Size selector not found'); + } + + }, + + // hide arrows at extremes + pagerArrows: function(c, disable) { + var p = c.pager, + dis = !!disable, + first = dis || p.page === 0, + tp = Math.min( p.totalPages, p.filteredPages ), + last = dis || p.page === tp - 1 || tp === 0, + wo = c.widgetOptions, + s = wo.pager_selectors; + if ( wo.pager_updateArrows ) { + p.$container.find(s.first + ',' + s.prev).toggleClass(wo.pager_css.disabled, first).attr('aria-disabled', first); + p.$container.find(s.next + ',' + s.last).toggleClass(wo.pager_css.disabled, last).attr('aria-disabled', last); + } + }, + + calcFilters: function(table, c) { + var normalized, indx, len, + wo = c.widgetOptions, + p = c.pager, + hasFilters = c.$table.hasClass('hasFilters'); + if (hasFilters && !wo.pager_ajaxUrl) { + if ($.isEmptyObject(c.cache)) { + // delayInit: true so nothing is in the cache + p.filteredRows = p.totalRows = c.$tbodies.eq(0).children('tr').not( wo.pager_countChildRows ? '' : '.' + c.cssChildRow ).length; + } else { + p.filteredRows = 0; + normalized = c.cache[0].normalized; + len = normalized.length; + for (indx = 0; indx < len; indx++) { + p.filteredRows += p.regexRows.test(normalized[indx][c.columns].$row[0].className) ? 0 : 1; + } } + } else if (!hasFilters) { + p.filteredRows = p.totalRows; } - } else if (!hasFilters) { - p.filteredRows = p.totalRows; - } - }, - - updatePageDisplay: function(table, c, completed) { - if ( c.pager.initializing ) { return; } - var s, t, $out, options, indx, len, - wo = c.widgetOptions, - p = c.pager, - sz = p.size || p.setSize || 10; // don't allow dividing by zero - if (wo.pager_countChildRows) { t.push(c.cssChildRow); } - p.$size.add(p.$goto).removeClass(wo.pager_css.disabled).removeAttr('disabled').attr('aria-disabled', 'false'); - p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method - c.totalRows = p.totalRows; - tsp.calcFilters(table, c); - c.filteredRows = p.filteredRows; - p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; - if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) { - t = (p.size * p.page > p.filteredRows) && completed; - p.page = (t) ? wo.pager_pageReset || 0 : p.page; - p.startRow = (t) ? p.size * p.page + 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1); - p.endRow = Math.min( p.filteredRows, p.totalRows, p.size * ( p.page + 1 ) ); - $out = p.$container.find(wo.pager_selectors.pageDisplay); - // form the output string (can now get a new output string from the server) - s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || wo.pager_output : wo.pager_output ) - // {page} = one-based index; {page+#} = zero based index +/- value - .replace(/\{page([\-+]\d+)?\}/gi, function(m,n){ - return p.totalPages ? p.page + (n ? parseInt(n, 10) : 1) : 0; - }) - // {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object) - .replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m){ - var len, indx, - str = m.replace(/[{}\s]/g,''), - extra = str.split(':'), - data = p.ajaxData, - // return zero for default page/row numbers - deflt = /(rows?|pages?)$/i.test(str) ? 0 : ''; - if (/(startRow|page)/.test(extra[0]) && extra[1] === 'input') { - len = ('' + (extra[0] === 'page' ? p.totalPages : p.totalRows)).length; - indx = extra[0] === 'page' ? p.page + 1 : p.startRow; - return '<input type="text" class="ts-' + extra[0] + '" style="max-width:' + len + 'em" value="' + indx + '"/>'; + }, + + updatePageDisplay: function(table, c, completed) { + if ( c.pager.initializing ) { return; } + var s, t, $out, options, indx, len, + wo = c.widgetOptions, + p = c.pager, + namespace = c.namespace + 'pager', + sz = p.size || p.setSize || 10; // don't allow dividing by zero + if (wo.pager_countChildRows) { t.push(c.cssChildRow); } + p.$size.add(p.$goto).removeClass(wo.pager_css.disabled).removeAttr('disabled').attr('aria-disabled', 'false'); + p.totalPages = Math.ceil( p.totalRows / sz ); // needed for 'pageSize' method + c.totalRows = p.totalRows; + tsp.calcFilters(table, c); + c.filteredRows = p.filteredRows; + p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; + if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) { + t = (p.size * p.page > p.filteredRows) && completed; + p.page = (t) ? wo.pager_pageReset || 0 : p.page; + p.startRow = (t) ? p.size * p.page + 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1); + p.endRow = Math.min( p.filteredRows, p.totalRows, p.size * ( p.page + 1 ) ); + $out = p.$container.find(wo.pager_selectors.pageDisplay); + // form the output string (can now get a new output string from the server) + s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || wo.pager_output : wo.pager_output ) + // {page} = one-based index; {page+#} = zero based index +/- value + .replace(/\{page([\-+]\d+)?\}/gi, function(m, n){ + return p.totalPages ? p.page + (n ? parseInt(n, 10) : 1) : 0; + }) + // {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object) + .replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m){ + var len, indx, + str = m.replace(/[{}\s]/g, ''), + extra = str.split(':'), + data = p.ajaxData, + // return zero for default page/row numbers + deflt = /(rows?|pages?)$/i.test(str) ? 0 : ''; + if (/(startRow|page)/.test(extra[0]) && extra[1] === 'input') { + len = ('' + (extra[0] === 'page' ? p.totalPages : p.totalRows)).length; + indx = extra[0] === 'page' ? p.page + 1 : p.startRow; + return '<input type="text" class="ts-' + extra[0] + '" style="max-width:' + len + 'em" value="' + indx + '"/>'; + } + return extra.length > 1 && data && data[extra[0]] ? data[extra[0]][extra[1]] : p[str] || (data ? data[str] : deflt) || deflt; + }); + if ( p.$goto.length ) { + t = ''; + options = tsp.buildPageSelect(p, c); + len = options.length; + for (indx = 0; indx < len; indx++) { + t += '<option value="' + options[indx] + '">' + options[indx] + '</option>'; } - return extra.length > 1 && data && data[extra[0]] ? data[extra[0]][extra[1]] : p[str] || (data ? data[str] : deflt) || deflt; - }); - if ( p.$goto.length ) { - t = ''; - options = tsp.buildPageSelect(p, c); - len = options.length; - for (indx = 0; indx < len; indx++) { - t += '<option value="' + options[indx] + '">' + options[indx] + '</option>'; + // innerHTML doesn't work in IE9 - http://support2.microsoft.com/kb/276228 + p.$goto.html(t).val( p.page + 1 ); + } + if ($out.length) { + $out[ ($out[0].nodeName === 'INPUT') ? 'val' : 'html' ](s); + // rebind startRow/page inputs + $out.find('.ts-startRow, .ts-page').off('change' + namespace).on('change' + namespace, function(){ + var v = $(this).val(), + pg = $(this).hasClass('ts-startRow') ? Math.floor( v / p.size ) + 1 : v; + c.$table.trigger('pageSet' + namespace, [ pg ]); + }); } - // innerHTML doesn't work in IE9 - http://support2.microsoft.com/kb/276228 - p.$goto.html(t).val( p.page + 1 ); } - if ($out.length) { - $out[ ($out[0].nodeName === 'INPUT') ? 'val' : 'html' ](s); - // rebind startRow/page inputs - $out.find('.ts-startRow, .ts-page').off('change.pager').on('change.pager', function(){ - var v = $(this).val(), - pg = $(this).hasClass('ts-startRow') ? Math.floor( v/p.size ) + 1 : v; - c.$table.trigger('pageSet.pager', [ pg ]); - }); + tsp.pagerArrows(c); + tsp.fixHeight(table, c); + if (p.initialized && completed !== false) { + if (c.debug) { + console.log('Pager: Triggering pagerComplete'); + } + c.$table.trigger('pagerComplete', c); + // save pager info to storage + if (wo.pager_savePages && ts.storage) { + ts.storage(table, wo.pager_storageKey, { + page : p.page, + size : p.size + }); + } } - } - tsp.pagerArrows(c); - tsp.fixHeight(table, c); - if (p.initialized && completed !== false) { - if (c.debug) { - ts.log('Pager: Triggering pagerComplete'); + }, + + buildPageSelect: function(p, c) { + // Filter the options page number link array if it's larger than 'pager_maxOptionSize' + // as large page set links will slow the browser on large dom inserts + var i, central_focus_size, focus_option_pages, insert_index, option_length, focus_length, + wo = c.widgetOptions, + pg = Math.min( p.totalPages, p.filteredPages ) || 1, + // make skip set size multiples of 5 + skip_set_size = Math.ceil( ( pg / wo.pager_maxOptionSize ) / 5 ) * 5, + large_collection = pg > wo.pager_maxOptionSize, + current_page = p.page + 1, + start_page = skip_set_size, + end_page = pg - skip_set_size, + option_pages = [ 1 ], + // construct default options pages array + option_pages_start_page = (large_collection) ? skip_set_size : 1; + + for ( i = option_pages_start_page; i <= pg; ) { + option_pages.push(i); + i = i + ( large_collection ? skip_set_size : 1 ); } - c.$table.trigger('pagerComplete', c); - // save pager info to storage - if (wo.pager_savePages && ts.storage) { - ts.storage(table, wo.pager_storageKey, { - page : p.page, - size : p.size + option_pages.push(pg); + + if (large_collection) { + focus_option_pages = []; + // don't allow central focus size to be > 5 on either side of current page + central_focus_size = Math.max( Math.floor( wo.pager_maxOptionSize / skip_set_size ) - 1, 5 ); + + start_page = current_page - central_focus_size; + if (start_page < 1) { start_page = 1; } + end_page = current_page + central_focus_size; + if (end_page > pg) { end_page = pg; } + // construct an array to get a focus set around the current page + for (i = start_page; i <= end_page ; i++) { + focus_option_pages.push(i); + } + + // keep unique values + option_pages = $.grep(option_pages, function(value, indx) { + return $.inArray(value, option_pages) === indx; }); - } - } - }, - - buildPageSelect: function(p, c) { - // Filter the options page number link array if it's larger than 'pager_maxOptionSize' - // as large page set links will slow the browser on large dom inserts - var i, central_focus_size, focus_option_pages, insert_index, option_length, focus_length, - wo = c.widgetOptions, - pg = Math.min( p.totalPages, p.filteredPages ) || 1, - // make skip set size multiples of 5 - skip_set_size = Math.ceil( ( pg / wo.pager_maxOptionSize ) / 5 ) * 5, - large_collection = pg > wo.pager_maxOptionSize, - current_page = p.page + 1, - start_page = skip_set_size, - end_page = pg - skip_set_size, - option_pages = [1], - // construct default options pages array - option_pages_start_page = (large_collection) ? skip_set_size : 1; - - for ( i = option_pages_start_page; i <= pg; ) { - option_pages.push(i); - i = i + ( large_collection ? skip_set_size : 1 ); - } - option_pages.push(pg); - - if (large_collection) { - focus_option_pages = []; - // don't allow central focus size to be > 5 on either side of current page - central_focus_size = Math.max( Math.floor( wo.pager_maxOptionSize / skip_set_size ) - 1, 5 ); - - start_page = current_page - central_focus_size; - if (start_page < 1) { start_page = 1; } - end_page = current_page + central_focus_size; - if (end_page > pg) { end_page = pg; } - // construct an array to get a focus set around the current page - for (i = start_page; i <= end_page ; i++) { - focus_option_pages.push(i); + + option_length = option_pages.length; + focus_length = focus_option_pages.length; + + // make sure at all option_pages aren't replaced + if (option_length - focus_length > skip_set_size / 2 && option_length + focus_length > wo.pager_maxOptionSize ) { + insert_index = Math.floor(option_length / 2) - Math.floor(focus_length / 2); + Array.prototype.splice.apply(option_pages, [ insert_index, focus_length ]); + } + option_pages = option_pages.concat(focus_option_pages); + } - // keep unique values + // keep unique values again option_pages = $.grep(option_pages, function(value, indx) { return $.inArray(value, option_pages) === indx; - }); + }) + .sort(function(a, b) { return a - b; }); - option_length = option_pages.length; - focus_length = focus_option_pages.length; + return option_pages; + }, - // make sure at all option_pages aren't replaced - if (option_length - focus_length > skip_set_size / 2 && option_length + focus_length > wo.pager_maxOptionSize ) { - insert_index = Math.floor(option_length / 2) - Math.floor(focus_length / 2); - Array.prototype.splice.apply(option_pages, [ insert_index, focus_length ]); + fixHeight: function(table, c) { + var d, h, + p = c.pager, + wo = c.widgetOptions, + $b = c.$tbodies.eq(0); + $b.find('tr.pagerSavedHeightSpacer').remove(); + if (wo.pager_fixedHeight && !p.isDisabled) { + h = $.data(table, 'pagerSavedHeight'); + if (h) { + d = h - $b.height(); + if ( d > 5 && $.data(table, 'pagerLastSize') === p.size && $b.children('tr:visible').length < p.size ) { + $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice(1) + '" style="height:' + d + 'px;"></tr>'); + } + } } - option_pages = option_pages.concat(focus_option_pages); - - } + }, - // keep unique values again - option_pages = $.grep(option_pages, function(value, indx) { - return $.inArray(value, option_pages) === indx; - }) - .sort(function(a,b) { return a - b; }); - - return option_pages; - }, - - fixHeight: function(table, c) { - var d, h, - p = c.pager, - wo = c.widgetOptions, - $b = c.$tbodies.eq(0); - $b.find('tr.pagerSavedHeightSpacer').remove(); - if (wo.pager_fixedHeight && !p.isDisabled) { - h = $.data(table, 'pagerSavedHeight'); - if (h) { - d = h - $b.height(); - if ( d > 5 && $.data(table, 'pagerLastSize') === p.size && $b.children('tr:visible').length < p.size ) { - $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice(1) + '" style="height:' + d + 'px;"></tr>'); - } + changeHeight: function(table, c) { + var h, $b = c.$tbodies.eq(0); + $b.find('tr.pagerSavedHeightSpacer').remove(); + if (!$b.children('tr:visible').length) { + $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice(1) + '"><td> </td></tr>'); } - } - }, + h = $b.children('tr').eq(0).height() * c.pager.size; + $.data(table, 'pagerSavedHeight', h); + tsp.fixHeight(table, c); + $.data(table, 'pagerLastSize', c.pager.size); + }, - changeHeight: function(table, c) { - var h, $b = c.$tbodies.eq(0); - $b.find('tr.pagerSavedHeightSpacer').remove(); - if (!$b.children('tr:visible').length) { - $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice(1) + '"><td> </td></tr>'); - } - h = $b.children('tr').eq(0).height() * c.pager.size; - $.data(table, 'pagerSavedHeight', h); - tsp.fixHeight(table, c); - $.data(table, 'pagerLastSize', c.pager.size); - }, - - hideRows: function(table, c){ - if (!c.widgetOptions.pager_ajaxUrl) { - var i, - lastIndex = 0, - p = c.pager, - wo = c.widgetOptions, - rows = c.$tbodies.eq(0).children('tr'), - l = rows.length, - s = ( p.page * p.size ), - e = s + p.size, - f = wo && wo.filter_filteredRow || 'filtered', - last = 0, // for cache indexing - j = 0; // size counter - p.cacheIndex = []; - for ( i = 0; i < l; i++ ){ - if ( !rows[i].className.match(f) ) { - if (j === s && rows[i].className.match(c.cssChildRow)) { - // hide child rows @ start of pager (if already visible) - rows[i].style.display = 'none'; - } else { - rows[i].style.display = ( j >= s && j < e ) ? '' : 'none'; - if ( last !== j && j >= s && j < e ) { - p.cacheIndex.push(i); - last = j; + hideRows: function(table, c){ + if ( !c.widgetOptions.pager_ajaxUrl ) { + var tbodyIndex, rowIndex, $rows, len, lastIndex, + p = c.pager, + wo = c.widgetOptions, + tbodyLen = c.$tbodies.length, + start = ( p.page * p.size ), + end = start + p.size, + filtr = wo && wo.filter_filteredRow || 'filtered', + last = 0, // for cache indexing + size = 0; // size counter + p.cacheIndex = []; + for ( tbodyIndex = 0; tbodyIndex < tbodyLen; tbodyIndex++ ) { + $rows = c.$tbodies.eq( tbodyIndex ).children( 'tr' ); + len = $rows.length; + lastIndex = 0; + last = 0; // for cache indexing + size = 0; // size counter + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { + if ( !$rows[ rowIndex ].className.match( filtr ) ) { + if ( size === start && $rows[ rowIndex ].className.match( c.cssChildRow ) ) { + // hide child rows @ start of pager (if already visible) + $rows[ rowIndex ].style.display = 'none'; + } else { + $rows[ rowIndex ].style.display = ( size >= start && size < end ) ? '' : 'none'; + if ( last !== size && size >= start && size < end ) { + p.cacheIndex.push( rowIndex ); + last = size; + } + // don't count child rows + size += $rows[ rowIndex ].className + .match( c.cssChildRow + '|' + c.selectorRemove.slice( 1 ) ) && !wo.pager_countChildRows ? 0 : 1; + if ( size === end && $rows[ rowIndex ].style.display !== 'none' && + $rows[ rowIndex ].className.match( ts.css.cssHasChild ) ) { + lastIndex = rowIndex; + } + } } - // don't count child rows - j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) && !wo.pager_countChildRows ? 0 : 1; - if ( j === e && rows[i].style.display !== 'none' && rows[i].className.match(ts.css.cssHasChild) ) { - lastIndex = i; + } + // add any attached child rows to last row of pager. Fixes part of issue #396 + if ( lastIndex > 0 && $rows[ lastIndex ].className.match( ts.css.cssHasChild ) ) { + while ( ++lastIndex < len && $rows[ lastIndex ].className.match( c.cssChildRow ) ) { + $rows[ lastIndex ].style.display = ''; } } } } - // add any attached child rows to last row of pager. Fixes part of issue #396 - if ( lastIndex > 0 && rows[lastIndex].className.match(ts.css.cssHasChild) ) { - while ( ++lastIndex < l && rows[lastIndex].className.match(c.cssChildRow) ) { - rows[lastIndex].style.display = ''; - } - } - } - }, - - hideRowsSetup: function(table, c){ - var p = c.pager; - p.size = parseInt( p.$size.val(), 10 ) || p.size || p.setSize || 10; - $.data(table, 'pagerLastSize', p.size); - tsp.pagerArrows(c); - if ( !c.widgetOptions.pager_removeRows ) { - tsp.hideRows(table, c); - c.$table.on('sortEnd.pager filterEnd.pager', function(){ + }, + + hideRowsSetup: function(table, c){ + var p = c.pager, + namespace = c.namespace + 'pager'; + p.size = parseInt( p.$size.val(), 10 ) || p.size || p.setSize || 10; + $.data(table, 'pagerLastSize', p.size); + tsp.pagerArrows(c); + if ( !c.widgetOptions.pager_removeRows ) { tsp.hideRows(table, c); - }); - } - }, - - renderAjax: function(data, table, c, xhr, exception){ - var p = c.pager, - wo = c.widgetOptions; - // process data - if ( $.isFunction(wo.pager_ajaxProcessing) ) { - // ajaxProcessing result: [ total, rows, headers ] - var i, j, t, hsh, $f, $sh, $headers, $h, icon, th, d, l, rr_count, len, - $table = c.$table, - tds = '', - result = wo.pager_ajaxProcessing(data, table, xhr) || [ 0, [] ], - hl = $table.find('thead th').length; - - // Clean up any previous error. - ts.showError(table); - - if ( exception ) { - if (c.debug) { - ts.log('Pager: >> Ajax Error', xhr, exception); - } - ts.showError(table, exception.message + ' (' + xhr.status + ')'); - c.$tbodies.eq(0).children('tr').detach(); - p.totalRows = 0; - } else { - // process ajax object - if (!$.isArray(result)) { - p.ajaxData = result; - c.totalRows = p.totalRows = result.total; - c.filteredRows = p.filteredRows = typeof result.filteredRows !== 'undefined' ? result.filteredRows : result.total; - th = result.headers; - d = result.rows || []; + c.$table.on('sortEnd filterEnd '.split(' ').join(namespace + ' '), function(){ + tsp.hideRows(table, c); + }); + } + }, + + renderAjax: function(data, table, c, xhr, exception){ + var p = c.pager, + wo = c.widgetOptions; + // process data + if ( $.isFunction(wo.pager_ajaxProcessing) ) { + // ajaxProcessing result: [ total, rows, headers ] + var i, j, t, hsh, $f, $sh, $headers, $h, icon, th, d, l, rr_count, len, + $table = c.$table, + tds = '', + result = wo.pager_ajaxProcessing(data, table, xhr) || [ 0, [] ], + hl = $table.find('thead th').length; + + // Clean up any previous error. + ts.showError(table); + + if ( exception ) { + if (c.debug) { + console.error('Pager: >> Ajax Error', xhr, exception); + } + ts.showError(table, exception.message + ' (' + xhr.status + ')'); + c.$tbodies.eq(0).children('tr').detach(); + p.totalRows = 0; } else { - // allow [ total, rows, headers ] or [ rows, total, headers ] - t = isNaN(result[0]) && !isNaN(result[1]); - // ensure a zero returned row count doesn't fail the logical || - rr_count = result[t ? 1 : 0]; - p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count; - // can't set filtered rows when returning an array - c.totalRows = c.filteredRows = p.filteredRows = p.totalRows; - // set row data to empty array if nothing found - see http://stackoverflow.com/q/30875583/145346 - d = p.totalRows === 0 ? [] : result[t ? 0 : 1] || []; // row data - th = result[2]; // headers - } - l = d && d.length; - if (d instanceof jQuery) { - if (wo.pager_processAjaxOnInit) { - // append jQuery object - c.$tbodies.eq(0).children('tr').detach(); - c.$tbodies.eq(0).append(d); + // process ajax object + if (!$.isArray(result)) { + p.ajaxData = result; + c.totalRows = p.totalRows = result.total; + c.filteredRows = p.filteredRows = typeof result.filteredRows !== 'undefined' ? result.filteredRows : result.total; + th = result.headers; + d = result.rows || []; + } else { + // allow [ total, rows, headers ] or [ rows, total, headers ] + t = isNaN(result[0]) && !isNaN(result[1]); + // ensure a zero returned row count doesn't fail the logical || + rr_count = result[t ? 1 : 0]; + p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count; + // can't set filtered rows when returning an array + c.totalRows = c.filteredRows = p.filteredRows = p.totalRows; + // set row data to empty array if nothing found - see http://stackoverflow.com/q/30875583/145346 + d = p.totalRows === 0 ? [] : result[t ? 0 : 1] || []; // row data + th = result[2]; // headers } - } else if (l) { - // build table from array - for ( i = 0; i < l; i++ ) { - tds += '<tr>'; - for ( j = 0; j < d[i].length; j++ ) { - // build tbody cells; watch for data containing HTML markup - see #434 - tds += /^\s*<td/.test(d[i][j]) ? $.trim(d[i][j]) : '<td>' + d[i][j] + '</td>'; + l = d && d.length; + if (d instanceof jQuery) { + if (wo.pager_processAjaxOnInit) { + // append jQuery object + c.$tbodies.eq(0).children('tr').detach(); + c.$tbodies.eq(0).append(d); } - tds += '</tr>'; - } - // add rows to first tbody - if (wo.pager_processAjaxOnInit) { - c.$tbodies.eq(0).html( tds ); - } - } - wo.pager_processAjaxOnInit = true; - // only add new header text if the length matches - if ( th && th.length === hl ) { - hsh = $table.hasClass('hasStickyHeaders'); - $sh = hsh ? wo.$sticky.children('thead:first').children('tr').children() : ''; - $f = $table.find('tfoot tr:first').children(); - // don't change td headers (may contain pager) - $headers = c.$headers.filter( 'th '); - len = $headers.length; - for ( j = 0; j < len; j++ ) { - $h = $headers.eq( j ); - // add new test within the first span it finds, or just in the header - if ( $h.find('.' + ts.css.icon).length ) { - icon = $h.find('.' + ts.css.icon).clone(true); - $h.find('.tablesorter-header-inner').html( th[j] ).append(icon); - if ( hsh && $sh.length ) { - icon = $sh.eq(j).find('.' + ts.css.icon).clone(true); - $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ).append(icon); + } else if (l) { + // build table from array + for ( i = 0; i < l; i++ ) { + tds += '<tr>'; + for ( j = 0; j < d[i].length; j++ ) { + // build tbody cells; watch for data containing HTML markup - see #434 + tds += /^\s*<td/.test(d[i][j]) ? $.trim(d[i][j]) : '<td>' + d[i][j] + '</td>'; } - } else { - $h.find('.tablesorter-header-inner').html( th[j] ); - if (hsh && $sh.length) { - $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ); + tds += '</tr>'; + } + // add rows to first tbody + if (wo.pager_processAjaxOnInit) { + c.$tbodies.eq(0).html( tds ); + } + } + wo.pager_processAjaxOnInit = true; + // only add new header text if the length matches + if ( th && th.length === hl ) { + hsh = $table.hasClass('hasStickyHeaders'); + $sh = hsh ? wo.$sticky.children('thead:first').children('tr').children() : ''; + $f = $table.find('tfoot tr:first').children(); + // don't change td headers (may contain pager) + $headers = c.$headers.filter( 'th '); + len = $headers.length; + for ( j = 0; j < len; j++ ) { + $h = $headers.eq( j ); + // add new test within the first span it finds, or just in the header + if ( $h.find('.' + ts.css.icon).length ) { + icon = $h.find('.' + ts.css.icon).clone(true); + $h.find('.tablesorter-header-inner').html( th[j] ).append(icon); + if ( hsh && $sh.length ) { + icon = $sh.eq(j).find('.' + ts.css.icon).clone(true); + $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ).append(icon); + } + } else { + $h.find('.tablesorter-header-inner').html( th[j] ); + if (hsh && $sh.length) { + $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ); + } } + $f.eq(j).html( th[j] ); } - $f.eq(j).html( th[j] ); } } - } - if (c.showProcessing) { - ts.isProcessing(table); // remove loading icon - } - // make sure last pager settings are saved, prevents multiple server side calls with - // the same parameters - p.totalPages = Math.ceil( p.totalRows / ( p.size || p.setSize || 10 ) ); - p.last.totalRows = p.totalRows; - p.last.currentFilters = p.currentFilters; - p.last.sortList = (c.sortList || []).join(','); - p.initializing = false; - // update display without triggering pager complete... before updating cache - tsp.updatePageDisplay(table, c, false); - $table.trigger('updateCache', [function(){ - if (p.initialized) { - // apply widgets after table has rendered & after a delay to prevent - // multiple applyWidget blocking code from blocking this trigger - setTimeout(function(){ - if (c.debug) { - ts.log('Pager: Triggering pagerChange'); - } - $table - .trigger('applyWidgets') - .trigger('pagerChange', p); - tsp.updatePageDisplay(table, c); - }, 0); + if (c.showProcessing) { + ts.isProcessing(table); // remove loading icon } - }]); - } - if (!p.initialized) { - c.$table.trigger('applyWidgets'); - } - }, - - getAjax: function(table, c){ - var counter, - url = tsp.getAjaxUrl(table, c), - $doc = $(document), - p = c.pager; - if ( url !== '' ) { - if (c.showProcessing) { - ts.isProcessing(table, true); // show loading icon + // make sure last pager settings are saved, prevents multiple server side calls with + // the same parameters + p.totalPages = Math.ceil( p.totalRows / ( p.size || p.setSize || 10 ) ); + p.last.totalRows = p.totalRows; + p.last.currentFilters = p.currentFilters; + p.last.sortList = (c.sortList || []).join(','); + p.initializing = false; + // update display without triggering pager complete... before updating cache + tsp.updatePageDisplay(table, c, false); + $table.trigger('updateCache', [ function(){ + if (p.initialized) { + // apply widgets after table has rendered & after a delay to prevent + // multiple applyWidget blocking code from blocking this trigger + setTimeout(function(){ + if (c.debug) { + console.log('Pager: Triggering pagerChange'); + } + $table + .trigger('applyWidgets') + .trigger('pagerChange', p); + tsp.updatePageDisplay(table, c); + }, 0); + } + } ]); + } + if (!p.initialized) { + c.$table.trigger('applyWidgets'); } - $doc.on('ajaxError.pager', function(e, xhr, settings, exception) { - tsp.renderAjax(null, table, c, xhr, exception); - $doc.off('ajaxError.pager'); - }); - counter = ++p.ajaxCounter; - p.last.ajaxUrl = url; // remember processed url - p.ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl - p.ajaxObject.success = function(data, status, jqxhr) { - // Refuse to process old ajax commands that were overwritten by new ones - see #443 - if (counter < p.ajaxCounter){ - return; + }, + + getAjax: function(table, c){ + var counter, + url = tsp.getAjaxUrl(table, c), + $doc = $(document), + namespace = c.namespace + 'pager', + p = c.pager; + if ( url !== '' ) { + if (c.showProcessing) { + ts.isProcessing(table, true); // show loading icon } - tsp.renderAjax(data, table, c, jqxhr); - $doc.off('ajaxError.pager'); + $doc.on('ajaxError' + namespace, function(e, xhr, settings, exception) { + tsp.renderAjax(null, table, c, xhr, exception); + $doc.off('ajaxError' + namespace); + }); + counter = ++p.ajaxCounter; + p.last.ajaxUrl = url; // remember processed url + p.ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl + p.ajaxObject.success = function(data, status, jqxhr) { + // Refuse to process old ajax commands that were overwritten by new ones - see #443 + if (counter < p.ajaxCounter){ + return; + } + tsp.renderAjax(data, table, c, jqxhr); + $doc.off('ajaxError' + namespace); if (typeof p.oldAjaxSuccess === 'function') { p.oldAjaxSuccess(data); } - }; - if (c.debug) { - ts.log('Pager: Ajax initialized', p.ajaxObject); + }; + if (c.debug) { + console.log('Pager: Ajax initialized', p.ajaxObject); + } + $.ajax(p.ajaxObject); } - $.ajax(p.ajaxObject); - } - }, - - getAjaxUrl: function(table, c) { - var indx, len, - p = c.pager, - wo = c.widgetOptions, - url = (wo.pager_ajaxUrl) ? wo.pager_ajaxUrl - // allow using "{page+1}" in the url string to switch to a non-zero based index - .replace(/\{page([\-+]\d+)?\}/, function(s,n){ return p.page + (n ? parseInt(n, 10) : 0); }) - .replace(/\{size\}/g, p.size) : '', - sortList = c.sortList, - filterList = p.currentFilters || $(table).data('lastSearch') || [], - sortCol = url.match(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/), - filterCol = url.match(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/), - arry = []; - if (sortCol) { - sortCol = sortCol[1]; - len = sortList.length; - for (indx = 0; indx < len; indx++) { - arry.push(sortCol + '[' + sortList[indx][0] + ']=' + sortList[indx][1]); + }, + + getAjaxUrl: function(table, c) { + var indx, len, + p = c.pager, + wo = c.widgetOptions, + url = (wo.pager_ajaxUrl) ? wo.pager_ajaxUrl + // allow using '{page+1}' in the url string to switch to a non-zero based index + .replace(/\{page([\-+]\d+)?\}/, function(s, n){ return p.page + (n ? parseInt(n, 10) : 0); }) + .replace(/\{size\}/g, p.size) : '', + sortList = c.sortList, + filterList = p.currentFilters || $(table).data('lastSearch') || [], + sortCol = url.match(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/), + filterCol = url.match(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/), + arry = []; + if (sortCol) { + sortCol = sortCol[1]; + len = sortList.length; + for (indx = 0; indx < len; indx++) { + arry.push(sortCol + '[' + sortList[indx][0] + ']=' + sortList[indx][1]); + } + // if the arry is empty, just add the col parameter... '&{sortList:col}' becomes '&col' + url = url.replace(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : sortCol ); + arry = []; } - // if the arry is empty, just add the col parameter... "&{sortList:col}" becomes "&col" - url = url.replace(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : sortCol ); - arry = []; - } - if (filterCol) { - filterCol = filterCol[1]; - len = filterList.length; - for (indx = 0; indx < len; indx++) { - if (filterList[indx]) { - arry.push(filterCol + '[' + indx + ']=' + encodeURIComponent(filterList[indx])); + if (filterCol) { + filterCol = filterCol[1]; + len = filterList.length; + for (indx = 0; indx < len; indx++) { + if (filterList[indx]) { + arry.push(filterCol + '[' + indx + ']=' + encodeURIComponent(filterList[indx])); + } } + // if the arry is empty, just add the fcol parameter... '&{filterList:fcol}' becomes '&fcol' + url = url.replace(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : filterCol ); + p.currentFilters = filterList; } - // if the arry is empty, just add the fcol parameter... "&{filterList:fcol}" becomes "&fcol" - url = url.replace(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : filterCol ); - p.currentFilters = filterList; - } - if ( $.isFunction(wo.pager_customAjaxUrl) ) { - url = wo.pager_customAjaxUrl(table, url); - } - if (c.debug) { - ts.log('Pager: Ajax url = ' + url); - } - return url; - }, - - renderTable: function(table, rows) { - var $tb, index, count, added, - c = table.config, - p = c.pager, - wo = c.widgetOptions, - f = c.$table.hasClass('hasFilters'), - l = rows && rows.length || 0, // rows may be undefined - s = ( p.page * p.size ), - e = p.size; - if ( l < 1 ) { - if (c.debug) { - ts.log('Pager: >> No rows for pager to render'); + if ( $.isFunction(wo.pager_customAjaxUrl) ) { + url = wo.pager_customAjaxUrl(table, url); } - // empty table, abort! - return; - } - if ( p.page >= p.totalPages ) { - // lets not render the table more than once - return tsp.moveToLastPage(table, p); - } - p.cacheIndex = []; - p.isDisabled = false; // needed because sorting will change the page and re-enable the pager - if (p.initialized) { if (c.debug) { - ts.log('Pager: Triggering pagerChange'); + console.log('Pager: Ajax url = ' + url); } - c.$table.trigger('pagerChange', c); - } - if ( !wo.pager_removeRows ) { - tsp.hideRows(table, c); - } else { - ts.clearTableBody(table); - $tb = ts.processTbody(table, c.$tbodies.eq(0), true); - // not filtered, start from the calculated starting point (s) - // if filtered, start from zero - index = f ? 0 : s; - count = f ? 0 : s; - added = 0; - while (added < e && index < rows.length) { - if (!f || !/filtered/.test(rows[index][0].className)){ - count++; - if (count > s && added <= e) { - added++; - p.cacheIndex.push(index); - $tb.append(rows[index]); + return url; + }, + + renderTable: function(table, rows) { + var $tb, index, count, added, + c = table.config, + p = c.pager, + wo = c.widgetOptions, + f = c.$table.hasClass('hasFilters'), + l = rows && rows.length || 0, // rows may be undefined + s = ( p.page * p.size ), + e = p.size; + if ( l < 1 ) { + if (c.debug) { + console.warn('Pager: >> No rows for pager to render'); + } + // empty table, abort! + return; + } + if ( p.page >= p.totalPages ) { + // lets not render the table more than once + return tsp.moveToLastPage(table, p); + } + p.cacheIndex = []; + p.isDisabled = false; // needed because sorting will change the page and re-enable the pager + if (p.initialized) { + if (c.debug) { + console.log('Pager: Triggering pagerChange'); + } + c.$table.trigger('pagerChange', c); + } + if ( !wo.pager_removeRows ) { + tsp.hideRows(table, c); + } else { + ts.clearTableBody(table); + $tb = ts.processTbody(table, c.$tbodies.eq(0), true); + // not filtered, start from the calculated starting point (s) + // if filtered, start from zero + index = f ? 0 : s; + count = f ? 0 : s; + added = 0; + while (added < e && index < rows.length) { + if (!f || !/filtered/.test(rows[index][0].className)){ + count++; + if (count > s && added <= e) { + added++; + p.cacheIndex.push(index); + $tb.append(rows[index]); + } } + index++; } - index++; + ts.processTbody(table, $tb, false); } - ts.processTbody(table, $tb, false); - } - tsp.updatePageDisplay(table, c); + tsp.updatePageDisplay(table, c); - wo.pager_startPage = p.page; - wo.pager_size = p.size; - if (table.isUpdating) { - if (c.debug) { - ts.log('Pager: Triggering updateComplete'); + wo.pager_startPage = p.page; + wo.pager_size = p.size; + if (table.isUpdating) { + if (c.debug) { + console.log('Pager: Triggering updateComplete'); + } + c.$table.trigger('updateComplete', [ table, true ]); } - c.$table.trigger('updateComplete', [ table, true ]); - } - }, + }, - showAllRows: function(table, c){ - var index, $controls, len, - p = c.pager, - wo = c.widgetOptions; - if ( p.ajax ) { - tsp.pagerArrows(c, true); - } else { - p.isDisabled = true; - $.data(table, 'pagerLastPage', p.page); - $.data(table, 'pagerLastSize', p.size); - p.page = 0; - p.size = p.totalRows; - p.totalPages = 1; - c.$table - .addClass('pagerDisabled') - .removeAttr('aria-describedby') - .find('tr.pagerSavedHeightSpacer').remove(); - tsp.renderTable(table, c.rowsCopy); - c.$table.trigger('applyWidgets'); - if (c.debug) { - ts.log('Pager: Disabled'); - } - } - // disable size selector - $controls = p.$size - .add( p.$goto ) - .add( p.$container.find( '.ts-startRow, .ts-page ' ) ); - len = $controls.length; - for ( index = 0; index < len; index++ ) { - $controls.eq( index ) - .attr( 'aria-disabled', 'true' ) - .addClass( wo.pager_css.disabled )[0].disabled = true; - } - }, - - // updateCache if delayInit: true - // this is normally done by "appendToTable" function in the tablesorter core AFTER a sort - updateCache: function(table) { - var c = table.config, - p = c.pager; - c.$table.trigger('updateCache', [ function(){ - if ( !$.isEmptyObject(table.config.cache) ) { - var i, - rows = [], - n = table.config.cache[0].normalized; - p.totalRows = n.length; - for (i = 0; i < p.totalRows; i++) { - rows.push(n[i][c.columns].$row); + showAllRows: function(table, c){ + var index, $controls, len, + p = c.pager, + wo = c.widgetOptions; + if ( p.ajax ) { + tsp.pagerArrows(c, true); + } else { + p.isDisabled = true; + $.data(table, 'pagerLastPage', p.page); + $.data(table, 'pagerLastSize', p.size); + p.page = 0; + p.size = p.totalRows; + p.totalPages = 1; + c.$table + .addClass('pagerDisabled') + .removeAttr('aria-describedby') + .find('tr.pagerSavedHeightSpacer').remove(); + tsp.renderTable(table, c.rowsCopy); + c.$table.trigger('applyWidgets'); + if (c.debug) { + console.log('Pager: Disabled'); } - c.rowsCopy = rows; - tsp.moveToPage(table, p, true); - // clear out last search to force an update - p.last.currentFilters = [' ']; } - } ]); - }, + // disable size selector + $controls = p.$size + .add( p.$goto ) + .add( p.$container.find( '.ts-startRow, .ts-page ' ) ); + len = $controls.length; + for ( index = 0; index < len; index++ ) { + $controls.eq( index ) + .attr( 'aria-disabled', 'true' ) + .addClass( wo.pager_css.disabled )[0].disabled = true; + } + }, - moveToPage: function(table, p, pageMoved) { - if ( p.isDisabled ) { return; } - if ( pageMoved !== false && p.initialized && $.isEmptyObject(table.config.cache)) { - return tsp.updateCache(table); - } - var pg, c = table.config, - wo = c.widgetOptions, - l = p.last; - - // abort page move if the table has filters and has not been initialized - if (p.ajax && !wo.filter_initialized && ts.hasWidget(table, 'filter')) { return; } - - tsp.calcFilters(table, c); - pg = Math.min( p.totalPages, p.filteredPages ); - if ( p.page < 0 ) { p.page = 0; } - if ( p.page > ( pg - 1 ) && pg !== 0 ) { p.page = pg - 1; } - - // fixes issue where one current filter is [] and the other is ['','',''], - // making the next if comparison think the filters as different. Fixes #202. - l.currentFilters = (l.currentFilters || []).join('') === '' ? [] : l.currentFilters; - p.currentFilters = (p.currentFilters || []).join('') === '' ? [] : p.currentFilters; - // don't allow rendering multiple times on the same page/size/totalRows/filters/sorts - if ( l.page === p.page && l.size === p.size && l.totalRows === p.totalRows && - (l.currentFilters || []).join(',') === (p.currentFilters || []).join(',') && - // check for ajax url changes see #730 - (l.ajaxUrl || '') === (p.ajaxObject.url || '') && - // & ajax url option changes (dynamically add/remove/rename sort & filter parameters) - (l.optAjaxUrl || '') === (wo.pager_ajaxUrl || '') && - l.sortList === (c.sortList || []).join(',') ) { + // updateCache if delayInit: true + // this is normally done by 'appendToTable' function in the tablesorter core AFTER a sort + updateCache: function(table) { + var c = table.config, + p = c.pager; + c.$table.trigger('updateCache', [ function(){ + if ( !$.isEmptyObject(table.config.cache) ) { + var i, + rows = [], + n = table.config.cache[0].normalized; + p.totalRows = n.length; + for (i = 0; i < p.totalRows; i++) { + rows.push(n[i][c.columns].$row); + } + c.rowsCopy = rows; + tsp.moveToPage(table, p, true); + // clear out last search to force an update + p.last.currentFilters = [ ' ' ]; + } + } ]); + }, + + moveToPage: function(table, p, pageMoved) { + if ( p.isDisabled ) { return; } + if ( pageMoved !== false && p.initialized && $.isEmptyObject(table.config.cache)) { + return tsp.updateCache(table); + } + var pg, c = table.config, + wo = c.widgetOptions, + l = p.last; + + // abort page move if the table has filters and has not been initialized + if (p.ajax && !wo.filter_initialized && ts.hasWidget(table, 'filter')) { return; } + + tsp.calcFilters(table, c); + pg = Math.min( p.totalPages, p.filteredPages ); + if ( p.page < 0 ) { p.page = 0; } + if ( p.page > ( pg - 1 ) && pg !== 0 ) { p.page = pg - 1; } + + // fixes issue where one current filter is [] and the other is [ '', '', '' ], + // making the next if comparison think the filters as different. Fixes #202. + l.currentFilters = (l.currentFilters || []).join('') === '' ? [] : l.currentFilters; + p.currentFilters = (p.currentFilters || []).join('') === '' ? [] : p.currentFilters; + // don't allow rendering multiple times on the same page/size/totalRows/filters/sorts + if ( l.page === p.page && l.size === p.size && l.totalRows === p.totalRows && + (l.currentFilters || []).join(',') === (p.currentFilters || []).join(',') && + // check for ajax url changes see #730 + (l.ajaxUrl || '') === (p.ajaxObject.url || '') && + // & ajax url option changes (dynamically add/remove/rename sort & filter parameters) + (l.optAjaxUrl || '') === (wo.pager_ajaxUrl || '') && + l.sortList === (c.sortList || []).join(',') ) { return; } - if (c.debug) { - ts.log('Pager: Changing to page ' + p.page); - } - p.last = { - page : p.page, - size : p.size, - // fixes #408; modify sortList otherwise it auto-updates - sortList : (c.sortList || []).join(','), - totalRows : p.totalRows, - currentFilters : p.currentFilters || [], - ajaxUrl : p.ajaxObject.url || '', - optAjaxUrl : wo.pager_ajaxUrl - }; - if (p.ajax) { - tsp.getAjax(table, c); - } else if (!p.ajax) { - tsp.renderTable(table, c.rowsCopy); - } - $.data(table, 'pagerLastPage', p.page); - if (p.initialized && pageMoved !== false) { if (c.debug) { - ts.log('Pager: Triggering pageMoved'); + console.log('Pager: Changing to page ' + p.page); } - c.$table - .trigger('pageMoved', c) - .trigger('applyWidgets'); - if (!p.ajax && table.isUpdating) { + p.last = { + page : p.page, + size : p.size, + // fixes #408; modify sortList otherwise it auto-updates + sortList : (c.sortList || []).join(','), + totalRows : p.totalRows, + currentFilters : p.currentFilters || [], + ajaxUrl : p.ajaxObject.url || '', + optAjaxUrl : wo.pager_ajaxUrl + }; + if (p.ajax) { + tsp.getAjax(table, c); + } else if (!p.ajax) { + tsp.renderTable(table, c.rowsCopy); + } + $.data(table, 'pagerLastPage', p.page); + if (p.initialized && pageMoved !== false) { if (c.debug) { - ts.log('Pager: Triggering updateComplete'); + console.log('Pager: Triggering pageMoved'); + } + c.$table + .trigger('pageMoved', c) + .trigger('applyWidgets'); + if (!p.ajax && table.isUpdating) { + if (c.debug) { + console.log('Pager: Triggering updateComplete'); + } + c.$table.trigger('updateComplete', [ table, true ]); } - c.$table.trigger('updateComplete', [ table, true ]); } - } - }, - - setPageSize: function(table, size, c) { - var p = c.pager; - p.size = size || p.size || p.setSize || 10; - p.$size.val(p.size); - $.data(table, 'pagerLastPage', p.page); - $.data(table, 'pagerLastSize', p.size); - p.totalPages = Math.ceil( p.totalRows / p.size ); - p.filteredPages = Math.ceil( p.filteredRows / p.size ); - tsp.moveToPage(table, p, true); - }, - - moveToFirstPage: function(table, p) { - p.page = 0; - tsp.moveToPage(table, p, true); - }, - - moveToLastPage: function(table, p) { - p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 ); - tsp.moveToPage(table, p, true); - }, - - moveToNextPage: function(table, p) { - p.page++; - if ( p.page >= ( Math.min( p.totalPages, p.filteredPages ) - 1 ) ) { - p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 ); - } - tsp.moveToPage(table, p, true); - }, + }, + + setPageSize: function(table, size, c) { + var p = c.pager; + p.size = size || p.size || p.setSize || 10; + p.$size.val(p.size); + $.data(table, 'pagerLastPage', p.page); + $.data(table, 'pagerLastSize', p.size); + p.totalPages = Math.ceil( p.totalRows / p.size ); + p.filteredPages = Math.ceil( p.filteredRows / p.size ); + tsp.moveToPage(table, p, true); + }, - moveToPrevPage: function(table, p) { - p.page--; - if ( p.page <= 0 ) { + moveToFirstPage: function(table, p) { p.page = 0; - } - tsp.moveToPage(table, p, true); - }, - - destroyPager: function(table, c, refreshing){ - var p = c.pager; - p.initialized = false; - c.$table.off( $.trim(p.events.split(' ').join('.pager ')) ); - if (refreshing) { return; } - tsp.showAllRows(table, c); - p.$container.hide(); // hide pager - c.appender = null; // remove pager appender function - delete table.config.rowsCopy; - if (ts.storage) { - ts.storage(table, c.widgetOptions.pager_storageKey, ''); - } - }, - - enablePager: function(table, c, triggered){ - var info, p = c.pager; - p.isDisabled = false; - p.page = $.data(table, 'pagerLastPage') || p.page || 0; - p.size = $.data(table, 'pagerLastSize') || parseInt(p.$size.find('option[selected]').val(), 10) || p.size || p.setSize || 10; - p.$size.val(p.size); // set page size - p.totalPages = Math.ceil( Math.min( p.totalRows, p.filteredRows ) / p.size ); - c.$table.removeClass('pagerDisabled'); - // if table id exists, include page display with aria info - if ( table.id ) { - info = table.id + '_pager_info'; - p.$container.find(c.widgetOptions.pager_selectors.pageDisplay).attr('id', info); - c.$table.attr('aria-describedby', info); - } - tsp.changeHeight(table, c); - if ( triggered ) { - c.$table.trigger('updateRows'); - tsp.setPageSize(table, p.size, c); - tsp.hideRowsSetup(table, c); - if (c.debug) { - ts.log('Pager: Enabled'); + tsp.moveToPage(table, p, true); + }, + + moveToLastPage: function(table, p) { + p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 ); + tsp.moveToPage(table, p, true); + }, + + moveToNextPage: function(table, p) { + p.page++; + if ( p.page >= ( Math.min( p.totalPages, p.filteredPages ) - 1 ) ) { + p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 ); } - } - }, - - appender: function(table, rows) { - var c = table.config, - wo = c.widgetOptions, - p = c.pager; - if ( !p.ajax ) { - c.rowsCopy = rows; - p.totalRows = wo.pager_countChildRows ? c.$tbodies.eq(0).children('tr').length : rows.length; - p.size = $.data(table, 'pagerLastSize') || p.size || wo.pager_size || p.setSize || 10; - p.totalPages = Math.ceil( p.totalRows / p.size ); - tsp.moveToPage(table, p); - // update display here in case all rows are removed - tsp.updatePageDisplay(table, c, false); - } else { tsp.moveToPage(table, p, true); - } - } - -}; - -// see #486 -ts.showError = function( table, message ) { - var index, $row, c, wo, errorRow, - $table = $( table ), - len = $table.length; - for ( index = 0; index < len; index++ ) { - c = $table[ index ].config; - if ( c ) { - wo = c.widgetOptions; - errorRow = c.pager && c.pager.cssErrorRow || wo.pager_css && wo.pager_css.errorRow || 'tablesorter-errorRow'; - if ( typeof message === 'undefined' ) { - c.$table.find('thead').find(c.selectorRemove).remove(); + }, + + moveToPrevPage: function(table, p) { + p.page--; + if ( p.page <= 0 ) { + p.page = 0; + } + tsp.moveToPage(table, p, true); + }, + + destroyPager: function(table, c, refreshing){ + var p = c.pager, + namespace = c.namespace + 'pager'; + p.initialized = false; + c.$table.off( $.trim(p.events.split(' ').join(namespace + ' ')) ); + if (refreshing) { return; } + tsp.showAllRows(table, c); + p.$container.hide(); // hide pager + c.appender = null; // remove pager appender function + delete table.config.rowsCopy; + if (ts.storage) { + ts.storage(table, c.widgetOptions.pager_storageKey, ''); + } + }, + + enablePager: function(table, c, triggered){ + var info, p = c.pager; + p.isDisabled = false; + p.page = $.data(table, 'pagerLastPage') || p.page || 0; + p.size = $.data(table, 'pagerLastSize') || parseInt(p.$size.find('option[selected]').val(), 10) || p.size || p.setSize || 10; + p.$size.val(p.size); // set page size + p.totalPages = Math.ceil( Math.min( p.totalRows, p.filteredRows ) / p.size ); + c.$table.removeClass('pagerDisabled'); + // if table id exists, include page display with aria info + if ( table.id ) { + info = table.id + '_pager_info'; + p.$container.find(c.widgetOptions.pager_selectors.pageDisplay).attr('id', info); + c.$table.attr('aria-describedby', info); + } + tsp.changeHeight(table, c); + if ( triggered ) { + c.$table.trigger('updateRows'); + tsp.setPageSize(table, p.size, c); + tsp.hideRowsSetup(table, c); + if (c.debug) { + console.log('Pager: Enabled'); + } + } + }, + + appender: function(table, rows) { + var c = table.config, + wo = c.widgetOptions, + p = c.pager; + if ( !p.ajax ) { + c.rowsCopy = rows; + p.totalRows = wo.pager_countChildRows ? c.$tbodies.eq(0).children('tr').length : rows.length; + p.size = $.data(table, 'pagerLastSize') || p.size || wo.pager_size || p.setSize || 10; + p.totalPages = Math.ceil( p.totalRows / p.size ); + tsp.moveToPage(table, p); + // update display here in case all rows are removed + tsp.updatePageDisplay(table, c, false); } else { - $row = ( /tr\>/.test(message) ? $(message) : $('<tr><td colspan="' + c.columns + '">' + message + '</td></tr>') ) - .click(function(){ - $(this).remove(); - }) - // add error row to thead instead of tbody, or clicking on the header will result in a parser error - .appendTo( c.$table.find('thead:first') ) - .addClass( errorRow + ' ' + c.selectorRemove.slice(1) ) - .attr({ - role : 'alert', - 'aria-live' : 'assertive' - }); + tsp.moveToPage(table, p, true); + } + } + + }; + + // see #486 + ts.showError = function( table, message ) { + var index, $row, c, wo, errorRow, + $table = $( table ), + len = $table.length; + for ( index = 0; index < len; index++ ) { + c = $table[ index ].config; + if ( c ) { + wo = c.widgetOptions; + errorRow = c.pager && c.pager.cssErrorRow || wo.pager_css && wo.pager_css.errorRow || 'tablesorter-errorRow'; + if ( typeof message === 'undefined' ) { + c.$table.find('thead').find(c.selectorRemove).remove(); + } else { + $row = ( /tr\>/.test(message) ? $(message) : $('<tr><td colspan="' + c.columns + '">' + message + '</td></tr>') ) + .click(function(){ + $(this).remove(); + }) + // add error row to thead instead of tbody, or clicking on the header will result in a parser error + .appendTo( c.$table.find('thead:first') ) + .addClass( errorRow + ' ' + c.selectorRemove.slice(1) ) + .attr({ + role : 'alert', + 'aria-live' : 'assertive' + }); + } } } - } -}; + }; })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js index e7a53bb..eca57fa 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js @@ -4,120 +4,120 @@ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ ;(function($){ -"use strict"; - -var ts = $.tablesorter, - -printTable = ts.printTable = { - - event : 'printTable', - basicStyle : 'table, tr, td, th { border : solid 1px black; border-collapse : collapse; } td, th { padding: 2px; }', - - init : function(c) { - c.$table - .unbind(printTable.event) - .bind(printTable.event, function(){ - // explicitly use table.config.widgetOptions because we want - // the most up-to-date values; not the "wo" from initialization - printTable.process(c, c.widgetOptions); + 'use strict'; + + var ts = $.tablesorter, + + printTable = ts.printTable = { + + event : 'printTable', + basicStyle : 'table, tr, td, th { border : solid 1px black; border-collapse : collapse; } td, th { padding: 2px; }', + + init : function(c) { + c.$table + .unbind(printTable.event) + .bind(printTable.event, function(){ + // explicitly use table.config.widgetOptions because we want + // the most up-to-date values; not the 'wo' from initialization + printTable.process(c, c.widgetOptions); + }); + }, + + process : function(c, wo) { + var $this, + $table = $('<div/>').append(c.$table.clone()), + printStyle = printTable.basicStyle + 'table { width: 100% }' + + // hide filter row + '.tablesorter-filter-row { display: none }' + + // hide sort arrows + '.tablesorter-header { background-image: none !important; }'; + + // replace content with data-attribute content + $table.find('[' + wo.print_dataAttrib + ']').each(function(){ + $this = $(this); + $this.text( $this.attr(wo.print_dataAttrib) ); }); - }, - - process : function(c, wo) { - var $this, - $table = $('<div/>').append(c.$table.clone()), - printStyle = printTable.basicStyle + 'table { width: 100% }' + - // hide filter row - '.tablesorter-filter-row { display: none }' + - // hide sort arrows - '.tablesorter-header { background-image: none !important; }'; - - // replace content with data-attribute content - $table.find('[' + wo.print_dataAttrib + ']').each(function(){ - $this = $(this); - $this.text( $this.attr(wo.print_dataAttrib) ); - }); - - // === rows === - // Assume "visible" means rows hidden by the pager (rows set to "display:none") - // or hidden by a class name which is added to the wo.print_extraCSS definition - if (/a/i.test(wo.print_rows)) { - // force show of all rows - printStyle += 'tbody tr { display: table-row !important; }'; - } else if (/f/i.test(wo.print_rows)) { - // add definition to show all non-filtered rows (cells hidden by the pager) - printStyle += 'tbody tr:not(.' + (wo.filter_filteredRow || 'filtered') + ') { display: table-row !important; }'; - } - // === columns === - // columnSelector -> c.selector.$style - // Assume "visible" means hidden columns have a "display:none" style, or a class name - // add the definition to the wo.print_extraCSS option - if (/s/i.test(wo.print_columns) && c.selector && c.widgets.indexOf('columnSelector') >= 0) { - // show selected (visible) columns; make a copy of the columnSelector widget css (not media queries) - printStyle += wo.columnSelector_mediaquery && c.selector.auto ? '' : c.selector.$style.text(); - } else if (/a/i.test(wo.print_columns)) { - // force show all cells - printStyle += 'td, th { display: table-cell !important; }'; + // === rows === + // Assume 'visible' means rows hidden by the pager (rows set to 'display:none') + // or hidden by a class name which is added to the wo.print_extraCSS definition + if (/a/i.test(wo.print_rows)) { + // force show of all rows + printStyle += 'tbody tr { display: table-row !important; }'; + } else if (/f/i.test(wo.print_rows)) { + // add definition to show all non-filtered rows (cells hidden by the pager) + printStyle += 'tbody tr:not(.' + (wo.filter_filteredRow || 'filtered') + ') { display: table-row !important; }'; + } + + // === columns === + // columnSelector -> c.selector.$style + // Assume 'visible' means hidden columns have a 'display:none' style, or a class name + // add the definition to the wo.print_extraCSS option + if (/s/i.test(wo.print_columns) && c.selector && c.widgets.indexOf('columnSelector') >= 0) { + // show selected (visible) columns; make a copy of the columnSelector widget css (not media queries) + printStyle += wo.columnSelector_mediaquery && c.selector.auto ? '' : c.selector.$style.text(); + } else if (/a/i.test(wo.print_columns)) { + // force show all cells + printStyle += 'td, th { display: table-cell !important; }'; + } + + printStyle += wo.print_extraCSS; + + // callback function + if ( $.isFunction(wo.print_callback) ) { + wo.print_callback( c, $table, printStyle ); + } else { + printTable.printOutput(c, $table.html(), printStyle); + } + + }, // end process + + printOutput : function(c, data, style) { + var wo = c.widgetOptions, + generator = window.open('', wo.print_title, 'width=500,height=300'), + t = wo.print_title || c.$table.find('caption').text() || c.$table[0].id || document.title || 'table'; + generator.document.write( + '<html><head><title>' + t + '</title>' + + ( wo.print_styleSheet ? '<link rel="stylesheet" href="' + wo.print_styleSheet + '">' : '' ) + + '<style>' + style + '</style>' + + '</head><body>' + data + '</body></html>' + ); + generator.document.close(); + generator.print(); + generator.close(); + return true; + }, + + remove : function(c) { + c.$table.off(printTable.event); } - printStyle += wo.print_extraCSS; - - // callback function - if ( $.isFunction(wo.print_callback) ) { - wo.print_callback( c, $table, printStyle ); - } else { - printTable.printOutput(c, $table.html(), printStyle); + }; + + ts.addWidget({ + id: 'print', + options: { + print_title : '', // this option > caption > table id > 'table' + print_dataAttrib : 'data-name', // header attrib containing modified header name + print_rows : 'filtered', // (a)ll, (v)isible or (f)iltered + print_columns : 'selected', // (a)ll or (s)elected (if columnSelector widget is added) + print_extraCSS : '', // add any extra css definitions for the popup window here + print_styleSheet : '', // add the url of your print stylesheet + // callback executed when processing completes + // to continue printing, use the following function: + // function( config, $table, printStyle ) { + // // do something to the table or printStyle string + // $.tablesorter.printTable.printOutput( config, $table.html(), printStyle ); + // } + print_callback : null + }, + init: function(table, thisWidget, c) { + printTable.init(c); + }, + remove: function(table, c){ + printTable.remove(c); } - }, // end process - - printOutput : function(c, data, style) { - var wo = c.widgetOptions, - generator = window.open('', wo.print_title, 'width=500,height=300'), - t = wo.print_title || c.$table.find('caption').text() || c.$table[0].id || document.title || 'table'; - generator.document.write( - '<html><head><title>' + t + '</title>' + - ( wo.print_styleSheet ? '<link rel="stylesheet" href="' + wo.print_styleSheet + '">' : '' ) + - '<style>' + style + '</style>' + - '</head><body>' + data + '</body></html>' - ); - generator.document.close(); - generator.print(); - generator.close(); - return true; - }, - - remove : function(c) { - c.$table.off(printTable.event); - } - -}; - -ts.addWidget({ - id: 'print', - options: { - print_title : '', // this option > caption > table id > "table" - print_dataAttrib : 'data-name', // header attrib containing modified header name - print_rows : 'filtered', // (a)ll, (v)isible or (f)iltered - print_columns : 'selected', // (a)ll or (s)elected (if columnSelector widget is added) - print_extraCSS : '', // add any extra css definitions for the popup window here - print_styleSheet : '', // add the url of your print stylesheet - // callback executed when processing completes - // to continue printing, use the following function: - // function( config, $table, printStyle ) { - // // do something to the table or printStyle string - // $.tablesorter.printTable.printOutput( config, $table.html(), printStyle ); - // } - print_callback : null - }, - init: function(table, thisWidget, c) { - printTable.init(c); - }, - remove: function(table, c){ - printTable.remove(c); - } - -}); + }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-reflow.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-reflow.js index 063ebeb..781e20a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-reflow.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-reflow.js @@ -50,131 +50,130 @@ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ ;(function($){ -"use strict"; + 'use strict'; -var ts = $.tablesorter, + var ts = $.tablesorter, -tablereflow = { - // simple reflow - // add data-attribute to each cell which shows when media query is active - // this widget DOES NOT WORK on a table with multiple thead rows - init : function(table, c, wo) { - var $this, - title = wo.reflow_dataAttrib, - header = wo.reflow_headerAttrib, - headers = []; - c.$table - .addClass(wo.reflow_className) - .off('refresh.tsreflow updateComplete.tsreflow2') - // emulate jQuery Mobile refresh - // https://api.jquerymobile.com/table-reflow/#method-refresh - .on('refresh.tsreflow updateComplete.tsreflow2', function(){ - tablereflow.init(table, c, wo); - }); - c.$headers.each(function(){ - $this = $(this); - headers.push( $.trim( $this.attr(header) || $this.text() ) ); - }); - c.$tbodies.children().each(function(){ - $(this).children().each(function(i){ - $(this).attr(title, headers[i]); + tablereflow = { + // simple reflow + // add data-attribute to each cell which shows when media query is active + // this widget DOES NOT WORK on a table with multiple thead rows + init : function(table, c, wo) { + var $this, + title = wo.reflow_dataAttrib, + header = wo.reflow_headerAttrib, + headers = []; + c.$table + .addClass(wo.reflow_className) + .off('refresh.tsreflow updateComplete.tsreflow2') + // emulate jQuery Mobile refresh + // https://api.jquerymobile.com/table-reflow/#method-refresh + .on('refresh.tsreflow updateComplete.tsreflow2', function(){ + tablereflow.init(table, c, wo); + }); + c.$headers.each(function(){ + $this = $(this); + headers.push( $.trim( $this.attr(header) || $this.text() ) ); }); - }); - }, - init2: function(table, c, wo) { - var $this, $tbody, i, $hdr, txt, len, - cols = c.columns, - header = wo.reflow2_headerAttrib, - headers = []; - c.$table - .addClass(wo.reflow2_className) - .off('refresh.tsreflow2 updateComplete.tsreflow2') - // emulate jQuery Mobile refresh - // https://api.jquerymobile.com/table-reflow/#method-refresh - .on('refresh.tsreflow2 updateComplete.tsreflow2', function(){ - tablereflow.init2(table, c, wo); + c.$tbodies.children().each(function(){ + $(this).children().each(function(i){ + $(this).attr(title, headers[i]); + }); }); + }, + init2: function(table, c, wo) { + var $this, $tbody, i, $hdr, txt, len, + cols = c.columns, + header = wo.reflow2_headerAttrib, + headers = []; + c.$table + .addClass(wo.reflow2_className) + .off('refresh.tsreflow2 updateComplete.tsreflow2') + // emulate jQuery Mobile refresh + // https://api.jquerymobile.com/table-reflow/#method-refresh + .on('refresh.tsreflow2 updateComplete.tsreflow2', function(){ + tablereflow.init2(table, c, wo); + }); - // add <b> to every table cell with thead cell contents - for (i = 0; i < cols; i++) { - $hdr = c.$headers.filter('[data-column="' + i + '"]'); - if ($hdr.length > 1) { - txt = []; - /*jshint loopfunc:true */ - $hdr.each(function(){ + // add <b> to every table cell with thead cell contents + for (i = 0; i < cols; i++) { + $hdr = c.$headers.filter('[data-column="' + i + '"]'); + if ($hdr.length > 1) { + txt = []; + /*jshint loopfunc:true */ + $hdr.each(function(){ + $this = $(this); + if (!$this.hasClass(wo.reflow2_classIgnore)) { + txt.push( $this.attr(header) || $this.text() ); + } + }); + } else { + txt = [ $hdr.attr(header) || $hdr.text() ]; + } + headers.push( txt ); + } + // include 'remove-me' class so these additional elements are removed before updating + txt = '<b class="' + c.selectorRemove.slice(1) + ' ' + wo.reflow2_labelClass; + c.$tbodies.children().each(function(){ + $tbody = ts.processTbody(table, $(this), true); + $tbody.children().each(function(j){ $this = $(this); - if (!$this.hasClass(wo.reflow2_classIgnore)) { - txt.push( $this.attr(header) || $this.text() ); + len = headers[j].length; + i = len - 1; + while (i >= 0) { + $this.prepend(txt + (i === 0 && len > 1 ? ' ' + wo.reflow2_labelTop : '') + '">' + headers[j][i] + '</b>'); + i--; } }); - } else { - txt = [ $hdr.attr(header) || $hdr.text() ]; - } - headers.push( txt ); - } - // include "remove-me" class so these additional elements are removed before updating - txt = '<b class="' + c.selectorRemove.slice(1) + ' ' + wo.reflow2_labelClass; - c.$tbodies.children().each(function(){ - $tbody = ts.processTbody(table, $(this), true); - $tbody.children().each(function(j){ - $this = $(this); - len = headers[j].length; - i = len - 1; - while (i >= 0) { - $this.prepend(txt + (i === 0 && len > 1 ? ' ' + wo.reflow2_labelTop : '') + '">' + headers[j][i] + '</b>'); - i--; - } + ts.processTbody(table, $tbody, false); }); - ts.processTbody(table, $tbody, false); - }); - }, - remove : function(table, c, wo) { - c.$table.removeClass(wo.reflow_className); - }, - remove2 : function(table, c, wo) { - c.$table.removeClass(wo.reflow2_className); - } -}; - -ts.addWidget({ - id: "reflow", - options: { - // class name added to make it responsive (class name within media query) - reflow_className : 'ui-table-reflow', - // header attribute containing modified header name - reflow_headerAttrib : 'data-name', - // data attribute added to each tbody cell - reflow_dataAttrib : 'data-title' - }, - init: function(table, thisWidget, c, wo) { - tablereflow.init(table, c, wo); - }, - remove: function(table, c, wo){ - tablereflow.remove(table, c, wo); - } -}); + }, + remove : function(table, c, wo) { + c.$table.removeClass(wo.reflow_className); + }, + remove2 : function(table, c, wo) { + c.$table.removeClass(wo.reflow2_className); + } + }; -ts.addWidget({ - id: "reflow2", - options: { - // class name added to make it responsive (class name within media query) - reflow2_className : 'ui-table-reflow', - // ignore header cell content with this class name - reflow2_classIgnore : 'ui-table-reflow-ignore', - // header attribute containing modified header name - reflow2_headerAttrib : 'data-name', - // class name applied to thead labels - reflow2_labelClass : 'ui-table-cell-label', - // class name applied to first row thead label - reflow2_labelTop : 'ui-table-cell-label-top' - }, - init: function(table, thisWidget, c, wo) { - tablereflow.init2(table, c, wo); - }, - remove: function(table, c, wo){ - tablereflow.remove2(table, c, wo); - } -}); + ts.addWidget({ + id: 'reflow', + options: { + // class name added to make it responsive (class name within media query) + reflow_className : 'ui-table-reflow', + // header attribute containing modified header name + reflow_headerAttrib : 'data-name', + // data attribute added to each tbody cell + reflow_dataAttrib : 'data-title' + }, + init: function(table, thisWidget, c, wo) { + tablereflow.init(table, c, wo); + }, + remove: function(table, c, wo){ + tablereflow.remove(table, c, wo); + } + }); + ts.addWidget({ + id: 'reflow2', + options: { + // class name added to make it responsive (class name within media query) + reflow2_className : 'ui-table-reflow', + // ignore header cell content with this class name + reflow2_classIgnore : 'ui-table-reflow-ignore', + // header attribute containing modified header name + reflow2_headerAttrib : 'data-name', + // class name applied to thead labels + reflow2_labelClass : 'ui-table-cell-label', + // class name applied to first row thead label + reflow2_labelTop : 'ui-table-cell-label-top' + }, + init: function(table, thisWidget, c, wo) { + tablereflow.init2(table, c, wo); + }, + remove: function(table, c, wo){ + tablereflow.remove2(table, c, wo); + } + }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js index ac91168..5a07ea4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js @@ -1,393 +1,394 @@ /*! Widget: resizable - updated 6/26/2015 (v2.22.2) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { -'use strict'; -var ts = $.tablesorter || {}; - -$.extend(ts.css, { - resizableContainer : 'tablesorter-resizable-container', - resizableHandle : 'tablesorter-resizable-handle', - resizableNoSelect : 'tablesorter-disableSelection', - resizableStorage : 'tablesorter-resizable' -}); - -// Add extra scroller css -$(function(){ - var s = '<style>' + - 'body.' + ts.css.resizableNoSelect + ' { -ms-user-select: none; -moz-user-select: -moz-none;' + - '-khtml-user-select: none; -webkit-user-select: none; user-select: none; }' + - '.' + ts.css.resizableContainer + ' { position: relative; height: 1px; }' + - // make handle z-index > than stickyHeader z-index, so the handle stays above sticky header - '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px;' + - 'top: 1px; cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' + - '</style>'; - $(s).appendTo('body'); -}); - -ts.resizable = { - init : function( c, wo ) { - if ( c.$table.hasClass( 'hasResizable' ) ) { return; } - c.$table.addClass( 'hasResizable' ); - - var noResize, $header, column, storedSizes, tmp, - $table = c.$table, - $parent = $table.parent(), - marginTop = parseInt( $table.css( 'margin-top' ), 10 ), - - // internal variables - vars = wo.resizable_ = { - useStorage : ts.storage && wo.resizable !== false, - $wrap : $parent, - mouseXPosition : 0, - $target : null, - $next : null, - overflow : $parent.css('overflow') === 'auto' || - $parent.css('overflow') === 'scroll' || - $parent.css('overflow-x') === 'auto' || - $parent.css('overflow-x') === 'scroll', - storedSizes : [] - }; - - // set default widths - ts.resizableReset( c.table, true ); - - // now get measurements! - vars.tableWidth = $table.width(); - // attempt to autodetect - vars.fullWidth = Math.abs( $parent.width() - vars.tableWidth ) < 20; - - /* - // Hacky method to determine if table width is set to "auto" - // http://stackoverflow.com/a/20892048/145346 - if ( !vars.fullWidth ) { - tmp = $table.width(); - $header = $table.wrap('<span>').parent(); // temp variable - storedSizes = parseInt( $table.css( 'margin-left' ), 10 ) || 0; - $table.css( 'margin-left', storedSizes + 50 ); - vars.tableWidth = $header.width() > tmp ? 'auto' : tmp; - $table.css( 'margin-left', storedSizes ? storedSizes : '' ); - $header = null; - $table.unwrap('<span>'); - } - */ + 'use strict'; + var ts = $.tablesorter || {}; + + $.extend(ts.css, { + resizableContainer : 'tablesorter-resizable-container', + resizableHandle : 'tablesorter-resizable-handle', + resizableNoSelect : 'tablesorter-disableSelection', + resizableStorage : 'tablesorter-resizable' + }); - if ( vars.useStorage && vars.overflow ) { - // save table width - ts.storage( c.table, 'tablesorter-table-original-css-width', vars.tableWidth ); - tmp = ts.storage( c.table, 'tablesorter-table-resized-width' ) || 'auto'; - ts.resizable.setWidth( $table, tmp, true ); - } - wo.resizable_.storedSizes = storedSizes = ( vars.useStorage ? - ts.storage( c.table, ts.css.resizableStorage ) : - [] ) || []; - ts.resizable.setWidths( c, wo, storedSizes ); - ts.resizable.updateStoredSizes( c, wo ); - - wo.$resizable_container = $( '<div class="' + ts.css.resizableContainer + '">' ) - .css({ top : marginTop }) - .insertBefore( $table ); - // add container - for ( column = 0; column < c.columns; column++ ) { - $header = c.$headerIndexed[ column ]; - tmp = ts.getColumnData( c.table, c.headers, column ); - noResize = ts.getData( $header, tmp, 'resizable' ) === 'false'; - if ( !noResize ) { - $( '<div class="' + ts.css.resizableHandle + '">' ) - .appendTo( wo.$resizable_container ) - .attr({ - 'data-column' : column, - 'unselectable' : 'on' - }) - .data( 'header', $header ) - .bind( 'selectstart', false ); + // Add extra scroller css + $(function(){ + var s = '<style>' + + 'body.' + ts.css.resizableNoSelect + ' { -ms-user-select: none; -moz-user-select: -moz-none;' + + '-khtml-user-select: none; -webkit-user-select: none; user-select: none; }' + + '.' + ts.css.resizableContainer + ' { position: relative; height: 1px; }' + + // make handle z-index > than stickyHeader z-index, so the handle stays above sticky header + '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px;' + + 'top: 1px; cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' + + '</style>'; + $(s).appendTo('body'); + }); + + ts.resizable = { + init : function( c, wo ) { + if ( c.$table.hasClass( 'hasResizable' ) ) { return; } + c.$table.addClass( 'hasResizable' ); + + var noResize, $header, column, storedSizes, tmp, + $table = c.$table, + $parent = $table.parent(), + marginTop = parseInt( $table.css( 'margin-top' ), 10 ), + + // internal variables + vars = wo.resizable_vars = { + useStorage : ts.storage && wo.resizable !== false, + $wrap : $parent, + mouseXPosition : 0, + $target : null, + $next : null, + overflow : $parent.css('overflow') === 'auto' || + $parent.css('overflow') === 'scroll' || + $parent.css('overflow-x') === 'auto' || + $parent.css('overflow-x') === 'scroll', + storedSizes : [] + }; + + // set default widths + ts.resizableReset( c.table, true ); + + // now get measurements! + vars.tableWidth = $table.width(); + // attempt to autodetect + vars.fullWidth = Math.abs( $parent.width() - vars.tableWidth ) < 20; + + /* + // Hacky method to determine if table width is set to 'auto' + // http://stackoverflow.com/a/20892048/145346 + if ( !vars.fullWidth ) { + tmp = $table.width(); + $header = $table.wrap('<span>').parent(); // temp variable + storedSizes = parseInt( $table.css( 'margin-left' ), 10 ) || 0; + $table.css( 'margin-left', storedSizes + 50 ); + vars.tableWidth = $header.width() > tmp ? 'auto' : tmp; + $table.css( 'margin-left', storedSizes ? storedSizes : '' ); + $header = null; + $table.unwrap('<span>'); } - } - $table.one('tablesorter-initialized', function() { - ts.resizable.setHandlePosition( c, wo ); - ts.resizable.bindings( this.config, this.config.widgetOptions ); - }); - }, - - updateStoredSizes : function( c, wo ) { - var column, $header, - len = c.columns, - vars = wo.resizable_; - vars.storedSizes = []; - for ( column = 0; column < len; column++ ) { - $header = c.$headerIndexed[ column ]; - vars.storedSizes[ column ] = $header.is(':visible') ? $header.width() : 0; - } - }, - - setWidth : function( $el, width, overflow ) { - // overflow tables need min & max width set as well - $el.css({ - 'width' : width, - 'min-width' : overflow ? width : '', - 'max-width' : overflow ? width : '' - }); - }, - - setWidths : function( c, wo, storedSizes ) { - var column, $temp, - vars = wo.resizable_, - $extra = $( c.namespace + '_extra_headers' ), - $col = c.$table.children( 'colgroup' ).children( 'col' ); - storedSizes = storedSizes || vars.storedSizes || []; - // process only if table ID or url match - if ( storedSizes.length ) { + */ + + if ( vars.useStorage && vars.overflow ) { + // save table width + ts.storage( c.table, 'tablesorter-table-original-css-width', vars.tableWidth ); + tmp = ts.storage( c.table, 'tablesorter-table-resized-width' ) || 'auto'; + ts.resizable.setWidth( $table, tmp, true ); + } + wo.resizable_vars.storedSizes = storedSizes = ( vars.useStorage ? + ts.storage( c.table, ts.css.resizableStorage ) : + [] ) || []; + ts.resizable.setWidths( c, wo, storedSizes ); + ts.resizable.updateStoredSizes( c, wo ); + + wo.$resizable_container = $( '<div class="' + ts.css.resizableContainer + '">' ) + .css({ top : marginTop }) + .insertBefore( $table ); + // add container for ( column = 0; column < c.columns; column++ ) { - // set saved resizable widths - ts.resizable.setWidth( c.$headerIndexed[ column ], storedSizes[ column ], vars.overflow ); - if ( $extra.length ) { - // stickyHeaders needs to modify min & max width as well - $temp = $extra.eq( column ).add( $col.eq( column ) ); - ts.resizable.setWidth( $temp, storedSizes[ column ], vars.overflow ); + $header = c.$headerIndexed[ column ]; + tmp = ts.getColumnData( c.table, c.headers, column ); + noResize = ts.getData( $header, tmp, 'resizable' ) === 'false'; + if ( !noResize ) { + $( '<div class="' + ts.css.resizableHandle + '">' ) + .appendTo( wo.$resizable_container ) + .attr({ + 'data-column' : column, + 'unselectable' : 'on' + }) + .data( 'header', $header ) + .bind( 'selectstart', false ); } } - $temp = $( c.namespace + '_extra_table' ); - if ( $temp.length && !ts.hasWidget( c.table, 'scroller' ) ) { - ts.resizable.setWidth( $temp, c.$table.outerWidth(), vars.overflow ); + $table.one('tablesorter-initialized', function() { + ts.resizable.setHandlePosition( c, wo ); + ts.resizable.bindings( this.config, this.config.widgetOptions ); + }); + }, + + updateStoredSizes : function( c, wo ) { + var column, $header, + len = c.columns, + vars = wo.resizable_vars; + vars.storedSizes = []; + for ( column = 0; column < len; column++ ) { + $header = c.$headerIndexed[ column ]; + vars.storedSizes[ column ] = $header.is(':visible') ? $header.width() : 0; } - } - }, - - setHandlePosition : function( c, wo ) { - var startPosition, - hasScroller = ts.hasWidget( c.table, 'scroller' ), - tableHeight = c.$table.height(), - $handles = wo.$resizable_container.children(), - handleCenter = Math.floor( $handles.width() / 2 ); - - if ( hasScroller ) { - tableHeight = 0; - c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function(){ - var $this = $(this); - // center table has a max-height set - tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); + }, + + setWidth : function( $el, width, overflow ) { + // overflow tables need min & max width set as well + $el.css({ + 'width' : width, + 'min-width' : overflow ? width : '', + 'max-width' : overflow ? width : '' }); - } - // subtract out table left position from resizable handles. Fixes #864 - startPosition = c.$table.position().left; - $handles.each( function() { - var $this = $(this), - column = parseInt( $this.attr( 'data-column' ), 10 ), - columns = c.columns - 1, - $header = $this.data( 'header' ); - if ( !$header ) { return; } // see #859 - if ( !$header.is(':visible') ) { - $this.hide(); - } else if ( column < columns || column === columns && wo.resizable_addLastColumn ) { - $this.css({ - display: 'inline-block', - height : tableHeight, - left : $header.position().left - startPosition + $header.outerWidth() - handleCenter + }, + + setWidths : function( c, wo, storedSizes ) { + var column, $temp, + vars = wo.resizable_vars, + $extra = $( c.namespace + '_extra_headers' ), + $col = c.$table.children( 'colgroup' ).children( 'col' ); + storedSizes = storedSizes || vars.storedSizes || []; + // process only if table ID or url match + if ( storedSizes.length ) { + for ( column = 0; column < c.columns; column++ ) { + // set saved resizable widths + ts.resizable.setWidth( c.$headerIndexed[ column ], storedSizes[ column ], vars.overflow ); + if ( $extra.length ) { + // stickyHeaders needs to modify min & max width as well + $temp = $extra.eq( column ).add( $col.eq( column ) ); + ts.resizable.setWidth( $temp, storedSizes[ column ], vars.overflow ); + } + } + $temp = $( c.namespace + '_extra_table' ); + if ( $temp.length && !ts.hasWidget( c.table, 'scroller' ) ) { + ts.resizable.setWidth( $temp, c.$table.outerWidth(), vars.overflow ); + } + } + }, + + setHandlePosition : function( c, wo ) { + var startPosition, + hasScroller = ts.hasWidget( c.table, 'scroller' ), + tableHeight = c.$table.height(), + $handles = wo.$resizable_container.children(), + handleCenter = Math.floor( $handles.width() / 2 ); + + if ( hasScroller ) { + tableHeight = 0; + c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function(){ + var $this = $(this); + // center table has a max-height set + tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); }); } - }); - }, - - // prevent text selection while dragging resize bar - toggleTextSelection : function( c, toggle ) { - var namespace = c.namespace + 'tsresize'; - c.widgetOptions.resizable_.disabled = toggle; - $( 'body' ).toggleClass( ts.css.resizableNoSelect, toggle ); - if ( toggle ) { - $( 'body' ) - .attr( 'unselectable', 'on' ) - .bind( 'selectstart' + namespace, false ); - } else { - $( 'body' ) - .removeAttr( 'unselectable' ) - .unbind( 'selectstart' + namespace ); - } - }, - - bindings : function( c, wo ) { - var namespace = c.namespace + 'tsresize'; - wo.$resizable_container.children().bind( 'mousedown', function( event ) { - // save header cell and mouse position - var column, - vars = wo.resizable_, - $extras = $( c.namespace + '_extra_headers' ), - $header = $( event.target ).data( 'header' ); - - column = parseInt( $header.attr( 'data-column' ), 10 ); - vars.$target = $header = $header.add( $extras.filter('[data-column="' + column + '"]') ); - vars.target = column; - - // if table is not as wide as it's parent, then resize the table - vars.$next = event.shiftKey || wo.resizable_targetLast ? - $header.parent().children().not( '.resizable-false' ).filter( ':last' ) : - $header.nextAll( ':not(.resizable-false)' ).eq( 0 ); + // subtract out table left position from resizable handles. Fixes #864 + startPosition = c.$table.position().left; + $handles.each( function() { + var $this = $(this), + column = parseInt( $this.attr( 'data-column' ), 10 ), + columns = c.columns - 1, + $header = $this.data( 'header' ); + if ( !$header ) { return; } // see #859 + if ( !$header.is(':visible') ) { + $this.hide(); + } else if ( column < columns || column === columns && wo.resizable_addLastColumn ) { + $this.css({ + display: 'inline-block', + height : tableHeight, + left : $header.position().left - startPosition + $header.outerWidth() - handleCenter + }); + } + }); + }, - column = parseInt( vars.$next.attr( 'data-column' ), 10 ); - vars.$next = vars.$next.add( $extras.filter('[data-column="' + column + '"]') ); - vars.next = column; + // prevent text selection while dragging resize bar + toggleTextSelection : function( c, toggle ) { + var namespace = c.namespace + 'tsresize'; + c.widgetOptions.resizable_vars.disabled = toggle; + $( 'body' ).toggleClass( ts.css.resizableNoSelect, toggle ); + if ( toggle ) { + $( 'body' ) + .attr( 'unselectable', 'on' ) + .bind( 'selectstart' + namespace, false ); + } else { + $( 'body' ) + .removeAttr( 'unselectable' ) + .unbind( 'selectstart' + namespace ); + } + }, - vars.mouseXPosition = event.pageX; - ts.resizable.updateStoredSizes( c, wo ); - ts.resizable.toggleTextSelection( c, true ); - }); + bindings : function( c, wo ) { + var namespace = c.namespace + 'tsresize'; + wo.$resizable_container.children().bind( 'mousedown', function( event ) { + // save header cell and mouse position + var column, + vars = wo.resizable_vars, + $extras = $( c.namespace + '_extra_headers' ), + $header = $( event.target ).data( 'header' ); + + column = parseInt( $header.attr( 'data-column' ), 10 ); + vars.$target = $header = $header.add( $extras.filter('[data-column="' + column + '"]') ); + vars.target = column; + + // if table is not as wide as it's parent, then resize the table + vars.$next = event.shiftKey || wo.resizable_targetLast ? + $header.parent().children().not( '.resizable-false' ).filter( ':last' ) : + $header.nextAll( ':not(.resizable-false)' ).eq( 0 ); + + column = parseInt( vars.$next.attr( 'data-column' ), 10 ); + vars.$next = vars.$next.add( $extras.filter('[data-column="' + column + '"]') ); + vars.next = column; + + vars.mouseXPosition = event.pageX; + ts.resizable.updateStoredSizes( c, wo ); + ts.resizable.toggleTextSelection( c, true ); + }); - $( document ) - .bind( 'mousemove' + namespace, function( event ) { - var vars = wo.resizable_; - // ignore mousemove if no mousedown - if ( !vars.disabled || vars.mouseXPosition === 0 || !vars.$target ) { return; } - if ( wo.resizable_throttle ) { - clearTimeout( vars.timer ); - vars.timer = setTimeout( function() { + $( document ) + .bind( 'mousemove' + namespace, function( event ) { + var vars = wo.resizable_vars; + // ignore mousemove if no mousedown + if ( !vars.disabled || vars.mouseXPosition === 0 || !vars.$target ) { return; } + if ( wo.resizable_throttle ) { + clearTimeout( vars.timer ); + vars.timer = setTimeout( function() { + ts.resizable.mouseMove( c, wo, event ); + }, isNaN( wo.resizable_throttle ) ? 5 : wo.resizable_throttle ); + } else { ts.resizable.mouseMove( c, wo, event ); - }, isNaN( wo.resizable_throttle ) ? 5 : wo.resizable_throttle ); - } else { - ts.resizable.mouseMove( c, wo, event ); - } - }) - .bind( 'mouseup' + namespace, function() { - if (!wo.resizable_.disabled) { return; } - ts.resizable.toggleTextSelection( c, false ); - ts.resizable.stopResize( c, wo ); + } + }) + .bind( 'mouseup' + namespace, function() { + if (!wo.resizable_vars.disabled) { return; } + ts.resizable.toggleTextSelection( c, false ); + ts.resizable.stopResize( c, wo ); + ts.resizable.setHandlePosition( c, wo ); + }); + + // resizeEnd event triggered by scroller widget + $( window ).bind( 'resize' + namespace + ' resizeEnd' + namespace, function() { ts.resizable.setHandlePosition( c, wo ); }); - // resizeEnd event triggered by scroller widget - $( window ).bind( 'resize' + namespace + ' resizeEnd' + namespace, function() { - ts.resizable.setHandlePosition( c, wo ); - }); + // right click to reset columns to default widths + c.$table + .bind( 'columnUpdate' + namespace, function() { + ts.resizable.setHandlePosition( c, wo ); + }) + .find( 'thead:first' ) + .add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) + .bind( 'contextmenu' + namespace, function() { + // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset + var allowClick = wo.resizable_vars.storedSizes.length === 0; + ts.resizableReset( c.table ); + ts.resizable.setHandlePosition( c, wo ); + wo.resizable_vars.storedSizes = []; + return allowClick; + }); - // right click to reset columns to default widths - c.$table - .bind( 'columnUpdate' + namespace, function() { - ts.resizable.setHandlePosition( c, wo ); - }) - .find( 'thead:first' ) - .add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) - .bind( 'contextmenu' + namespace, function() { - // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset - var allowClick = wo.resizable_.storedSizes.length === 0; - ts.resizableReset( c.table ); - ts.resizable.setHandlePosition( c, wo ); - wo.resizable_.storedSizes = []; - return allowClick; - }); + }, - }, - - mouseMove : function( c, wo, event ) { - if ( wo.resizable_.mouseXPosition === 0 || !wo.resizable_.$target ) { return; } - // resize columns - var column, - total = 0, - vars = wo.resizable_, - $next = vars.$next, - tar = vars.storedSizes[ vars.target ], - leftEdge = event.pageX - vars.mouseXPosition; - if ( vars.overflow ) { - if ( tar + leftEdge > 0 ) { - vars.storedSizes[ vars.target ] += leftEdge; - ts.resizable.setWidth( vars.$target, vars.storedSizes[ vars.target ], true ); - // update the entire table width - for ( column = 0; column < c.columns; column++ ) { - total += vars.storedSizes[ column ]; + mouseMove : function( c, wo, event ) { + if ( wo.resizable_vars.mouseXPosition === 0 || !wo.resizable_vars.$target ) { return; } + // resize columns + var column, + total = 0, + vars = wo.resizable_vars, + $next = vars.$next, + tar = vars.storedSizes[ vars.target ], + leftEdge = event.pageX - vars.mouseXPosition; + if ( vars.overflow ) { + if ( tar + leftEdge > 0 ) { + vars.storedSizes[ vars.target ] += leftEdge; + ts.resizable.setWidth( vars.$target, vars.storedSizes[ vars.target ], true ); + // update the entire table width + for ( column = 0; column < c.columns; column++ ) { + total += vars.storedSizes[ column ]; + } + ts.resizable.setWidth( c.$table.add( $( c.namespace + '_extra_table' ) ), total ); } - ts.resizable.setWidth( c.$table.add( $( c.namespace + '_extra_table' ) ), total ); + if ( !$next.length ) { + // if expanding right-most column, scroll the wrapper + vars.$wrap[0].scrollLeft = c.$table.width(); + } + } else if ( vars.fullWidth ) { + vars.storedSizes[ vars.target ] += leftEdge; + vars.storedSizes[ vars.next ] -= leftEdge; + ts.resizable.setWidths( c, wo ); + } else { + vars.storedSizes[ vars.target ] += leftEdge; + ts.resizable.setWidths( c, wo ); } - if ( !$next.length ) { - // if expanding right-most column, scroll the wrapper - vars.$wrap[0].scrollLeft = c.$table.width(); + vars.mouseXPosition = event.pageX; + // dynamically update sticky header widths + c.$table.trigger('stickyHeadersUpdate'); + }, + + stopResize : function( c, wo ) { + var vars = wo.resizable_vars; + ts.resizable.updateStoredSizes( c, wo ); + if ( vars.useStorage ) { + // save all column widths + ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); + ts.storage( c.table, 'tablesorter-table-resized-width', c.$table.width() ); } - } else if ( vars.fullWidth ) { - vars.storedSizes[ vars.target ] += leftEdge; - vars.storedSizes[ vars.next ] -= leftEdge; - ts.resizable.setWidths( c, wo ); - } else { - vars.storedSizes[ vars.target ] += leftEdge; - ts.resizable.setWidths( c, wo ); + vars.mouseXPosition = 0; + vars.$target = vars.$next = null; + // will update stickyHeaders, just in case, see #912 + c.$table.trigger('stickyHeadersUpdate'); } - vars.mouseXPosition = event.pageX; - // dynamically update sticky header widths - c.$table.trigger('stickyHeadersUpdate'); - }, - - stopResize : function( c, wo ) { - var vars = wo.resizable_; - ts.resizable.updateStoredSizes( c, wo ); - if ( vars.useStorage ) { - // save all column widths - ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); - ts.storage( c.table, 'tablesorter-table-resized-width', c.$table.width() ); - } - vars.mouseXPosition = 0; - vars.$target = vars.$next = null; - // will update stickyHeaders, just in case, see #912 - c.$table.trigger('stickyHeadersUpdate'); - } -}; - -// this widget saves the column widths if -// $.tablesorter.storage function is included -// ************************** -ts.addWidget({ - id: "resizable", - priority: 40, - options: { - resizable : true, // save column widths to storage - resizable_addLastColumn : false, - resizable_widths : [], - resizable_throttle : false, // set to true (5ms) or any number 0-10 range - resizable_targetLast : false, - resizable_fullWidth : null - }, - init: function(table, thisWidget, c, wo) { - ts.resizable.init( c, wo ); - }, - remove: function( table, c, wo, refreshing ) { - if (wo.$resizable_container) { - var namespace = c.namespace + 'tsresize'; - c.$table.add( $( c.namespace + '_extra_table' ) ) - .removeClass('hasResizable') - .children( 'thead' ).unbind( 'contextmenu' + namespace ); + }; + + // this widget saves the column widths if + // $.tablesorter.storage function is included + // ************************** + ts.addWidget({ + id: 'resizable', + priority: 40, + options: { + resizable : true, // save column widths to storage + resizable_addLastColumn : false, + resizable_widths : [], + resizable_throttle : false, // set to true (5ms) or any number 0-10 range + resizable_targetLast : false, + resizable_fullWidth : null + }, + init: function(table, thisWidget, c, wo) { + ts.resizable.init( c, wo ); + }, + remove: function( table, c, wo, refreshing ) { + if (wo.$resizable_container) { + var namespace = c.namespace + 'tsresize'; + c.$table.add( $( c.namespace + '_extra_table' ) ) + .removeClass('hasResizable') + .children( 'thead' ) + .unbind( 'contextmenu' + namespace ); wo.$resizable_container.remove(); - ts.resizable.toggleTextSelection( c, false ); - ts.resizableReset( table, refreshing ); - $( document ).unbind( 'mousemove' + namespace + ' mouseup' + namespace ); + ts.resizable.toggleTextSelection( c, false ); + ts.resizableReset( table, refreshing ); + $( document ).unbind( 'mousemove' + namespace + ' mouseup' + namespace ); + } } - } -}); - -ts.resizableReset = function( table, refreshing ) { - $( table ).each(function(){ - var index, $t, - c = this.config, - wo = c && c.widgetOptions, - vars = wo.resizable_; - if ( table && c && c.$headerIndexed.length ) { - // restore the initial table width - if ( vars.overflow && vars.tableWidth ) { - ts.resizable.setWidth( c.$table, vars.tableWidth, true ); - if ( vars.useStorage ) { - ts.storage( table, 'tablesorter-table-resized-width', 'auto' ); + }); + + ts.resizableReset = function( table, refreshing ) { + $( table ).each(function(){ + var index, $t, + c = this.config, + wo = c && c.widgetOptions, + vars = wo.resizable_vars; + if ( table && c && c.$headerIndexed.length ) { + // restore the initial table width + if ( vars.overflow && vars.tableWidth ) { + ts.resizable.setWidth( c.$table, vars.tableWidth, true ); + if ( vars.useStorage ) { + ts.storage( table, 'tablesorter-table-resized-width', 'auto' ); + } } - } - for ( index = 0; index < c.columns; index++ ) { - $t = c.$headerIndexed[ index ]; - if ( wo.resizable_widths && wo.resizable_widths[ index ] ) { - ts.resizable.setWidth( $t, wo.resizable_widths[ index ], vars.overflow ); - } else if ( !$t.hasClass( 'resizable-false' ) ) { - // don't clear the width of any column that is not resizable - ts.resizable.setWidth( $t, '', vars.overflow ); + for ( index = 0; index < c.columns; index++ ) { + $t = c.$headerIndexed[ index ]; + if ( wo.resizable_widths && wo.resizable_widths[ index ] ) { + ts.resizable.setWidth( $t, wo.resizable_widths[ index ], vars.overflow ); + } else if ( !$t.hasClass( 'resizable-false' ) ) { + // don't clear the width of any column that is not resizable + ts.resizable.setWidth( $t, '', vars.overflow ); + } } - } - // reset stickyHeader widths - c.$table.trigger( 'stickyHeadersUpdate' ); - if ( ts.storage && !refreshing ) { - ts.storage( this, ts.css.resizableStorage, {} ); + // reset stickyHeader widths + c.$table.trigger( 'stickyHeadersUpdate' ); + if ( ts.storage && !refreshing ) { + ts.storage( this, ts.css.resizableStorage, {} ); + } } - } - }); -}; + }); + }; })( jQuery, window ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js index d34e2ad..5cd0deb 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js @@ -1,68 +1,68 @@ /*! Widget: saveSort */ ;(function ($) { -'use strict'; -var ts = $.tablesorter || {}; + 'use strict'; + var ts = $.tablesorter || {}; -// this widget saves the last sort only if the -// saveSort widget option is true AND the -// $.tablesorter.storage function is included -// ************************** -ts.addWidget({ - id: 'saveSort', - priority: 20, - options: { - saveSort : true - }, - init: function(table, thisWidget, c, wo) { - // run widget format before all other widgets are applied to the table - thisWidget.format(table, c, wo, true); - }, - format: function(table, c, wo, init) { - var stored, time, - $table = c.$table, - saveSort = wo.saveSort !== false, // make saveSort active/inactive; default to true - sortList = { "sortList" : c.sortList }; - if (c.debug) { - time = new Date(); - } - if ($table.hasClass('hasSaveSort')) { - if (saveSort && table.hasInitialized && ts.storage) { - ts.storage( table, 'tablesorter-savesort', sortList ); - if (c.debug) { - ts.benchmark('saveSort widget: Saving last sort: ' + c.sortList, time); - } + // this widget saves the last sort only if the + // saveSort widget option is true AND the + // $.tablesorter.storage function is included + // ************************** + ts.addWidget({ + id: 'saveSort', + priority: 20, + options: { + saveSort : true + }, + init: function(table, thisWidget, c, wo) { + // run widget format before all other widgets are applied to the table + thisWidget.format(table, c, wo, true); + }, + format: function(table, c, wo, init) { + var stored, time, + $table = c.$table, + saveSort = wo.saveSort !== false, // make saveSort active/inactive; default to true + sortList = { 'sortList' : c.sortList }; + if (c.debug) { + time = new Date(); } - } else { - // set table sort on initial run of the widget - $table.addClass('hasSaveSort'); - sortList = ''; - // get data - if (ts.storage) { - stored = ts.storage( table, 'tablesorter-savesort' ); - sortList = (stored && stored.hasOwnProperty('sortList') && $.isArray(stored.sortList)) ? stored.sortList : ''; - if (c.debug) { - ts.benchmark('saveSort: Last sort loaded: "' + sortList + '"', time); + if ($table.hasClass('hasSaveSort')) { + if (saveSort && table.hasInitialized && ts.storage) { + ts.storage( table, 'tablesorter-savesort', sortList ); + if (c.debug) { + console.log('saveSort widget: Saving last sort: ' + c.sortList + ts.benchmark(time)); + } + } + } else { + // set table sort on initial run of the widget + $table.addClass('hasSaveSort'); + sortList = ''; + // get data + if (ts.storage) { + stored = ts.storage( table, 'tablesorter-savesort' ); + sortList = (stored && stored.hasOwnProperty('sortList') && $.isArray(stored.sortList)) ? stored.sortList : ''; + if (c.debug) { + console.log('saveSort: Last sort loaded: "' + sortList + '"' + ts.benchmark(time)); + } + $table.bind('saveSortReset', function(event) { + event.stopPropagation(); + ts.storage( table, 'tablesorter-savesort', '' ); + }); + } + // init is true when widget init is run, this will run this widget before all other widgets have initialized + // this method allows using this widget in the original tablesorter plugin; but then it will run all widgets twice. + if (init && sortList && sortList.length > 0) { + c.sortList = sortList; + } else if (table.hasInitialized && sortList && sortList.length > 0) { + // update sort change + $table.trigger('sorton', [ sortList ]); } - $table.bind('saveSortReset', function(event) { - event.stopPropagation(); - ts.storage( table, 'tablesorter-savesort', '' ); - }); - } - // init is true when widget init is run, this will run this widget before all other widgets have initialized - // this method allows using this widget in the original tablesorter plugin; but then it will run all widgets twice. - if (init && sortList && sortList.length > 0) { - c.sortList = sortList; - } else if (table.hasInitialized && sortList && sortList.length > 0) { - // update sort change - $table.trigger('sorton', [sortList]); } + }, + remove: function(table, c) { + c.$table.removeClass('hasSaveSort'); + // clear storage + if (ts.storage) { ts.storage( table, 'tablesorter-savesort', '' ); } } - }, - remove: function(table, c) { - c.$table.removeClass('hasSaveSort'); - // clear storage - if (ts.storage) { ts.storage( table, 'tablesorter-savesort', '' ); } - } -}); + }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index a02e6ee..823b040 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -1,4 +1,4 @@ -/*! Widget: scroller - updated 5/17/2015 (v2.22.0) *//* +/*! Widget: scroller - updated 7/28/2015 (v2.22.4) *//* Copyright (C) 2011 T. Connell & Associates, Inc. Dual-licensed under the MIT and GPL licenses @@ -33,873 +33,885 @@ */ /*jshint browser:true, jquery:true, unused:false */ ;( function( $, window ) { -'use strict'; - -var ts = $.tablesorter, - tscss = ts.css; - -$.extend( ts.css, { - scrollerWrap : 'tablesorter-scroller', - scrollerHeader : 'tablesorter-scroller-header', - scrollerTable : 'tablesorter-scroller-table', - scrollerFooter : 'tablesorter-scroller-footer', - scrollerFixed : 'tablesorter-scroller-fixed', - scrollerFixedPanel : 'tablesorter-scroller-fixed-panel', - scrollerHasFix : 'tablesorter-scroller-has-fixed-columns', - scrollerHideColumn : 'tablesorter-scroller-hidden-column', - scrollerHideElement : 'tablesorter-scroller-hidden', - scrollerSpacerRow : 'tablesorter-scroller-spacer', - scrollerBarSpacer : 'tablesorter-scroller-bar-spacer', - scrollerAddedHeight : 'tablesorter-scroller-added-height', - scrollerHack : 'tablesorter-scroller-scrollbar-hack', - // class name on table cannot start with 'tablesorter-' or the - // suffix "scroller-rtl" will match as a theme name - scrollerRtl : 'ts-scroller-rtl' -}); - -ts.addWidget({ - id : 'scroller', - priority : 60, // run after the filter widget - options : { - scroller_height : 300, - // pop table header into view while scrolling up the page - scroller_jumpToHeader : true, - // scroll tbody to top after sorting - scroller_upAfterSort : true, - // set number of fixed columns - scroller_fixedColumns : 0, - // add hover highlighting to the fixed column (disable if it causes slowing) - scroller_rowHighlight : 'hover', - // add a fixed column overlay for styling - scroller_addFixedOverlay : false, - // In tablesorter v2.19.0 the scroll bar width is auto-detected - // add a value here to override the auto-detected setting - scroller_barWidth : null - }, - format : function( table, c, wo ) { - if ( !c.isScrolling ) { - // initialize here instead of in widget init to give the - // filter widget time to finish building the filter row - ts.scroller.setup( c, wo ); - } - }, - remove : function( table, c, wo ) { - ts.scroller.remove( c, wo ); - } -}); - -/* Add window resizeEnd event */ -ts.window_resize = function() { - if ( ts.timer_resize ) { - clearTimeout( ts.timer_resize ); - } - ts.timer_resize = setTimeout( function() { - $( window ).trigger( 'resizeEnd' ); - }, 250 ); -}; - -// Add extra scroller css -$( function() { - var style = '<style>' + - /* overall wrapper & table section wrappers */ - '.' + tscss.scrollerWrap + ' { position: relative; overflow: hidden; }' + - /* add border-box sizing to all scroller widget tables; see #135 */ - '.' + tscss.scrollerWrap + ' * { box-sizing: border-box; }' + - '.' + tscss.scrollerHeader + ', .' + tscss.scrollerFooter + ' { position: relative; overflow: hidden; }' + - '.' + tscss.scrollerHeader + ' table.' + tscss.table + ' { margin-bottom: 0; }' + - /* always leave the scroll bar visible for tbody, or table overflows into the scrollbar - when height < max height (filtering) */ - '.' + tscss.scrollerTable + ' { position: relative; overflow: auto; }' + - '.' + tscss.scrollerTable + ' table.' + tscss.table + - ' { border-top: 0; margin-top: 0; margin-bottom: 0; overflow: hidden; }' + - /* hide footer in original table */ - '.' + tscss.scrollerTable + ' tfoot, .' + tscss.scrollerHideElement + ', .' + tscss.scrollerHideColumn + - ' { display: none; }' + - - /*** fixed column ***/ - /* disable pointer-events on fixed column wrapper or the user can't interact with the horizontal scrollbar */ - '.' + tscss.scrollerFixed + ', .' + tscss.scrollerFixed + ' .' + tscss.scrollerFixedPanel + - ' { pointer-events: none; }' + - /* enable pointer-events for fixed column children; see #135 & #878 */ - '.' + tscss.scrollerFixed + ' > div { pointer-events: all; }' + - '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixed + ' { position: absolute; top: 0; z-index: 1; left: 0 } ' + - '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixed + '.' + tscss.scrollerRtl + ' { left: auto; right: 0 } ' + - /* add horizontal scroll bar; set to "auto", see #135 */ - '.' + tscss.scrollerWrap + '.' + tscss.scrollerHasFix + ' > .' + tscss.scrollerTable + ' { overflow: auto; }' + - /* need to position the tbody & tfoot absolutely to hide the scrollbar & move the footer - below the horizontal scrollbar */ - '.' + tscss.scrollerFixed + ' .' + tscss.scrollerFooter + ' { position: absolute; bottom: 0; }' + - /* hide fixed tbody scrollbar - see http://goo.gl/VsLe6n - set overflow to auto here for mousewheel scroll */ - '.' + tscss.scrollerFixed + ' .' + tscss.scrollerTable + - ' { position: relative; left: 0; overflow: auto; -ms-overflow-style: none; }' + - '.' + tscss.scrollerFixed + ' .' + tscss.scrollerTable + '::-webkit-scrollbar { display: none; }' + - /*** fixed column panel ***/ - '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixedPanel + - ' { position: absolute; top: 0; bottom: 0; z-index: 2; left: 0; right: 0; } ' + - '</style>'; - $( style ).appendTo( 'body' ); -}); - -ts.scroller = { - - // Ugh.. Firefox misbehaves, so it needs to be detected - isFirefox : navigator.userAgent.toLowerCase().indexOf( 'firefox' ) > -1, - // old IE needs a wrap to hide the fixed column scrollbar; http://stackoverflow.com/a/24408672/145346 - isOldIE : document.all && !window.atob, - isIE : ( document.all && !window.atob ) || navigator.appVersion.indexOf( 'Trident/' ) > 0, - // http://stackoverflow.com/questions/7944460/detect-safari-browser - needed to position scrolling body - // when the table is set up in RTL direction - isSafari : navigator.userAgent.toLowerCase().indexOf( 'safari' ) > -1 && - navigator.userAgent.toLowerCase().indexOf( 'chrome' ) === -1, - - hasScrollBar : function( $target, checkWidth ) { - if ( checkWidth ) { - return $target.get(0).scrollWidth > $target.width(); - } else { - return $target.get(0).scrollHeight > $target.height(); + 'use strict'; + + var ts = $.tablesorter, + tscss = ts.css; + + $.extend( ts.css, { + scrollerWrap : 'tablesorter-scroller', + scrollerHeader : 'tablesorter-scroller-header', + scrollerTable : 'tablesorter-scroller-table', + scrollerFooter : 'tablesorter-scroller-footer', + scrollerFixed : 'tablesorter-scroller-fixed', + scrollerFixedPanel : 'tablesorter-scroller-fixed-panel', + scrollerHasFix : 'tablesorter-scroller-has-fixed-columns', + scrollerHideColumn : 'tablesorter-scroller-hidden-column', + scrollerHideElement : 'tablesorter-scroller-hidden', + scrollerSpacerRow : 'tablesorter-scroller-spacer', + scrollerBarSpacer : 'tablesorter-scroller-bar-spacer', + scrollerAddedHeight : 'tablesorter-scroller-added-height', + scrollerHack : 'tablesorter-scroller-scrollbar-hack', + // class name on table cannot start with 'tablesorter-' or the + // suffix 'scroller-rtl' will match as a theme name + scrollerRtl : 'ts-scroller-rtl' + }); + + ts.addWidget({ + id : 'scroller', + priority : 60, // run after the filter widget + options : { + scroller_height : 300, + // pop table header into view while scrolling up the page + scroller_jumpToHeader : true, + // scroll tbody to top after sorting + scroller_upAfterSort : true, + // set number of fixed columns + scroller_fixedColumns : 0, + // add hover highlighting to the fixed column (disable if it causes slowing) + scroller_rowHighlight : 'hover', + // add a fixed column overlay for styling + scroller_addFixedOverlay : false, + // In tablesorter v2.19.0 the scroll bar width is auto-detected + // add a value here to override the auto-detected setting + scroller_barWidth : null + }, + format : function( table, c, wo ) { + if ( !c.isScrolling ) { + // initialize here instead of in widget init to give the + // filter widget time to finish building the filter row + ts.scroller.setup( c, wo ); + } + }, + remove : function( table, c, wo ) { + ts.scroller.remove( c, wo ); } - }, + }); - setWidth : function( $el, width ) { - $el.css({ - 'width' : width, - 'min-width' : width, - 'max-width' : width - }); - }, - - // modified from http://davidwalsh.name/detect-scrollbar-width - getBarWidth : function() { - var $div = $( '<div>' ).css({ - 'position' : 'absolute', - 'top' : '-9999px', - 'left' : 0, - 'width' : '100px', - 'height' : '100px', - 'overflow' : 'scroll', - 'visibility' : 'hidden' - }).appendTo( 'body' ), - div = $div[0], - barWidth = div.offsetWidth - div.clientWidth; - $div.remove(); - return barWidth; - }, - - setup : function( c, wo ) { - var maxHt, tbHt, $hdr, $t, $hCells, $fCells, $tableWrap, events, tmp, - $win = $( window ), - tsScroller = ts.scroller, - namespace = c.namespace + 'tsscroller', - $foot = $(), - // c.namespace contains a unique tablesorter ID, per table - id = c.namespace.slice( 1 ) + 'tsscroller', - $table = c.$table; - - // force config.widthFixed option - this helps maintain proper alignment across cloned tables - c.widthFixed = true; - - wo.scroller_calcWidths = []; - wo.scroller_saved = [ 0, 0 ]; - wo.scroller_isBusy = true; - - // set scrollbar width & allow setting width to zero - wo.scroller_barSetWidth = wo.scroller_barWidth !== null ? - wo.scroller_barWidth : - ( tsScroller.getBarWidth() || 15 ); - - maxHt = wo.scroller_height || 300; - - $hdr = $( '<table class="' + $table.attr( 'class' ) + '" cellpadding=0 cellspacing=0>' + - $table.children( 'thead' )[ 0 ].outerHTML + '</table>' ); - wo.scroller_$header = $hdr.addClass( c.namespace.slice( 1 ) + '_extra_table' ); - - $t = $table.children( 'tfoot' ); - if ( $t.length ) { - $foot = $( '<table class="' + $table.attr( 'class' ) + - '" cellpadding=0 cellspacing=0 style="margin-top:0"></table>' ) - .addClass( c.namespace.slice( 1 ) + '_extra_table' ) - // maintain any bindings on the tfoot cells - .append( $t.clone( true ) ) - .wrap( '<div class="' + tscss.scrollerFooter + '"/>' ); - $fCells = $foot.children( 'tfoot' ).eq( 0 ).children( 'tr' ).children(); + /* Add window resizeEnd event */ + ts.window_resize = function() { + if ( ts.timer_resize ) { + clearTimeout( ts.timer_resize ); } - wo.scroller_$footer = $foot; + ts.timer_resize = setTimeout( function() { + $( window ).trigger( 'resizeEnd' ); + }, 250 ); + }; + + // Add extra scroller css + $( function() { + var style = '<style>' + + /* overall wrapper & table section wrappers */ + '.' + tscss.scrollerWrap + ' { position: relative; overflow: hidden; }' + + /* add border-box sizing to all scroller widget tables; see #135 */ + '.' + tscss.scrollerWrap + ' * { box-sizing: border-box; }' + + '.' + tscss.scrollerHeader + ', .' + tscss.scrollerFooter + ' { position: relative; overflow: hidden; }' + + '.' + tscss.scrollerHeader + ' table.' + tscss.table + ' { margin-bottom: 0; }' + + /* always leave the scroll bar visible for tbody, or table overflows into the scrollbar + when height < max height (filtering) */ + '.' + tscss.scrollerTable + ' { position: relative; overflow: auto; }' + + '.' + tscss.scrollerTable + ' table.' + tscss.table + + ' { border-top: 0; margin-top: 0; margin-bottom: 0; overflow: hidden; }' + + /* hide footer in original table */ + '.' + tscss.scrollerTable + ' tfoot, .' + tscss.scrollerHideElement + ', .' + tscss.scrollerHideColumn + + ' { display: none; }' + + + /*** fixed column ***/ + /* disable pointer-events on fixed column wrapper or the user can't interact with the horizontal scrollbar */ + '.' + tscss.scrollerFixed + ', .' + tscss.scrollerFixed + ' .' + tscss.scrollerFixedPanel + + ' { pointer-events: none; }' + + /* enable pointer-events for fixed column children; see #135 & #878 */ + '.' + tscss.scrollerFixed + ' > div { pointer-events: all; }' + + '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixed + ' { position: absolute; top: 0; z-index: 1; left: 0 } ' + + '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixed + '.' + tscss.scrollerRtl + ' { left: auto; right: 0 } ' + + /* add horizontal scroll bar; set to 'auto', see #135 */ + '.' + tscss.scrollerWrap + '.' + tscss.scrollerHasFix + ' > .' + tscss.scrollerTable + ' { overflow: auto; }' + + /* need to position the tbody & tfoot absolutely to hide the scrollbar & move the footer + below the horizontal scrollbar */ + '.' + tscss.scrollerFixed + ' .' + tscss.scrollerFooter + ' { position: absolute; bottom: 0; }' + + /* hide fixed tbody scrollbar - see http://goo.gl/VsLe6n - set overflow to auto here for mousewheel scroll */ + '.' + tscss.scrollerFixed + ' .' + tscss.scrollerTable + + ' { position: relative; left: 0; overflow: auto; -ms-overflow-style: none; }' + + '.' + tscss.scrollerFixed + ' .' + tscss.scrollerTable + '::-webkit-scrollbar { display: none; }' + + /*** fixed column panel ***/ + '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixedPanel + + ' { position: absolute; top: 0; bottom: 0; z-index: 2; left: 0; right: 0; } ' + + '</style>'; + $( style ).appendTo( 'body' ); + }); + + ts.scroller = { + + // Ugh.. Firefox misbehaves, so it needs to be detected + isFirefox : navigator.userAgent.toLowerCase().indexOf( 'firefox' ) > -1, + // old IE needs a wrap to hide the fixed column scrollbar; http://stackoverflow.com/a/24408672/145346 + isOldIE : document.all && !window.atob, + isIE : ( document.all && !window.atob ) || navigator.appVersion.indexOf( 'Trident/' ) > 0, + // http://stackoverflow.com/questions/7944460/detect-safari-browser - needed to position scrolling body + // when the table is set up in RTL direction + isSafari : navigator.userAgent.toLowerCase().indexOf( 'safari' ) > -1 && + navigator.userAgent.toLowerCase().indexOf( 'chrome' ) === -1, + + hasScrollBar : function( $target, checkWidth ) { + if ( checkWidth ) { + return $target.get(0).scrollWidth > $target.width(); + } else { + return $target.get(0).scrollHeight > $target.height(); + } + }, - $table - .wrap( '<div id="' + id + '" class="' + tscss.scrollerWrap + '" />' ) - .before( $hdr ) - // shrink filter row but don't completely hide it because the inputs/selectors may distort the columns - .find( '.' + tscss.filterRow ) - .addClass( tscss.filterRowHide ); + setWidth : function( $el, width ) { + $el.css({ + 'width' : width, + 'min-width' : width, + 'max-width' : width + }); + }, + + // modified from http://davidwalsh.name/detect-scrollbar-width + getBarWidth : function() { + var $div = $( '<div>' ).css({ + 'position' : 'absolute', + 'top' : '-9999px', + 'left' : 0, + 'width' : '100px', + 'height' : '100px', + 'overflow' : 'scroll', + 'visibility' : 'hidden' + }).appendTo( 'body' ), + div = $div[0], + barWidth = div.offsetWidth - div.clientWidth; + $div.remove(); + return barWidth; + }, + + setup : function( c, wo ) { + var maxHt, tbHt, $hdr, $t, $hCells, $fCells, $tableWrap, events, tmp, + $win = $( window ), + tsScroller = ts.scroller, + namespace = c.namespace + 'tsscroller', + $foot = $(), + // c.namespace contains a unique tablesorter ID, per table + id = c.namespace.slice( 1 ) + 'tsscroller', + $table = c.$table; + + // force config.widthFixed option - this helps maintain proper alignment across cloned tables + c.widthFixed = true; + + wo.scroller_calcWidths = []; + wo.scroller_saved = [ 0, 0 ]; + wo.scroller_isBusy = true; + + // set scrollbar width & allow setting width to zero + wo.scroller_barSetWidth = wo.scroller_barWidth !== null ? + wo.scroller_barWidth : + ( tsScroller.getBarWidth() || 15 ); + + maxHt = wo.scroller_height || 300; + + $hdr = $( '<table class="' + $table.attr( 'class' ) + '" cellpadding=0 cellspacing=0>' + + $table.children( 'thead' )[ 0 ].outerHTML + '</table>' ); + wo.scroller_$header = $hdr.addClass( c.namespace.slice( 1 ) + '_extra_table' ); + + $t = $table.children( 'tfoot' ); + if ( $t.length ) { + $foot = $( '<table class="' + $table.attr( 'class' ) + + '" cellpadding=0 cellspacing=0 style="margin-top:0"></table>' ) + .addClass( c.namespace.slice( 1 ) + '_extra_table' ) + // maintain any bindings on the tfoot cells + .append( $t.clone( true ) ) + .wrap( '<div class="' + tscss.scrollerFooter + '"/>' ); + $fCells = $foot.children( 'tfoot' ).eq( 0 ).children( 'tr' ).children(); + } + wo.scroller_$footer = $foot; - wo.scroller_$container = $table.parent(); + $table + .wrap( '<div id="' + id + '" class="' + tscss.scrollerWrap + '" />' ) + .before( $hdr ) + // shrink filter row but don't completely hide it because the inputs/selectors may distort the columns + .find( '.' + tscss.filterRow ) + .addClass( tscss.filterRowHide ); - if ( $foot.length ) { - // $foot.parent() to include <div> wrapper - $table.after( $foot.parent() ); - } + wo.scroller_$container = $table.parent(); - $hCells = $hdr - .wrap( '<div class="' + tscss.scrollerHeader + '" />' ) - .find( '.' + tscss.header ); + if ( $foot.length ) { + // $foot.parent() to include <div> wrapper + $table.after( $foot.parent() ); + } - // use max-height, so the height resizes dynamically while filtering - $table.wrap( '<div class="' + tscss.scrollerTable + '" style="max-height:' + maxHt + 'px;" />' ); - $tableWrap = $table.parent(); + $hCells = $hdr + .wrap( '<div class="' + tscss.scrollerHeader + '" />' ) + .find( '.' + tscss.header ); - // make scroller header sortable - ts.bindEvents( c.table, $hCells ); + // use max-height, so the height resizes dynamically while filtering + $table.wrap( '<div class="' + tscss.scrollerTable + '" style="max-height:' + maxHt + 'px;" />' ); + $tableWrap = $table.parent(); - // look for filter widget - if ( $table.hasClass( 'hasFilters' ) ) { - ts.filter.bindSearch( $table, $hdr.find( '.' + tscss.filter ) ); - } + // make scroller header sortable + ts.bindEvents( c.table, $hCells ); - $table - .find( 'thead' ) - .addClass( tscss.scrollerHideElement ); + // look for filter widget + if ( $table.hasClass( 'hasFilters' ) ) { + ts.filter.bindSearch( $table, $hdr.find( '.' + tscss.filter ) ); + } - tbHt = $tableWrap.parent().height(); + $table + .find( 'thead' ) + .addClass( tscss.scrollerHideElement ); + + tbHt = $tableWrap.parent().height(); + + // The header will always jump into view if scrolling the table body + $tableWrap + .off( 'scroll' + namespace ) + .on( 'scroll' + namespace, function() { + if ( wo.scroller_jumpToHeader ) { + var pos = $win.scrollTop() - $hdr.offset().top; + if ( $( this ).scrollTop() !== 0 && pos < tbHt && pos > 0 ) { + $win.scrollTop( $hdr.offset().top ); + } + } + $hdr + .parent() + .add( $foot.parent() ) + .scrollLeft( $( this ).scrollLeft() ); + }); - // The header will always jump into view if scrolling the table body - $tableWrap - .off( 'scroll' + namespace ) - .on( 'scroll' + namespace, function() { - if ( wo.scroller_jumpToHeader ) { - var pos = $win.scrollTop() - $hdr.offset().top; - if ( $( this ).scrollTop() !== 0 && pos < tbHt && pos > 0 ) { - $win.scrollTop( $hdr.offset().top ); + // resize/update events - filterEnd fires after "tablesorter-initialized" and "updateComplete" + events = ( ( ts.hasWidget( c.table, 'filter' ) ? 'filterEnd' : 'tablesorter-initialized updateComplete' ) + + ' sortEnd pagerComplete columnUpdate ' ).split( ' ' ).join( namespace + ' ' ); + + $table + .off( namespace ) + .on( 'sortEnd filterEnd'.split( ' ' ).join( namespace + ' ' ), function( event ) { + // Sorting, so scroll to top + if ( event.type === 'sortEnd' && wo.scroller_upAfterSort ) { + $tableWrap.animate({ + scrollTop : 0 + }, 'fast' ); + } else if ( wo.scroller_fixedColumns ) { + setTimeout( function() { + // restore previous scroll position + $tableWrap + .scrollTop( wo.scroller_saved[1] ) + .scrollLeft( wo.scroller_saved[0] ); + tsScroller.updateFixed( c, wo ); + }, 0 ); } - } - $hdr - .parent() - .add( $foot.parent() ) - .scrollLeft( $( this ).scrollLeft() ); - }); + }) + .on( 'setFixedColumnSize' + namespace, function( event, size ) { + var $wrap = wo.scroller_$container; + if ( typeof size !== 'undefined' && !isNaN( size ) ) { + wo.scroller_fixedColumns = parseInt( size, 10 ); + } + // remove fixed columns + tsScroller.removeFixed( c, wo ); + size = wo.scroller_fixedColumns; + if ( size > 0 && size < c.columns - 1 ) { + tsScroller.updateFixed( c, wo ); + } else if ( $wrap.hasClass( tscss.scrollerHasFix ) ) { + $wrap.removeClass( tscss.scrollerHasFix ); + // resize needed to make tables full width + tsScroller.resize( c, wo ); + } + }) + .on( events, function( event ) { + // Stop from running twice with pager + if ( ts.hasWidget( 'pager' ) && event.type === 'updateComplete' ) { + return; + } + if ( wo.scroller_fixedColumns > 0 ) { + tsScroller.updateFixed( c, wo ); + } + // adjust column sizes after an update + tsScroller.resize( c, wo ); + }); - // resize/update events - events = ( ( ts.hasWidget( c.table, 'filter' ) ? 'filterEnd' : 'tablesorter-initialized' ) + - ' updateComplete pagerComplete columnUpdate ' ).split( ' ' ).join( namespace + ' ' ); - - $table - .off( namespace ) - .on( 'sortEnd filterEnd'.split( ' ' ).join( namespace + ' ' ), function( event ) { - // Sorting, so scroll to top - if ( event.type === 'sortEnd' && wo.scroller_upAfterSort ) { - $tableWrap.animate({ - scrollTop : 0 - }, 'fast' ); - } else if ( wo.scroller_fixedColumns ) { - setTimeout( function() { - // restore previous scroll position - $tableWrap - .scrollTop( wo.scroller_saved[1] ) - .scrollLeft( wo.scroller_saved[0] ); - tsScroller.updateFixed( c, wo, false ); - }, 0 ); - } - }) - .on( 'setFixedColumnSize' + namespace, function( event, size ) { - var $wrap = wo.scroller_$container; - if ( typeof size !== 'undefined' && !isNaN( size ) ) { - wo.scroller_fixedColumns = parseInt( size, 10 ); - } - // remove fixed columns - tsScroller.removeFixed( c, wo ); - size = wo.scroller_fixedColumns; - if ( size > 0 && size < c.columns - 1 ) { - tsScroller.updateFixed( c, wo ); - } else if ( $wrap.hasClass( tscss.scrollerHasFix ) ) { - $wrap.removeClass( tscss.scrollerHasFix ); - // resize needed to make tables full width + // Setup window.resizeEnd event + $win + .off( 'resize resizeEnd '.split( ' ' ).join( namespace + ' ' ) ) + .on( 'resize' + namespace, ts.window_resize ) + .on( 'resizeEnd' + namespace, function() { + // IE calls resize when you modify content, so we have to unbind the resize event + // so we don't end up with an infinite loop. we can rebind after we're done. + $win.off( 'resize' + namespace, ts.window_resize ); tsScroller.resize( c, wo ); - } - }) - .on( events, function( event ) { - // Stop from running twice with pager - if ( ts.hasWidget( 'pager' ) && event.type === 'updateComplete' ) { - return; - } - if ( wo.scroller_fixedColumns > 0 ) { - tsScroller.updateFixed( c, wo, false ); - } - // adjust column sizes after an update - tsScroller.resize( c, wo ); - }); + $win.on( 'resize' + namespace, ts.window_resize ); + $tableWrap.trigger( 'scroll' + namespace ); + }); - // Setup window.resizeEnd event - $win - .off( 'resize resizeEnd '.split( ' ' ).join( namespace + ' ' ) ) - .on( 'resize' + namespace, ts.window_resize ) - .on( 'resizeEnd' + namespace, function() { - // IE calls resize when you modify content, so we have to unbind the resize event - // so we don't end up with an infinite loop. we can rebind after we're done. - $win.off( 'resize' + namespace, ts.window_resize ); - tsScroller.resize( c, wo ); - $win.on( 'resize' + namespace, ts.window_resize ); - $tableWrap.trigger( 'scroll' + namespace ); - }); + // initialization flag + c.isScrolling = true; - // initialization flag - c.isScrolling = true; - - tsScroller.updateFixed( c, wo ); - - }, - - resize : function( c, wo ) { - if ( wo.scroller_isBusy ) { return; } - var index, borderWidth, setWidth, $hCells, $bCells, $fCells, $headers, $this, temp, - tsScroller = ts.scroller, - $container = wo.scroller_$container, - $table = c.$table, - $tableWrap = $table.parent(), - $hdr = wo.scroller_$header, - $foot = wo.scroller_$footer, - id = c.namespace.slice( 1 ) + 'tsscroller', - // Hide other scrollers so we can resize - $div = $( 'div.' + tscss.scrollerWrap + '[id!="' + id + '"]' ) - .addClass( tscss.scrollerHideElement ), - row = '<tr class="' + tscss.scrollerSpacerRow + ' ' + c.selectorRemove.slice(1) + '">'; - - wo.scroller_calcWidths = []; - - // Remove fixed so we get proper widths and heights - tsScroller.removeFixed( c, wo ); - $container.find( '.' + tscss.scrollerSpacerRow ).remove(); - // remove ts added colgroups - $container.find( '.' + ts.css.colgroup ).remove(); - - // show original table elements to get proper alignment - $table - .find( '.' + tscss.scrollerHideElement ) - .removeClass( tscss.scrollerHideElement ); - - // include left & right border widths - borderWidth = parseInt( $table.css( 'border-left-width' ), 10 ); - - $headers = c.$headerIndexed; - - for ( index = 0; index < c.columns; index++ ) { - $this = $headers[ index ]; - // code from https://github.com/jmosbech/StickyTableHeaders - if ( $this.css( 'box-sizing' ) === 'border-box' ) { - setWidth = $this.outerWidth(); - } else { - if ( $hCells.eq( index ).css( 'border-collapse' ) === 'collapse' ) { - if ( $this.length && window.getComputedStyle ) { - setWidth = parseFloat( window.getComputedStyle( $this[ 0 ], null ).width ); + tsScroller.updateFixed( c, wo ); + + // updateAll called - need to give the browser time to adjust the layout + // before calculating fix column widths + if ( c.table.hasInitialized && c.isScrolling ) { + setTimeout(function(){ + ts.scroller.resize( c, wo ); + }, 50); + } + + }, + + resize : function( c, wo ) { + if ( wo.scroller_isBusy ) { return; } + var index, borderWidth, setWidth, $hCells, $bCells, $fCells, $headers, $this, temp, + tsScroller = ts.scroller, + $container = wo.scroller_$container, + $table = c.$table, + $tableWrap = $table.parent(), + $hdr = wo.scroller_$header, + $foot = wo.scroller_$footer, + id = c.namespace.slice( 1 ) + 'tsscroller', + // Hide other scrollers so we can resize + $div = $( 'div.' + tscss.scrollerWrap + '[id!="' + id + '"]' ) + .addClass( tscss.scrollerHideElement ), + row = '<tr class="' + tscss.scrollerSpacerRow + ' ' + c.selectorRemove.slice(1) + '">'; + + wo.scroller_calcWidths = []; + + // Remove fixed so we get proper widths and heights + tsScroller.removeFixed( c, wo ); + $container.find( '.' + tscss.scrollerSpacerRow ).remove(); + // remove ts added colgroups + $container.find( '.' + ts.css.colgroup ).remove(); + + // show original table elements to get proper alignment + $table + .find( '.' + tscss.scrollerHideElement ) + .removeClass( tscss.scrollerHideElement ); + + // include left & right border widths + borderWidth = parseInt( $table.css( 'border-left-width' ), 10 ); + + $headers = c.$headerIndexed; + + for ( index = 0; index < c.columns; index++ ) { + $this = $headers[ index ]; + // code from https://github.com/jmosbech/StickyTableHeaders + if ( $this.css( 'box-sizing' ) === 'border-box' ) { + setWidth = $this.outerWidth(); + } else { + if ( $this.css( 'border-collapse' ) === 'collapse' ) { + if ( $this.length && window.getComputedStyle ) { + setWidth = parseFloat( window.getComputedStyle( $this[ 0 ], null ).width ); + } else { + // ie8 only + setWidth = $this.outerWidth() - parseFloat( $this.css( 'padding-left' ) ) - + parseFloat( $this.css( 'padding-right' ) ) - + ( parseFloat( $this.css( 'border-width' ) ) || 0 ); + } } else { - // ie8 only - setWidth = $this.outerWidth() - parseFloat( $this.css( 'padding-left' ) ) - - parseFloat( $this.css( 'padding-right' ) ) - - ( parseFloat( $this.css( 'border-width' ) ) || 0 ); + setWidth = $this.width(); } - } else { - setWidth = $this.width(); } + row += '<td data-column="' + index + '" style="padding:0;margin:0;border:0;height:0;max-height:0;' + + 'min-height:0;width:' + setWidth + 'px;min-width:' + setWidth + 'px;max-width:' + setWidth + 'px"></td>'; + + // save current widths + wo.scroller_calcWidths[ index ] = setWidth; } - row += '<td data-column="' + index + '" style="padding:0;margin:0;border:0;height:0;max-height:0;' + - 'min-height:0;width:' + setWidth + 'px;min-width:' + setWidth + 'px;max-width:' + setWidth + 'px"></td>'; + row += '</tr>'; + c.$tbodies.eq(0).prepend( row ); // tbody + $hdr.children( 'thead' ).append( row ); + $foot.children( 'tfoot' ).append( row ); + + // include colgroup or alignment is off + ts.fixColumnWidth( c.table ); + row = c.$table.children( 'colgroup' )[0].outerHTML; + $hdr.prepend( row ); + $foot.prepend( row ); + + temp = $tableWrap.parent().innerWidth() - + ( tsScroller.hasScrollBar( $tableWrap ) ? wo.scroller_barSetWidth : 0 ); + $tableWrap.width( temp ); - // save current widths - wo.scroller_calcWidths[ index ] = setWidth; - } - row += '</tr>'; - c.$tbodies.eq(0).prepend( row ); // tbody - $hdr.children( 'thead' ).append( row ); - $foot.children( 'tfoot' ).append( row ); - - // include colgroup or alignment is off - ts.fixColumnWidth( c.table ); - row = c.$table.children( 'colgroup' )[0].outerHTML; - $hdr.prepend( row ); - $foot.prepend( row ); - - temp = $tableWrap.parent().innerWidth() - - ( tsScroller.hasScrollBar( $tableWrap ) ? wo.scroller_barSetWidth : 0 ); - $tableWrap.width( temp ); - - temp = ( tsScroller.hasScrollBar( $tableWrap ) ? wo.scroller_barSetWidth : 0 ) + borderWidth; - setWidth = $tableWrap.innerWidth() - temp; - - $hdr - .parent() - .add( $foot.parent() ) - .width( setWidth ); - - $tableWrap - .width( setWidth + temp ); - - // hide original table thead - $table.children( 'thead' ).addClass( tscss.scrollerHideElement ); - - // update fixed column sizes - tsScroller.updateFixed( c, wo ); - - $div.removeClass( tscss.scrollerHideElement ); - - // restore scrollTop - fixes #926 - $tableWrap.scrollTop( wo.scroller_saved[1] ); - wo.scroller_$container - .find( '.' + tscss.scrollerFixed ) - .find( '.' + tscss.scrollerTable ) - .scrollTop( wo.scroller_saved[1] ); - - // update resizable widget handles - setTimeout( function() { - c.$table.trigger( 'resizableUpdate' ); - }, 100 ); - - }, - - // Add fixed (frozen) columns (Do not call directly, use updateFixed) - setupFixed : function( c, wo ) { - var index, index2, $el, len, temp, $fixedColumn, $fixedTbody, - $table = c.$table, - $wrapper = wo.scroller_$container, - fixedColumns = wo.scroller_fixedColumns; - - $fixedColumn = $wrapper - .addClass( tscss.scrollerHasFix ) - .clone() - .addClass( tscss.scrollerFixed ) - .removeClass( tscss.scrollerWrap ) - .attr( 'id', '' ); - - if ( wo.scroller_addFixedOverlay ) { - $fixedColumn.append( '<div class="' + tscss.scrollerFixedPanel + '">' ); - } + temp = ( tsScroller.hasScrollBar( $tableWrap ) ? wo.scroller_barSetWidth : 0 ) + borderWidth; + setWidth = $tableWrap.innerWidth() - temp; - $fixedTbody = $fixedColumn.find( '.' + tscss.scrollerTable ); - $fixedTbody - .children( 'table' ) - .addClass( c.namespace.slice( 1 ) + '_extra_table' ) - .attr( 'id', '' ) - .children( 'thead, tfoot' ) - .remove(); + $hdr + .parent() + .add( $foot.parent() ) + .width( setWidth ); - wo.scroller_$fixedColumns = $fixedColumn; + $tableWrap + .width( setWidth + temp ); - // RTL support (fixes column on right) - if ( $table.hasClass( tscss.scrollerRtl ) ) { - $fixedColumn.addClass( tscss.scrollerRtl ); - } + // hide original table thead + $table.children( 'thead' ).addClass( tscss.scrollerHideElement ); - $el = $fixedColumn.find( 'tr' ); - len = $el.length; - for ( index = 0; index < len; index++ ) { - $el.eq( index ).children( ':gt(' + ( fixedColumns - 1 ) + ')' ).remove(); - } - $fixedColumn - .addClass( tscss.scrollerHideElement ) - .prependTo( $wrapper ); - - // look for filter widget - if ( c.$table.hasClass( 'hasFilters' ) ) { - // make sure fixed column filters aren't disabled - $el = $fixedColumn - .find( '.' + tscss.filter ) - .not( '.' + tscss.filterDisabled ) - .prop( 'disabled', false ); - ts.filter.bindSearch( $table, $fixedColumn.find( '.' + tscss.filter ) ); - // disable/enable filters behind fixed column - $el = $wrapper - .children( '.' + tscss.scrollerHeader ) - .find( '.' + tscss.filter ); + // update fixed column sizes + tsScroller.updateFixed( c, wo ); + + $div.removeClass( tscss.scrollerHideElement ); + + // restore scrollTop - fixes #926 + $tableWrap.scrollTop( wo.scroller_saved[1] ); + wo.scroller_$container + .find( '.' + tscss.scrollerFixed ) + .find( '.' + tscss.scrollerTable ) + .scrollTop( wo.scroller_saved[1] ); + + // update resizable widget handles + setTimeout( function() { + c.$table.trigger( 'resizableUpdate' ); + }, 100 ); + + }, + + // Add fixed (frozen) columns (Do not call directly, use updateFixed) + setupFixed : function( c, wo ) { + var index, index2, $el, len, temp, $fixedColumn, $fixedTbody, + $table = c.$table, + $wrapper = wo.scroller_$container, + fixedColumns = wo.scroller_fixedColumns; + + $fixedColumn = $wrapper + .addClass( tscss.scrollerHasFix ) + .clone() + .addClass( tscss.scrollerFixed ) + .removeClass( tscss.scrollerWrap ) + .attr( 'id', '' ); + + if ( wo.scroller_addFixedOverlay ) { + $fixedColumn.append( '<div class="' + tscss.scrollerFixedPanel + '">' ); + } + + $fixedTbody = $fixedColumn.find( '.' + tscss.scrollerTable ); + $fixedTbody + .children( 'table' ) + .addClass( c.namespace.slice( 1 ) + '_extra_table' ) + .attr( 'id', '' ) + .children( 'thead, tfoot' ) + .remove(); + + wo.scroller_$fixedColumns = $fixedColumn; + + // RTL support (fixes column on right) + if ( $table.hasClass( tscss.scrollerRtl ) ) { + $fixedColumn.addClass( tscss.scrollerRtl ); + } + + $el = $fixedColumn.find( 'tr' ); len = $el.length; for ( index = 0; index < len; index++ ) { - // previously disabled filter; don't mess with it! filterDisabled class added by filter widget - if ( !$el.eq( index ).hasClass( tscss.filterDisabled || 'disabled' ) ) { - // disable filters behind fixed column; don't disable visible filters - $el.eq( index ).prop( 'disabled', index < fixedColumns ); + $el.eq( index ).children( ':gt(' + ( fixedColumns - 1 ) + ')' ).remove(); + } + $fixedColumn + .addClass( tscss.scrollerHideElement ) + .prependTo( $wrapper ); + + // look for filter widget + if ( c.$table.hasClass( 'hasFilters' ) ) { + // make sure fixed column filters aren't disabled + $el = $fixedColumn + .find( '.' + tscss.filter ) + .not( '.' + tscss.filterDisabled ) + .prop( 'disabled', false ); + ts.filter.bindSearch( $table, $fixedColumn.find( '.' + tscss.filter ) ); + // disable/enable filters behind fixed column + $el = $wrapper + .children( '.' + tscss.scrollerHeader ) + .find( '.' + tscss.filter ); + len = $el.length; + for ( index = 0; index < len; index++ ) { + // previously disabled filter; don't mess with it! filterDisabled class added by filter widget + if ( !$el.eq( index ).hasClass( tscss.filterDisabled || 'disabled' ) ) { + // disable filters behind fixed column; don't disable visible filters + $el.eq( index ).prop( 'disabled', index < fixedColumns ); + } } } - } - // disable/enable tab indexes behind fixed column - c.$table - .add( '.' + tscss.scrollerFooter + ' table' ) - .children( 'thead' ) - .children( 'tr.' + tscss.headerRow ) - .children() - .attr( 'tabindex', -1 ); - - $el = wo.scroller_$header - .add( $fixedColumn.find( '.' + tscss.scrollerTable + ' table' ) ) - .children( 'thead' ) - .children( 'tr.' + tscss.headerRow ); - len = $el.length; - for ( index = 0; index < len; index++ ) { - temp = $el.eq( index ).children(); - for ( index2 = 0; index2 < temp.length; index2++ ) { - temp.eq( index2 ).attr( 'tabindex', index2 < fixedColumns ? -1 : 0 ); + // disable/enable tab indexes behind fixed column + c.$table + .add( '.' + tscss.scrollerFooter + ' table' ) + .children( 'thead' ) + .children( 'tr.' + tscss.headerRow ) + .children() + .attr( 'tabindex', -1 ); + + $el = wo.scroller_$header + .add( $fixedColumn.find( '.' + tscss.scrollerTable + ' table' ) ) + .children( 'thead' ) + .children( 'tr.' + tscss.headerRow ); + len = $el.length; + for ( index = 0; index < len; index++ ) { + temp = $el.eq( index ).children(); + for ( index2 = 0; index2 < temp.length; index2++ ) { + temp.eq( index2 ).attr( 'tabindex', index2 < fixedColumns ? -1 : 0 ); + } } - } - ts.bindEvents( c.table, $fixedColumn.find( '.' + tscss.header ) ); - ts.scroller.bindFixedColumnEvents( c, wo ); + ts.bindEvents( c.table, $fixedColumn.find( '.' + tscss.header ) ); + ts.scroller.bindFixedColumnEvents( c, wo ); - /*** Scrollbar hack! Since we can't hide the scrollbar with css ***/ - if ( ts.scroller.isFirefox || ts.scroller.isOldIE ) { - $fixedTbody.wrap( '<div class="' + tscss.scrollerHack + '" style="overflow:hidden;">' ); - } + /*** Scrollbar hack! Since we can't hide the scrollbar with css ***/ + if ( ts.scroller.isFirefox || ts.scroller.isOldIE ) { + $fixedTbody.wrap( '<div class="' + tscss.scrollerHack + '" style="overflow:hidden;">' ); + } - }, - - bindFixedColumnEvents : function( c, wo ) { - // update thead & tbody in fixed column - var tsScroller = ts.scroller, - namespace = c.namespace + 'tsscrollerFixed', - events = 'scroll' + namespace, - $fixedTbody = wo.scroller_$fixedColumns.find( '.' + tscss.scrollerTable ), - fixedScroll = true, - tableScroll = true; - - c.$table - .parent() - // *** SCROLL *** scroll fixed column along with main - .off( events ) - .on( events, function() { - if ( wo.scroller_isBusy ) { return; } - // using flags to prevent firing the scroll event excessively leading to slow scrolling in Firefox - if ( !wo.scroller_isBusy && ( fixedScroll || !( tsScroller.isFirefox || tsScroller.isIE ) ) ) { - tableScroll = false; - var $this = $( this ); - $fixedTbody[0].scrollTop = wo.scroller_saved[1] = $this.scrollTop(); - wo.scroller_saved[0] = $this.scrollLeft(); - setTimeout( function() { - tableScroll = true; - }, 20 ); - } - }); - // scroll main along with fixed column - $fixedTbody - .off( events ) - .on( events, function() { - // using flags to prevent firing the scroll event excessively leading to slow scrolling in Firefox - if ( !wo.scroller_isBusy && ( tableScroll || !( tsScroller.isFirefox || tsScroller.isIE ) ) ) { - fixedScroll = false; - var $this = $( this ); - c.$table.parent()[0].scrollTop = wo.scroller_saved[1] = $this.scrollTop(); - setTimeout( function() { - fixedScroll = true; - }, 20 ); - } - }) - .scroll(); + }, + + bindFixedColumnEvents : function( c, wo ) { + // update thead & tbody in fixed column + var tsScroller = ts.scroller, + namespace = c.namespace + 'tsscrollerFixed', + events = 'scroll' + namespace, + $fixedTbody = wo.scroller_$fixedColumns.find( '.' + tscss.scrollerTable ), + fixedScroll = true, + tableScroll = true; - // *** ROW HIGHLIGHT *** - if ( wo.scroller_rowHighlight !== '' ) { - events = 'mouseover mouseleave '.split( ' ' ).join( namespace + ' ' ); - // can't use c.$tbodies because it doesn't include info-only tbodies c.$table - .off( events, 'tbody > tr' ) - .on( events, 'tbody > tr', function( event ) { - var indx = c.$table.children( 'tbody' ).children( 'tr' ).index( this ); - $fixedTbody - .children( 'table' ) - .children( 'tbody' ) - .children( 'tr' ) - .eq( indx ) - .add( this ) - .toggleClass( wo.scroller_rowHighlight, event.type === 'mouseover' ); + .parent() + // *** SCROLL *** scroll fixed column along with main + .off( events ) + .on( events, function() { + if ( wo.scroller_isBusy ) { return; } + // using flags to prevent firing the scroll event excessively leading to slow scrolling in Firefox + if ( !wo.scroller_isBusy && ( fixedScroll || !( tsScroller.isFirefox || tsScroller.isIE ) ) ) { + tableScroll = false; + var $this = $( this ); + $fixedTbody[0].scrollTop = wo.scroller_saved[1] = $this.scrollTop(); + wo.scroller_saved[0] = $this.scrollLeft(); + setTimeout( function() { + tableScroll = true; + }, 20 ); + } }); + // scroll main along with fixed column $fixedTbody - .find( 'table' ) - .off( events, 'tbody > tr' ) - .on( events, 'tbody > tr', function( event ) { - var $fixed = $fixedTbody.children( 'table' ).children( 'tbody' ).children( 'tr' ), - indx = $fixed.index( this ); - c.$table - .children( 'tbody' ) - .children( 'tr' ) - .eq( indx ) - .add( this ) - .toggleClass( wo.scroller_rowHighlight, event.type === 'mouseover' ); - }); - } - }, - - adjustWidth : function( c, wo, totalWidth, adj, dir ) { - var $wrapper = wo.scroller_$container; - - // RTL support (fixes column on right) - $wrapper - .children( '.' + tscss.scrollerTable ) - .css( dir ? 'right' : 'left', totalWidth ); - $wrapper - .children( '.' + tscss.scrollerHeader + ', .' + tscss.scrollerFooter ) - // Safari needs a scrollbar width of extra adjusment to align the fixed & scrolling columns - .css( dir ? 'right' : 'left', totalWidth + ( dir && ts.scroller.isSafari ? adj : 0 ) ); - }, - - updateFixed : function( c, wo ) { - var temp, adj, - $wrapper = wo.scroller_$container, - $hdr = wo.scroller_$header, - $foot = wo.scroller_$footer, - $table = c.$table, - $tableWrap = $table.parent(), - scrollBarWidth = wo.scroller_barSetWidth, - dir = $table.hasClass( tscss.scrollerRtl ); - - if ( wo.scroller_fixedColumns === 0 ) { - wo.scroller_isBusy = false; - ts.scroller.removeFixed( c, wo ); - temp = $wrapper.width(); - $tableWrap.width( temp ); + .off( events ) + .on( events, function() { + // using flags to prevent firing the scroll event excessively leading to slow scrolling in Firefox + if ( !wo.scroller_isBusy && ( tableScroll || !( tsScroller.isFirefox || tsScroller.isIE ) ) ) { + fixedScroll = false; + var $this = $( this ); + c.$table.parent()[0].scrollTop = wo.scroller_saved[1] = $this.scrollTop(); + setTimeout( function() { + fixedScroll = true; + }, 20 ); + } + }) + .scroll(); + + // *** ROW HIGHLIGHT *** + if ( wo.scroller_rowHighlight !== '' ) { + events = 'mouseover mouseleave '.split( ' ' ).join( namespace + ' ' ); + // can't use c.$tbodies because it doesn't include info-only tbodies + c.$table + .off( events, 'tbody > tr' ) + .on( events, 'tbody > tr', function( event ) { + var indx = c.$table.children( 'tbody' ).children( 'tr' ).index( this ); + $fixedTbody + .children( 'table' ) + .children( 'tbody' ) + .children( 'tr' ) + .eq( indx ) + .add( this ) + .toggleClass( wo.scroller_rowHighlight, event.type === 'mouseover' ); + }); + $fixedTbody + .find( 'table' ) + .off( events, 'tbody > tr' ) + .on( events, 'tbody > tr', function( event ) { + var $fixed = $fixedTbody.children( 'table' ).children( 'tbody' ).children( 'tr' ), + indx = $fixed.index( this ); + c.$table + .children( 'tbody' ) + .children( 'tr' ) + .eq( indx ) + .add( this ) + .toggleClass( wo.scroller_rowHighlight, event.type === 'mouseover' ); + }); + } + }, + + adjustWidth : function( c, wo, totalWidth, adj, dir ) { + var $wrapper = wo.scroller_$container; + + // RTL support (fixes column on right) + $wrapper + .children( '.' + tscss.scrollerTable ) + .css( dir ? 'right' : 'left', totalWidth ); + $wrapper + .children( '.' + tscss.scrollerHeader + ', .' + tscss.scrollerFooter ) + // Safari needs a scrollbar width of extra adjusment to align the fixed & scrolling columns + .css( dir ? 'right' : 'left', totalWidth + ( dir && ts.scroller.isSafari ? adj : 0 ) ); + }, + + updateFixed : function( c, wo ) { + var temp, adj, + $wrapper = wo.scroller_$container, + $hdr = wo.scroller_$header, + $foot = wo.scroller_$footer, + $table = c.$table, + $tableWrap = $table.parent(), + scrollBarWidth = wo.scroller_barSetWidth, + dir = $table.hasClass( tscss.scrollerRtl ); + + if ( wo.scroller_fixedColumns === 0 ) { + wo.scroller_isBusy = false; + ts.scroller.removeFixed( c, wo ); + temp = $wrapper.width(); + $tableWrap.width( temp ); + adj = ts.scroller.hasScrollBar( $tableWrap ) ? scrollBarWidth : 0; + $hdr + .parent() + .add( $foot.parent() ) + .width( temp - adj ); + return; + } + + if ( !c.isScrolling ) { + return; + } + + wo.scroller_isBusy = true; + + // Make sure the wo.scroller_$fixedColumns container exists, if not build it + if ( !$wrapper.find( '.' + tscss.scrollerFixed ).length ) { + ts.scroller.setupFixed( c, wo ); + } + + // scroller_fixedColumns + var index, tbodyIndex, rowIndex, $tbody, $adjCol, $fb, $fixHead, $fixBody, $fixFoot, + totalRows, row, + + // source cells for measurement + $mainTbodies = wo.scroller_$container + .children( '.' + tscss.scrollerTable ) + .children( 'table' ) + .children( 'tbody' ), + // variable gets redefined + $rows = wo.scroller_$header + .children( 'thead' ) + .children( '.' + tscss.headerRow ), + + // hide fixed column during resize, or we get a FOUC + $fixedColumn = wo.scroller_$fixedColumns + .addClass( tscss.scrollerHideElement ), + + // target cells + $fixedTbodiesTable = $fixedColumn + .find( '.' + tscss.scrollerTable ) + .children( 'table' ), + $fixedTbodies = $fixedTbodiesTable + .children( 'tbody' ), + // variables + tsScroller = ts.scroller, + fixedColumns = wo.scroller_fixedColumns, + // get dimensions + $temp = $table.find( 'tbody td' ), + borderRightWidth = parseInt( $temp.css( 'border-right-width' ), 10 ) || 1, + borderSpacing = parseInt( ( $temp.css( 'border-spacing' ) || '' ).split( /\s/ )[ 0 ], 10 ) / 2 || 0, + totalWidth = parseInt( $table.css( 'padding-left' ), 10 ) + + parseInt( $table.css( 'padding-right' ), 10 ) - + borderRightWidth, + widths = wo.scroller_calcWidths; + + ts.scroller.removeFixed( c, wo, false ); + + // calculate fixed column width + for ( index = 0; index < fixedColumns; index++ ) { + totalWidth += widths[ index ] + borderSpacing; + } + + // set fixed column width + totalWidth = totalWidth + borderRightWidth * 2 - borderSpacing; + tsScroller.setWidth( $fixedColumn.add( $fixedColumn.children() ), totalWidth ); + tsScroller.setWidth( $fixedColumn.children().children( 'table' ), totalWidth ); + + // update fixed column tbody content, set cell widths on hidden row + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + $tbody = $mainTbodies.eq( tbodyIndex ); + if ( $tbody.length ) { + // get tbody + $rows = $tbody.children(); + totalRows = $rows.length; + $fb = ts.processTbody( $fixedTbodiesTable, $fixedTbodies.eq( tbodyIndex ), true ); + $fb.empty(); + // update tbody cells after sort/filtering + for ( rowIndex = 0; rowIndex < totalRows; rowIndex++ ) { + $adjCol = $( $rows[ rowIndex ].outerHTML ); + $adjCol + .children( 'td, th' ) + .slice( fixedColumns ) + .remove(); + $fb.append( $adjCol ); + } + + // restore tbody + ts.processTbody( $fixedTbodiesTable, $fb, false ); + } + } + adj = ts.scroller.hasScrollBar( $tableWrap ) ? scrollBarWidth : 0; + + /*** scrollbar HACK! Since we can't hide the scrollbar with css ***/ + if ( tsScroller.isFirefox || tsScroller.isOldIE ) { + $fixedTbodiesTable + .css( 'width', totalWidth ) + .parent() + .css( 'width', totalWidth + adj ); + } + + $fixedColumn.removeClass( tscss.scrollerHideElement ); + for ( index = 0; index < fixedColumns; index++ ) { + $wrapper + .children( 'div' ) + .children( 'table' ) + .find( 'th:nth-child(' + ( index + 1 ) + '), td:nth-child(' + ( index + 1 ) + ')' ) + .addClass( tscss.scrollerHideColumn ); + } + + totalWidth = totalWidth - borderRightWidth; + temp = $tableWrap.parent().innerWidth() - totalWidth; + $tableWrap.width( temp ); + // RTL support (fixes column on right) + $wrapper + .children( '.' + tscss.scrollerTable ) + .css( dir ? 'right' : 'left', totalWidth ); + $wrapper + .children( '.' + tscss.scrollerHeader + ', .' + tscss.scrollerFooter ) + // Safari needs a scrollbar width of extra adjusment to align the fixed & scrolling columns + .css( dir ? 'right' : 'left', totalWidth + ( dir && ts.scroller.isSafari ? adj : 0 ) ); + $hdr .parent() .add( $foot.parent() ) .width( temp - adj ); - return; - } - if ( !c.isScrolling ) { - return; - } + // fix gap under the tbody for the horizontal scrollbar + temp = ts.scroller.hasScrollBar( $tableWrap, true ); + adj = temp ? scrollBarWidth : 0; + if ( !$fixedColumn.find( '.' + tscss.scrollerBarSpacer ).length && temp ) { + $temp = $( '<div class="' + tscss.scrollerBarSpacer + '">' ) + .css( 'height', adj + 'px' ); + $fixedColumn.find( '.' + tscss.scrollerTable ).append( $temp ); + } else if ( !temp ) { + $fixedColumn.find( '.' + tscss.scrollerBarSpacer ).remove(); + } - wo.scroller_isBusy = true; + ts.scroller.updateRowHeight( c, wo ); + // set fixed column height (changes with filtering) + $fixedColumn.height( $wrapper.height() ); - // Make sure the wo.scroller_$fixedColumns container exists, if not build it - if ( !$wrapper.find( '.' + tscss.scrollerFixed ).length ) { - ts.scroller.setupFixed( c, wo ); - } + $fixedColumn.removeClass( tscss.scrollerHideElement ); - // scroller_fixedColumns - var index, tbodyIndex, rowIndex, $tbody, $adjCol, $fb, $fixHead, $fixBody, $fixFoot, - totalRows, row, + wo.scroller_isBusy = false; - // source cells for measurement - $mainTbodies = wo.scroller_$container - .children( '.' + tscss.scrollerTable ) - .children( 'table' ) - .children( 'tbody' ), - // variable gets redefined - $rows = wo.scroller_$header - .children( 'thead' ) - .children( '.' + tscss.headerRow ), + }, - // hide fixed column during resize, or we get a FOUC - $fixedColumn = wo.scroller_$fixedColumns - .addClass( tscss.scrollerHideElement ), + fixHeight : function( $rows, $fixedRows ) { + var index, heightRow, heightFixed, $r, $f, + addedHt = tscss.scrollerAddedHeight, + len = $rows.length; + for ( index = 0; index < len; index++ ) { + $r = $rows.eq( index ); + $f = $fixedRows.eq( index ); + heightRow = $r.height(); + heightFixed = $f.height(); + if ( heightRow > heightFixed ) { + $f.addClass( addedHt ).height( heightRow ); + } else if ( heightRow < heightFixed ) { + $r.addClass( addedHt ).height( heightFixed ); + } + } + }, - // target cells - $fixedTbodiesTable = $fixedColumn - .find( '.' + tscss.scrollerTable ) - .children( 'table' ), - $fixedTbodies = $fixedTbodiesTable - .children( 'tbody' ), - // variables - tsScroller = ts.scroller, - fixedColumns = wo.scroller_fixedColumns, - // get dimensions - $temp = $table.find( 'tbody td' ), - borderRightWidth = parseInt( $temp.css( 'border-right-width' ), 10 ) || 1, - borderSpacing = parseInt( ( $temp.css( 'border-spacing' ) || '' ).split( /\s/ )[ 0 ], 10 ) / 2 || 0, - totalWidth = parseInt( $table.css( 'padding-left' ), 10 ) + - parseInt( $table.css( 'padding-right' ), 10 ) - - borderRightWidth, - widths = wo.scroller_calcWidths; - - ts.scroller.removeFixed( c, wo, false ); - - // calculate fixed column width - for ( index = 0; index < fixedColumns; index++ ) { - totalWidth += widths[ index ] + borderSpacing; - } + updateRowHeight : function( c, wo ) { + var $rows, $fixed, + $fixedColumns = wo.scroller_$fixedColumns; - // set fixed column width - totalWidth = totalWidth + borderRightWidth * 2 - borderSpacing; - tsScroller.setWidth( $fixedColumn.add( $fixedColumn.children() ), totalWidth ); - tsScroller.setWidth( $fixedColumn.children().children( 'table' ), totalWidth ); - - // update fixed column tbody content, set cell widths on hidden row - for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { - $tbody = $mainTbodies.eq( tbodyIndex ); - if ( $tbody.length ) { - // get tbody - $rows = $tbody.children(); - totalRows = $rows.length; - $fb = ts.processTbody( $fixedTbodiesTable, $fixedTbodies.eq( tbodyIndex ), true ); - $fb.empty(); - // update tbody cells after sort/filtering - for ( rowIndex = 0; rowIndex < totalRows; rowIndex++ ) { - $adjCol = $( $rows[ rowIndex ].outerHTML ); - $adjCol - .children( 'td, th' ) - .slice( fixedColumns ) - .remove(); - $fb.append( $adjCol ); - } + wo.scroller_$container + .find( '.' + tscss.scrollerAddedHeight ) + .removeClass( tscss.scrollerAddedHeight ) + .height( '' ); + + $rows = wo.scroller_$header + .children( 'thead' ) + .children( 'tr' ); + $fixed = $fixedColumns + .children( '.' + tscss.scrollerHeader ) + .children( 'table' ) + .children( 'thead' ) + .children( 'tr' ); + ts.scroller.fixHeight( $rows, $fixed ); + + $rows = wo.scroller_$footer + .children( 'tfoot' ) + .children( 'tr' ); + $fixed = $fixedColumns + .children( '.' + tscss.scrollerFooter ) + .children( 'table' ) + .children( 'tfoot' ) + .children( 'tr' ); + ts.scroller.fixHeight( $rows, $fixed ); - // restore tbody - ts.processTbody( $fixedTbodiesTable, $fb, false ); + if ( ts.scroller.isFirefox || ts.scroller.isOldIE ) { + // Firefox/Old IE scrollbar hack (wraps table to hide the scrollbar) + $fixedColumns = $fixedColumns.find( '.' + tscss.scrollerHack ); } - } + $rows = c.$table + .children( 'tbody' ) + .children( 'tr' ); + $fixed = $fixedColumns + .children( '.' + tscss.scrollerTable ) + .children( 'table' ) + .children( 'tbody' ) + .children( 'tr' ); + ts.scroller.fixHeight( $rows, $fixed ); - adj = ts.scroller.hasScrollBar( $tableWrap ) ? scrollBarWidth : 0; + }, - /*** scrollbar HACK! Since we can't hide the scrollbar with css ***/ - if ( tsScroller.isFirefox || tsScroller.isOldIE ) { - $fixedTbodiesTable - .css( 'width', totalWidth ) - .parent() - .css( 'width', totalWidth + adj ); - } + removeFixed : function( c, wo, removeIt ) { + var $table = c.$table, + $wrapper = wo.scroller_$container, + dir = $table.hasClass( tscss.scrollerRtl ); - $fixedColumn.removeClass( tscss.scrollerHideElement ); - for ( index = 0; index < fixedColumns; index++ ) { - $wrapper - .children( 'div' ) - .children( 'table' ) - .find( 'th:nth-child(' + ( index + 1 ) + '), td:nth-child(' + ( index + 1 ) + ')' ) - .addClass( tscss.scrollerHideColumn ); - } + // remove fixed columns + if ( removeIt || typeof removeIt === 'undefined' ) { + $wrapper.find( '.' + tscss.scrollerFixed ).remove(); + } - totalWidth = totalWidth - borderRightWidth; - temp = $tableWrap.parent().innerWidth() - totalWidth; - $tableWrap.width( temp ); - // RTL support (fixes column on right) - $wrapper - .children( '.' + tscss.scrollerTable ) - .css( dir ? 'right' : 'left', totalWidth ); - $wrapper - .children( '.' + tscss.scrollerHeader + ', .' + tscss.scrollerFooter ) - // Safari needs a scrollbar width of extra adjusment to align the fixed & scrolling columns - .css( dir ? 'right' : 'left', totalWidth + ( dir && ts.scroller.isSafari ? adj : 0 ) ); - - $hdr - .parent() - .add( $foot.parent() ) - .width( temp - adj ); - - // fix gap under the tbody for the horizontal scrollbar - temp = ts.scroller.hasScrollBar( $tableWrap, true ); - adj = temp ? scrollBarWidth : 0; - if ( !$fixedColumn.find( '.' + tscss.scrollerBarSpacer ).length && temp ) { - $temp = $( '<div class="' + tscss.scrollerBarSpacer + '">' ) - .css( 'height', adj + 'px' ); - $fixedColumn.find( '.' + tscss.scrollerTable ).append( $temp ); - } else if ( !temp ) { - $fixedColumn.find( '.' + tscss.scrollerBarSpacer ).remove(); - } + $wrapper + .find( '.' + tscss.scrollerHideColumn ) + .removeClass( tscss.scrollerHideColumn ); - ts.scroller.updateRowHeight( c, wo ); - // set fixed column height (changes with filtering) - $fixedColumn.height( $wrapper.height() ); - - $fixedColumn.removeClass( tscss.scrollerHideElement ); - - wo.scroller_isBusy = false; - }, - - fixHeight : function( $rows, $fixedRows ) { - var index, heightRow, heightFixed, $r, $f, - len = $rows.length; - for ( index = 0; index < len; index++ ) { - $r = $rows.eq( index ); - $f = $fixedRows.eq( index ); - heightRow = $r.height(); - heightFixed = $f.height(); - if ( heightRow > heightFixed ) { - $f.addClass( tscss.scrollerAddedHeight ).height( heightRow ); - } else if ( heightRow < heightFixed ) { - $r.addClass( tscss.scrollerAddedHeight ).height( heightFixed ); + // RTL support ( fixes column on right ) + $wrapper + .children( ':not(.' + tscss.scrollerFixed + ')' ) + .css( dir ? 'right' : 'left', 0 ); + }, + + remove : function( c, wo ) { + var $wrap = wo.scroller_$container, + namespace = c.namespace + 'tsscroller'; + c.$table.off( namespace ); + $( window ).off( namespace ); + if ( $wrap ) { + c.$table + .insertBefore( $wrap ) + .find( 'thead' ) + .removeClass( tscss.scrollerHideElement ) + .children( 'tr.' + tscss.headerRow ) + .children() + .attr( 'tabindex', 0 ) + .end() + .find( '.' + tscss.filterRow ) + .removeClass( tscss.scrollerHideElement + ' ' + tscss.filterRowHide ); + c.$table + .find( '.' + tscss.filter ) + .not( '.' + tscss.filterDisabled ) + .prop( 'disabled', false ); + $wrap.remove(); + c.isScrolling = false; } } - }, - - updateRowHeight : function( c, wo ) { - var $rows, $fixed, - $fixedColumns = wo.scroller_$fixedColumns; - - wo.scroller_$container - .find( '.' + tscss.scrollerAddedHeight ) - .removeClass( tscss.scrollerAddedHeight ) - .height( '' ); - - $rows = wo.scroller_$header - .children( 'thead' ) - .children( 'tr' ); - $fixed = $fixedColumns - .children( '.' + tscss.scrollerHeader ) - .children( 'table' ) - .children( 'thead' ) - .children( 'tr' ); - ts.scroller.fixHeight( $rows, $fixed ); - - $rows = wo.scroller_$footer - .children( 'tfoot' ) - .children( 'tr' ); - $fixed = $fixedColumns - .children( '.' + tscss.scrollerFooter ) - .children( 'table' ) - .children( 'tfoot' ) - .children( 'tr' ); - ts.scroller.fixHeight( $rows, $fixed ); - - if ( ts.scroller.isFirefox || ts.scroller.isOldIE ) { - // Firefox/Old IE scrollbar hack (wraps table to hide the scrollbar) - $fixedColumns = $fixedColumns.find( '.' + tscss.scrollerHack ); - } - $rows = c.$table - .children( 'tbody' ) - .children( 'tr' ); - $fixed = $fixedColumns - .children( '.' + tscss.scrollerTable ) - .children( 'table' ) - .children( 'tbody' ) - .children( 'tr' ); - ts.scroller.fixHeight( $rows, $fixed ); - - }, - - removeFixed : function( c, wo, removeIt ) { - var $table = c.$table, - $wrapper = wo.scroller_$container, - dir = $table.hasClass( tscss.scrollerRtl ); - - // remove fixed columns - if ( removeIt || typeof removeIt === 'undefined' ) { - $wrapper.find( '.' + tscss.scrollerFixed ).remove(); - } - $wrapper - .find( '.' + tscss.scrollerHideColumn ) - .removeClass( tscss.scrollerHideColumn ); - - // RTL support ( fixes column on right ) - $wrapper - .children( ':not(.' + tscss.scrollerFixed + ')' ) - .css( dir ? 'right' : 'left', 0 ); - }, - - remove : function( c, wo ) { - var $wrap = wo.scroller_$container, - namespace = c.namespace + 'tsscroller'; - c.$table - .off( namespace ) - .insertBefore( $wrap ) - .find( 'thead' ) - .removeClass( tscss.scrollerHideElement ) - .children( 'tr.' + tscss.headerRow ) - .children() - .attr( 'tabindex', 0 ) - .end() - .find( '.' + tscss.filterRow ) - .removeClass( tscss.scrollerHideElement + ' ' + tscss.filterRowHide ); - c.$table - .find( '.' + tscss.filter ) - .not( '.' + tscss.filterDisabled ) - .prop( 'disabled', false ); - $wrap.remove(); - $( window ).off( namespace ); - c.isScrolling = false; - } - -}; + }; })( jQuery, window ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js new file mode 100644 index 0000000..8a35f63 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js @@ -0,0 +1,128 @@ +/*! Widget: sort2Hash - updated 7/28/2015 (v2.22.4) */ +;( function( $ ) { + 'use strict'; + var ts = $.tablesorter || {}, + s2h = { + init : function( c, wo ) { + var hasSaveSort = ts.hasWidget( c.table, 'saveSort' ), + sort = s2h.getSort( c, wo ); + if ( ( sort && !hasSaveSort ) || ( sort && hasSaveSort && wo.sort2Hash_overrideSaveSort ) ) { + s2h.processHash( c, wo, sort ); + } + c.$table.on( 'sortEnd.sort2hash', function() { + s2h.setHash( c, wo ); + }); + }, + getTableId : function( c, wo ) { + // option > table id > table index on page + return wo.sort2Hash_tableId || + c.table.id || + 'table' + $( 'table' ).index( c.$table ); + }, + getSort : function( c, wo, clean ) { + // modified original code from http://www.netlobo.com/url_query_string_javascript.html + var value, + name = s2h.getTableId( c, wo ).replace( /[\[]/, '\\[' ).replace( /[\]]/, '\\]' ), + sort = ( new RegExp( '[\\#&]' + name + '=([^&]*)' ) ).exec( window.location.hash ); + if ( sort === null ) { + return ''; + } else { + value = s2h.processSort( c, wo, sort[ 1 ] ); + if ( clean ) { + window.location.hash = window.location.hash.replace( '&' + name + '=' + sort[ 1 ], '' ); + return value; + } + return sort[ 1 ]; + } + }, + // convert 'first%20name,asc,last%20name,desc' into [[0,0], [1,1]] + processHash : function( c, wo, sortHash ) { + var regex, column, direction, temp, + arry = decodeURI( sortHash || '' ).split( wo.sort2Hash_separator ), + indx = 0, + len = arry.length, + sort = []; + while ( indx < len ) { + // column index or text + column = arry[ indx++ ]; + temp = parseInt( column, 10 ); + // ignore wo.sort2Hash_useHeaderText setting & + // just see if column contains a number + if ( isNaN( temp ) || temp > c.columns ) { + regex = new RegExp( '(' + column + ')', 'i' ); + column = c.$headers.filter( function( index, cell ) { + return regex.test( c.$headers[ index ].textContent || '' ); + }).attr( 'data-column' ); + } + direction = arry[ indx++ ]; + // ignore unpaired values + if ( typeof direction !== 'undefined' ) { + // convert text to 0, 1 + if ( isNaN( direction ) ) { + // default to ascending sort + direction = direction.indexOf( wo.sort2Hash_directionText[ 1 ] ) > -1 ? 1 : 0; + } + sort.push( [ column, direction ] ); + } + } + if ( sort.length ) { + c.sortList = sort; + } + }, + + // convert [[0,0],[1,1]] to 'first%20name,asc,last%20name,desc' + processSort : function( c, wo ) { + var index, txt, column, direction, + sort = [], + arry = c.sortList || [], + len = arry.length; + for ( index = 0; index < len; index++ ) { + column = arry[ index ][ 0 ]; + if ( wo.sort2Hash_useHeaderText ) { + txt = $.trim( c.$headerIndexed[ column ].text() ); + if ( typeof wo.sort2Hash_processHeaderText === 'function' ) { + txt = wo.sort2Hash_processHeaderText( txt, c, column ); + } + column = txt; + } + sort.push( column ); + direction = wo.sort2Hash_directionText[ arry[ index ][ 1 ] ]; + sort.push( direction ); + + } + // join with separator + return sort.join( wo.sort2Hash_separator ); + }, + setHash : function( c, wo ) { + var arry = [], + sort = s2h.processSort( c, wo ); + if ( sort.length ) { + // remove old hash + s2h.getSort( c, wo, true ); + window.location.hash += ( window.location.hash.length ? '' : wo.sort2Hash_hash ) + + '&' + s2h.getTableId( c, wo ) + '=' + encodeURI( sort ); + } + } + }; + + ts.addWidget({ + id: 'sort2Hash', + priority: 30, // after saveSort + options: { + sort2Hash_hash : '#', // hash prefix + sort2Hash_separator : '-', // don't '#' or '=' here + sort2Hash_tableId : null, // this option > table ID > table index on page, + sort2Hash_useHeaderText : false, // use column header text (true) or zero-based column index + sort2Hash_processHeaderText : null, // function( text, config, columnIndex ) {}, + sort2Hash_directionText : [ 0, 1 ], // [ 'asc', 'desc' ], + sort2Hash_overrideSaveSort : false // if true, override saveSort widget if saved sort available + }, + init: function(table, thisWidget, c, wo) { + s2h.init( c, wo ); + }, + remove: function(table, c) { + c.$table.off( 'sortEnd.sort2hash' ); + } + }); + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js index 9d49740..73af3e2 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js @@ -5,224 +5,224 @@ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ ;( function( $ ) { -'use strict'; -var ts = $.tablesorter; + 'use strict'; + var ts = $.tablesorter; -ts.sortTbodies = { - init: function( c, wo ) { + ts.sortTbodies = { + init: function( c, wo ) { - var index, rows, txt, max, $rows, - namespace = c.namespace + 'sortTbody', - $tbodies = c.$table.children( 'tbody' ), - len = $tbodies.length; + var index, rows, txt, max, $rows, + namespace = c.namespace + 'sortTbody', + $tbodies = c.$table.children( 'tbody' ), + len = $tbodies.length; - // save serverSideSorting value; use to toggle internal row sorting - wo.sortTbody_original_serverSideSorting = c.serverSideSorting; + // save serverSideSorting value; use to toggle internal row sorting + wo.sortTbody_original_serverSideSorting = c.serverSideSorting; - // include info-only tbodies - we need parsed data from *all* tbodies - wo.sortTbody_original_cssInfoBlock = c.cssInfoBlock; - c.cssInfoBlock = wo.sortTbody_noSort; - ts.sortTbodies.setTbodies( c, wo ); + // include info-only tbodies - we need parsed data from *all* tbodies + wo.sortTbody_original_cssInfoBlock = c.cssInfoBlock; + c.cssInfoBlock = wo.sortTbody_noSort; + ts.sortTbodies.setTbodies( c, wo ); - // add original order index for stable sort - for ( index = 0; index < len; index++ ) { - $tbodies.eq( index ).attr( 'data-ts-original-order', index ); - } + // add original order index for stable sort + for ( index = 0; index < len; index++ ) { + $tbodies.eq( index ).attr( 'data-ts-original-order', index ); + } - c.$table - .unbind( 'sortBegin updateComplete '.split( ' ' ).join( namespace + ' ' ) ) - .bind( 'sortBegin' + namespace, function() { - ts.sortTbodies.sorter( c ); - }) - .bind( 'updateComplete' + namespace, function() { - // find parsers for each column + c.$table + .unbind( 'sortBegin updateComplete '.split( ' ' ).join( namespace + ' ' ) ) + .bind( 'sortBegin' + namespace, function() { + ts.sortTbodies.sorter( c ); + }) + .bind( 'updateComplete' + namespace, function() { + // find parsers for each column + ts.sortTbodies.setTbodies( c, wo ); + c.$table.trigger( 'updateCache', [ null, c.$tbodies ] ); + }); + + // detect parsers - in case the table contains only info-only tbodies + if ( $.isEmptyObject( c.parsers ) || c.$tbodies.length !== $tbodies.length ) { ts.sortTbodies.setTbodies( c, wo ); c.$table.trigger( 'updateCache', [ null, c.$tbodies ] ); - }); - - // detect parsers - in case the table contains only info-only tbodies - if ( $.isEmptyObject( c.parsers ) || c.$tbodies.length !== $tbodies.length ) { - ts.sortTbodies.setTbodies( c, wo ); - c.$table.trigger( 'updateCache', [ null, c.$tbodies ] ); - } + } - // find colMax; this only matter for numeric columns - $rows = $tbodies.children( 'tr' ); - len = $rows.length; - for ( index = 0; index < c.columns; index++ ) { - max = 0; - if ( c.parsers[ index ].type === 'numeric' ) { - for ( rows = 0; rows < len; rows++ ) { - // update column max value (ignore sign) - txt = ts.getParsedText( c, $rows.eq( rows ).children()[ index ], index ); - max = Math.max( Math.abs( txt ) || 0, max ); + // find colMax; this only matter for numeric columns + $rows = $tbodies.children( 'tr' ); + len = $rows.length; + for ( index = 0; index < c.columns; index++ ) { + max = 0; + if ( c.parsers[ index ].type === 'numeric' ) { + for ( rows = 0; rows < len; rows++ ) { + // update column max value (ignore sign) + txt = ts.getParsedText( c, $rows.eq( rows ).children()[ index ], index ); + max = Math.max( Math.abs( txt ) || 0, max ); + } } + c.$headerIndexed[ index ].attr( 'data-ts-col-max-value', max ); } - c.$headerIndexed[ index ].attr( 'data-ts-col-max-value', max ); - } - - }, - - // make sure c.$tbodies is up-to-date (init & after updates) - setTbodies: function( c, wo ) { - c.$tbodies = c.$table.children( 'tbody' ).not( '.' + wo.sortTbody_noSort ); - }, - - sorter: function( c ) { - var $table = c.$table, - wo = c.widgetOptions; - - // prevent multiple calls while processing - if ( wo.sortTbody_busy !== true ) { - wo.sortTbody_busy = true; - var $tbodies = $table.children( 'tbody' ).not( '.' + wo.sortTbody_noSort ), - primary = wo.sortTbody_primaryRow || 'tr:eq(0)', - sortList = c.sortList || [], - len = sortList.length; - - if ( len ) { - - // toggle internal row sorting - c.serverSideSorting = !wo.sortTbody_sortRows; - - $tbodies.sort( function( a, b ) { - var sortListIndex, txt, dir, num, colMax, sort, col, order, colA, colB, x, y, - table = c.table, - parsers = c.parsers, - cts = c.textSorter || '', - $tbodyA = $( a ), - $tbodyB = $( b ), - $a = $tbodyA.find( primary ).children( 'td, th' ), - $b = $tbodyB.find( primary ).children( 'td, th' ); - for ( sortListIndex = 0; sortListIndex < len; sortListIndex++ ) { - col = sortList[ sortListIndex ][0]; - order = sortList[ sortListIndex ][1]; - // sort direction, true = asc, false = desc - dir = order === 0; - // column txt - tbody A - txt = ts.getElementText( c, $a.eq( col ), col ); - colA = parsers[ col ].format( txt, table, $a[ col ], col ); - // column txt - tbody B - txt = ts.getElementText( c, $b.eq( col ), col ); - colB = parsers[ col ].format( txt, table, $b[ col ], col ); - - if (c.sortStable && colA === colB && len === 1) { - return $tbodyA.attr( 'data-ts-original-order' ) - $tbodyB.attr( 'data-ts-original-order' ); - } - // fallback to natural sort since it is more robust - num = /n/i.test( parsers && parsers[ col ] ? parsers[ col ].type || '' : '' ); - if ( num && c.strings[ col ] ) { - colMax = c.$headerIndexed[ col ].attr( 'data-ts-col-max-value' ) || - 1.79E+308; // close to Number.MAX_VALUE - // sort strings in numerical columns - if ( typeof ( c.string[ c.strings[ col ] ] ) === 'boolean' ) { - num = ( dir ? 1 : -1 ) * ( c.string[ c.strings[ col ] ] ? -1 : 1 ); - } else { - num = ( c.strings[ col ] ) ? c.string[ c.strings[ col ] ] || 0 : 0; + }, + + // make sure c.$tbodies is up-to-date (init & after updates) + setTbodies: function( c, wo ) { + c.$tbodies = c.$table.children( 'tbody' ).not( '.' + wo.sortTbody_noSort ); + }, + + sorter: function( c ) { + var $table = c.$table, + wo = c.widgetOptions; + + // prevent multiple calls while processing + if ( wo.sortTbody_busy !== true ) { + wo.sortTbody_busy = true; + var $tbodies = $table.children( 'tbody' ).not( '.' + wo.sortTbody_noSort ), + primary = wo.sortTbody_primaryRow || 'tr:eq(0)', + sortList = c.sortList || [], + len = sortList.length; + + if ( len ) { + + // toggle internal row sorting + c.serverSideSorting = !wo.sortTbody_sortRows; + + $tbodies.sort( function( a, b ) { + var sortListIndex, txt, dir, num, colMax, sort, col, order, colA, colB, x, y, + table = c.table, + parsers = c.parsers, + cts = c.textSorter || '', + $tbodyA = $( a ), + $tbodyB = $( b ), + $a = $tbodyA.find( primary ).children( 'td, th' ), + $b = $tbodyB.find( primary ).children( 'td, th' ); + for ( sortListIndex = 0; sortListIndex < len; sortListIndex++ ) { + col = sortList[ sortListIndex ][0]; + order = sortList[ sortListIndex ][1]; + // sort direction, true = asc, false = desc + dir = order === 0; + // column txt - tbody A + txt = ts.getElementText( c, $a.eq( col ), col ); + colA = parsers[ col ].format( txt, table, $a[ col ], col ); + // column txt - tbody B + txt = ts.getElementText( c, $b.eq( col ), col ); + colB = parsers[ col ].format( txt, table, $b[ col ], col ); + + if (c.sortStable && colA === colB && len === 1) { + return $tbodyA.attr( 'data-ts-original-order' ) - $tbodyB.attr( 'data-ts-original-order' ); } - // fall back to built-in numeric sort - // var sort = $.tablesorter['sort' + s](a, b, dir, colMax, table); - sort = c.numberSorter ? c.numberSorter( colA, colB, dir, colMax, table ) : - ts[ 'sortNumeric' + ( dir ? 'Asc' : 'Desc' ) ]( colA, colB, num, colMax, col, table ); - } else { - // set a & b depending on sort direction - x = dir ? colA : colB; - y = dir ? colB : colA; - // text sort function - if ( typeof ( cts ) === 'function' ) { - // custom OVERALL text sorter - sort = cts( x, y, dir, col, table ); - } else if ( typeof ( cts ) === 'object' && cts.hasOwnProperty( col ) ) { - // custom text sorter for a SPECIFIC COLUMN - sort = cts[ col ]( x, y, dir, col, table ); + + // fallback to natural sort since it is more robust + num = /n/i.test( parsers && parsers[ col ] ? parsers[ col ].type || '' : '' ); + if ( num && c.strings[ col ] ) { + colMax = c.$headerIndexed[ col ].attr( 'data-ts-col-max-value' ) || + 1.79E+308; // close to Number.MAX_VALUE + // sort strings in numerical columns + if ( typeof ( c.string[ c.strings[ col ] ] ) === 'boolean' ) { + num = ( dir ? 1 : -1 ) * ( c.string[ c.strings[ col ] ] ? -1 : 1 ); + } else { + num = ( c.strings[ col ] ) ? c.string[ c.strings[ col ] ] || 0 : 0; + } + // fall back to built-in numeric sort + // var sort = $.tablesorter['sort' + s](a, b, dir, colMax, table); + sort = c.numberSorter ? c.numberSorter( colA, colB, dir, colMax, table ) : + ts[ 'sortNumeric' + ( dir ? 'Asc' : 'Desc' ) ]( colA, colB, num, colMax, col, table ); } else { - // fall back to natural sort - sort = ts[ 'sortNatural' + ( dir ? 'Asc' : 'Desc' ) ]( colA, colB, col, table, c ); + // set a & b depending on sort direction + x = dir ? colA : colB; + y = dir ? colB : colA; + // text sort function + if ( typeof ( cts ) === 'function' ) { + // custom OVERALL text sorter + sort = cts( x, y, dir, col, table ); + } else if ( typeof ( cts ) === 'object' && cts.hasOwnProperty( col ) ) { + // custom text sorter for a SPECIFIC COLUMN + sort = cts[ col ]( x, y, dir, col, table ); + } else { + // fall back to natural sort + sort = ts[ 'sortNatural' + ( dir ? 'Asc' : 'Desc' ) ]( colA, colB, col, table, c ); + } } + if ( sort ) { return sort; } } - if ( sort ) { return sort; } - } - return $tbodyA.attr( 'data-ts-original-order' ) - $tbodyB.attr( 'data-ts-original-order' ); - }); + return $tbodyA.attr( 'data-ts-original-order' ) - $tbodyB.attr( 'data-ts-original-order' ); + }); - ts.sortTbodies.restoreTbodies ( c, wo, $tbodies ); - wo.sortTbody_busy = false; + ts.sortTbodies.restoreTbodies( c, wo, $tbodies ); + wo.sortTbody_busy = false; + } } - } - }, - - restoreTbodies : function ( c, wo, $sortedTbodies ) { - var $nosort, $tbodies, $thisTbody, tbLen, nsLen, index, targetIndex, - $table = c.$table, - hasShuffled = true, - indx = 0; - - // hide entire table to improve sort performance - $table.hide(); - $sortedTbodies.appendTo( $table ); - - // reposition no-sort tbodies - $tbodies = $table.children( 'tbody' ); - tbLen = $tbodies.length; - $nosort = $tbodies.filter( '.' + wo.sortTbody_noSort ).appendTo( $table ); - nsLen = $nosort.length; - - if ( nsLen ) { - // don't allow the while loop to cycle more times than the set number of no-sort tbodies - while ( hasShuffled && indx < nsLen ) { - hasShuffled = false; - for ( index = 0; index < nsLen; index++ ) { - targetIndex = parseInt( $nosort.eq( index ).attr( 'data-ts-original-order' ), 10 ); - // if target index > number of tbodies, make it last - targetIndex = targetIndex >= tbLen ? tbLen : targetIndex < 0 ? 0 : targetIndex; - - if ( targetIndex !== $nosort.eq( index ).index() ) { - hasShuffled = true; - $thisTbody = $nosort.eq( index ).detach(); - - if ( targetIndex >= tbLen ) { - // Are we trying to be the last tbody? - $thisTbody.appendTo( $table ); - } else if ( targetIndex === 0 ) { - // Are we trying to be the first tbody? - $thisTbody.prependTo( $table ); - } else { - // No, we want to be somewhere in the middle! - $thisTbody.insertBefore( $table.children( 'tbody:eq(' + targetIndex + ')' ) ); - } + }, + + restoreTbodies : function ( c, wo, $sortedTbodies ) { + var $nosort, $tbodies, $thisTbody, tbLen, nsLen, index, targetIndex, + $table = c.$table, + hasShuffled = true, + indx = 0; + + // hide entire table to improve sort performance + $table.hide(); + $sortedTbodies.appendTo( $table ); + + // reposition no-sort tbodies + $tbodies = $table.children( 'tbody' ); + tbLen = $tbodies.length; + $nosort = $tbodies.filter( '.' + wo.sortTbody_noSort ).appendTo( $table ); + nsLen = $nosort.length; + + if ( nsLen ) { + // don't allow the while loop to cycle more times than the set number of no-sort tbodies + while ( hasShuffled && indx < nsLen ) { + hasShuffled = false; + for ( index = 0; index < nsLen; index++ ) { + targetIndex = parseInt( $nosort.eq( index ).attr( 'data-ts-original-order' ), 10 ); + // if target index > number of tbodies, make it last + targetIndex = targetIndex >= tbLen ? tbLen : targetIndex < 0 ? 0 : targetIndex; + + if ( targetIndex !== $nosort.eq( index ).index() ) { + hasShuffled = true; + $thisTbody = $nosort.eq( index ).detach(); + + if ( targetIndex >= tbLen ) { + // Are we trying to be the last tbody? + $thisTbody.appendTo( $table ); + } else if ( targetIndex === 0 ) { + // Are we trying to be the first tbody? + $thisTbody.prependTo( $table ); + } else { + // No, we want to be somewhere in the middle! + $thisTbody.insertBefore( $table.children( 'tbody:eq(' + targetIndex + ')' ) ); + } + } } + indx++; } - indx++; } + + $table.show(); } - $table.show(); - } - -}; - -ts.addWidget({ - id: 'sortTbody', - // priority < 50 (filter widget), so c.$tbodies has the correct elements - priority: 40, - options: { - // point to a row within the tbody that matches the number of header columns - sortTbody_primaryRow : null, - // sort tbody internal rows - sortTbody_sortRows : false, - // static tbodies (like static rows) - sortTbody_noSort : 'tablesorter-no-sort-tbody' - }, - init : function( table, thisWidget, c, wo ) { - ts.sortTbodies.init( c, wo ); - }, - remove : function( table, c, wo, refreshing ) { - c.$table.unbind( 'sortBegin updateComplete '.split( ' ' ).join( c.namespace + 'sortTbody ' ) ); - c.serverSideSorting = wo.sortTbody_original_serverSideSorting; - c.cssInfoBlock = wo.sortTbody_original_cssInfoBlock; - } -}); + }; + + ts.addWidget({ + id: 'sortTbody', + // priority < 50 (filter widget), so c.$tbodies has the correct elements + priority: 40, + options: { + // point to a row within the tbody that matches the number of header columns + sortTbody_primaryRow : null, + // sort tbody internal rows + sortTbody_sortRows : false, + // static tbodies (like static rows) + sortTbody_noSort : 'tablesorter-no-sort-tbody' + }, + init : function( table, thisWidget, c, wo ) { + ts.sortTbodies.init( c, wo ); + }, + remove : function( table, c, wo, refreshing ) { + c.$table.unbind( 'sortBegin updateComplete '.split( ' ' ).join( c.namespace + 'sortTbody ' ) ); + c.serverSideSorting = wo.sortTbody_original_serverSideSorting; + c.cssInfoBlock = wo.sortTbody_original_cssInfoBlock; + } + }); })( jQuery ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js index ea04b85..7c34b80 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js @@ -12,110 +12,110 @@ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ ;(function($){ -"use strict"; -var ts = $.tablesorter, + 'use strict'; + var ts = $.tablesorter, -// add/refresh row indexes -addIndexes = function(table){ - var $tr, wo, v, indx, rows, - c = table.config; - // "Index" the static rows, saving their current (starting) position in the - // table inside a data() param on the <tr> element itself for later use. - if (c) { - wo = c.widgetOptions; - c.$tbodies.each(function(){ - $tr = $(this).children(); - rows = $tr.length; - $tr.filter(wo.staticRow_class).each(function() { - $tr = $(this); - indx = $tr.data(wo.staticRow_index); - if (typeof indx !== "undefined") { - v = parseFloat(indx); - // percentage of total rows - indx = (/%/.test(indx)) ? Math.round(v/100 * rows) : v; - } else { - indx = $tr.index(); - } - // row indexing starts over within each tbody - $tr.data( wo.staticRow_data, indx ); + // add/refresh row indexes + addIndexes = function(table){ + var $tr, wo, v, indx, rows, + c = table.config; + // 'Index' the static rows, saving their current (starting) position in the + // table inside a data() param on the <tr> element itself for later use. + if (c) { + wo = c.widgetOptions; + c.$tbodies.each(function(){ + $tr = $(this).children(); + rows = $tr.length; + $tr.filter(wo.staticRow_class).each(function() { + $tr = $(this); + indx = $tr.data(wo.staticRow_index); + if (typeof indx !== 'undefined') { + v = parseFloat(indx); + // percentage of total rows + indx = (/%/.test(indx)) ? Math.round(v / 100 * rows) : v; + } else { + indx = $tr.index(); + } + // row indexing starts over within each tbody + $tr.data( wo.staticRow_data, indx ); + }); }); - }); - } -}; + } + }; -ts.addWidget({ - // Give the new Widget an ID to be used in the tablesorter() call, as follows: - // $('#myElement').tablesorter({ widgets: ['zebra', 'staticRow'] }); - id: 'staticRow', + ts.addWidget({ + // Give the new Widget an ID to be used in the tablesorter() call, as follows: + // $('#myElement').tablesorter({ widgets: ['zebra', 'staticRow'] }); + id: 'staticRow', - options: { - staticRow_class : '.static', - staticRow_data : 'static-index', - staticRow_index : 'row-index', - staticRow_event : 'staticRowsRefresh' - }, + options: { + staticRow_class : '.static', + staticRow_data : 'static-index', + staticRow_index : 'row-index', + staticRow_event : 'staticRowsRefresh' + }, - init: function(table, thisWidget, c, wo){ - addIndexes(table); - // refresh static rows after updates - c.$table - .unbind( ('updateComplete.tsstaticrows ' + wo.staticRow_event).replace(/\s+/g, ' ') ) - .bind('updateComplete.tsstaticrows ' + wo.staticRow_event, function(){ - addIndexes(table); - c.$table.trigger('applyWidgets'); - }); - }, + init: function(table, thisWidget, c, wo){ + addIndexes(table); + // refresh static rows after updates + c.$table + .unbind( ('updateComplete.tsstaticrows ' + wo.staticRow_event).replace(/\s+/g, ' ') ) + .bind('updateComplete.tsstaticrows ' + wo.staticRow_event, function(){ + addIndexes(table); + c.$table.trigger('applyWidgets'); + }); + }, - format: function(table, c, wo) { - // Loop thru static rows, moving them to their original "indexed" position, - // & repeat until no more re-shuffling is needed - var targetIndex, $thisRow, indx, numRows, $tbody, hasShuffled, $rows, max; + format: function(table, c, wo) { + // Loop thru static rows, moving them to their original 'indexed' position, + // & repeat until no more re-shuffling is needed + var targetIndex, $thisRow, indx, numRows, $tbody, hasShuffled, $rows, max; - c.$tbodies.each(function(){ - $tbody = $.tablesorter.processTbody(table, $(this), true); // remove tbody - hasShuffled = true; - indx = 0; - $rows = $tbody.children(wo.staticRow_class); - numRows = $tbody.children('tr').length - 1; - max = $rows.length; + c.$tbodies.each(function(){ + $tbody = $.tablesorter.processTbody(table, $(this), true); // remove tbody + hasShuffled = true; + indx = 0; + $rows = $tbody.children(wo.staticRow_class); + numRows = $tbody.children('tr').length - 1; + max = $rows.length; - // don't allow the while loop to cycle more times than the set number of static rows - while (hasShuffled && indx < max) { - hasShuffled = false; - /*jshint loopfunc:true */ - $rows.each(function() { - targetIndex = $(this).data(wo.staticRow_data); - // allow setting target index >> num rows to always make a row last - targetIndex = targetIndex >= numRows ? numRows : targetIndex < 0 ? 0 : targetIndex; - if (targetIndex !== $(this).index()) { - hasShuffled = true; - $thisRow = $(this).detach(); + // don't allow the while loop to cycle more times than the set number of static rows + while (hasShuffled && indx < max) { + hasShuffled = false; + /*jshint loopfunc:true */ + $rows.each(function() { + targetIndex = $(this).data(wo.staticRow_data); + // allow setting target index >> num rows to always make a row last + targetIndex = targetIndex >= numRows ? numRows : targetIndex < 0 ? 0 : targetIndex; + if (targetIndex !== $(this).index()) { + hasShuffled = true; + $thisRow = $(this).detach(); - if (targetIndex >= numRows) { - // Are we trying to be the last row? - $thisRow.appendTo( $tbody ); - } else if (targetIndex === 0) { + if (targetIndex >= numRows) { + // Are we trying to be the last row? + $thisRow.appendTo( $tbody ); + } else if (targetIndex === 0) { // Are we trying to be the first row? $thisRow.prependTo( $tbody ); - } else { - // No, we want to be somewhere in the middle! - $thisRow.insertBefore( $tbody.find('tr:eq(' + targetIndex + ')') ); + } else { + // No, we want to be somewhere in the middle! + $thisRow.insertBefore( $tbody.find('tr:eq(' + targetIndex + ')') ); + } } - } - }); - indx++; - } + }); + indx++; + } - $.tablesorter.processTbody(table, $tbody, false); // restore tbody - }); + $.tablesorter.processTbody(table, $tbody, false); // restore tbody + }); - c.$table.trigger('staticRowsComplete', table); - }, + c.$table.trigger('staticRowsComplete', table); + }, - remove : function(table, c, wo){ - c.$table.unbind( ('updateComplete.tsstaticrows ' + wo.staticRow_event).replace(/\s+/g, ' ') ); - } + remove : function(table, c, wo){ + c.$table.unbind( ('updateComplete.tsstaticrows ' + wo.staticRow_event).replace(/\s+/g, ' ') ); + } -}); + }); -})(jQuery); \ No newline at end of file +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js index f0fa8fc..e545dc6 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js @@ -3,286 +3,286 @@ * by Rob Garrison */ ;(function ($, window) { -'use strict'; -var ts = $.tablesorter || {}; + 'use strict'; + var ts = $.tablesorter || {}; -$.extend(ts.css, { - sticky : 'tablesorter-stickyHeader', // stickyHeader - stickyVis : 'tablesorter-sticky-visible', - stickyHide: 'tablesorter-sticky-hidden', - stickyWrap: 'tablesorter-sticky-wrapper' -}); + $.extend(ts.css, { + sticky : 'tablesorter-stickyHeader', // stickyHeader + stickyVis : 'tablesorter-sticky-visible', + stickyHide: 'tablesorter-sticky-hidden', + stickyWrap: 'tablesorter-sticky-wrapper' + }); -// Add a resize event to table headers -ts.addHeaderResizeEvent = function(table, disable, settings) { - table = $(table)[0]; // make sure we're using a dom element - if ( !table.config ) { return; } - var defaults = { - timer : 250 - }, - options = $.extend({}, defaults, settings), - c = table.config, - wo = c.widgetOptions, - checkSizes = function( triggerEvent ) { - var index, headers, $header, sizes, width, height, - len = c.$headers.length; - wo.resize_flag = true; - headers = []; - for ( index = 0; index < len; index++ ) { - $header = c.$headers.eq( index ); - sizes = $header.data( 'savedSizes' ) || [ 0,0 ]; // fixes #394 - width = $header[0].offsetWidth; - height = $header[0].offsetHeight; - if ( width !== sizes[0] || height !== sizes[1] ) { - $header.data( 'savedSizes', [ width, height ] ); - headers.push( $header[0] ); + // Add a resize event to table headers + ts.addHeaderResizeEvent = function(table, disable, settings) { + table = $(table)[0]; // make sure we're using a dom element + if ( !table.config ) { return; } + var defaults = { + timer : 250 + }, + options = $.extend({}, defaults, settings), + c = table.config, + wo = c.widgetOptions, + checkSizes = function( triggerEvent ) { + var index, headers, $header, sizes, width, height, + len = c.$headers.length; + wo.resize_flag = true; + headers = []; + for ( index = 0; index < len; index++ ) { + $header = c.$headers.eq( index ); + sizes = $header.data( 'savedSizes' ) || [ 0, 0 ]; // fixes #394 + width = $header[0].offsetWidth; + height = $header[0].offsetHeight; + if ( width !== sizes[0] || height !== sizes[1] ) { + $header.data( 'savedSizes', [ width, height ] ); + headers.push( $header[0] ); + } } - } - if ( headers.length && triggerEvent !== false ) { - c.$table.trigger( 'resize', [ headers ] ); - } + if ( headers.length && triggerEvent !== false ) { + c.$table.trigger( 'resize', [ headers ] ); + } + wo.resize_flag = false; + }; + checkSizes( false ); + clearInterval(wo.resize_timer); + if (disable) { wo.resize_flag = false; - }; - checkSizes( false ); - clearInterval(wo.resize_timer); - if (disable) { - wo.resize_flag = false; - return false; - } - wo.resize_timer = setInterval(function() { - if (wo.resize_flag) { return; } - checkSizes(); - }, options.timer); -}; - -// Sticky headers based on this awesome article: -// http://css-tricks.com/13465-persistent-headers/ -// and https://github.com/jmosbech/StickyTableHeaders by Jonas Mosbech -// ************************** -ts.addWidget({ - id: "stickyHeaders", - priority: 60, // sticky widget must be initialized after the filter widget! - options: { - stickyHeaders : '', // extra class name added to the sticky header row - stickyHeaders_attachTo : null, // jQuery selector or object to attach sticky header to - stickyHeaders_xScroll : null, // jQuery selector or object to monitor horizontal scroll position (defaults: xScroll > attachTo > window) - stickyHeaders_yScroll : null, // jQuery selector or object to monitor vertical scroll position (defaults: yScroll > attachTo > window) - stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element - stickyHeaders_filteredToTop: true, // scroll table top into view after filtering - stickyHeaders_cloneId : '-sticky', // added to table ID, if it exists - stickyHeaders_addResizeEvent : true, // trigger "resize" event on headers - stickyHeaders_includeCaption : true, // if false and a caption exist, it won't be included in the sticky header - stickyHeaders_zIndex : 2 // The zIndex of the stickyHeaders, allows the user to adjust this to their needs - }, - format: function(table, c, wo) { - // filter widget doesn't initialize on an empty table. Fixes #449 - if ( c.$table.hasClass('hasStickyHeaders') || ($.inArray('filter', c.widgets) >= 0 && !c.$table.hasClass('hasFilters')) ) { - return; + return false; } - var index, len, $t, - $table = c.$table, - // add position: relative to attach element, hopefully it won't cause trouble. - $attach = $(wo.stickyHeaders_attachTo), - namespace = c.namespace + 'stickyheaders ', - // element to watch for the scroll event - $yScroll = $(wo.stickyHeaders_yScroll || wo.stickyHeaders_attachTo || window), - $xScroll = $(wo.stickyHeaders_xScroll || wo.stickyHeaders_attachTo || window), - $thead = $table.children('thead:first'), - $header = $thead.children('tr').not('.sticky-false').children(), - $tfoot = $table.children('tfoot'), - $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '', - stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, - // is this table nested? If so, find parent sticky header wrapper (div, not table) - $nestedSticky = $table.parent().closest('.' + ts.css.table).hasClass('hasStickyHeaders') ? - $table.parent().closest('table.tablesorter')[0].config.widgetOptions.$sticky.parent() : [], - nestedStickyTop = $nestedSticky.length ? $nestedSticky.height() : 0, - // clone table, then wrap to make sticky header - $stickyTable = wo.$sticky = $table.clone() - .addClass('containsStickyHeaders ' + ts.css.sticky + ' ' + wo.stickyHeaders + ' ' + c.namespace.slice(1) + '_extra_table' ) - .wrap('<div class="' + ts.css.stickyWrap + '">'), - $stickyWrap = $stickyTable.parent() - .addClass(ts.css.stickyHide) - .css({ - position : $attach.length ? 'absolute' : 'fixed', - padding : parseInt( $stickyTable.parent().parent().css('padding-left'), 10 ), - top : stickyOffset + nestedStickyTop, - left : 0, - visibility : 'hidden', - zIndex : wo.stickyHeaders_zIndex || 2 - }), - $stickyThead = $stickyTable.children('thead:first'), - $stickyCells, - laststate = '', - spacing = 0, - setWidth = function($orig, $clone){ - var index, width, border, $cell, $this, - $cells = $orig.filter(':visible'), - len = $cells.length; - for ( index = 0; index < len; index++ ) { - $cell = $clone.filter(':visible').eq(index); - $this = $cells.eq(index); - // code from https://github.com/jmosbech/StickyTableHeaders - if ($this.css('box-sizing') === 'border-box') { - width = $this.outerWidth(); - } else { - if ($cell.css('border-collapse') === 'collapse') { - if (window.getComputedStyle) { - width = parseFloat( window.getComputedStyle($this[0], null).width ); + wo.resize_timer = setInterval(function() { + if (wo.resize_flag) { return; } + checkSizes(); + }, options.timer); + }; + + // Sticky headers based on this awesome article: + // http://css-tricks.com/13465-persistent-headers/ + // and https://github.com/jmosbech/StickyTableHeaders by Jonas Mosbech + // ************************** + ts.addWidget({ + id: 'stickyHeaders', + priority: 60, // sticky widget must be initialized after the filter widget! + options: { + stickyHeaders : '', // extra class name added to the sticky header row + stickyHeaders_attachTo : null, // jQuery selector or object to attach sticky header to + stickyHeaders_xScroll : null, // jQuery selector or object to monitor horizontal scroll position (defaults: xScroll > attachTo > window) + stickyHeaders_yScroll : null, // jQuery selector or object to monitor vertical scroll position (defaults: yScroll > attachTo > window) + stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element + stickyHeaders_filteredToTop: true, // scroll table top into view after filtering + stickyHeaders_cloneId : '-sticky', // added to table ID, if it exists + stickyHeaders_addResizeEvent : true, // trigger 'resize' event on headers + stickyHeaders_includeCaption : true, // if false and a caption exist, it won't be included in the sticky header + stickyHeaders_zIndex : 2 // The zIndex of the stickyHeaders, allows the user to adjust this to their needs + }, + format: function(table, c, wo) { + // filter widget doesn't initialize on an empty table. Fixes #449 + if ( c.$table.hasClass('hasStickyHeaders') || ($.inArray('filter', c.widgets) >= 0 && !c.$table.hasClass('hasFilters')) ) { + return; + } + var index, len, $t, + $table = c.$table, + // add position: relative to attach element, hopefully it won't cause trouble. + $attach = $(wo.stickyHeaders_attachTo), + namespace = c.namespace + 'stickyheaders ', + // element to watch for the scroll event + $yScroll = $(wo.stickyHeaders_yScroll || wo.stickyHeaders_attachTo || window), + $xScroll = $(wo.stickyHeaders_xScroll || wo.stickyHeaders_attachTo || window), + $thead = $table.children('thead:first'), + $header = $thead.children('tr').not('.sticky-false').children(), + $tfoot = $table.children('tfoot'), + $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '', + stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, + // is this table nested? If so, find parent sticky header wrapper (div, not table) + $nestedSticky = $table.parent().closest('.' + ts.css.table).hasClass('hasStickyHeaders') ? + $table.parent().closest('table.tablesorter')[0].config.widgetOptions.$sticky.parent() : [], + nestedStickyTop = $nestedSticky.length ? $nestedSticky.height() : 0, + // clone table, then wrap to make sticky header + $stickyTable = wo.$sticky = $table.clone() + .addClass('containsStickyHeaders ' + ts.css.sticky + ' ' + wo.stickyHeaders + ' ' + c.namespace.slice(1) + '_extra_table' ) + .wrap('<div class="' + ts.css.stickyWrap + '">'), + $stickyWrap = $stickyTable.parent() + .addClass(ts.css.stickyHide) + .css({ + position : $attach.length ? 'absolute' : 'fixed', + padding : parseInt( $stickyTable.parent().parent().css('padding-left'), 10 ), + top : stickyOffset + nestedStickyTop, + left : 0, + visibility : 'hidden', + zIndex : wo.stickyHeaders_zIndex || 2 + }), + $stickyThead = $stickyTable.children('thead:first'), + $stickyCells, + laststate = '', + spacing = 0, + setWidth = function($orig, $clone){ + var index, width, border, $cell, $this, + $cells = $orig.filter(':visible'), + len = $cells.length; + for ( index = 0; index < len; index++ ) { + $cell = $clone.filter(':visible').eq(index); + $this = $cells.eq(index); + // code from https://github.com/jmosbech/StickyTableHeaders + if ($this.css('box-sizing') === 'border-box') { + width = $this.outerWidth(); + } else { + if ($cell.css('border-collapse') === 'collapse') { + if (window.getComputedStyle) { + width = parseFloat( window.getComputedStyle($this[0], null).width ); + } else { + // ie8 only + border = parseFloat( $this.css('border-width') ); + width = $this.outerWidth() - parseFloat( $this.css('padding-left') ) - parseFloat( $this.css('padding-right') ) - border; + } } else { - // ie8 only - border = parseFloat( $this.css('border-width') ); - width = $this.outerWidth() - parseFloat( $this.css('padding-left') ) - parseFloat( $this.css('padding-right') ) - border; + width = $this.width(); } - } else { - width = $this.width(); } + $cell.css({ + 'width': width, + 'min-width': width, + 'max-width': width + }); } - $cell.css({ - 'width': width, - 'min-width': width, - 'max-width': width + }, + resizeHeader = function() { + stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0; + spacing = 0; + $stickyWrap.css({ + left : $attach.length ? parseInt($attach.css('padding-left'), 10) || 0 : + $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing, + width: $table.outerWidth() }); - } - }, - resizeHeader = function() { - stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0; - spacing = 0; - $stickyWrap.css({ - left : $attach.length ? parseInt($attach.css('padding-left'), 10) || 0 : - $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing, - width: $table.outerWidth() - }); - setWidth( $table, $stickyTable ); - setWidth( $header, $stickyCells ); - }, - scrollSticky = function( resizing ) { - if (!$table.is(':visible')) { return; } // fixes #278 - // Detect nested tables - fixes #724 - nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; - var offset = $table.offset(), - yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 - xWindow = $.isWindow( $xScroll[0] ), - // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, - scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, - tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)), - isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', - cssSettings = { visibility : isVisible }; + setWidth( $table, $stickyTable ); + setWidth( $header, $stickyCells ); + }, + scrollSticky = function( resizing ) { + if (!$table.is(':visible')) { return; } // fixes #278 + // Detect nested tables - fixes #724 + nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; + var offset = $table.offset(), + yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 + xWindow = $.isWindow( $xScroll[0] ), + // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, + scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, + tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)), + isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', + cssSettings = { visibility : isVisible }; - if ($attach.length) { - cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); - } - if (xWindow) { - // adjust when scrolling horizontally - fixes issue #143 - cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing; - } - if ($nestedSticky.length) { - cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; - } - $stickyWrap - .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) - .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) - .css(cssSettings); - if (isVisible !== laststate || resizing) { - // make sure the column widths match + if ($attach.length) { + cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); + } + if (xWindow) { + // adjust when scrolling horizontally - fixes issue #143 + cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing; + } + if ($nestedSticky.length) { + cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; + } + $stickyWrap + .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) + .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) + .css(cssSettings); + if (isVisible !== laststate || resizing) { + // make sure the column widths match + resizeHeader(); + laststate = isVisible; + } + }; + // only add a position relative if a position isn't already defined + if ($attach.length && !$attach.css('position')) { + $attach.css('position', 'relative'); + } + // fix clone ID, if it exists - fixes #271 + if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; } + // clear out cloned table, except for sticky header + // include caption & filter row (fixes #126 & #249) - don't remove cells to get correct cell indexing + $stickyTable.find('thead:gt(0), tr.sticky-false').hide(); + $stickyTable.find('tbody, tfoot').remove(); + $stickyTable.find('caption').toggle(wo.stickyHeaders_includeCaption); + // issue #172 - find td/th in sticky header + $stickyCells = $stickyThead.children().children(); + $stickyTable.css({ height:0, width:0, margin: 0 }); + // remove resizable block + $stickyCells.find('.' + ts.css.resizer).remove(); + // update sticky header class names to match real header after sorting + $table + .addClass('hasStickyHeaders') + .bind('pagerComplete' + namespace, function() { resizeHeader(); - laststate = isVisible; - } - }; - // only add a position relative if a position isn't already defined - if ($attach.length && !$attach.css('position')) { - $attach.css('position', 'relative'); - } - // fix clone ID, if it exists - fixes #271 - if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; } - // clear out cloned table, except for sticky header - // include caption & filter row (fixes #126 & #249) - don't remove cells to get correct cell indexing - $stickyTable.find('thead:gt(0), tr.sticky-false').hide(); - $stickyTable.find('tbody, tfoot').remove(); - $stickyTable.find('caption').toggle(wo.stickyHeaders_includeCaption); - // issue #172 - find td/th in sticky header - $stickyCells = $stickyThead.children().children(); - $stickyTable.css({ height:0, width:0, margin: 0 }); - // remove resizable block - $stickyCells.find('.' + ts.css.resizer).remove(); - // update sticky header class names to match real header after sorting - $table - .addClass('hasStickyHeaders') - .bind('pagerComplete' + namespace, function() { - resizeHeader(); - }); + }); - ts.bindEvents(table, $stickyThead.children().children('.' + ts.css.header)); + ts.bindEvents(table, $stickyThead.children().children('.' + ts.css.header)); - // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. - $table.after( $stickyWrap ); + // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. + $table.after( $stickyWrap ); - // onRenderHeader is defined, we need to do something about it (fixes #641) - if (c.onRenderHeader) { - $t = $stickyThead.children('tr').children(); - len = $t.length; - for ( index = 0; index < len; index++ ) { - // send second parameter - c.onRenderHeader.apply( $t.eq( index ), [ index, c, $stickyTable ] ); + // onRenderHeader is defined, we need to do something about it (fixes #641) + if (c.onRenderHeader) { + $t = $stickyThead.children('tr').children(); + len = $t.length; + for ( index = 0; index < len; index++ ) { + // send second parameter + c.onRenderHeader.apply( $t.eq( index ), [ index, c, $stickyTable ] ); + } } - } - // make it sticky! - $xScroll.add($yScroll) - .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') ) - .bind('scroll resize '.split(' ').join( namespace ), function( event ) { - scrollSticky( event.type === 'resize' ); - }); - c.$table - .unbind('stickyHeadersUpdate' + namespace) - .bind('stickyHeadersUpdate' + namespace, function(){ - scrollSticky( true ); - }); + // make it sticky! + $xScroll.add($yScroll) + .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') ) + .bind('scroll resize '.split(' ').join( namespace ), function( event ) { + scrollSticky( event.type === 'resize' ); + }); + c.$table + .unbind('stickyHeadersUpdate' + namespace) + .bind('stickyHeadersUpdate' + namespace, function(){ + scrollSticky( true ); + }); - if (wo.stickyHeaders_addResizeEvent) { - ts.addHeaderResizeEvent(table); - } + if (wo.stickyHeaders_addResizeEvent) { + ts.addHeaderResizeEvent(table); + } - // look for filter widget - if ($table.hasClass('hasFilters') && wo.filter_columnFilters) { - // scroll table into view after filtering, if sticky header is active - #482 - $table.bind('filterEnd' + namespace, function() { - // $(':focus') needs jQuery 1.6+ - var $td = $(document.activeElement).closest('td'), - column = $td.parent().children().index($td); - // only scroll if sticky header is active - if ($stickyWrap.hasClass(ts.css.stickyVis) && wo.stickyHeaders_filteredToTop) { - // scroll to original table (not sticky clone) - window.scrollTo(0, $table.position().top); - // give same input/select focus; check if c.$filters exists; fixes #594 - if (column >= 0 && c.$filters) { - c.$filters.eq(column).find('a, select, input').filter(':visible').focus(); + // look for filter widget + if ($table.hasClass('hasFilters') && wo.filter_columnFilters) { + // scroll table into view after filtering, if sticky header is active - #482 + $table.bind('filterEnd' + namespace, function() { + // $(':focus') needs jQuery 1.6+ + var $td = $(document.activeElement).closest('td'), + column = $td.parent().children().index($td); + // only scroll if sticky header is active + if ($stickyWrap.hasClass(ts.css.stickyVis) && wo.stickyHeaders_filteredToTop) { + // scroll to original table (not sticky clone) + window.scrollTo(0, $table.position().top); + // give same input/select focus; check if c.$filters exists; fixes #594 + if (column >= 0 && c.$filters) { + c.$filters.eq(column).find('a, select, input').filter(':visible').focus(); + } } + }); + ts.filter.bindSearch( $table, $stickyCells.find('.' + ts.css.filter) ); + // support hideFilters + if (wo.filter_hideFilters) { + ts.filter.hideFilters($stickyTable, c); } - }); - ts.filter.bindSearch( $table, $stickyCells.find('.' + ts.css.filter) ); - // support hideFilters - if (wo.filter_hideFilters) { - ts.filter.hideFilters($stickyTable, c); } - } - $table.trigger('stickyHeadersInit'); + $table.trigger('stickyHeadersInit'); - }, - remove: function(table, c, wo) { - var namespace = c.namespace + 'stickyheaders '; - c.$table - .removeClass('hasStickyHeaders') - .unbind( ('pagerComplete filterEnd stickyHeadersUpdate '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) - .next('.' + ts.css.stickyWrap).remove(); - if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table - $(window) - .add(wo.stickyHeaders_xScroll) - .add(wo.stickyHeaders_yScroll) - .add(wo.stickyHeaders_attachTo) - .unbind( ('scroll resize '.split(' ').join(namespace)).replace(/\s+/g, ' ') ); - ts.addHeaderResizeEvent(table, false); - } -}); + }, + remove: function(table, c, wo) { + var namespace = c.namespace + 'stickyheaders '; + c.$table + .removeClass('hasStickyHeaders') + .unbind( ('pagerComplete filterEnd stickyHeadersUpdate '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) + .next('.' + ts.css.stickyWrap).remove(); + if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table + $(window) + .add(wo.stickyHeaders_xScroll) + .add(wo.stickyHeaders_yScroll) + .add(wo.stickyHeaders_attachTo) + .unbind( ('scroll resize '.split(' ').join(namespace)).replace(/\s+/g, ' ') ); + ts.addHeaderResizeEvent(table, false); + } + }); })(jQuery, window); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js index 472d34c..bfb9c74 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js @@ -1,89 +1,89 @@ /*! Widget: storage - updated 3/26/2015 (v2.21.3) */ ;(function ($, window, document) { -'use strict'; + 'use strict'; -var ts = $.tablesorter || {}; -// *** Store data in local storage, with a cookie fallback *** -/* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json) - if you need it, then include https://github.com/douglascrockford/JSON-js + var ts = $.tablesorter || {}; + // *** Store data in local storage, with a cookie fallback *** + /* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json) + if you need it, then include https://github.com/douglascrockford/JSON-js - $.parseJSON is not available is jQuery versions older than 1.4.1, using older - versions will only allow storing information for one page at a time + $.parseJSON is not available is jQuery versions older than 1.4.1, using older + versions will only allow storing information for one page at a time - // *** Save data (JSON format only) *** - // val must be valid JSON... use http://jsonlint.com/ to ensure it is valid - var val = { "mywidget" : "data1" }; // valid JSON uses double quotes - // $.tablesorter.storage(table, key, val); - $.tablesorter.storage(table, 'tablesorter-mywidget', val); + // *** Save data (JSON format only) *** + // val must be valid JSON... use http://jsonlint.com/ to ensure it is valid + var val = { "mywidget" : "data1" }; // valid JSON uses double quotes + // $.tablesorter.storage(table, key, val); + $.tablesorter.storage(table, 'tablesorter-mywidget', val); - // *** Get data: $.tablesorter.storage(table, key); *** - v = $.tablesorter.storage(table, 'tablesorter-mywidget'); - // val may be empty, so also check for your data - val = (v && v.hasOwnProperty('mywidget')) ? v.mywidget : ''; - alert(val); // "data1" if saved, or "" if not -*/ -ts.storage = function(table, key, value, options) { - table = $(table)[0]; - var cookieIndex, cookies, date, - hasStorage = false, - values = {}, - c = table.config, - wo = c && c.widgetOptions, - storageType = ( options && options.useSessionStorage ) || ( wo && wo.storage_useSessionStorage ) ? - 'sessionStorage' : 'localStorage', - $table = $(table), - // id from (1) options ID, (2) table "data-table-group" attribute, (3) widgetOptions.storage_tableId, - // (4) table ID, then (5) table index - id = options && options.id || - $table.attr( options && options.group || wo && wo.storage_group || 'data-table-group') || - wo && wo.storage_tableId || table.id || $('.tablesorter').index( $table ), - // url from (1) options url, (2) table "data-table-page" attribute, (3) widgetOptions.storage_fixedUrl, - // (4) table.config.fixedUrl (deprecated), then (5) window location path - url = options && options.url || - $table.attr(options && options.page || wo && wo.storage_page || 'data-table-page') || - wo && wo.storage_fixedUrl || c && c.fixedUrl || window.location.pathname; - // https://gist.github.com/paulirish/5558557 - if (storageType in window) { - try { - window[storageType].setItem('_tmptest', 'temp'); - hasStorage = true; - window[storageType].removeItem('_tmptest'); - } catch(error) { - if (c && c.debug) { - ts.log( storageType + ' is not supported in this browser' ); + // *** Get data: $.tablesorter.storage(table, key); *** + v = $.tablesorter.storage(table, 'tablesorter-mywidget'); + // val may be empty, so also check for your data + val = (v && v.hasOwnProperty('mywidget')) ? v.mywidget : ''; + alert(val); // 'data1' if saved, or '' if not + */ + ts.storage = function(table, key, value, options) { + table = $(table)[0]; + var cookieIndex, cookies, date, + hasStorage = false, + values = {}, + c = table.config, + wo = c && c.widgetOptions, + storageType = ( options && options.useSessionStorage ) || ( wo && wo.storage_useSessionStorage ) ? + 'sessionStorage' : 'localStorage', + $table = $(table), + // id from (1) options ID, (2) table 'data-table-group' attribute, (3) widgetOptions.storage_tableId, + // (4) table ID, then (5) table index + id = options && options.id || + $table.attr( options && options.group || wo && wo.storage_group || 'data-table-group') || + wo && wo.storage_tableId || table.id || $('.tablesorter').index( $table ), + // url from (1) options url, (2) table 'data-table-page' attribute, (3) widgetOptions.storage_fixedUrl, + // (4) table.config.fixedUrl (deprecated), then (5) window location path + url = options && options.url || + $table.attr(options && options.page || wo && wo.storage_page || 'data-table-page') || + wo && wo.storage_fixedUrl || c && c.fixedUrl || window.location.pathname; + // https://gist.github.com/paulirish/5558557 + if (storageType in window) { + try { + window[storageType].setItem('_tmptest', 'temp'); + hasStorage = true; + window[storageType].removeItem('_tmptest'); + } catch (error) { + if (c && c.debug) { + console.warn( storageType + ' is not supported in this browser' ); + } } } - } - // *** get value *** - if ($.parseJSON) { - if (hasStorage) { - values = $.parseJSON( window[storageType][key] || 'null' ) || {}; - } else { - // old browser, using cookies - cookies = document.cookie.split(/[;\s|=]/); - // add one to get from the key to the value - cookieIndex = $.inArray(key, cookies) + 1; - values = (cookieIndex !== 0) ? $.parseJSON(cookies[cookieIndex] || 'null') || {} : {}; - } - } - // allow value to be an empty string too - if ((value || value === '') && window.JSON && JSON.hasOwnProperty('stringify')) { - // add unique identifiers = url pathname > table ID/index on page > data - if (!values[url]) { - values[url] = {}; + // *** get value *** + if ($.parseJSON) { + if (hasStorage) { + values = $.parseJSON( window[storageType][key] || 'null' ) || {}; + } else { + // old browser, using cookies + cookies = document.cookie.split(/[;\s|=]/); + // add one to get from the key to the value + cookieIndex = $.inArray(key, cookies) + 1; + values = (cookieIndex !== 0) ? $.parseJSON(cookies[cookieIndex] || 'null') || {} : {}; + } } - values[url][id] = value; - // *** set value *** - if (hasStorage) { - window[storageType][key] = JSON.stringify(values); + // allow value to be an empty string too + if ((value || value === '') && window.JSON && JSON.hasOwnProperty('stringify')) { + // add unique identifiers = url pathname > table ID/index on page > data + if (!values[url]) { + values[url] = {}; + } + values[url][id] = value; + // *** set value *** + if (hasStorage) { + window[storageType][key] = JSON.stringify(values); + } else { + date = new Date(); + date.setTime(date.getTime() + (31536e+6)); // 365 days + document.cookie = key + '=' + (JSON.stringify(values)).replace(/\"/g, '\"') + '; expires=' + date.toGMTString() + '; path=/'; + } } else { - date = new Date(); - date.setTime(date.getTime() + (31536e+6)); // 365 days - document.cookie = key + '=' + (JSON.stringify(values)).replace(/\"/g,'\"') + '; expires=' + date.toGMTString() + '; path=/'; + return values && values[url] ? values[url][id] : ''; } - } else { - return values && values[url] ? values[url][id] : ''; - } -}; + }; })(jQuery, window, document); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js index 9d6a095..467fcfc 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js @@ -1,185 +1,185 @@ /*! Widget: uitheme - updated 3/26/2015 (v2.21.3) */ ;(function ($) { -'use strict'; -var ts = $.tablesorter || {}; + 'use strict'; + var ts = $.tablesorter || {}; -ts.themes = { - 'bootstrap' : { - table : 'table table-bordered table-striped', - caption : 'caption', - // header class names - header : 'bootstrap-header', // give the header a gradient background (theme.bootstrap_2.css) - sortNone : '', - sortAsc : '', - sortDesc : '', - active : '', // applied when column is sorted - hover : '', // custom css required - a defined bootstrap style may not override other classes - // icon class names - icons : '', // add "icon-white" to make them white; this icon class is added to the <i> in the header - iconSortNone : 'bootstrap-icon-unsorted', // class name added to icon when column is not sorted - iconSortAsc : 'icon-chevron-up glyphicon glyphicon-chevron-up', // class name added to icon when column has ascending sort - iconSortDesc : 'icon-chevron-down glyphicon glyphicon-chevron-down', // class name added to icon when column has descending sort - filterRow : '', // filter row class - footerRow : '', - footerCells : '', - even : '', // even row zebra striping - odd : '' // odd row zebra striping - }, - 'jui' : { - table : 'ui-widget ui-widget-content ui-corner-all', // table classes - caption : 'ui-widget-content', - // header class names - header : 'ui-widget-header ui-corner-all ui-state-default', // header classes - sortNone : '', - sortAsc : '', - sortDesc : '', - active : 'ui-state-active', // applied when column is sorted - hover : 'ui-state-hover', // hover class - // icon class names - icons : 'ui-icon', // icon class added to the <i> in the header - iconSortNone : 'ui-icon-carat-2-n-s', // class name added to icon when column is not sorted - iconSortAsc : 'ui-icon-carat-1-n', // class name added to icon when column has ascending sort - iconSortDesc : 'ui-icon-carat-1-s', // class name added to icon when column has descending sort - filterRow : '', - footerRow : '', - footerCells : '', - even : 'ui-widget-content', // even row zebra striping - odd : 'ui-state-default' // odd row zebra striping - } -}; - -$.extend(ts.css, { - wrapper : 'tablesorter-wrapper' // ui theme & resizable -}); + ts.themes = { + 'bootstrap' : { + table : 'table table-bordered table-striped', + caption : 'caption', + // header class names + header : 'bootstrap-header', // give the header a gradient background (theme.bootstrap_2.css) + sortNone : '', + sortAsc : '', + sortDesc : '', + active : '', // applied when column is sorted + hover : '', // custom css required - a defined bootstrap style may not override other classes + // icon class names + icons : '', // add 'icon-white' to make them white; this icon class is added to the <i> in the header + iconSortNone : 'bootstrap-icon-unsorted', // class name added to icon when column is not sorted + iconSortAsc : 'icon-chevron-up glyphicon glyphicon-chevron-up', // class name added to icon when column has ascending sort + iconSortDesc : 'icon-chevron-down glyphicon glyphicon-chevron-down', // class name added to icon when column has descending sort + filterRow : '', // filter row class + footerRow : '', + footerCells : '', + even : '', // even row zebra striping + odd : '' // odd row zebra striping + }, + 'jui' : { + table : 'ui-widget ui-widget-content ui-corner-all', // table classes + caption : 'ui-widget-content', + // header class names + header : 'ui-widget-header ui-corner-all ui-state-default', // header classes + sortNone : '', + sortAsc : '', + sortDesc : '', + active : 'ui-state-active', // applied when column is sorted + hover : 'ui-state-hover', // hover class + // icon class names + icons : 'ui-icon', // icon class added to the <i> in the header + iconSortNone : 'ui-icon-carat-2-n-s', // class name added to icon when column is not sorted + iconSortAsc : 'ui-icon-carat-1-n', // class name added to icon when column has ascending sort + iconSortDesc : 'ui-icon-carat-1-s', // class name added to icon when column has descending sort + filterRow : '', + footerRow : '', + footerCells : '', + even : 'ui-widget-content', // even row zebra striping + odd : 'ui-state-default' // odd row zebra striping + } + }; -ts.addWidget({ - id: "uitheme", - priority: 10, - format: function(table, c, wo) { - var i, hdr, icon, time, $header, $icon, $tfoot, $h, oldtheme, oldremove, oldIconRmv, hasOldTheme, - themesAll = ts.themes, - $table = c.$table.add( $( c.namespace + '_extra_table' ) ), - $headers = c.$headers.add( $( c.namespace + '_extra_headers' ) ), - theme = c.theme || 'jui', - themes = themesAll[theme] || {}, - remove = $.trim( [ themes.sortNone, themes.sortDesc, themes.sortAsc, themes.active ].join( ' ' ) ), - iconRmv = $.trim( [ themes.iconSortNone, themes.iconSortDesc, themes.iconSortAsc ].join( ' ' ) ); - if (c.debug) { time = new Date(); } - // initialization code - run once - if (!$table.hasClass('tablesorter-' + theme) || c.theme !== c.appliedTheme || !wo.uitheme_applied) { - wo.uitheme_applied = true; - oldtheme = themesAll[c.appliedTheme] || {}; - hasOldTheme = !$.isEmptyObject(oldtheme); - oldremove = hasOldTheme ? [ oldtheme.sortNone, oldtheme.sortDesc, oldtheme.sortAsc, oldtheme.active ].join( ' ' ) : ''; - oldIconRmv = hasOldTheme ? [ oldtheme.iconSortNone, oldtheme.iconSortDesc, oldtheme.iconSortAsc ].join( ' ' ) : ''; - if (hasOldTheme) { - wo.zebra[0] = $.trim( ' ' + wo.zebra[0].replace(' ' + oldtheme.even, '') ); - wo.zebra[1] = $.trim( ' ' + wo.zebra[1].replace(' ' + oldtheme.odd, '') ); - c.$tbodies.children().removeClass( [oldtheme.even, oldtheme.odd].join(' ') ); - } - // update zebra stripes - if (themes.even) { wo.zebra[0] += ' ' + themes.even; } - if (themes.odd) { wo.zebra[1] += ' ' + themes.odd; } - // add caption style - $table.children('caption') - .removeClass(oldtheme.caption || '') - .addClass(themes.caption); - // add table/footer class names - $tfoot = $table - // remove other selected themes - .removeClass( (c.appliedTheme ? 'tablesorter-' + (c.appliedTheme || '') : '') + ' ' + (oldtheme.table || '') ) - .addClass('tablesorter-' + theme + ' ' + (themes.table || '')) // add theme widget class name - .children('tfoot'); - c.appliedTheme = c.theme; + $.extend(ts.css, { + wrapper : 'tablesorter-wrapper' // ui theme & resizable + }); - if ($tfoot.length) { - $tfoot - // if oldtheme.footerRow or oldtheme.footerCells are undefined, all class names are removed - .children('tr').removeClass(oldtheme.footerRow || '').addClass(themes.footerRow) - .children('th, td').removeClass(oldtheme.footerCells || '').addClass(themes.footerCells); - } - // update header classes - $headers - .removeClass( (hasOldTheme ? [oldtheme.header, oldtheme.hover, oldremove].join(' ') : '') || '' ) - .addClass(themes.header) - .not('.sorter-false') - .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') - .bind('mouseenter.tsuitheme mouseleave.tsuitheme', function(event) { - // toggleClass with switch added in jQuery 1.3 - $(this)[ event.type === 'mouseenter' ? 'addClass' : 'removeClass' ](themes.hover || ''); - }); + ts.addWidget({ + id: 'uitheme', + priority: 10, + format: function(table, c, wo) { + var i, hdr, icon, time, $header, $icon, $tfoot, $h, oldtheme, oldremove, oldIconRmv, hasOldTheme, + themesAll = ts.themes, + $table = c.$table.add( $( c.namespace + '_extra_table' ) ), + $headers = c.$headers.add( $( c.namespace + '_extra_headers' ) ), + theme = c.theme || 'jui', + themes = themesAll[theme] || {}, + remove = $.trim( [ themes.sortNone, themes.sortDesc, themes.sortAsc, themes.active ].join( ' ' ) ), + iconRmv = $.trim( [ themes.iconSortNone, themes.iconSortDesc, themes.iconSortAsc ].join( ' ' ) ); + if (c.debug) { time = new Date(); } + // initialization code - run once + if (!$table.hasClass('tablesorter-' + theme) || c.theme !== c.appliedTheme || !wo.uitheme_applied) { + wo.uitheme_applied = true; + oldtheme = themesAll[c.appliedTheme] || {}; + hasOldTheme = !$.isEmptyObject(oldtheme); + oldremove = hasOldTheme ? [ oldtheme.sortNone, oldtheme.sortDesc, oldtheme.sortAsc, oldtheme.active ].join( ' ' ) : ''; + oldIconRmv = hasOldTheme ? [ oldtheme.iconSortNone, oldtheme.iconSortDesc, oldtheme.iconSortAsc ].join( ' ' ) : ''; + if (hasOldTheme) { + wo.zebra[0] = $.trim( ' ' + wo.zebra[0].replace(' ' + oldtheme.even, '') ); + wo.zebra[1] = $.trim( ' ' + wo.zebra[1].replace(' ' + oldtheme.odd, '') ); + c.$tbodies.children().removeClass( [ oldtheme.even, oldtheme.odd ].join(' ') ); + } + // update zebra stripes + if (themes.even) { wo.zebra[0] += ' ' + themes.even; } + if (themes.odd) { wo.zebra[1] += ' ' + themes.odd; } + // add caption style + $table.children('caption') + .removeClass(oldtheme.caption || '') + .addClass(themes.caption); + // add table/footer class names + $tfoot = $table + // remove other selected themes + .removeClass( (c.appliedTheme ? 'tablesorter-' + (c.appliedTheme || '') : '') + ' ' + (oldtheme.table || '') ) + .addClass('tablesorter-' + theme + ' ' + (themes.table || '')) // add theme widget class name + .children('tfoot'); + c.appliedTheme = c.theme; - $headers.each(function(){ - var $this = $(this); - if (!$this.find('.' + ts.css.wrapper).length) { - // Firefox needs this inner div to position the icon & resizer correctly - $this.wrapInner('<div class="' + ts.css.wrapper + '" style="position:relative;height:100%;width:100%"></div>'); + if ($tfoot.length) { + $tfoot + // if oldtheme.footerRow or oldtheme.footerCells are undefined, all class names are removed + .children('tr').removeClass(oldtheme.footerRow || '').addClass(themes.footerRow) + .children('th, td').removeClass(oldtheme.footerCells || '').addClass(themes.footerCells); } - }); - if (c.cssIcon) { - // if c.cssIcon is '', then no <i> is added to the header + // update header classes $headers - .find('.' + ts.css.icon) - .removeClass(hasOldTheme ? [oldtheme.icons, oldIconRmv].join(' ') : '') - .addClass(themes.icons || ''); - } - if ($table.hasClass('hasFilters')) { - $table.children('thead').children('.' + ts.css.filterRow) - .removeClass(hasOldTheme ? oldtheme.filterRow || '' : '') - .addClass(themes.filterRow || ''); + .removeClass( (hasOldTheme ? [ oldtheme.header, oldtheme.hover, oldremove ].join(' ') : '') || '' ) + .addClass(themes.header) + .not('.sorter-false') + .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') + .bind('mouseenter.tsuitheme mouseleave.tsuitheme', function(event) { + // toggleClass with switch added in jQuery 1.3 + $(this)[ event.type === 'mouseenter' ? 'addClass' : 'removeClass' ](themes.hover || ''); + }); + + $headers.each(function(){ + var $this = $(this); + if (!$this.find('.' + ts.css.wrapper).length) { + // Firefox needs this inner div to position the icon & resizer correctly + $this.wrapInner('<div class="' + ts.css.wrapper + '" style="position:relative;height:100%;width:100%"></div>'); + } + }); + if (c.cssIcon) { + // if c.cssIcon is '', then no <i> is added to the header + $headers + .find('.' + ts.css.icon) + .removeClass(hasOldTheme ? [ oldtheme.icons, oldIconRmv ].join(' ') : '') + .addClass(themes.icons || ''); + } + if ($table.hasClass('hasFilters')) { + $table.children('thead').children('.' + ts.css.filterRow) + .removeClass(hasOldTheme ? oldtheme.filterRow || '' : '') + .addClass(themes.filterRow || ''); + } } - } - for (i = 0; i < c.columns; i++) { - $header = c.$headers - .add($(c.namespace + '_extra_headers')) - .not('.sorter-false') - .filter('[data-column="' + i + '"]'); - $icon = (ts.css.icon) ? $header.find('.' + ts.css.icon) : $(); - $h = $headers.not('.sorter-false').filter('[data-column="' + i + '"]:last'); - if ($h.length) { - $header.removeClass(remove); - $icon.removeClass(iconRmv); - if ($h[0].sortDisabled) { - // no sort arrows for disabled columns! - $icon.removeClass(themes.icons || ''); - } else { - hdr = themes.sortNone; - icon = themes.iconSortNone; - if ($h.hasClass(ts.css.sortAsc)) { - hdr = [themes.sortAsc, themes.active].join(' '); - icon = themes.iconSortAsc; - } else if ($h.hasClass(ts.css.sortDesc)) { - hdr = [themes.sortDesc, themes.active].join(' '); - icon = themes.iconSortDesc; + for (i = 0; i < c.columns; i++) { + $header = c.$headers + .add($(c.namespace + '_extra_headers')) + .not('.sorter-false') + .filter('[data-column="' + i + '"]'); + $icon = (ts.css.icon) ? $header.find('.' + ts.css.icon) : $(); + $h = $headers.not('.sorter-false').filter('[data-column="' + i + '"]:last'); + if ($h.length) { + $header.removeClass(remove); + $icon.removeClass(iconRmv); + if ($h[0].sortDisabled) { + // no sort arrows for disabled columns! + $icon.removeClass(themes.icons || ''); + } else { + hdr = themes.sortNone; + icon = themes.iconSortNone; + if ($h.hasClass(ts.css.sortAsc)) { + hdr = [ themes.sortAsc, themes.active ].join(' '); + icon = themes.iconSortAsc; + } else if ($h.hasClass(ts.css.sortDesc)) { + hdr = [ themes.sortDesc, themes.active ].join(' '); + icon = themes.iconSortDesc; + } + $header.addClass(hdr); + $icon.addClass(icon || ''); } - $header.addClass(hdr); - $icon.addClass(icon || ''); } } + if (c.debug) { + console.log('Applying ' + theme + ' theme' + ts.benchmark(time)); + } + }, + remove: function(table, c, wo, refreshing) { + if (!wo.uitheme_applied) { return; } + var $table = c.$table, + theme = c.appliedTheme || 'jui', + themes = ts.themes[ theme ] || ts.themes.jui, + $headers = $table.children('thead').children(), + remove = themes.sortNone + ' ' + themes.sortDesc + ' ' + themes.sortAsc, + iconRmv = themes.iconSortNone + ' ' + themes.iconSortDesc + ' ' + themes.iconSortAsc; + $table.removeClass('tablesorter-' + theme + ' ' + themes.table); + wo.uitheme_applied = false; + if (refreshing) { return; } + $table.find(ts.css.header).removeClass(themes.header); + $headers + .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') // remove hover + .removeClass(themes.hover + ' ' + remove + ' ' + themes.active) + .filter('.' + ts.css.filterRow) + .removeClass(themes.filterRow); + $headers.find('.' + ts.css.icon).removeClass(themes.icons + ' ' + iconRmv); } - if (c.debug) { - ts.benchmark("Applying " + theme + " theme", time); - } - }, - remove: function(table, c, wo, refreshing) { - if (!wo.uitheme_applied) { return; } - var $table = c.$table, - theme = c.appliedTheme || 'jui', - themes = ts.themes[ theme ] || ts.themes.jui, - $headers = $table.children('thead').children(), - remove = themes.sortNone + ' ' + themes.sortDesc + ' ' + themes.sortAsc, - iconRmv = themes.iconSortNone + ' ' + themes.iconSortDesc + ' ' + themes.iconSortAsc; - $table.removeClass('tablesorter-' + theme + ' ' + themes.table); - wo.uitheme_applied = false; - if (refreshing) { return; } - $table.find(ts.css.header).removeClass(themes.header); - $headers - .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') // remove hover - .removeClass(themes.hover + ' ' + remove + ' ' + themes.active) - .filter('.' + ts.css.filterRow) - .removeClass(themes.filterRow); - $headers.find('.' + ts.css.icon).removeClass(themes.icons + ' ' + iconRmv); - } -}); + }); })(jQuery); From 8e316bf86b34de614dc1b8f58b8c538b9ffe0090 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Wed, 29 Jul 2015 19:04:27 +0200 Subject: [PATCH 061/138] * updated tablesorter to latest version (2.22.5) --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 13 +++++++------ .../jquery-tablesorter/jquery.tablesorter.js | 6 +++--- .../jquery.tablesorter.widgets.js | 7 ++++--- .../jquery-tablesorter/parsers/parser-globalize.js | 1 + .../jquery-tablesorter/parsers/parser-network.js | 9 +++++---- .../jquery-tablesorter/widgets/widget-filter.js | 4 ++-- .../jquery-tablesorter/widgets/widget-math.js | 13 ++++++------- .../jquery-tablesorter/widgets/widget-output.js | 2 +- .../jquery-tablesorter/widgets/widget-sort2Hash.js | 5 ++--- .../jquery-tablesorter/widgets/widget-storage.js | 1 + 14 files changed, 39 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4319dad..e686b3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.17.4 (2015-07-29) + +* Upgrade tablesorter to v2.22.5 + #### v1.17.3 (2015-07-28) * Upgrade tablesorter to v2.22.4 diff --git a/README.md b/README.md index c611239..7aa9f8e 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.22.4 (7/28/2015), [documentation] +Current tablesorter version: 2.22.5 (7/28/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index cd0bd6d..6202d17 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.17.3' + VERSION = '1.17.4' end diff --git a/tablesorter b/tablesorter index b3dd4b7..3cd026e 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit b3dd4b7c7504451eb98d2a342314045534b07496 +Subproject commit 3cd026ee051b2653f260ffd076a100a0d7544331 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 37ba337..8696566 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 07-28-2015 (v2.22.4)*/ +/*! tablesorter (FORK) - updated 07-28-2015 (v2.22.5)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.22.4 *//* +/*! TableSorter (FORK) v2.22.5 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -35,7 +35,7 @@ * @contributor Rob Garrison - https://github.com/Mottie/tablesorter */ /*jshint browser:true, jquery:true, unused:false, expr: true */ -/*global console:false, alert:false, require:false, define:false, module:false */ +/*global console:false */ ;(function($){ 'use strict'; $.extend({ @@ -44,7 +44,7 @@ var ts = this; - ts.version = '2.22.4'; + ts.version = '2.22.5'; ts.parsers = []; ts.widgets = []; @@ -2250,6 +2250,7 @@ })(jQuery); /*! Widget: storage - updated 3/26/2015 (v2.21.3) */ +/*global JSON:false */ ;(function ($, window, document) { 'use strict'; @@ -2711,7 +2712,7 @@ types: { or : function( c, data, vars ) { if ( /\|/.test( data.iFilter ) || ts.filter.regex.orSplit.test( data.filter ) ) { - var indx, filterMatched, txt, query, regex, + var indx, filterMatched, query, regex, // duplicate data but split filter data2 = $.extend( {}, data ), index = data.index, @@ -2746,7 +2747,7 @@ // Look for an AND or && operator ( logical and ) and : function( c, data, vars ) { if ( ts.filter.regex.andTest.test( data.filter ) ) { - var indx, filterMatched, result, txt, query, regex, + var indx, filterMatched, result, query, regex, // duplicate data but split filter data2 = $.extend( {}, data ), index = data.index, diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index a2eda6d..af20963 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.22.4 *//* +/*! TableSorter (FORK) v2.22.5 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -17,7 +17,7 @@ * @contributor Rob Garrison - https://github.com/Mottie/tablesorter */ /*jshint browser:true, jquery:true, unused:false, expr: true */ -/*global console:false, alert:false, require:false, define:false, module:false */ +/*global console:false */ ;(function($){ 'use strict'; $.extend({ @@ -26,7 +26,7 @@ var ts = this; - ts.version = '2.22.4'; + ts.version = '2.22.5'; ts.parsers = []; ts.widgets = []; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 27e7752..29c2a1c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 07-28-2015 (v2.22.4)*/ +/*! tablesorter (FORK) - updated 07-28-2015 (v2.22.5)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -17,6 +17,7 @@ }(function($) { /*! Widget: storage - updated 3/26/2015 (v2.21.3) */ +/*global JSON:false */ ;(function ($, window, document) { 'use strict'; @@ -478,7 +479,7 @@ types: { or : function( c, data, vars ) { if ( /\|/.test( data.iFilter ) || ts.filter.regex.orSplit.test( data.filter ) ) { - var indx, filterMatched, txt, query, regex, + var indx, filterMatched, query, regex, // duplicate data but split filter data2 = $.extend( {}, data ), index = data.index, @@ -513,7 +514,7 @@ // Look for an AND or && operator ( logical and ) and : function( c, data, vars ) { if ( ts.filter.regex.andTest.test( data.filter ) ) { - var indx, filterMatched, result, txt, query, regex, + var indx, filterMatched, result, query, regex, // duplicate data but split filter data2 = $.extend( {}, data ), index = data.index, diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-globalize.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-globalize.js index 24a08bd..aa64fc7 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-globalize.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-globalize.js @@ -2,6 +2,7 @@ /* Extract localized data using jQuery's Globalize parsers; set Globalize.locale( 'xx' ) prior to initializing tablesorter! */ /*jshint jquery:true */ +/*global Globalize:false */ ;( function( $ ) { 'use strict'; diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js index 53927e2..a39de29 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js @@ -29,13 +29,14 @@ return ts.regex.ipv6Validate.test(s); }, format: function(address, table) { - // code modified from http://forrst.com/posts/JS_Expand_Abbreviated_IPv6_Addresses-1OR + // code modified from http://zurb.com/forrst/posts/JS_Expand_Abbreviated_IPv6_Addresses-1OR + // Saved to https://gist.github.com/Mottie/7018157 var i, t, sides, groups, groupsPresent, hex = table ? (typeof table === 'boolean' ? table : table && table.config.ipv6HexFormat || false) : false, fullAddress = '', expandedAddress = '', - validGroupCount = 8, - validGroupSize = 4; + validGroupCount = 8; + // validGroupSize = 4; <- removed while loop // remove any extra spaces address = address.replace(/\s*/g, ''); // look for embedded ipv4 @@ -112,7 +113,7 @@ */ ts.addParser({ id : 'MAC', - is : function( str ) { + is : function() { return false; }, format : function( str ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index d83d433..5ca182b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -105,7 +105,7 @@ types: { or : function( c, data, vars ) { if ( /\|/.test( data.iFilter ) || ts.filter.regex.orSplit.test( data.filter ) ) { - var indx, filterMatched, txt, query, regex, + var indx, filterMatched, query, regex, // duplicate data but split filter data2 = $.extend( {}, data ), index = data.index, @@ -140,7 +140,7 @@ // Look for an AND or && operator ( logical and ) and : function( c, data, vars ) { if ( ts.filter.regex.andTest.test( data.filter ) ) { - var indx, filterMatched, result, txt, query, regex, + var indx, filterMatched, result, query, regex, // duplicate data but split filter data2 = $.extend( {}, data ), index = data.index, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index 180188b..b531faf 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -40,8 +40,7 @@ // get all of the row numerical values in an arry getRow : function( c, $el ) { - var $t, txt, - wo = c.widgetOptions, + var wo = c.widgetOptions, arry = [], $row = $el.closest( 'tr' ), $cells = $row.children().not( '[' + wo.math_dataAttrib + '=ignore]' ); @@ -58,7 +57,7 @@ // get all of the column numerical values in an arry getColumn : function( c, $el, type ) { - var index, txt, $t, len, $mathRows, mathAbove, + var index, $t, len, $mathRows, mathAbove, arry = [], wo = c.widgetOptions, filtered = wo.filter_filteredRow || 'filtered', @@ -104,7 +103,7 @@ // get all of the column numerical values in an arry getAll : function( c ) { - var txt, $t, col, $row, rowIndex, rowLen, $cells, cellIndex, cellLen, + var $t, col, $row, rowIndex, rowLen, $cells, cellIndex, cellLen, arry = [], wo = c.widgetOptions, filtered = wo.filter_filteredRow || 'filtered', @@ -128,7 +127,7 @@ return arry; }, - recalculate : function(table, c, wo, init) { + recalculate : function(c, wo, init) { if ( c && ( !wo.math_isUpdating || init ) ) { // add data-column attributes to all table cells @@ -315,7 +314,7 @@ str += integer.charAt( index ); // ie6 only support charAt for sz. // -posSeparator so that won't trail separator on full length /*jshint -W018 */ - if ( !( ( index - offset + 1 ) % posSeparator ) && index < l - posSeparator ) { + if ( !( ( index - offset + 1 ) % posSeparator ) && index < len - posSeparator ) { str += group; } } @@ -456,7 +455,7 @@ // redo data-column indexes on update ts.computeColumnIndex( c.$table.children('tbody').children() ); } - math.recalculate( table, c, wo, init ); + math.recalculate( c, wo, init ); } }) .on( update + '.tsmath', function() { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index 7fcd324..cbe107b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -5,7 +5,7 @@ * Download-File-JS: https://github.com/PixelsCommander/Download-File-JS (http://www.apache.org/licenses/LICENSE-2.0) */ /*jshint browser:true, jquery:true, unused:false */ -/*global jQuery: false */ +/*global jQuery:false, alert:false */ ;(function($){ 'use strict'; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js index 8a35f63..86db9a1 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js @@ -50,7 +50,7 @@ // just see if column contains a number if ( isNaN( temp ) || temp > c.columns ) { regex = new RegExp( '(' + column + ')', 'i' ); - column = c.$headers.filter( function( index, cell ) { + column = c.$headers.filter( function( index ) { return regex.test( c.$headers[ index ].textContent || '' ); }).attr( 'data-column' ); } @@ -94,8 +94,7 @@ return sort.join( wo.sort2Hash_separator ); }, setHash : function( c, wo ) { - var arry = [], - sort = s2h.processSort( c, wo ); + var sort = s2h.processSort( c, wo ); if ( sort.length ) { // remove old hash s2h.getSort( c, wo, true ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js index bfb9c74..6c482a3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js @@ -1,4 +1,5 @@ /*! Widget: storage - updated 3/26/2015 (v2.21.3) */ +/*global JSON:false */ ;(function ($, window, document) { 'use strict'; From 827453eb1b524a7f743597b95415d5caed961c17 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Tue, 18 Aug 2015 21:47:31 +0200 Subject: [PATCH 062/138] * updated tablesorter to latest version (2.23.0) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 130 +++-- .../jquery.tablesorter.combined.js | 534 ++++++++++-------- .../jquery-tablesorter/jquery.tablesorter.js | 527 +++++++++-------- .../jquery.tablesorter.widgets.js | 7 +- .../parsers/parser-input-select.js | 4 +- .../widgets/widget-columnSelector.js | 87 ++- .../widgets/widget-editable.js | 75 +-- .../widgets/widget-filter.js | 5 +- .../widgets/widget-pager.js | 122 ++-- 13 files changed, 894 insertions(+), 607 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e686b3d..fdf916c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.18.0 (2015-08-18) + +* Upgrade tablesorter to v2.23.0 + #### v1.17.4 (2015-07-29) * Upgrade tablesorter to v2.22.5 diff --git a/README.md b/README.md index 7aa9f8e..a4a7f65 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.22.5 (7/28/2015), [documentation] +Current tablesorter version: 2.23.0 (8/17/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 6202d17..24c0593 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.17.4' + VERSION = '1.18.0' end diff --git a/tablesorter b/tablesorter index 3cd026e..c602ca7 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 3cd026ee051b2653f260ffd076a100a0d7544331 +Subproject commit c602ca7f01ba7b623580eac7972aa1f523f0dae9 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 99badd5..86da091 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin - * updated 7/28/2015 (v2.22.4) + * updated 8/17/2015 (v2.23.0) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -25,6 +25,11 @@ // modify the url after all processing has been applied customAjaxUrl: function(table, url) { return url; }, + // ajax error callback from $.tablesorter.showError function + // ajaxError: function( config, xhr, exception ){ return exception; }; + // returning false will abort the error message + ajaxError: null, + // modify the $.ajax object to allow complete control over your ajax requests ajaxObject: { dataType: 'json' @@ -114,7 +119,7 @@ }; - var pagerEvents = 'filterInit filterStart filterEnd sortEnd disable enable destroy updateComplete ' + + var pagerEvents = 'filterInit filterStart filterEnd sortEnd disablePager enablePager destroyPager updateComplete ' + 'pageSize pageSet pageAndSize pagerUpdate refreshComplete ', $this = this, @@ -139,7 +144,7 @@ c = table.config, hasFilters = c.$table.hasClass('hasFilters'); if (hasFilters && !p.ajaxUrl) { - if ($.isEmptyObject(c.cache)) { + if (ts.isEmptyObject(c.cache)) { // delayInit: true so nothing is in the cache p.filteredRows = p.totalRows = c.$tbodies.eq(0).children('tr').not( p.countChildRows ? '' : '.' + c.cssChildRow ).length; } else { @@ -386,20 +391,13 @@ hl = $table.find('thead th').length; // Clean up any previous error. - ts.showError(table); + ts.showError( table ); if ( exception ) { if (c.debug) { console.error('Pager: >> Ajax Error', xhr, exception); } - ts.showError(table, - xhr.status === 0 ? 'Not connected, verify Network' : - xhr.status === 404 ? 'Requested page not found [404]' : - xhr.status === 500 ? 'Internal Server Error [500]' : - exception === 'parsererror' ? 'Requested JSON parse failed' : - exception === 'timeout' ? 'Time out error' : - exception === 'abort' ? 'Ajax Request aborted' : - 'Uncaught error: ' + xhr.statusText + ' [' + xhr.status + ']' ); + ts.showError( table, xhr, exception ); c.$tbodies.eq(0).children('tr').detach(); p.totalRows = 0; } else { @@ -708,7 +706,7 @@ var pg, c = table.config, $t = $(table), l = p.last; - if ( pageMoved !== false && p.initialized && $.isEmptyObject(c.cache)) { + if ( pageMoved !== false && p.initialized && ts.isEmptyObject(c.cache)) { return updateCache(table); } // abort page move if the table has filters and has not been initialized @@ -801,16 +799,23 @@ }, destroyPager = function(table, p) { + var c = table.config, + namespace = c.namespace + 'pager', + ctrls = [ p.cssFirst, p.cssPrev, p.cssNext, p.cssLast, p.cssGoto, p.cssPageSize ].join( ',' ); showAllRows(table, p); - p.$container.hide(); // hide pager - var c = table.config; + p.$container + // hide pager controls + .hide() + // unbind + .find( ctrls ) + .unbind( namespace ); c.appender = null; // remove pager appender function - p.initialized = false; - delete c.rowsCopy; - $(table).unbind( pagerEvents.split(' ').join(c.namespace + 'pager ').replace(/\s+/g, ' ') ); + c.$table.unbind( namespace ); if (ts.storage) { ts.storage(table, p.storageKey, ''); } + delete c.pager; + delete c.rowsCopy; }, enablePager = function(table, p, triggered) { @@ -888,6 +893,7 @@ p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.slice(1) + '|' + c.cssChildRow + ')'); $t + // .unbind( namespace ) adding in jQuery 1.4.3 ( I think ) .unbind( pagerEvents.split(' ').join(namespace + ' ').replace(/\s+/g, ' ') ) .bind('filterInit filterStart '.split(' ').join(namespace + ' '), function(e, filters) { p.currentFilters = $.isArray(filters) ? filters : c.$table.data('lastSearch'); @@ -909,15 +915,15 @@ c.$table.trigger('applyWidgets'); } }) - .bind('disable' + namespace, function(e){ + .bind('disablePager' + namespace, function(e){ e.stopPropagation(); showAllRows(table, p); }) - .bind('enable' + namespace, function(e){ + .bind('enablePager' + namespace, function(e){ e.stopPropagation(); enablePager(table, p, true); }) - .bind('destroy' + namespace, function(e){ + .bind('destroyPager' + namespace, function(e){ e.stopPropagation(); destroyPager(table, p); }) @@ -1061,31 +1067,73 @@ }() }); // see #486 - ts.showError = function(table, message) { - var index, $row, c, errorRow, + ts.showError = function( table, xhr, exception ) { + var $row, $table = $( table ), - len = $table.length; - for ( index = 0; index < len; index++ ) { - c = $table[ index ].config; - if ( c ) { - errorRow = c.pager && c.pager.cssErrorRow || c.widgetOptions.pager_css && c.widgetOptions.pager_css.errorRow || 'tablesorter-errorRow'; - if ( typeof message === 'undefined' ) { - c.$table.find('thead').find(c.selectorRemove).remove(); + c = $table[0].config, + wo = c && c.widgetOptions, + errorRow = c.pager && c.pager.cssErrorRow || wo.pager_css && wo.pager_css.errorRow || 'tablesorter-errorRow', + typ = typeof xhr, + valid = true, + message = '', + removeRow = function(){ + c.$table.find( 'thead' ).find( '.' + errorRow ).remove(); + }; + + if ( !$table.length ) { + console.error('tablesorter showError: no table parameter passed'); + return; + } + + if ( typ !== 'string' ) { + // ajaxError callback for plugin or widget - see #992 + if ( typeof c.pager.ajaxError === 'function' ) { + valid = c.pager.ajaxError( c, xhr, exception ); + if ( valid === false ) { + return removeRow(); } else { - $row = ( /tr\>/.test(message) ? $(message) : $('<tr><td colspan="' + c.columns + '">' + message + '</td></tr>') ) - .click(function(){ - $(this).remove(); - }) - // add error row to thead instead of tbody, or clicking on the header will result in a parser error - .appendTo( c.$table.find('thead:first') ) - .addClass( errorRow + ' ' + c.selectorRemove.slice(1) ) - .attr({ - role : 'alert', - 'aria-live' : 'assertive' - }); + message = valid; } + } else if ( typeof wo.pager_ajaxError === 'function' ) { + valid = wo.pager_ajaxError( c, xhr, exception ); + if ( valid === false ) { + return removeRow(); + } else { + message = valid; + } + } else { + message = + xhr.status === 0 ? 'Not connected, verify Network' : + xhr.status === 404 ? 'Requested page not found [404]' : + xhr.status === 500 ? 'Internal Server Error [500]' : + exception === 'parsererror' ? 'Requested JSON parse failed' : + exception === 'timeout' ? 'Time out error' : + exception === 'abort' ? 'Ajax Request aborted' : + 'Uncaught error: ' + xhr.statusText + ' [' + xhr.status + ']'; } + } else if ( typ !== 'undefined' ) { + // keep backward compatibility (external usage just passes a message string) + message = xhr; + } + + if ( message === '' ) { + // remove all error rows + return removeRow(); } + + // allow message to include HTML (must include entire row!) + $row = ( /tr\>/.test(message) ? $(message) : $('<tr><td colspan="' + c.columns + '">' + message + '</td></tr>') ) + .click( function() { + $( this ).remove(); + }) + // add error row to thead instead of tbody, or clicking on the header will result in a parser error + .appendTo( c.$table.find( 'thead:first' ) ) + .addClass( errorRow + ' ' + c.selectorRemove.slice(1) ) + .attr({ + role : 'alert', + 'aria-live' : 'assertive' + }); + }; // extend plugin scope diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 8696566..0e03875 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 07-28-2015 (v2.22.5)*/ +/*! tablesorter (FORK) - updated 08-17-2015 (v2.23.0)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.22.5 *//* +/*! TableSorter (FORK) v2.23.0 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -44,7 +44,7 @@ var ts = this; - ts.version = '2.22.5'; + ts.version = '2.23.0'; ts.parsers = []; ts.widgets = []; @@ -173,13 +173,13 @@ ts.instanceMethods = {}; // $.isEmptyObject from jQuery v1.4 - function isEmptyObject(obj) { + ts.isEmptyObject = function( obj ) { /*jshint forin: false */ - for (var name in obj) { + for ( var name in obj ) { return false; } return true; - } + }; ts.getElementText = function(c, node, cellIndex) { if (!node) { return ''; } @@ -322,7 +322,7 @@ j += (list.parsers.length) ? len : 1; } if ( c.debug ) { - if ( !isEmptyObject( debug ) ) { + if ( !ts.isEmptyObject( debug ) ) { console[ console.table ? 'table' : 'log' ]( debug ); } else { console.warn( ' No parsers detected!' ); @@ -335,7 +335,7 @@ } /* utils */ - function buildCache(table, $tbodies) { + function buildCache(table, callback, $tbodies) { var cc, t, v, i, j, k, $tb, $row, cols, cacheTime, totalRows, rowData, prevRowData, colMax, c = table.config, @@ -428,54 +428,8 @@ if ( c.debug ) { console.log( 'Building cache for ' + totalRows + ' rows' + ts.benchmark( cacheTime ) ); } - } - - // init flag (true) used by pager plugin to prevent widget application - function appendToTable(table, init) { - var c = table.config, - wo = c.widgetOptions, - $tbodies = c.$tbodies, - rows = [], - cc = c.cache, - n, totalRows, $bk, $tb, - i, k, appendTime; - // empty table - fixes #206/#346 - if (isEmptyObject(cc)) { - // run pager appender in case the table was just emptied - return c.appender ? c.appender(table, rows) : - table.isUpdating ? c.$table.trigger('updateComplete', table) : ''; // Fixes #532 - } - if (c.debug) { - appendTime = new Date(); - } - for (k = 0; k < $tbodies.length; k++) { - $bk = $tbodies.eq(k); - if ($bk.length) { - // get tbody - $tb = ts.processTbody(table, $bk, true); - n = cc[k].normalized; - totalRows = n.length; - for (i = 0; i < totalRows; i++) { - rows.push(n[i][c.columns].$row); - // removeRows used by the pager plugin; don't render if using ajax - fixes #411 - if (!c.appender || (c.pager && (!c.pager.removeRows || !wo.pager_removeRows) && !c.pager.ajax)) { - $tb.append(n[i][c.columns].$row); - } - } - // restore tbody - ts.processTbody(table, $tb, false); - } - } - if (c.appender) { - c.appender(table, rows); - } - if (c.debug) { - console.log( 'Rebuilt table' + ts.benchmark(appendTime) ); - } - // apply table widgets; but not before ajax completes - if (!init && !c.appender) { ts.applyWidget(table); } - if (table.isUpdating) { - c.$table.trigger('updateComplete', table); + if ( $.isFunction( callback ) ) { + callback( table ); } } @@ -484,9 +438,8 @@ return (/^d/i.test(v) || v === 1); } - function buildHeaders(table) { - var ch, $t, h, i, t, lock, time, indx, - c = table.config; + function buildHeaders( c ) { + var ch, $t, h, i, t, lock, time, indx; c.headerList = []; c.headerContent = []; if (c.debug) { @@ -497,12 +450,12 @@ // add icon if cssIcon option exists i = c.cssIcon ? '<i class="' + ( c.cssIcon === ts.css.icon ? ts.css.icon : c.cssIcon + ' ' + ts.css.icon ) + '"></i>' : ''; // redefine c.$headers here in case of an updateAll that replaces or adds an entire header cell - see #683 - c.$headers = $( $.map( $(table).find(c.selectorHeaders), function(elem, index) { + c.$headers = $( $.map( c.$table.find(c.selectorHeaders), function(elem, index) { $t = $(elem); // ignore cell (don't add it to c.$headers) if row has ignoreRow class if ($t.parent().hasClass(c.cssIgnoreRow)) { return; } // make sure to get header cell & not column indexed cell - ch = ts.getColumnData( table, c.headers, index, true ); + ch = ts.getColumnData( c.table, c.headers, index, true ); // save original header content c.headerContent[index] = $t.html(); // if headerTemplate is empty, don't reformat the header cell @@ -544,12 +497,12 @@ // .last() added in jQuery 1.4; use .filter(':last') to maintain compatibility with jQuery v1.2.6 c.$headerIndexed[indx] = $t.not('.sorter-false').length ? $t.not('.sorter-false').filter(':last') : $t.filter(':last'); } - $(table).find(c.selectorHeaders).attr({ + c.$table.find(c.selectorHeaders).attr({ scope: 'col', role : 'columnheader' }); // enable/disable sorting - updateHeader(table); + updateHeader(c.table); if (c.debug) { console.log( 'Built headers:' + ts.benchmark( time ) ); console.log( c.$headers ); @@ -806,7 +759,7 @@ // set css for headers setHeadersCss(table); multisort(table); - appendToTable(table); + ts.appendCache( c ); $table.trigger('sortEnd', table); }, 1); } @@ -821,7 +774,7 @@ sortList = c.sortList, l = sortList.length, bl = c.$tbodies.length; - if (c.serverSideSorting || isEmptyObject(c.cache)) { // empty table - fixes #206/#346 + if (c.serverSideSorting || ts.isEmptyObject(c.cache)) { // empty table - fixes #206/#346 return; } if (c.debug) { sortTime = new Date(); } @@ -912,188 +865,84 @@ } } - function bindMethods(table){ + function bindMethods( table ){ var c = table.config, $table = c.$table, - events = ('sortReset update updateRows updateCell updateAll addRows updateComplete sorton appendCache ' + - 'updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave ').split(' ') - .join(c.namespace + ' '); + events = ( 'sortReset update updateRows updateAll updateHeaders addRows updateCell updateComplete ' + + 'sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup ' + + 'mouseleave ' ).split( ' ' ) + .join( c.namespace + ' ' ); // apply easy methods that trigger bound events $table - .unbind( events.replace(/\s+/g, ' ') ) - .bind('sortReset' + c.namespace, function(e, callback){ + .unbind( events.replace( /\s+/g, ' ' ) ) + .bind( 'sortReset' + c.namespace, function( e, callback ) { e.stopPropagation(); - c.sortList = []; - setHeadersCss(table); - multisort(table); - appendToTable(table); - if ($.isFunction(callback)) { - callback(table); - } + // using this.config to ensure functions are getting a non-cached version of the config + ts.sortReset( this.config, callback ); }) - .bind('updateAll' + c.namespace, function(e, resort, callback){ + .bind( 'updateAll' + c.namespace, function( e, resort, callback ) { e.stopPropagation(); - table.isUpdating = true; - ts.refreshWidgets(table, true, true); - buildHeaders(table); - ts.bindEvents(table, c.$headers, true); - bindMethods(table); - commonUpdate(table, resort, callback); + ts.updateAll( this.config, resort, callback ); }) - .bind('update' + c.namespace + ' updateRows' + c.namespace, function(e, resort, callback) { + .bind( 'update' + c.namespace + ' updateRows' + c.namespace, function( e, resort, callback ) { e.stopPropagation(); - table.isUpdating = true; - // update sorting (if enabled/disabled) - updateHeader(table); - commonUpdate(table, resort, callback); + ts.update( this.config, resort, callback ); }) - .bind('updateCell' + c.namespace, function(e, cell, resort, callback) { + .bind( 'updateHeaders' + c.namespace, function( e, callback ) { e.stopPropagation(); - table.isUpdating = true; - $table.find(c.selectorRemove).remove(); - // get position from the dom - var t, row, icell, cache, - $tb = c.$tbodies, - $cell = $(cell), - // update cache - format: function(s, table, cell, cellIndex) - // no closest in jQuery v1.2.6 - tbdy = $tb.index( $(cell).closest('tbody') ),$row = $(cell).closest('tr'); - tbdy = $tb.index( $.fn.closest ? $cell.closest('tbody') : $cell.parents('tbody').filter(':first') ), - tbcache = c.cache[ tbdy ], - $row = $.fn.closest ? $cell.closest('tr') : $cell.parents('tr').filter(':first'); - cell = $cell[0]; // in case cell is a jQuery object - // tbody may not exist if update is initialized while tbody is removed for processing - if ($tb.length && tbdy >= 0) { - row = $tb.eq( tbdy ).find( 'tr' ).index( $row ); - cache = tbcache.normalized[ row ]; - icell = $cell.index(); - t = ts.getParsedText( c, cell, icell ); - cache[ icell ] = t; - cache[ c.columns ].$row = $row; - if ( (c.parsers[icell].type || '').toLowerCase() === 'numeric' ) { - // update column max value (ignore sign) - tbcache.colMax[icell] = Math.max(Math.abs(t) || 0, tbcache.colMax[icell] || 0); - } - t = resort !== 'undefined' ? resort : c.resort; - if (t !== false) { - // widgets will be reapplied - checkResort(c, t, callback); - } else { - // don't reapply widgets is resort is false, just in case it causes - // problems with element focus - if ($.isFunction(callback)) { - callback(table); - } - c.$table.trigger('updateComplete', c.table); - } - } + ts.updateHeaders( this.config, callback ); }) - .bind('addRows' + c.namespace, function(e, $row, resort, callback) { + .bind( 'updateCell' + c.namespace, function(e, cell, resort, callback ) { e.stopPropagation(); - table.isUpdating = true; - if (isEmptyObject(c.cache)) { - // empty table, do an update instead - fixes #450 - updateHeader(table); - commonUpdate(table, resort, callback); - } else { - $row = $($row).attr('role', 'row'); // make sure we're using a jQuery object - var i, j, l, rowData, cells, - rows = $row.filter('tr').length, - tbdy = c.$tbodies.index( $row.parents('tbody').filter(':first') ); - // fixes adding rows to an empty table - see issue #179 - if (!(c.parsers && c.parsers.length)) { - buildParserCache(c); - } - // add each row - for (i = 0; i < rows; i++) { - l = $row[i].cells.length; - cells = []; - rowData = { - child: [], - $row : $row.eq(i), - order: c.cache[tbdy].normalized.length - }; - // add each cell - for (j = 0; j < l; j++) { - cells[j] = ts.getParsedText( c, $row[i].cells[j], j ); - if ((c.parsers[j].type || '').toLowerCase() === 'numeric') { - // update column max value (ignore sign) - c.cache[tbdy].colMax[j] = Math.max(Math.abs(cells[j]) || 0, c.cache[tbdy].colMax[j] || 0); - } - } - // add the row data to the end - cells.push(rowData); - // update cache - c.cache[tbdy].normalized.push(cells); - } - // resort using current settings - checkResort(c, resort, callback); - } + ts.updateCell( this.config, cell, resort, callback ); }) - .bind('updateComplete' + c.namespace, function(){ + .bind( 'addRows' + c.namespace, function(e, $row, resort, callback) { + e.stopPropagation(); + ts.addRows( this.config, $row, resort, callback ); + }) + .bind( 'updateComplete' + c.namespace, function() { table.isUpdating = false; }) - .bind('sorton' + c.namespace, function(e, list, callback, init) { - var c = table.config; + .bind( 'sorton' + c.namespace, function( e, list, callback, init ) { e.stopPropagation(); - $table.trigger('sortStart', this); - // update header count index - updateHeaderSortCount(table, list); - // set css for headers - setHeadersCss(table); - // fixes #346 - if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); } - $table.trigger('sortBegin', this); - // sort the table and append it to the dom - multisort(table); - appendToTable(table, init); - $table.trigger('sortEnd', this); - ts.applyWidget(table); - if ($.isFunction(callback)) { - callback(table); - } + ts.sortOn( this.config, list, callback, init ); }) - .bind('appendCache' + c.namespace, function(e, callback, init) { + .bind( 'appendCache' + c.namespace, function( e, callback, init ) { e.stopPropagation(); - appendToTable(table, init); - if ($.isFunction(callback)) { - callback(table); + ts.appendCache( this.config, init ); + if ( $.isFunction( callback ) ) { + callback( table ); } }) // $tbodies variable is used by the tbody sorting widget - .bind('updateCache' + c.namespace, function(e, callback, $tbodies){ - // rebuild parsers - if (!(c.parsers && c.parsers.length)) { - buildParserCache(c, $tbodies); - } - // rebuild the cache map - buildCache(table, $tbodies); - if ($.isFunction(callback)) { - callback(table); - } + .bind( 'updateCache' + c.namespace, function( e, callback, $tbodies ){ + e.stopPropagation(); + ts.updateCache( this.config, callback, $tbodies ); }) - .bind('applyWidgetId' + c.namespace, function(e, id) { + .bind( 'applyWidgetId' + c.namespace, function( e, id ) { e.stopPropagation(); - ts.getWidgetById(id).format(table, c, c.widgetOptions); + ts.getWidgetById( id ).format( table, this.config, this.config.widgetOptions ); }) - .bind('applyWidgets' + c.namespace, function(e, init) { + .bind( 'applyWidgets' + c.namespace, function( e, init ) { e.stopPropagation(); // apply widgets - ts.applyWidget(table, init); + ts.applyWidget( table, init ); }) - .bind('refreshWidgets' + c.namespace, function(e, all, dontapply){ + .bind( 'refreshWidgets' + c.namespace, function( e, all, dontapply ) { e.stopPropagation(); - ts.refreshWidgets(table, all, dontapply); + ts.refreshWidgets( table, all, dontapply ); }) - .bind('destroy' + c.namespace, function(e, c, cb){ + .bind( 'destroy' + c.namespace, function( e, removeClasses, callback ) { e.stopPropagation(); - ts.destroy(table, c, cb); + ts.destroy( table, removeClasses, callback ); }) - .bind('resetToLoadState' + c.namespace, function(){ + .bind( 'resetToLoadState' + c.namespace, function( e ) { + e.stopPropagation(); // remove all widgets - ts.removeWidget(table, true, false); + ts.removeWidget( table, true, false ); // restore original settings; this clears out current settings, but does not clear // values saved to storage. - c = $.extend(true, ts.defaults, c.originalSettings); + c = $.extend( true, ts.defaults, c.originalSettings ); table.hasInitialized = false; // setup the entire table again ts.setup( table, c ); @@ -1189,7 +1038,7 @@ // change textExtraction via data-attribute c.textExtraction = c.$table.attr('data-text-extraction') || c.textExtraction || 'basic'; // build headers - buildHeaders(table); + buildHeaders( c ); // fixate columns if the users supplies the fixedWidth option // do this after theme has been applied ts.fixColumnWidth(table); @@ -1409,7 +1258,7 @@ .find(c.selectorSort).add( $headers.filter(c.selectorSort) ) .unbind(t) .bind(t, function(e, external) { - var cell, temp, + var $cell, cell, temp, $target = $(e.target), // wrap event type in spaces, so the match doesn't trigger on inner words type = ' ' + e.type + ' '; @@ -1441,12 +1290,13 @@ $target.parents('button').length > 0 ) { return !c.cancelSelection; } - if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); } + if (c.delayInit && ts.isEmptyObject(c.cache)) { buildCache(table); } // jQuery v1.2.6 doesn't have closest() - cell = $.fn.closest ? $(this).closest('th, td')[0] : /TH|TD/.test(this.nodeName) ? this : $(this).parents('th, td')[0]; + $cell = $.fn.closest ? $(this).closest('th, td') : /TH|TD/.test(this.nodeName) ? $(this) : $(this).parents('th, td'); // reference original table headers and find the same cell - cell = c.$headers[ $headers.index( cell ) ]; - if (!cell.sortDisabled) { + // don't use $headers or IE8 throws an error - see #987 + cell = c.$headers[ $cell.prevAll().length ]; + if (cell && !cell.sortDisabled) { initSort(table, cell, e); } }); @@ -1462,6 +1312,219 @@ } }; + ts.sortReset = function( c, callback ) { + var table = c.table; + c.sortList = []; + setHeadersCss( table ); + multisort( table ); + ts.appendCache( c ); + if ( $.isFunction( callback ) ) { + callback( table ); + } + }; + + ts.updateAll = function( c, resort, callback ) { + var table = c.table; + table.isUpdating = true; + ts.refreshWidgets( table, true, true ); + buildHeaders( c ); + ts.bindEvents( table, c.$headers, true ); + bindMethods( table); + commonUpdate( table, resort, callback ); + }; + + ts.update = function( c, resort, callback ) { + var table = c.table; + table.isUpdating = true; + // update sorting (if enabled/disabled) + updateHeader( table ); + commonUpdate( table, resort, callback ); + }; + + // simple header update - see #989 + ts.updateHeaders = function( c, callback ) { + c.table.isUpdating = true; + buildHeaders( c ); + ts.bindEvents( c.table, c.$headers, true ); + resortComplete( c, callback ); + }; + + ts.updateCell = function( c, cell, resort, callback ) { + c.table.isUpdating = true; + c.$table.find( c.selectorRemove ).remove(); + // get position from the dom + var t, row, icell, cache, + table = c.table, + $tb = c.$tbodies, + $cell = $(cell), + // update cache - format: function(s, table, cell, cellIndex) + // no closest in jQuery v1.2.6 - tbdy = $tb.index( $(cell).closest('tbody') ),$row = $(cell).closest('tr'); + tbdy = $tb.index( $.fn.closest ? $cell.closest( 'tbody' ) : $cell.parents( 'tbody' ).filter( ':first' ) ), + tbcache = c.cache[ tbdy ], + $row = $.fn.closest ? $cell.closest( 'tr' ) : $cell.parents( 'tr' ).filter( ':first' ); + cell = $cell[ 0 ]; // in case cell is a jQuery object + // tbody may not exist if update is initialized while tbody is removed for processing + if ( $tb.length && tbdy >= 0 ) { + row = $tb.eq( tbdy ).find( 'tr' ).index( $row ); + cache = tbcache.normalized[ row ]; + icell = $cell.index(); + t = ts.getParsedText( c, cell, icell ); + cache[ icell ] = t; + cache[ c.columns ].$row = $row; + if ( ( c.parsers[ icell ].type || '' ).toLowerCase() === 'numeric' ) { + // update column max value (ignore sign) + tbcache.colMax[ icell ] = Math.max( Math.abs( t ) || 0, tbcache.colMax[ icell ] || 0 ); + } + t = resort !== 'undefined' ? resort : c.resort; + if ( t !== false ) { + // widgets will be reapplied + checkResort( c, t, callback ); + } else { + // don't reapply widgets is resort is false, just in case it causes + // problems with element focus + resortComplete( c, callback ); + } + } + }; + + ts.addRows = function( c, $row, resort, callback ) { + var i, j, l, rowData, cells, rows, tbdy, + // allow passing a row string if only one non-info tbody exists in the table + valid = typeof $row === 'string' && c.$tbodies.length === 1 && /<tr/.test( $row || '' ), + table = c.table; + if ( valid ) { + $row = $( $row ); + c.$tbodies.append( $row ); + } else if ( !$row || + // row is a jQuery object? + !( $row instanceof jQuery ) || + // row contained in the table? + ( $.fn.closest ? $row.closest( 'table' )[ 0 ] : $row.parents( 'table' )[ 0 ] ) !== c.table ) { + if ( c.debug ) { + console.error( 'addRows method requires (1) a jQuery selector reference to rows that have already ' + + 'been added to the table, or (2) row HTML string to be added to a table with only one tbody' ); + } + return false; + } + table.isUpdating = true; + if ( ts.isEmptyObject( c.cache ) ) { + // empty table, do an update instead - fixes #450 + updateHeader( table ); + commonUpdate( table, resort, callback ); + } else { + rows = $row.filter( 'tr' ).attr( 'role', 'row' ).length; + tbdy = c.$tbodies.index( $row.parents( 'tbody' ).filter( ':first' ) ); + // fixes adding rows to an empty table - see issue #179 + if ( !( c.parsers && c.parsers.length ) ) { + buildParserCache( c ); + } + // add each row + for ( i = 0; i < rows; i++ ) { + l = $row[ i ].cells.length; + cells = []; + rowData = { + child: [], + $row : $row.eq( i ), + order: c.cache[ tbdy ].normalized.length + }; + // add each cell + for ( j = 0; j < l; j++ ) { + cells[ j ] = ts.getParsedText( c, $row[ i ].cells[ j ], j ); + if ( ( c.parsers[ j ].type || '' ).toLowerCase() === 'numeric' ) { + // update column max value (ignore sign) + c.cache[ tbdy ].colMax[ j ] = Math.max( Math.abs( cells[ j ] ) || 0, c.cache[ tbdy ].colMax[ j ] || 0 ); + } + } + // add the row data to the end + cells.push( rowData ); + // update cache + c.cache[ tbdy ].normalized.push( cells ); + } + // resort using current settings + checkResort( c, resort, callback ); + } + }; + + ts.updateCache = function( c, callback, $tbodies ) { + // rebuild parsers + if ( !( c.parsers && c.parsers.length ) ) { + buildParserCache( c, $tbodies ); + } + // rebuild the cache map + buildCache( c.table, callback, $tbodies ); + }; + + // init flag (true) used by pager plugin to prevent widget application + // renamed from appendToTable + ts.appendCache = function( c, init ) { + var n, totalRows, $bk, $tb, i, k, appendTime, + table = c.table, + wo = c.widgetOptions, + $tbodies = c.$tbodies, + rows = [], + cc = c.cache; + // empty table - fixes #206/#346 + if ( ts.isEmptyObject( cc ) ) { + // run pager appender in case the table was just emptied + return c.appender ? c.appender( table, rows ) : + table.isUpdating ? c.$table.trigger( 'updateComplete', table ) : ''; // Fixes #532 + } + if ( c.debug ) { + appendTime = new Date(); + } + for ( k = 0; k < $tbodies.length; k++ ) { + $bk = $tbodies.eq( k ); + if ( $bk.length ) { + // get tbody + $tb = ts.processTbody( table, $bk, true ); + n = cc[ k ].normalized; + totalRows = n.length; + for ( i = 0; i < totalRows; i++ ) { + rows.push( n[ i ][ c.columns ].$row ); + // removeRows used by the pager plugin; don't render if using ajax - fixes #411 + if ( !c.appender || ( c.pager && ( !c.pager.removeRows || !wo.pager_removeRows ) && !c.pager.ajax ) ) { + $tb.append( n[ i ][ c.columns ].$row ); + } + } + // restore tbody + ts.processTbody( table, $tb, false ); + } + } + if ( c.appender ) { + c.appender( table, rows ); + } + if ( c.debug ) { + console.log( 'Rebuilt table' + ts.benchmark( appendTime ) ); + } + // apply table widgets; but not before ajax completes + if ( !init && !c.appender ) { ts.applyWidget( table ); } + if ( table.isUpdating ) { + c.$table.trigger( 'updateComplete', table ); + } + }; + + ts.sortOn = function( c, list, callback, init ) { + var table = c.table; + c.$table.trigger( 'sortStart', table ); + // update header count index + updateHeaderSortCount( table, list ); + // set css for headers + setHeadersCss( table ); + // fixes #346 + if ( c.delayInit && ts.isEmptyObject( c.cache ) ) { + buildCache( table ); + } + c.$table.trigger( 'sortBegin', table ); + // sort the table and append it to the dom + multisort( table ); + ts.appendCache( c, init ); + c.$table.trigger( 'sortEnd', table ); + ts.applyWidget( table ); + if ( $.isFunction( callback ) ) { + callback( table ); + } + }; + // restore headers ts.restoreHeaders = function(table){ var index, $cell, @@ -1500,8 +1563,9 @@ // remove widget added rows, just in case $h.find('tr').not($r).remove(); // disable tablesorter - events = 'sortReset update updateAll updateRows updateCell addRows updateComplete sorton appendCache updateCache ' + - 'applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress sortBegin sortEnd resetToLoadState '.split(' ') + events = 'sortReset update updateRows updateAll updateHeaders updateCell addRows updateComplete sorton ' + + 'appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress ' + + 'sortBegin sortEnd resetToLoadState '.split(' ') .join(c.namespace + ' '); $t .removeData('tablesorter') @@ -1923,7 +1987,7 @@ allColumns = column === 'all', data = { raw : [], parsed: [], $cell: [] }, c = table.config; - if ( isEmptyObject( c ) ) { + if ( ts.isEmptyObject( c ) ) { if ( c.debug ) { console.warn( 'No cache found - aborting getColumnText function!' ); } @@ -2075,7 +2139,8 @@ }, format: function(s, table) { var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ''), table); - return s && typeof n === 'number' ? n : s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; + return s && typeof n === 'number' ? n : + s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; }, type: 'numeric' }); @@ -2083,11 +2148,14 @@ ts.addParser({ id: 'currency', is: function(s) { - return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test((s || '').replace(/[+\-,. ]/g, '')); // £$€¤¥¢ + s = (s || '').replace(/[+\-,. ]/g, ''); + // test for £$€¤¥¢ + return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test(s); }, format: function(s, table) { var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ''), table); - return s && typeof n === 'number' ? n : s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; + return s && typeof n === 'number' ? n : + s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; }, type: 'numeric' }); @@ -2145,7 +2213,8 @@ is: function(s) { // two digit years are not allowed cross-browser // Jan 01, 2013 12:34:56 PM or 01 Jan 2013 - return (/^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i).test(s) || (/^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i).test(s); + return (/^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i).test(s) || + (/^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i).test(s); }, format: function(s, table) { var date = s ? new Date( s.replace(/(\S)([AP]M)$/i, '$1 $2') ) : s; @@ -2157,16 +2226,20 @@ ts.addParser({ id: 'shortDate', // 'mmddyyyy', 'ddmmyyyy' or 'yyyymmdd' is: function(s) { + s = (s || '').replace(/\s+/g, ' ').replace(/[\-.,]/g, '/'); // testing for ##-##-#### or ####-##-##, so it's not perfect; time can be included - return (/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/).test((s || '').replace(/\s+/g, ' ').replace(/[\-.,]/g, '/')); + return (/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/).test(s); }, format: function(s, table, cell, cellIndex) { if (s) { var date, d, c = table.config, ci = c.$headerIndexed[ cellIndex ], - format = ci.length && ci[0].dateFormat || ts.getData( ci, ts.getColumnData( table, c.headers, cellIndex ), 'dateFormat') || c.dateFormat; - d = s.replace(/\s+/g, ' ').replace(/[\-.,]/g, '/'); // escaped - because JSHint in Firefox was showing it as an error + format = ci.length && ci[0].dateFormat || + ts.getData( ci, ts.getColumnData( table, c.headers, cellIndex ), 'dateFormat') || + c.dateFormat; + // escaped "-" because JSHint in Firefox was showing it as an error + d = s.replace(/\s+/g, ' ').replace(/[\-.,]/g, '/'); if (format === 'mmddyyyy') { d = d.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, '$3/$1/$2'); } else if (format === 'ddmmyyyy') { @@ -2605,7 +2678,7 @@ })(jQuery); -/*! Widget: filter - updated 7/28/2015 (v2.22.4) *//* +/*! Widget: filter - updated 8/17/2015 (v2.23.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3594,7 +3667,8 @@ } else { if ( wo.filter_startsWith ) { showRow = false; - columnIndex = c.columns; + // data.rowArray may not contain all columns + columnIndex = Math.min( c.columns, data.rowArray.length ); while ( !showRow && columnIndex > 0 ) { columnIndex--; showRow = showRow || data.rowArray[ columnIndex ].indexOf( data.iFilter ) === 0; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index af20963..290ee76 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.22.5 *//* +/*! TableSorter (FORK) v2.23.0 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -26,7 +26,7 @@ var ts = this; - ts.version = '2.22.5'; + ts.version = '2.23.0'; ts.parsers = []; ts.widgets = []; @@ -155,13 +155,13 @@ ts.instanceMethods = {}; // $.isEmptyObject from jQuery v1.4 - function isEmptyObject(obj) { + ts.isEmptyObject = function( obj ) { /*jshint forin: false */ - for (var name in obj) { + for ( var name in obj ) { return false; } return true; - } + }; ts.getElementText = function(c, node, cellIndex) { if (!node) { return ''; } @@ -304,7 +304,7 @@ j += (list.parsers.length) ? len : 1; } if ( c.debug ) { - if ( !isEmptyObject( debug ) ) { + if ( !ts.isEmptyObject( debug ) ) { console[ console.table ? 'table' : 'log' ]( debug ); } else { console.warn( ' No parsers detected!' ); @@ -317,7 +317,7 @@ } /* utils */ - function buildCache(table, $tbodies) { + function buildCache(table, callback, $tbodies) { var cc, t, v, i, j, k, $tb, $row, cols, cacheTime, totalRows, rowData, prevRowData, colMax, c = table.config, @@ -410,54 +410,8 @@ if ( c.debug ) { console.log( 'Building cache for ' + totalRows + ' rows' + ts.benchmark( cacheTime ) ); } - } - - // init flag (true) used by pager plugin to prevent widget application - function appendToTable(table, init) { - var c = table.config, - wo = c.widgetOptions, - $tbodies = c.$tbodies, - rows = [], - cc = c.cache, - n, totalRows, $bk, $tb, - i, k, appendTime; - // empty table - fixes #206/#346 - if (isEmptyObject(cc)) { - // run pager appender in case the table was just emptied - return c.appender ? c.appender(table, rows) : - table.isUpdating ? c.$table.trigger('updateComplete', table) : ''; // Fixes #532 - } - if (c.debug) { - appendTime = new Date(); - } - for (k = 0; k < $tbodies.length; k++) { - $bk = $tbodies.eq(k); - if ($bk.length) { - // get tbody - $tb = ts.processTbody(table, $bk, true); - n = cc[k].normalized; - totalRows = n.length; - for (i = 0; i < totalRows; i++) { - rows.push(n[i][c.columns].$row); - // removeRows used by the pager plugin; don't render if using ajax - fixes #411 - if (!c.appender || (c.pager && (!c.pager.removeRows || !wo.pager_removeRows) && !c.pager.ajax)) { - $tb.append(n[i][c.columns].$row); - } - } - // restore tbody - ts.processTbody(table, $tb, false); - } - } - if (c.appender) { - c.appender(table, rows); - } - if (c.debug) { - console.log( 'Rebuilt table' + ts.benchmark(appendTime) ); - } - // apply table widgets; but not before ajax completes - if (!init && !c.appender) { ts.applyWidget(table); } - if (table.isUpdating) { - c.$table.trigger('updateComplete', table); + if ( $.isFunction( callback ) ) { + callback( table ); } } @@ -466,9 +420,8 @@ return (/^d/i.test(v) || v === 1); } - function buildHeaders(table) { - var ch, $t, h, i, t, lock, time, indx, - c = table.config; + function buildHeaders( c ) { + var ch, $t, h, i, t, lock, time, indx; c.headerList = []; c.headerContent = []; if (c.debug) { @@ -479,12 +432,12 @@ // add icon if cssIcon option exists i = c.cssIcon ? '<i class="' + ( c.cssIcon === ts.css.icon ? ts.css.icon : c.cssIcon + ' ' + ts.css.icon ) + '"></i>' : ''; // redefine c.$headers here in case of an updateAll that replaces or adds an entire header cell - see #683 - c.$headers = $( $.map( $(table).find(c.selectorHeaders), function(elem, index) { + c.$headers = $( $.map( c.$table.find(c.selectorHeaders), function(elem, index) { $t = $(elem); // ignore cell (don't add it to c.$headers) if row has ignoreRow class if ($t.parent().hasClass(c.cssIgnoreRow)) { return; } // make sure to get header cell & not column indexed cell - ch = ts.getColumnData( table, c.headers, index, true ); + ch = ts.getColumnData( c.table, c.headers, index, true ); // save original header content c.headerContent[index] = $t.html(); // if headerTemplate is empty, don't reformat the header cell @@ -526,12 +479,12 @@ // .last() added in jQuery 1.4; use .filter(':last') to maintain compatibility with jQuery v1.2.6 c.$headerIndexed[indx] = $t.not('.sorter-false').length ? $t.not('.sorter-false').filter(':last') : $t.filter(':last'); } - $(table).find(c.selectorHeaders).attr({ + c.$table.find(c.selectorHeaders).attr({ scope: 'col', role : 'columnheader' }); // enable/disable sorting - updateHeader(table); + updateHeader(c.table); if (c.debug) { console.log( 'Built headers:' + ts.benchmark( time ) ); console.log( c.$headers ); @@ -788,7 +741,7 @@ // set css for headers setHeadersCss(table); multisort(table); - appendToTable(table); + ts.appendCache( c ); $table.trigger('sortEnd', table); }, 1); } @@ -803,7 +756,7 @@ sortList = c.sortList, l = sortList.length, bl = c.$tbodies.length; - if (c.serverSideSorting || isEmptyObject(c.cache)) { // empty table - fixes #206/#346 + if (c.serverSideSorting || ts.isEmptyObject(c.cache)) { // empty table - fixes #206/#346 return; } if (c.debug) { sortTime = new Date(); } @@ -894,188 +847,84 @@ } } - function bindMethods(table){ + function bindMethods( table ){ var c = table.config, $table = c.$table, - events = ('sortReset update updateRows updateCell updateAll addRows updateComplete sorton appendCache ' + - 'updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave ').split(' ') - .join(c.namespace + ' '); + events = ( 'sortReset update updateRows updateAll updateHeaders addRows updateCell updateComplete ' + + 'sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup ' + + 'mouseleave ' ).split( ' ' ) + .join( c.namespace + ' ' ); // apply easy methods that trigger bound events $table - .unbind( events.replace(/\s+/g, ' ') ) - .bind('sortReset' + c.namespace, function(e, callback){ + .unbind( events.replace( /\s+/g, ' ' ) ) + .bind( 'sortReset' + c.namespace, function( e, callback ) { e.stopPropagation(); - c.sortList = []; - setHeadersCss(table); - multisort(table); - appendToTable(table); - if ($.isFunction(callback)) { - callback(table); - } + // using this.config to ensure functions are getting a non-cached version of the config + ts.sortReset( this.config, callback ); }) - .bind('updateAll' + c.namespace, function(e, resort, callback){ + .bind( 'updateAll' + c.namespace, function( e, resort, callback ) { e.stopPropagation(); - table.isUpdating = true; - ts.refreshWidgets(table, true, true); - buildHeaders(table); - ts.bindEvents(table, c.$headers, true); - bindMethods(table); - commonUpdate(table, resort, callback); + ts.updateAll( this.config, resort, callback ); }) - .bind('update' + c.namespace + ' updateRows' + c.namespace, function(e, resort, callback) { + .bind( 'update' + c.namespace + ' updateRows' + c.namespace, function( e, resort, callback ) { e.stopPropagation(); - table.isUpdating = true; - // update sorting (if enabled/disabled) - updateHeader(table); - commonUpdate(table, resort, callback); + ts.update( this.config, resort, callback ); }) - .bind('updateCell' + c.namespace, function(e, cell, resort, callback) { + .bind( 'updateHeaders' + c.namespace, function( e, callback ) { e.stopPropagation(); - table.isUpdating = true; - $table.find(c.selectorRemove).remove(); - // get position from the dom - var t, row, icell, cache, - $tb = c.$tbodies, - $cell = $(cell), - // update cache - format: function(s, table, cell, cellIndex) - // no closest in jQuery v1.2.6 - tbdy = $tb.index( $(cell).closest('tbody') ),$row = $(cell).closest('tr'); - tbdy = $tb.index( $.fn.closest ? $cell.closest('tbody') : $cell.parents('tbody').filter(':first') ), - tbcache = c.cache[ tbdy ], - $row = $.fn.closest ? $cell.closest('tr') : $cell.parents('tr').filter(':first'); - cell = $cell[0]; // in case cell is a jQuery object - // tbody may not exist if update is initialized while tbody is removed for processing - if ($tb.length && tbdy >= 0) { - row = $tb.eq( tbdy ).find( 'tr' ).index( $row ); - cache = tbcache.normalized[ row ]; - icell = $cell.index(); - t = ts.getParsedText( c, cell, icell ); - cache[ icell ] = t; - cache[ c.columns ].$row = $row; - if ( (c.parsers[icell].type || '').toLowerCase() === 'numeric' ) { - // update column max value (ignore sign) - tbcache.colMax[icell] = Math.max(Math.abs(t) || 0, tbcache.colMax[icell] || 0); - } - t = resort !== 'undefined' ? resort : c.resort; - if (t !== false) { - // widgets will be reapplied - checkResort(c, t, callback); - } else { - // don't reapply widgets is resort is false, just in case it causes - // problems with element focus - if ($.isFunction(callback)) { - callback(table); - } - c.$table.trigger('updateComplete', c.table); - } - } + ts.updateHeaders( this.config, callback ); }) - .bind('addRows' + c.namespace, function(e, $row, resort, callback) { + .bind( 'updateCell' + c.namespace, function(e, cell, resort, callback ) { e.stopPropagation(); - table.isUpdating = true; - if (isEmptyObject(c.cache)) { - // empty table, do an update instead - fixes #450 - updateHeader(table); - commonUpdate(table, resort, callback); - } else { - $row = $($row).attr('role', 'row'); // make sure we're using a jQuery object - var i, j, l, rowData, cells, - rows = $row.filter('tr').length, - tbdy = c.$tbodies.index( $row.parents('tbody').filter(':first') ); - // fixes adding rows to an empty table - see issue #179 - if (!(c.parsers && c.parsers.length)) { - buildParserCache(c); - } - // add each row - for (i = 0; i < rows; i++) { - l = $row[i].cells.length; - cells = []; - rowData = { - child: [], - $row : $row.eq(i), - order: c.cache[tbdy].normalized.length - }; - // add each cell - for (j = 0; j < l; j++) { - cells[j] = ts.getParsedText( c, $row[i].cells[j], j ); - if ((c.parsers[j].type || '').toLowerCase() === 'numeric') { - // update column max value (ignore sign) - c.cache[tbdy].colMax[j] = Math.max(Math.abs(cells[j]) || 0, c.cache[tbdy].colMax[j] || 0); - } - } - // add the row data to the end - cells.push(rowData); - // update cache - c.cache[tbdy].normalized.push(cells); - } - // resort using current settings - checkResort(c, resort, callback); - } + ts.updateCell( this.config, cell, resort, callback ); }) - .bind('updateComplete' + c.namespace, function(){ + .bind( 'addRows' + c.namespace, function(e, $row, resort, callback) { + e.stopPropagation(); + ts.addRows( this.config, $row, resort, callback ); + }) + .bind( 'updateComplete' + c.namespace, function() { table.isUpdating = false; }) - .bind('sorton' + c.namespace, function(e, list, callback, init) { - var c = table.config; + .bind( 'sorton' + c.namespace, function( e, list, callback, init ) { e.stopPropagation(); - $table.trigger('sortStart', this); - // update header count index - updateHeaderSortCount(table, list); - // set css for headers - setHeadersCss(table); - // fixes #346 - if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); } - $table.trigger('sortBegin', this); - // sort the table and append it to the dom - multisort(table); - appendToTable(table, init); - $table.trigger('sortEnd', this); - ts.applyWidget(table); - if ($.isFunction(callback)) { - callback(table); - } + ts.sortOn( this.config, list, callback, init ); }) - .bind('appendCache' + c.namespace, function(e, callback, init) { + .bind( 'appendCache' + c.namespace, function( e, callback, init ) { e.stopPropagation(); - appendToTable(table, init); - if ($.isFunction(callback)) { - callback(table); + ts.appendCache( this.config, init ); + if ( $.isFunction( callback ) ) { + callback( table ); } }) // $tbodies variable is used by the tbody sorting widget - .bind('updateCache' + c.namespace, function(e, callback, $tbodies){ - // rebuild parsers - if (!(c.parsers && c.parsers.length)) { - buildParserCache(c, $tbodies); - } - // rebuild the cache map - buildCache(table, $tbodies); - if ($.isFunction(callback)) { - callback(table); - } + .bind( 'updateCache' + c.namespace, function( e, callback, $tbodies ){ + e.stopPropagation(); + ts.updateCache( this.config, callback, $tbodies ); }) - .bind('applyWidgetId' + c.namespace, function(e, id) { + .bind( 'applyWidgetId' + c.namespace, function( e, id ) { e.stopPropagation(); - ts.getWidgetById(id).format(table, c, c.widgetOptions); + ts.getWidgetById( id ).format( table, this.config, this.config.widgetOptions ); }) - .bind('applyWidgets' + c.namespace, function(e, init) { + .bind( 'applyWidgets' + c.namespace, function( e, init ) { e.stopPropagation(); // apply widgets - ts.applyWidget(table, init); + ts.applyWidget( table, init ); }) - .bind('refreshWidgets' + c.namespace, function(e, all, dontapply){ + .bind( 'refreshWidgets' + c.namespace, function( e, all, dontapply ) { e.stopPropagation(); - ts.refreshWidgets(table, all, dontapply); + ts.refreshWidgets( table, all, dontapply ); }) - .bind('destroy' + c.namespace, function(e, c, cb){ + .bind( 'destroy' + c.namespace, function( e, removeClasses, callback ) { e.stopPropagation(); - ts.destroy(table, c, cb); + ts.destroy( table, removeClasses, callback ); }) - .bind('resetToLoadState' + c.namespace, function(){ + .bind( 'resetToLoadState' + c.namespace, function( e ) { + e.stopPropagation(); // remove all widgets - ts.removeWidget(table, true, false); + ts.removeWidget( table, true, false ); // restore original settings; this clears out current settings, but does not clear // values saved to storage. - c = $.extend(true, ts.defaults, c.originalSettings); + c = $.extend( true, ts.defaults, c.originalSettings ); table.hasInitialized = false; // setup the entire table again ts.setup( table, c ); @@ -1171,7 +1020,7 @@ // change textExtraction via data-attribute c.textExtraction = c.$table.attr('data-text-extraction') || c.textExtraction || 'basic'; // build headers - buildHeaders(table); + buildHeaders( c ); // fixate columns if the users supplies the fixedWidth option // do this after theme has been applied ts.fixColumnWidth(table); @@ -1391,7 +1240,7 @@ .find(c.selectorSort).add( $headers.filter(c.selectorSort) ) .unbind(t) .bind(t, function(e, external) { - var cell, temp, + var $cell, cell, temp, $target = $(e.target), // wrap event type in spaces, so the match doesn't trigger on inner words type = ' ' + e.type + ' '; @@ -1423,12 +1272,13 @@ $target.parents('button').length > 0 ) { return !c.cancelSelection; } - if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); } + if (c.delayInit && ts.isEmptyObject(c.cache)) { buildCache(table); } // jQuery v1.2.6 doesn't have closest() - cell = $.fn.closest ? $(this).closest('th, td')[0] : /TH|TD/.test(this.nodeName) ? this : $(this).parents('th, td')[0]; + $cell = $.fn.closest ? $(this).closest('th, td') : /TH|TD/.test(this.nodeName) ? $(this) : $(this).parents('th, td'); // reference original table headers and find the same cell - cell = c.$headers[ $headers.index( cell ) ]; - if (!cell.sortDisabled) { + // don't use $headers or IE8 throws an error - see #987 + cell = c.$headers[ $cell.prevAll().length ]; + if (cell && !cell.sortDisabled) { initSort(table, cell, e); } }); @@ -1444,6 +1294,219 @@ } }; + ts.sortReset = function( c, callback ) { + var table = c.table; + c.sortList = []; + setHeadersCss( table ); + multisort( table ); + ts.appendCache( c ); + if ( $.isFunction( callback ) ) { + callback( table ); + } + }; + + ts.updateAll = function( c, resort, callback ) { + var table = c.table; + table.isUpdating = true; + ts.refreshWidgets( table, true, true ); + buildHeaders( c ); + ts.bindEvents( table, c.$headers, true ); + bindMethods( table); + commonUpdate( table, resort, callback ); + }; + + ts.update = function( c, resort, callback ) { + var table = c.table; + table.isUpdating = true; + // update sorting (if enabled/disabled) + updateHeader( table ); + commonUpdate( table, resort, callback ); + }; + + // simple header update - see #989 + ts.updateHeaders = function( c, callback ) { + c.table.isUpdating = true; + buildHeaders( c ); + ts.bindEvents( c.table, c.$headers, true ); + resortComplete( c, callback ); + }; + + ts.updateCell = function( c, cell, resort, callback ) { + c.table.isUpdating = true; + c.$table.find( c.selectorRemove ).remove(); + // get position from the dom + var t, row, icell, cache, + table = c.table, + $tb = c.$tbodies, + $cell = $(cell), + // update cache - format: function(s, table, cell, cellIndex) + // no closest in jQuery v1.2.6 - tbdy = $tb.index( $(cell).closest('tbody') ),$row = $(cell).closest('tr'); + tbdy = $tb.index( $.fn.closest ? $cell.closest( 'tbody' ) : $cell.parents( 'tbody' ).filter( ':first' ) ), + tbcache = c.cache[ tbdy ], + $row = $.fn.closest ? $cell.closest( 'tr' ) : $cell.parents( 'tr' ).filter( ':first' ); + cell = $cell[ 0 ]; // in case cell is a jQuery object + // tbody may not exist if update is initialized while tbody is removed for processing + if ( $tb.length && tbdy >= 0 ) { + row = $tb.eq( tbdy ).find( 'tr' ).index( $row ); + cache = tbcache.normalized[ row ]; + icell = $cell.index(); + t = ts.getParsedText( c, cell, icell ); + cache[ icell ] = t; + cache[ c.columns ].$row = $row; + if ( ( c.parsers[ icell ].type || '' ).toLowerCase() === 'numeric' ) { + // update column max value (ignore sign) + tbcache.colMax[ icell ] = Math.max( Math.abs( t ) || 0, tbcache.colMax[ icell ] || 0 ); + } + t = resort !== 'undefined' ? resort : c.resort; + if ( t !== false ) { + // widgets will be reapplied + checkResort( c, t, callback ); + } else { + // don't reapply widgets is resort is false, just in case it causes + // problems with element focus + resortComplete( c, callback ); + } + } + }; + + ts.addRows = function( c, $row, resort, callback ) { + var i, j, l, rowData, cells, rows, tbdy, + // allow passing a row string if only one non-info tbody exists in the table + valid = typeof $row === 'string' && c.$tbodies.length === 1 && /<tr/.test( $row || '' ), + table = c.table; + if ( valid ) { + $row = $( $row ); + c.$tbodies.append( $row ); + } else if ( !$row || + // row is a jQuery object? + !( $row instanceof jQuery ) || + // row contained in the table? + ( $.fn.closest ? $row.closest( 'table' )[ 0 ] : $row.parents( 'table' )[ 0 ] ) !== c.table ) { + if ( c.debug ) { + console.error( 'addRows method requires (1) a jQuery selector reference to rows that have already ' + + 'been added to the table, or (2) row HTML string to be added to a table with only one tbody' ); + } + return false; + } + table.isUpdating = true; + if ( ts.isEmptyObject( c.cache ) ) { + // empty table, do an update instead - fixes #450 + updateHeader( table ); + commonUpdate( table, resort, callback ); + } else { + rows = $row.filter( 'tr' ).attr( 'role', 'row' ).length; + tbdy = c.$tbodies.index( $row.parents( 'tbody' ).filter( ':first' ) ); + // fixes adding rows to an empty table - see issue #179 + if ( !( c.parsers && c.parsers.length ) ) { + buildParserCache( c ); + } + // add each row + for ( i = 0; i < rows; i++ ) { + l = $row[ i ].cells.length; + cells = []; + rowData = { + child: [], + $row : $row.eq( i ), + order: c.cache[ tbdy ].normalized.length + }; + // add each cell + for ( j = 0; j < l; j++ ) { + cells[ j ] = ts.getParsedText( c, $row[ i ].cells[ j ], j ); + if ( ( c.parsers[ j ].type || '' ).toLowerCase() === 'numeric' ) { + // update column max value (ignore sign) + c.cache[ tbdy ].colMax[ j ] = Math.max( Math.abs( cells[ j ] ) || 0, c.cache[ tbdy ].colMax[ j ] || 0 ); + } + } + // add the row data to the end + cells.push( rowData ); + // update cache + c.cache[ tbdy ].normalized.push( cells ); + } + // resort using current settings + checkResort( c, resort, callback ); + } + }; + + ts.updateCache = function( c, callback, $tbodies ) { + // rebuild parsers + if ( !( c.parsers && c.parsers.length ) ) { + buildParserCache( c, $tbodies ); + } + // rebuild the cache map + buildCache( c.table, callback, $tbodies ); + }; + + // init flag (true) used by pager plugin to prevent widget application + // renamed from appendToTable + ts.appendCache = function( c, init ) { + var n, totalRows, $bk, $tb, i, k, appendTime, + table = c.table, + wo = c.widgetOptions, + $tbodies = c.$tbodies, + rows = [], + cc = c.cache; + // empty table - fixes #206/#346 + if ( ts.isEmptyObject( cc ) ) { + // run pager appender in case the table was just emptied + return c.appender ? c.appender( table, rows ) : + table.isUpdating ? c.$table.trigger( 'updateComplete', table ) : ''; // Fixes #532 + } + if ( c.debug ) { + appendTime = new Date(); + } + for ( k = 0; k < $tbodies.length; k++ ) { + $bk = $tbodies.eq( k ); + if ( $bk.length ) { + // get tbody + $tb = ts.processTbody( table, $bk, true ); + n = cc[ k ].normalized; + totalRows = n.length; + for ( i = 0; i < totalRows; i++ ) { + rows.push( n[ i ][ c.columns ].$row ); + // removeRows used by the pager plugin; don't render if using ajax - fixes #411 + if ( !c.appender || ( c.pager && ( !c.pager.removeRows || !wo.pager_removeRows ) && !c.pager.ajax ) ) { + $tb.append( n[ i ][ c.columns ].$row ); + } + } + // restore tbody + ts.processTbody( table, $tb, false ); + } + } + if ( c.appender ) { + c.appender( table, rows ); + } + if ( c.debug ) { + console.log( 'Rebuilt table' + ts.benchmark( appendTime ) ); + } + // apply table widgets; but not before ajax completes + if ( !init && !c.appender ) { ts.applyWidget( table ); } + if ( table.isUpdating ) { + c.$table.trigger( 'updateComplete', table ); + } + }; + + ts.sortOn = function( c, list, callback, init ) { + var table = c.table; + c.$table.trigger( 'sortStart', table ); + // update header count index + updateHeaderSortCount( table, list ); + // set css for headers + setHeadersCss( table ); + // fixes #346 + if ( c.delayInit && ts.isEmptyObject( c.cache ) ) { + buildCache( table ); + } + c.$table.trigger( 'sortBegin', table ); + // sort the table and append it to the dom + multisort( table ); + ts.appendCache( c, init ); + c.$table.trigger( 'sortEnd', table ); + ts.applyWidget( table ); + if ( $.isFunction( callback ) ) { + callback( table ); + } + }; + // restore headers ts.restoreHeaders = function(table){ var index, $cell, @@ -1482,8 +1545,9 @@ // remove widget added rows, just in case $h.find('tr').not($r).remove(); // disable tablesorter - events = 'sortReset update updateAll updateRows updateCell addRows updateComplete sorton appendCache updateCache ' + - 'applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress sortBegin sortEnd resetToLoadState '.split(' ') + events = 'sortReset update updateRows updateAll updateHeaders updateCell addRows updateComplete sorton ' + + 'appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress ' + + 'sortBegin sortEnd resetToLoadState '.split(' ') .join(c.namespace + ' '); $t .removeData('tablesorter') @@ -1905,7 +1969,7 @@ allColumns = column === 'all', data = { raw : [], parsed: [], $cell: [] }, c = table.config; - if ( isEmptyObject( c ) ) { + if ( ts.isEmptyObject( c ) ) { if ( c.debug ) { console.warn( 'No cache found - aborting getColumnText function!' ); } @@ -2057,7 +2121,8 @@ }, format: function(s, table) { var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ''), table); - return s && typeof n === 'number' ? n : s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; + return s && typeof n === 'number' ? n : + s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; }, type: 'numeric' }); @@ -2065,11 +2130,14 @@ ts.addParser({ id: 'currency', is: function(s) { - return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test((s || '').replace(/[+\-,. ]/g, '')); // £$€¤¥¢ + s = (s || '').replace(/[+\-,. ]/g, ''); + // test for £$€¤¥¢ + return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test(s); }, format: function(s, table) { var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ''), table); - return s && typeof n === 'number' ? n : s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; + return s && typeof n === 'number' ? n : + s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; }, type: 'numeric' }); @@ -2127,7 +2195,8 @@ is: function(s) { // two digit years are not allowed cross-browser // Jan 01, 2013 12:34:56 PM or 01 Jan 2013 - return (/^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i).test(s) || (/^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i).test(s); + return (/^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i).test(s) || + (/^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i).test(s); }, format: function(s, table) { var date = s ? new Date( s.replace(/(\S)([AP]M)$/i, '$1 $2') ) : s; @@ -2139,16 +2208,20 @@ ts.addParser({ id: 'shortDate', // 'mmddyyyy', 'ddmmyyyy' or 'yyyymmdd' is: function(s) { + s = (s || '').replace(/\s+/g, ' ').replace(/[\-.,]/g, '/'); // testing for ##-##-#### or ####-##-##, so it's not perfect; time can be included - return (/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/).test((s || '').replace(/\s+/g, ' ').replace(/[\-.,]/g, '/')); + return (/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/).test(s); }, format: function(s, table, cell, cellIndex) { if (s) { var date, d, c = table.config, ci = c.$headerIndexed[ cellIndex ], - format = ci.length && ci[0].dateFormat || ts.getData( ci, ts.getColumnData( table, c.headers, cellIndex ), 'dateFormat') || c.dateFormat; - d = s.replace(/\s+/g, ' ').replace(/[\-.,]/g, '/'); // escaped - because JSHint in Firefox was showing it as an error + format = ci.length && ci[0].dateFormat || + ts.getData( ci, ts.getColumnData( table, c.headers, cellIndex ), 'dateFormat') || + c.dateFormat; + // escaped "-" because JSHint in Firefox was showing it as an error + d = s.replace(/\s+/g, ' ').replace(/[\-.,]/g, '/'); if (format === 'mmddyyyy') { d = d.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, '$3/$1/$2'); } else if (format === 'ddmmyyyy') { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 29c2a1c..2458c70 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 07-28-2015 (v2.22.5)*/ +/*! tablesorter (FORK) - updated 08-17-2015 (v2.23.0)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -372,7 +372,7 @@ })(jQuery); -/*! Widget: filter - updated 7/28/2015 (v2.22.4) *//* +/*! Widget: filter - updated 8/17/2015 (v2.23.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -1361,7 +1361,8 @@ } else { if ( wo.filter_startsWith ) { showRow = false; - columnIndex = c.columns; + // data.rowArray may not contain all columns + columnIndex = Math.min( c.columns, data.rowArray.length ); while ( !showRow && columnIndex > 0 ) { columnIndex--; showRow = showRow || data.rowArray[ columnIndex ].indexOf( data.iFilter ) === 0; diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index 2db3ee2..f78d9b1 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,4 +1,4 @@ -/*! Parser: input & select - updated 7/28/2015 (v2.22.4) *//* +/*! Parser: input & select - updated 8/17/2015 (v2.23.0) *//* * for jQuery 1.7+ & tablesorter 2.7.11+ * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ @@ -66,7 +66,7 @@ $row.toggleClass( checkedClass + '-' + cellIndex, isChecked ); if ( isChecked ) { $row.addClass( checkedClass ); - } else if ( !( $row[0].className || '' ).match( checkedClass + '-' ) ) { + } else if ( $row.length && !( $row[0].className || '' ).match( checkedClass + '-' ) ) { // don't remove checked class if other columns have a check $row.removeClass( checkedClass ); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index 3d031f0..f1eaba8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Widget: columnSelector (responsive table widget) - updated 3/5/2015 (v2.21.0) *//* +/* Widget: columnSelector (responsive table widget) - updated 8/17/2015 (v2.23.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -35,7 +35,7 @@ colSel.$breakpoints = $('<style></style>').prop('disabled', true).appendTo('head'); colSel.isInitializing = true; - tsColSel.setupSelector(table, c, wo); + tsColSel.setupSelector(c, wo); if (wo.columnSelector_mediaquery) { tsColSel.setupBreakpoints(c, wo); @@ -50,43 +50,69 @@ c.$table .off('refreshColumnSelector' + namespace) - .on('refreshColumnSelector' + namespace, function(e, opt){ + /* $('table').trigger('refreshColumnSelector', arguments ); showing arguments below + undefined = refresh current settings (update css hiding columns) + 'selectors' = update container contents (replace inputs/labels) + [ [2,3,4] ] = set visible columns; turn off "auto" mode. + [ 'columns', [2,3,4] ] = set visible columns; turn off "auto" mode. + [ 'auto', [2,3,4] ] = set visible columns; turn on "auto" mode. + true = turn on "auto" mode. + */ + .on('refreshColumnSelector' + namespace, function( e, optName, optState ){ // make sure we're using current config settings - var i, - isArry = $.isArray(opt), - c = this.config, - wo = c.widgetOptions; - // see #798 - if (opt && c.selector.$container.length) { - if (isArry) { - // make sure array contains numbers - $.each(opt, function(i, v){ - opt[i] = parseInt(v, 10); - }); - for (i = 0; i < c.columns; i++) { - c.selector.$container - .find('input[data-column=' + i + ']') - .prop('checked', $.inArray( i, opt ) >= 0 ); - } - } - // if passing an array, set auto to false to allow manual column selection & update columns - tsColSel.updateAuto( c, wo, colSel.$container.find('input[data-column="auto"]').prop('checked', !isArry) ); - } else { - tsColSel.updateBreakpoints(c, wo); - tsColSel.updateCols(c, wo); - } + tsColSel.refreshColumns( this.config, optName, optState ); }); }, - setupSelector: function(table, c, wo) { + refreshColumns: function( c, optName, optState ) { + var i, arry, + isArry = $.isArray(optState || optName), + wo = c.widgetOptions; + // see #798 + if (typeof optName !== 'undefined' && c.selector.$container.length) { + // pass "selectors" to update the all of the container contents + if ( optName === 'selectors' ) { + c.selector.$container.empty(); + tsColSel.setupSelector(c, wo); + tsColSel.setupBreakpoints(c, wo); + // if optState is undefined, maintain the current "auto" state + if ( typeof optState === 'undefined' ) { + optState = c.selector.auto; + } + } + // pass an array of column zero-based indexes to turn off auto mode & toggle selected columns + if (isArry) { + arry = optState || optName; + // make sure array contains numbers + $.each(arry, function(i, v){ + arry[i] = parseInt(v, 10); + }); + for (i = 0; i < c.columns; i++) { + c.selector.$container + .find('input[data-column=' + i + ']') + .prop('checked', $.inArray( i, arry ) >= 0 ); + } + } + // if passing an array, set auto to false to allow manual column selection & update columns + // refreshColumns( c, 'auto', true ) === refreshColumns( c, true ); + tsColSel + .updateAuto( c, wo, c.selector.$container.find('input[data-column="auto"]') + .prop('checked', optState === true || optName === true || optName === 'auto' && optState !== false) ); + } else { + tsColSel.updateBreakpoints(c, wo); + tsColSel.updateCols(c, wo); + } + }, + + setupSelector: function(c, wo) { var name, colSel = c.selector, $container = colSel.$container, useStorage = wo.columnSelector_saveColumns && ts.storage, // get stored column states - saved = useStorage ? ts.storage( table, 'tablesorter-columnSelector' ) : [], - state = useStorage ? ts.storage( table, 'tablesorter-columnSelector-auto') : {}; + saved = useStorage ? ts.storage( c.table, 'tablesorter-columnSelector' ) : [], + state = useStorage ? ts.storage( c.table, 'tablesorter-columnSelector-auto') : {}; // initial states colSel.auto = $.isEmptyObject(state) || $.type(state.auto) !== 'boolean' ? wo.columnSelector_mediaqueryState : state.auto; @@ -95,7 +121,7 @@ colSel.$wrapper = []; colSel.$checkbox = []; // populate the selector container - c.$table.children('thead').find('tr:first th', table).each(function() { + c.$table.children('thead').find('tr:first th', c.table).each(function() { var $this = $(this), // if no data-priority is assigned, default to 1, but don't remove it from the selector list priority = $this.attr(wo.columnSelector_priority) || 1, @@ -117,7 +143,6 @@ // set default col title name = $this.attr(wo.columnSelector_name) || $this.text(); - if ($container.length) { colSel.$wrapper[colId] = $(wo.columnSelector_layout.replace(/\{name\}/g, name)).appendTo($container); colSel.$checkbox[colId] = colSel.$wrapper[colId] diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js index 596177a..470fdc6 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js @@ -1,4 +1,4 @@ -/*! Widget: editable - updated 5/17/2015 (v2.22.0) *//* +/*! Widget: editable - updated 8/17/2015 (v2.23.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -45,38 +45,43 @@ }, getColumns : function( c, wo ) { - var indx, tmp, - colIndex = [], + var list, indx, range, len, tmp, + editableColumns = wo.editable_columns, cols = []; - if ( !wo.editable_columnsArray && $.type( wo.editable_columns ) === 'string' && wo.editable_columns.indexOf( '-' ) >= 0 ) { - // editable_columns can contain a range string ( i.e. '2-4' ) - tmp = wo.editable_columns.split( /\s*-\s*/ ); - indx = parseInt( tmp[ 0 ], 10 ) || 0; - tmp = parseInt( tmp[ 1 ], 10 ) || ( c.columns - 1 ); - if ( tmp > c.columns ) { - tmp = c.columns - 1; - } - for ( ; indx <= tmp; indx++ ) { - colIndex.push( indx ); - cols.push( 'td:nth-child(' + ( indx + 1 ) + ')' ); + if ( typeof editableColumns === 'string' ) { + // editable_columns can contain a range string, or comma separated values (e.g. '1,2-4,7') + list = editableColumns.replace( /\s+/, '' ).split( /,/ ); + len = list.length - 1; + while ( len >= 0 ) { + if ( list[ len ].indexOf( '-' ) >= 0 ) { + range = list[ len ].split( '-' ); + indx = parseInt( range[ 0 ], 10 ) || 0; + range = parseInt( range[ 1 ], 10) || c.columns - 1; + if ( indx > range ) { + // in case someone does '5-3' + tmp = indx; indx = range; range = tmp; + } + for ( ; indx <= range; indx++ ) { + cols.push( 'td:nth-child(' + ( indx + 1 ) + ')' ); + } + } else { + cols.push( 'td:nth-child(' + ( ( parseInt( list[ len ], 10 ) || 0 ) + 1 ) + ')' ); + } + len--; } - } else if ( $.isArray( wo.editable_columns ) ) { - $.each( wo.editable_columnsArray || wo.editable_columns, function( i, col ) { - if ( col < c.columns ) { - colIndex.push( col ); - cols.push( 'td:nth-child(' + ( col + 1 ) + ')' ); + } else if ( $.isArray( editableColumns ) ) { + len = editableColumns.length; + for ( indx = 0; indx < len; indx++ ) { + if ( editableColumns[ indx ] < c.columns ) { + cols.push( 'td:nth-child(' + ( editableColumns[ indx ] + 1 ) + ')' ); } - }); - } - if ( !wo.editable_columnsArray ) { - wo.editable_columnsArray = colIndex; - wo.editable_columnsArray.sort(function(a, b){ return a - b; }); + } } return cols; }, update: function( c, wo ) { - var $t, + var $t, $cells, cellIndex, cellLen, $editable, editableIndex, editableLen, tmp = $( '<div>' ).wrapInner( wo.editable_wrapContent ).children().length || $.isFunction( wo.editable_wrapContent ), cols = tse.getColumns( c, wo ).join( ',' ); @@ -86,24 +91,28 @@ // IE does not allow making TR/TH/TD cells directly editable ( issue #404 ) // so add a div or span inside ( it's faster than using wrapInner() ) - c.$tbodies.find( cols ).not( '.' + wo.editable_noEdit ).each( function() { + $cells = c.$tbodies.find( cols ).not( '.' + wo.editable_noEdit ); + cellLen = $cells.length; + for ( cellIndex = 0; cellIndex < cellLen; cellIndex++ ) { + /*jshint loopfunc:true */ // test for children, if they exist, then make the children editable - $t = $( this ); - + $t = $cells.eq( cellIndex ); if ( tmp && $t.children( 'div, span' ).length === 0 ) { $t.wrapInner( wo.editable_wrapContent ); } - if ( $t.children( 'div, span' ).length ) { + $editable = $t.children( 'div, span' ).not( '.' + wo.editable_noEdit ); + editableLen = $editable.length; + if ( editableLen ) { // make div/span children content editable - $t.children( 'div, span' ).not( '.' + wo.editable_noEdit ).each( function() { - var $this = $( this ); + for ( editableIndex = 0; editableIndex < editableLen; editableIndex++ ) { + var $this = $editable.eq( editableIndex ); if ( wo.editable_trimContent ) { $this.html( function( i, txt ) { return $.trim( txt ); }); } $this.prop( 'contenteditable', true ); - }); + } } else { if ( wo.editable_trimContent ) { $t.html( function( i, txt ) { @@ -112,7 +121,7 @@ } $t.prop( 'contenteditable', true ); } - }); + } }, bindEvents: function( c, wo ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 5ca182b..302de5e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 7/28/2015 (v2.22.4) *//* +/*! Widget: filter - updated 8/17/2015 (v2.23.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -987,7 +987,8 @@ } else { if ( wo.filter_startsWith ) { showRow = false; - columnIndex = c.columns; + // data.rowArray may not contain all columns + columnIndex = Math.min( c.columns, data.rowArray.length ); while ( !showRow && columnIndex > 0 ) { columnIndex--; showRow = showRow || data.rowArray[ columnIndex ].indexOf( data.iFilter ) === 0; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 363cc21..a5a8c1b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 7/28/2015 (v2.22.4) */ +/*! Widget: Pager - updated 8/17/2015 (v2.23.0) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -62,6 +62,11 @@ // modify the url after all processing has been applied pager_customAjaxUrl: function(table, url) { return url; }, + // ajax error callback from $.tablesorter.showError function + // pager_ajaxError: function( config, xhr, exception ){ return exception; }; + // returning false will abort the error message + pager_ajaxError: null, + // modify the $.ajax object to allow complete control over your ajax requests pager_ajaxObject: { dataType: 'json' @@ -144,9 +149,7 @@ last: {}, // save original pager size setSize: wo.pager_size, - setPage: wo.pager_startPage, - events: 'filterInit filterStart filterEnd sortEnd disable enable destroy updateComplete ' + - 'pageSize pageSet pageAndSize pagerUpdate refreshComplete ' + setPage: wo.pager_startPage }, c.pager); // pager initializes multiple times before table has completed initialization @@ -236,7 +239,7 @@ s = wo.pager_selectors; c.$table - .off( $.trim(p.events.split(' ').join(namespace + ' ')) ) + .off( namespace ) .on('filterInit filterStart '.split(' ').join(namespace + ' '), function(e, filters) { p.currentFilters = $.isArray(filters) ? filters : c.$table.data('lastSearch'); // don't change page if filters are the same (pager updating, etc) @@ -257,15 +260,15 @@ c.$table.trigger('applyWidgets'); } }) - .on('disable' + namespace, function(e){ + .on('disablePager' + namespace, function(e){ e.stopPropagation(); tsp.showAllRows(table, c); }) - .on('enable' + namespace, function(e){ + .on('enablePager' + namespace, function(e){ e.stopPropagation(); tsp.enablePager(table, c, true); }) - .on('destroy' + namespace, function(e, refreshing){ + .on('destroyPager' + namespace, function(e, refreshing){ e.stopPropagation(); tsp.destroyPager(table, c, refreshing); }) @@ -655,13 +658,13 @@ hl = $table.find('thead th').length; // Clean up any previous error. - ts.showError(table); + ts.showError( table ); if ( exception ) { if (c.debug) { console.error('Pager: >> Ajax Error', xhr, exception); } - ts.showError(table, exception.message + ' (' + xhr.status + ')'); + ts.showError( table, xhr, exception ); c.$tbodies.eq(0).children('tr').detach(); p.totalRows = 0; } else { @@ -1073,17 +1076,25 @@ destroyPager: function(table, c, refreshing){ var p = c.pager, + s = c.widgetOptions.pager_selectors, + ctrls = [ s.first, s.prev, s.next, s.last, s.gotoPage, s.pageSize ].join( ',' ), namespace = c.namespace + 'pager'; p.initialized = false; - c.$table.off( $.trim(p.events.split(' ').join(namespace + ' ')) ); - if (refreshing) { return; } + c.$table.off( namespace ); + p.$container + // hide pager + .hide() + // unbind pager controls + .find( ctrls ) + .off( namespace ); + if ( refreshing ) { return; } tsp.showAllRows(table, c); - p.$container.hide(); // hide pager c.appender = null; // remove pager appender function - delete table.config.rowsCopy; if (ts.storage) { ts.storage(table, c.widgetOptions.pager_storageKey, ''); } + delete table.config.pager; + delete table.config.rowsCopy; }, enablePager: function(table, c, triggered){ @@ -1131,32 +1142,73 @@ }; // see #486 - ts.showError = function( table, message ) { - var index, $row, c, wo, errorRow, + ts.showError = function( table, xhr, exception ) { + var $row, $table = $( table ), - len = $table.length; - for ( index = 0; index < len; index++ ) { - c = $table[ index ].config; - if ( c ) { - wo = c.widgetOptions; - errorRow = c.pager && c.pager.cssErrorRow || wo.pager_css && wo.pager_css.errorRow || 'tablesorter-errorRow'; - if ( typeof message === 'undefined' ) { - c.$table.find('thead').find(c.selectorRemove).remove(); + c = $table[0].config, + wo = c && c.widgetOptions, + errorRow = c.pager && c.pager.cssErrorRow || wo.pager_css && wo.pager_css.errorRow || 'tablesorter-errorRow', + typ = typeof xhr, + valid = true, + message = '', + removeRow = function(){ + c.$table.find( 'thead' ).find( '.' + errorRow ).remove(); + }; + + if ( !$table.length ) { + console.error('tablesorter showError: no table parameter passed'); + return; + } + + if ( typ !== 'string' ) { + // ajaxError callback for plugin or widget - see #992 + if ( typeof c.pager.ajaxError === 'function' ) { + valid = c.pager.ajaxError( c, xhr, exception ); + if ( valid === false ) { + return removeRow(); } else { - $row = ( /tr\>/.test(message) ? $(message) : $('<tr><td colspan="' + c.columns + '">' + message + '</td></tr>') ) - .click(function(){ - $(this).remove(); - }) - // add error row to thead instead of tbody, or clicking on the header will result in a parser error - .appendTo( c.$table.find('thead:first') ) - .addClass( errorRow + ' ' + c.selectorRemove.slice(1) ) - .attr({ - role : 'alert', - 'aria-live' : 'assertive' - }); + message = valid; } + } else if ( typeof wo.pager_ajaxError === 'function' ) { + valid = wo.pager_ajaxError( c, xhr, exception ); + if ( valid === false ) { + return removeRow(); + } else { + message = valid; + } + } else { + message = + xhr.status === 0 ? 'Not connected, verify Network' : + xhr.status === 404 ? 'Requested page not found [404]' : + xhr.status === 500 ? 'Internal Server Error [500]' : + exception === 'parsererror' ? 'Requested JSON parse failed' : + exception === 'timeout' ? 'Time out error' : + exception === 'abort' ? 'Ajax Request aborted' : + 'Uncaught error: ' + xhr.statusText + ' [' + xhr.status + ']'; } + } else if ( typ !== 'undefined' ) { + // keep backward compatibility (external usage just passes a message string) + message = xhr; + } + + if ( message === '' ) { + // remove all error rows + return removeRow(); } + + // allow message to include HTML (must include entire row!) + $row = ( /tr\>/.test(message) ? $(message) : $('<tr><td colspan="' + c.columns + '">' + message + '</td></tr>') ) + .click( function() { + $( this ).remove(); + }) + // add error row to thead instead of tbody, or clicking on the header will result in a parser error + .appendTo( c.$table.find( 'thead:first' ) ) + .addClass( errorRow + ' ' + c.selectorRemove.slice(1) ) + .attr({ + role : 'alert', + 'aria-live' : 'assertive' + }); + }; })(jQuery); From 38f2b4894c39f66436afaa8d87830bba2c0a7a9b Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Thu, 20 Aug 2015 20:31:11 +0200 Subject: [PATCH 063/138] * updated tablesorter to latest version (2.23.1) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 68 ++++++++++--------- .../jquery.tablesorter.combined.js | 6 +- .../jquery-tablesorter/jquery.tablesorter.js | 4 +- .../jquery.tablesorter.widgets.js | 2 +- .../widgets/widget-pager.js | 68 ++++++++++--------- 9 files changed, 83 insertions(+), 75 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdf916c..de36af4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.18.1 (2015-08-20) + +* Upgrade tablesorter to v2.23.1 + #### v1.18.0 (2015-08-18) * Upgrade tablesorter to v2.23.0 diff --git a/README.md b/README.md index a4a7f65..d9c3101 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.23.0 (8/17/2015), [documentation] +Current tablesorter version: 2.23.1 (8/19/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 24c0593..6469d14 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.18.0' + VERSION = '1.18.1' end diff --git a/tablesorter b/tablesorter index c602ca7..3a207c9 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit c602ca7f01ba7b623580eac7972aa1f523f0dae9 +Subproject commit 3a207c99ef45977be2028ca1ac835592085b0de1 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 86da091..feebf60 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin - * updated 8/17/2015 (v2.23.0) + * updated 8/19/2015 (v2.23.1) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -26,7 +26,7 @@ customAjaxUrl: function(table, url) { return url; }, // ajax error callback from $.tablesorter.showError function - // ajaxError: function( config, xhr, exception ){ return exception; }; + // ajaxError: function( config, xhr, settings, exception ){ return exception; }; // returning false will abort the error message ajaxError: null, @@ -379,7 +379,7 @@ } }, - renderAjax = function(data, table, p, xhr, exception){ + renderAjax = function(data, table, p, xhr, settings, exception){ // process data if ( typeof p.ajaxProcessing === 'function' ) { // ajaxProcessing result: [ total, rows, headers ] @@ -395,9 +395,9 @@ if ( exception ) { if (c.debug) { - console.error('Pager: >> Ajax Error', xhr, exception); + console.error('Pager: >> Ajax Error', xhr, settings, exception); } - ts.showError( table, xhr, exception ); + ts.showError( table, xhr, settings, exception ); c.$tbodies.eq(0).children('tr').detach(); p.totalRows = 0; } else { @@ -522,7 +522,7 @@ ts.isProcessing(table, true); // show loading icon } $doc.bind('ajaxError' + namespace, function(e, xhr, settings, exception) { - renderAjax(null, table, p, xhr, exception); + renderAjax(null, table, p, xhr, settings, exception); $doc.unbind('ajaxError' + namespace); }); @@ -1067,12 +1067,14 @@ }() }); // see #486 - ts.showError = function( table, xhr, exception ) { + ts.showError = function( table, xhr, settings, exception ) { var $row, $table = $( table ), c = $table[0].config, wo = c && c.widgetOptions, - errorRow = c.pager && c.pager.cssErrorRow || wo.pager_css && wo.pager_css.errorRow || 'tablesorter-errorRow', + errorRow = c.pager && c.pager.cssErrorRow || + wo && wo.pager_css && wo.pager_css.errorRow || + 'tablesorter-errorRow', typ = typeof xhr, valid = true, message = '', @@ -1085,23 +1087,25 @@ return; } - if ( typ !== 'string' ) { - // ajaxError callback for plugin or widget - see #992 - if ( typeof c.pager.ajaxError === 'function' ) { - valid = c.pager.ajaxError( c, xhr, exception ); - if ( valid === false ) { - return removeRow(); - } else { - message = valid; - } - } else if ( typeof wo.pager_ajaxError === 'function' ) { - valid = wo.pager_ajaxError( c, xhr, exception ); - if ( valid === false ) { - return removeRow(); - } else { - message = valid; - } + // ajaxError callback for plugin or widget - see #992 + if ( typeof c.pager.ajaxError === 'function' ) { + valid = c.pager.ajaxError( c, xhr, settings, exception ); + if ( valid === false ) { + return removeRow(); } else { + message = valid; + } + } else if ( typeof wo.pager_ajaxError === 'function' ) { + valid = wo.pager_ajaxError( c, xhr, settings, exception ); + if ( valid === false ) { + return removeRow(); + } else { + message = valid; + } + } + + if ( message === '' ) { + if ( typ === 'object' ) { message = xhr.status === 0 ? 'Not connected, verify Network' : xhr.status === 404 ? 'Requested page not found [404]' : @@ -1110,18 +1114,16 @@ exception === 'timeout' ? 'Time out error' : exception === 'abort' ? 'Ajax Request aborted' : 'Uncaught error: ' + xhr.statusText + ' [' + xhr.status + ']'; + } else if ( typ === 'string' ) { + // keep backward compatibility (external usage just passes a message string) + message = xhr; + } else { + // remove all error rows + return removeRow(); } - } else if ( typ !== 'undefined' ) { - // keep backward compatibility (external usage just passes a message string) - message = xhr; - } - - if ( message === '' ) { - // remove all error rows - return removeRow(); } - // allow message to include HTML (must include entire row!) + // allow message to include entire row HTML! $row = ( /tr\>/.test(message) ? $(message) : $('<tr><td colspan="' + c.columns + '">' + message + '</td></tr>') ) .click( function() { $( this ).remove(); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 0e03875..4112c20 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 08-17-2015 (v2.23.0)*/ +/*! tablesorter (FORK) - updated 08-19-2015 (v2.23.1)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.23.0 *//* +/*! TableSorter (FORK) v2.23.1 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -44,7 +44,7 @@ var ts = this; - ts.version = '2.23.0'; + ts.version = '2.23.1'; ts.parsers = []; ts.widgets = []; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 290ee76..3969639 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.23.0 *//* +/*! TableSorter (FORK) v2.23.1 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -26,7 +26,7 @@ var ts = this; - ts.version = '2.23.0'; + ts.version = '2.23.1'; ts.parsers = []; ts.widgets = []; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 2458c70..a9e8de9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 08-17-2015 (v2.23.0)*/ +/*! tablesorter (FORK) - updated 08-19-2015 (v2.23.1)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index a5a8c1b..fef33cb 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 8/17/2015 (v2.23.0) */ +/*! Widget: Pager - updated 8/19/2015 (v2.23.1) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -63,7 +63,7 @@ pager_customAjaxUrl: function(table, url) { return url; }, // ajax error callback from $.tablesorter.showError function - // pager_ajaxError: function( config, xhr, exception ){ return exception; }; + // pager_ajaxError: function( config, xhr, settings, exception ){ return exception; }; // returning false will abort the error message pager_ajaxError: null, @@ -645,7 +645,7 @@ } }, - renderAjax: function(data, table, c, xhr, exception){ + renderAjax: function(data, table, c, xhr, settings, exception) { var p = c.pager, wo = c.widgetOptions; // process data @@ -662,9 +662,9 @@ if ( exception ) { if (c.debug) { - console.error('Pager: >> Ajax Error', xhr, exception); + console.error('Pager: >> Ajax Error', xhr, settings, exception); } - ts.showError( table, xhr, exception ); + ts.showError( table, xhr, settings, exception ); c.$tbodies.eq(0).children('tr').detach(); p.totalRows = 0; } else { @@ -782,7 +782,7 @@ ts.isProcessing(table, true); // show loading icon } $doc.on('ajaxError' + namespace, function(e, xhr, settings, exception) { - tsp.renderAjax(null, table, c, xhr, exception); + tsp.renderAjax(null, table, c, xhr, settings, exception); $doc.off('ajaxError' + namespace); }); counter = ++p.ajaxCounter; @@ -1142,12 +1142,14 @@ }; // see #486 - ts.showError = function( table, xhr, exception ) { + ts.showError = function( table, xhr, settings, exception ) { var $row, $table = $( table ), c = $table[0].config, wo = c && c.widgetOptions, - errorRow = c.pager && c.pager.cssErrorRow || wo.pager_css && wo.pager_css.errorRow || 'tablesorter-errorRow', + errorRow = c.pager && c.pager.cssErrorRow || + wo && wo.pager_css && wo.pager_css.errorRow || + 'tablesorter-errorRow', typ = typeof xhr, valid = true, message = '', @@ -1160,23 +1162,25 @@ return; } - if ( typ !== 'string' ) { - // ajaxError callback for plugin or widget - see #992 - if ( typeof c.pager.ajaxError === 'function' ) { - valid = c.pager.ajaxError( c, xhr, exception ); - if ( valid === false ) { - return removeRow(); - } else { - message = valid; - } - } else if ( typeof wo.pager_ajaxError === 'function' ) { - valid = wo.pager_ajaxError( c, xhr, exception ); - if ( valid === false ) { - return removeRow(); - } else { - message = valid; - } + // ajaxError callback for plugin or widget - see #992 + if ( typeof c.pager.ajaxError === 'function' ) { + valid = c.pager.ajaxError( c, xhr, settings, exception ); + if ( valid === false ) { + return removeRow(); } else { + message = valid; + } + } else if ( typeof wo.pager_ajaxError === 'function' ) { + valid = wo.pager_ajaxError( c, xhr, settings, exception ); + if ( valid === false ) { + return removeRow(); + } else { + message = valid; + } + } + + if ( message === '' ) { + if ( typ === 'object' ) { message = xhr.status === 0 ? 'Not connected, verify Network' : xhr.status === 404 ? 'Requested page not found [404]' : @@ -1185,18 +1189,16 @@ exception === 'timeout' ? 'Time out error' : exception === 'abort' ? 'Ajax Request aborted' : 'Uncaught error: ' + xhr.statusText + ' [' + xhr.status + ']'; + } else if ( typ === 'string' ) { + // keep backward compatibility (external usage just passes a message string) + message = xhr; + } else { + // remove all error rows + return removeRow(); } - } else if ( typ !== 'undefined' ) { - // keep backward compatibility (external usage just passes a message string) - message = xhr; - } - - if ( message === '' ) { - // remove all error rows - return removeRow(); } - // allow message to include HTML (must include entire row!) + // allow message to include entire row HTML! $row = ( /tr\>/.test(message) ? $(message) : $('<tr><td colspan="' + c.columns + '">' + message + '</td></tr>') ) .click( function() { $( this ).remove(); From 85fb40264007d7b1d4c39d0f3e8eccb256ff3603 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Thu, 20 Aug 2015 20:48:25 +0200 Subject: [PATCH 064/138] * clarified version compatibility in Readme --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d9c3101..01e5657 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,8 @@ Or install it yourself as: ## Requirements -Rails 3.2 and higher (tested up to 4.2) - -Tested with ruby 1.9.3 - 2.2.2 +It should work with Rails 3.2 and higher (tested up to 4.2) as well as with ruby 1.9.3 - 2.2.x. +Each release is always tested with the latest version of both. ## Usage From e3f92f25da49613207c9f29a344724b3ccbacbd9 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 24 Aug 2015 20:23:27 +0200 Subject: [PATCH 065/138] * updated tablesorter to latest version (2.23.2) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 373 ++++++++++-------- .../jquery-tablesorter/jquery.tablesorter.js | 122 ++++-- .../jquery.tablesorter.widgets.js | 251 ++++++------ .../widgets/widget-columnSelector.js | 10 +- .../widgets/widget-filter.js | 249 ++++++------ 9 files changed, 587 insertions(+), 428 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de36af4..6484bb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.18.2 (2015-08-24) + +* Upgrade tablesorter to v2.23.2 + #### v1.18.1 (2015-08-20) * Upgrade tablesorter to v2.23.1 diff --git a/README.md b/README.md index 01e5657..50bfcd4 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.23.1 (8/19/2015), [documentation] +Current tablesorter version: 2.23.2 (8/23/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 6469d14..514d9ae 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.18.1' + VERSION = '1.18.2' end diff --git a/tablesorter b/tablesorter index 3a207c9..68ae07c 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 3a207c99ef45977be2028ca1ac835592085b0de1 +Subproject commit 68ae07cb79ab93443d4ba582719671769eca6d7a diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 4112c20..70be4c8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 08-19-2015 (v2.23.1)*/ +/*! tablesorter (FORK) - updated 08-23-2015 (v2.23.2)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.23.1 *//* +/*! TableSorter (FORK) v2.23.2 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -44,7 +44,7 @@ var ts = this; - ts.version = '2.23.1'; + ts.version = '2.23.2'; ts.parsers = []; ts.widgets = []; @@ -169,6 +169,15 @@ nextNone : 'activate to remove the sort' }; + ts.regex = { + templateContent : /\{content\}/g, + templateIcon : /\{icon\}/g, + templateName : /\{name\}/i, + spaces : /\s+/g, + nonWord : /\W/g, + formElements : /(input|select|button|textarea)/i + }; + // These methods can be applied on table.config instance ts.instanceMethods = {}; @@ -461,7 +470,9 @@ // if headerTemplate is empty, don't reformat the header cell if ( c.headerTemplate !== '' && !$t.find('.' + ts.css.headerIn).length ) { // set up header template - t = c.headerTemplate.replace(/\{content\}/g, $t.html()).replace(/\{icon\}/g, $t.find('.' + ts.css.icon).length ? '' : i); + t = c.headerTemplate + .replace(ts.regex.templateContent, $t.html()) + .replace(ts.regex.templateIcon, $t.find('.' + ts.css.icon).length ? '' : i); if (c.onRenderTemplate) { h = c.onRenderTemplate.apply( $t, [ index, t ] ); if (h && typeof h === 'string') { t = h; } // only change t if something is returned @@ -874,7 +885,7 @@ .join( c.namespace + ' ' ); // apply easy methods that trigger bound events $table - .unbind( events.replace( /\s+/g, ' ' ) ) + .unbind( events.replace( ts.regex.spaces, ' ' ) ) .bind( 'sortReset' + c.namespace, function( e, callback ) { e.stopPropagation(); // using this.config to ensure functions are getting a non-cached version of the config @@ -1021,7 +1032,7 @@ c.namespace = '.tablesorter' + Math.random().toString(16).slice(2); } else { // make sure namespace starts with a period & doesn't have weird characters - c.namespace = '.' + c.namespace.replace(/\W/g, ''); + c.namespace = '.' + c.namespace.replace(ts.regex.nonWord, ''); } c.$table.children().children('tr').attr('role', 'row'); @@ -1249,7 +1260,7 @@ } } t = ( c.pointerDown + ' ' + c.pointerUp + ' ' + c.pointerClick + ' sort keyup ' ) - .replace(/\s+/g, ' ') + .replace(ts.regex.spaces, ' ') .split(' ') .join(c.namespace + ' '); // apply event handling to headers and/or additional headers (stickyheaders, scroller, etc) @@ -1283,7 +1294,7 @@ } downTarget = null; // prevent sort being triggered on form elements - if ( /(input|select|button|textarea)/i.test(e.target.nodeName) || + if ( ts.regex.formElements.test(e.target.nodeName) || // nosort class name, or elements within a nosort container $target.hasClass(c.cssNoSort) || $target.parents('.' + c.cssNoSort).length > 0 || // elements within a button @@ -1569,13 +1580,13 @@ .join(c.namespace + ' '); $t .removeData('tablesorter') - .unbind( events.replace(/\s+/g, ' ') ); + .unbind( events.replace(ts.regex.spaces, ' ') ); c.$headers.add($f) .removeClass( [ ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone ].join(' ') ) .removeAttr('data-column') .removeAttr('aria-label') .attr('aria-disabled', 'true'); - $r.find(c.selectorSort).unbind( ('mousedown mouseup keypress '.split(' ').join(c.namespace + ' ')).replace(/\s+/g, ' ') ); + $r.find(c.selectorSort).unbind( ('mousedown mouseup keypress '.split(' ').join(c.namespace + ' ')).replace(ts.regex.spaces, ' ') ); ts.restoreHeaders(table); $t.toggleClass(ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme, removeClasses === false); // clear flag in case the plugin is initialized again @@ -1591,11 +1602,9 @@ // *** sort functions *** // regex used in natural sort - ts.regex = { - chunk : /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi, // chunk/tokenize numbers & letters - chunks: /(^\\0|\\0$)/, // replace chunks @ ends - hex: /^0x[0-9a-f]+$/i // hex - }; + ts.regex.chunk = /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi; // chunk/tokenize numbers & letters + ts.regex.chunks = /(^\\0|\\0$)/; // replace chunks @ ends + ts.regex.hex = /^0x[0-9a-f]+$/i; // hex // Natural sort - https://github.com/overset/javascript-natural-sort (date sorting removed) // this function will only accept strings, or you'll see 'TypeError: undefined is not a function' @@ -1822,7 +1831,7 @@ if (c.debug) { time = new Date(); } // look for widgets to apply from in table class // stop using \b otherwise this matches 'ui-widget-content' & adds 'content' widget - wd = new RegExp( '\\s' + c.widgetClass.replace( /\{name\}/i, '([\\w-]+)' ) + '\\s', 'g' ); + wd = new RegExp( '\\s' + c.widgetClass.replace( ts.regex.templateName, '([\\w-]+)' ) + '\\s', 'g' ); if ( tableClass.match( wd ) ) { // extract out the widget id from the table class (widget id's can include dashes) w = tableClass.match( wd ); @@ -2048,6 +2057,10 @@ return $.trim(val); }; + ts.regex.comma = /,/g; + ts.regex.digitNonUS = /[\s|\.]/g; + ts.regex.digitNegativeTest = /^\s*\([.\d]+\)/; + ts.regex.digitNegativeReplace = /^\s*\(([.\d]+)\)/; ts.formatFloat = function(s, table) { if (typeof s !== 'string' || s === '') { return s; } // allow using formatFloat without a table; defaults to US number format @@ -2056,24 +2069,28 @@ typeof table !== 'undefined' ? table : true; if (t) { // US Format - 1,234,567.89 -> 1234567.89 - s = s.replace(/,/g, ''); + s = s.replace(ts.regex.comma, ''); } else { // German Format = 1.234.567,89 -> 1234567.89 // French Format = 1 234 567,89 -> 1234567.89 - s = s.replace(/[\s|\.]/g, '').replace(/,/g, '.'); + s = s.replace(ts.regex.digitNonUS, '').replace(ts.regex.comma, '.'); } - if (/^\s*\([.\d]+\)/.test(s)) { + if (ts.regex.digitNegativeTest.test(s)) { // make (#) into a negative number -> (10) = -10 - s = s.replace(/^\s*\(([.\d]+)\)/, '-$1'); + s = s.replace(ts.regex.digitNegativeReplace, '-$1'); } i = parseFloat(s); // return the text instead of zero return isNaN(i) ? $.trim(s) : i; }; + ts.regex.digitTest = /^[\-+(]?\d+[)]?$/; + ts.regex.digitReplace = /[,.'"\s]/g; ts.isDigit = function(s) { // replace all unwanted chars and match - return isNaN(s) ? (/^[\-+(]?\d+[)]?$/).test(s.toString().replace(/[,.'"\s]/g, '')) : s !== ''; + return isNaN(s) ? + ts.regex.digitTest.test( s.toString().replace( ts.regex.digitReplace, '' ) ) : + s !== ''; }; }() @@ -2132,65 +2149,76 @@ type: 'text' }); + ts.regex.nondigit = /[^\w,. \-()]/g; ts.addParser({ id: 'digit', is: function(s) { return ts.isDigit(s); }, format: function(s, table) { - var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ''), table); + var n = ts.formatFloat((s || '').replace(ts.regex.nondigit, ''), table); return s && typeof n === 'number' ? n : s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; }, type: 'numeric' }); + ts.regex.currencyReplace = /[+\-,. ]/g; + ts.regex.currencyTest = /^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/; ts.addParser({ id: 'currency', is: function(s) { - s = (s || '').replace(/[+\-,. ]/g, ''); + s = (s || '').replace(ts.regex.currencyReplace, ''); // test for £$€¤¥¢ - return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test(s); + return ts.regex.currencyTest.test(s); }, format: function(s, table) { - var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ''), table); + var n = ts.formatFloat((s || '').replace(ts.regex.nondigit, ''), table); return s && typeof n === 'number' ? n : s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; }, type: 'numeric' }); + // too many protocols to add them all https://en.wikipedia.org/wiki/URI_scheme + // now, this regex can be updated before initialization + ts.regex.urlProtocolTest = /^(https?|ftp|file):\/\//; + ts.regex.urlProtocolReplace = /(https?|ftp|file):\/\//; ts.addParser({ id: 'url', is: function(s) { - return (/^(https?|ftp|file):\/\//).test(s); + return ts.regex.urlProtocolTest.test(s); }, format: function(s) { - return s ? $.trim(s.replace(/(https?|ftp|file):\/\//, '')) : s; + return s ? $.trim(s.replace(ts.regex.urlProtocolReplace, '')) : s; }, parsed : true, // filter widget flag type: 'text' }); + ts.regex.dash = /-/g; + ts.regex.isoDate = /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}/; ts.addParser({ id: 'isoDate', is: function(s) { - return (/^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}/).test(s); + return ts.regex.isoDate.test(s); }, format: function(s, table) { - var date = s ? new Date( s.replace(/-/g, '/') ) : s; + var date = s ? new Date( s.replace(ts.regex.dash, '/') ) : s; return date instanceof Date && isFinite(date) ? date.getTime() : s; }, type: 'numeric' }); + ts.regex.percent = /%/g; + ts.regex.percentTest = /(\d\s*?%|%\s*?\d)/; ts.addParser({ id: 'percent', is: function(s) { - return (/(\d\s*?%|%\s*?\d)/).test(s) && s.length < 15; + return ts.regex.percentTest.test(s) && s.length < 15; }, format: function(s, table) { - return s ? ts.formatFloat(s.replace(/%/g, ''), table) : s; + return s ? ts.formatFloat(s.replace(ts.regex.percent, ''), table) : s; }, type: 'numeric' }); @@ -2208,27 +2236,35 @@ type: 'text' }); + ts.regex.dateReplace = /(\S)([AP]M)$/i; // used by usLongDate & time parser + ts.regex.usLongDateTest1 = /^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i; + ts.regex.usLongDateTest2 = /^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i; ts.addParser({ id: 'usLongDate', is: function(s) { // two digit years are not allowed cross-browser // Jan 01, 2013 12:34:56 PM or 01 Jan 2013 - return (/^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i).test(s) || - (/^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i).test(s); + return ts.regex.usLongDateTest1.test(s) || ts.regex.usLongDateTest2.test(s); }, format: function(s, table) { - var date = s ? new Date( s.replace(/(\S)([AP]M)$/i, '$1 $2') ) : s; + var date = s ? new Date( s.replace(ts.regex.dateReplace, '$1 $2') ) : s; return date instanceof Date && isFinite(date) ? date.getTime() : s; }, type: 'numeric' }); + // testing for ##-##-#### or ####-##-##, so it's not perfect; time can be included + ts.regex.shortDateTest = /(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/; + // escaped "-" because JSHint in Firefox was showing it as an error + ts.regex.shortDateReplace = /[\-.,]/g; + // XXY covers MDY & DMY formats + ts.regex.shortDateXXY = /(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/; + ts.regex.shortDateYMD = /(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/; ts.addParser({ id: 'shortDate', // 'mmddyyyy', 'ddmmyyyy' or 'yyyymmdd' is: function(s) { - s = (s || '').replace(/\s+/g, ' ').replace(/[\-.,]/g, '/'); - // testing for ##-##-#### or ####-##-##, so it's not perfect; time can be included - return (/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/).test(s); + s = (s || '').replace(ts.regex.spaces, ' ').replace(ts.regex.shortDateReplace, '/'); + return ts.regex.shortDateTest.test(s); }, format: function(s, table, cell, cellIndex) { if (s) { @@ -2238,14 +2274,13 @@ format = ci.length && ci[0].dateFormat || ts.getData( ci, ts.getColumnData( table, c.headers, cellIndex ), 'dateFormat') || c.dateFormat; - // escaped "-" because JSHint in Firefox was showing it as an error - d = s.replace(/\s+/g, ' ').replace(/[\-.,]/g, '/'); + d = s.replace(ts.regex.spaces, ' ').replace(ts.regex.shortDateReplace, '/'); if (format === 'mmddyyyy') { - d = d.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, '$3/$1/$2'); + d = d.replace(ts.regex.shortDateXXY, '$3/$1/$2'); } else if (format === 'ddmmyyyy') { - d = d.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, '$3/$2/$1'); + d = d.replace(ts.regex.shortDateXXY, '$3/$2/$1'); } else if (format === 'yyyymmdd') { - d = d.replace(/(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/, '$1/$2/$3'); + d = d.replace(ts.regex.shortDateYMD, '$1/$2/$3'); } date = new Date(d); return date instanceof Date && isFinite(date) ? date.getTime() : s; @@ -2255,13 +2290,14 @@ type: 'numeric' }); + ts.regex.timeTest = /^(([0-2]?\d:[0-5]\d)|([0-1]?\d:[0-5]\d\s?([AP]M)))$/i; ts.addParser({ id: 'time', is: function(s) { - return (/^(([0-2]?\d:[0-5]\d)|([0-1]?\d:[0-5]\d\s?([AP]M)))$/i).test(s); + return ts.regex.timeTest.test(s); }, format: function(s, table) { - var date = s ? new Date( '2000/01/01 ' + s.replace(/(\S)([AP]M)$/i, '$1 $2') ) : s; + var date = s ? new Date( '2000/01/01 ' + s.replace(ts.regex.dateReplace, '$1 $2') ) : s; return date instanceof Date && isFinite(date) ? date.getTime() : s; }, type: 'numeric' @@ -2678,14 +2714,15 @@ })(jQuery); -/*! Widget: filter - updated 8/17/2015 (v2.23.0) *//* +/*! Widget: filter - updated 8/23/2015 (v2.23.2) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ ;( function ( $ ) { 'use strict'; - var ts = $.tablesorter || {}, - tscss = ts.css; + var tsf, + ts = $.tablesorter || {}, + tscss = ts.css; $.extend( tscss, { filterRow : 'tablesorter-filter-row', @@ -2729,7 +2766,7 @@ }, format: function( table, c, wo ) { if ( !c.$table.hasClass( 'hasFilters' ) ) { - ts.filter.init( table, c, wo ); + tsf.init( table, c, wo ); } }, remove: function( table, c, wo, refreshing ) { @@ -2741,7 +2778,7 @@ $table .removeClass( 'hasFilters' ) // add .tsfilter namespace to all BUT search - .unbind( events.replace( /\s+/g, ' ' ) ) + .unbind( events.replace( ts.regex.spaces, ' ' ) ) // remove the filter row even if refreshing, because the column might have been moved .find( '.' + tscss.filterRow ).remove(); if ( refreshing ) { return; } @@ -2756,7 +2793,7 @@ } }); - ts.filter = { + tsf = ts.filter = { // regex used in filter 'check' functions - not for general use and not documented regex: { @@ -2765,9 +2802,13 @@ filtered : /filtered/, // filtered (hidden) row class name; updated in the script type : /undefined|number/, // check type exact : /(^[\"\'=]+)|([\"\'=]+$)/g, // exact match (allow '==') - nondigit : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser) operators : /[<>=]/g, // replace operators - query : '(q|query)' // replace filter queries + query : '(q|query)', // replace filter queries + wild01 : /\?/g, // wild card match 0 or 1 + wild0More : /\*/g, // wild care match 0 or more + quote : /\"/g, + isNeg1 : /(>=?\s*-\d)/, + isNeg2 : /(<=?\s*\d)/ }, // function( c, data ) { } // c = table.config @@ -2784,27 +2825,27 @@ // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class ) types: { or : function( c, data, vars ) { - if ( /\|/.test( data.iFilter ) || ts.filter.regex.orSplit.test( data.filter ) ) { + if ( tsf.regex.orTest.test( data.iFilter ) || tsf.regex.orSplit.test( data.filter ) ) { var indx, filterMatched, query, regex, // duplicate data but split filter data2 = $.extend( {}, data ), index = data.index, parsed = data.parsed[ index ], - filter = data.filter.split( ts.filter.regex.orSplit ), - iFilter = data.iFilter.split( ts.filter.regex.orSplit ), + filter = data.filter.split( tsf.regex.orSplit ), + iFilter = data.iFilter.split( tsf.regex.orSplit ), len = filter.length; for ( indx = 0; indx < len; indx++ ) { data2.nestedFilters = true; - data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); - data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); - query = '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')'; + data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' ); + data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); + query = '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')'; try { // use try/catch, because query may not be a valid regex if "|" is contained within a partial regex search, // e.g "/(Alex|Aar" -> Uncaught SyntaxError: Invalid regular expression: /(/(Alex)/: Unterminated group regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); // filterMatched = data2.filter === '' && indx > 0 ? true // look for an exact match with the 'or' unless the 'filter-match' class is found - filterMatched = regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ); + filterMatched = regex.test( data2.exact ) || tsf.processTypes( c, data2, vars ); if ( filterMatched ) { return filterMatched; } @@ -2819,27 +2860,27 @@ }, // Look for an AND or && operator ( logical and ) and : function( c, data, vars ) { - if ( ts.filter.regex.andTest.test( data.filter ) ) { + if ( tsf.regex.andTest.test( data.filter ) ) { var indx, filterMatched, result, query, regex, // duplicate data but split filter data2 = $.extend( {}, data ), index = data.index, parsed = data.parsed[ index ], - filter = data.filter.split( ts.filter.regex.andSplit ), - iFilter = data.iFilter.split( ts.filter.regex.andSplit ), + filter = data.filter.split( tsf.regex.andSplit ), + iFilter = data.iFilter.split( tsf.regex.andSplit ), len = filter.length; for ( indx = 0; indx < len; indx++ ) { data2.nestedFilters = true; - data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); - data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); - query = ( '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' ) + data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' ); + data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); + query = ( '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' ) // replace wild cards since /(a*)/i will match anything - .replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ); + .replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' ); try { // use try/catch just in case RegExp is invalid regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); // look for an exact match with the 'and' unless the 'filter-match' class is found - result = ( regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ) ); + result = ( regex.test( data2.exact ) || tsf.processTypes( c, data2, vars ) ); if ( indx === 0 ) { filterMatched = result; } else { @@ -2856,10 +2897,10 @@ }, // Look for regex regex: function( c, data ) { - if ( ts.filter.regex.regex.test( data.filter ) ) { + if ( tsf.regex.regex.test( data.filter ) ) { var matches, // cache regex per column for optimal speed - regex = data.filter_regexCache[ data.index ] || ts.filter.regex.regex.exec( data.filter ), + regex = data.filter_regexCache[ data.index ] || tsf.regex.regex.exec( data.filter ), isRegex = regex instanceof RegExp; try { if ( !isRegex ) { @@ -2878,18 +2919,18 @@ // Look for operators >, >=, < or <= operators: function( c, data ) { // ignore empty strings... because '' < 10 is true - if ( /^[<>]=?/.test( data.iFilter ) && data.iExact !== '' ) { + if ( tsf.regex.operTest.test( data.iFilter ) && data.iExact !== '' ) { var cachedValue, result, txt, table = c.table, index = data.index, parsed = data.parsed[index], - query = ts.formatFloat( data.iFilter.replace( ts.filter.regex.operators, '' ), table ), + query = ts.formatFloat( data.iFilter.replace( tsf.regex.operators, '' ), table ), parser = c.parsers[index], savedSearch = query; // parse filter value in case we're comparing numbers ( dates ) if ( parsed || parser.type === 'numeric' ) { - txt = $.trim( '' + data.iFilter.replace( ts.filter.regex.operators, '' ) ); - result = ts.filter.parseFilter( c, txt, index, true ); + txt = $.trim( '' + data.iFilter.replace( tsf.regex.operators, '' ) ); + result = tsf.parseFilter( c, txt, index, true ); query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query; } // iExact may be numeric - see issue #149; @@ -2898,13 +2939,13 @@ typeof data.cache !== 'undefined' ) { cachedValue = data.cache; } else { - txt = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; + txt = isNaN( data.iExact ) ? data.iExact.replace( ts.regex.nondigit, '' ) : data.iExact; cachedValue = ts.formatFloat( txt, table ); } - if ( />/.test( data.iFilter ) ) { - result = />=/.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; - } else if ( /</.test( data.iFilter ) ) { - result = /<=/.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; + if ( tsf.regex.gtTest.test( data.iFilter ) ) { + result = tsf.regex.gteTest.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; + } else if ( tsf.regex.ltTest.test( data.iFilter ) ) { + result = tsf.regex.lteTest.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; } // keep showing all rows if nothing follows the operator if ( !result && savedSearch === '' ) { @@ -2916,13 +2957,13 @@ }, // Look for a not match notMatch: function( c, data ) { - if ( /^\!/.test( data.iFilter ) ) { + if ( tsf.regex.notTest.test( data.iFilter ) ) { var indx, txt = data.iFilter.replace( '!', '' ), - filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; - if ( ts.filter.regex.exact.test( filter ) ) { + filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + if ( tsf.regex.exact.test( filter ) ) { // look for exact not matches - see #628 - filter = filter.replace( ts.filter.regex.exact, '' ); + filter = filter.replace( tsf.regex.exact, '' ); return filter === '' ? true : $.trim( filter ) !== data.iExact; } else { indx = data.iExact.search( $.trim( filter ) ); @@ -2934,27 +2975,27 @@ // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric exact: function( c, data ) { /*jshint eqeqeq:false */ - if ( ts.filter.regex.exact.test( data.iFilter ) ) { - var txt = data.iFilter.replace( ts.filter.regex.exact, '' ), - filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + if ( tsf.regex.exact.test( data.iFilter ) ) { + var txt = data.iFilter.replace( tsf.regex.exact, '' ), + filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact; } return null; }, // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu! range : function( c, data ) { - if ( ts.filter.regex.toTest.test( data.iFilter ) ) { + if ( tsf.regex.toTest.test( data.iFilter ) ) { var result, tmp, range1, range2, table = c.table, index = data.index, parsed = data.parsed[index], // make sure the dash is for a range and not indicating a negative number - query = data.iFilter.split( ts.filter.regex.toSplit ); + query = data.iFilter.split( tsf.regex.toSplit ); - tmp = query[0].replace( ts.filter.regex.nondigit, '' ) || ''; - range1 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); - tmp = query[1].replace( ts.filter.regex.nondigit, '' ) || ''; - range2 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); + tmp = query[0].replace( ts.regex.nondigit, '' ) || ''; + range1 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table ); + tmp = query[1].replace( ts.regex.nondigit, '' ) || ''; + range2 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table ); // parse filter value in case we're comparing numbers ( dates ) if ( parsed || c.parsers[index].type === 'numeric' ) { result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index ); @@ -2965,7 +3006,7 @@ if ( ( parsed || c.parsers[ index ].type === 'numeric' ) && !isNaN( range1 ) && !isNaN( range2 ) ) { result = data.cache; } else { - tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; + tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.regex.nondigit, '' ) : data.iExact; result = ts.formatFloat( tmp, table ); } if ( range1 > range2 ) { @@ -2977,18 +3018,18 @@ }, // Look for wild card: ? = single, * = multiple, or | = logical OR wild : function( c, data ) { - if ( /[\?\*\|]/.test( data.iFilter ) ) { + if ( tsf.regex.wildOrTest.test( data.iFilter ) ) { var index = data.index, parsed = data.parsed[ index ], - query = '' + ( ts.filter.parseFilter( c, data.iFilter, index, parsed ) || '' ); + query = '' + ( tsf.parseFilter( c, data.iFilter, index, parsed ) || '' ); // look for an exact match with the 'or' unless the 'filter-match' class is found - if ( !/\?\*/.test( query ) && data.nestedFilters ) { + if ( !tsf.regex.wildTest.test( query ) && data.nestedFilters ) { query = data.isMatch ? query : '^(' + query + ')$'; } // parsing the filter may not work properly when using wildcards =/ try { return new RegExp( - query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ), + query.replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' ), c.widgetOptions.filter_ignoreCase ? 'i' : '' ) .test( data.exact ); @@ -3000,12 +3041,12 @@ }, // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license ) fuzzy: function( c, data ) { - if ( /^~/.test( data.iFilter ) ) { + if ( tsf.regex.fuzzyTest.test( data.iFilter ) ) { var indx, patternIndx = 0, len = data.iExact.length, txt = data.iFilter.slice( 1 ), - pattern = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + pattern = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; for ( indx = 0; indx < len; indx++ ) { if ( data.iExact[ indx ] === pattern[ patternIndx ] ) { patternIndx += 1; @@ -3028,7 +3069,7 @@ }, ts.language ); var options, string, txt, $header, column, filters, val, fxn, noSelect, - regex = ts.filter.regex; + regex = tsf.regex; c.$table.addClass( 'hasFilters' ); // define timers so using clearTimeout won't cause an undefined error @@ -3039,7 +3080,7 @@ wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; - val = '\\{' + ts.filter.regex.query + '\\}'; + val = '\\{' + tsf.regex.query + '\\}'; $.extend( regex, { child : new RegExp( c.cssChildRow ), filtered : new RegExp( wo.filter_filteredRow ), @@ -3048,9 +3089,20 @@ toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)', 'gi' ), andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), + orTest : /\|/, orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ), iQuery : new RegExp( val, 'i' ), - igQuery : new RegExp( val, 'ig' ) + igQuery : new RegExp( val, 'ig' ), + operTest : /^[<>]=?/, + gtTest : />/, + gteTest : />=/, + ltTest : /</, + lteTest : /<=/, + notTest : /^\!/, + wildOrTest : /[\?\*\|]/, + wildTest : /\?\*/, + fuzzyTest : /^~/, + exactTest : /[=\"\|!]/ }); // don't build filter row if columnFilters is false or all columns are set to 'filter-false' @@ -3058,7 +3110,7 @@ val = c.$headers.filter( '.filter-false, .parser-false' ).length; if ( wo.filter_columnFilters !== false && val !== c.$headers.length ) { // build filter row - ts.filter.buildRow( table, c, wo ); + tsf.buildRow( table, c, wo ); } txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' @@ -3071,13 +3123,13 @@ c.$table.find( '.' + tscss.filterRow ).toggleClass( wo.filter_filteredRow, val ); // fixes #450 if ( !/(search|filter)/.test( event.type ) ) { event.stopPropagation(); - ts.filter.buildDefault( table, true ); + tsf.buildDefault( table, true ); } if ( event.type === 'filterReset' ) { c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' ); - ts.filter.searching( table, [] ); + tsf.searching( table, [] ); } else if ( event.type === 'filterEnd' ) { - ts.filter.buildDefault( table, true ); + tsf.buildDefault( table, true ); } else { // send false argument to force a new search; otherwise if the filter hasn't changed, // it will return @@ -3091,7 +3143,7 @@ // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first // input ensures all inputs are updated when a search is triggered on the table // $( 'table' ).trigger( 'search', [...] ); - ts.filter.searching( table, filter, true ); + tsf.searching( table, filter, true ); } return false; }); @@ -3124,7 +3176,7 @@ noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); options = ''; if ( fxn === true && noSelect ) { - ts.filter.buildSelect( table, column ); + tsf.buildSelect( table, column ); } else if ( typeof fxn === 'object' && noSelect ) { // add custom drop down list for ( string in fxn ) { @@ -3157,7 +3209,7 @@ fxn = $.isFunction( txt ) ? true : ts.getColumnData( table, txt, column ); if ( fxn ) { // updating so the extra options are appended - ts.filter.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) ); + tsf.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) ); } } } @@ -3165,22 +3217,22 @@ } // not really updating, but if the column has both the 'filter-select' class & // filter_functions set to true, it would append the same options twice. - ts.filter.buildDefault( table, true ); + tsf.buildDefault( table, true ); - ts.filter.bindSearch( table, c.$table.find( '.' + tscss.filter ), true ); + tsf.bindSearch( table, c.$table.find( '.' + tscss.filter ), true ); if ( wo.filter_external ) { - ts.filter.bindSearch( table, wo.filter_external ); + tsf.bindSearch( table, wo.filter_external ); } if ( wo.filter_hideFilters ) { - ts.filter.hideFilters( table, c ); + tsf.hideFilters( table, c ); } // show processing icon if ( c.showProcessing ) { txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter ' ); c.$table - .unbind( txt.replace( /\s+/g, ' ' ) ) + .unbind( txt.replace( ts.regex.spaces, ' ' ) ) .bind( txt, function( event, columns ) { // only add processing to certain columns to all columns $header = ( columns ) ? @@ -3200,11 +3252,11 @@ // add default values txt = 'tablesorter-initialized pagerBeforeInitialized '.split( ' ' ).join( c.namespace + 'filter ' ); c.$table - .unbind( txt.replace( /\s+/g, ' ' ) ) + .unbind( txt.replace( ts.regex.spaces, ' ' ) ) .bind( txt, function() { // redefine 'wo' as it does not update properly inside this callback var wo = this.config.widgetOptions; - filters = ts.filter.setDefaults( table, c, wo ) || []; + filters = tsf.setDefaults( table, c, wo ) || []; if ( filters.length ) { // prevent delayInit from triggering a cache build if filters are empty if ( !( c.delayInit && filters.join( '' ) === '' ) ) { @@ -3215,7 +3267,7 @@ // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers setTimeout( function() { if ( !wo.filter_initialized ) { - ts.filter.filterInitComplete( c ); + tsf.filterInitComplete( c ); } }, 100 ); }); @@ -3223,7 +3275,7 @@ if ( c.pager && c.pager.initialized && !wo.filter_initialized ) { c.$table.trigger( 'filterFomatterUpdate' ); setTimeout( function() { - ts.filter.filterInitComplete( c ); + tsf.filterInitComplete( c ); }, 100 ); } }, @@ -3244,7 +3296,7 @@ completed = function() { wo.filter_initialized = true; c.$table.trigger( 'filterInit', c ); - ts.filter.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); + tsf.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); }; if ( $.isEmptyObject( wo.filter_formatter ) ) { completed(); @@ -3396,7 +3448,7 @@ // use data attribute instead of jQuery data since the head is cloned without including // the data/binding .attr( 'data-lastSearchTime', new Date().getTime() ) - .unbind( tmp.replace( /\s+/g, ' ' ) ) + .unbind( tmp.replace( ts.regex.spaces, ' ' ) ) // include change for select - fixes #473 .bind( 'keyup' + namespace, function( event ) { $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); @@ -3416,17 +3468,18 @@ return; } // change event = no delay; last true flag tells getFilters to skip newest timed input - ts.filter.searching( table, true, true ); + tsf.searching( table, true, true ); }) .bind( 'search change keypress '.split( ' ' ).join( namespace + ' ' ), function( event ) { - var column = $( this ).data( 'column' ); + // don't get cached data, in case data-column changes dynamically + var column = parseInt( $( this ).attr( 'data-column' ), 10 ); // don't allow 'change' event to process if the input value is the same - fixes #685 if ( event.which === 13 || event.type === 'search' || event.type === 'change' && this.value !== c.lastSearch[column] ) { event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - ts.filter.searching( table, false, true ); + tsf.searching( table, false, true ); } }); }, @@ -3436,11 +3489,11 @@ if ( typeof filter === 'undefined' || filter === true ) { // delay filtering wo.searchTimer = setTimeout( function() { - ts.filter.checkFilters( table, filter, skipFirst ); + tsf.checkFilters( table, filter, skipFirst ); }, wo.filter_liveSearch ? wo.filter_searchDelay : 10 ); } else { // skip delay - ts.filter.checkFilters( table, filter, skipFirst ); + tsf.checkFilters( table, filter, skipFirst ); } }, checkFilters: function( table, filter, skipFirst ) { @@ -3454,7 +3507,7 @@ // update cache if delayInit set & pager has initialized ( after user initiates a search ) if ( c.delayInit && c.pager && c.pager.initialized ) { c.$table.trigger( 'updateCache', [ function() { - ts.filter.checkFilters( table, false, skipFirst ); + tsf.checkFilters( table, false, skipFirst ); } ] ); } return; @@ -3485,11 +3538,11 @@ if ( c.showProcessing ) { // give it time for the processing icon to kick in setTimeout( function() { - ts.filter.findRows( table, filters, combinedFilters ); + tsf.findRows( table, filters, combinedFilters ); return false; }, 30 ); } else { - ts.filter.findRows( table, filters, combinedFilters ); + tsf.findRows( table, filters, combinedFilters ); return false; } }, @@ -3532,8 +3585,8 @@ }, defaultFilter: function( filter, mask ) { if ( filter === '' ) { return filter; } - var regex = ts.filter.regex.iQuery, - maskLen = mask.match( ts.filter.regex.igQuery ).length, + var regex = tsf.regex.iQuery, + maskLen = mask.match( tsf.regex.igQuery ).length, query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ], len = query.length - 1, indx = 0, @@ -3569,7 +3622,10 @@ // & don't target 'all' column inputs if they don't exist targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, columns = [], - val = $.trim( ts.filter.getLatestSearch( $input ).attr( 'data-column' ) || '' ); + val = $.trim( tsf.getLatestSearch( $input ).attr( 'data-column' ) || '' ); + if ( !/[,-]/.test(val) && val.length === 1 ) { + return parseInt( val, 10 ); + } // process column range if ( targets && /-/.test( val ) ) { ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); @@ -3616,9 +3672,9 @@ var ffxn, filterMatched = null, matches = null; - for ( ffxn in ts.filter.types ) { + for ( ffxn in tsf.types ) { if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) { - matches = ts.filter.types[ffxn]( c, data, vars ); + matches = tsf.types[ffxn]( c, data, vars ); if ( matches !== null ) { filterMatched = matches; } @@ -3627,16 +3683,23 @@ return filterMatched; }, processRow: function( c, data, vars ) { - var columnIndex, hasSelect, result, val, filterMatched, + var hasSelect, result, val, filterMatched, fxn, ffxn, txt, - regex = ts.filter.regex, + regex = tsf.regex, wo = c.widgetOptions, - showRow = true; + showRow = true, + + // if wo.filter_$anyMatch data-column attribute is changed dynamically + // we don't want to do an "anyMatch" search on one column using data + // for the entire row - see #998 + columnIndex = wo.filter_$anyMatch && wo.filter_$anyMatch.length ? + // look for multiple columns '1-3,4-6,8' + tsf.multipleColumns( c, wo.filter_$anyMatch ) : + []; + data.$cells = data.$row.children(); - if ( data.anyMatchFlag ) { - // look for multiple columns '1-3,4-6,8' - columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); + if ( data.anyMatchFlag && columnIndex.length > 1 ) { data.anyMatch = true; data.isMatch = true; data.rowArray = data.$cells.map( function( i ) { @@ -3660,7 +3723,7 @@ data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' ); vars.excludeMatch = vars.noAnyMatch; - filterMatched = ts.filter.processTypes( c, data, vars ); + filterMatched = tsf.processTypes( c, data, vars ); if ( filterMatched !== null ) { showRow = filterMatched; @@ -3721,7 +3784,7 @@ val = true; if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) { - data.filter = ts.filter.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); + data.filter = tsf.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); // val is used to indicate that a filter select is using a default filter; // so we override the exact & partial matches val = false; @@ -3752,13 +3815,13 @@ if ( filterMatched === null ) { // cycle through the different filters // filters return a boolean or null if nothing matches - filterMatched = ts.filter.processTypes( c, data, vars ); + filterMatched = tsf.processTypes( c, data, vars ); if ( filterMatched !== null ) { result = filterMatched; // Look for match, and add child row data for matching } else { txt = ( data.iExact + data.childRowText ) - .indexOf( ts.filter.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) ); + .indexOf( tsf.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) ); result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); } } else { @@ -3778,7 +3841,7 @@ isChild, childRow, lastSearch, showRow, time, val, indx, notFiltered, searchFiltered, query, injected, res, id, txt, storedFilters = $.extend( [], filters ), - regex = ts.filter.regex, + regex = tsf.regex, c = table.config, wo = c.widgetOptions, // data object passed to filters; anyMatch is a flag for the filters @@ -3855,7 +3918,7 @@ data.anyMatchFlag = true; data.anyMatchFilter = '' + ( filters[ c.columns ] || - wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || + wo.filter_$anyMatch && tsf.getLatestSearch( wo.filter_$anyMatch ).val() || '' ); if ( wo.filter_columnAnyMatch ) { @@ -3897,10 +3960,10 @@ // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string !regex.alreadyFiltered.test( val ) && // if we are not doing exact matches, using '|' ( logical or ) or not '!' - !/[=\"\|!]/.test( val ) && + !regex.exactTest.test( val ) && // don't search only filtered if the value is negative // ( '> -10' => '> -100' will ignore hidden rows ) - !( /(>=?\s*-\d)/.test( val ) || /(<=?\s*\d)/.test( val ) ) && + !( regex.isNeg1.test( val ) || regex.isNeg2.test( val ) ) && // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 !( val !== '' && c.$filters && c.$filters.eq( indx ).find( 'select' ).length && !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); @@ -3919,7 +3982,7 @@ data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter ); } if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultAnyFilter ) ) { - data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter ); + data.anyMatchFilter = tsf.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter ); // clear search filtered flag because default filters are not saved to the last search searchFiltered = false; } @@ -3962,7 +4025,7 @@ ''; } - showRow = ts.filter.processRow( c, data, vars ); + showRow = tsf.processRow( c, data, vars ); childRow = rowData.$row.filter( ':gt( 0 )' ); if ( wo.filter_childRows && childRow.length ) { @@ -3973,7 +4036,7 @@ data.cacheArray = rowData.child[ indx ]; data.rawArray = data.cacheArray; // use OR comparison on child rows - showRow = showRow || ts.filter.processRow( c, data, vars ); + showRow = showRow || tsf.processRow( c, data, vars ); } } childRow.toggleClass( wo.filter_filteredRow, !showRow ); @@ -4035,7 +4098,7 @@ } if ( arry === false ) { // fall back to original method - arry = ts.filter.getOptions( table, column, onlyAvail ); + arry = tsf.getOptions( table, column, onlyAvail ); } // get unique elements and sort the list @@ -4147,13 +4210,13 @@ // nothing included in arry ( external source ), so get the options from // filter_selectSource or column data if ( typeof arry === 'undefined' || arry === '' ) { - arry = ts.filter.getOptionSource( table, column, onlyAvail ); + arry = tsf.getOptionSource( table, column, onlyAvail ); } if ( $.isArray( arry ) ) { // build option list for ( indx = 0; indx < arry.length; indx++ ) { - txt = arry[indx] = ( '' + arry[indx] ).replace( /\"/g, '"' ); + txt = arry[indx] = ( '' + arry[indx] ).replace( tsf.regex.quote, '"' ); val = txt; // allow including a symbol in the selectSource array // 'a-z|A through Z' so that 'a-z' becomes the option value @@ -4209,7 +4272,7 @@ // look for the filter-select class; build/update it if found if ( ( $header.hasClass( 'filter-select' ) || ts.getColumnData( table, wo.filter_functions, columnIndex ) === true ) && noSelect ) { - ts.filter.buildSelect( table, columnIndex, '', updating, $header.hasClass( wo.filter_onlyAvail ) ); + tsf.buildSelect( table, columnIndex, '', updating, $header.hasClass( wo.filter_onlyAvail ) ); } } } @@ -4245,7 +4308,7 @@ $column = $filters.filter( cols ); if ( $column.length ) { // move the latest search to the first slot in the array - $column = ts.filter.getLatestSearch( $column ); + $column = tsf.getLatestSearch( $column ); if ( $.isArray( setFilters ) ) { // skip first ( latest input ) to maintain cursor position while typing if ( skipFirst && $column.length > 1 ) { @@ -4295,7 +4358,7 @@ // ensure new set filters are applied, even if the search is the same c.lastCombinedFilter = null; c.lastSearch = []; - ts.filter.searching( c.table, filter, skipFirst ); + tsf.searching( c.table, filter, skipFirst ); c.$table.trigger( 'filterFomatterUpdate' ); } return !!valid; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 3969639..9b1c19a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.23.1 *//* +/*! TableSorter (FORK) v2.23.2 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -26,7 +26,7 @@ var ts = this; - ts.version = '2.23.1'; + ts.version = '2.23.2'; ts.parsers = []; ts.widgets = []; @@ -151,6 +151,15 @@ nextNone : 'activate to remove the sort' }; + ts.regex = { + templateContent : /\{content\}/g, + templateIcon : /\{icon\}/g, + templateName : /\{name\}/i, + spaces : /\s+/g, + nonWord : /\W/g, + formElements : /(input|select|button|textarea)/i + }; + // These methods can be applied on table.config instance ts.instanceMethods = {}; @@ -443,7 +452,9 @@ // if headerTemplate is empty, don't reformat the header cell if ( c.headerTemplate !== '' && !$t.find('.' + ts.css.headerIn).length ) { // set up header template - t = c.headerTemplate.replace(/\{content\}/g, $t.html()).replace(/\{icon\}/g, $t.find('.' + ts.css.icon).length ? '' : i); + t = c.headerTemplate + .replace(ts.regex.templateContent, $t.html()) + .replace(ts.regex.templateIcon, $t.find('.' + ts.css.icon).length ? '' : i); if (c.onRenderTemplate) { h = c.onRenderTemplate.apply( $t, [ index, t ] ); if (h && typeof h === 'string') { t = h; } // only change t if something is returned @@ -856,7 +867,7 @@ .join( c.namespace + ' ' ); // apply easy methods that trigger bound events $table - .unbind( events.replace( /\s+/g, ' ' ) ) + .unbind( events.replace( ts.regex.spaces, ' ' ) ) .bind( 'sortReset' + c.namespace, function( e, callback ) { e.stopPropagation(); // using this.config to ensure functions are getting a non-cached version of the config @@ -1003,7 +1014,7 @@ c.namespace = '.tablesorter' + Math.random().toString(16).slice(2); } else { // make sure namespace starts with a period & doesn't have weird characters - c.namespace = '.' + c.namespace.replace(/\W/g, ''); + c.namespace = '.' + c.namespace.replace(ts.regex.nonWord, ''); } c.$table.children().children('tr').attr('role', 'row'); @@ -1231,7 +1242,7 @@ } } t = ( c.pointerDown + ' ' + c.pointerUp + ' ' + c.pointerClick + ' sort keyup ' ) - .replace(/\s+/g, ' ') + .replace(ts.regex.spaces, ' ') .split(' ') .join(c.namespace + ' '); // apply event handling to headers and/or additional headers (stickyheaders, scroller, etc) @@ -1265,7 +1276,7 @@ } downTarget = null; // prevent sort being triggered on form elements - if ( /(input|select|button|textarea)/i.test(e.target.nodeName) || + if ( ts.regex.formElements.test(e.target.nodeName) || // nosort class name, or elements within a nosort container $target.hasClass(c.cssNoSort) || $target.parents('.' + c.cssNoSort).length > 0 || // elements within a button @@ -1551,13 +1562,13 @@ .join(c.namespace + ' '); $t .removeData('tablesorter') - .unbind( events.replace(/\s+/g, ' ') ); + .unbind( events.replace(ts.regex.spaces, ' ') ); c.$headers.add($f) .removeClass( [ ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone ].join(' ') ) .removeAttr('data-column') .removeAttr('aria-label') .attr('aria-disabled', 'true'); - $r.find(c.selectorSort).unbind( ('mousedown mouseup keypress '.split(' ').join(c.namespace + ' ')).replace(/\s+/g, ' ') ); + $r.find(c.selectorSort).unbind( ('mousedown mouseup keypress '.split(' ').join(c.namespace + ' ')).replace(ts.regex.spaces, ' ') ); ts.restoreHeaders(table); $t.toggleClass(ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme, removeClasses === false); // clear flag in case the plugin is initialized again @@ -1573,11 +1584,9 @@ // *** sort functions *** // regex used in natural sort - ts.regex = { - chunk : /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi, // chunk/tokenize numbers & letters - chunks: /(^\\0|\\0$)/, // replace chunks @ ends - hex: /^0x[0-9a-f]+$/i // hex - }; + ts.regex.chunk = /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi; // chunk/tokenize numbers & letters + ts.regex.chunks = /(^\\0|\\0$)/; // replace chunks @ ends + ts.regex.hex = /^0x[0-9a-f]+$/i; // hex // Natural sort - https://github.com/overset/javascript-natural-sort (date sorting removed) // this function will only accept strings, or you'll see 'TypeError: undefined is not a function' @@ -1804,7 +1813,7 @@ if (c.debug) { time = new Date(); } // look for widgets to apply from in table class // stop using \b otherwise this matches 'ui-widget-content' & adds 'content' widget - wd = new RegExp( '\\s' + c.widgetClass.replace( /\{name\}/i, '([\\w-]+)' ) + '\\s', 'g' ); + wd = new RegExp( '\\s' + c.widgetClass.replace( ts.regex.templateName, '([\\w-]+)' ) + '\\s', 'g' ); if ( tableClass.match( wd ) ) { // extract out the widget id from the table class (widget id's can include dashes) w = tableClass.match( wd ); @@ -2030,6 +2039,10 @@ return $.trim(val); }; + ts.regex.comma = /,/g; + ts.regex.digitNonUS = /[\s|\.]/g; + ts.regex.digitNegativeTest = /^\s*\([.\d]+\)/; + ts.regex.digitNegativeReplace = /^\s*\(([.\d]+)\)/; ts.formatFloat = function(s, table) { if (typeof s !== 'string' || s === '') { return s; } // allow using formatFloat without a table; defaults to US number format @@ -2038,24 +2051,28 @@ typeof table !== 'undefined' ? table : true; if (t) { // US Format - 1,234,567.89 -> 1234567.89 - s = s.replace(/,/g, ''); + s = s.replace(ts.regex.comma, ''); } else { // German Format = 1.234.567,89 -> 1234567.89 // French Format = 1 234 567,89 -> 1234567.89 - s = s.replace(/[\s|\.]/g, '').replace(/,/g, '.'); + s = s.replace(ts.regex.digitNonUS, '').replace(ts.regex.comma, '.'); } - if (/^\s*\([.\d]+\)/.test(s)) { + if (ts.regex.digitNegativeTest.test(s)) { // make (#) into a negative number -> (10) = -10 - s = s.replace(/^\s*\(([.\d]+)\)/, '-$1'); + s = s.replace(ts.regex.digitNegativeReplace, '-$1'); } i = parseFloat(s); // return the text instead of zero return isNaN(i) ? $.trim(s) : i; }; + ts.regex.digitTest = /^[\-+(]?\d+[)]?$/; + ts.regex.digitReplace = /[,.'"\s]/g; ts.isDigit = function(s) { // replace all unwanted chars and match - return isNaN(s) ? (/^[\-+(]?\d+[)]?$/).test(s.toString().replace(/[,.'"\s]/g, '')) : s !== ''; + return isNaN(s) ? + ts.regex.digitTest.test( s.toString().replace( ts.regex.digitReplace, '' ) ) : + s !== ''; }; }() @@ -2114,65 +2131,76 @@ type: 'text' }); + ts.regex.nondigit = /[^\w,. \-()]/g; ts.addParser({ id: 'digit', is: function(s) { return ts.isDigit(s); }, format: function(s, table) { - var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ''), table); + var n = ts.formatFloat((s || '').replace(ts.regex.nondigit, ''), table); return s && typeof n === 'number' ? n : s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; }, type: 'numeric' }); + ts.regex.currencyReplace = /[+\-,. ]/g; + ts.regex.currencyTest = /^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/; ts.addParser({ id: 'currency', is: function(s) { - s = (s || '').replace(/[+\-,. ]/g, ''); + s = (s || '').replace(ts.regex.currencyReplace, ''); // test for £$€¤¥¢ - return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test(s); + return ts.regex.currencyTest.test(s); }, format: function(s, table) { - var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ''), table); + var n = ts.formatFloat((s || '').replace(ts.regex.nondigit, ''), table); return s && typeof n === 'number' ? n : s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; }, type: 'numeric' }); + // too many protocols to add them all https://en.wikipedia.org/wiki/URI_scheme + // now, this regex can be updated before initialization + ts.regex.urlProtocolTest = /^(https?|ftp|file):\/\//; + ts.regex.urlProtocolReplace = /(https?|ftp|file):\/\//; ts.addParser({ id: 'url', is: function(s) { - return (/^(https?|ftp|file):\/\//).test(s); + return ts.regex.urlProtocolTest.test(s); }, format: function(s) { - return s ? $.trim(s.replace(/(https?|ftp|file):\/\//, '')) : s; + return s ? $.trim(s.replace(ts.regex.urlProtocolReplace, '')) : s; }, parsed : true, // filter widget flag type: 'text' }); + ts.regex.dash = /-/g; + ts.regex.isoDate = /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}/; ts.addParser({ id: 'isoDate', is: function(s) { - return (/^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}/).test(s); + return ts.regex.isoDate.test(s); }, format: function(s, table) { - var date = s ? new Date( s.replace(/-/g, '/') ) : s; + var date = s ? new Date( s.replace(ts.regex.dash, '/') ) : s; return date instanceof Date && isFinite(date) ? date.getTime() : s; }, type: 'numeric' }); + ts.regex.percent = /%/g; + ts.regex.percentTest = /(\d\s*?%|%\s*?\d)/; ts.addParser({ id: 'percent', is: function(s) { - return (/(\d\s*?%|%\s*?\d)/).test(s) && s.length < 15; + return ts.regex.percentTest.test(s) && s.length < 15; }, format: function(s, table) { - return s ? ts.formatFloat(s.replace(/%/g, ''), table) : s; + return s ? ts.formatFloat(s.replace(ts.regex.percent, ''), table) : s; }, type: 'numeric' }); @@ -2190,27 +2218,35 @@ type: 'text' }); + ts.regex.dateReplace = /(\S)([AP]M)$/i; // used by usLongDate & time parser + ts.regex.usLongDateTest1 = /^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i; + ts.regex.usLongDateTest2 = /^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i; ts.addParser({ id: 'usLongDate', is: function(s) { // two digit years are not allowed cross-browser // Jan 01, 2013 12:34:56 PM or 01 Jan 2013 - return (/^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i).test(s) || - (/^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i).test(s); + return ts.regex.usLongDateTest1.test(s) || ts.regex.usLongDateTest2.test(s); }, format: function(s, table) { - var date = s ? new Date( s.replace(/(\S)([AP]M)$/i, '$1 $2') ) : s; + var date = s ? new Date( s.replace(ts.regex.dateReplace, '$1 $2') ) : s; return date instanceof Date && isFinite(date) ? date.getTime() : s; }, type: 'numeric' }); + // testing for ##-##-#### or ####-##-##, so it's not perfect; time can be included + ts.regex.shortDateTest = /(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/; + // escaped "-" because JSHint in Firefox was showing it as an error + ts.regex.shortDateReplace = /[\-.,]/g; + // XXY covers MDY & DMY formats + ts.regex.shortDateXXY = /(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/; + ts.regex.shortDateYMD = /(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/; ts.addParser({ id: 'shortDate', // 'mmddyyyy', 'ddmmyyyy' or 'yyyymmdd' is: function(s) { - s = (s || '').replace(/\s+/g, ' ').replace(/[\-.,]/g, '/'); - // testing for ##-##-#### or ####-##-##, so it's not perfect; time can be included - return (/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/).test(s); + s = (s || '').replace(ts.regex.spaces, ' ').replace(ts.regex.shortDateReplace, '/'); + return ts.regex.shortDateTest.test(s); }, format: function(s, table, cell, cellIndex) { if (s) { @@ -2220,14 +2256,13 @@ format = ci.length && ci[0].dateFormat || ts.getData( ci, ts.getColumnData( table, c.headers, cellIndex ), 'dateFormat') || c.dateFormat; - // escaped "-" because JSHint in Firefox was showing it as an error - d = s.replace(/\s+/g, ' ').replace(/[\-.,]/g, '/'); + d = s.replace(ts.regex.spaces, ' ').replace(ts.regex.shortDateReplace, '/'); if (format === 'mmddyyyy') { - d = d.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, '$3/$1/$2'); + d = d.replace(ts.regex.shortDateXXY, '$3/$1/$2'); } else if (format === 'ddmmyyyy') { - d = d.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, '$3/$2/$1'); + d = d.replace(ts.regex.shortDateXXY, '$3/$2/$1'); } else if (format === 'yyyymmdd') { - d = d.replace(/(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/, '$1/$2/$3'); + d = d.replace(ts.regex.shortDateYMD, '$1/$2/$3'); } date = new Date(d); return date instanceof Date && isFinite(date) ? date.getTime() : s; @@ -2237,13 +2272,14 @@ type: 'numeric' }); + ts.regex.timeTest = /^(([0-2]?\d:[0-5]\d)|([0-1]?\d:[0-5]\d\s?([AP]M)))$/i; ts.addParser({ id: 'time', is: function(s) { - return (/^(([0-2]?\d:[0-5]\d)|([0-1]?\d:[0-5]\d\s?([AP]M)))$/i).test(s); + return ts.regex.timeTest.test(s); }, format: function(s, table) { - var date = s ? new Date( '2000/01/01 ' + s.replace(/(\S)([AP]M)$/i, '$1 $2') ) : s; + var date = s ? new Date( '2000/01/01 ' + s.replace(ts.regex.dateReplace, '$1 $2') ) : s; return date instanceof Date && isFinite(date) ? date.getTime() : s; }, type: 'numeric' diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index a9e8de9..b636971 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 08-19-2015 (v2.23.1)*/ +/*! tablesorter (FORK) - updated 08-23-2015 (v2.23.2)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -372,14 +372,15 @@ })(jQuery); -/*! Widget: filter - updated 8/17/2015 (v2.23.0) *//* +/*! Widget: filter - updated 8/23/2015 (v2.23.2) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ ;( function ( $ ) { 'use strict'; - var ts = $.tablesorter || {}, - tscss = ts.css; + var tsf, + ts = $.tablesorter || {}, + tscss = ts.css; $.extend( tscss, { filterRow : 'tablesorter-filter-row', @@ -423,7 +424,7 @@ }, format: function( table, c, wo ) { if ( !c.$table.hasClass( 'hasFilters' ) ) { - ts.filter.init( table, c, wo ); + tsf.init( table, c, wo ); } }, remove: function( table, c, wo, refreshing ) { @@ -435,7 +436,7 @@ $table .removeClass( 'hasFilters' ) // add .tsfilter namespace to all BUT search - .unbind( events.replace( /\s+/g, ' ' ) ) + .unbind( events.replace( ts.regex.spaces, ' ' ) ) // remove the filter row even if refreshing, because the column might have been moved .find( '.' + tscss.filterRow ).remove(); if ( refreshing ) { return; } @@ -450,7 +451,7 @@ } }); - ts.filter = { + tsf = ts.filter = { // regex used in filter 'check' functions - not for general use and not documented regex: { @@ -459,9 +460,13 @@ filtered : /filtered/, // filtered (hidden) row class name; updated in the script type : /undefined|number/, // check type exact : /(^[\"\'=]+)|([\"\'=]+$)/g, // exact match (allow '==') - nondigit : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser) operators : /[<>=]/g, // replace operators - query : '(q|query)' // replace filter queries + query : '(q|query)', // replace filter queries + wild01 : /\?/g, // wild card match 0 or 1 + wild0More : /\*/g, // wild care match 0 or more + quote : /\"/g, + isNeg1 : /(>=?\s*-\d)/, + isNeg2 : /(<=?\s*\d)/ }, // function( c, data ) { } // c = table.config @@ -478,27 +483,27 @@ // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class ) types: { or : function( c, data, vars ) { - if ( /\|/.test( data.iFilter ) || ts.filter.regex.orSplit.test( data.filter ) ) { + if ( tsf.regex.orTest.test( data.iFilter ) || tsf.regex.orSplit.test( data.filter ) ) { var indx, filterMatched, query, regex, // duplicate data but split filter data2 = $.extend( {}, data ), index = data.index, parsed = data.parsed[ index ], - filter = data.filter.split( ts.filter.regex.orSplit ), - iFilter = data.iFilter.split( ts.filter.regex.orSplit ), + filter = data.filter.split( tsf.regex.orSplit ), + iFilter = data.iFilter.split( tsf.regex.orSplit ), len = filter.length; for ( indx = 0; indx < len; indx++ ) { data2.nestedFilters = true; - data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); - data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); - query = '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')'; + data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' ); + data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); + query = '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')'; try { // use try/catch, because query may not be a valid regex if "|" is contained within a partial regex search, // e.g "/(Alex|Aar" -> Uncaught SyntaxError: Invalid regular expression: /(/(Alex)/: Unterminated group regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); // filterMatched = data2.filter === '' && indx > 0 ? true // look for an exact match with the 'or' unless the 'filter-match' class is found - filterMatched = regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ); + filterMatched = regex.test( data2.exact ) || tsf.processTypes( c, data2, vars ); if ( filterMatched ) { return filterMatched; } @@ -513,27 +518,27 @@ }, // Look for an AND or && operator ( logical and ) and : function( c, data, vars ) { - if ( ts.filter.regex.andTest.test( data.filter ) ) { + if ( tsf.regex.andTest.test( data.filter ) ) { var indx, filterMatched, result, query, regex, // duplicate data but split filter data2 = $.extend( {}, data ), index = data.index, parsed = data.parsed[ index ], - filter = data.filter.split( ts.filter.regex.andSplit ), - iFilter = data.iFilter.split( ts.filter.regex.andSplit ), + filter = data.filter.split( tsf.regex.andSplit ), + iFilter = data.iFilter.split( tsf.regex.andSplit ), len = filter.length; for ( indx = 0; indx < len; indx++ ) { data2.nestedFilters = true; - data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); - data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); - query = ( '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' ) + data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' ); + data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); + query = ( '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' ) // replace wild cards since /(a*)/i will match anything - .replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ); + .replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' ); try { // use try/catch just in case RegExp is invalid regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); // look for an exact match with the 'and' unless the 'filter-match' class is found - result = ( regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ) ); + result = ( regex.test( data2.exact ) || tsf.processTypes( c, data2, vars ) ); if ( indx === 0 ) { filterMatched = result; } else { @@ -550,10 +555,10 @@ }, // Look for regex regex: function( c, data ) { - if ( ts.filter.regex.regex.test( data.filter ) ) { + if ( tsf.regex.regex.test( data.filter ) ) { var matches, // cache regex per column for optimal speed - regex = data.filter_regexCache[ data.index ] || ts.filter.regex.regex.exec( data.filter ), + regex = data.filter_regexCache[ data.index ] || tsf.regex.regex.exec( data.filter ), isRegex = regex instanceof RegExp; try { if ( !isRegex ) { @@ -572,18 +577,18 @@ // Look for operators >, >=, < or <= operators: function( c, data ) { // ignore empty strings... because '' < 10 is true - if ( /^[<>]=?/.test( data.iFilter ) && data.iExact !== '' ) { + if ( tsf.regex.operTest.test( data.iFilter ) && data.iExact !== '' ) { var cachedValue, result, txt, table = c.table, index = data.index, parsed = data.parsed[index], - query = ts.formatFloat( data.iFilter.replace( ts.filter.regex.operators, '' ), table ), + query = ts.formatFloat( data.iFilter.replace( tsf.regex.operators, '' ), table ), parser = c.parsers[index], savedSearch = query; // parse filter value in case we're comparing numbers ( dates ) if ( parsed || parser.type === 'numeric' ) { - txt = $.trim( '' + data.iFilter.replace( ts.filter.regex.operators, '' ) ); - result = ts.filter.parseFilter( c, txt, index, true ); + txt = $.trim( '' + data.iFilter.replace( tsf.regex.operators, '' ) ); + result = tsf.parseFilter( c, txt, index, true ); query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query; } // iExact may be numeric - see issue #149; @@ -592,13 +597,13 @@ typeof data.cache !== 'undefined' ) { cachedValue = data.cache; } else { - txt = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; + txt = isNaN( data.iExact ) ? data.iExact.replace( ts.regex.nondigit, '' ) : data.iExact; cachedValue = ts.formatFloat( txt, table ); } - if ( />/.test( data.iFilter ) ) { - result = />=/.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; - } else if ( /</.test( data.iFilter ) ) { - result = /<=/.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; + if ( tsf.regex.gtTest.test( data.iFilter ) ) { + result = tsf.regex.gteTest.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; + } else if ( tsf.regex.ltTest.test( data.iFilter ) ) { + result = tsf.regex.lteTest.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; } // keep showing all rows if nothing follows the operator if ( !result && savedSearch === '' ) { @@ -610,13 +615,13 @@ }, // Look for a not match notMatch: function( c, data ) { - if ( /^\!/.test( data.iFilter ) ) { + if ( tsf.regex.notTest.test( data.iFilter ) ) { var indx, txt = data.iFilter.replace( '!', '' ), - filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; - if ( ts.filter.regex.exact.test( filter ) ) { + filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + if ( tsf.regex.exact.test( filter ) ) { // look for exact not matches - see #628 - filter = filter.replace( ts.filter.regex.exact, '' ); + filter = filter.replace( tsf.regex.exact, '' ); return filter === '' ? true : $.trim( filter ) !== data.iExact; } else { indx = data.iExact.search( $.trim( filter ) ); @@ -628,27 +633,27 @@ // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric exact: function( c, data ) { /*jshint eqeqeq:false */ - if ( ts.filter.regex.exact.test( data.iFilter ) ) { - var txt = data.iFilter.replace( ts.filter.regex.exact, '' ), - filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + if ( tsf.regex.exact.test( data.iFilter ) ) { + var txt = data.iFilter.replace( tsf.regex.exact, '' ), + filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact; } return null; }, // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu! range : function( c, data ) { - if ( ts.filter.regex.toTest.test( data.iFilter ) ) { + if ( tsf.regex.toTest.test( data.iFilter ) ) { var result, tmp, range1, range2, table = c.table, index = data.index, parsed = data.parsed[index], // make sure the dash is for a range and not indicating a negative number - query = data.iFilter.split( ts.filter.regex.toSplit ); + query = data.iFilter.split( tsf.regex.toSplit ); - tmp = query[0].replace( ts.filter.regex.nondigit, '' ) || ''; - range1 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); - tmp = query[1].replace( ts.filter.regex.nondigit, '' ) || ''; - range2 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); + tmp = query[0].replace( ts.regex.nondigit, '' ) || ''; + range1 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table ); + tmp = query[1].replace( ts.regex.nondigit, '' ) || ''; + range2 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table ); // parse filter value in case we're comparing numbers ( dates ) if ( parsed || c.parsers[index].type === 'numeric' ) { result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index ); @@ -659,7 +664,7 @@ if ( ( parsed || c.parsers[ index ].type === 'numeric' ) && !isNaN( range1 ) && !isNaN( range2 ) ) { result = data.cache; } else { - tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; + tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.regex.nondigit, '' ) : data.iExact; result = ts.formatFloat( tmp, table ); } if ( range1 > range2 ) { @@ -671,18 +676,18 @@ }, // Look for wild card: ? = single, * = multiple, or | = logical OR wild : function( c, data ) { - if ( /[\?\*\|]/.test( data.iFilter ) ) { + if ( tsf.regex.wildOrTest.test( data.iFilter ) ) { var index = data.index, parsed = data.parsed[ index ], - query = '' + ( ts.filter.parseFilter( c, data.iFilter, index, parsed ) || '' ); + query = '' + ( tsf.parseFilter( c, data.iFilter, index, parsed ) || '' ); // look for an exact match with the 'or' unless the 'filter-match' class is found - if ( !/\?\*/.test( query ) && data.nestedFilters ) { + if ( !tsf.regex.wildTest.test( query ) && data.nestedFilters ) { query = data.isMatch ? query : '^(' + query + ')$'; } // parsing the filter may not work properly when using wildcards =/ try { return new RegExp( - query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ), + query.replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' ), c.widgetOptions.filter_ignoreCase ? 'i' : '' ) .test( data.exact ); @@ -694,12 +699,12 @@ }, // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license ) fuzzy: function( c, data ) { - if ( /^~/.test( data.iFilter ) ) { + if ( tsf.regex.fuzzyTest.test( data.iFilter ) ) { var indx, patternIndx = 0, len = data.iExact.length, txt = data.iFilter.slice( 1 ), - pattern = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + pattern = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; for ( indx = 0; indx < len; indx++ ) { if ( data.iExact[ indx ] === pattern[ patternIndx ] ) { patternIndx += 1; @@ -722,7 +727,7 @@ }, ts.language ); var options, string, txt, $header, column, filters, val, fxn, noSelect, - regex = ts.filter.regex; + regex = tsf.regex; c.$table.addClass( 'hasFilters' ); // define timers so using clearTimeout won't cause an undefined error @@ -733,7 +738,7 @@ wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; - val = '\\{' + ts.filter.regex.query + '\\}'; + val = '\\{' + tsf.regex.query + '\\}'; $.extend( regex, { child : new RegExp( c.cssChildRow ), filtered : new RegExp( wo.filter_filteredRow ), @@ -742,9 +747,20 @@ toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)', 'gi' ), andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), + orTest : /\|/, orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ), iQuery : new RegExp( val, 'i' ), - igQuery : new RegExp( val, 'ig' ) + igQuery : new RegExp( val, 'ig' ), + operTest : /^[<>]=?/, + gtTest : />/, + gteTest : />=/, + ltTest : /</, + lteTest : /<=/, + notTest : /^\!/, + wildOrTest : /[\?\*\|]/, + wildTest : /\?\*/, + fuzzyTest : /^~/, + exactTest : /[=\"\|!]/ }); // don't build filter row if columnFilters is false or all columns are set to 'filter-false' @@ -752,7 +768,7 @@ val = c.$headers.filter( '.filter-false, .parser-false' ).length; if ( wo.filter_columnFilters !== false && val !== c.$headers.length ) { // build filter row - ts.filter.buildRow( table, c, wo ); + tsf.buildRow( table, c, wo ); } txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' @@ -765,13 +781,13 @@ c.$table.find( '.' + tscss.filterRow ).toggleClass( wo.filter_filteredRow, val ); // fixes #450 if ( !/(search|filter)/.test( event.type ) ) { event.stopPropagation(); - ts.filter.buildDefault( table, true ); + tsf.buildDefault( table, true ); } if ( event.type === 'filterReset' ) { c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' ); - ts.filter.searching( table, [] ); + tsf.searching( table, [] ); } else if ( event.type === 'filterEnd' ) { - ts.filter.buildDefault( table, true ); + tsf.buildDefault( table, true ); } else { // send false argument to force a new search; otherwise if the filter hasn't changed, // it will return @@ -785,7 +801,7 @@ // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first // input ensures all inputs are updated when a search is triggered on the table // $( 'table' ).trigger( 'search', [...] ); - ts.filter.searching( table, filter, true ); + tsf.searching( table, filter, true ); } return false; }); @@ -818,7 +834,7 @@ noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); options = ''; if ( fxn === true && noSelect ) { - ts.filter.buildSelect( table, column ); + tsf.buildSelect( table, column ); } else if ( typeof fxn === 'object' && noSelect ) { // add custom drop down list for ( string in fxn ) { @@ -851,7 +867,7 @@ fxn = $.isFunction( txt ) ? true : ts.getColumnData( table, txt, column ); if ( fxn ) { // updating so the extra options are appended - ts.filter.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) ); + tsf.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) ); } } } @@ -859,22 +875,22 @@ } // not really updating, but if the column has both the 'filter-select' class & // filter_functions set to true, it would append the same options twice. - ts.filter.buildDefault( table, true ); + tsf.buildDefault( table, true ); - ts.filter.bindSearch( table, c.$table.find( '.' + tscss.filter ), true ); + tsf.bindSearch( table, c.$table.find( '.' + tscss.filter ), true ); if ( wo.filter_external ) { - ts.filter.bindSearch( table, wo.filter_external ); + tsf.bindSearch( table, wo.filter_external ); } if ( wo.filter_hideFilters ) { - ts.filter.hideFilters( table, c ); + tsf.hideFilters( table, c ); } // show processing icon if ( c.showProcessing ) { txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter ' ); c.$table - .unbind( txt.replace( /\s+/g, ' ' ) ) + .unbind( txt.replace( ts.regex.spaces, ' ' ) ) .bind( txt, function( event, columns ) { // only add processing to certain columns to all columns $header = ( columns ) ? @@ -894,11 +910,11 @@ // add default values txt = 'tablesorter-initialized pagerBeforeInitialized '.split( ' ' ).join( c.namespace + 'filter ' ); c.$table - .unbind( txt.replace( /\s+/g, ' ' ) ) + .unbind( txt.replace( ts.regex.spaces, ' ' ) ) .bind( txt, function() { // redefine 'wo' as it does not update properly inside this callback var wo = this.config.widgetOptions; - filters = ts.filter.setDefaults( table, c, wo ) || []; + filters = tsf.setDefaults( table, c, wo ) || []; if ( filters.length ) { // prevent delayInit from triggering a cache build if filters are empty if ( !( c.delayInit && filters.join( '' ) === '' ) ) { @@ -909,7 +925,7 @@ // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers setTimeout( function() { if ( !wo.filter_initialized ) { - ts.filter.filterInitComplete( c ); + tsf.filterInitComplete( c ); } }, 100 ); }); @@ -917,7 +933,7 @@ if ( c.pager && c.pager.initialized && !wo.filter_initialized ) { c.$table.trigger( 'filterFomatterUpdate' ); setTimeout( function() { - ts.filter.filterInitComplete( c ); + tsf.filterInitComplete( c ); }, 100 ); } }, @@ -938,7 +954,7 @@ completed = function() { wo.filter_initialized = true; c.$table.trigger( 'filterInit', c ); - ts.filter.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); + tsf.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); }; if ( $.isEmptyObject( wo.filter_formatter ) ) { completed(); @@ -1090,7 +1106,7 @@ // use data attribute instead of jQuery data since the head is cloned without including // the data/binding .attr( 'data-lastSearchTime', new Date().getTime() ) - .unbind( tmp.replace( /\s+/g, ' ' ) ) + .unbind( tmp.replace( ts.regex.spaces, ' ' ) ) // include change for select - fixes #473 .bind( 'keyup' + namespace, function( event ) { $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); @@ -1110,17 +1126,18 @@ return; } // change event = no delay; last true flag tells getFilters to skip newest timed input - ts.filter.searching( table, true, true ); + tsf.searching( table, true, true ); }) .bind( 'search change keypress '.split( ' ' ).join( namespace + ' ' ), function( event ) { - var column = $( this ).data( 'column' ); + // don't get cached data, in case data-column changes dynamically + var column = parseInt( $( this ).attr( 'data-column' ), 10 ); // don't allow 'change' event to process if the input value is the same - fixes #685 if ( event.which === 13 || event.type === 'search' || event.type === 'change' && this.value !== c.lastSearch[column] ) { event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - ts.filter.searching( table, false, true ); + tsf.searching( table, false, true ); } }); }, @@ -1130,11 +1147,11 @@ if ( typeof filter === 'undefined' || filter === true ) { // delay filtering wo.searchTimer = setTimeout( function() { - ts.filter.checkFilters( table, filter, skipFirst ); + tsf.checkFilters( table, filter, skipFirst ); }, wo.filter_liveSearch ? wo.filter_searchDelay : 10 ); } else { // skip delay - ts.filter.checkFilters( table, filter, skipFirst ); + tsf.checkFilters( table, filter, skipFirst ); } }, checkFilters: function( table, filter, skipFirst ) { @@ -1148,7 +1165,7 @@ // update cache if delayInit set & pager has initialized ( after user initiates a search ) if ( c.delayInit && c.pager && c.pager.initialized ) { c.$table.trigger( 'updateCache', [ function() { - ts.filter.checkFilters( table, false, skipFirst ); + tsf.checkFilters( table, false, skipFirst ); } ] ); } return; @@ -1179,11 +1196,11 @@ if ( c.showProcessing ) { // give it time for the processing icon to kick in setTimeout( function() { - ts.filter.findRows( table, filters, combinedFilters ); + tsf.findRows( table, filters, combinedFilters ); return false; }, 30 ); } else { - ts.filter.findRows( table, filters, combinedFilters ); + tsf.findRows( table, filters, combinedFilters ); return false; } }, @@ -1226,8 +1243,8 @@ }, defaultFilter: function( filter, mask ) { if ( filter === '' ) { return filter; } - var regex = ts.filter.regex.iQuery, - maskLen = mask.match( ts.filter.regex.igQuery ).length, + var regex = tsf.regex.iQuery, + maskLen = mask.match( tsf.regex.igQuery ).length, query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ], len = query.length - 1, indx = 0, @@ -1263,7 +1280,10 @@ // & don't target 'all' column inputs if they don't exist targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, columns = [], - val = $.trim( ts.filter.getLatestSearch( $input ).attr( 'data-column' ) || '' ); + val = $.trim( tsf.getLatestSearch( $input ).attr( 'data-column' ) || '' ); + if ( !/[,-]/.test(val) && val.length === 1 ) { + return parseInt( val, 10 ); + } // process column range if ( targets && /-/.test( val ) ) { ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); @@ -1310,9 +1330,9 @@ var ffxn, filterMatched = null, matches = null; - for ( ffxn in ts.filter.types ) { + for ( ffxn in tsf.types ) { if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) { - matches = ts.filter.types[ffxn]( c, data, vars ); + matches = tsf.types[ffxn]( c, data, vars ); if ( matches !== null ) { filterMatched = matches; } @@ -1321,16 +1341,23 @@ return filterMatched; }, processRow: function( c, data, vars ) { - var columnIndex, hasSelect, result, val, filterMatched, + var hasSelect, result, val, filterMatched, fxn, ffxn, txt, - regex = ts.filter.regex, + regex = tsf.regex, wo = c.widgetOptions, - showRow = true; + showRow = true, + + // if wo.filter_$anyMatch data-column attribute is changed dynamically + // we don't want to do an "anyMatch" search on one column using data + // for the entire row - see #998 + columnIndex = wo.filter_$anyMatch && wo.filter_$anyMatch.length ? + // look for multiple columns '1-3,4-6,8' + tsf.multipleColumns( c, wo.filter_$anyMatch ) : + []; + data.$cells = data.$row.children(); - if ( data.anyMatchFlag ) { - // look for multiple columns '1-3,4-6,8' - columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); + if ( data.anyMatchFlag && columnIndex.length > 1 ) { data.anyMatch = true; data.isMatch = true; data.rowArray = data.$cells.map( function( i ) { @@ -1354,7 +1381,7 @@ data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' ); vars.excludeMatch = vars.noAnyMatch; - filterMatched = ts.filter.processTypes( c, data, vars ); + filterMatched = tsf.processTypes( c, data, vars ); if ( filterMatched !== null ) { showRow = filterMatched; @@ -1415,7 +1442,7 @@ val = true; if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) { - data.filter = ts.filter.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); + data.filter = tsf.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); // val is used to indicate that a filter select is using a default filter; // so we override the exact & partial matches val = false; @@ -1446,13 +1473,13 @@ if ( filterMatched === null ) { // cycle through the different filters // filters return a boolean or null if nothing matches - filterMatched = ts.filter.processTypes( c, data, vars ); + filterMatched = tsf.processTypes( c, data, vars ); if ( filterMatched !== null ) { result = filterMatched; // Look for match, and add child row data for matching } else { txt = ( data.iExact + data.childRowText ) - .indexOf( ts.filter.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) ); + .indexOf( tsf.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) ); result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); } } else { @@ -1472,7 +1499,7 @@ isChild, childRow, lastSearch, showRow, time, val, indx, notFiltered, searchFiltered, query, injected, res, id, txt, storedFilters = $.extend( [], filters ), - regex = ts.filter.regex, + regex = tsf.regex, c = table.config, wo = c.widgetOptions, // data object passed to filters; anyMatch is a flag for the filters @@ -1549,7 +1576,7 @@ data.anyMatchFlag = true; data.anyMatchFilter = '' + ( filters[ c.columns ] || - wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || + wo.filter_$anyMatch && tsf.getLatestSearch( wo.filter_$anyMatch ).val() || '' ); if ( wo.filter_columnAnyMatch ) { @@ -1591,10 +1618,10 @@ // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string !regex.alreadyFiltered.test( val ) && // if we are not doing exact matches, using '|' ( logical or ) or not '!' - !/[=\"\|!]/.test( val ) && + !regex.exactTest.test( val ) && // don't search only filtered if the value is negative // ( '> -10' => '> -100' will ignore hidden rows ) - !( /(>=?\s*-\d)/.test( val ) || /(<=?\s*\d)/.test( val ) ) && + !( regex.isNeg1.test( val ) || regex.isNeg2.test( val ) ) && // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 !( val !== '' && c.$filters && c.$filters.eq( indx ).find( 'select' ).length && !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); @@ -1613,7 +1640,7 @@ data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter ); } if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultAnyFilter ) ) { - data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter ); + data.anyMatchFilter = tsf.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter ); // clear search filtered flag because default filters are not saved to the last search searchFiltered = false; } @@ -1656,7 +1683,7 @@ ''; } - showRow = ts.filter.processRow( c, data, vars ); + showRow = tsf.processRow( c, data, vars ); childRow = rowData.$row.filter( ':gt( 0 )' ); if ( wo.filter_childRows && childRow.length ) { @@ -1667,7 +1694,7 @@ data.cacheArray = rowData.child[ indx ]; data.rawArray = data.cacheArray; // use OR comparison on child rows - showRow = showRow || ts.filter.processRow( c, data, vars ); + showRow = showRow || tsf.processRow( c, data, vars ); } } childRow.toggleClass( wo.filter_filteredRow, !showRow ); @@ -1729,7 +1756,7 @@ } if ( arry === false ) { // fall back to original method - arry = ts.filter.getOptions( table, column, onlyAvail ); + arry = tsf.getOptions( table, column, onlyAvail ); } // get unique elements and sort the list @@ -1841,13 +1868,13 @@ // nothing included in arry ( external source ), so get the options from // filter_selectSource or column data if ( typeof arry === 'undefined' || arry === '' ) { - arry = ts.filter.getOptionSource( table, column, onlyAvail ); + arry = tsf.getOptionSource( table, column, onlyAvail ); } if ( $.isArray( arry ) ) { // build option list for ( indx = 0; indx < arry.length; indx++ ) { - txt = arry[indx] = ( '' + arry[indx] ).replace( /\"/g, '"' ); + txt = arry[indx] = ( '' + arry[indx] ).replace( tsf.regex.quote, '"' ); val = txt; // allow including a symbol in the selectSource array // 'a-z|A through Z' so that 'a-z' becomes the option value @@ -1903,7 +1930,7 @@ // look for the filter-select class; build/update it if found if ( ( $header.hasClass( 'filter-select' ) || ts.getColumnData( table, wo.filter_functions, columnIndex ) === true ) && noSelect ) { - ts.filter.buildSelect( table, columnIndex, '', updating, $header.hasClass( wo.filter_onlyAvail ) ); + tsf.buildSelect( table, columnIndex, '', updating, $header.hasClass( wo.filter_onlyAvail ) ); } } } @@ -1939,7 +1966,7 @@ $column = $filters.filter( cols ); if ( $column.length ) { // move the latest search to the first slot in the array - $column = ts.filter.getLatestSearch( $column ); + $column = tsf.getLatestSearch( $column ); if ( $.isArray( setFilters ) ) { // skip first ( latest input ) to maintain cursor position while typing if ( skipFirst && $column.length > 1 ) { @@ -1989,7 +2016,7 @@ // ensure new set filters are applied, even if the search is the same c.lastCombinedFilter = null; c.lastSearch = []; - ts.filter.searching( c.table, filter, skipFirst ); + tsf.searching( c.table, filter, skipFirst ); c.$table.trigger( 'filterFomatterUpdate' ); } return !!valid; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index f1eaba8..368af1c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Widget: columnSelector (responsive table widget) - updated 8/17/2015 (v2.23.0) *//* +/* Widget: columnSelector (responsive table widget) - updated 8/23/2015 (v2.23.2) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -224,7 +224,7 @@ } // trigger columnUpdate if auto is true (it gets skipped in updateCols() if (colSel.auto) { - c.$table.trigger('columnUpdate'); + c.$table.trigger(wo.columnSelector_updated); } }, @@ -299,7 +299,7 @@ if (wo.columnSelector_saveColumns && ts.storage) { ts.storage( c.$table[0], 'tablesorter-columnSelector', colSel.states ); } - c.$table.trigger('columnUpdate'); + c.$table.trigger(wo.columnSelector_updated); }, attachTo : function(table, elm) { @@ -370,7 +370,9 @@ columnSelector_priority : 'data-priority', // class name added to checked checkboxes - this fixes an issue with Chrome not updating FontAwesome // applied icons; use this class name (input.checked) instead of input:checked - columnSelector_cssChecked : 'checked' + columnSelector_cssChecked : 'checked', + // event triggered when columnSelector completes + columnSelector_updated : 'columnUpdate' }, init: function(table, thisWidget, c, wo) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 302de5e..db16084 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,11 +1,12 @@ -/*! Widget: filter - updated 8/17/2015 (v2.23.0) *//* +/*! Widget: filter - updated 8/23/2015 (v2.23.2) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ ;( function ( $ ) { 'use strict'; - var ts = $.tablesorter || {}, - tscss = ts.css; + var tsf, + ts = $.tablesorter || {}, + tscss = ts.css; $.extend( tscss, { filterRow : 'tablesorter-filter-row', @@ -49,7 +50,7 @@ }, format: function( table, c, wo ) { if ( !c.$table.hasClass( 'hasFilters' ) ) { - ts.filter.init( table, c, wo ); + tsf.init( table, c, wo ); } }, remove: function( table, c, wo, refreshing ) { @@ -61,7 +62,7 @@ $table .removeClass( 'hasFilters' ) // add .tsfilter namespace to all BUT search - .unbind( events.replace( /\s+/g, ' ' ) ) + .unbind( events.replace( ts.regex.spaces, ' ' ) ) // remove the filter row even if refreshing, because the column might have been moved .find( '.' + tscss.filterRow ).remove(); if ( refreshing ) { return; } @@ -76,7 +77,7 @@ } }); - ts.filter = { + tsf = ts.filter = { // regex used in filter 'check' functions - not for general use and not documented regex: { @@ -85,9 +86,13 @@ filtered : /filtered/, // filtered (hidden) row class name; updated in the script type : /undefined|number/, // check type exact : /(^[\"\'=]+)|([\"\'=]+$)/g, // exact match (allow '==') - nondigit : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser) operators : /[<>=]/g, // replace operators - query : '(q|query)' // replace filter queries + query : '(q|query)', // replace filter queries + wild01 : /\?/g, // wild card match 0 or 1 + wild0More : /\*/g, // wild care match 0 or more + quote : /\"/g, + isNeg1 : /(>=?\s*-\d)/, + isNeg2 : /(<=?\s*\d)/ }, // function( c, data ) { } // c = table.config @@ -104,27 +109,27 @@ // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class ) types: { or : function( c, data, vars ) { - if ( /\|/.test( data.iFilter ) || ts.filter.regex.orSplit.test( data.filter ) ) { + if ( tsf.regex.orTest.test( data.iFilter ) || tsf.regex.orSplit.test( data.filter ) ) { var indx, filterMatched, query, regex, // duplicate data but split filter data2 = $.extend( {}, data ), index = data.index, parsed = data.parsed[ index ], - filter = data.filter.split( ts.filter.regex.orSplit ), - iFilter = data.iFilter.split( ts.filter.regex.orSplit ), + filter = data.filter.split( tsf.regex.orSplit ), + iFilter = data.iFilter.split( tsf.regex.orSplit ), len = filter.length; for ( indx = 0; indx < len; indx++ ) { data2.nestedFilters = true; - data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); - data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); - query = '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')'; + data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' ); + data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); + query = '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')'; try { // use try/catch, because query may not be a valid regex if "|" is contained within a partial regex search, // e.g "/(Alex|Aar" -> Uncaught SyntaxError: Invalid regular expression: /(/(Alex)/: Unterminated group regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); // filterMatched = data2.filter === '' && indx > 0 ? true // look for an exact match with the 'or' unless the 'filter-match' class is found - filterMatched = regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ); + filterMatched = regex.test( data2.exact ) || tsf.processTypes( c, data2, vars ); if ( filterMatched ) { return filterMatched; } @@ -139,27 +144,27 @@ }, // Look for an AND or && operator ( logical and ) and : function( c, data, vars ) { - if ( ts.filter.regex.andTest.test( data.filter ) ) { + if ( tsf.regex.andTest.test( data.filter ) ) { var indx, filterMatched, result, query, regex, // duplicate data but split filter data2 = $.extend( {}, data ), index = data.index, parsed = data.parsed[ index ], - filter = data.filter.split( ts.filter.regex.andSplit ), - iFilter = data.iFilter.split( ts.filter.regex.andSplit ), + filter = data.filter.split( tsf.regex.andSplit ), + iFilter = data.iFilter.split( tsf.regex.andSplit ), len = filter.length; for ( indx = 0; indx < len; indx++ ) { data2.nestedFilters = true; - data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' ); - data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); - query = ( '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' ) + data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' ); + data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); + query = ( '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' ) // replace wild cards since /(a*)/i will match anything - .replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ); + .replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' ); try { // use try/catch just in case RegExp is invalid regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); // look for an exact match with the 'and' unless the 'filter-match' class is found - result = ( regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ) ); + result = ( regex.test( data2.exact ) || tsf.processTypes( c, data2, vars ) ); if ( indx === 0 ) { filterMatched = result; } else { @@ -176,10 +181,10 @@ }, // Look for regex regex: function( c, data ) { - if ( ts.filter.regex.regex.test( data.filter ) ) { + if ( tsf.regex.regex.test( data.filter ) ) { var matches, // cache regex per column for optimal speed - regex = data.filter_regexCache[ data.index ] || ts.filter.regex.regex.exec( data.filter ), + regex = data.filter_regexCache[ data.index ] || tsf.regex.regex.exec( data.filter ), isRegex = regex instanceof RegExp; try { if ( !isRegex ) { @@ -198,18 +203,18 @@ // Look for operators >, >=, < or <= operators: function( c, data ) { // ignore empty strings... because '' < 10 is true - if ( /^[<>]=?/.test( data.iFilter ) && data.iExact !== '' ) { + if ( tsf.regex.operTest.test( data.iFilter ) && data.iExact !== '' ) { var cachedValue, result, txt, table = c.table, index = data.index, parsed = data.parsed[index], - query = ts.formatFloat( data.iFilter.replace( ts.filter.regex.operators, '' ), table ), + query = ts.formatFloat( data.iFilter.replace( tsf.regex.operators, '' ), table ), parser = c.parsers[index], savedSearch = query; // parse filter value in case we're comparing numbers ( dates ) if ( parsed || parser.type === 'numeric' ) { - txt = $.trim( '' + data.iFilter.replace( ts.filter.regex.operators, '' ) ); - result = ts.filter.parseFilter( c, txt, index, true ); + txt = $.trim( '' + data.iFilter.replace( tsf.regex.operators, '' ) ); + result = tsf.parseFilter( c, txt, index, true ); query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query; } // iExact may be numeric - see issue #149; @@ -218,13 +223,13 @@ typeof data.cache !== 'undefined' ) { cachedValue = data.cache; } else { - txt = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; + txt = isNaN( data.iExact ) ? data.iExact.replace( ts.regex.nondigit, '' ) : data.iExact; cachedValue = ts.formatFloat( txt, table ); } - if ( />/.test( data.iFilter ) ) { - result = />=/.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; - } else if ( /</.test( data.iFilter ) ) { - result = /<=/.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; + if ( tsf.regex.gtTest.test( data.iFilter ) ) { + result = tsf.regex.gteTest.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; + } else if ( tsf.regex.ltTest.test( data.iFilter ) ) { + result = tsf.regex.lteTest.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; } // keep showing all rows if nothing follows the operator if ( !result && savedSearch === '' ) { @@ -236,13 +241,13 @@ }, // Look for a not match notMatch: function( c, data ) { - if ( /^\!/.test( data.iFilter ) ) { + if ( tsf.regex.notTest.test( data.iFilter ) ) { var indx, txt = data.iFilter.replace( '!', '' ), - filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; - if ( ts.filter.regex.exact.test( filter ) ) { + filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + if ( tsf.regex.exact.test( filter ) ) { // look for exact not matches - see #628 - filter = filter.replace( ts.filter.regex.exact, '' ); + filter = filter.replace( tsf.regex.exact, '' ); return filter === '' ? true : $.trim( filter ) !== data.iExact; } else { indx = data.iExact.search( $.trim( filter ) ); @@ -254,27 +259,27 @@ // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric exact: function( c, data ) { /*jshint eqeqeq:false */ - if ( ts.filter.regex.exact.test( data.iFilter ) ) { - var txt = data.iFilter.replace( ts.filter.regex.exact, '' ), - filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + if ( tsf.regex.exact.test( data.iFilter ) ) { + var txt = data.iFilter.replace( tsf.regex.exact, '' ), + filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact; } return null; }, // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu! range : function( c, data ) { - if ( ts.filter.regex.toTest.test( data.iFilter ) ) { + if ( tsf.regex.toTest.test( data.iFilter ) ) { var result, tmp, range1, range2, table = c.table, index = data.index, parsed = data.parsed[index], // make sure the dash is for a range and not indicating a negative number - query = data.iFilter.split( ts.filter.regex.toSplit ); + query = data.iFilter.split( tsf.regex.toSplit ); - tmp = query[0].replace( ts.filter.regex.nondigit, '' ) || ''; - range1 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); - tmp = query[1].replace( ts.filter.regex.nondigit, '' ) || ''; - range2 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table ); + tmp = query[0].replace( ts.regex.nondigit, '' ) || ''; + range1 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table ); + tmp = query[1].replace( ts.regex.nondigit, '' ) || ''; + range2 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table ); // parse filter value in case we're comparing numbers ( dates ) if ( parsed || c.parsers[index].type === 'numeric' ) { result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index ); @@ -285,7 +290,7 @@ if ( ( parsed || c.parsers[ index ].type === 'numeric' ) && !isNaN( range1 ) && !isNaN( range2 ) ) { result = data.cache; } else { - tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact; + tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.regex.nondigit, '' ) : data.iExact; result = ts.formatFloat( tmp, table ); } if ( range1 > range2 ) { @@ -297,18 +302,18 @@ }, // Look for wild card: ? = single, * = multiple, or | = logical OR wild : function( c, data ) { - if ( /[\?\*\|]/.test( data.iFilter ) ) { + if ( tsf.regex.wildOrTest.test( data.iFilter ) ) { var index = data.index, parsed = data.parsed[ index ], - query = '' + ( ts.filter.parseFilter( c, data.iFilter, index, parsed ) || '' ); + query = '' + ( tsf.parseFilter( c, data.iFilter, index, parsed ) || '' ); // look for an exact match with the 'or' unless the 'filter-match' class is found - if ( !/\?\*/.test( query ) && data.nestedFilters ) { + if ( !tsf.regex.wildTest.test( query ) && data.nestedFilters ) { query = data.isMatch ? query : '^(' + query + ')$'; } // parsing the filter may not work properly when using wildcards =/ try { return new RegExp( - query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ), + query.replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' ), c.widgetOptions.filter_ignoreCase ? 'i' : '' ) .test( data.exact ); @@ -320,12 +325,12 @@ }, // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license ) fuzzy: function( c, data ) { - if ( /^~/.test( data.iFilter ) ) { + if ( tsf.regex.fuzzyTest.test( data.iFilter ) ) { var indx, patternIndx = 0, len = data.iExact.length, txt = data.iFilter.slice( 1 ), - pattern = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + pattern = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; for ( indx = 0; indx < len; indx++ ) { if ( data.iExact[ indx ] === pattern[ patternIndx ] ) { patternIndx += 1; @@ -348,7 +353,7 @@ }, ts.language ); var options, string, txt, $header, column, filters, val, fxn, noSelect, - regex = ts.filter.regex; + regex = tsf.regex; c.$table.addClass( 'hasFilters' ); // define timers so using clearTimeout won't cause an undefined error @@ -359,7 +364,7 @@ wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; - val = '\\{' + ts.filter.regex.query + '\\}'; + val = '\\{' + tsf.regex.query + '\\}'; $.extend( regex, { child : new RegExp( c.cssChildRow ), filtered : new RegExp( wo.filter_filteredRow ), @@ -368,9 +373,20 @@ toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)', 'gi' ), andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), + orTest : /\|/, orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ), iQuery : new RegExp( val, 'i' ), - igQuery : new RegExp( val, 'ig' ) + igQuery : new RegExp( val, 'ig' ), + operTest : /^[<>]=?/, + gtTest : />/, + gteTest : />=/, + ltTest : /</, + lteTest : /<=/, + notTest : /^\!/, + wildOrTest : /[\?\*\|]/, + wildTest : /\?\*/, + fuzzyTest : /^~/, + exactTest : /[=\"\|!]/ }); // don't build filter row if columnFilters is false or all columns are set to 'filter-false' @@ -378,7 +394,7 @@ val = c.$headers.filter( '.filter-false, .parser-false' ).length; if ( wo.filter_columnFilters !== false && val !== c.$headers.length ) { // build filter row - ts.filter.buildRow( table, c, wo ); + tsf.buildRow( table, c, wo ); } txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' @@ -391,13 +407,13 @@ c.$table.find( '.' + tscss.filterRow ).toggleClass( wo.filter_filteredRow, val ); // fixes #450 if ( !/(search|filter)/.test( event.type ) ) { event.stopPropagation(); - ts.filter.buildDefault( table, true ); + tsf.buildDefault( table, true ); } if ( event.type === 'filterReset' ) { c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' ); - ts.filter.searching( table, [] ); + tsf.searching( table, [] ); } else if ( event.type === 'filterEnd' ) { - ts.filter.buildDefault( table, true ); + tsf.buildDefault( table, true ); } else { // send false argument to force a new search; otherwise if the filter hasn't changed, // it will return @@ -411,7 +427,7 @@ // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first // input ensures all inputs are updated when a search is triggered on the table // $( 'table' ).trigger( 'search', [...] ); - ts.filter.searching( table, filter, true ); + tsf.searching( table, filter, true ); } return false; }); @@ -444,7 +460,7 @@ noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); options = ''; if ( fxn === true && noSelect ) { - ts.filter.buildSelect( table, column ); + tsf.buildSelect( table, column ); } else if ( typeof fxn === 'object' && noSelect ) { // add custom drop down list for ( string in fxn ) { @@ -477,7 +493,7 @@ fxn = $.isFunction( txt ) ? true : ts.getColumnData( table, txt, column ); if ( fxn ) { // updating so the extra options are appended - ts.filter.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) ); + tsf.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) ); } } } @@ -485,22 +501,22 @@ } // not really updating, but if the column has both the 'filter-select' class & // filter_functions set to true, it would append the same options twice. - ts.filter.buildDefault( table, true ); + tsf.buildDefault( table, true ); - ts.filter.bindSearch( table, c.$table.find( '.' + tscss.filter ), true ); + tsf.bindSearch( table, c.$table.find( '.' + tscss.filter ), true ); if ( wo.filter_external ) { - ts.filter.bindSearch( table, wo.filter_external ); + tsf.bindSearch( table, wo.filter_external ); } if ( wo.filter_hideFilters ) { - ts.filter.hideFilters( table, c ); + tsf.hideFilters( table, c ); } // show processing icon if ( c.showProcessing ) { txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter ' ); c.$table - .unbind( txt.replace( /\s+/g, ' ' ) ) + .unbind( txt.replace( ts.regex.spaces, ' ' ) ) .bind( txt, function( event, columns ) { // only add processing to certain columns to all columns $header = ( columns ) ? @@ -520,11 +536,11 @@ // add default values txt = 'tablesorter-initialized pagerBeforeInitialized '.split( ' ' ).join( c.namespace + 'filter ' ); c.$table - .unbind( txt.replace( /\s+/g, ' ' ) ) + .unbind( txt.replace( ts.regex.spaces, ' ' ) ) .bind( txt, function() { // redefine 'wo' as it does not update properly inside this callback var wo = this.config.widgetOptions; - filters = ts.filter.setDefaults( table, c, wo ) || []; + filters = tsf.setDefaults( table, c, wo ) || []; if ( filters.length ) { // prevent delayInit from triggering a cache build if filters are empty if ( !( c.delayInit && filters.join( '' ) === '' ) ) { @@ -535,7 +551,7 @@ // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers setTimeout( function() { if ( !wo.filter_initialized ) { - ts.filter.filterInitComplete( c ); + tsf.filterInitComplete( c ); } }, 100 ); }); @@ -543,7 +559,7 @@ if ( c.pager && c.pager.initialized && !wo.filter_initialized ) { c.$table.trigger( 'filterFomatterUpdate' ); setTimeout( function() { - ts.filter.filterInitComplete( c ); + tsf.filterInitComplete( c ); }, 100 ); } }, @@ -564,7 +580,7 @@ completed = function() { wo.filter_initialized = true; c.$table.trigger( 'filterInit', c ); - ts.filter.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); + tsf.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); }; if ( $.isEmptyObject( wo.filter_formatter ) ) { completed(); @@ -716,7 +732,7 @@ // use data attribute instead of jQuery data since the head is cloned without including // the data/binding .attr( 'data-lastSearchTime', new Date().getTime() ) - .unbind( tmp.replace( /\s+/g, ' ' ) ) + .unbind( tmp.replace( ts.regex.spaces, ' ' ) ) // include change for select - fixes #473 .bind( 'keyup' + namespace, function( event ) { $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); @@ -736,17 +752,18 @@ return; } // change event = no delay; last true flag tells getFilters to skip newest timed input - ts.filter.searching( table, true, true ); + tsf.searching( table, true, true ); }) .bind( 'search change keypress '.split( ' ' ).join( namespace + ' ' ), function( event ) { - var column = $( this ).data( 'column' ); + // don't get cached data, in case data-column changes dynamically + var column = parseInt( $( this ).attr( 'data-column' ), 10 ); // don't allow 'change' event to process if the input value is the same - fixes #685 if ( event.which === 13 || event.type === 'search' || event.type === 'change' && this.value !== c.lastSearch[column] ) { event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - ts.filter.searching( table, false, true ); + tsf.searching( table, false, true ); } }); }, @@ -756,11 +773,11 @@ if ( typeof filter === 'undefined' || filter === true ) { // delay filtering wo.searchTimer = setTimeout( function() { - ts.filter.checkFilters( table, filter, skipFirst ); + tsf.checkFilters( table, filter, skipFirst ); }, wo.filter_liveSearch ? wo.filter_searchDelay : 10 ); } else { // skip delay - ts.filter.checkFilters( table, filter, skipFirst ); + tsf.checkFilters( table, filter, skipFirst ); } }, checkFilters: function( table, filter, skipFirst ) { @@ -774,7 +791,7 @@ // update cache if delayInit set & pager has initialized ( after user initiates a search ) if ( c.delayInit && c.pager && c.pager.initialized ) { c.$table.trigger( 'updateCache', [ function() { - ts.filter.checkFilters( table, false, skipFirst ); + tsf.checkFilters( table, false, skipFirst ); } ] ); } return; @@ -805,11 +822,11 @@ if ( c.showProcessing ) { // give it time for the processing icon to kick in setTimeout( function() { - ts.filter.findRows( table, filters, combinedFilters ); + tsf.findRows( table, filters, combinedFilters ); return false; }, 30 ); } else { - ts.filter.findRows( table, filters, combinedFilters ); + tsf.findRows( table, filters, combinedFilters ); return false; } }, @@ -852,8 +869,8 @@ }, defaultFilter: function( filter, mask ) { if ( filter === '' ) { return filter; } - var regex = ts.filter.regex.iQuery, - maskLen = mask.match( ts.filter.regex.igQuery ).length, + var regex = tsf.regex.iQuery, + maskLen = mask.match( tsf.regex.igQuery ).length, query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ], len = query.length - 1, indx = 0, @@ -889,7 +906,10 @@ // & don't target 'all' column inputs if they don't exist targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, columns = [], - val = $.trim( ts.filter.getLatestSearch( $input ).attr( 'data-column' ) || '' ); + val = $.trim( tsf.getLatestSearch( $input ).attr( 'data-column' ) || '' ); + if ( !/[,-]/.test(val) && val.length === 1 ) { + return parseInt( val, 10 ); + } // process column range if ( targets && /-/.test( val ) ) { ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); @@ -936,9 +956,9 @@ var ffxn, filterMatched = null, matches = null; - for ( ffxn in ts.filter.types ) { + for ( ffxn in tsf.types ) { if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) { - matches = ts.filter.types[ffxn]( c, data, vars ); + matches = tsf.types[ffxn]( c, data, vars ); if ( matches !== null ) { filterMatched = matches; } @@ -947,16 +967,23 @@ return filterMatched; }, processRow: function( c, data, vars ) { - var columnIndex, hasSelect, result, val, filterMatched, + var hasSelect, result, val, filterMatched, fxn, ffxn, txt, - regex = ts.filter.regex, + regex = tsf.regex, wo = c.widgetOptions, - showRow = true; + showRow = true, + + // if wo.filter_$anyMatch data-column attribute is changed dynamically + // we don't want to do an "anyMatch" search on one column using data + // for the entire row - see #998 + columnIndex = wo.filter_$anyMatch && wo.filter_$anyMatch.length ? + // look for multiple columns '1-3,4-6,8' + tsf.multipleColumns( c, wo.filter_$anyMatch ) : + []; + data.$cells = data.$row.children(); - if ( data.anyMatchFlag ) { - // look for multiple columns '1-3,4-6,8' - columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch ); + if ( data.anyMatchFlag && columnIndex.length > 1 ) { data.anyMatch = true; data.isMatch = true; data.rowArray = data.$cells.map( function( i ) { @@ -980,7 +1007,7 @@ data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' ); vars.excludeMatch = vars.noAnyMatch; - filterMatched = ts.filter.processTypes( c, data, vars ); + filterMatched = tsf.processTypes( c, data, vars ); if ( filterMatched !== null ) { showRow = filterMatched; @@ -1041,7 +1068,7 @@ val = true; if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) { - data.filter = ts.filter.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); + data.filter = tsf.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); // val is used to indicate that a filter select is using a default filter; // so we override the exact & partial matches val = false; @@ -1072,13 +1099,13 @@ if ( filterMatched === null ) { // cycle through the different filters // filters return a boolean or null if nothing matches - filterMatched = ts.filter.processTypes( c, data, vars ); + filterMatched = tsf.processTypes( c, data, vars ); if ( filterMatched !== null ) { result = filterMatched; // Look for match, and add child row data for matching } else { txt = ( data.iExact + data.childRowText ) - .indexOf( ts.filter.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) ); + .indexOf( tsf.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) ); result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); } } else { @@ -1098,7 +1125,7 @@ isChild, childRow, lastSearch, showRow, time, val, indx, notFiltered, searchFiltered, query, injected, res, id, txt, storedFilters = $.extend( [], filters ), - regex = ts.filter.regex, + regex = tsf.regex, c = table.config, wo = c.widgetOptions, // data object passed to filters; anyMatch is a flag for the filters @@ -1175,7 +1202,7 @@ data.anyMatchFlag = true; data.anyMatchFilter = '' + ( filters[ c.columns ] || - wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || + wo.filter_$anyMatch && tsf.getLatestSearch( wo.filter_$anyMatch ).val() || '' ); if ( wo.filter_columnAnyMatch ) { @@ -1217,10 +1244,10 @@ // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string !regex.alreadyFiltered.test( val ) && // if we are not doing exact matches, using '|' ( logical or ) or not '!' - !/[=\"\|!]/.test( val ) && + !regex.exactTest.test( val ) && // don't search only filtered if the value is negative // ( '> -10' => '> -100' will ignore hidden rows ) - !( /(>=?\s*-\d)/.test( val ) || /(<=?\s*\d)/.test( val ) ) && + !( regex.isNeg1.test( val ) || regex.isNeg2.test( val ) ) && // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 !( val !== '' && c.$filters && c.$filters.eq( indx ).find( 'select' ).length && !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); @@ -1239,7 +1266,7 @@ data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter ); } if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultAnyFilter ) ) { - data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter ); + data.anyMatchFilter = tsf.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter ); // clear search filtered flag because default filters are not saved to the last search searchFiltered = false; } @@ -1282,7 +1309,7 @@ ''; } - showRow = ts.filter.processRow( c, data, vars ); + showRow = tsf.processRow( c, data, vars ); childRow = rowData.$row.filter( ':gt( 0 )' ); if ( wo.filter_childRows && childRow.length ) { @@ -1293,7 +1320,7 @@ data.cacheArray = rowData.child[ indx ]; data.rawArray = data.cacheArray; // use OR comparison on child rows - showRow = showRow || ts.filter.processRow( c, data, vars ); + showRow = showRow || tsf.processRow( c, data, vars ); } } childRow.toggleClass( wo.filter_filteredRow, !showRow ); @@ -1355,7 +1382,7 @@ } if ( arry === false ) { // fall back to original method - arry = ts.filter.getOptions( table, column, onlyAvail ); + arry = tsf.getOptions( table, column, onlyAvail ); } // get unique elements and sort the list @@ -1467,13 +1494,13 @@ // nothing included in arry ( external source ), so get the options from // filter_selectSource or column data if ( typeof arry === 'undefined' || arry === '' ) { - arry = ts.filter.getOptionSource( table, column, onlyAvail ); + arry = tsf.getOptionSource( table, column, onlyAvail ); } if ( $.isArray( arry ) ) { // build option list for ( indx = 0; indx < arry.length; indx++ ) { - txt = arry[indx] = ( '' + arry[indx] ).replace( /\"/g, '"' ); + txt = arry[indx] = ( '' + arry[indx] ).replace( tsf.regex.quote, '"' ); val = txt; // allow including a symbol in the selectSource array // 'a-z|A through Z' so that 'a-z' becomes the option value @@ -1529,7 +1556,7 @@ // look for the filter-select class; build/update it if found if ( ( $header.hasClass( 'filter-select' ) || ts.getColumnData( table, wo.filter_functions, columnIndex ) === true ) && noSelect ) { - ts.filter.buildSelect( table, columnIndex, '', updating, $header.hasClass( wo.filter_onlyAvail ) ); + tsf.buildSelect( table, columnIndex, '', updating, $header.hasClass( wo.filter_onlyAvail ) ); } } } @@ -1565,7 +1592,7 @@ $column = $filters.filter( cols ); if ( $column.length ) { // move the latest search to the first slot in the array - $column = ts.filter.getLatestSearch( $column ); + $column = tsf.getLatestSearch( $column ); if ( $.isArray( setFilters ) ) { // skip first ( latest input ) to maintain cursor position while typing if ( skipFirst && $column.length > 1 ) { @@ -1615,7 +1642,7 @@ // ensure new set filters are applied, even if the search is the same c.lastCombinedFilter = null; c.lastSearch = []; - ts.filter.searching( c.table, filter, skipFirst ); + tsf.searching( c.table, filter, skipFirst ); c.$table.trigger( 'filterFomatterUpdate' ); } return !!valid; From ae4c797e1b084d3c1298910d955c79bb3504d4d2 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Tue, 1 Sep 2015 21:43:03 +0200 Subject: [PATCH 066/138] * updated tablesorter to latest version (2.23.3) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 35 ++++++++++-------- .../jquery-tablesorter/jquery.tablesorter.js | 16 +++++--- .../jquery.tablesorter.widgets.js | 19 +++++----- .../widgets/widget-filter.js | 17 +++++---- .../widgets/widget-grouping.js | 37 ++++++++++++------- 9 files changed, 79 insertions(+), 55 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6484bb4..fbbf3d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.18.3 (2015-09-01) + +* Upgrade tablesorter to v2.23.3 + #### v1.18.2 (2015-08-24) * Upgrade tablesorter to v2.23.2 diff --git a/README.md b/README.md index 50bfcd4..814720a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.23.2 (8/23/2015), [documentation] +Current tablesorter version: 2.23.3 (9/1/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 514d9ae..a8b255c 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.18.2' + VERSION = '1.18.3' end diff --git a/tablesorter b/tablesorter index 68ae07c..829d379 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 68ae07cb79ab93443d4ba582719671769eca6d7a +Subproject commit 829d3792fa0ae15a524add665c4871c0c241daa6 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 70be4c8..9aa295d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 08-23-2015 (v2.23.2)*/ +/*! tablesorter (FORK) - updated 09-01-2015 (v2.23.3)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.23.2 *//* +/*! TableSorter (FORK) v2.23.3 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -35,7 +35,6 @@ * @contributor Rob Garrison - https://github.com/Mottie/tablesorter */ /*jshint browser:true, jquery:true, unused:false, expr: true */ -/*global console:false */ ;(function($){ 'use strict'; $.extend({ @@ -44,7 +43,7 @@ var ts = this; - ts.version = '2.23.2'; + ts.version = '2.23.3'; ts.parsers = []; ts.widgets = []; @@ -576,8 +575,10 @@ for (i = 0; i < len; i++) { // direction = 2 means reset! if (list[i][1] !== 2) { - // multicolumn sorting updating - choose the :last in case there are nested columns - f = c.$headers.not('.sorter-false').filter('[data-column="' + list[i][0] + '"]' + (len === 1 ? ':last' : '') ); + // multicolumn sorting updating - see #1005 + f = c.lastClickedIndex > 0 ? c.$headers.filter(':gt(' + ( c.lastClickedIndex - 1 ) + ')') : c.$headers; + // choose the :last in case there are nested columns + f = f.not('.sorter-false').filter('[data-column="' + list[i][0] + '"]' + (len === 1 ? ':last' : '') ); if (f.length) { for (j = 0; j < f.length; j++) { if (!f[j].sortDisabled) { @@ -1306,7 +1307,10 @@ $cell = $.fn.closest ? $(this).closest('th, td') : /TH|TD/.test(this.nodeName) ? $(this) : $(this).parents('th, td'); // reference original table headers and find the same cell // don't use $headers or IE8 throws an error - see #987 - cell = c.$headers[ $cell.prevAll().length ]; + temp = $headers.index( $cell ); + c.lastClickedIndex = ( temp < 0 ) ? $cell.attr('data-column') : temp; + // use column index if $headers is undefined + cell = c.$headers[ c.lastClickedIndex ]; if (cell && !cell.sortDisabled) { initSort(table, cell, e); } @@ -2714,7 +2718,7 @@ })(jQuery); -/*! Widget: filter - updated 8/23/2015 (v2.23.2) *//* +/*! Widget: filter - updated 9/1/2015 (v2.23.3) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -2777,7 +2781,7 @@ .split( ' ' ).join( c.namespace + 'filter ' ); $table .removeClass( 'hasFilters' ) - // add .tsfilter namespace to all BUT search + // add filter namespace to all BUT search .unbind( events.replace( ts.regex.spaces, ' ' ) ) // remove the filter row even if refreshing, because the column might have been moved .find( '.' + tscss.filterRow ).remove(); @@ -2788,7 +2792,7 @@ ts.processTbody( table, $tbody, false ); // restore tbody } if ( wo.filter_reset ) { - $( document ).undelegate( wo.filter_reset, 'click.tsfilter' ); + $( document ).undelegate( wo.filter_reset, 'click' + c.namespace + 'filter' ); } } }); @@ -3158,8 +3162,8 @@ } else if ( $( wo.filter_reset ).length ) { // reset is a jQuery selector, use event delegation $( document ) - .undelegate( wo.filter_reset, 'click.tsfilter' ) - .delegate( wo.filter_reset, 'click.tsfilter', function() { + .undelegate( wo.filter_reset, 'click' + c.namespace + 'filter' ) + .delegate( wo.filter_reset, 'click' + c.namespace + 'filter', function() { // trigger a reset event, so other functions ( filter_formatter ) know when to reset c.$table.trigger( 'filterReset' ); }); @@ -3474,8 +3478,8 @@ // don't get cached data, in case data-column changes dynamically var column = parseInt( $( this ).attr( 'data-column' ), 10 ); // don't allow 'change' event to process if the input value is the same - fixes #685 - if ( event.which === 13 || event.type === 'search' || - event.type === 'change' && this.value !== c.lastSearch[column] ) { + if ( wo.filter_initialized && ( event.which === 13 || event.type === 'search' || + event.type === 'change' && this.value !== c.lastSearch[column] ) ) { event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); @@ -4321,7 +4325,8 @@ } $column .val( setFilters[ i ] ) - .trigger( 'change.tsfilter' ); + // must include a namespace here; but not c.namespace + 'filter'? + .trigger( 'change' + c.namespace ); } else { filters[i] = $column.val() || ''; // don't change the first... it will move the cursor diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 9b1c19a..42edb61 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.23.2 *//* +/*! TableSorter (FORK) v2.23.3 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -17,7 +17,6 @@ * @contributor Rob Garrison - https://github.com/Mottie/tablesorter */ /*jshint browser:true, jquery:true, unused:false, expr: true */ -/*global console:false */ ;(function($){ 'use strict'; $.extend({ @@ -26,7 +25,7 @@ var ts = this; - ts.version = '2.23.2'; + ts.version = '2.23.3'; ts.parsers = []; ts.widgets = []; @@ -558,8 +557,10 @@ for (i = 0; i < len; i++) { // direction = 2 means reset! if (list[i][1] !== 2) { - // multicolumn sorting updating - choose the :last in case there are nested columns - f = c.$headers.not('.sorter-false').filter('[data-column="' + list[i][0] + '"]' + (len === 1 ? ':last' : '') ); + // multicolumn sorting updating - see #1005 + f = c.lastClickedIndex > 0 ? c.$headers.filter(':gt(' + ( c.lastClickedIndex - 1 ) + ')') : c.$headers; + // choose the :last in case there are nested columns + f = f.not('.sorter-false').filter('[data-column="' + list[i][0] + '"]' + (len === 1 ? ':last' : '') ); if (f.length) { for (j = 0; j < f.length; j++) { if (!f[j].sortDisabled) { @@ -1288,7 +1289,10 @@ $cell = $.fn.closest ? $(this).closest('th, td') : /TH|TD/.test(this.nodeName) ? $(this) : $(this).parents('th, td'); // reference original table headers and find the same cell // don't use $headers or IE8 throws an error - see #987 - cell = c.$headers[ $cell.prevAll().length ]; + temp = $headers.index( $cell ); + c.lastClickedIndex = ( temp < 0 ) ? $cell.attr('data-column') : temp; + // use column index if $headers is undefined + cell = c.$headers[ c.lastClickedIndex ]; if (cell && !cell.sortDisabled) { initSort(table, cell, e); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index b636971..0a34d47 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 08-23-2015 (v2.23.2)*/ +/*! tablesorter (FORK) - updated 09-01-2015 (v2.23.3)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -372,7 +372,7 @@ })(jQuery); -/*! Widget: filter - updated 8/23/2015 (v2.23.2) *//* +/*! Widget: filter - updated 9/1/2015 (v2.23.3) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -435,7 +435,7 @@ .split( ' ' ).join( c.namespace + 'filter ' ); $table .removeClass( 'hasFilters' ) - // add .tsfilter namespace to all BUT search + // add filter namespace to all BUT search .unbind( events.replace( ts.regex.spaces, ' ' ) ) // remove the filter row even if refreshing, because the column might have been moved .find( '.' + tscss.filterRow ).remove(); @@ -446,7 +446,7 @@ ts.processTbody( table, $tbody, false ); // restore tbody } if ( wo.filter_reset ) { - $( document ).undelegate( wo.filter_reset, 'click.tsfilter' ); + $( document ).undelegate( wo.filter_reset, 'click' + c.namespace + 'filter' ); } } }); @@ -816,8 +816,8 @@ } else if ( $( wo.filter_reset ).length ) { // reset is a jQuery selector, use event delegation $( document ) - .undelegate( wo.filter_reset, 'click.tsfilter' ) - .delegate( wo.filter_reset, 'click.tsfilter', function() { + .undelegate( wo.filter_reset, 'click' + c.namespace + 'filter' ) + .delegate( wo.filter_reset, 'click' + c.namespace + 'filter', function() { // trigger a reset event, so other functions ( filter_formatter ) know when to reset c.$table.trigger( 'filterReset' ); }); @@ -1132,8 +1132,8 @@ // don't get cached data, in case data-column changes dynamically var column = parseInt( $( this ).attr( 'data-column' ), 10 ); // don't allow 'change' event to process if the input value is the same - fixes #685 - if ( event.which === 13 || event.type === 'search' || - event.type === 'change' && this.value !== c.lastSearch[column] ) { + if ( wo.filter_initialized && ( event.which === 13 || event.type === 'search' || + event.type === 'change' && this.value !== c.lastSearch[column] ) ) { event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); @@ -1979,7 +1979,8 @@ } $column .val( setFilters[ i ] ) - .trigger( 'change.tsfilter' ); + // must include a namespace here; but not c.namespace + 'filter'? + .trigger( 'change' + c.namespace ); } else { filters[i] = $column.val() || ''; // don't change the first... it will move the cursor diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index db16084..fc628a3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 8/23/2015 (v2.23.2) *//* +/*! Widget: filter - updated 9/1/2015 (v2.23.3) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -61,7 +61,7 @@ .split( ' ' ).join( c.namespace + 'filter ' ); $table .removeClass( 'hasFilters' ) - // add .tsfilter namespace to all BUT search + // add filter namespace to all BUT search .unbind( events.replace( ts.regex.spaces, ' ' ) ) // remove the filter row even if refreshing, because the column might have been moved .find( '.' + tscss.filterRow ).remove(); @@ -72,7 +72,7 @@ ts.processTbody( table, $tbody, false ); // restore tbody } if ( wo.filter_reset ) { - $( document ).undelegate( wo.filter_reset, 'click.tsfilter' ); + $( document ).undelegate( wo.filter_reset, 'click' + c.namespace + 'filter' ); } } }); @@ -442,8 +442,8 @@ } else if ( $( wo.filter_reset ).length ) { // reset is a jQuery selector, use event delegation $( document ) - .undelegate( wo.filter_reset, 'click.tsfilter' ) - .delegate( wo.filter_reset, 'click.tsfilter', function() { + .undelegate( wo.filter_reset, 'click' + c.namespace + 'filter' ) + .delegate( wo.filter_reset, 'click' + c.namespace + 'filter', function() { // trigger a reset event, so other functions ( filter_formatter ) know when to reset c.$table.trigger( 'filterReset' ); }); @@ -758,8 +758,8 @@ // don't get cached data, in case data-column changes dynamically var column = parseInt( $( this ).attr( 'data-column' ), 10 ); // don't allow 'change' event to process if the input value is the same - fixes #685 - if ( event.which === 13 || event.type === 'search' || - event.type === 'change' && this.value !== c.lastSearch[column] ) { + if ( wo.filter_initialized && ( event.which === 13 || event.type === 'search' || + event.type === 'change' && this.value !== c.lastSearch[column] ) ) { event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); @@ -1605,7 +1605,8 @@ } $column .val( setFilters[ i ] ) - .trigger( 'change.tsfilter' ); + // must include a namespace here; but not c.namespace + 'filter'? + .trigger( 'change' + c.namespace ); } else { filters[i] = $column.val() || ''; // don't change the first... it will move the cursor diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index d0133da..b33eaf0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -1,4 +1,4 @@ -/*! Widget: grouping - updated 3/5/2015 (v2.21.0) *//* +/*! Widget: grouping - updated 9/1/2015 (v2.23.3) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -55,10 +55,13 @@ update : function(table, c, wo){ if ($.isEmptyObject(c.cache)) { return; } var rowIndex, tbodyIndex, currentGroup, $rows, groupClass, grouping, norm_rows, saveName, direction, - lang = wo.grouping_language, + hasSort = typeof c.sortList[0] !== 'undefined', group = '', + groupIndex = 0, savedGroup = false, - column = c.sortList[0] ? c.sortList[0][0] : -1; + column = typeof wo.group_forceColumn[0] !== 'undefined' ? + ( wo.group_enforceSort && !hasSort ? -1 : wo.group_forceColumn[0] ) : + ( hasSort ? c.sortList[0][0] : -1 ); c.$table .find('tr.group-hidden').removeClass('group-hidden').end() .find('tr.group-header').remove(); @@ -66,7 +69,7 @@ // clear pager saved spacer height (in case the rows are collapsed) c.$table.data('pagerSavedHeight', 0); } - if (column >= 0 && !c.$headerIndexed[column].hasClass('group-false')) { + if (column >= 0 && column < c.columns && !c.$headerIndexed[column].hasClass('group-false')) { wo.group_currentGroup = ''; // save current groups wo.group_currentGroups = {}; @@ -78,10 +81,10 @@ // save current grouping if (wo.group_collapsible && wo.group_saveGroups && ts.storage) { wo.group_currentGroups = ts.storage( table, 'tablesorter-groups' ) || {}; - // include direction when grouping numbers > 1 (reversed direction shows different range values) - direction = (grouping[1] === 'number' && grouping[2] > 1) ? 'dir' + c.sortList[0][1] : ''; + // include direction when saving groups (reversed numbers shows different range values) + direction = 'dir' + c.sortList[0][1]; // combine column, sort direction & grouping as save key - saveName = wo.group_currentGroup = '' + column + direction + grouping.join(''); + saveName = wo.group_currentGroup = '' + c.sortList[0][0] + direction + grouping.join(''); if (!wo.group_currentGroups[saveName]) { wo.group_currentGroups[saveName] = []; } else { @@ -98,7 +101,7 @@ if (ts.grouping.types[grouping[1]]) { currentGroup = norm_rows[rowIndex] ? ts.grouping.types[grouping[1]]( c, c.$headerIndexed[column], norm_rows[rowIndex][column], /date/.test(groupClass) ? - grouping[2] : parseInt(grouping[2] || 1, 10) || 1, group, lang ) : currentGroup; + grouping[2] : parseInt(grouping[2] || 1, 10) || 1, group ) : currentGroup; if (group !== currentGroup) { group = currentGroup; // show range if number > 1 @@ -110,9 +113,11 @@ currentGroup = wo.group_formatter((currentGroup || '').toString(), column, table, c, wo) || currentGroup; } $rows.eq(rowIndex).before('<tr class="group-header ' + c.selectorRemove.slice(1) + - '" unselectable="on"' + ( c.tabIndex ? ' tabindex="0"' : '' ) + '><td colspan="' + - c.columns + '">' + (wo.group_collapsible ? '<i/>' : '') + '<span class="group-name">' + - currentGroup + '</span><span class="group-count"></span></td></tr>'); + '" unselectable="on" ' + ( c.tabIndex ? 'tabindex="0" ' : '' ) + 'data-group-index="' + + ( groupIndex++ ) + '"><td colspan="' + c.columns + '">' + + ( wo.group_collapsible ? '<i/>' : '' ) + + '<span class="group-name">' + currentGroup + '</span>' + + '<span class="group-count"></span></td></tr>'); if (wo.group_saveGroups && !savedGroup && wo.group_collapsed && wo.group_collapsible) { // all groups start collapsed wo.group_currentGroups[wo.group_currentGroup].push(currentGroup); @@ -139,8 +144,8 @@ } } } - if (wo.group_saveGroups && wo.group_currentGroups.length && wo.group_currentGroups[wo.group_currentGroup].length) { - name = $row.find('.group-name').text().toLowerCase(); + if (wo.group_saveGroups && !$.isEmptyObject(wo.group_currentGroups) && wo.group_currentGroups[wo.group_currentGroup].length) { + name = $row.find('.group-name').text().toLowerCase() + $row.attr('data-group-index'); isHidden = $.inArray( name, wo.group_currentGroups[wo.group_currentGroup] ) > -1; $row.toggleClass('collapsed', isHidden); $rows.toggleClass('group-hidden', isHidden); @@ -163,7 +168,7 @@ if (event.type === 'keyup' && event.which !== 13) { return; } var isCollapsed, $groups, indx, $this = $(this), - name = $this.find('.group-name').text().toLowerCase(); + name = $this.find('.group-name').text().toLowerCase() + $this.attr('data-group-index'); // use shift-click to toggle ALL groups if (event.shiftKey && (event.type === 'click' || event.type === 'keyup')) { $this.siblings('.group-header').trigger('toggleGroup'); @@ -221,6 +226,10 @@ group_callback : null, // function($cell, $rows, column, table){}, callback allowing modification of the group header labels group_complete : 'groupingComplete', // event triggered on the table when the grouping widget has finished work + // apply the grouping widget only to selected column + group_forceColumn : [], // only the first value is used; set as an array for future expansion + group_enforceSort : true, // only apply group_forceColumn when a sort is applied to the table + // checkbox parser text used for checked/unchecked values group_checkbox : [ 'checked', 'unchecked' ], // change these default date names based on your language preferences From d93a812b8ede9810f0107b5c8c0f89df39cadf47 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Wed, 23 Sep 2015 21:51:36 +0200 Subject: [PATCH 067/138] Update tablesorter to latest version (2.23.4) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 103 +++++++++++------- .../jquery-tablesorter/jquery.tablesorter.js | 20 ++-- .../jquery.tablesorter.widgets.js | 83 +++++++++----- .../widgets/widget-filter.js | 81 +++++++++----- .../jquery-tablesorter/widgets/widget-math.js | 4 + 9 files changed, 196 insertions(+), 105 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbbf3d6..cff0e88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.18.4 (2015-09-23) + +* Upgrade tablesorter to v2.23.4 + #### v1.18.3 (2015-09-01) * Upgrade tablesorter to v2.23.3 diff --git a/README.md b/README.md index 814720a..7eecb03 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.23.3 (9/1/2015), [documentation] +Current tablesorter version: 2.23.4 (9/23/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index a8b255c..00d22f3 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.18.3' + VERSION = '1.18.4' end diff --git a/tablesorter b/tablesorter index 829d379..fa4f916 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 829d3792fa0ae15a524add665c4871c0c241daa6 +Subproject commit fa4f916633607e928e9c73c26a156ca0b50416ec diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 9aa295d..dec1655 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 09-01-2015 (v2.23.3)*/ +/*! tablesorter (FORK) - updated 09-23-2015 (v2.23.4)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.23.3 *//* +/*! TableSorter (FORK) v2.23.4 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -43,7 +43,7 @@ var ts = this; - ts.version = '2.23.3'; + ts.version = '2.23.4'; ts.parsers = []; ts.widgets = []; @@ -1606,7 +1606,7 @@ // *** sort functions *** // regex used in natural sort - ts.regex.chunk = /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi; // chunk/tokenize numbers & letters + ts.regex.chunk = /(^([+\-]?(?:\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi; // chunk/tokenize numbers & letters ts.regex.chunks = /(^\\0|\\0$)/; // replace chunks @ ends ts.regex.hex = /^0x[0-9a-f]+$/i; // hex @@ -1810,13 +1810,12 @@ ts.applyWidgetOptions = function( table, c ){ var indx, widget, - len = c.widgets.length, - wo = c.widgetOptions; + len = c.widgets.length; if (len) { for (indx = 0; indx < len; indx++) { widget = ts.getWidgetById( c.widgets[indx] ); if ( widget && 'options' in widget ) { - wo = table.config.widgetOptions = $.extend( true, {}, widget.options, wo ); + c.widgetOptions = $.extend( true, {}, widget.options, c.widgetOptions ); } } } @@ -1826,7 +1825,6 @@ table = $(table)[0]; // in case this is called externally var indx, len, names, widget, name, applied, c = table.config, - wo = c.widgetOptions, tableClass = ' ' + c.table.className + ' ', widgets = [], time, time2, w, wd; @@ -1884,14 +1882,14 @@ c.widgetInit[ name ] = true; if (table.hasInitialized) { // don't reapply widget options on tablesorter init - ts.applyWidgetOptions( table, c ); + ts.applyWidgetOptions( table, table.config ); } if ( 'init' in widget ) { applied = true; if (c.debug) { console[ console.group ? 'group' : 'log' ]( 'Initializing ' + name + ' widget' ); } - widget.init(table, widget, c, wo); + widget.init(table, widget, table.config, table.config.widgetOptions); } } if ( !init && 'format' in widget ) { @@ -1899,7 +1897,7 @@ if (c.debug) { console[ console.group ? 'group' : 'log' ]( 'Updating ' + name + ' widget' ); } - widget.format(table, c, wo, false); + widget.format(table, table.config, table.config.widgetOptions, false); } if (c.debug) { if (applied) { @@ -2109,7 +2107,7 @@ }); // set up debug logs - if ( !( console && console.log ) ) { + if ( !( window.console && window.console.log ) ) { ts.logs = []; /*jshint -W020 */ console = {}; @@ -2718,7 +2716,7 @@ })(jQuery); -/*! Widget: filter - updated 9/1/2015 (v2.23.3) *//* +/*! Widget: filter - updated 9/23/2015 (v2.23.4) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -2741,6 +2739,7 @@ options : { filter_childRows : false, // if true, filter includes child row content in the search filter_childByColumn : false, // ( filter_childRows must be true ) if true = search child rows by column; false = search all child row text grouped + filter_childWithSibs : true, // if true, include matching child row siblings filter_columnFilters : true, // if true, a filter will be added to the top of each table column filter_columnAnyMatch: true, // if true, allows using '#:{query}' in AnyMatch searches ( column:query ) filter_cellFilter : '', // css class name added to the filter cell ( string or array ) @@ -3077,7 +3076,7 @@ c.$table.addClass( 'hasFilters' ); // define timers so using clearTimeout won't cause an undefined error - wo.searchTimer = null; + wo.filter_searchTimer = null; wo.filter_initTimer = null; wo.filter_formatterCount = 0; wo.filter_formatterInit = []; @@ -3210,7 +3209,7 @@ .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) .append( options ); txt = wo.filter_selectSource; - fxn = $.isFunction( txt ) ? true : ts.getColumnData( table, txt, column ); + fxn = typeof txt === 'function' ? true : ts.getColumnData( table, txt, column ); if ( fxn ) { // updating so the extra options are appended tsf.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) ); @@ -3229,7 +3228,7 @@ } if ( wo.filter_hideFilters ) { - tsf.hideFilters( table, c ); + tsf.hideFilters( c ); } // show processing icon @@ -3356,7 +3355,7 @@ return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter; }, buildRow: function( table, c, wo ) { - var col, column, $header, buildSelect, disabled, name, ffxn, tmp, + var col, column, $header, makeSelect, disabled, name, ffxn, tmp, // c.columns defined in computeThIndexes() cellFilter = wo.filter_cellFilter, columns = c.columns, @@ -3380,14 +3379,14 @@ // assuming last cell of a column is the main column $header = c.$headerIndexed[ column ]; ffxn = ts.getColumnData( table, wo.filter_functions, column ); - buildSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || + makeSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || $header.hasClass( 'filter-select' ); // get data from jQuery data, metadata, headers option or header class name col = ts.getColumnData( table, c.headers, column ); disabled = ts.getData( $header[0], col, 'filter' ) === 'false' || ts.getData( $header[0], col, 'parser' ) === 'false'; - if ( buildSelect ) { + if ( makeSelect ) { buildFilter = $( '<select>' ).appendTo( c.$filters.eq( column ) ); } else { ffxn = ts.getColumnData( table, wo.filter_formatter, column ); @@ -3489,10 +3488,10 @@ }, searching: function( table, filter, skipFirst ) { var wo = table.config.widgetOptions; - clearTimeout( wo.searchTimer ); + clearTimeout( wo.filter_searchTimer ); if ( typeof filter === 'undefined' || filter === true ) { // delay filtering - wo.searchTimer = setTimeout( function() { + wo.filter_searchTimer = setTimeout( function() { tsf.checkFilters( table, filter, skipFirst ); }, wo.filter_liveSearch ? wo.filter_searchDelay : 10 ); } else { @@ -3550,7 +3549,7 @@ return false; } }, - hideFilters: function( table, c ) { + hideFilters: function( c ) { var timer; c.$table .find( '.' + tscss.filterRow ) @@ -3627,7 +3626,7 @@ targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, columns = [], val = $.trim( tsf.getLatestSearch( $input ).attr( 'data-column' ) || '' ); - if ( !/[,-]/.test(val) && val.length === 1 ) { + if ( /^[0-9]+$/.test(val)) { return parseInt( val, 10 ); } // process column range @@ -3841,7 +3840,7 @@ !table.config.widgetOptions.filter_initialized ) { return; } - var len, norm_rows, rowData, $rows, rowIndex, tbodyIndex, $tbody, columnIndex, + var len, norm_rows, rowData, $rows, $row, rowIndex, tbodyIndex, $tbody, columnIndex, isChild, childRow, lastSearch, showRow, time, val, indx, notFiltered, searchFiltered, query, injected, res, id, txt, storedFilters = $.extend( [], filters ), @@ -4029,24 +4028,37 @@ ''; } - showRow = tsf.processRow( c, data, vars ); + showRow = false; + val = tsf.processRow( c, data, vars ); childRow = rowData.$row.filter( ':gt( 0 )' ); - if ( wo.filter_childRows && childRow.length ) { + if ( !wo.filter_childWithSibs ) { + // hide all child rows + childRow.addClass( wo.filter_filteredRow ); + } if ( wo.filter_childByColumn ) { // cycle through each child row for ( indx = 0; indx < childRow.length; indx++ ) { data.$row = childRow.eq( indx ); data.cacheArray = rowData.child[ indx ]; data.rawArray = data.cacheArray; + val = tsf.processRow( c, data, vars ); // use OR comparison on child rows - showRow = showRow || tsf.processRow( c, data, vars ); + showRow = showRow || val; + if ( !wo.filter_childWithSibs && val ) { + childRow.eq( indx ).removeClass( wo.filter_filteredRow ); + } } } - childRow.toggleClass( wo.filter_filteredRow, !showRow ); + } else { + showRow = val; } - - rowData.$row + $row = rowData.$row; + // if only showing resulting child row, only include parent + if ( !wo.filter_childWithSibs ) { + $row = $row.eq( 0 ); + } + $row .toggleClass( wo.filter_filteredRow, !showRow )[0] .display = showRow ? '' : 'none'; } @@ -4074,14 +4086,12 @@ }, getOptionSource: function( table, column, onlyAvail ) { table = $( table )[0]; - var cts, txt, indx, len, - c = table.config, + var c = table.config, wo = c.widgetOptions, - parsed = [], arry = false, source = wo.filter_selectSource, last = c.$table.data( 'lastSearch' ) || [], - fxn = $.isFunction( source ) ? true : ts.getColumnData( table, source, column ); + fxn = typeof source === 'function' ? true : ts.getColumnData( table, source, column ); if ( onlyAvail && last[column] !== '' ) { onlyAvail = false; @@ -4100,11 +4110,25 @@ // custom select source function for a SPECIFIC COLUMN arry = fxn( table, column, onlyAvail ); } + if ( arry === false ) { // fall back to original method arry = tsf.getOptions( table, column, onlyAvail ); } + return tsf.processOptions( table, column, arry ); + + }, + processOptions: function( table, column, arry ) { + if ( !$.isArray( arry ) ) { + return false; + } + table = $( table )[0]; + var cts, txt, indx, len, + c = table.config, + validColumn = typeof column !== 'undefined' && column !== null && column >= 0 && column < c.columns, + parsed = []; + // get unique elements and sort the list // if $.tablesorter.sortText exists ( not in the original tablesorter ), // then natural sort the list otherwise use a basic sort @@ -4112,7 +4136,7 @@ return $.inArray( value, arry ) === indx; }); - if ( c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) { + if ( validColumn && c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) { // unsorted select options return arry; } else { @@ -4125,7 +4149,8 @@ parsed.push({ t : txt, // check parser length - fixes #934 - p : c.parsers && c.parsers.length && c.parsers[ column ].format( txt, table, [], column ) || txt + p : validColumn && c.parsers && c.parsers.length && + c.parsers[ column ].format( txt, table, [], column ) || txt }); } @@ -4135,10 +4160,10 @@ // sortNatural breaks if you don't pass it strings var x = a.p.toString(), y = b.p.toString(); - if ( $.isFunction( cts ) ) { + if ( validColumn && typeof cts === 'function' ) { // custom OVERALL text sorter return cts( x, y, true, column, table ); - } else if ( typeof cts === 'object' && cts.hasOwnProperty( column ) ) { + } else if ( validColumn && typeof cts === 'object' && cts.hasOwnProperty( column ) ) { // custom text sorter for a SPECIFIC COLUMN return cts[column]( x, y, true, column, table ); } else if ( ts.sortNatural ) { @@ -4196,6 +4221,7 @@ if ( !table.config.cache || $.isEmptyObject( table.config.cache ) ) { return; } + var indx, val, txt, t, $filters, $filter, c = table.config, wo = c.widgetOptions, @@ -4211,6 +4237,7 @@ .find( 'thead' ) .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) .val(); + // nothing included in arry ( external source ), so get the options from // filter_selectSource or column data if ( typeof arry === 'undefined' || arry === '' ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 42edb61..b941b5b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.23.3 *//* +/*! TableSorter (FORK) v2.23.4 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -25,7 +25,7 @@ var ts = this; - ts.version = '2.23.3'; + ts.version = '2.23.4'; ts.parsers = []; ts.widgets = []; @@ -1588,7 +1588,7 @@ // *** sort functions *** // regex used in natural sort - ts.regex.chunk = /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi; // chunk/tokenize numbers & letters + ts.regex.chunk = /(^([+\-]?(?:\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi; // chunk/tokenize numbers & letters ts.regex.chunks = /(^\\0|\\0$)/; // replace chunks @ ends ts.regex.hex = /^0x[0-9a-f]+$/i; // hex @@ -1792,13 +1792,12 @@ ts.applyWidgetOptions = function( table, c ){ var indx, widget, - len = c.widgets.length, - wo = c.widgetOptions; + len = c.widgets.length; if (len) { for (indx = 0; indx < len; indx++) { widget = ts.getWidgetById( c.widgets[indx] ); if ( widget && 'options' in widget ) { - wo = table.config.widgetOptions = $.extend( true, {}, widget.options, wo ); + c.widgetOptions = $.extend( true, {}, widget.options, c.widgetOptions ); } } } @@ -1808,7 +1807,6 @@ table = $(table)[0]; // in case this is called externally var indx, len, names, widget, name, applied, c = table.config, - wo = c.widgetOptions, tableClass = ' ' + c.table.className + ' ', widgets = [], time, time2, w, wd; @@ -1866,14 +1864,14 @@ c.widgetInit[ name ] = true; if (table.hasInitialized) { // don't reapply widget options on tablesorter init - ts.applyWidgetOptions( table, c ); + ts.applyWidgetOptions( table, table.config ); } if ( 'init' in widget ) { applied = true; if (c.debug) { console[ console.group ? 'group' : 'log' ]( 'Initializing ' + name + ' widget' ); } - widget.init(table, widget, c, wo); + widget.init(table, widget, table.config, table.config.widgetOptions); } } if ( !init && 'format' in widget ) { @@ -1881,7 +1879,7 @@ if (c.debug) { console[ console.group ? 'group' : 'log' ]( 'Updating ' + name + ' widget' ); } - widget.format(table, c, wo, false); + widget.format(table, table.config, table.config.widgetOptions, false); } if (c.debug) { if (applied) { @@ -2091,7 +2089,7 @@ }); // set up debug logs - if ( !( console && console.log ) ) { + if ( !( window.console && window.console.log ) ) { ts.logs = []; /*jshint -W020 */ console = {}; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 0a34d47..c421000 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 09-01-2015 (v2.23.3)*/ +/*! tablesorter (FORK) - updated 09-23-2015 (v2.23.4)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -372,7 +372,7 @@ })(jQuery); -/*! Widget: filter - updated 9/1/2015 (v2.23.3) *//* +/*! Widget: filter - updated 9/23/2015 (v2.23.4) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -395,6 +395,7 @@ options : { filter_childRows : false, // if true, filter includes child row content in the search filter_childByColumn : false, // ( filter_childRows must be true ) if true = search child rows by column; false = search all child row text grouped + filter_childWithSibs : true, // if true, include matching child row siblings filter_columnFilters : true, // if true, a filter will be added to the top of each table column filter_columnAnyMatch: true, // if true, allows using '#:{query}' in AnyMatch searches ( column:query ) filter_cellFilter : '', // css class name added to the filter cell ( string or array ) @@ -731,7 +732,7 @@ c.$table.addClass( 'hasFilters' ); // define timers so using clearTimeout won't cause an undefined error - wo.searchTimer = null; + wo.filter_searchTimer = null; wo.filter_initTimer = null; wo.filter_formatterCount = 0; wo.filter_formatterInit = []; @@ -864,7 +865,7 @@ .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) .append( options ); txt = wo.filter_selectSource; - fxn = $.isFunction( txt ) ? true : ts.getColumnData( table, txt, column ); + fxn = typeof txt === 'function' ? true : ts.getColumnData( table, txt, column ); if ( fxn ) { // updating so the extra options are appended tsf.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) ); @@ -883,7 +884,7 @@ } if ( wo.filter_hideFilters ) { - tsf.hideFilters( table, c ); + tsf.hideFilters( c ); } // show processing icon @@ -1010,7 +1011,7 @@ return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter; }, buildRow: function( table, c, wo ) { - var col, column, $header, buildSelect, disabled, name, ffxn, tmp, + var col, column, $header, makeSelect, disabled, name, ffxn, tmp, // c.columns defined in computeThIndexes() cellFilter = wo.filter_cellFilter, columns = c.columns, @@ -1034,14 +1035,14 @@ // assuming last cell of a column is the main column $header = c.$headerIndexed[ column ]; ffxn = ts.getColumnData( table, wo.filter_functions, column ); - buildSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || + makeSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || $header.hasClass( 'filter-select' ); // get data from jQuery data, metadata, headers option or header class name col = ts.getColumnData( table, c.headers, column ); disabled = ts.getData( $header[0], col, 'filter' ) === 'false' || ts.getData( $header[0], col, 'parser' ) === 'false'; - if ( buildSelect ) { + if ( makeSelect ) { buildFilter = $( '<select>' ).appendTo( c.$filters.eq( column ) ); } else { ffxn = ts.getColumnData( table, wo.filter_formatter, column ); @@ -1143,10 +1144,10 @@ }, searching: function( table, filter, skipFirst ) { var wo = table.config.widgetOptions; - clearTimeout( wo.searchTimer ); + clearTimeout( wo.filter_searchTimer ); if ( typeof filter === 'undefined' || filter === true ) { // delay filtering - wo.searchTimer = setTimeout( function() { + wo.filter_searchTimer = setTimeout( function() { tsf.checkFilters( table, filter, skipFirst ); }, wo.filter_liveSearch ? wo.filter_searchDelay : 10 ); } else { @@ -1204,7 +1205,7 @@ return false; } }, - hideFilters: function( table, c ) { + hideFilters: function( c ) { var timer; c.$table .find( '.' + tscss.filterRow ) @@ -1281,7 +1282,7 @@ targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, columns = [], val = $.trim( tsf.getLatestSearch( $input ).attr( 'data-column' ) || '' ); - if ( !/[,-]/.test(val) && val.length === 1 ) { + if ( /^[0-9]+$/.test(val)) { return parseInt( val, 10 ); } // process column range @@ -1495,7 +1496,7 @@ !table.config.widgetOptions.filter_initialized ) { return; } - var len, norm_rows, rowData, $rows, rowIndex, tbodyIndex, $tbody, columnIndex, + var len, norm_rows, rowData, $rows, $row, rowIndex, tbodyIndex, $tbody, columnIndex, isChild, childRow, lastSearch, showRow, time, val, indx, notFiltered, searchFiltered, query, injected, res, id, txt, storedFilters = $.extend( [], filters ), @@ -1683,24 +1684,37 @@ ''; } - showRow = tsf.processRow( c, data, vars ); + showRow = false; + val = tsf.processRow( c, data, vars ); childRow = rowData.$row.filter( ':gt( 0 )' ); - if ( wo.filter_childRows && childRow.length ) { + if ( !wo.filter_childWithSibs ) { + // hide all child rows + childRow.addClass( wo.filter_filteredRow ); + } if ( wo.filter_childByColumn ) { // cycle through each child row for ( indx = 0; indx < childRow.length; indx++ ) { data.$row = childRow.eq( indx ); data.cacheArray = rowData.child[ indx ]; data.rawArray = data.cacheArray; + val = tsf.processRow( c, data, vars ); // use OR comparison on child rows - showRow = showRow || tsf.processRow( c, data, vars ); + showRow = showRow || val; + if ( !wo.filter_childWithSibs && val ) { + childRow.eq( indx ).removeClass( wo.filter_filteredRow ); + } } } - childRow.toggleClass( wo.filter_filteredRow, !showRow ); + } else { + showRow = val; } - - rowData.$row + $row = rowData.$row; + // if only showing resulting child row, only include parent + if ( !wo.filter_childWithSibs ) { + $row = $row.eq( 0 ); + } + $row .toggleClass( wo.filter_filteredRow, !showRow )[0] .display = showRow ? '' : 'none'; } @@ -1728,14 +1742,12 @@ }, getOptionSource: function( table, column, onlyAvail ) { table = $( table )[0]; - var cts, txt, indx, len, - c = table.config, + var c = table.config, wo = c.widgetOptions, - parsed = [], arry = false, source = wo.filter_selectSource, last = c.$table.data( 'lastSearch' ) || [], - fxn = $.isFunction( source ) ? true : ts.getColumnData( table, source, column ); + fxn = typeof source === 'function' ? true : ts.getColumnData( table, source, column ); if ( onlyAvail && last[column] !== '' ) { onlyAvail = false; @@ -1754,11 +1766,25 @@ // custom select source function for a SPECIFIC COLUMN arry = fxn( table, column, onlyAvail ); } + if ( arry === false ) { // fall back to original method arry = tsf.getOptions( table, column, onlyAvail ); } + return tsf.processOptions( table, column, arry ); + + }, + processOptions: function( table, column, arry ) { + if ( !$.isArray( arry ) ) { + return false; + } + table = $( table )[0]; + var cts, txt, indx, len, + c = table.config, + validColumn = typeof column !== 'undefined' && column !== null && column >= 0 && column < c.columns, + parsed = []; + // get unique elements and sort the list // if $.tablesorter.sortText exists ( not in the original tablesorter ), // then natural sort the list otherwise use a basic sort @@ -1766,7 +1792,7 @@ return $.inArray( value, arry ) === indx; }); - if ( c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) { + if ( validColumn && c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) { // unsorted select options return arry; } else { @@ -1779,7 +1805,8 @@ parsed.push({ t : txt, // check parser length - fixes #934 - p : c.parsers && c.parsers.length && c.parsers[ column ].format( txt, table, [], column ) || txt + p : validColumn && c.parsers && c.parsers.length && + c.parsers[ column ].format( txt, table, [], column ) || txt }); } @@ -1789,10 +1816,10 @@ // sortNatural breaks if you don't pass it strings var x = a.p.toString(), y = b.p.toString(); - if ( $.isFunction( cts ) ) { + if ( validColumn && typeof cts === 'function' ) { // custom OVERALL text sorter return cts( x, y, true, column, table ); - } else if ( typeof cts === 'object' && cts.hasOwnProperty( column ) ) { + } else if ( validColumn && typeof cts === 'object' && cts.hasOwnProperty( column ) ) { // custom text sorter for a SPECIFIC COLUMN return cts[column]( x, y, true, column, table ); } else if ( ts.sortNatural ) { @@ -1850,6 +1877,7 @@ if ( !table.config.cache || $.isEmptyObject( table.config.cache ) ) { return; } + var indx, val, txt, t, $filters, $filter, c = table.config, wo = c.widgetOptions, @@ -1865,6 +1893,7 @@ .find( 'thead' ) .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) .val(); + // nothing included in arry ( external source ), so get the options from // filter_selectSource or column data if ( typeof arry === 'undefined' || arry === '' ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index fc628a3..0ba4ce9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 9/1/2015 (v2.23.3) *//* +/*! Widget: filter - updated 9/23/2015 (v2.23.4) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -21,6 +21,7 @@ options : { filter_childRows : false, // if true, filter includes child row content in the search filter_childByColumn : false, // ( filter_childRows must be true ) if true = search child rows by column; false = search all child row text grouped + filter_childWithSibs : true, // if true, include matching child row siblings filter_columnFilters : true, // if true, a filter will be added to the top of each table column filter_columnAnyMatch: true, // if true, allows using '#:{query}' in AnyMatch searches ( column:query ) filter_cellFilter : '', // css class name added to the filter cell ( string or array ) @@ -357,7 +358,7 @@ c.$table.addClass( 'hasFilters' ); // define timers so using clearTimeout won't cause an undefined error - wo.searchTimer = null; + wo.filter_searchTimer = null; wo.filter_initTimer = null; wo.filter_formatterCount = 0; wo.filter_formatterInit = []; @@ -490,7 +491,7 @@ .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) .append( options ); txt = wo.filter_selectSource; - fxn = $.isFunction( txt ) ? true : ts.getColumnData( table, txt, column ); + fxn = typeof txt === 'function' ? true : ts.getColumnData( table, txt, column ); if ( fxn ) { // updating so the extra options are appended tsf.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) ); @@ -509,7 +510,7 @@ } if ( wo.filter_hideFilters ) { - tsf.hideFilters( table, c ); + tsf.hideFilters( c ); } // show processing icon @@ -636,7 +637,7 @@ return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter; }, buildRow: function( table, c, wo ) { - var col, column, $header, buildSelect, disabled, name, ffxn, tmp, + var col, column, $header, makeSelect, disabled, name, ffxn, tmp, // c.columns defined in computeThIndexes() cellFilter = wo.filter_cellFilter, columns = c.columns, @@ -660,14 +661,14 @@ // assuming last cell of a column is the main column $header = c.$headerIndexed[ column ]; ffxn = ts.getColumnData( table, wo.filter_functions, column ); - buildSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || + makeSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || $header.hasClass( 'filter-select' ); // get data from jQuery data, metadata, headers option or header class name col = ts.getColumnData( table, c.headers, column ); disabled = ts.getData( $header[0], col, 'filter' ) === 'false' || ts.getData( $header[0], col, 'parser' ) === 'false'; - if ( buildSelect ) { + if ( makeSelect ) { buildFilter = $( '<select>' ).appendTo( c.$filters.eq( column ) ); } else { ffxn = ts.getColumnData( table, wo.filter_formatter, column ); @@ -769,10 +770,10 @@ }, searching: function( table, filter, skipFirst ) { var wo = table.config.widgetOptions; - clearTimeout( wo.searchTimer ); + clearTimeout( wo.filter_searchTimer ); if ( typeof filter === 'undefined' || filter === true ) { // delay filtering - wo.searchTimer = setTimeout( function() { + wo.filter_searchTimer = setTimeout( function() { tsf.checkFilters( table, filter, skipFirst ); }, wo.filter_liveSearch ? wo.filter_searchDelay : 10 ); } else { @@ -830,7 +831,7 @@ return false; } }, - hideFilters: function( table, c ) { + hideFilters: function( c ) { var timer; c.$table .find( '.' + tscss.filterRow ) @@ -907,7 +908,7 @@ targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, columns = [], val = $.trim( tsf.getLatestSearch( $input ).attr( 'data-column' ) || '' ); - if ( !/[,-]/.test(val) && val.length === 1 ) { + if ( /^[0-9]+$/.test(val)) { return parseInt( val, 10 ); } // process column range @@ -1121,7 +1122,7 @@ !table.config.widgetOptions.filter_initialized ) { return; } - var len, norm_rows, rowData, $rows, rowIndex, tbodyIndex, $tbody, columnIndex, + var len, norm_rows, rowData, $rows, $row, rowIndex, tbodyIndex, $tbody, columnIndex, isChild, childRow, lastSearch, showRow, time, val, indx, notFiltered, searchFiltered, query, injected, res, id, txt, storedFilters = $.extend( [], filters ), @@ -1309,24 +1310,37 @@ ''; } - showRow = tsf.processRow( c, data, vars ); + showRow = false; + val = tsf.processRow( c, data, vars ); childRow = rowData.$row.filter( ':gt( 0 )' ); - if ( wo.filter_childRows && childRow.length ) { + if ( !wo.filter_childWithSibs ) { + // hide all child rows + childRow.addClass( wo.filter_filteredRow ); + } if ( wo.filter_childByColumn ) { // cycle through each child row for ( indx = 0; indx < childRow.length; indx++ ) { data.$row = childRow.eq( indx ); data.cacheArray = rowData.child[ indx ]; data.rawArray = data.cacheArray; + val = tsf.processRow( c, data, vars ); // use OR comparison on child rows - showRow = showRow || tsf.processRow( c, data, vars ); + showRow = showRow || val; + if ( !wo.filter_childWithSibs && val ) { + childRow.eq( indx ).removeClass( wo.filter_filteredRow ); + } } } - childRow.toggleClass( wo.filter_filteredRow, !showRow ); + } else { + showRow = val; } - - rowData.$row + $row = rowData.$row; + // if only showing resulting child row, only include parent + if ( !wo.filter_childWithSibs ) { + $row = $row.eq( 0 ); + } + $row .toggleClass( wo.filter_filteredRow, !showRow )[0] .display = showRow ? '' : 'none'; } @@ -1354,14 +1368,12 @@ }, getOptionSource: function( table, column, onlyAvail ) { table = $( table )[0]; - var cts, txt, indx, len, - c = table.config, + var c = table.config, wo = c.widgetOptions, - parsed = [], arry = false, source = wo.filter_selectSource, last = c.$table.data( 'lastSearch' ) || [], - fxn = $.isFunction( source ) ? true : ts.getColumnData( table, source, column ); + fxn = typeof source === 'function' ? true : ts.getColumnData( table, source, column ); if ( onlyAvail && last[column] !== '' ) { onlyAvail = false; @@ -1380,11 +1392,25 @@ // custom select source function for a SPECIFIC COLUMN arry = fxn( table, column, onlyAvail ); } + if ( arry === false ) { // fall back to original method arry = tsf.getOptions( table, column, onlyAvail ); } + return tsf.processOptions( table, column, arry ); + + }, + processOptions: function( table, column, arry ) { + if ( !$.isArray( arry ) ) { + return false; + } + table = $( table )[0]; + var cts, txt, indx, len, + c = table.config, + validColumn = typeof column !== 'undefined' && column !== null && column >= 0 && column < c.columns, + parsed = []; + // get unique elements and sort the list // if $.tablesorter.sortText exists ( not in the original tablesorter ), // then natural sort the list otherwise use a basic sort @@ -1392,7 +1418,7 @@ return $.inArray( value, arry ) === indx; }); - if ( c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) { + if ( validColumn && c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) { // unsorted select options return arry; } else { @@ -1405,7 +1431,8 @@ parsed.push({ t : txt, // check parser length - fixes #934 - p : c.parsers && c.parsers.length && c.parsers[ column ].format( txt, table, [], column ) || txt + p : validColumn && c.parsers && c.parsers.length && + c.parsers[ column ].format( txt, table, [], column ) || txt }); } @@ -1415,10 +1442,10 @@ // sortNatural breaks if you don't pass it strings var x = a.p.toString(), y = b.p.toString(); - if ( $.isFunction( cts ) ) { + if ( validColumn && typeof cts === 'function' ) { // custom OVERALL text sorter return cts( x, y, true, column, table ); - } else if ( typeof cts === 'object' && cts.hasOwnProperty( column ) ) { + } else if ( validColumn && typeof cts === 'object' && cts.hasOwnProperty( column ) ) { // custom text sorter for a SPECIFIC COLUMN return cts[column]( x, y, true, column, table ); } else if ( ts.sortNatural ) { @@ -1476,6 +1503,7 @@ if ( !table.config.cache || $.isEmptyObject( table.config.cache ) ) { return; } + var indx, val, txt, t, $filters, $filter, c = table.config, wo = c.widgetOptions, @@ -1491,6 +1519,7 @@ .find( 'thead' ) .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) .val(); + // nothing included in arry ( external source ), so get the options from // filter_selectSource or column data if ( typeof arry === 'undefined' || arry === '' ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index b531faf..d28edae 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -465,6 +465,10 @@ }, 40 ); }); wo.math_isUpdating = false; + // math widget initialized after table - see #946 + if ( table.hasInitialized ) { + math.recalculate( c, wo, true ); + } }, // this remove function is called when using the refreshWidgets method or when destroying the tablesorter plugin // this function only applies to tablesorter v2.4+ From 204658a940586dad7495a1c2bce87468e3000faa Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Sun, 4 Oct 2015 20:43:23 +0200 Subject: [PATCH 068/138] Update tablesorter to latest version (2.23.5) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 10 ++- .../jquery.tablesorter.combined.js | 77 +++++++++++++------ .../jquery-tablesorter/jquery.tablesorter.js | 22 ++++-- .../jquery.tablesorter.widgets.js | 55 ++++++++----- .../widgets/widget-filter.js | 53 +++++++++---- .../widgets/widget-pager.js | 9 ++- 10 files changed, 159 insertions(+), 77 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cff0e88..0e870ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.18.5 (2015-10-04) + +* Upgrade tablesorter to v2.23.5 + #### v1.18.4 (2015-09-23) * Upgrade tablesorter to v2.23.4 diff --git a/README.md b/README.md index 7eecb03..14bae13 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.23.4 (9/23/2015), [documentation] +Current tablesorter version: 2.23.5 (10/4/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 00d22f3..b3dcd66 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.18.4' + VERSION = '1.18.5' end diff --git a/tablesorter b/tablesorter index fa4f916..40dcaac 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit fa4f916633607e928e9c73c26a156ca0b50416ec +Subproject commit 40dcaace25b749faab7b1e6c526456bc5ee0b136 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index feebf60..4a3e2df 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin - * updated 8/19/2015 (v2.23.1) + * updated 10/4/2015 (v2.23.5) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -618,8 +618,7 @@ } $t.trigger('pagerChange', p); } - - if ( !p.removeRows ) { + if ( !p.removeRows && !p.showAll ) { hideRows(table, p); } else { ts.clearTableBody(table); @@ -656,17 +655,18 @@ if ( p.ajax ) { pagerArrows(p, true); } else { - p.isDisabled = true; $.data(table, 'pagerLastPage', p.page); $.data(table, 'pagerLastSize', p.size); p.page = 0; p.size = p.totalRows; p.totalPages = 1; + p.showAll = true; $(table) .addClass('pagerDisabled') .removeAttr('aria-describedby') .find('tr.pagerSavedHeightSpacer').remove(); renderTable(table, table.config.rowsCopy, p); + p.isDisabled = true; $(table).trigger('applyWidgets'); if (table.config.debug) { console.log('Pager: Disabled'); @@ -826,6 +826,7 @@ .removeAttr('disabled') .attr('aria-disabled', 'false'); p.isDisabled = false; + p.showAll = false; p.page = $.data(table, 'pagerLastPage') || p.page || 0; p.size = $.data(table, 'pagerLastSize') || parseInt(p.$size.find('option[selected]').val(), 10) || p.size || p.settings.size || 10; p.$size.val(p.size); // set page size @@ -882,6 +883,7 @@ p.oldAjaxSuccess = p.oldAjaxSuccess || p.ajaxObject.success; c.appender = $this.appender; p.initializing = true; + p.showAll = false; if (p.savePages && ts.storage) { t = ts.storage(table, p.storageKey) || {}; // fixes #387 p.page = isNaN(t.page) ? p.page : t.page; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index dec1655..13c849d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 09-23-2015 (v2.23.4)*/ +/*! tablesorter (FORK) - updated 10-04-2015 (v2.23.5)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.23.4 *//* +/*! TableSorter (FORK) v2.23.5 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -43,7 +43,7 @@ var ts = this; - ts.version = '2.23.4'; + ts.version = '2.23.5'; ts.parsers = []; ts.widgets = []; @@ -531,19 +531,27 @@ } function updateHeader(table) { - var index, s, $th, col, + var index, isDisabled, $th, col, c = table.config, len = c.$headers.length; for ( index = 0; index < len; index++ ) { $th = c.$headers.eq( index ); col = ts.getColumnData( table, c.headers, index, true ); // add 'sorter-false' class if 'parser-false' is set - s = ts.getData( $th, col, 'sorter' ) === 'false' || ts.getData( $th, col, 'parser' ) === 'false'; - $th[0].sortDisabled = s; - $th[ s ? 'addClass' : 'removeClass' ]('sorter-false').attr('aria-disabled', '' + s); + isDisabled = ts.getData( $th, col, 'sorter' ) === 'false' || ts.getData( $th, col, 'parser' ) === 'false'; + $th[0].sortDisabled = isDisabled; + $th[ isDisabled ? 'addClass' : 'removeClass' ]( 'sorter-false' ).attr( 'aria-disabled', '' + isDisabled ); + // disable tab index on disabled cells + if ( c.tabIndex ) { + if ( isDisabled ) { + $th.removeAttr( 'tabindex' ); + } else { + $th.attr( 'tabindex', '0' ); + } + } // aria-controls - requires table ID if (table.id) { - if (s) { + if ( isDisabled ) { $th.removeAttr('aria-controls'); } else { $th.attr('aria-controls', table.id); @@ -2716,7 +2724,7 @@ })(jQuery); -/*! Widget: filter - updated 9/23/2015 (v2.23.4) *//* +/*! Widget: filter - updated 10/4/2015 (v2.23.5) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -2820,8 +2828,8 @@ // data.filters = array of filters for all columns ( some may be undefined ) // data.filter = filter for the current column // data.iFilter = same as data.filter, except lowercase ( if wo.filter_ignoreCase is true ) - // data.exact = table cell text ( or parsed data if column parser enabled ) - // data.iExact = same as data.exact, except lowercase ( if wo.filter_ignoreCase is true ) + // data.exact = table cell text ( or parsed data if column parser enabled; may be a number & not a string ) + // data.iExact = same as data.exact, except lowercase ( if wo.filter_ignoreCase is true; may be a number & not a string ) // data.cache = table cell text from cache, so it has been parsed ( & in all lower case if c.ignoreCase is true ) // data.cacheArray = An array of parsed content from each table cell in the row being processed // data.index = column index; table = table element ( DOM ) @@ -3802,7 +3810,8 @@ if ( fxn === true || hasSelect ) { // default selector uses exact match unless 'filter-match' class is found filterMatched = data.isMatch ? - data.iExact.search( data.iFilter ) >= 0 : + // data.iExact may be a number + ( '' + data.iExact ).search( data.iFilter ) >= 0 : data.filter === data.exact; } else if ( typeof fxn === 'function' ) { // filter callback( exact cell content, parser normalized content, @@ -3841,7 +3850,7 @@ return; } var len, norm_rows, rowData, $rows, $row, rowIndex, tbodyIndex, $tbody, columnIndex, - isChild, childRow, lastSearch, showRow, time, val, indx, + isChild, childRow, lastSearch, showRow, showParent, time, val, indx, notFiltered, searchFiltered, query, injected, res, id, txt, storedFilters = $.extend( [], filters ), regex = tsf.regex, @@ -4021,7 +4030,7 @@ // a match anywhere in the child row, then it will make the row visible // checked here so the option can be changed dynamically for ( indx = 0; indx < childRow.length; indx++ ) { - txt += ' ' + childRow[indx].join( '' ) || ''; + txt += ' ' + childRow[indx].join( ' ' ) || ''; } data.childRowText = wo.filter_childRows ? ( wo.filter_ignoreCase ? txt.toLowerCase() : txt ) : @@ -4029,14 +4038,20 @@ } showRow = false; - val = tsf.processRow( c, data, vars ); + showParent = tsf.processRow( c, data, vars ); + $row = rowData.$row; + + // don't pass reference to val + val = showParent ? true : false; childRow = rowData.$row.filter( ':gt( 0 )' ); if ( wo.filter_childRows && childRow.length ) { - if ( !wo.filter_childWithSibs ) { - // hide all child rows - childRow.addClass( wo.filter_filteredRow ); - } if ( wo.filter_childByColumn ) { + if ( !wo.filter_childWithSibs ) { + // hide all child rows + childRow.addClass( wo.filter_filteredRow ); + // if only showing resulting child row, only include parent + $row = $row.eq( 0 ); + } // cycle through each child row for ( indx = 0; indx < childRow.length; indx++ ) { data.$row = childRow.eq( indx ); @@ -4050,14 +4065,11 @@ } } } + // keep parent row match even if no child matches... see #1020 + showRow = showRow || showParent; } else { showRow = val; } - $row = rowData.$row; - // if only showing resulting child row, only include parent - if ( !wo.filter_childWithSibs ) { - $row = $row.eq( 0 ); - } $row .toggleClass( wo.filter_filteredRow, !showRow )[0] .display = showRow ? '' : 'none'; @@ -4184,7 +4196,7 @@ }, getOptions: function( table, column, onlyAvail ) { table = $( table )[0]; - var rowIndex, tbodyIndex, len, row, cache, + var rowIndex, tbodyIndex, len, row, cache, indx, child, childLen, c = table.config, wo = c.widgetOptions, arry = []; @@ -4207,9 +4219,24 @@ c.parsers[column].parsed || c.$headerIndexed[column].hasClass( 'filter-parsed' ) ) { arry.push( '' + cache.normalized[ rowIndex ][ column ] ); + // child row parsed data + if ( wo.filter_childRows && wo.filter_childByColumn ) { + childLen = cache.normalized[ rowIndex ][ c.columns ].$row.length - 1; + for ( indx = 0; indx < childLen; indx++ ) { + arry.push( '' + cache.normalized[ rowIndex ][ c.columns ].child[ indx ][ column ] ); + } + } } else { // get raw cached data instead of content directly from the cells arry.push( cache.normalized[ rowIndex ][ c.columns ].raw[ column ] ); + // child row unparsed data + if ( wo.filter_childRows && wo.filter_childByColumn ) { + childLen = cache.normalized[ rowIndex ][ c.columns ].$row.length; + for ( indx = 1; indx < childLen; indx++ ) { + child = cache.normalized[ rowIndex ][ c.columns ].$row.eq( indx ).children().eq( column ); + arry.push( '' + ts.getElementText( c, child, column ) ); + } + } } } } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index b941b5b..4243076 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.23.4 *//* +/*! TableSorter (FORK) v2.23.5 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -25,7 +25,7 @@ var ts = this; - ts.version = '2.23.4'; + ts.version = '2.23.5'; ts.parsers = []; ts.widgets = []; @@ -513,19 +513,27 @@ } function updateHeader(table) { - var index, s, $th, col, + var index, isDisabled, $th, col, c = table.config, len = c.$headers.length; for ( index = 0; index < len; index++ ) { $th = c.$headers.eq( index ); col = ts.getColumnData( table, c.headers, index, true ); // add 'sorter-false' class if 'parser-false' is set - s = ts.getData( $th, col, 'sorter' ) === 'false' || ts.getData( $th, col, 'parser' ) === 'false'; - $th[0].sortDisabled = s; - $th[ s ? 'addClass' : 'removeClass' ]('sorter-false').attr('aria-disabled', '' + s); + isDisabled = ts.getData( $th, col, 'sorter' ) === 'false' || ts.getData( $th, col, 'parser' ) === 'false'; + $th[0].sortDisabled = isDisabled; + $th[ isDisabled ? 'addClass' : 'removeClass' ]( 'sorter-false' ).attr( 'aria-disabled', '' + isDisabled ); + // disable tab index on disabled cells + if ( c.tabIndex ) { + if ( isDisabled ) { + $th.removeAttr( 'tabindex' ); + } else { + $th.attr( 'tabindex', '0' ); + } + } // aria-controls - requires table ID if (table.id) { - if (s) { + if ( isDisabled ) { $th.removeAttr('aria-controls'); } else { $th.attr('aria-controls', table.id); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index c421000..6f5e545 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 09-23-2015 (v2.23.4)*/ +/*! tablesorter (FORK) - updated 10-04-2015 (v2.23.5)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -372,7 +372,7 @@ })(jQuery); -/*! Widget: filter - updated 9/23/2015 (v2.23.4) *//* +/*! Widget: filter - updated 10/4/2015 (v2.23.5) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -476,8 +476,8 @@ // data.filters = array of filters for all columns ( some may be undefined ) // data.filter = filter for the current column // data.iFilter = same as data.filter, except lowercase ( if wo.filter_ignoreCase is true ) - // data.exact = table cell text ( or parsed data if column parser enabled ) - // data.iExact = same as data.exact, except lowercase ( if wo.filter_ignoreCase is true ) + // data.exact = table cell text ( or parsed data if column parser enabled; may be a number & not a string ) + // data.iExact = same as data.exact, except lowercase ( if wo.filter_ignoreCase is true; may be a number & not a string ) // data.cache = table cell text from cache, so it has been parsed ( & in all lower case if c.ignoreCase is true ) // data.cacheArray = An array of parsed content from each table cell in the row being processed // data.index = column index; table = table element ( DOM ) @@ -1458,7 +1458,8 @@ if ( fxn === true || hasSelect ) { // default selector uses exact match unless 'filter-match' class is found filterMatched = data.isMatch ? - data.iExact.search( data.iFilter ) >= 0 : + // data.iExact may be a number + ( '' + data.iExact ).search( data.iFilter ) >= 0 : data.filter === data.exact; } else if ( typeof fxn === 'function' ) { // filter callback( exact cell content, parser normalized content, @@ -1497,7 +1498,7 @@ return; } var len, norm_rows, rowData, $rows, $row, rowIndex, tbodyIndex, $tbody, columnIndex, - isChild, childRow, lastSearch, showRow, time, val, indx, + isChild, childRow, lastSearch, showRow, showParent, time, val, indx, notFiltered, searchFiltered, query, injected, res, id, txt, storedFilters = $.extend( [], filters ), regex = tsf.regex, @@ -1677,7 +1678,7 @@ // a match anywhere in the child row, then it will make the row visible // checked here so the option can be changed dynamically for ( indx = 0; indx < childRow.length; indx++ ) { - txt += ' ' + childRow[indx].join( '' ) || ''; + txt += ' ' + childRow[indx].join( ' ' ) || ''; } data.childRowText = wo.filter_childRows ? ( wo.filter_ignoreCase ? txt.toLowerCase() : txt ) : @@ -1685,14 +1686,20 @@ } showRow = false; - val = tsf.processRow( c, data, vars ); + showParent = tsf.processRow( c, data, vars ); + $row = rowData.$row; + + // don't pass reference to val + val = showParent ? true : false; childRow = rowData.$row.filter( ':gt( 0 )' ); if ( wo.filter_childRows && childRow.length ) { - if ( !wo.filter_childWithSibs ) { - // hide all child rows - childRow.addClass( wo.filter_filteredRow ); - } if ( wo.filter_childByColumn ) { + if ( !wo.filter_childWithSibs ) { + // hide all child rows + childRow.addClass( wo.filter_filteredRow ); + // if only showing resulting child row, only include parent + $row = $row.eq( 0 ); + } // cycle through each child row for ( indx = 0; indx < childRow.length; indx++ ) { data.$row = childRow.eq( indx ); @@ -1706,14 +1713,11 @@ } } } + // keep parent row match even if no child matches... see #1020 + showRow = showRow || showParent; } else { showRow = val; } - $row = rowData.$row; - // if only showing resulting child row, only include parent - if ( !wo.filter_childWithSibs ) { - $row = $row.eq( 0 ); - } $row .toggleClass( wo.filter_filteredRow, !showRow )[0] .display = showRow ? '' : 'none'; @@ -1840,7 +1844,7 @@ }, getOptions: function( table, column, onlyAvail ) { table = $( table )[0]; - var rowIndex, tbodyIndex, len, row, cache, + var rowIndex, tbodyIndex, len, row, cache, indx, child, childLen, c = table.config, wo = c.widgetOptions, arry = []; @@ -1863,9 +1867,24 @@ c.parsers[column].parsed || c.$headerIndexed[column].hasClass( 'filter-parsed' ) ) { arry.push( '' + cache.normalized[ rowIndex ][ column ] ); + // child row parsed data + if ( wo.filter_childRows && wo.filter_childByColumn ) { + childLen = cache.normalized[ rowIndex ][ c.columns ].$row.length - 1; + for ( indx = 0; indx < childLen; indx++ ) { + arry.push( '' + cache.normalized[ rowIndex ][ c.columns ].child[ indx ][ column ] ); + } + } } else { // get raw cached data instead of content directly from the cells arry.push( cache.normalized[ rowIndex ][ c.columns ].raw[ column ] ); + // child row unparsed data + if ( wo.filter_childRows && wo.filter_childByColumn ) { + childLen = cache.normalized[ rowIndex ][ c.columns ].$row.length; + for ( indx = 1; indx < childLen; indx++ ) { + child = cache.normalized[ rowIndex ][ c.columns ].$row.eq( indx ).children().eq( column ); + arry.push( '' + ts.getElementText( c, child, column ) ); + } + } } } } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 0ba4ce9..739f0b0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 9/23/2015 (v2.23.4) *//* +/*! Widget: filter - updated 10/4/2015 (v2.23.5) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -102,8 +102,8 @@ // data.filters = array of filters for all columns ( some may be undefined ) // data.filter = filter for the current column // data.iFilter = same as data.filter, except lowercase ( if wo.filter_ignoreCase is true ) - // data.exact = table cell text ( or parsed data if column parser enabled ) - // data.iExact = same as data.exact, except lowercase ( if wo.filter_ignoreCase is true ) + // data.exact = table cell text ( or parsed data if column parser enabled; may be a number & not a string ) + // data.iExact = same as data.exact, except lowercase ( if wo.filter_ignoreCase is true; may be a number & not a string ) // data.cache = table cell text from cache, so it has been parsed ( & in all lower case if c.ignoreCase is true ) // data.cacheArray = An array of parsed content from each table cell in the row being processed // data.index = column index; table = table element ( DOM ) @@ -1084,7 +1084,8 @@ if ( fxn === true || hasSelect ) { // default selector uses exact match unless 'filter-match' class is found filterMatched = data.isMatch ? - data.iExact.search( data.iFilter ) >= 0 : + // data.iExact may be a number + ( '' + data.iExact ).search( data.iFilter ) >= 0 : data.filter === data.exact; } else if ( typeof fxn === 'function' ) { // filter callback( exact cell content, parser normalized content, @@ -1123,7 +1124,7 @@ return; } var len, norm_rows, rowData, $rows, $row, rowIndex, tbodyIndex, $tbody, columnIndex, - isChild, childRow, lastSearch, showRow, time, val, indx, + isChild, childRow, lastSearch, showRow, showParent, time, val, indx, notFiltered, searchFiltered, query, injected, res, id, txt, storedFilters = $.extend( [], filters ), regex = tsf.regex, @@ -1303,7 +1304,7 @@ // a match anywhere in the child row, then it will make the row visible // checked here so the option can be changed dynamically for ( indx = 0; indx < childRow.length; indx++ ) { - txt += ' ' + childRow[indx].join( '' ) || ''; + txt += ' ' + childRow[indx].join( ' ' ) || ''; } data.childRowText = wo.filter_childRows ? ( wo.filter_ignoreCase ? txt.toLowerCase() : txt ) : @@ -1311,14 +1312,20 @@ } showRow = false; - val = tsf.processRow( c, data, vars ); + showParent = tsf.processRow( c, data, vars ); + $row = rowData.$row; + + // don't pass reference to val + val = showParent ? true : false; childRow = rowData.$row.filter( ':gt( 0 )' ); if ( wo.filter_childRows && childRow.length ) { - if ( !wo.filter_childWithSibs ) { - // hide all child rows - childRow.addClass( wo.filter_filteredRow ); - } if ( wo.filter_childByColumn ) { + if ( !wo.filter_childWithSibs ) { + // hide all child rows + childRow.addClass( wo.filter_filteredRow ); + // if only showing resulting child row, only include parent + $row = $row.eq( 0 ); + } // cycle through each child row for ( indx = 0; indx < childRow.length; indx++ ) { data.$row = childRow.eq( indx ); @@ -1332,14 +1339,11 @@ } } } + // keep parent row match even if no child matches... see #1020 + showRow = showRow || showParent; } else { showRow = val; } - $row = rowData.$row; - // if only showing resulting child row, only include parent - if ( !wo.filter_childWithSibs ) { - $row = $row.eq( 0 ); - } $row .toggleClass( wo.filter_filteredRow, !showRow )[0] .display = showRow ? '' : 'none'; @@ -1466,7 +1470,7 @@ }, getOptions: function( table, column, onlyAvail ) { table = $( table )[0]; - var rowIndex, tbodyIndex, len, row, cache, + var rowIndex, tbodyIndex, len, row, cache, indx, child, childLen, c = table.config, wo = c.widgetOptions, arry = []; @@ -1489,9 +1493,24 @@ c.parsers[column].parsed || c.$headerIndexed[column].hasClass( 'filter-parsed' ) ) { arry.push( '' + cache.normalized[ rowIndex ][ column ] ); + // child row parsed data + if ( wo.filter_childRows && wo.filter_childByColumn ) { + childLen = cache.normalized[ rowIndex ][ c.columns ].$row.length - 1; + for ( indx = 0; indx < childLen; indx++ ) { + arry.push( '' + cache.normalized[ rowIndex ][ c.columns ].child[ indx ][ column ] ); + } + } } else { // get raw cached data instead of content directly from the cells arry.push( cache.normalized[ rowIndex ][ c.columns ].raw[ column ] ); + // child row unparsed data + if ( wo.filter_childRows && wo.filter_childByColumn ) { + childLen = cache.normalized[ rowIndex ][ c.columns ].$row.length; + for ( indx = 1; indx < childLen; indx++ ) { + child = cache.normalized[ rowIndex ][ c.columns ].$row.eq( indx ).children().eq( column ); + arry.push( '' + ts.getElementText( c, child, column ) ); + } + } } } } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index fef33cb..c66e4e3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 8/19/2015 (v2.23.1) */ +/*! Widget: Pager - updated 10/4/2015 (v2.23.5) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -171,6 +171,7 @@ p.oldAjaxSuccess = p.oldAjaxSuccess || wo.pager_ajaxObject.success; c.appender = tsp.appender; p.initializing = true; + p.showAll = false; if (wo.pager_savePages && ts.storage) { t = ts.storage(table, wo.pager_storageKey) || {}; // fixes #387 p.page = ( isNaN(t.page) ? p.page : t.page ) || p.setPage || 0; @@ -878,7 +879,7 @@ } c.$table.trigger('pagerChange', c); } - if ( !wo.pager_removeRows ) { + if ( !wo.pager_removeRows && !p.showAll ) { tsp.hideRows(table, c); } else { ts.clearTableBody(table); @@ -921,17 +922,18 @@ if ( p.ajax ) { tsp.pagerArrows(c, true); } else { - p.isDisabled = true; $.data(table, 'pagerLastPage', p.page); $.data(table, 'pagerLastSize', p.size); p.page = 0; p.size = p.totalRows; p.totalPages = 1; + p.showAll = true; c.$table .addClass('pagerDisabled') .removeAttr('aria-describedby') .find('tr.pagerSavedHeightSpacer').remove(); tsp.renderTable(table, c.rowsCopy); + p.isDisabled = true; c.$table.trigger('applyWidgets'); if (c.debug) { console.log('Pager: Disabled'); @@ -1100,6 +1102,7 @@ enablePager: function(table, c, triggered){ var info, p = c.pager; p.isDisabled = false; + p.showAll = false; p.page = $.data(table, 'pagerLastPage') || p.page || 0; p.size = $.data(table, 'pagerLastSize') || parseInt(p.$size.find('option[selected]').val(), 10) || p.size || p.setSize || 10; p.$size.val(p.size); // set page size From 120a947377e9168786fdf011f56c0bff4c35486d Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Tue, 3 Nov 2015 22:56:25 +0100 Subject: [PATCH 069/138] Update tablesorter to latest version (2.24.2) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 114 +- .../extras/jquery.dragtable.mod.js | 6 +- .../jquery.tablesorter.combined.js | 4483 +++++++++-------- .../jquery-tablesorter/jquery.tablesorter.js | 4337 ++++++++-------- .../jquery.tablesorter.widgets.js | 156 +- .../parsers/parser-date-month.js | 65 +- .../parsers/parser-date-weekday.js | 98 +- .../parsers/parser-globalize.js | 52 +- .../parsers/parser-input-select.js | 8 +- .../widgets/widget-chart.js | 4 +- .../widgets/widget-columnSelector.js | 152 +- .../widgets/widget-editable.js | 14 +- .../widgets/widget-filter.js | 137 +- .../widgets/widget-grouping.js | 337 +- .../widgets/widget-headerTitles.js | 9 +- .../widgets/widget-lazyload.js | 367 ++ .../jquery-tablesorter/widgets/widget-math.js | 116 +- .../widgets/widget-output.js | 6 +- .../widgets/widget-pager.js | 132 +- .../widgets/widget-print.js | 39 +- .../widgets/widget-saveSort.js | 7 +- .../widgets/widget-scroller.js | 18 +- .../widgets/widget-sort2Hash.js | 199 +- .../widgets/widget-sortTbodies.js | 14 +- .../widgets/widget-staticRow.js | 4 +- .../widgets/widget-stickyHeaders.js | 4 +- .../jquery-tablesorter/widgets/widget-view.js | 192 + 31 files changed, 6361 insertions(+), 4719 deletions(-) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-lazyload.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-view.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e870ef..164a672 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.19.0 (2015-11-03) + +* Upgrade tablesorter to v2.24.2 + #### v1.18.5 (2015-10-04) * Upgrade tablesorter to v2.23.5 diff --git a/README.md b/README.md index 14bae13..f4b8d91 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.23.5 (10/4/2015), [documentation] +Current tablesorter version: 2.24.2 (11/2/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index b3dcd66..8901b9e 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.18.5' + VERSION = '1.19.0' end diff --git a/tablesorter b/tablesorter index 40dcaac..73153f2 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 40dcaace25b749faab7b1e6c526456bc5ee0b136 +Subproject commit 73153f2bdf4c4bcc7c228374f066034a4bffa9c0 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 4a3e2df..aeb0aa4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin - * updated 10/4/2015 (v2.23.5) + * updated 10/31/2015 (v2.24.0) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -165,10 +165,11 @@ var s, t, $out, indx, len, options, c = table.config, namespace = c.namespace + 'pager', - sz = p.size || p.settings.size || 10; // don't allow dividing by zero + sz = parsePageSize( p, p.size, 'get' ); // don't allow dividing by zero if (p.countChildRows) { t.push(c.cssChildRow); } p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method c.totalRows = p.totalRows; + parsePageNumber( p ); calcFilters(table, p); c.filteredRows = p.filteredRows; p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; @@ -368,7 +369,8 @@ }, hideRowsSetup = function(table, p){ - p.size = parseInt( p.$size.val(), 10 ) || p.size || p.settings.size || 10; + p.size = parsePageSize( p, p.$size.val(), 'get' ); + p.$size.val( parsePageSize( p, p.size, 'set' ) ); $.data(table, 'pagerLastSize', p.size); pagerArrows(p); if ( !p.removeRows ) { @@ -424,7 +426,7 @@ if (d instanceof jQuery) { if (p.processAjaxOnInit) { // append jQuery object - c.$tbodies.eq(0).children('tr').detach(); + c.$tbodies.eq(0).empty(); c.$tbodies.eq(0).append(d); } } else if (l) { @@ -441,6 +443,9 @@ if (p.processAjaxOnInit) { c.$tbodies.eq(0).html( tds ); } + } else { + // nothing returned by ajax, empty out the table; see #1032 + c.$tbodies.eq(0).empty(); } p.processAjaxOnInit = true; // only add new header text if the length matches @@ -476,12 +481,13 @@ } // make sure last pager settings are saved, prevents multiple server side calls with // the same parameters - p.totalPages = Math.ceil( p.totalRows / ( p.size || p.settings.size || 10 ) ); + p.totalPages = Math.ceil( p.totalRows / parsePageSize( p, p.size, 'get' ) ); p.last.totalRows = p.totalRows; p.last.currentFilters = p.currentFilters; p.last.sortList = (c.sortList || []).join(','); updatePageDisplay(table, p, false); - $table.trigger('updateCache', [ function(){ + // tablesorter core updateCache (not pager) + ts.updateCache( c, function(){ if (p.initialized) { // apply widgets after table has rendered & after a delay to prevent // multiple applyWidget blocking code from blocking this trigger @@ -489,13 +495,12 @@ if (c.debug) { console.log('Pager: Triggering pagerChange'); } - $table - .trigger('applyWidgets') - .trigger('pagerChange', p); + $table.trigger( 'pagerChange', p ); + ts.applyWidget( table ); updatePageDisplay(table, p, true); }, 0); } - } ]); + }); } if (!p.initialized) { @@ -504,9 +509,8 @@ if (table.config.debug) { console.log('Pager: Triggering pagerInitialized'); } - $(table) - .trigger('applyWidgets') - .trigger('pagerInitialized', p); + $(table).trigger( 'pagerInitialized', p ); + ts.applyWidget( table ); updatePageDisplay(table, p); } }, @@ -616,9 +620,9 @@ if (c.debug) { console.log('Pager: Triggering pagerChange'); } - $t.trigger('pagerChange', p); + $t.trigger( 'pagerChange', p ); } - if ( !p.removeRows && !p.showAll ) { + if ( !p.removeRows ) { hideRows(table, p); } else { ts.clearTableBody(table); @@ -660,14 +664,13 @@ p.page = 0; p.size = p.totalRows; p.totalPages = 1; - p.showAll = true; $(table) .addClass('pagerDisabled') .removeAttr('aria-describedby') .find('tr.pagerSavedHeightSpacer').remove(); renderTable(table, table.config.rowsCopy, p); p.isDisabled = true; - $(table).trigger('applyWidgets'); + ts.applyWidget( table ); if (table.config.debug) { console.log('Pager: Disabled'); } @@ -688,7 +691,8 @@ updateCache = function(table) { var c = table.config, p = c.pager; - c.$table.trigger('updateCache', [ function(){ + // tablesorter core updateCache (not pager) + ts.updateCache( c, function(){ var i, rows = [], n = table.config.cache[0].normalized; @@ -698,12 +702,12 @@ } c.rowsCopy = rows; moveToPage(table, p, true); - } ]); + }); }, moveToPage = function(table, p, pageMoved) { if ( p.isDisabled ) { return; } - var pg, c = table.config, + var c = table.config, $t = $(table), l = p.last; if ( pageMoved !== false && p.initialized && ts.isEmptyObject(c.cache)) { @@ -711,10 +715,9 @@ } // abort page move if the table has filters and has not been initialized if (p.ajax && ts.hasWidget(table, 'filter') && !c.widgetOptions.filter_initialized) { return; } + + parsePageNumber( p ); calcFilters(table, p); - pg = Math.min( p.totalPages, p.filteredPages ); - if ( p.page < 0 ) { p.page = 0; } - if ( p.page > ( pg - 1 ) && pg !== 0 ) { p.page = pg - 1; } // fixes issue where one currentFilter is [] and the other is ['','',''], // making the next if comparison think the filters are different (joined by commas). Fixes #202. l.currentFilters = (l.currentFilters || []).join('') === '' ? [] : l.currentFilters; @@ -750,9 +753,8 @@ if (c.debug) { console.log('Pager: Triggering pageMoved'); } - $t - .trigger('pageMoved', p) - .trigger('applyWidgets'); + $t.trigger('pageMoved', p); + ts.applyWidget( table ); if (table.isUpdating) { if (c.debug) { console.log('Pager: Triggering updateComplete'); @@ -762,10 +764,29 @@ } }, + // set to either set or get value + parsePageSize = function( p, size, mode ) { + var s = parseInt( size, 10 ) || p.size || p.settings.size || 10, + // if select does not contain an "all" option, use size + setAll = p.$size.find( 'option[value="all"]' ).length ? 'all' : p.totalRows; + return /all/i.test( size ) || s === p.totalRows ? + // "get" to set `p.size` or "set" to set `p.$size.val()` + ( mode === 'get' ? p.totalRows : setAll ) : + ( mode === 'get' ? s : p.size ); + }, + + parsePageNumber = function( p ) { + var min = Math.min( p.totalPages, p.filteredPages ) - 1; + p.page = parseInt( p.page, 10 ); + if ( p.page < 0 || isNaN( p.page ) ) { p.page = 0; } + if ( p.page > min && p.page !== 0 ) { p.page = min; } + return p.page; + }, + setPageSize = function(table, size, p) { - p.size = size || p.size || p.settings.size || 10; - p.$size.val(p.size); - $.data(table, 'pagerLastPage', p.page); + p.size = parsePageSize( p, size, 'get' ); + p.$size.val( parsePageSize( p, p.size, 'set' ) ); + $.data(table, 'pagerLastPage', parsePageNumber( p ) ); $.data(table, 'pagerLastSize', p.size); p.totalPages = Math.ceil( p.totalRows / p.size ); p.filteredPages = Math.ceil( p.filteredRows / p.size ); @@ -819,17 +840,17 @@ }, enablePager = function(table, p, triggered) { - var info, + var info, size, c = table.config; p.$size.add(p.$goto).add(p.$container.find('.ts-startRow, .ts-page')) .removeClass(p.cssDisabled) .removeAttr('disabled') .attr('aria-disabled', 'false'); p.isDisabled = false; - p.showAll = false; p.page = $.data(table, 'pagerLastPage') || p.page || 0; - p.size = $.data(table, 'pagerLastSize') || parseInt(p.$size.find('option[selected]').val(), 10) || p.size || p.settings.size || 10; - p.$size.val(p.size); // set page size + size = p.$size.find('option[selected]').val(); + p.size = $.data(table, 'pagerLastSize') || parsePageSize( p, p.size, 'get' ); + p.$size.val( parsePageSize( p, p.size, 'set' ) ); // set page size p.totalPages = Math.ceil( Math.min( p.totalRows, p.filteredRows ) / p.size ); // if table id exists, include page display with aria info if ( table.id ) { @@ -839,7 +860,8 @@ } changeHeight(table, p); if ( triggered ) { - c.$table.trigger('updateRows'); + // tablesorter core update table + ts.update( c ); setPageSize(table, p.size, p); hideRowsSetup(table, p); if (c.debug) { @@ -883,7 +905,6 @@ p.oldAjaxSuccess = p.oldAjaxSuccess || p.ajaxObject.success; c.appender = $this.appender; p.initializing = true; - p.showAll = false; if (p.savePages && ts.storage) { t = ts.storage(table, p.storageKey) || {}; // fixes #387 p.page = isNaN(t.page) ? p.page : t.page; @@ -914,7 +935,7 @@ } updatePageDisplay(table, p, false); moveToPage(table, p, false); - c.$table.trigger('applyWidgets'); + ts.applyWidget( table ); } }) .bind('disablePager' + namespace, function(e){ @@ -948,27 +969,27 @@ changeHeight(table, p); updatePageDisplay(table, p, true); }) - .bind('pageSize refreshComplete '.split(' ').join(namespace + ' '), function(e, v){ + .bind('pageSize refreshComplete '.split(' ').join(namespace + ' '), function(e, size){ e.stopPropagation(); - setPageSize(table, parseInt(v, 10) || p.settings.size || 10, p); + setPageSize(table, parsePageSize( p, size, 'get' ), p); hideRows(table, p); updatePageDisplay(table, p, false); }) - .bind('pageSet pagerUpdate '.split(' ').join(namespace + ' '), function(e, v){ + .bind('pageSet pagerUpdate '.split(' ').join(namespace + ' '), function(e, num){ e.stopPropagation(); // force pager refresh if (e.type === 'pagerUpdate') { - v = typeof v === 'undefined' ? p.page + 1 : v; + num = typeof num === 'undefined' ? p.page + 1 : num; p.last.page = true; } - p.page = (parseInt(v, 10) || 1) - 1; + p.page = (parseInt(num, 10) || 1) - 1; moveToPage(table, p, true); updatePageDisplay(table, p, false); }) .bind('pageAndSize' + namespace, function(e, page, size){ e.stopPropagation(); p.page = (parseInt(page, 10) || 1) - 1; - setPageSize(table, parseInt(size, 10) || p.settings.size || 10, p); + setPageSize(table, parsePageSize( p, size, 'get' ), p); moveToPage(table, p, true); hideRows(table, p); updatePageDisplay(table, p, false); @@ -1015,9 +1036,10 @@ // setting an option as selected appears to cause issues with initial page size p.$size.find('option').removeAttr('selected'); p.$size.unbind('change' + namespace).bind('change' + namespace, function() { - p.$size.val( $(this).val() ); // in case there are more than one pagers if ( !$(this).hasClass(p.cssDisabled) ) { - setPageSize(table, parseInt( $(this).val(), 10 ), p); + var size = $(this).val(); + p.$size.val( size ); // in case there are more than one pagers + setPageSize(table, size, p); changeHeight(table, p); } return false; @@ -1043,7 +1065,7 @@ } else { p.ajax = false; // Regular pager; all rows stored in memory - $(this).trigger('appendCache', true); + ts.appendCache( c, true ); // true = don't apply widgets hideRowsSetup(table, p); } @@ -1055,7 +1077,7 @@ if (c.debug) { console.log('Pager: Triggering pagerInitialized'); } - c.$table.trigger('pagerInitialized', p); + c.$table.trigger( 'pagerInitialized', p ); if ( !( c.widgetOptions.filter_initialized && ts.hasWidget(table, 'filter') ) ) { updatePageDisplay(table, p, false); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.dragtable.mod.js b/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.dragtable.mod.js index 04675cf..fe86683 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.dragtable.mod.js +++ b/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.dragtable.mod.js @@ -1,4 +1,4 @@ -/*! Dragtable Mod for TableSorter - 2/7/2015 (v2.19.0) *//* +/*! Dragtable Mod for TableSorter - updated 10/31/2015 (v2.24.0) *//* * Requires * tablesorter v2.8+ * jQuery 1.7+ @@ -99,7 +99,7 @@ } */ - $table.trigger('updateAll', [ false, function() { + ts.updateAll( c, false, function() { if ( hasFilters ) { setTimeout( function() { // just update the filter values @@ -111,7 +111,7 @@ } }, 10 ); } - } ]); + }); } }, getOrder : function( table ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 13c849d..fa8c302 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -1,10 +1,10 @@ /*** This file is dynamically generated *** █████▄ ▄████▄ █████▄ ▄████▄ ██████ ███████▄ ▄████▄ █████▄ ██ ██████ ██ ██ -██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ +██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▄▄ ██▄▄██ +██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 10-04-2015 (v2.23.5)*/ +/*! tablesorter (FORK) - updated 11-02-2015 (v2.24.2)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.23.5 *//* +/*! TableSorter (FORK) v2.24.2 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -35,2159 +35,2392 @@ * @contributor Rob Garrison - https://github.com/Mottie/tablesorter */ /*jshint browser:true, jquery:true, unused:false, expr: true */ -;(function($){ +;( function( $ ) { 'use strict'; - $.extend({ - /*jshint supernew:true */ - tablesorter: new function() { - - var ts = this; - - ts.version = '2.23.5'; - - ts.parsers = []; - ts.widgets = []; - ts.defaults = { - - // *** appearance - theme : 'default', // adds tablesorter-{theme} to the table for styling - widthFixed : false, // adds colgroup to fix widths of columns - showProcessing : false, // show an indeterminate timer icon in the header when the table is sorted or filtered. - - headerTemplate : '{content}',// header layout template (HTML ok); {content} = innerHTML, {icon} = <i/> (class from cssIcon) - onRenderTemplate : null, // function(index, template){ return template; }, (template is a string) - onRenderHeader : null, // function(index){}, (nothing to return) - - // *** functionality - cancelSelection : true, // prevent text selection in the header - tabIndex : true, // add tabindex to header for keyboard accessibility - dateFormat : 'mmddyyyy', // other options: 'ddmmyyy' or 'yyyymmdd' - sortMultiSortKey : 'shiftKey', // key used to select additional columns - sortResetKey : 'ctrlKey', // key used to remove sorting on a column - usNumberFormat : true, // false for German '1.234.567,89' or French '1 234 567,89' - delayInit : false, // if false, the parsed table contents will not update until the first sort - serverSideSorting: false, // if true, server-side sorting should be performed because client-side sorting will be disabled, but the ui and events will still be used. - resort : true, // default setting to trigger a resort after an 'update', 'addRows', 'updateCell', etc has completed - - // *** sort options - headers : {}, // set sorter, string, empty, locked order, sortInitialOrder, filter, etc. - ignoreCase : true, // ignore case while sorting - sortForce : null, // column(s) first sorted; always applied - sortList : [], // Initial sort order; applied initially; updated when manually sorted - sortAppend : null, // column(s) sorted last; always applied - sortStable : false, // when sorting two rows with exactly the same content, the original sort order is maintained - - sortInitialOrder : 'asc', // sort direction on first click - sortLocaleCompare: false, // replace equivalent character (accented characters) - sortReset : false, // third click on the header will reset column to default - unsorted - sortRestart : false, // restart sort to 'sortInitialOrder' when clicking on previously unsorted columns - - emptyTo : 'bottom', // sort empty cell to bottom, top, none, zero, emptyMax, emptyMin - stringTo : 'max', // sort strings in numerical column as max, min, top, bottom, zero - textExtraction : 'basic', // text extraction method/function - function(node, table, cellIndex){} - textAttribute : 'data-text',// data-attribute that contains alternate cell text (used in default textExtraction function) - textSorter : null, // choose overall or specific column sorter function(a, b, direction, table, columnIndex) [alt: ts.sortText] - numberSorter : null, // choose overall numeric sorter function(a, b, direction, maxColumnValue) - - // *** widget options - widgets: [], // method to add widgets, e.g. widgets: ['zebra'] - widgetOptions : { - zebra : [ 'even', 'odd' ] // zebra widget alternating row class names - }, - initWidgets : true, // apply widgets on tablesorter initialization - widgetClass : 'widget-{name}', // table class name template to match to include a widget - - // *** callbacks - initialized : null, // function(table){}, - - // *** extra css class names - tableClass : '', - cssAsc : '', - cssDesc : '', - cssNone : '', - cssHeader : '', - cssHeaderRow : '', - cssProcessing : '', // processing icon applied to header during sort/filter - - cssChildRow : 'tablesorter-childRow', // class name indiciating that a row is to be attached to the its parent - cssIcon : 'tablesorter-icon', // if this class does not exist, the {icon} will not be added from the headerTemplate - cssIconNone : '', // class name added to the icon when there is no column sort - cssIconAsc : '', // class name added to the icon when the column has an ascending sort - cssIconDesc : '', // class name added to the icon when the column has a descending sort - cssInfoBlock : 'tablesorter-infoOnly', // don't sort tbody with this class name (only one class name allowed here!) - cssNoSort : 'tablesorter-noSort', // class name added to element inside header; clicking on it won't cause a sort - cssIgnoreRow : 'tablesorter-ignoreRow', // header row to ignore; cells within this row will not be added to c.$headers - - // *** events - pointerClick : 'click', - pointerDown : 'mousedown', - pointerUp : 'mouseup', - - // *** selectors - selectorHeaders : '> thead th, > thead td', - selectorSort : 'th, td', // jQuery selector of content within selectorHeaders that is clickable to trigger a sort - selectorRemove : '.remove-me', - - // *** advanced - debug : false, - - // *** Internal variables - headerList: [], - empties: {}, - strings: {}, - parsers: [] + var ts = $.tablesorter = { + + version : '2.24.2', + + parsers : [], + widgets : [], + defaults : { + + // *** appearance + theme : 'default', // adds tablesorter-{theme} to the table for styling + widthFixed : false, // adds colgroup to fix widths of columns + showProcessing : false, // show an indeterminate timer icon in the header when the table is sorted or filtered. + + headerTemplate : '{content}',// header layout template (HTML ok); {content} = innerHTML, {icon} = <i/> // class from cssIcon + onRenderTemplate : null, // function( index, template ){ return template; }, // template is a string + onRenderHeader : null, // function( index ){}, // nothing to return + + // *** functionality + cancelSelection : true, // prevent text selection in the header + tabIndex : true, // add tabindex to header for keyboard accessibility + dateFormat : 'mmddyyyy', // other options: 'ddmmyyy' or 'yyyymmdd' + sortMultiSortKey : 'shiftKey', // key used to select additional columns + sortResetKey : 'ctrlKey', // key used to remove sorting on a column + usNumberFormat : true, // false for German '1.234.567,89' or French '1 234 567,89' + delayInit : false, // if false, the parsed table contents will not update until the first sort + serverSideSorting: false, // if true, server-side sorting should be performed because client-side sorting will be disabled, but the ui and events will still be used. + resort : true, // default setting to trigger a resort after an 'update', 'addRows', 'updateCell', etc has completed + + // *** sort options + headers : {}, // set sorter, string, empty, locked order, sortInitialOrder, filter, etc. + ignoreCase : true, // ignore case while sorting + sortForce : null, // column(s) first sorted; always applied + sortList : [], // Initial sort order; applied initially; updated when manually sorted + sortAppend : null, // column(s) sorted last; always applied + sortStable : false, // when sorting two rows with exactly the same content, the original sort order is maintained + + sortInitialOrder : 'asc', // sort direction on first click + sortLocaleCompare: false, // replace equivalent character (accented characters) + sortReset : false, // third click on the header will reset column to default - unsorted + sortRestart : false, // restart sort to 'sortInitialOrder' when clicking on previously unsorted columns + + emptyTo : 'bottom', // sort empty cell to bottom, top, none, zero, emptyMax, emptyMin + stringTo : 'max', // sort strings in numerical column as max, min, top, bottom, zero + textExtraction : 'basic', // text extraction method/function - function( node, table, cellIndex ){} + textAttribute : 'data-text',// data-attribute that contains alternate cell text (used in default textExtraction function) + textSorter : null, // choose overall or specific column sorter function( a, b, direction, table, columnIndex ) [alt: ts.sortText] + numberSorter : null, // choose overall numeric sorter function( a, b, direction, maxColumnValue ) + + // *** widget options + widgets: [], // method to add widgets, e.g. widgets: ['zebra'] + widgetOptions : { + zebra : [ 'even', 'odd' ] // zebra widget alternating row class names + }, + initWidgets : true, // apply widgets on tablesorter initialization + widgetClass : 'widget-{name}', // table class name template to match to include a widget + + // *** callbacks + initialized : null, // function( table ){}, + + // *** extra css class names + tableClass : '', + cssAsc : '', + cssDesc : '', + cssNone : '', + cssHeader : '', + cssHeaderRow : '', + cssProcessing : '', // processing icon applied to header during sort/filter + + cssChildRow : 'tablesorter-childRow', // class name indiciating that a row is to be attached to the its parent + cssInfoBlock : 'tablesorter-infoOnly', // don't sort tbody with this class name (only one class name allowed here!) + cssNoSort : 'tablesorter-noSort', // class name added to element inside header; clicking on it won't cause a sort + cssIgnoreRow : 'tablesorter-ignoreRow', // header row to ignore; cells within this row will not be added to c.$headers + + cssIcon : 'tablesorter-icon', // if this class does not exist, the {icon} will not be added from the headerTemplate + cssIconNone : '', // class name added to the icon when there is no column sort + cssIconAsc : '', // class name added to the icon when the column has an ascending sort + cssIconDesc : '', // class name added to the icon when the column has a descending sort + + // *** events + pointerClick : 'click', + pointerDown : 'mousedown', + pointerUp : 'mouseup', + + // *** selectors + selectorHeaders : '> thead th, > thead td', + selectorSort : 'th, td', // jQuery selector of content within selectorHeaders that is clickable to trigger a sort + selectorRemove : '.remove-me', + + // *** advanced + debug : false, + + // *** Internal variables + headerList: [], + empties: {}, + strings: {}, + parsers: [] + + // removed: widgetZebra: { css: ['even', 'odd'] } - // removed: widgetZebra: { css: ['even', 'odd'] } + }, - }; + // internal css classes - these will ALWAYS be added to + // the table and MUST only contain one class name - fixes #381 + css : { + table : 'tablesorter', + cssHasChild: 'tablesorter-hasChildRow', + childRow : 'tablesorter-childRow', + colgroup : 'tablesorter-colgroup', + header : 'tablesorter-header', + headerRow : 'tablesorter-headerRow', + headerIn : 'tablesorter-header-inner', + icon : 'tablesorter-icon', + processing : 'tablesorter-processing', + sortAsc : 'tablesorter-headerAsc', + sortDesc : 'tablesorter-headerDesc', + sortNone : 'tablesorter-headerUnSorted' + }, - // internal css classes - these will ALWAYS be added to - // the table and MUST only contain one class name - fixes #381 - ts.css = { - table : 'tablesorter', - cssHasChild: 'tablesorter-hasChildRow', - childRow : 'tablesorter-childRow', - colgroup : 'tablesorter-colgroup', - header : 'tablesorter-header', - headerRow : 'tablesorter-headerRow', - headerIn : 'tablesorter-header-inner', - icon : 'tablesorter-icon', - processing : 'tablesorter-processing', - sortAsc : 'tablesorter-headerAsc', - sortDesc : 'tablesorter-headerDesc', - sortNone : 'tablesorter-headerUnSorted' - }; + // labels applied to sortable headers for accessibility (aria) support + language : { + sortAsc : 'Ascending sort applied, ', + sortDesc : 'Descending sort applied, ', + sortNone : 'No sort applied, ', + nextAsc : 'activate to apply an ascending sort', + nextDesc : 'activate to apply a descending sort', + nextNone : 'activate to remove the sort' + }, - // labels applied to sortable headers for accessibility (aria) support - ts.language = { - sortAsc : 'Ascending sort applied, ', - sortDesc : 'Descending sort applied, ', - sortNone : 'No sort applied, ', - nextAsc : 'activate to apply an ascending sort', - nextDesc : 'activate to apply a descending sort', - nextNone : 'activate to remove the sort' - }; + regex : { + templateContent : /\{content\}/g, + templateIcon : /\{icon\}/g, + templateName : /\{name\}/i, + spaces : /\s+/g, + nonWord : /\W/g, + formElements : /(input|select|button|textarea)/i, - ts.regex = { - templateContent : /\{content\}/g, - templateIcon : /\{icon\}/g, - templateName : /\{name\}/i, - spaces : /\s+/g, - nonWord : /\W/g, - formElements : /(input|select|button|textarea)/i - }; + // *** sort functions *** + // regex used in natural sort + // chunk/tokenize numbers & letters + chunk : /(^([+\-]?(?:\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi, + // replace chunks @ ends + chunks : /(^\\0|\\0$)/, + hex : /^0x[0-9a-f]+$/i, + + // *** formatFloat *** + comma : /,/g, + digitNonUS : /[\s|\.]/g, + digitNegativeTest : /^\s*\([.\d]+\)/, + digitNegativeReplace : /^\s*\(([.\d]+)\)/, + + // *** isDigit *** + digitTest : /^[\-+(]?\d+[)]?$/, + digitReplace : /[,.'"\s]/g - // These methods can be applied on table.config instance - ts.instanceMethods = {}; + }, - // $.isEmptyObject from jQuery v1.4 - ts.isEmptyObject = function( obj ) { - /*jshint forin: false */ - for ( var name in obj ) { - return false; - } - return true; - }; + // digit sort text location; keeping max+/- for backwards compatibility + string : { + max : 1, + min : -1, + emptymin : 1, + emptymax : -1, + zero : 0, + none : 0, + 'null' : 0, + top : true, + bottom : false + }, - ts.getElementText = function(c, node, cellIndex) { - if (!node) { return ''; } - var te, - t = c.textExtraction || '', - // node could be a jquery object - // http://jsperf.com/jquery-vs-instanceof-jquery/2 - $node = node.jquery ? node : $(node); - if (typeof t === 'string') { - // check data-attribute first when set to 'basic'; don't use node.innerText - it's really slow! - // http://www.kellegous.com/j/2013/02/27/innertext-vs-textcontent/ - if ( t === 'basic' && typeof ( te = $node.attr(c.textAttribute) ) !== 'undefined' ) { - return $.trim( te ); - } - return $.trim( node.textContent || $node.text() ); - } else { - if (typeof t === 'function') { - return $.trim( t($node[0], c.table, cellIndex) ); - } else if (typeof (te = ts.getColumnData( c.table, t, cellIndex )) === 'function') { - return $.trim( te($node[0], c.table, cellIndex) ); - } - } - // fallback - return $.trim( $node[0].textContent || $node.text() ); - }; + // These methods can be applied on table.config instance + instanceMethods : {}, - function detectParserForColumn(c, rows, rowIndex, cellIndex) { - var cur, $node, - i = ts.parsers.length, - node = false, - nodeValue = '', - keepLooking = true; - while (nodeValue === '' && keepLooking) { - rowIndex++; - if (rows[rowIndex]) { - node = rows[rowIndex].cells[cellIndex]; - nodeValue = ts.getElementText(c, node, cellIndex); - $node = $(node); - if (c.debug) { - console.log('Checking if value was empty on row ' + rowIndex + ', column: ' + cellIndex + ': "' + nodeValue + '"'); - } + /* + ▄█████ ██████ ██████ ██ ██ █████▄ + ▀█▄ ██▄▄ ██ ██ ██ ██▄▄██ + ▀█▄ ██▀▀ ██ ██ ██ ██▀▀▀ + █████▀ ██████ ██ ▀████▀ ██ + */ + + setup : function( table, c ) { + // if no thead or tbody, or tablesorter is already present, quit + if ( !table || !table.tHead || table.tBodies.length === 0 || table.hasInitialized === true ) { + if ( c.debug ) { + if ( table.hasInitialized ) { + console.warn( 'Stopping initialization. Tablesorter has already been initialized' ); } else { - keepLooking = false; + console.error( 'Stopping initialization! No table, thead or tbody' ); } } - while (--i >= 0) { - cur = ts.parsers[i]; - // ignore the default text parser because it will always be true - if (cur && cur.id !== 'text' && cur.is && cur.is(nodeValue, c.table, node, $node)) { - return cur; - } - } - // nothing found, return the generic parser (text) - return ts.getParserById('text'); + return; + } + + var tmp = '', + $table = $( table ), + meta = $.metadata; + // initialization flag + table.hasInitialized = false; + // table is being processed flag + table.isProcessing = true; + // make sure to store the config object + table.config = c; + // save the settings where they read + $.data( table, 'tablesorter', c ); + if ( c.debug ) { + console[ console.group ? 'group' : 'log' ]( 'Initializing tablesorter' ); + $.data( table, 'startoveralltimer', new Date() ); + } + + // removing this in version 3 (only supports jQuery 1.7+) + c.supportsDataObject = ( function( version ) { + version[ 0 ] = parseInt( version[ 0 ], 10 ); + return ( version[ 0 ] > 1 ) || ( version[ 0 ] === 1 && parseInt( version[ 1 ], 10 ) >= 4 ); + })( $.fn.jquery.split( '.' ) ); + // ensure case insensitivity + c.emptyTo = c.emptyTo.toLowerCase(); + c.stringTo = c.stringTo.toLowerCase(); + c.last = { sortList : [], clickedIndex : -1 }; + // add table theme class only if there isn't already one there + if ( !/tablesorter\-/.test( $table.attr( 'class' ) ) ) { + tmp = ( c.theme !== '' ? ' tablesorter-' + c.theme : '' ); + } + c.table = table; + c.$table = $table + .addClass( ts.css.table + ' ' + c.tableClass + tmp ) + .attr( 'role', 'grid' ); + c.$headers = $table.find( c.selectorHeaders ); + + // give the table a unique id, which will be used in namespace binding + if ( !c.namespace ) { + c.namespace = '.tablesorter' + Math.random().toString( 16 ).slice( 2 ); + } else { + // make sure namespace starts with a period & doesn't have weird characters + c.namespace = '.' + c.namespace.replace( ts.regex.nonWord, '' ); } - // centralized function to extract/parse cell contents - ts.getParsedText = function( c, cell, colIndex, txt ) { - if ( typeof txt === 'undefined' ) { - txt = ts.getElementText( c, cell, colIndex ); + c.$table.children().children( 'tr' ).attr( 'role', 'row' ); + c.$tbodies = $table.children( 'tbody:not(.' + c.cssInfoBlock + ')' ).attr({ + 'aria-live' : 'polite', + 'aria-relevant' : 'all' + }); + if ( c.$table.children( 'caption' ).length ) { + tmp = c.$table.children( 'caption' )[ 0 ]; + if ( !tmp.id ) { tmp.id = c.namespace.slice( 1 ) + 'caption'; } + c.$table.attr( 'aria-labelledby', tmp.id ); + } + c.widgetInit = {}; // keep a list of initialized widgets + // change textExtraction via data-attribute + c.textExtraction = c.$table.attr( 'data-text-extraction' ) || c.textExtraction || 'basic'; + // build headers + ts.buildHeaders( c ); + // fixate columns if the users supplies the fixedWidth option + // do this after theme has been applied + ts.fixColumnWidth( table ); + // add widgets from class name + ts.addWidgetFromClass( table ); + // add widget options before parsing (e.g. grouping widget has parser settings) + ts.applyWidgetOptions( table ); + // try to auto detect column type, and store in tables config + ts.setupParsers( c ); + // start total row count at zero + c.totalRows = 0; + // build the cache for the tbody cells + // delayInit will delay building the cache until the user starts a sort + if ( !c.delayInit ) { ts.buildCache( c ); } + // bind all header events and methods + ts.bindEvents( table, c.$headers, true ); + ts.bindMethods( c ); + // get sort list from jQuery data or metadata + // in jQuery < 1.4, an error occurs when calling $table.data() + if ( c.supportsDataObject && typeof $table.data().sortlist !== 'undefined' ) { + c.sortList = $table.data().sortlist; + } else if ( meta && ( $table.metadata() && $table.metadata().sortlist ) ) { + c.sortList = $table.metadata().sortlist; + } + // apply widget init code + ts.applyWidget( table, true ); + // if user has supplied a sort list to constructor + if ( c.sortList.length > 0 ) { + ts.sortOn( c, c.sortList, {}, !c.initWidgets ); + } else { + ts.setHeadersCss( c ); + if ( c.initWidgets ) { + // apply widget format + ts.applyWidget( table, false ); } - // if no parser, make sure to return the txt - var val = '' + txt, - parser = c.parsers[ colIndex ], - extractor = c.extractors[ colIndex ]; - if ( parser ) { - // do extract before parsing, if there is one - if ( extractor && typeof extractor.format === 'function' ) { - txt = extractor.format( txt, c.table, cell, colIndex ); - } - // allow parsing if the string is empty, previously parsing would change it to zero, - // in case the parser needs to extract data from the table cell attributes - val = parser.id === 'no-parser' ? '' : - // make sure txt is a string (extractor may have converted it) - parser.format( '' + txt, c.table, cell, colIndex ); - if ( c.ignoreCase && typeof val === 'string' ) { - val = val.toLowerCase(); + } + + // show processesing icon + if ( c.showProcessing ) { + $table + .unbind( 'sortBegin' + c.namespace + ' sortEnd' + c.namespace ) + .bind( 'sortBegin' + c.namespace + ' sortEnd' + c.namespace, function( e ) { + clearTimeout( c.processTimer ); + ts.isProcessing( table ); + if ( e.type === 'sortBegin' ) { + c.processTimer = setTimeout( function() { + ts.isProcessing( table, true ); + }, 500 ); } + }); + } + + // initialized + table.hasInitialized = true; + table.isProcessing = false; + if ( c.debug ) { + console.log( 'Overall initialization time: ' + ts.benchmark( $.data( table, 'startoveralltimer' ) ) ); + if ( c.debug && console.groupEnd ) { console.groupEnd(); } + } + $table.trigger( 'tablesorter-initialized', table ); + if ( typeof c.initialized === 'function' ) { + c.initialized( table ); + } + }, + + bindMethods : function( c ) { + var $table = c.$table, + namespace = c.namespace, + events = ( 'sortReset update updateRows updateAll updateHeaders addRows updateCell updateComplete ' + + 'sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup ' + + 'mouseleave ' ).split( ' ' ) + .join( namespace + ' ' ); + // apply easy methods that trigger bound events + $table + .unbind( events.replace( ts.regex.spaces, ' ' ) ) + .bind( 'sortReset' + namespace, function( e, callback ) { + e.stopPropagation(); + // using this.config to ensure functions are getting a non-cached version of the config + ts.sortReset( this.config, callback ); + }) + .bind( 'updateAll' + namespace, function( e, resort, callback ) { + e.stopPropagation(); + ts.updateAll( this.config, resort, callback ); + }) + .bind( 'update' + namespace + ' updateRows' + namespace, function( e, resort, callback ) { + e.stopPropagation(); + ts.update( this.config, resort, callback ); + }) + .bind( 'updateHeaders' + namespace, function( e, callback ) { + e.stopPropagation(); + ts.updateHeaders( this.config, callback ); + }) + .bind( 'updateCell' + namespace, function( e, cell, resort, callback ) { + e.stopPropagation(); + ts.updateCell( this.config, cell, resort, callback ); + }) + .bind( 'addRows' + namespace, function( e, $row, resort, callback ) { + e.stopPropagation(); + ts.addRows( this.config, $row, resort, callback ); + }) + .bind( 'updateComplete' + namespace, function() { + this.isUpdating = false; + }) + .bind( 'sorton' + namespace, function( e, list, callback, init ) { + e.stopPropagation(); + ts.sortOn( this.config, list, callback, init ); + }) + .bind( 'appendCache' + namespace, function( e, callback, init ) { + e.stopPropagation(); + ts.appendCache( this.config, init ); + if ( $.isFunction( callback ) ) { + callback( this ); } - return val; - }; + }) + // $tbodies variable is used by the tbody sorting widget + .bind( 'updateCache' + namespace, function( e, callback, $tbodies ) { + e.stopPropagation(); + ts.updateCache( this.config, callback, $tbodies ); + }) + .bind( 'applyWidgetId' + namespace, function( e, id ) { + e.stopPropagation(); + ts.getWidgetById( id ).format( this, this.config, this.config.widgetOptions ); + }) + .bind( 'applyWidgets' + namespace, function( e, init ) { + e.stopPropagation(); + // apply widgets + ts.applyWidget( this, init ); + }) + .bind( 'refreshWidgets' + namespace, function( e, all, dontapply ) { + e.stopPropagation(); + ts.refreshWidgets( this, all, dontapply ); + }) + .bind( 'destroy' + namespace, function( e, removeClasses, callback ) { + e.stopPropagation(); + ts.destroy( this, removeClasses, callback ); + }) + .bind( 'resetToLoadState' + namespace, function( e ) { + e.stopPropagation(); + // remove all widgets + ts.removeWidget( this, true, false ); + // restore original settings; this clears out current settings, but does not clear + // values saved to storage. + c = $.extend( true, ts.defaults, c.originalSettings ); + this.hasInitialized = false; + // setup the entire table again + ts.setup( this, c ); + }); + }, + + bindEvents : function( table, $headers, core ) { + table = $( table )[ 0 ]; + var tmp, + c = table.config, + namespace = c.namespace, + downTarget = null; + if ( core !== true ) { + $headers.addClass( namespace.slice( 1 ) + '_extra_headers' ); + tmp = $.fn.closest ? $headers.closest( 'table' )[ 0 ] : $headers.parents( 'table' )[ 0 ]; + if ( tmp && tmp.nodeName === 'TABLE' && tmp !== table ) { + $( tmp ).addClass( namespace.slice( 1 ) + '_extra_table' ); + } + } + tmp = ( c.pointerDown + ' ' + c.pointerUp + ' ' + c.pointerClick + ' sort keyup ' ) + .replace( ts.regex.spaces, ' ' ) + .split( ' ' ) + .join( namespace + ' ' ); + // apply event handling to headers and/or additional headers (stickyheaders, scroller, etc) + $headers + // http://stackoverflow.com/questions/5312849/jquery-find-self; + .find( c.selectorSort ) + .add( $headers.filter( c.selectorSort ) ) + .unbind( tmp ) + .bind( tmp, function( e, external ) { + var $cell, cell, temp, + $target = $( e.target ), + // wrap event type in spaces, so the match doesn't trigger on inner words + type = ' ' + e.type + ' '; + // only recognize left clicks + if ( ( ( e.which || e.button ) !== 1 && !type.match( ' ' + c.pointerClick + ' | sort | keyup ' ) ) || + // allow pressing enter + ( type === ' keyup ' && e.which !== 13 ) || + // allow triggering a click event (e.which is undefined) & ignore physical clicks + ( type.match( ' ' + c.pointerClick + ' ' ) && typeof e.which !== 'undefined' ) ) { + return; + } + // ignore mouseup if mousedown wasn't on the same target + if ( type.match( ' ' + c.pointerUp + ' ' ) && downTarget !== e.target && external !== true ) { + return; + } + // set target on mousedown + if ( type.match( ' ' + c.pointerDown + ' ' ) ) { + downTarget = e.target; + // preventDefault needed or jQuery v1.3.2 and older throws an + // "Uncaught TypeError: handler.apply is not a function" error + temp = $target.jquery.split( '.' ); + if ( temp[ 0 ] === '1' && temp[ 1 ] < 4 ) { e.preventDefault(); } + return; + } + downTarget = null; + // prevent sort being triggered on form elements + if ( ts.regex.formElements.test( e.target.nodeName ) || + // nosort class name, or elements within a nosort container + $target.hasClass( c.cssNoSort ) || $target.parents( '.' + c.cssNoSort ).length > 0 || + // elements within a button + $target.parents( 'button' ).length > 0 ) { + return !c.cancelSelection; + } + if ( c.delayInit && ts.isEmptyObject( c.cache ) ) { + ts.buildCache( c ); + } + // jQuery v1.2.6 doesn't have closest() + $cell = $.fn.closest ? $( this ).closest( 'th, td' ) : + /TH|TD/.test( this.nodeName ) ? $( this ) : $( this ).parents( 'th, td' ); + // reference original table headers and find the same cell + // don't use $headers or IE8 throws an error - see #987 + temp = $headers.index( $cell ); + c.last.clickedIndex = ( temp < 0 ) ? $cell.attr( 'data-column' ) : temp; + // use column index if $headers is undefined + cell = c.$headers[ c.last.clickedIndex ]; + if ( cell && !cell.sortDisabled ) { + ts.initSort( c, cell, e ); + } + }); + if ( c.cancelSelection ) { + // cancel selection + $headers + .attr( 'unselectable', 'on' ) + .bind( 'selectstart', false ) + .css({ + 'user-select' : 'none', + 'MozUserSelect' : 'none' // not needed for jQuery 1.8+ + }); + } + }, - function buildParserCache( c, $tbodies ) { - var rows, list, l, i, h, ch, np, p, e, time, tb, len, - table = c.table, - j = 0, - debug = {}; - // update table bodies in case we start with an empty table - c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'); - tb = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies; - len = tb.length; - if ( len === 0) { - return c.debug ? console.warn('Warning: *Empty table!* Not building a parser cache') : ''; - } else if (c.debug) { - time = new Date(); - console[ console.group ? 'group' : 'log' ]('Detecting parsers for each column'); - } - list = { - extractors: [], - parsers: [] + buildHeaders : function( c ) { + var $temp, icon, timer, indx; + c.headerList = []; + c.headerContent = []; + c.sortVars = []; + if ( c.debug ) { + timer = new Date(); + } + // children tr in tfoot - see issue #196 & #547 + c.columns = ts.computeColumnIndex( c.$table.children( 'thead, tfoot' ).children( 'tr' ) ); + // add icon if cssIcon option exists + icon = c.cssIcon ? + '<i class="' + ( c.cssIcon === ts.css.icon ? ts.css.icon : c.cssIcon + ' ' + ts.css.icon ) + '"></i>' : + ''; + // redefine c.$headers here in case of an updateAll that replaces or adds an entire header cell - see #683 + c.$headers = $( $.map( c.$table.find( c.selectorHeaders ), function( elem, index ) { + var configHeaders, header, column, template, tmp, + $elem = $( elem ); + // ignore cell (don't add it to c.$headers) if row has ignoreRow class + if ( $elem.parent().hasClass( c.cssIgnoreRow ) ) { return; } + // make sure to get header cell & not column indexed cell + configHeaders = ts.getColumnData( c.table, c.headers, index, true ); + // save original header content + c.headerContent[ index ] = $elem.html(); + // if headerTemplate is empty, don't reformat the header cell + if ( c.headerTemplate !== '' && !$elem.find( '.' + ts.css.headerIn ).length ) { + // set up header template + template = c.headerTemplate + .replace( ts.regex.templateContent, $elem.html() ) + .replace( ts.regex.templateIcon, $elem.find( '.' + ts.css.icon ).length ? '' : icon ); + if ( c.onRenderTemplate ) { + header = c.onRenderTemplate.apply( $elem, [ index, template ] ); + // only change t if something is returned + if ( header && typeof header === 'string' ) { + template = header; + } + } + $elem.html( '<div class="' + ts.css.headerIn + '">' + template + '</div>' ); // faster than wrapInner + } + if ( c.onRenderHeader ) { + c.onRenderHeader.apply( $elem, [ index, c, c.$table ] ); + } + column = parseInt( $elem.attr( 'data-column' ), 10 ); + elem.column = column; + tmp = ts.getData( $elem, configHeaders, 'sortInitialOrder' ) || c.sortInitialOrder; + // this may get updated numerous times if there are multiple rows + c.sortVars[ column ] = { + count : -1, // set to -1 because clicking on the header automatically adds one + order: ts.formatSortingOrder( tmp ) ? + [ 1, 0, 2 ] : // desc, asc, unsorted + [ 0, 1, 2 ], // asc, desc, unsorted + lockedOrder : false }; - while (j < len) { - rows = tb[j].rows; - if (rows.length) { - l = c.columns; // rows[j].cells.length; - for (i = 0; i < l; i++) { - h = c.$headerIndexed[i]; + tmp = ts.getData( $elem, configHeaders, 'lockedOrder' ) || false; + if ( typeof tmp !== 'undefined' && tmp !== false ) { + c.sortVars[ column ].lockedOrder = true; + c.sortVars[ column ].order = ts.formatSortingOrder( tmp ) ? [ 1, 1, 1 ] : [ 0, 0, 0 ]; + } + // add cell to headerList + c.headerList[ index ] = elem; + // add to parent in case there are multiple rows + $elem + .addClass( ts.css.header + ' ' + c.cssHeader ) + .parent() + .addClass( ts.css.headerRow + ' ' + c.cssHeaderRow ) + .attr( 'role', 'row' ); + // allow keyboard cursor to focus on element + if ( c.tabIndex ) { + $elem.attr( 'tabindex', 0 ); + } + return elem; + }) ); + // cache headers per column + c.$headerIndexed = []; + for ( indx = 0; indx < c.columns; indx++ ) { + // colspan in header making a column undefined + if ( ts.isEmptyObject( c.sortVars[ indx ] ) ) { + c.sortVars[ indx ] = {}; + } + $temp = c.$headers.filter( '[data-column="' + indx + '"]' ); + // target sortable column cells, unless there are none, then use non-sortable cells + // .last() added in jQuery 1.4; use .filter(':last') to maintain compatibility with jQuery v1.2.6 + c.$headerIndexed[ indx ] = $temp.length ? + $temp.not( '.sorter-false' ).length ? + $temp.not( '.sorter-false' ).filter( ':last' ) : + $temp.filter( ':last' ) : + $(); + } + c.$table.find( c.selectorHeaders ).attr({ + scope: 'col', + role : 'columnheader' + }); + // enable/disable sorting + ts.updateHeader( c ); + if ( c.debug ) { + console.log( 'Built headers:' + ts.benchmark( timer ) ); + console.log( c.$headers ); + } + }, + + // Use it to add a set of methods to table.config which will be available for all tables. + // This should be done before table initialization + addInstanceMethods : function( methods ) { + $.extend( ts.instanceMethods, methods ); + }, + + /* + █████▄ ▄████▄ █████▄ ▄█████ ██████ █████▄ ▄█████ + ██▄▄██ ██▄▄██ ██▄▄██ ▀█▄ ██▄▄ ██▄▄██ ▀█▄ + ██▀▀▀ ██▀▀██ ██▀██ ▀█▄ ██▀▀ ██▀██ ▀█▄ + ██ ██ ██ ██ ██ █████▀ ██████ ██ ██ █████▀ + */ + setupParsers : function( c, $tbodies ) { + var rows, list, span, max, colIndex, indx, header, configHeaders, + noParser, parser, extractor, time, tbody, len, + table = c.table, + tbodyIndex = 0, + debug = {}; + // update table bodies in case we start with an empty table + c.$tbodies = c.$table.children( 'tbody:not(.' + c.cssInfoBlock + ')' ); + tbody = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies; + len = tbody.length; + if ( len === 0 ) { + return c.debug ? console.warn( 'Warning: *Empty table!* Not building a parser cache' ) : ''; + } else if ( c.debug ) { + time = new Date(); + console[ console.group ? 'group' : 'log' ]( 'Detecting parsers for each column' ); + } + list = { + extractors: [], + parsers: [] + }; + while ( tbodyIndex < len ) { + rows = tbody[ tbodyIndex ].rows; + if ( rows.length ) { + colIndex = 0; + max = c.columns; + for ( indx = 0; indx < max; indx++ ) { + header = c.$headerIndexed[ colIndex ]; + if ( header && header.length ) { // get column indexed table cell - ch = ts.getColumnData( table, c.headers, i ); + configHeaders = ts.getColumnData( table, c.headers, colIndex ); // get column parser/extractor - e = ts.getParserById( ts.getData(h, ch, 'extractor') ); - p = ts.getParserById( ts.getData(h, ch, 'sorter') ); - np = ts.getData(h, ch, 'parser') === 'false'; + extractor = ts.getParserById( ts.getData( header, configHeaders, 'extractor' ) ); + parser = ts.getParserById( ts.getData( header, configHeaders, 'sorter' ) ); + noParser = ts.getData( header, configHeaders, 'parser' ) === 'false'; // empty cells behaviour - keeping emptyToBottom for backwards compatibility - c.empties[i] = ( ts.getData(h, ch, 'empty') || c.emptyTo || (c.emptyToBottom ? 'bottom' : 'top' ) ).toLowerCase(); + c.empties[colIndex] = ( + ts.getData( header, configHeaders, 'empty' ) || + c.emptyTo || ( c.emptyToBottom ? 'bottom' : 'top' ) ).toLowerCase(); // text strings behaviour in numerical sorts - c.strings[i] = ( ts.getData(h, ch, 'string') || c.stringTo || 'max' ).toLowerCase(); - if (np) { - p = ts.getParserById('no-parser'); + c.strings[colIndex] = ( + ts.getData( header, configHeaders, 'string' ) || + c.stringTo || + 'max' ).toLowerCase(); + if ( noParser ) { + parser = ts.getParserById( 'no-parser' ); } - if (!e) { + if ( !extractor ) { // For now, maybe detect someday - e = false; + extractor = false; } - if (!p) { - p = detectParserForColumn(c, rows, -1, i); + if ( !parser ) { + parser = ts.detectParserForColumn( c, rows, -1, colIndex ); } - if (c.debug) { - debug[ '(' + i + ') ' + h.text() ] = { - parser : p.id, - extractor : e ? e.id : 'none', - string : c.strings[i], - empty : c.empties[i] + if ( c.debug ) { + debug[ '(' + colIndex + ') ' + header.text() ] = { + parser : parser.id, + extractor : extractor ? extractor.id : 'none', + string : c.strings[ colIndex ], + empty : c.empties[ colIndex ] }; } - list.parsers[i] = p; - list.extractors[i] = e; - } - } - j += (list.parsers.length) ? len : 1; - } - if ( c.debug ) { - if ( !ts.isEmptyObject( debug ) ) { - console[ console.table ? 'table' : 'log' ]( debug ); - } else { - console.warn( ' No parsers detected!' ); - } - console.log( 'Completed detecting parsers' + ts.benchmark( time ) ); - if ( console.groupEnd ) { console.groupEnd(); } - } - c.parsers = list.parsers; - c.extractors = list.extractors; - } - - /* utils */ - function buildCache(table, callback, $tbodies) { - var cc, t, v, i, j, k, $tb, $row, cols, cacheTime, - totalRows, rowData, prevRowData, colMax, - c = table.config, - parsers = c.parsers; - // update tbody variable - c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'); - $tb = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies, - c.cache = {}; - c.totalRows = 0; - // if no parsers found, return - it's an empty table. - if (!parsers) { - return c.debug ? console.warn('Warning: *Empty table!* Not building a cache') : ''; - } - if (c.debug) { - cacheTime = new Date(); - } - // processing icon - if (c.showProcessing) { - ts.isProcessing(table, true); - } - for (k = 0; k < $tb.length; k++) { - colMax = []; // column max value per tbody - cc = c.cache[k] = { - normalized: [] // array of normalized row data; last entry contains 'rowData' above - // colMax: # // added at the end - }; - - totalRows = ($tb[k] && $tb[k].rows.length) || 0; - for (i = 0; i < totalRows; ++i) { - rowData = { - // order: original row order # - // $row : jQuery Object[] - child: [], // child row text (filter widget) - raw: [] // original row text - }; - /** Add the table data to main data array */ - $row = $( $tb[ k ].rows[ i ] ); - cols = []; - // if this is a child row, add it to the last row's children and continue to the next row - // ignore child row class, if it is the first row - if ( $row.hasClass( c.cssChildRow ) && i !== 0 ) { - t = cc.normalized.length - 1; - prevRowData = cc.normalized[ t ][ c.columns ]; - prevRowData.$row = prevRowData.$row.add( $row ); - // add 'hasChild' class name to parent row - if ( !$row.prev().hasClass( c.cssChildRow ) ) { - $row.prev().addClass( ts.css.cssHasChild ); + list.parsers[ colIndex ] = parser; + list.extractors[ colIndex ] = extractor; + span = header[ 0 ].colSpan - 1; + if ( span > 0 ) { + colIndex += span; + max += span; } - // save child row content (un-parsed!) - v = $row.children( 'th, td' ); - t = prevRowData.child.length; - prevRowData.child[ t ] = []; - // child row content does not account for colspans/rowspans; so indexing may be off - for ( j = 0; j < c.columns; j++ ) { - prevRowData.child[ t ][ j ] = ts.getParsedText( c, v[ j ], j ); - } - // go to the next for loop - continue; } - rowData.$row = $row; - rowData.order = i; // add original row position to rowCache - for ( j = 0; j < c.columns; ++j ) { - if (typeof parsers[ j ] === 'undefined') { - if ( c.debug ) { - console.warn( 'No parser found for cell:', $row[ 0 ].cells[ j ], 'does it have a header?' ); - } - continue; - } - t = ts.getElementText( c, $row[ 0 ].cells[j], j ); - rowData.raw.push( t ); // save original row text - v = ts.getParsedText( c, $row[ 0 ].cells[ j ], j, t ); - cols.push( v ); - if ( ( parsers[ j ].type || '' ).toLowerCase() === 'numeric' ) { - // determine column max value (ignore sign) - colMax[ j ] = Math.max( Math.abs( v ) || 0, colMax[ j ] || 0 ); - } - } - // ensure rowData is always in the same location (after the last column) - cols[ c.columns ] = rowData; - cc.normalized.push( cols ); + colIndex++; } - cc.colMax = colMax; - // total up rows, not including child rows - c.totalRows += cc.normalized.length; - } - if ( c.showProcessing ) { - ts.isProcessing( table ); // remove processing icon - } - if ( c.debug ) { - console.log( 'Building cache for ' + totalRows + ' rows' + ts.benchmark( cacheTime ) ); - } - if ( $.isFunction( callback ) ) { - callback( table ); + tbodyIndex += ( list.parsers.length ) ? len : 1; + } + if ( c.debug ) { + if ( !ts.isEmptyObject( debug ) ) { + console[ console.table ? 'table' : 'log' ]( debug ); + } else { + console.warn( ' No parsers detected!' ); } + console.log( 'Completed detecting parsers' + ts.benchmark( time ) ); + if ( console.groupEnd ) { console.groupEnd(); } } + c.parsers = list.parsers; + c.extractors = list.extractors; + }, - function formatSortingOrder(v) { - // look for 'd' in 'desc' order; return true - return (/^d/i.test(v) || v === 1); - } - - function buildHeaders( c ) { - var ch, $t, h, i, t, lock, time, indx; - c.headerList = []; - c.headerContent = []; - if (c.debug) { - time = new Date(); - } - // children tr in tfoot - see issue #196 & #547 - c.columns = ts.computeColumnIndex( c.$table.children('thead, tfoot').children('tr') ); - // add icon if cssIcon option exists - i = c.cssIcon ? '<i class="' + ( c.cssIcon === ts.css.icon ? ts.css.icon : c.cssIcon + ' ' + ts.css.icon ) + '"></i>' : ''; - // redefine c.$headers here in case of an updateAll that replaces or adds an entire header cell - see #683 - c.$headers = $( $.map( c.$table.find(c.selectorHeaders), function(elem, index) { - $t = $(elem); - // ignore cell (don't add it to c.$headers) if row has ignoreRow class - if ($t.parent().hasClass(c.cssIgnoreRow)) { return; } - // make sure to get header cell & not column indexed cell - ch = ts.getColumnData( c.table, c.headers, index, true ); - // save original header content - c.headerContent[index] = $t.html(); - // if headerTemplate is empty, don't reformat the header cell - if ( c.headerTemplate !== '' && !$t.find('.' + ts.css.headerIn).length ) { - // set up header template - t = c.headerTemplate - .replace(ts.regex.templateContent, $t.html()) - .replace(ts.regex.templateIcon, $t.find('.' + ts.css.icon).length ? '' : i); - if (c.onRenderTemplate) { - h = c.onRenderTemplate.apply( $t, [ index, t ] ); - if (h && typeof h === 'string') { t = h; } // only change t if something is returned - } - $t.html('<div class="' + ts.css.headerIn + '">' + t + '</div>'); // faster than wrapInner - } - if (c.onRenderHeader) { c.onRenderHeader.apply( $t, [ index, c, c.$table ] ); } - // *** remove this.column value if no conflicts found - elem.column = parseInt( $t.attr('data-column'), 10); - elem.order = formatSortingOrder( ts.getData( $t, ch, 'sortInitialOrder' ) || c.sortInitialOrder ) ? - [ 1, 0, 2 ] : // desc, asc, unsorted - [ 0, 1, 2 ]; // asc, desc, unsorted - elem.count = -1; // set to -1 because clicking on the header automatically adds one - elem.lockedOrder = false; - lock = ts.getData($t, ch, 'lockedOrder') || false; - if (typeof lock !== 'undefined' && lock !== false) { - elem.order = elem.lockedOrder = formatSortingOrder(lock) ? [ 1, 1, 1 ] : [ 0, 0, 0 ]; - } - $t.addClass(ts.css.header + ' ' + c.cssHeader); - // add cell to headerList - c.headerList[index] = elem; - // add to parent in case there are multiple rows - $t.parent().addClass(ts.css.headerRow + ' ' + c.cssHeaderRow).attr('role', 'row'); - // allow keyboard cursor to focus on element - if (c.tabIndex) { $t.attr('tabindex', 0); } - return elem; - })); - // cache headers per column - c.$headerIndexed = []; - for (indx = 0; indx < c.columns; indx++) { - $t = c.$headers.filter('[data-column="' + indx + '"]'); - // target sortable column cells, unless there are none, then use non-sortable cells - // .last() added in jQuery 1.4; use .filter(':last') to maintain compatibility with jQuery v1.2.6 - c.$headerIndexed[indx] = $t.not('.sorter-false').length ? $t.not('.sorter-false').filter(':last') : $t.filter(':last'); - } - c.$table.find(c.selectorHeaders).attr({ - scope: 'col', - role : 'columnheader' - }); - // enable/disable sorting - updateHeader(c.table); - if (c.debug) { - console.log( 'Built headers:' + ts.benchmark( time ) ); - console.log( c.$headers ); + addParser : function( parser ) { + var indx, + len = ts.parsers.length, + add = true; + for ( indx = 0; indx < len; indx++ ) { + if ( ts.parsers[ indx ].id.toLowerCase() === parser.id.toLowerCase() ) { + add = false; } } - - function commonUpdate(table, resort, callback) { - var c = table.config; - // remove rows/elements before update - c.$table.find(c.selectorRemove).remove(); - // rebuild parsers - buildParserCache(c); - // rebuild the cache map - buildCache(table); - checkResort(c, resort, callback); + if ( add ) { + ts.parsers.push( parser ); } + }, - function updateHeader(table) { - var index, isDisabled, $th, col, - c = table.config, - len = c.$headers.length; - for ( index = 0; index < len; index++ ) { - $th = c.$headers.eq( index ); - col = ts.getColumnData( table, c.headers, index, true ); - // add 'sorter-false' class if 'parser-false' is set - isDisabled = ts.getData( $th, col, 'sorter' ) === 'false' || ts.getData( $th, col, 'parser' ) === 'false'; - $th[0].sortDisabled = isDisabled; - $th[ isDisabled ? 'addClass' : 'removeClass' ]( 'sorter-false' ).attr( 'aria-disabled', '' + isDisabled ); - // disable tab index on disabled cells - if ( c.tabIndex ) { - if ( isDisabled ) { - $th.removeAttr( 'tabindex' ); - } else { - $th.attr( 'tabindex', '0' ); - } - } - // aria-controls - requires table ID - if (table.id) { - if ( isDisabled ) { - $th.removeAttr('aria-controls'); - } else { - $th.attr('aria-controls', table.id); - } - } + getParserById : function( name ) { + /*jshint eqeqeq:false */ + if ( name == 'false' ) { return false; } + var indx, + len = ts.parsers.length; + for ( indx = 0; indx < len; indx++ ) { + if ( ts.parsers[ indx ].id.toLowerCase() === ( name.toString() ).toLowerCase() ) { + return ts.parsers[ indx ]; } } + return false; + }, - function setHeadersCss(table) { - var f, h, i, j, $headers, $h, nextSort, txt, - c = table.config, - list = c.sortList, - len = list.length, - none = ts.css.sortNone + ' ' + c.cssNone, - css = [ ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc ], - cssIcon = [ c.cssIconAsc, c.cssIconDesc, c.cssIconNone ], - aria = [ 'ascending', 'descending' ], - // find the footer - $t = $(table).find('tfoot tr').children() - .add( $( c.namespace + '_extra_headers' ) ) - .removeClass( css.join( ' ' ) ); - // remove all header information - c.$headers - .removeClass(css.join(' ')) - .addClass(none).attr('aria-sort', 'none') - .find('.' + ts.css.icon) - .removeClass(cssIcon.join(' ')) - .addClass(cssIcon[2]); - for (i = 0; i < len; i++) { - // direction = 2 means reset! - if (list[i][1] !== 2) { - // multicolumn sorting updating - see #1005 - f = c.lastClickedIndex > 0 ? c.$headers.filter(':gt(' + ( c.lastClickedIndex - 1 ) + ')') : c.$headers; - // choose the :last in case there are nested columns - f = f.not('.sorter-false').filter('[data-column="' + list[i][0] + '"]' + (len === 1 ? ':last' : '') ); - if (f.length) { - for (j = 0; j < f.length; j++) { - if (!f[j].sortDisabled) { - f.eq(j) - .removeClass(none) - .addClass(css[list[i][1]]) - .attr('aria-sort', aria[list[i][1]]) - .find('.' + ts.css.icon) - .removeClass(cssIcon[2]) - .addClass(cssIcon[list[i][1]]); - } - } - // add sorted class to footer & extra headers, if they exist - if ($t.length) { - $t.filter('[data-column="' + list[i][0] + '"]').removeClass(none).addClass(css[list[i][1]]); - } - } + detectParserForColumn : function( c, rows, rowIndex, cellIndex ) { + var cur, $node, + indx = ts.parsers.length, + node = false, + nodeValue = '', + keepLooking = true; + while ( nodeValue === '' && keepLooking ) { + rowIndex++; + if ( rows[ rowIndex ] ) { + node = rows[ rowIndex ].cells[ cellIndex ]; + nodeValue = ts.getElementText( c, node, cellIndex ); + $node = $( node ); + if ( c.debug ) { + console.log( 'Checking if value was empty on row ' + rowIndex + ', column: ' + + cellIndex + ': "' + nodeValue + '"' ); } + } else { + keepLooking = false; } - // add verbose aria labels - len = c.$headers.length; - $headers = c.$headers.not('.sorter-false'); - for ( i = 0; i < len; i++ ) { - $h = $headers.eq( i ); - if ( $h.length ) { - h = $headers[ i ]; - nextSort = h.order[ ( h.count + 1 ) % ( c.sortReset ? 3 : 2 ) ], - txt = $.trim( $h.text() ) + ': ' + - ts.language[ $h.hasClass( ts.css.sortAsc ) ? 'sortAsc' : $h.hasClass( ts.css.sortDesc ) ? 'sortDesc' : 'sortNone' ] + - ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ]; - $h.attr( 'aria-label', txt ); - } + } + while ( --indx >= 0 ) { + cur = ts.parsers[ indx ]; + // ignore the default text parser because it will always be true + if ( cur && cur.id !== 'text' && cur.is && cur.is( nodeValue, c.table, node, $node ) ) { + return cur; } } + // nothing found, return the generic parser (text) + return ts.getParserById( 'text' ); + }, - function updateHeaderSortCount( table, list ) { - var col, dir, group, header, indx, primary, temp, val, - c = table.config, - sortList = list || c.sortList, - len = sortList.length; - c.sortList = []; - for (indx = 0; indx < len; indx++) { - val = sortList[indx]; - // ensure all sortList values are numeric - fixes #127 - col = parseInt(val[0], 10); - // prevents error if sorton array is wrong - if ( col < c.columns && c.$headerIndexed[col] ) { - // make sure header exists - header = c.$headerIndexed[col][0]; - // o.count = o.count + 1; - dir = ('' + val[1]).match(/^(1|d|s|o|n)/); - dir = dir ? dir[0] : ''; - // 0/(a)sc (default), 1/(d)esc, (s)ame, (o)pposite, (n)ext - switch (dir) { - case '1': case 'd': // descending - dir = 1; - break; - case 's': // same direction (as primary column) - // if primary sort is set to 's', make it ascending - dir = primary || 0; - break; - case 'o': - temp = header.order[(primary || 0) % (c.sortReset ? 3 : 2)]; - // opposite of primary column; but resets if primary resets - dir = temp === 0 ? 1 : temp === 1 ? 0 : 2; - break; - case 'n': - header.count = header.count + 1; - dir = header.order[(header.count) % (c.sortReset ? 3 : 2)]; - break; - default: // ascending - dir = 0; - break; - } - primary = indx === 0 ? dir : primary; - group = [ col, parseInt(dir, 10) || 0 ]; - c.sortList.push(group); - dir = $.inArray(group[1], header.order); // fixes issue #167 - header.count = dir >= 0 ? dir : group[1] % (c.sortReset ? 3 : 2); - } + getElementText : function( c, node, cellIndex ) { + if ( !node ) { return ''; } + var tmp, + extract = c.textExtraction || '', + // node could be a jquery object + // http://jsperf.com/jquery-vs-instanceof-jquery/2 + $node = node.jquery ? node : $( node ); + if ( typeof extract === 'string' ) { + // check data-attribute first when set to 'basic'; don't use node.innerText - it's really slow! + // http://www.kellegous.com/j/2013/02/27/innertext-vs-textcontent/ + if ( extract === 'basic' && typeof ( tmp = $node.attr( c.textAttribute ) ) !== 'undefined' ) { + return $.trim( tmp ); + } + return $.trim( node.textContent || $node.text() ); + } else { + if ( typeof extract === 'function' ) { + return $.trim( extract( $node[ 0 ], c.table, cellIndex ) ); + } else if ( typeof ( tmp = ts.getColumnData( c.table, extract, cellIndex ) ) === 'function' ) { + return $.trim( tmp( $node[ 0 ], c.table, cellIndex ) ); } } + // fallback + return $.trim( $node[ 0 ].textContent || $node.text() ); + }, - function getCachedSortType(parsers, i) { - return (parsers && parsers[i]) ? parsers[i].type || '' : ''; + // centralized function to extract/parse cell contents + getParsedText : function( c, cell, colIndex, txt ) { + if ( typeof txt === 'undefined' ) { + txt = ts.getElementText( c, cell, colIndex ); } - - function initSort(table, cell, event){ - if (table.isUpdating) { - // let any updates complete before initializing a sort - return setTimeout(function(){ initSort(table, cell, event); }, 50); - } - var arry, indx, i, col, order, s, $header, - c = table.config, - key = !event[c.sortMultiSortKey], - $table = c.$table, - len = c.$headers.length; - // Only call sortStart if sorting is enabled - $table.trigger('sortStart', table); - // get current column sort order - cell.count = event[c.sortResetKey] ? 2 : (cell.count + 1) % (c.sortReset ? 3 : 2); - // reset all sorts on non-current column - issue #30 - if (c.sortRestart) { - indx = cell; - for ( i = 0; i < len; i++ ) { - $header = c.$headers.eq( i ); - // only reset counts on columns that weren't just clicked on and if not included in a multisort - if ( $header[0] !== indx && ( key || !$header.is('.' + ts.css.sortDesc + ',.' + ts.css.sortAsc) ) ) { - $header[0].count = -1; - } - } + // if no parser, make sure to return the txt + var val = '' + txt, + parser = c.parsers[ colIndex ], + extractor = c.extractors[ colIndex ]; + if ( parser ) { + // do extract before parsing, if there is one + if ( extractor && typeof extractor.format === 'function' ) { + txt = extractor.format( txt, c.table, cell, colIndex ); + } + // allow parsing if the string is empty, previously parsing would change it to zero, + // in case the parser needs to extract data from the table cell attributes + val = parser.id === 'no-parser' ? '' : + // make sure txt is a string (extractor may have converted it) + parser.format( '' + txt, c.table, cell, colIndex ); + if ( c.ignoreCase && typeof val === 'string' ) { + val = val.toLowerCase(); } - // get current column index - indx = parseInt( $(cell).attr('data-column'), 10 ); - // user only wants to sort on one column - if (key) { - // flush the sort list - c.sortList = []; - if (c.sortForce !== null) { - arry = c.sortForce; - for (col = 0; col < arry.length; col++) { - if (arry[col][0] !== indx) { - c.sortList.push(arry[col]); - } + } + return val; + }, + + /* + ▄████▄ ▄████▄ ▄████▄ ██ ██ ██████ + ██ ▀▀ ██▄▄██ ██ ▀▀ ██▄▄██ ██▄▄ + ██ ▄▄ ██▀▀██ ██ ▄▄ ██▀▀██ ██▀▀ + ▀████▀ ██ ██ ▀████▀ ██ ██ ██████ + */ + buildCache : function( c, callback, $tbodies ) { + var cache, val, txt, rowIndex, colIndex, tbodyIndex, $tbody, $row, + cols, $cells, cell, cacheTime, totalRows, rowData, prevRowData, + colMax, span, cacheIndex, max, len, + table = c.table, + parsers = c.parsers; + // update tbody variable + c.$tbodies = c.$table.children( 'tbody:not(.' + c.cssInfoBlock + ')' ); + $tbody = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies, + c.cache = {}; + c.totalRows = 0; + // if no parsers found, return - it's an empty table. + if ( !parsers ) { + return c.debug ? console.warn( 'Warning: *Empty table!* Not building a cache' ) : ''; + } + if ( c.debug ) { + cacheTime = new Date(); + } + // processing icon + if ( c.showProcessing ) { + ts.isProcessing( table, true ); + } + for ( tbodyIndex = 0; tbodyIndex < $tbody.length; tbodyIndex++ ) { + colMax = []; // column max value per tbody + cache = c.cache[ tbodyIndex ] = { + normalized: [] // array of normalized row data; last entry contains 'rowData' above + // colMax: # // added at the end + }; + + totalRows = ( $tbody[ tbodyIndex ] && $tbody[ tbodyIndex ].rows.length ) || 0; + for ( rowIndex = 0; rowIndex < totalRows; ++rowIndex ) { + rowData = { + // order: original row order # + // $row : jQuery Object[] + child: [], // child row text (filter widget) + raw: [] // original row text + }; + /** Add the table data to main data array */ + $row = $( $tbody[ tbodyIndex ].rows[ rowIndex ] ); + cols = []; + // if this is a child row, add it to the last row's children and continue to the next row + // ignore child row class, if it is the first row + if ( $row.hasClass( c.cssChildRow ) && rowIndex !== 0 ) { + len = cache.normalized.length - 1; + prevRowData = cache.normalized[ len ][ c.columns ]; + prevRowData.$row = prevRowData.$row.add( $row ); + // add 'hasChild' class name to parent row + if ( !$row.prev().hasClass( c.cssChildRow ) ) { + $row.prev().addClass( ts.css.cssHasChild ); } - } - // add column to sort list - order = cell.order[cell.count]; - if (order < 2) { - c.sortList.push([ indx, order ]); - // add other columns if header spans across multiple - if (cell.colSpan > 1) { - for (col = 1; col < cell.colSpan; col++) { - c.sortList.push([ indx + col, order ]); + // save child row content (un-parsed!) + $cells = $row.children( 'th, td' ); + len = prevRowData.child.length; + prevRowData.child[ len ] = []; + // child row content does not account for colspans/rowspans; so indexing may be off + cacheIndex = 0; + max = c.columns; + for ( colIndex = 0; colIndex < max; colIndex++ ) { + cell = $cells[ colIndex ]; + if ( cell ) { + prevRowData.child[ len ][ colIndex ] = ts.getParsedText( c, cell, colIndex ); + span = $cells[ colIndex ].colSpan - 1; + if ( span > 0 ) { + cacheIndex += span; + max += span; + } } + cacheIndex++; } + // go to the next for loop + continue; } - // multi column sorting - } else { - // get rid of the sortAppend before adding more - fixes issue #115 & #523 - if (c.sortAppend && c.sortList.length > 1) { - for (col = 0; col < c.sortAppend.length; col++) { - s = ts.isValueInArray(c.sortAppend[col][0], c.sortList); - if (s >= 0) { - c.sortList.splice(s, 1); + rowData.$row = $row; + rowData.order = rowIndex; // add original row position to rowCache + cacheIndex = 0; + max = c.columns; + for ( colIndex = 0; colIndex < max; ++colIndex ) { + cell = $row[ 0 ].cells[ colIndex ]; + if ( typeof parsers[ cacheIndex ] === 'undefined' ) { + if ( c.debug ) { + console.warn( 'No parser found for column ' + colIndex + '; cell:', cell, 'does it have a header?' ); } - } - } - // the user has clicked on an already sorted column - if (ts.isValueInArray(indx, c.sortList) >= 0) { - // reverse the sorting direction - for (col = 0; col < c.sortList.length; col++) { - s = c.sortList[col]; - order = c.$headerIndexed[ s[0] ][0]; - if (s[0] === indx) { - // order.count seems to be incorrect when compared to cell.count - s[1] = order.order[cell.count]; - if (s[1] === 2) { - c.sortList.splice(col, 1); - order.count = -1; - } + } else if ( cell ) { + val = ts.getElementText( c, cell, cacheIndex ); + rowData.raw[ cacheIndex ] = val; // save original row text + txt = ts.getParsedText( c, cell, cacheIndex, val ); + cols[ cacheIndex ] = txt; + if ( ( parsers[ cacheIndex ].type || '' ).toLowerCase() === 'numeric' ) { + // determine column max value (ignore sign) + colMax[ cacheIndex ] = Math.max( Math.abs( txt ) || 0, colMax[ cacheIndex ] || 0 ); } - } - } else { - // add column to sort list array - order = cell.order[cell.count]; - if (order < 2) { - c.sortList.push([ indx, order ]); - // add other columns if header spans across multiple - if (cell.colSpan > 1) { - for (col = 1; col < cell.colSpan; col++) { - c.sortList.push([ indx + col, order ]); - } + // allow colSpan in tbody + span = cell.colSpan - 1; + if ( span > 0 ) { + cacheIndex += span; + max += span; } } + cacheIndex++; } + // ensure rowData is always in the same location (after the last column) + cols[ c.columns ] = rowData; + cache.normalized.push( cols ); + } + cache.colMax = colMax; + // total up rows, not including child rows + c.totalRows += cache.normalized.length; + + } + if ( c.showProcessing ) { + ts.isProcessing( table ); // remove processing icon + } + if ( c.debug ) { + console.log( 'Building cache for ' + totalRows + ' rows' + ts.benchmark( cacheTime ) ); + } + if ( $.isFunction( callback ) ) { + callback( table ); + } + }, + + getColumnText : function( table, column, callback, rowFilter ) { + table = $( table )[0]; + var tbodyIndex, rowIndex, cache, row, tbodyLen, rowLen, raw, parsed, $cell, result, + hasCallback = typeof callback === 'function', + allColumns = column === 'all', + data = { raw : [], parsed: [], $cell: [] }, + c = table.config; + if ( ts.isEmptyObject( c ) ) { + if ( c.debug ) { + console.warn( 'No cache found - aborting getColumnText function!' ); } - if (c.sortAppend !== null) { - arry = c.sortAppend; - for (col = 0; col < arry.length; col++) { - if (arry[col][0] !== indx) { - c.sortList.push(arry[col]); + } else { + tbodyLen = c.$tbodies.length; + for ( tbodyIndex = 0; tbodyIndex < tbodyLen; tbodyIndex++ ) { + cache = c.cache[ tbodyIndex ].normalized; + rowLen = cache.length; + for ( rowIndex = 0; rowIndex < rowLen; rowIndex++ ) { + row = cache[ rowIndex ]; + if ( rowFilter && !row[ c.columns ].$row.is( rowFilter ) ) { + continue; + } + result = true; + parsed = ( allColumns ) ? row.slice( 0, c.columns ) : row[ column ]; + row = row[ c.columns ]; + raw = ( allColumns ) ? row.raw : row.raw[ column ]; + $cell = ( allColumns ) ? row.$row.children() : row.$row.children().eq( column ); + if ( hasCallback ) { + result = callback({ + tbodyIndex : tbodyIndex, + rowIndex : rowIndex, + parsed : parsed, + raw : raw, + $row : row.$row, + $cell : $cell + }); + } + if ( result !== false ) { + data.parsed.push( parsed ); + data.raw.push( raw ); + data.$cell.push( $cell ); } } } - // sortBegin event triggered immediately before the sort - $table.trigger('sortBegin', table); - // setTimeout needed so the processing icon shows up - setTimeout(function(){ - // set css for headers - setHeadersCss(table); - multisort(table); - ts.appendCache( c ); - $table.trigger('sortEnd', table); - }, 1); - } - - // sort multiple columns - function multisort(table) { /*jshint loopfunc:true */ - var i, k, num, col, sortTime, colMax, - rows, order, sort, x, y, - dir = 0, - c = table.config, - cts = c.textSorter || '', - sortList = c.sortList, - l = sortList.length, - bl = c.$tbodies.length; - if (c.serverSideSorting || ts.isEmptyObject(c.cache)) { // empty table - fixes #206/#346 - return; - } - if (c.debug) { sortTime = new Date(); } - for (k = 0; k < bl; k++) { - colMax = c.cache[k].colMax; - rows = c.cache[k].normalized; - - rows.sort(function(a, b) { - // rows is undefined here in IE, so don't use it! - for (i = 0; i < l; i++) { - col = sortList[i][0]; - order = sortList[i][1]; - // sort direction, true = asc, false = desc - dir = order === 0; - - if (c.sortStable && a[col] === b[col] && l === 1) { - return a[c.columns].order - b[c.columns].order; - } + // return everything + return data; + } + }, - // fallback to natural sort since it is more robust - num = /n/i.test(getCachedSortType(c.parsers, col)); - if (num && c.strings[col]) { - // sort strings in numerical columns - if (typeof (c.string[c.strings[col]]) === 'boolean') { - num = (dir ? 1 : -1) * (c.string[c.strings[col]] ? -1 : 1); - } else { - num = (c.strings[col]) ? c.string[c.strings[col]] || 0 : 0; - } - // fall back to built-in numeric sort - // var sort = $.tablesorter['sort' + s]( a[c], b[c], dir, colMax[c], table); - sort = c.numberSorter ? c.numberSorter(a[col], b[col], dir, colMax[col], table) : - ts[ 'sortNumeric' + (dir ? 'Asc' : 'Desc') ](a[col], b[col], num, colMax[col], col, table); - } else { - // set a & b depending on sort direction - x = dir ? a : b; - y = dir ? b : a; - // text sort function - if (typeof cts === 'function') { - // custom OVERALL text sorter - sort = cts(x[col], y[col], dir, col, table); - } else if (typeof cts === 'object' && cts.hasOwnProperty(col)) { - // custom text sorter for a SPECIFIC COLUMN - sort = cts[col](x[col], y[col], dir, col, table); - } else { - // fall back to natural sort - sort = ts[ 'sortNatural' + (dir ? 'Asc' : 'Desc') ](a[col], b[col], col, table, c); - } - } - if (sort) { return sort; } + /* + ██ ██ █████▄ █████▄ ▄████▄ ██████ ██████ + ██ ██ ██▄▄██ ██ ██ ██▄▄██ ██ ██▄▄ + ██ ██ ██▀▀▀ ██ ██ ██▀▀██ ██ ██▀▀ + ▀████▀ ██ █████▀ ██ ██ ██ ██████ + */ + setHeadersCss : function( c ) { + var $sorted, header, indx, column, $header, nextSort, txt, tmp, + list = c.sortList, + len = list.length, + none = ts.css.sortNone + ' ' + c.cssNone, + css = [ ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc ], + cssIcon = [ c.cssIconAsc, c.cssIconDesc, c.cssIconNone ], + aria = [ 'ascending', 'descending' ], + // find the footer + $headers = c.$table + .find( 'tfoot tr' ) + .children() + .add( $( c.namespace + '_extra_headers' ) ) + .removeClass( css.join( ' ' ) ); + // remove all header information + c.$headers + .removeClass( css.join( ' ' ) ) + .addClass( none ) + .attr( 'aria-sort', 'none' ) + .find( '.' + ts.css.icon ) + .removeClass( cssIcon.join( ' ' ) ) + .addClass( cssIcon[ 2 ] ); + for ( indx = 0; indx < len; indx++ ) { + // direction = 2 means reset! + if ( list[ indx ][ 1 ] !== 2 ) { + // multicolumn sorting updating - see #1005 + // .not(function(){}) needs jQuery 1.4 + $sorted = c.$headers.filter( function( i, el ) { + // only include headers that are in the sortList (this includes colspans) + var include = true, + $el = $( el ), + col = parseInt( $el.attr( 'data-column' ), 10 ), + end = col + el.colSpan; + for ( ; col < end; col++ ) { + include = include ? ts.isValueInArray( col, c.sortList ) > -1 : false; } - return a[c.columns].order - b[c.columns].order; + return include; }); - } - if (c.debug) { - console.log( 'Sorting on ' + sortList.toString() + ' and dir ' + order + ' time' + ts.benchmark(sortTime) ); - } - } - function resortComplete(c, callback){ - if (c.table.isUpdating) { - c.$table.trigger('updateComplete', c.table); + // choose the :last in case there are nested columns + $sorted = $sorted + .not( '.sorter-false' ) + .filter( '[data-column="' + list[ indx ][ 0 ] + '"]' + ( len === 1 ? ':last' : '' ) ); + if ( $sorted.length ) { + for ( column = 0; column < $sorted.length; column++ ) { + if ( !$sorted[ column ].sortDisabled ) { + $sorted + .eq( column ) + .removeClass( none ) + .addClass( css[ list[ indx ][ 1 ] ] ) + .attr( 'aria-sort', aria[ list[ indx ][ 1 ] ] ) + .find( '.' + ts.css.icon ) + .removeClass( cssIcon[ 2 ] ) + .addClass( cssIcon[ list[ indx ][ 1 ] ] ); + } + } + // add sorted class to footer & extra headers, if they exist + if ( $headers.length ) { + $headers + .filter( '[data-column="' + list[ indx ][ 0 ] + '"]' ) + .removeClass( none ) + .addClass( css[ list[ indx ][ 1 ] ] ); + } + } } - if ($.isFunction(callback)) { - callback(c.table); + } + // add verbose aria labels + len = c.$headers.length; + $headers = c.$headers.not( '.sorter-false' ); + for ( indx = 0; indx < len; indx++ ) { + $header = $headers.eq( indx ); + if ( $header.length ) { + header = $headers[ indx ]; + column = parseInt( $header.attr( 'data-column' ), 10 ); + nextSort = c.sortVars[ column ].order[ ( c.sortVars[ column ].count + 1 ) % ( c.sortReset ? 3 : 2 ) ]; + tmp = $header.hasClass( ts.css.sortAsc ) ? + 'sortAsc' : + $header.hasClass( ts.css.sortDesc ) ? 'sortDesc' : 'sortNone'; + txt = $.trim( $header.text() ) + ': ' + + ts.language[ tmp ] + + ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ]; + $header.attr( 'aria-label', txt ); } } + }, - function checkResort(c, resort, callback) { - var sl = $.isArray(resort) ? resort : c.sortList, - // if no resort parameter is passed, fallback to config.resort (true by default) - resrt = typeof resort === 'undefined' ? c.resort : resort; - // don't try to resort if the table is still processing - // this will catch spamming of the updateCell method - if (resrt !== false && !c.serverSideSorting && !c.table.isProcessing) { - if (sl.length) { - c.$table.trigger('sorton', [ sl, function(){ - resortComplete(c, callback); - }, true ]); + updateHeader : function( c ) { + var index, isDisabled, $th, col, + table = c.table, + len = c.$headers.length; + for ( index = 0; index < len; index++ ) { + $th = c.$headers.eq( index ); + col = ts.getColumnData( table, c.headers, index, true ); + // add 'sorter-false' class if 'parser-false' is set + isDisabled = ts.getData( $th, col, 'sorter' ) === 'false' || ts.getData( $th, col, 'parser' ) === 'false'; + $th[ 0 ].sortDisabled = isDisabled; + $th[ isDisabled ? 'addClass' : 'removeClass' ]( 'sorter-false' ).attr( 'aria-disabled', '' + isDisabled ); + // disable tab index on disabled cells + if ( c.tabIndex ) { + if ( isDisabled ) { + $th.removeAttr( 'tabindex' ); } else { - c.$table.trigger('sortReset', [ function(){ - resortComplete(c, callback); - ts.applyWidget(c.table, false); - } ]); + $th.attr( 'tabindex', '0' ); + } + } + // aria-controls - requires table ID + if ( table.id ) { + if ( isDisabled ) { + $th.removeAttr( 'aria-controls' ); + } else { + $th.attr( 'aria-controls', table.id ); } - } else { - resortComplete(c, callback); - ts.applyWidget(c.table, false); } } + }, - function bindMethods( table ){ - var c = table.config, - $table = c.$table, - events = ( 'sortReset update updateRows updateAll updateHeaders addRows updateCell updateComplete ' + - 'sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup ' + - 'mouseleave ' ).split( ' ' ) - .join( c.namespace + ' ' ); - // apply easy methods that trigger bound events - $table - .unbind( events.replace( ts.regex.spaces, ' ' ) ) - .bind( 'sortReset' + c.namespace, function( e, callback ) { - e.stopPropagation(); - // using this.config to ensure functions are getting a non-cached version of the config - ts.sortReset( this.config, callback ); - }) - .bind( 'updateAll' + c.namespace, function( e, resort, callback ) { - e.stopPropagation(); - ts.updateAll( this.config, resort, callback ); - }) - .bind( 'update' + c.namespace + ' updateRows' + c.namespace, function( e, resort, callback ) { - e.stopPropagation(); - ts.update( this.config, resort, callback ); - }) - .bind( 'updateHeaders' + c.namespace, function( e, callback ) { - e.stopPropagation(); - ts.updateHeaders( this.config, callback ); - }) - .bind( 'updateCell' + c.namespace, function(e, cell, resort, callback ) { - e.stopPropagation(); - ts.updateCell( this.config, cell, resort, callback ); - }) - .bind( 'addRows' + c.namespace, function(e, $row, resort, callback) { - e.stopPropagation(); - ts.addRows( this.config, $row, resort, callback ); - }) - .bind( 'updateComplete' + c.namespace, function() { - table.isUpdating = false; - }) - .bind( 'sorton' + c.namespace, function( e, list, callback, init ) { - e.stopPropagation(); - ts.sortOn( this.config, list, callback, init ); - }) - .bind( 'appendCache' + c.namespace, function( e, callback, init ) { - e.stopPropagation(); - ts.appendCache( this.config, init ); - if ( $.isFunction( callback ) ) { - callback( table ); + updateHeaderSortCount : function( c, list ) { + var col, dir, group, indx, primary, temp, val, order, + sortList = list || c.sortList, + len = sortList.length; + c.sortList = []; + for ( indx = 0; indx < len; indx++ ) { + val = sortList[ indx ]; + // ensure all sortList values are numeric - fixes #127 + col = parseInt( val[ 0 ], 10 ); + // prevents error if sorton array is wrong + if ( col < c.columns ) { + order = c.sortVars[ col ].order; + dir = ( '' + val[ 1 ] ).match( /^(1|d|s|o|n)/ ); + dir = dir ? dir[ 0 ] : ''; + // 0/(a)sc (default), 1/(d)esc, (s)ame, (o)pposite, (n)ext + switch ( dir ) { + case '1' : case 'd' : // descending + dir = 1; + break; + case 's' : // same direction (as primary column) + // if primary sort is set to 's', make it ascending + dir = primary || 0; + break; + case 'o' : + temp = order[ ( primary || 0 ) % ( c.sortReset ? 3 : 2 ) ]; + // opposite of primary column; but resets if primary resets + dir = temp === 0 ? 1 : temp === 1 ? 0 : 2; + break; + case 'n' : + dir = order[ ( ++c.sortVars[ col ].count ) % ( c.sortReset ? 3 : 2 ) ]; + break; + default : // ascending + dir = 0; + break; } - }) - // $tbodies variable is used by the tbody sorting widget - .bind( 'updateCache' + c.namespace, function( e, callback, $tbodies ){ - e.stopPropagation(); - ts.updateCache( this.config, callback, $tbodies ); - }) - .bind( 'applyWidgetId' + c.namespace, function( e, id ) { - e.stopPropagation(); - ts.getWidgetById( id ).format( table, this.config, this.config.widgetOptions ); - }) - .bind( 'applyWidgets' + c.namespace, function( e, init ) { - e.stopPropagation(); - // apply widgets - ts.applyWidget( table, init ); - }) - .bind( 'refreshWidgets' + c.namespace, function( e, all, dontapply ) { - e.stopPropagation(); - ts.refreshWidgets( table, all, dontapply ); - }) - .bind( 'destroy' + c.namespace, function( e, removeClasses, callback ) { - e.stopPropagation(); - ts.destroy( table, removeClasses, callback ); - }) - .bind( 'resetToLoadState' + c.namespace, function( e ) { - e.stopPropagation(); - // remove all widgets - ts.removeWidget( table, true, false ); - // restore original settings; this clears out current settings, but does not clear - // values saved to storage. - c = $.extend( true, ts.defaults, c.originalSettings ); - table.hasInitialized = false; - // setup the entire table again - ts.setup( table, c ); - }); + primary = indx === 0 ? dir : primary; + group = [ col, parseInt( dir, 10 ) || 0 ]; + c.sortList.push( group ); + dir = $.inArray( group[ 1 ], order ); // fixes issue #167 + c.sortVars[ col ].count = dir >= 0 ? dir : group[ 1 ] % ( c.sortReset ? 3 : 2 ); + } } + }, - /* public methods */ - ts.construct = function(settings) { - return this.each(function() { - var table = this, - // merge & extend config options - c = $.extend(true, {}, ts.defaults, settings, ts.instanceMethods); - // save initial settings - c.originalSettings = settings; - // create a table from data (build table widget) - if (!table.hasInitialized && ts.buildTable && this.nodeName !== 'TABLE') { - // return the table (in case the original target is the table's container) - ts.buildTable(table, c); - } else { - ts.setup(table, c); - } - }); - }; + updateAll : function( c, resort, callback ) { + var table = c.table; + table.isUpdating = true; + ts.refreshWidgets( table, true, true ); + ts.buildHeaders( c ); + ts.bindEvents( table, c.$headers, true ); + ts.bindMethods( c ); + ts.commonUpdate( c, resort, callback ); + }, - ts.setup = function(table, c) { - // if no thead or tbody, or tablesorter is already present, quit - if (!table || !table.tHead || table.tBodies.length === 0 || table.hasInitialized === true) { - if ( c.debug ) { - if ( table.hasInitialized ) { - console.warn( 'Stopping initialization. Tablesorter has already been initialized' ); + update : function( c, resort, callback ) { + var table = c.table; + table.isUpdating = true; + // update sorting (if enabled/disabled) + ts.updateHeader( c ); + ts.commonUpdate( c, resort, callback ); + }, + + // simple header update - see #989 + updateHeaders : function( c, callback ) { + c.table.isUpdating = true; + ts.buildHeaders( c ); + ts.bindEvents( c.table, c.$headers, true ); + ts.resortComplete( c, callback ); + }, + + updateCell : function( c, cell, resort, callback ) { + c.table.isUpdating = true; + c.$table.find( c.selectorRemove ).remove(); + // get position from the dom + var tmp, indx, row, icell, cache, len, + $tbodies = c.$tbodies, + $cell = $( cell ), + // update cache - format: function( s, table, cell, cellIndex ) + // no closest in jQuery v1.2.6 + tbodyIndex = $tbodies + .index( $.fn.closest ? $cell.closest( 'tbody' ) : $cell.parents( 'tbody' ).filter( ':first' ) ), + tbcache = c.cache[ tbodyIndex ], + $row = $.fn.closest ? $cell.closest( 'tr' ) : $cell.parents( 'tr' ).filter( ':first' ); + cell = $cell[ 0 ]; // in case cell is a jQuery object + // tbody may not exist if update is initialized while tbody is removed for processing + if ( $tbodies.length && tbodyIndex >= 0 ) { + row = $tbodies.eq( tbodyIndex ).find( 'tr' ).index( $row ); + cache = tbcache.normalized[ row ]; + len = $row[ 0 ].cells.length; + if ( len !== c.columns ) { + // colspan in here somewhere! + icell = 0; + tmp = false; + for ( indx = 0; indx < len; indx++ ) { + if ( !tmp && $row[ 0 ].cells[ indx ] !== cell ) { + icell += $row[ 0 ].cells[ indx ].colSpan; } else { - console.error( 'Stopping initialization! No table, thead or tbody' ); + tmp = true; } } - return; - } - - var k = '', - $table = $(table), - m = $.metadata; - // initialization flag - table.hasInitialized = false; - // table is being processed flag - table.isProcessing = true; - // make sure to store the config object - table.config = c; - // save the settings where they read - $.data(table, 'tablesorter', c); - if (c.debug) { - console[ console.group ? 'group' : 'log' ]( 'Initializing tablesorter' ); - $.data( table, 'startoveralltimer', new Date()); - } - - // removing this in version 3 (only supports jQuery 1.7+) - c.supportsDataObject = (function(version) { - version[0] = parseInt(version[0], 10); - return (version[0] > 1) || (version[0] === 1 && parseInt(version[1], 10) >= 4); - })($.fn.jquery.split('.')); - // digit sort text location; keeping max+/- for backwards compatibility - c.string = { 'max': 1, 'min': -1, 'emptymin': 1, 'emptymax': -1, 'zero': 0, 'none': 0, 'null': 0, 'top': true, 'bottom': false }; - // ensure case insensitivity - c.emptyTo = c.emptyTo.toLowerCase(); - c.stringTo = c.stringTo.toLowerCase(); - // add table theme class only if there isn't already one there - if (!/tablesorter\-/.test($table.attr('class'))) { - k = (c.theme !== '' ? ' tablesorter-' + c.theme : ''); - } - c.table = table; - c.$table = $table - .addClass(ts.css.table + ' ' + c.tableClass + k) - .attr('role', 'grid'); - c.$headers = $table.find(c.selectorHeaders); - - // give the table a unique id, which will be used in namespace binding - if (!c.namespace) { - c.namespace = '.tablesorter' + Math.random().toString(16).slice(2); } else { - // make sure namespace starts with a period & doesn't have weird characters - c.namespace = '.' + c.namespace.replace(ts.regex.nonWord, ''); + icell = $cell.index(); } - - c.$table.children().children('tr').attr('role', 'row'); - c.$tbodies = $table.children('tbody:not(.' + c.cssInfoBlock + ')').attr({ - 'aria-live' : 'polite', - 'aria-relevant' : 'all' - }); - if (c.$table.children('caption').length) { - k = c.$table.children('caption')[0]; - if (!k.id) { k.id = c.namespace.slice(1) + 'caption'; } - c.$table.attr('aria-labelledby', k.id); - } - c.widgetInit = {}; // keep a list of initialized widgets - // change textExtraction via data-attribute - c.textExtraction = c.$table.attr('data-text-extraction') || c.textExtraction || 'basic'; - // build headers - buildHeaders( c ); - // fixate columns if the users supplies the fixedWidth option - // do this after theme has been applied - ts.fixColumnWidth(table); - // add widget options before parsing (e.g. grouping widget has parser settings) - ts.applyWidgetOptions(table, c); - // try to auto detect column type, and store in tables config - buildParserCache(c); - // start total row count at zero - c.totalRows = 0; - // build the cache for the tbody cells - // delayInit will delay building the cache until the user starts a sort - if (!c.delayInit) { buildCache(table); } - // bind all header events and methods - ts.bindEvents(table, c.$headers, true); - bindMethods(table); - // get sort list from jQuery data or metadata - // in jQuery < 1.4, an error occurs when calling $table.data() - if (c.supportsDataObject && typeof $table.data().sortlist !== 'undefined') { - c.sortList = $table.data().sortlist; - } else if (m && ($table.metadata() && $table.metadata().sortlist)) { - c.sortList = $table.metadata().sortlist; - } - // apply widget init code - ts.applyWidget(table, true); - // if user has supplied a sort list to constructor - if (c.sortList.length > 0) { - $table.trigger('sorton', [ c.sortList, {}, !c.initWidgets, true ]); + tmp = ts.getElementText( c, cell, icell ); // raw + cache[ c.columns ].raw[ icell ] = tmp; + tmp = ts.getParsedText( c, cell, icell, tmp ); + cache[ icell ] = tmp; // parsed + cache[ c.columns ].$row = $row; + if ( ( c.parsers[ icell ].type || '' ).toLowerCase() === 'numeric' ) { + // update column max value (ignore sign) + tbcache.colMax[ icell ] = Math.max( Math.abs( tmp ) || 0, tbcache.colMax[ icell ] || 0 ); + } + tmp = resort !== 'undefined' ? resort : c.resort; + if ( tmp !== false ) { + // widgets will be reapplied + ts.checkResort( c, tmp, callback ); } else { - setHeadersCss(table); - if (c.initWidgets) { - // apply widget format - ts.applyWidget(table, false); - } - } - - // show processesing icon - if (c.showProcessing) { - $table - .unbind('sortBegin' + c.namespace + ' sortEnd' + c.namespace) - .bind('sortBegin' + c.namespace + ' sortEnd' + c.namespace, function(e) { - clearTimeout(c.processTimer); - ts.isProcessing(table); - if (e.type === 'sortBegin') { - c.processTimer = setTimeout(function(){ - ts.isProcessing(table, true); - }, 500); - } - }); + // don't reapply widgets is resort is false, just in case it causes + // problems with element focus + ts.resortComplete( c, callback ); } + } + }, - // initialized - table.hasInitialized = true; - table.isProcessing = false; - if (c.debug) { - console.log( 'Overall initialization time: ' + ts.benchmark( $.data( table, 'startoveralltimer') ) ); - if ( c.debug && console.groupEnd ) { console.groupEnd(); } - } - $table.trigger('tablesorter-initialized', table); - if (typeof c.initialized === 'function') { c.initialized(table); } - }; - - // automatically add a colgroup with col elements set to a percentage width - ts.fixColumnWidth = function(table) { - table = $(table)[0]; - var overallWidth, percent, $tbodies, len, index, - c = table.config, - $colgroup = c.$table.children('colgroup'); - // remove plugin-added colgroup, in case we need to refresh the widths - if ($colgroup.length && $colgroup.hasClass(ts.css.colgroup)) { - $colgroup.remove(); - } - if (c.widthFixed && c.$table.children('colgroup').length === 0) { - $colgroup = $('<colgroup class="' + ts.css.colgroup + '">'); - overallWidth = c.$table.width(); - // only add col for visible columns - fixes #371 - $tbodies = c.$tbodies.find('tr:first').children(':visible'); // .each(function() - len = $tbodies.length; - for ( index = 0; index < len; index++ ) { - percent = parseInt( ( $tbodies.eq( index ).width() / overallWidth ) * 1000, 10 ) / 10 + '%'; - $colgroup.append( $('<col>').css('width', percent) ); - } - c.$table.prepend($colgroup); - } - }; - - ts.getColumnData = function(table, obj, indx, getCell, $headers){ - if (typeof obj === 'undefined' || obj === null) { return; } - table = $(table)[0]; - var $h, k, - c = table.config, - $cells = ( $headers || c.$headers ), - // c.$headerIndexed is not defined initially - $cell = c.$headerIndexed && c.$headerIndexed[indx] || $cells.filter('[data-column="' + indx + '"]:last'); - if (obj[indx]) { - return getCell ? obj[indx] : obj[$cells.index( $cell )]; - } - for (k in obj) { - if (typeof k === 'string') { - $h = $cell - // header cell with class/id - .filter(k) - // find elements within the header cell with cell/id - .add( $cell.find(k) ); - if ($h.length) { - return obj[k]; - } - } + addRows : function( c, $row, resort, callback ) { + var txt, val, tbodyIndex, rowIndex, rows, cellIndex, len, + cacheIndex, rowData, cells, cell, span, + // allow passing a row string if only one non-info tbody exists in the table + valid = typeof $row === 'string' && c.$tbodies.length === 1 && /<tr/.test( $row || '' ), + table = c.table; + if ( valid ) { + $row = $( $row ); + c.$tbodies.append( $row ); + } else if ( !$row || + // row is a jQuery object? + !( $row instanceof jQuery ) || + // row contained in the table? + ( $.fn.closest ? $row.closest( 'table' )[ 0 ] : $row.parents( 'table' )[ 0 ] ) !== c.table ) { + if ( c.debug ) { + console.error( 'addRows method requires (1) a jQuery selector reference to rows that have already ' + + 'been added to the table, or (2) row HTML string to be added to a table with only one tbody' ); } - return; - }; - - // computeTableHeaderCellIndexes from: - // http://www.javascripttoolbox.com/lib/table/examples.php - // http://www.javascripttoolbox.com/temp/table_cellindex.html - ts.computeColumnIndex = function(trs) { - var i, j, k, l, $cell, cell, cells, rowIndex, cellId, rowSpan, colSpan, firstAvailCol, - matrix = [], - matrixrow = [], - lookup = {}; - for (i = 0; i < trs.length; i++) { - cells = trs[i].cells; - for (j = 0; j < cells.length; j++) { - cell = cells[j]; - $cell = $(cell); - rowIndex = cell.parentNode.rowIndex; - cellId = rowIndex + '-' + $cell.index(); - rowSpan = cell.rowSpan || 1; - colSpan = cell.colSpan || 1; - if (typeof matrix[rowIndex] === 'undefined') { - matrix[rowIndex] = []; - } - // Find first available column in the first row - for (k = 0; k < matrix[rowIndex].length + 1; k++) { - if (typeof matrix[rowIndex][k] === 'undefined') { - firstAvailCol = k; - break; - } + return false; + } + table.isUpdating = true; + if ( ts.isEmptyObject( c.cache ) ) { + // empty table, do an update instead - fixes #450 + ts.updateHeader( c ); + ts.commonUpdate( c, resort, callback ); + } else { + rows = $row.filter( 'tr' ).attr( 'role', 'row' ).length; + tbodyIndex = c.$tbodies.index( $row.parents( 'tbody' ).filter( ':first' ) ); + // fixes adding rows to an empty table - see issue #179 + if ( !( c.parsers && c.parsers.length ) ) { + ts.setupParsers( c ); + } + // add each row + for ( rowIndex = 0; rowIndex < rows; rowIndex++ ) { + cacheIndex = 0; + len = $row[ rowIndex ].cells.length; + cells = []; + rowData = { + child : [], + raw : [], + $row : $row.eq( rowIndex ), + order : c.cache[ tbodyIndex ].normalized.length + }; + // add each cell + for ( cellIndex = 0; cellIndex < len; cellIndex++ ) { + cell = $row[ rowIndex ].cells[ cellIndex ]; + txt = ts.getElementText( c, cell, cacheIndex ); + rowData.raw[ cacheIndex ] = txt; + val = ts.getParsedText( c, cell, cacheIndex, txt ); + cells[ cacheIndex ] = val; + if ( ( c.parsers[ cacheIndex ].type || '' ).toLowerCase() === 'numeric' ) { + // update column max value (ignore sign) + c.cache[ tbodyIndex ].colMax[ cacheIndex ] = + Math.max( Math.abs( val ) || 0, c.cache[ tbodyIndex ].colMax[ cacheIndex ] || 0 ); } - lookup[cellId] = firstAvailCol; - // add data-column - $cell.attr({ 'data-column' : firstAvailCol }); // 'data-row' : rowIndex - for (k = rowIndex; k < rowIndex + rowSpan; k++) { - if (typeof matrix[k] === 'undefined') { - matrix[k] = []; - } - matrixrow = matrix[k]; - for (l = firstAvailCol; l < firstAvailCol + colSpan; l++) { - matrixrow[l] = 'x'; - } + span = cell.colSpan - 1; + if ( span > 0 ) { + cacheIndex += span; } + cacheIndex++; } + // add the row data to the end + cells[ c.columns ] = rowData; + // update cache + c.cache[ tbodyIndex ].normalized.push( cells ); } - return matrixrow.length; - }; - - // *** Process table *** - // add processing indicator - ts.isProcessing = function( $table, toggle, $ths ) { - $table = $( $table ); - var c = $table[0].config, - // default to all headers - $h = $ths || $table.find('.' + ts.css.header); - if (toggle) { - // don't use sortList if custom $ths used - if (typeof $ths !== 'undefined' && c.sortList.length > 0) { - // get headers from the sortList - $h = $h.filter(function(){ - // get data-column from attr to keep compatibility with jQuery 1.2.6 - return this.sortDisabled ? false : ts.isValueInArray( parseFloat($(this).attr('data-column')), c.sortList) >= 0; - }); - } - $table.add($h).addClass(ts.css.processing + ' ' + c.cssProcessing); - } else { - $table.add($h).removeClass(ts.css.processing + ' ' + c.cssProcessing); - } - }; - - // detach tbody but save the position - // don't use tbody because there are portions that look for a tbody index (updateCell) - ts.processTbody = function(table, $tb, getIt){ - table = $(table)[0]; - var holdr; - if (getIt) { - table.isProcessing = true; - $tb.before('<colgroup class="tablesorter-savemyplace"/>'); - holdr = ($.fn.detach) ? $tb.detach() : $tb.remove(); - return holdr; - } - holdr = $(table).find('colgroup.tablesorter-savemyplace'); - $tb.insertAfter( holdr ); - holdr.remove(); - table.isProcessing = false; - }; + // resort using current settings + ts.checkResort( c, resort, callback ); + } + }, - ts.clearTableBody = function(table) { - $(table)[0].config.$tbodies.children().detach(); - }; + updateCache : function( c, callback, $tbodies ) { + // rebuild parsers + if ( !( c.parsers && c.parsers.length ) ) { + ts.setupParsers( c, $tbodies ); + } + // rebuild the cache map + ts.buildCache( c, callback, $tbodies ); + }, - ts.bindEvents = function(table, $headers, core) { - table = $(table)[0]; - var t, downTarget = null, - c = table.config; - if (core !== true) { - $headers.addClass( c.namespace.slice(1) + '_extra_headers' ); - t = $.fn.closest ? $headers.closest('table')[0] : $headers.parents('table')[0]; - if (t && t.nodeName === 'TABLE' && t !== table) { - $(t).addClass( c.namespace.slice(1) + '_extra_table' ); - } - } - t = ( c.pointerDown + ' ' + c.pointerUp + ' ' + c.pointerClick + ' sort keyup ' ) - .replace(ts.regex.spaces, ' ') - .split(' ') - .join(c.namespace + ' '); - // apply event handling to headers and/or additional headers (stickyheaders, scroller, etc) - $headers - // http://stackoverflow.com/questions/5312849/jquery-find-self; - .find(c.selectorSort).add( $headers.filter(c.selectorSort) ) - .unbind(t) - .bind(t, function(e, external) { - var $cell, cell, temp, - $target = $(e.target), - // wrap event type in spaces, so the match doesn't trigger on inner words - type = ' ' + e.type + ' '; - // only recognize left clicks - if ( ( ( e.which || e.button ) !== 1 && !type.match( ' ' + c.pointerClick + ' | sort | keyup ' ) ) || - // allow pressing enter - ( type === ' keyup ' && e.which !== 13 ) || - // allow triggering a click event (e.which is undefined) & ignore physical clicks - ( type.match(' ' + c.pointerClick + ' ') && typeof e.which !== 'undefined' ) ) { - return; - } - // ignore mouseup if mousedown wasn't on the same target - if ( type.match(' ' + c.pointerUp + ' ') && downTarget !== e.target && external !== true ) { return; } - // set target on mousedown - if ( type.match(' ' + c.pointerDown + ' ') ) { - downTarget = e.target; - // preventDefault needed or jQuery v1.3.2 and older throws an - // "Uncaught TypeError: handler.apply is not a function" error - temp = $target.jquery.split( '.' ); - if ( temp[0] === '1' && temp[1] < 4 ) { e.preventDefault(); } - return; - } - downTarget = null; - // prevent sort being triggered on form elements - if ( ts.regex.formElements.test(e.target.nodeName) || - // nosort class name, or elements within a nosort container - $target.hasClass(c.cssNoSort) || $target.parents('.' + c.cssNoSort).length > 0 || - // elements within a button - $target.parents('button').length > 0 ) { - return !c.cancelSelection; - } - if (c.delayInit && ts.isEmptyObject(c.cache)) { buildCache(table); } - // jQuery v1.2.6 doesn't have closest() - $cell = $.fn.closest ? $(this).closest('th, td') : /TH|TD/.test(this.nodeName) ? $(this) : $(this).parents('th, td'); - // reference original table headers and find the same cell - // don't use $headers or IE8 throws an error - see #987 - temp = $headers.index( $cell ); - c.lastClickedIndex = ( temp < 0 ) ? $cell.attr('data-column') : temp; - // use column index if $headers is undefined - cell = c.$headers[ c.lastClickedIndex ]; - if (cell && !cell.sortDisabled) { - initSort(table, cell, e); + // init flag (true) used by pager plugin to prevent widget application + // renamed from appendToTable + appendCache : function( c, init ) { + var parsed, totalRows, $tbody, $curTbody, rowIndex, tbodyIndex, appendTime, + table = c.table, + wo = c.widgetOptions, + $tbodies = c.$tbodies, + rows = [], + cache = c.cache; + // empty table - fixes #206/#346 + if ( ts.isEmptyObject( cache ) ) { + // run pager appender in case the table was just emptied + return c.appender ? c.appender( table, rows ) : + table.isUpdating ? c.$table.trigger( 'updateComplete', table ) : ''; // Fixes #532 + } + if ( c.debug ) { + appendTime = new Date(); + } + for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = $tbodies.eq( tbodyIndex ); + if ( $tbody.length ) { + // detach tbody for manipulation + $curTbody = ts.processTbody( table, $tbody, true ); + parsed = cache[ tbodyIndex ].normalized; + totalRows = parsed.length; + for ( rowIndex = 0; rowIndex < totalRows; rowIndex++ ) { + rows.push( parsed[ rowIndex ][ c.columns ].$row ); + // removeRows used by the pager plugin; don't render if using ajax - fixes #411 + if ( !c.appender || ( c.pager && ( !c.pager.removeRows || !wo.pager_removeRows ) && !c.pager.ajax ) ) { + $curTbody.append( parsed[ rowIndex ][ c.columns ].$row ); + } } - }); - if (c.cancelSelection) { - // cancel selection - $headers - .attr('unselectable', 'on') - .bind('selectstart', false) - .css({ - 'user-select': 'none', - 'MozUserSelect': 'none' // not needed for jQuery 1.8+ - }); + // restore tbody + ts.processTbody( table, $curTbody, false ); } - }; - - ts.sortReset = function( c, callback ) { - var table = c.table; - c.sortList = []; - setHeadersCss( table ); - multisort( table ); - ts.appendCache( c ); - if ( $.isFunction( callback ) ) { - callback( table ); - } - }; - - ts.updateAll = function( c, resort, callback ) { - var table = c.table; - table.isUpdating = true; - ts.refreshWidgets( table, true, true ); - buildHeaders( c ); - ts.bindEvents( table, c.$headers, true ); - bindMethods( table); - commonUpdate( table, resort, callback ); - }; - - ts.update = function( c, resort, callback ) { - var table = c.table; - table.isUpdating = true; - // update sorting (if enabled/disabled) - updateHeader( table ); - commonUpdate( table, resort, callback ); - }; + } + if ( c.appender ) { + c.appender( table, rows ); + } + if ( c.debug ) { + console.log( 'Rebuilt table' + ts.benchmark( appendTime ) ); + } + // apply table widgets; but not before ajax completes + if ( !init && !c.appender ) { + ts.applyWidget( table ); + } + if ( table.isUpdating ) { + c.$table.trigger( 'updateComplete', table ); + } + }, - // simple header update - see #989 - ts.updateHeaders = function( c, callback ) { - c.table.isUpdating = true; - buildHeaders( c ); - ts.bindEvents( c.table, c.$headers, true ); - resortComplete( c, callback ); - }; + commonUpdate : function( c, resort, callback ) { + // remove rows/elements before update + c.$table.find( c.selectorRemove ).remove(); + // rebuild parsers + ts.setupParsers( c ); + // rebuild the cache map + ts.buildCache( c ); + ts.checkResort( c, resort, callback ); + }, - ts.updateCell = function( c, cell, resort, callback ) { - c.table.isUpdating = true; - c.$table.find( c.selectorRemove ).remove(); - // get position from the dom - var t, row, icell, cache, + /* + ▄█████ ▄████▄ █████▄ ██████ ██ █████▄ ▄████▄ + ▀█▄ ██ ██ ██▄▄██ ██ ██ ██ ██ ██ ▄▄▄ + ▀█▄ ██ ██ ██▀██ ██ ██ ██ ██ ██ ▀██ + █████▀ ▀████▀ ██ ██ ██ ██ ██ ██ ▀████▀ + */ + initSort : function( c, cell, event ) { + if ( c.table.isUpdating ) { + // let any updates complete before initializing a sort + return setTimeout( function(){ + ts.initSort( c, cell, event ); + }, 50 ); + } + var arry, indx, headerIndx, dir, temp, tmp, $header, + notMultiSort = !event[ c.sortMultiSortKey ], table = c.table, - $tb = c.$tbodies, - $cell = $(cell), - // update cache - format: function(s, table, cell, cellIndex) - // no closest in jQuery v1.2.6 - tbdy = $tb.index( $(cell).closest('tbody') ),$row = $(cell).closest('tr'); - tbdy = $tb.index( $.fn.closest ? $cell.closest( 'tbody' ) : $cell.parents( 'tbody' ).filter( ':first' ) ), - tbcache = c.cache[ tbdy ], - $row = $.fn.closest ? $cell.closest( 'tr' ) : $cell.parents( 'tr' ).filter( ':first' ); - cell = $cell[ 0 ]; // in case cell is a jQuery object - // tbody may not exist if update is initialized while tbody is removed for processing - if ( $tb.length && tbdy >= 0 ) { - row = $tb.eq( tbdy ).find( 'tr' ).index( $row ); - cache = tbcache.normalized[ row ]; - icell = $cell.index(); - t = ts.getParsedText( c, cell, icell ); - cache[ icell ] = t; - cache[ c.columns ].$row = $row; - if ( ( c.parsers[ icell ].type || '' ).toLowerCase() === 'numeric' ) { - // update column max value (ignore sign) - tbcache.colMax[ icell ] = Math.max( Math.abs( t ) || 0, tbcache.colMax[ icell ] || 0 ); + len = c.$headers.length, + // get current column index + col = parseInt( $( cell ).attr( 'data-column' ), 10 ), + order = c.sortVars[ col ].order; + + // Only call sortStart if sorting is enabled + c.$table.trigger( 'sortStart', table ); + // get current column sort order + c.sortVars[ col ].count = + event[ c.sortResetKey ] ? 2 : ( c.sortVars[ col ].count + 1 ) % ( c.sortReset ? 3 : 2 ); + // reset all sorts on non-current column - issue #30 + if ( c.sortRestart ) { + tmp = cell; + for ( headerIndx = 0; headerIndx < len; headerIndx++ ) { + $header = c.$headers.eq( headerIndx ); + // only reset counts on columns that weren't just clicked on and if not included in a multisort + if ( $header[ 0 ] !== tmp && + ( notMultiSort || !$header.is( '.' + ts.css.sortDesc + ',.' + ts.css.sortAsc ) ) ) { + c.sortVars[ col ].count = -1; } - t = resort !== 'undefined' ? resort : c.resort; - if ( t !== false ) { - // widgets will be reapplied - checkResort( c, t, callback ); - } else { - // don't reapply widgets is resort is false, just in case it causes - // problems with element focus - resortComplete( c, callback ); + } + } + // user only wants to sort on one column + if ( notMultiSort ) { + // flush the sort list + c.sortList = []; + c.last.sortList = []; + if ( c.sortForce !== null ) { + arry = c.sortForce; + for ( indx = 0; indx < arry.length; indx++ ) { + if ( arry[ indx ][ 0 ] !== col ) { + c.sortList.push( arry[ indx ] ); + } } } - }; - - ts.addRows = function( c, $row, resort, callback ) { - var i, j, l, rowData, cells, rows, tbdy, - // allow passing a row string if only one non-info tbody exists in the table - valid = typeof $row === 'string' && c.$tbodies.length === 1 && /<tr/.test( $row || '' ), - table = c.table; - if ( valid ) { - $row = $( $row ); - c.$tbodies.append( $row ); - } else if ( !$row || - // row is a jQuery object? - !( $row instanceof jQuery ) || - // row contained in the table? - ( $.fn.closest ? $row.closest( 'table' )[ 0 ] : $row.parents( 'table' )[ 0 ] ) !== c.table ) { - if ( c.debug ) { - console.error( 'addRows method requires (1) a jQuery selector reference to rows that have already ' + - 'been added to the table, or (2) row HTML string to be added to a table with only one tbody' ); + // add column to sort list + dir = order[ c.sortVars[ col ].count ]; + if ( dir < 2 ) { + c.sortList.push( [ col, dir ] ); + // add other columns if header spans across multiple + if ( cell.colSpan > 1 ) { + for ( indx = 1; indx < cell.colSpan; indx++ ) { + c.sortList.push( [ col + indx, dir ] ); + // update count on columns in colSpan + c.sortVars[ col + indx ].count = $.inArray( dir, order ); + } } - return false; } - table.isUpdating = true; - if ( ts.isEmptyObject( c.cache ) ) { - // empty table, do an update instead - fixes #450 - updateHeader( table ); - commonUpdate( table, resort, callback ); - } else { - rows = $row.filter( 'tr' ).attr( 'role', 'row' ).length; - tbdy = c.$tbodies.index( $row.parents( 'tbody' ).filter( ':first' ) ); - // fixes adding rows to an empty table - see issue #179 - if ( !( c.parsers && c.parsers.length ) ) { - buildParserCache( c ); + // multi column sorting + } else { + // get rid of the sortAppend before adding more - fixes issue #115 & #523 + c.sortList = $.extend( [], c.last.sortList ); + + // the user has clicked on an already sorted column + if ( ts.isValueInArray( col, c.sortList ) >= 0 ) { + // reverse the sorting direction + for ( indx = 0; indx < c.sortList.length; indx++ ) { + tmp = c.sortList[ indx ]; + if ( tmp[ 0 ] === col ) { + // order.count seems to be incorrect when compared to cell.count + tmp[ 1 ] = order[ c.sortVars[ col ].count ]; + if ( tmp[1] === 2 ) { + c.sortList.splice( indx, 1 ); + c.sortVars[ col ].count = -1; + } + } } - // add each row - for ( i = 0; i < rows; i++ ) { - l = $row[ i ].cells.length; - cells = []; - rowData = { - child: [], - $row : $row.eq( i ), - order: c.cache[ tbdy ].normalized.length - }; - // add each cell - for ( j = 0; j < l; j++ ) { - cells[ j ] = ts.getParsedText( c, $row[ i ].cells[ j ], j ); - if ( ( c.parsers[ j ].type || '' ).toLowerCase() === 'numeric' ) { - // update column max value (ignore sign) - c.cache[ tbdy ].colMax[ j ] = Math.max( Math.abs( cells[ j ] ) || 0, c.cache[ tbdy ].colMax[ j ] || 0 ); + } else { + // add column to sort list array + dir = order[ c.sortVars[ col ].count ]; + if ( dir < 2 ) { + c.sortList.push( [ col, dir ] ); + // add other columns if header spans across multiple + if ( cell.colSpan > 1 ) { + for ( indx = 1; indx < cell.colSpan; indx++ ) { + c.sortList.push( [ col + indx, dir ] ); + // update count on columns in colSpan + c.sortVars[ col + indx ].count = $.inArray( dir, order ); } } - // add the row data to the end - cells.push( rowData ); - // update cache - c.cache[ tbdy ].normalized.push( cells ); } - // resort using current settings - checkResort( c, resort, callback ); - } - }; - - ts.updateCache = function( c, callback, $tbodies ) { - // rebuild parsers - if ( !( c.parsers && c.parsers.length ) ) { - buildParserCache( c, $tbodies ); } - // rebuild the cache map - buildCache( c.table, callback, $tbodies ); - }; - - // init flag (true) used by pager plugin to prevent widget application - // renamed from appendToTable - ts.appendCache = function( c, init ) { - var n, totalRows, $bk, $tb, i, k, appendTime, - table = c.table, - wo = c.widgetOptions, - $tbodies = c.$tbodies, - rows = [], - cc = c.cache; - // empty table - fixes #206/#346 - if ( ts.isEmptyObject( cc ) ) { - // run pager appender in case the table was just emptied - return c.appender ? c.appender( table, rows ) : - table.isUpdating ? c.$table.trigger( 'updateComplete', table ) : ''; // Fixes #532 - } - if ( c.debug ) { - appendTime = new Date(); - } - for ( k = 0; k < $tbodies.length; k++ ) { - $bk = $tbodies.eq( k ); - if ( $bk.length ) { - // get tbody - $tb = ts.processTbody( table, $bk, true ); - n = cc[ k ].normalized; - totalRows = n.length; - for ( i = 0; i < totalRows; i++ ) { - rows.push( n[ i ][ c.columns ].$row ); - // removeRows used by the pager plugin; don't render if using ajax - fixes #411 - if ( !c.appender || ( c.pager && ( !c.pager.removeRows || !wo.pager_removeRows ) && !c.pager.ajax ) ) { - $tb.append( n[ i ][ c.columns ].$row ); + } + // save sort before applying sortAppend + c.last.sortList = $.extend( [], c.sortList ); + if ( c.sortList.length && c.sortAppend ) { + arry = $.isArray( c.sortAppend ) ? c.sortAppend : c.sortAppend[ c.sortList[ 0 ][ 0 ] ]; + if ( !ts.isEmptyObject( arry ) ) { + for ( indx = 0; indx < arry.length; indx++ ) { + if ( arry[ indx ][ 0 ] !== col && ts.isValueInArray( arry[ indx ][ 0 ], c.sortList ) < 0 ) { + dir = arry[ indx ][ 1 ]; + temp = ( '' + dir ).match( /^(a|d|s|o|n)/ ); + if ( temp ) { + tmp = c.sortList[ 0 ][ 1 ]; + switch ( temp[ 0 ] ) { + case 'd' : + dir = 1; + break; + case 's' : + dir = tmp; + break; + case 'o' : + dir = tmp === 0 ? 1 : 0; + break; + case 'n' : + dir = ( tmp + 1 ) % ( c.sortReset ? 3 : 2 ); + break; + default: + dir = 0; + break; + } } + c.sortList.push( [ arry[ indx ][ 0 ], dir ] ); } - // restore tbody - ts.processTbody( table, $tb, false ); } } - if ( c.appender ) { - c.appender( table, rows ); - } - if ( c.debug ) { - console.log( 'Rebuilt table' + ts.benchmark( appendTime ) ); - } - // apply table widgets; but not before ajax completes - if ( !init && !c.appender ) { ts.applyWidget( table ); } - if ( table.isUpdating ) { - c.$table.trigger( 'updateComplete', table ); - } - }; - - ts.sortOn = function( c, list, callback, init ) { - var table = c.table; - c.$table.trigger( 'sortStart', table ); - // update header count index - updateHeaderSortCount( table, list ); + } + // sortBegin event triggered immediately before the sort + c.$table.trigger( 'sortBegin', table ); + // setTimeout needed so the processing icon shows up + setTimeout( function() { // set css for headers - setHeadersCss( table ); - // fixes #346 - if ( c.delayInit && ts.isEmptyObject( c.cache ) ) { - buildCache( table ); - } - c.$table.trigger( 'sortBegin', table ); - // sort the table and append it to the dom - multisort( table ); - ts.appendCache( c, init ); + ts.setHeadersCss( c ); + ts.multisort( c ); + ts.appendCache( c ); c.$table.trigger( 'sortEnd', table ); - ts.applyWidget( table ); - if ( $.isFunction( callback ) ) { - callback( table ); - } - }; + }, 1 ); + }, - // restore headers - ts.restoreHeaders = function(table){ - var index, $cell, - c = $(table)[0].config, - $headers = c.$table.find( c.selectorHeaders ), - len = $headers.length; - // don't use c.$headers here in case header cells were swapped - for ( index = 0; index < len; index++ ) { - // c.$table.find(c.selectorHeaders).each(function(i){ - $cell = $headers.eq( index ); - // only restore header cells if it is wrapped - // because this is also used by the updateAll method - if ( $cell.find( '.' + ts.css.headerIn ).length ) { - $cell.html( c.headerContent[ index ] ); + // sort multiple columns + multisort : function( c ) { /*jshint loopfunc:true */ + var tbodyIndex, sortTime, colMax, rows, + table = c.table, + dir = 0, + textSorter = c.textSorter || '', + sortList = c.sortList, + sortLen = sortList.length, + len = c.$tbodies.length; + if ( c.serverSideSorting || ts.isEmptyObject( c.cache ) ) { + // empty table - fixes #206/#346 + return; + } + if ( c.debug ) { sortTime = new Date(); } + for ( tbodyIndex = 0; tbodyIndex < len; tbodyIndex++ ) { + colMax = c.cache[ tbodyIndex ].colMax; + rows = c.cache[ tbodyIndex ].normalized; + + rows.sort( function( a, b ) { + var sortIndex, num, col, order, sort, x, y; + // rows is undefined here in IE, so don't use it! + for ( sortIndex = 0; sortIndex < sortLen; sortIndex++ ) { + col = sortList[ sortIndex ][ 0 ]; + order = sortList[ sortIndex ][ 1 ]; + // sort direction, true = asc, false = desc + dir = order === 0; + + if ( c.sortStable && a[ col ] === b[ col ] && sortLen === 1 ) { + return a[ c.columns ].order - b[ c.columns ].order; + } + + // fallback to natural sort since it is more robust + num = /n/i.test( ts.getSortType( c.parsers, col ) ); + if ( num && c.strings[ col ] ) { + // sort strings in numerical columns + if ( typeof ( ts.string[ c.strings[ col ] ] ) === 'boolean' ) { + num = ( dir ? 1 : -1 ) * ( ts.string[ c.strings[ col ] ] ? -1 : 1 ); + } else { + num = ( c.strings[ col ] ) ? ts.string[ c.strings[ col ] ] || 0 : 0; + } + // fall back to built-in numeric sort + // var sort = $.tablesorter['sort' + s]( a[col], b[col], dir, colMax[col], table ); + sort = c.numberSorter ? c.numberSorter( a[ col ], b[ col ], dir, colMax[ col ], table ) : + ts[ 'sortNumeric' + ( dir ? 'Asc' : 'Desc' ) ]( a[ col ], b[ col ], num, colMax[ col ], col, c ); + } else { + // set a & b depending on sort direction + x = dir ? a : b; + y = dir ? b : a; + // text sort function + if ( typeof textSorter === 'function' ) { + // custom OVERALL text sorter + sort = textSorter( x[ col ], y[ col ], dir, col, table ); + } else if ( typeof textSorter === 'object' && textSorter.hasOwnProperty( col ) ) { + // custom text sorter for a SPECIFIC COLUMN + sort = textSorter[ col ]( x[ col ], y[ col ], dir, col, table ); + } else { + // fall back to natural sort + sort = ts[ 'sortNatural' + ( dir ? 'Asc' : 'Desc' ) ]( a[ col ], b[ col ], col, c ); + } + } + if ( sort ) { return sort; } } - } - }; + return a[ c.columns ].order - b[ c.columns ].order; + }); + } + if ( c.debug ) { + console.log( 'Applying sort ' + sortList.toString() + ts.benchmark( sortTime ) ); + } + }, - ts.destroy = function(table, removeClasses, callback){ - table = $(table)[0]; - if (!table.hasInitialized) { return; } - // remove all widgets - ts.removeWidget(table, true, false); - var events, - $t = $(table), - c = table.config, - debug = c.debug, - $h = $t.find('thead:first'), - $r = $h.find('tr.' + ts.css.headerRow).removeClass(ts.css.headerRow + ' ' + c.cssHeaderRow), - $f = $t.find('tfoot:first > tr').children('th, td'); - if (removeClasses === false && $.inArray('uitheme', c.widgets) >= 0) { - // reapply uitheme classes, in case we want to maintain appearance - $t.trigger('applyWidgetId', [ 'uitheme' ]); - $t.trigger('applyWidgetId', [ 'zebra' ]); - } - // remove widget added rows, just in case - $h.find('tr').not($r).remove(); - // disable tablesorter - events = 'sortReset update updateRows updateAll updateHeaders updateCell addRows updateComplete sorton ' + - 'appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress ' + - 'sortBegin sortEnd resetToLoadState '.split(' ') - .join(c.namespace + ' '); - $t - .removeData('tablesorter') - .unbind( events.replace(ts.regex.spaces, ' ') ); - c.$headers.add($f) - .removeClass( [ ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone ].join(' ') ) - .removeAttr('data-column') - .removeAttr('aria-label') - .attr('aria-disabled', 'true'); - $r.find(c.selectorSort).unbind( ('mousedown mouseup keypress '.split(' ').join(c.namespace + ' ')).replace(ts.regex.spaces, ' ') ); - ts.restoreHeaders(table); - $t.toggleClass(ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme, removeClasses === false); - // clear flag in case the plugin is initialized again - table.hasInitialized = false; - delete table.config.cache; - if (typeof callback === 'function') { - callback(table); - } - if (debug) { - console.log( 'tablesorter has been removed' ); - } - }; + resortComplete : function( c, callback ) { + if ( c.table.isUpdating ) { + c.$table.trigger( 'updateComplete', c.table ); + } + if ( $.isFunction( callback ) ) { + callback( c.table ); + } + }, - // *** sort functions *** - // regex used in natural sort - ts.regex.chunk = /(^([+\-]?(?:\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi; // chunk/tokenize numbers & letters - ts.regex.chunks = /(^\\0|\\0$)/; // replace chunks @ ends - ts.regex.hex = /^0x[0-9a-f]+$/i; // hex - - // Natural sort - https://github.com/overset/javascript-natural-sort (date sorting removed) - // this function will only accept strings, or you'll see 'TypeError: undefined is not a function' - // I could add a = a.toString(); b = b.toString(); but it'll slow down the sort overall - ts.sortNatural = function(a, b) { - if (a === b) { return 0; } - var xN, xD, yN, yD, xF, yF, i, mx, - r = ts.regex; - // first try and sort Hex codes - if (r.hex.test(b)) { - xD = parseInt(a.match(r.hex), 16); - yD = parseInt(b.match(r.hex), 16); - if ( xD < yD ) { return -1; } - if ( xD > yD ) { return 1; } - } - // chunk/tokenize - xN = a.replace(r.chunk, '\\0$1\\0').replace(r.chunks, '').split('\\0'); - yN = b.replace(r.chunk, '\\0$1\\0').replace(r.chunks, '').split('\\0'); - mx = Math.max(xN.length, yN.length); - // natural sorting through split numeric strings and default strings - for (i = 0; i < mx; i++) { - // find floats not starting with '0', string or 0 if not defined - xF = isNaN(xN[i]) ? xN[i] || 0 : parseFloat(xN[i]) || 0; - yF = isNaN(yN[i]) ? yN[i] || 0 : parseFloat(yN[i]) || 0; - // handle numeric vs string comparison - number < string - (Kyle Adams) - if (isNaN(xF) !== isNaN(yF)) { return (isNaN(xF)) ? 1 : -1; } - // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2' - if (typeof xF !== typeof yF) { - xF += ''; - yF += ''; - } - if (xF < yF) { return -1; } - if (xF > yF) { return 1; } + checkResort : function( c, resort, callback ) { + var sortList = $.isArray( resort ) ? resort : c.sortList, + // if no resort parameter is passed, fallback to config.resort (true by default) + resrt = typeof resort === 'undefined' ? c.resort : resort; + // don't try to resort if the table is still processing + // this will catch spamming of the updateCell method + if ( resrt !== false && !c.serverSideSorting && !c.table.isProcessing ) { + if ( sortList.length ) { + ts.sortOn( c, sortList, function() { + ts.resortComplete( c, callback ); + }, true ); + } else { + ts.sortReset( c, function() { + ts.resortComplete( c, callback ); + ts.applyWidget( c.table, false ); + } ); } - return 0; - }; + } else { + ts.resortComplete( c, callback ); + ts.applyWidget( c.table, false ); + } + }, - ts.sortNaturalAsc = function(a, b, col, table, c) { - if (a === b) { return 0; } - var e = c.string[ (c.empties[col] || c.emptyTo ) ]; - if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : -e || -1; } - if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : e || 1; } - return ts.sortNatural(a, b); - }; + sortOn : function( c, list, callback, init ) { + var table = c.table; + c.$table.trigger( 'sortStart', table ); + // update header count index + ts.updateHeaderSortCount( c, list ); + // set css for headers + ts.setHeadersCss( c ); + // fixes #346 + if ( c.delayInit && ts.isEmptyObject( c.cache ) ) { + ts.buildCache( c ); + } + c.$table.trigger( 'sortBegin', table ); + // sort the table and append it to the dom + ts.multisort( c ); + ts.appendCache( c, init ); + c.$table.trigger( 'sortEnd', table ); + ts.applyWidget( table ); + if ( $.isFunction( callback ) ) { + callback( table ); + } + }, - ts.sortNaturalDesc = function(a, b, col, table, c) { - if (a === b) { return 0; } - var e = c.string[ (c.empties[col] || c.emptyTo ) ]; - if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : e || 1; } - if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : -e || -1; } - return ts.sortNatural(b, a); - }; + sortReset : function( c, callback ) { + c.sortList = []; + ts.setHeadersCss( c ); + ts.multisort( c ); + ts.appendCache( c ); + if ( $.isFunction( callback ) ) { + callback( c.table ); + } + }, - // basic alphabetical sort - ts.sortText = function(a, b) { - return a > b ? 1 : (a < b ? -1 : 0); - }; + getSortType : function( parsers, column ) { + return ( parsers && parsers[ column ] ) ? parsers[ column ].type || '' : ''; + }, - // return text string value by adding up ascii value - // so the text is somewhat sorted when using a digital sort - // this is NOT an alphanumeric sort - ts.getTextValue = function(a, num, mx) { - if (mx) { - // make sure the text value is greater than the max numerical value (mx) - var i, l = a ? a.length : 0, n = mx + num; - for (i = 0; i < l; i++) { - n += a.charCodeAt(i); - } - return num * n; - } - return 0; - }; + formatSortingOrder : function( val ) { + // look for 'd' in 'desc' order; return true + return ( /^d/i.test( val ) || val === 1 ); + }, - ts.sortNumericAsc = function(a, b, num, mx, col, table) { - if (a === b) { return 0; } - var c = table.config, - e = c.string[ (c.empties[col] || c.emptyTo ) ]; - if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : -e || -1; } - if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : e || 1; } - if (isNaN(a)) { a = ts.getTextValue(a, num, mx); } - if (isNaN(b)) { b = ts.getTextValue(b, num, mx); } - return a - b; - }; + // Natural sort - https://github.com/overset/javascript-natural-sort (date sorting removed) + // this function will only accept strings, or you'll see 'TypeError: undefined is not a function' + // I could add a = a.toString(); b = b.toString(); but it'll slow down the sort overall + sortNatural : function( a, b ) { + if ( a === b ) { return 0; } + var aNum, bNum, aFloat, bFloat, indx, max, + regex = ts.regex; + // first try and sort Hex codes + if ( regex.hex.test( b ) ) { + aNum = parseInt( a.match( regex.hex ), 16 ); + bNum = parseInt( b.match( regex.hex ), 16 ); + if ( aNum < bNum ) { return -1; } + if ( aNum > bNum ) { return 1; } + } + // chunk/tokenize + aNum = a.replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); + bNum = b.replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); + max = Math.max( aNum.length, bNum.length ); + // natural sorting through split numeric strings and default strings + for ( indx = 0; indx < max; indx++ ) { + // find floats not starting with '0', string or 0 if not defined + aFloat = isNaN( aNum[ indx ] ) ? aNum[ indx ] || 0 : parseFloat( aNum[ indx ] ) || 0; + bFloat = isNaN( bNum[ indx ] ) ? bNum[ indx ] || 0 : parseFloat( bNum[ indx ] ) || 0; + // handle numeric vs string comparison - number < string - (Kyle Adams) + if ( isNaN( aFloat ) !== isNaN( bFloat ) ) { return isNaN( aFloat ) ? 1 : -1; } + // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2' + if ( typeof aFloat !== typeof bFloat ) { + aFloat += ''; + bFloat += ''; + } + if ( aFloat < bFloat ) { return -1; } + if ( aFloat > bFloat ) { return 1; } + } + return 0; + }, - ts.sortNumericDesc = function(a, b, num, mx, col, table) { - if (a === b) { return 0; } - var c = table.config, - e = c.string[ (c.empties[col] || c.emptyTo ) ]; - if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : e || 1; } - if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : -e || -1; } - if (isNaN(a)) { a = ts.getTextValue(a, num, mx); } - if (isNaN(b)) { b = ts.getTextValue(b, num, mx); } - return b - a; - }; + sortNaturalAsc : function( a, b, col, c ) { + if ( a === b ) { return 0; } + var empty = ts.string[ ( c.empties[ col ] || c.emptyTo ) ]; + if ( a === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? -1 : 1 ) : -empty || -1; } + if ( b === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? 1 : -1 ) : empty || 1; } + return ts.sortNatural( a, b ); + }, - ts.sortNumeric = function(a, b) { - return a - b; - }; + sortNaturalDesc : function( a, b, col, c ) { + if ( a === b ) { return 0; } + var empty = ts.string[ ( c.empties[ col ] || c.emptyTo ) ]; + if ( a === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? -1 : 1 ) : empty || 1; } + if ( b === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? 1 : -1 ) : -empty || -1; } + return ts.sortNatural( b, a ); + }, - // used when replacing accented characters during sorting - ts.characterEquivalents = { - 'a' : '\u00e1\u00e0\u00e2\u00e3\u00e4\u0105\u00e5', // áàâãäąå - 'A' : '\u00c1\u00c0\u00c2\u00c3\u00c4\u0104\u00c5', // ÁÀÂÃÄĄÅ - 'c' : '\u00e7\u0107\u010d', // çćč - 'C' : '\u00c7\u0106\u010c', // ÇĆČ - 'e' : '\u00e9\u00e8\u00ea\u00eb\u011b\u0119', // éèêëěę - 'E' : '\u00c9\u00c8\u00ca\u00cb\u011a\u0118', // ÉÈÊËĚĘ - 'i' : '\u00ed\u00ec\u0130\u00ee\u00ef\u0131', // íìİîïı - 'I' : '\u00cd\u00cc\u0130\u00ce\u00cf', // ÍÌİÎÏ - 'o' : '\u00f3\u00f2\u00f4\u00f5\u00f6\u014d', // óòôõöō - 'O' : '\u00d3\u00d2\u00d4\u00d5\u00d6\u014c', // ÓÒÔÕÖŌ - 'ss': '\u00df', // ß (s sharp) - 'SS': '\u1e9e', // ẞ (Capital sharp s) - 'u' : '\u00fa\u00f9\u00fb\u00fc\u016f', // úùûüů - 'U' : '\u00da\u00d9\u00db\u00dc\u016e' // ÚÙÛÜŮ - }; - ts.replaceAccents = function(s) { - var a, acc = '[', eq = ts.characterEquivalents; - if (!ts.characterRegex) { - ts.characterRegexArray = {}; - for (a in eq) { - if (typeof a === 'string') { - acc += eq[a]; - ts.characterRegexArray[a] = new RegExp('[' + eq[a] + ']', 'g'); - } - } - ts.characterRegex = new RegExp(acc + ']'); - } - if (ts.characterRegex.test(s)) { - for (a in eq) { - if (typeof a === 'string') { - s = s.replace( ts.characterRegexArray[a], a ); - } - } - } - return s; - }; + // basic alphabetical sort + sortText : function( a, b ) { + return a > b ? 1 : ( a < b ? -1 : 0 ); + }, - // *** utilities *** - ts.isValueInArray = function( column, arry ) { + // return text string value by adding up ascii value + // so the text is somewhat sorted when using a digital sort + // this is NOT an alphanumeric sort + getTextValue : function( val, num, max ) { + if ( max ) { + // make sure the text value is greater than the max numerical value (max) var indx, - len = arry && arry.length || 0; + len = val ? val.length : 0, + n = max + num; for ( indx = 0; indx < len; indx++ ) { - if ( arry[ indx ][ 0 ] === column ) { - return indx; - } + n += val.charCodeAt( indx ); } - return -1; - }; + return num * n; + } + return 0; + }, - ts.addParser = function(parser) { - var i, l = ts.parsers.length, a = true; - for (i = 0; i < l; i++) { - if (ts.parsers[i].id.toLowerCase() === parser.id.toLowerCase()) { - a = false; - } - } - if (a) { - ts.parsers.push(parser); - } - }; + sortNumericAsc : function( a, b, num, max, col, c ) { + if ( a === b ) { return 0; } + var empty = ts.string[ ( c.empties[ col ] || c.emptyTo ) ]; + if ( a === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? -1 : 1 ) : -empty || -1; } + if ( b === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? 1 : -1 ) : empty || 1; } + if ( isNaN( a ) ) { a = ts.getTextValue( a, num, max ); } + if ( isNaN( b ) ) { b = ts.getTextValue( b, num, max ); } + return a - b; + }, - // Use it to add a set of methods to table.config which will be available for all tables. - // This should be done before table initialization - ts.addInstanceMethods = function(methods) { - $.extend(ts.instanceMethods, methods); - }; + sortNumericDesc : function( a, b, num, max, col, c ) { + if ( a === b ) { return 0; } + var empty = ts.string[ ( c.empties[ col ] || c.emptyTo ) ]; + if ( a === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? -1 : 1 ) : empty || 1; } + if ( b === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? 1 : -1 ) : -empty || -1; } + if ( isNaN( a ) ) { a = ts.getTextValue( a, num, max ); } + if ( isNaN( b ) ) { b = ts.getTextValue( b, num, max ); } + return b - a; + }, - ts.getParserById = function(name) { - /*jshint eqeqeq:false */ - if (name == 'false') { return false; } - var i, l = ts.parsers.length; - for (i = 0; i < l; i++) { - if (ts.parsers[i].id.toLowerCase() === (name.toString()).toLowerCase()) { - return ts.parsers[i]; - } - } - return false; - }; + sortNumeric : function( a, b ) { + return a - b; + }, - ts.addWidget = function(widget) { - ts.widgets.push(widget); - }; + /* + ██ ██ ██ ██ █████▄ ▄████▄ ██████ ██████ ▄█████ + ██ ██ ██ ██ ██ ██ ██ ▄▄▄ ██▄▄ ██ ▀█▄ + ██ ██ ██ ██ ██ ██ ██ ▀██ ██▀▀ ██ ▀█▄ + ███████▀ ██ █████▀ ▀████▀ ██████ ██ █████▀ + */ + addWidget : function( widget ) { + ts.widgets.push( widget ); + }, - ts.hasWidget = function( $table, name ) { - $table = $( $table ); - return $table.length && $table[0].config && $table[0].config.widgetInit[name] || false; - }; + hasWidget : function( $table, name ) { + $table = $( $table ); + return $table.length && $table[ 0 ].config && $table[ 0 ].config.widgetInit[ name ] || false; + }, - ts.getWidgetById = function(name) { - var i, w, l = ts.widgets.length; - for (i = 0; i < l; i++) { - w = ts.widgets[i]; - if (w && w.hasOwnProperty('id') && w.id.toLowerCase() === name.toLowerCase()) { - return w; - } + getWidgetById : function( name ) { + var indx, widget, + len = ts.widgets.length; + for ( indx = 0; indx < len; indx++ ) { + widget = ts.widgets[ indx ]; + if ( widget && widget.id && widget.id.toLowerCase() === name.toLowerCase() ) { + return widget; } - }; + } + }, - ts.applyWidgetOptions = function( table, c ){ - var indx, widget, - len = c.widgets.length; - if (len) { - for (indx = 0; indx < len; indx++) { - widget = ts.getWidgetById( c.widgets[indx] ); - if ( widget && 'options' in widget ) { - c.widgetOptions = $.extend( true, {}, widget.options, c.widgetOptions ); - } + applyWidgetOptions : function( table ) { + var indx, widget, + c = table.config, + len = c.widgets.length; + if ( len ) { + for ( indx = 0; indx < len; indx++ ) { + widget = ts.getWidgetById( c.widgets[ indx ] ); + if ( widget && widget.options ) { + c.widgetOptions = $.extend( true, {}, widget.options, c.widgetOptions ); } } - }; + } + }, - ts.applyWidget = function(table, init, callback) { - table = $(table)[0]; // in case this is called externally - var indx, len, names, widget, name, applied, - c = table.config, - tableClass = ' ' + c.table.className + ' ', - widgets = [], - time, time2, w, wd; - // prevent numerous consecutive widget applications - if (init !== false && table.hasInitialized && (table.isApplyingWidgets || table.isUpdating)) { return; } - if (c.debug) { time = new Date(); } - // look for widgets to apply from in table class + addWidgetFromClass : function( table ) { + var len, indx, + c = table.config, + // look for widgets to apply from table class // stop using \b otherwise this matches 'ui-widget-content' & adds 'content' widget - wd = new RegExp( '\\s' + c.widgetClass.replace( ts.regex.templateName, '([\\w-]+)' ) + '\\s', 'g' ); - if ( tableClass.match( wd ) ) { - // extract out the widget id from the table class (widget id's can include dashes) - w = tableClass.match( wd ); - if ( w ) { - len = w.length; - for (indx = 0; indx < len; indx++) { - c.widgets.push( w[indx].replace( wd, '$1' ) ); - } - } + regex = '\\s' + c.widgetClass.replace( ts.regex.templateName, '([\\w-]+)' ) + '\\s', + widgetClass = new RegExp( regex, 'g' ), + // extract out the widget id from the table class (widget id's can include dashes) + widget = ( ' ' + c.table.className + ' ' ).match( widgetClass ); + if ( widget ) { + len = widget.length; + for ( indx = 0; indx < len; indx++ ) { + c.widgets.push( widget[ indx ].replace( widgetClass, '$1' ) ); } - if (c.widgets.length) { - table.isApplyingWidgets = true; - // ensure unique widget ids - c.widgets = $.grep(c.widgets, function(v, k){ - return $.inArray(v, c.widgets) === k; - }); - names = c.widgets || []; - len = names.length; - // build widget array & add priority as needed - for (indx = 0; indx < len; indx++) { - wd = ts.getWidgetById(names[indx]); - if (wd && wd.id) { - // set priority to 10 if not defined - if (!wd.priority) { wd.priority = 10; } - widgets[indx] = wd; - } - } - // sort widgets by priority - widgets.sort(function(a, b){ - return a.priority < b.priority ? -1 : a.priority === b.priority ? 0 : 1; - }); - // add/update selected widgets - len = widgets.length; - if (c.debug) { - console[ console.group ? 'group' : 'log' ]( 'Start ' + ( init ? 'initializing' : 'applying' ) + ' widgets' ); + } + }, + + applyWidget : function( table, init, callback ) { + table = $( table )[ 0 ]; // in case this is called externally + var indx, len, names, widget, name, applied, time, time2, + c = table.config, + widgets = []; + // prevent numerous consecutive widget applications + if ( init !== false && table.hasInitialized && ( table.isApplyingWidgets || table.isUpdating ) ) { + return; + } + if ( c.debug ) { time = new Date(); } + ts.addWidgetFromClass( table ); + if ( c.widgets.length ) { + table.isApplyingWidgets = true; + // ensure unique widget ids + c.widgets = $.grep( c.widgets, function( val, index ) { + return $.inArray( val, c.widgets ) === index; + }); + names = c.widgets || []; + len = names.length; + // build widget array & add priority as needed + for ( indx = 0; indx < len; indx++ ) { + widget = ts.getWidgetById( names[ indx ] ); + if ( widget && widget.id ) { + // set priority to 10 if not defined + if ( !widget.priority ) { widget.priority = 10; } + widgets[ indx ] = widget; } - for (indx = 0; indx < len; indx++) { - widget = widgets[indx]; - if (widget) { - name = widget.id; - applied = false; - if (c.debug) { time2 = new Date(); } - - if ( init || !( c.widgetInit[ name ] ) ) { - // set init flag first to prevent calling init more than once (e.g. pager) - c.widgetInit[ name ] = true; - if (table.hasInitialized) { - // don't reapply widget options on tablesorter init - ts.applyWidgetOptions( table, table.config ); - } - if ( 'init' in widget ) { - applied = true; - if (c.debug) { - console[ console.group ? 'group' : 'log' ]( 'Initializing ' + name + ' widget' ); - } - widget.init(table, widget, table.config, table.config.widgetOptions); - } + } + // sort widgets by priority + widgets.sort( function( a, b ) { + return a.priority < b.priority ? -1 : a.priority === b.priority ? 0 : 1; + }); + // add/update selected widgets + len = widgets.length; + if ( c.debug ) { + console[ console.group ? 'group' : 'log' ]( 'Start ' + ( init ? 'initializing' : 'applying' ) + ' widgets' ); + } + for ( indx = 0; indx < len; indx++ ) { + widget = widgets[ indx ]; + if ( widget ) { + name = widget.id; + applied = false; + if ( c.debug ) { time2 = new Date(); } + + if ( init || !( c.widgetInit[ name ] ) ) { + // set init flag first to prevent calling init more than once (e.g. pager) + c.widgetInit[ name ] = true; + if ( table.hasInitialized ) { + // don't reapply widget options on tablesorter init + ts.applyWidgetOptions( table ); } - if ( !init && 'format' in widget ) { + if ( typeof widget.init === 'function' ) { applied = true; - if (c.debug) { - console[ console.group ? 'group' : 'log' ]( 'Updating ' + name + ' widget' ); + if ( c.debug ) { + console[ console.group ? 'group' : 'log' ]( 'Initializing ' + name + ' widget' ); } - widget.format(table, table.config, table.config.widgetOptions, false); + widget.init( table, widget, table.config, table.config.widgetOptions ); } - if (c.debug) { - if (applied) { - console.log( 'Completed ' + ( init ? 'initializing ' : 'applying ' ) + name + ' widget' + ts.benchmark( time2 ) ); - if ( console.groupEnd ) { console.groupEnd(); } - } + } + if ( !init && typeof widget.format === 'function' ) { + applied = true; + if ( c.debug ) { + console[ console.group ? 'group' : 'log' ]( 'Updating ' + name + ' widget' ); + } + widget.format( table, table.config, table.config.widgetOptions, false ); + } + if ( c.debug ) { + if ( applied ) { + console.log( 'Completed ' + ( init ? 'initializing ' : 'applying ' ) + name + ' widget' + ts.benchmark( time2 ) ); + if ( console.groupEnd ) { console.groupEnd(); } } } } - if ( c.debug && console.groupEnd ) { console.groupEnd(); } - // callback executed on init only - if (!init && typeof callback === 'function') { - callback(table); + } + if ( c.debug && console.groupEnd ) { console.groupEnd(); } + // callback executed on init only + if ( !init && typeof callback === 'function' ) { + callback( table ); + } + } + setTimeout( function() { + table.isApplyingWidgets = false; + $.data( table, 'lastWidgetApplication', new Date() ); + c.$table.trigger('tablesorter-ready'); + }, 0 ); + if ( c.debug ) { + widget = c.widgets.length; + console.log( 'Completed ' + + ( init === true ? 'initializing ' : 'applying ' ) + widget + + ' widget' + ( widget !== 1 ? 's' : '' ) + ts.benchmark( time ) ); + } + }, + + removeWidget : function( table, name, refreshing ) { + table = $( table )[ 0 ]; + var index, widget, indx, len, + c = table.config; + // if name === true, add all widgets from $.tablesorter.widgets + if ( name === true ) { + name = []; + len = ts.widgets.length; + for ( indx = 0; indx < len; indx++ ) { + widget = ts.widgets[ indx ]; + if ( widget && widget.id ) { + name.push( widget.id ); + } + } + } else { + // name can be either an array of widgets names, + // or a space/comma separated list of widget names + name = ( $.isArray( name ) ? name.join( ',' ) : name || '' ).toLowerCase().split( /[\s,]+/ ); + } + len = name.length; + for ( index = 0; index < len; index++ ) { + widget = ts.getWidgetById( name[ index ] ); + indx = $.inArray( name[ index ], c.widgets ); + if ( widget && widget.remove ) { + if ( c.debug ) { + console.log( ( refreshing ? 'Refreshing' : 'Removing' ) + ' "' + name[ index ] + '" widget' ); } + widget.remove( table, c, c.widgetOptions, refreshing ); + c.widgetInit[ name[ index ] ] = false; } - setTimeout(function(){ - table.isApplyingWidgets = false; - $.data(table, 'lastWidgetApplication', new Date()); - }, 0); - if (c.debug) { - w = c.widgets.length; - console.log( 'Completed ' + (init === true ? 'initializing ' : 'applying ') + w + ' widget' + (w !== 1 ? 's' : '') + ts.benchmark(time) ); + // don't remove the widget from config.widget if refreshing + if ( indx >= 0 && refreshing !== true ) { + c.widgets.splice( indx, 1 ); } - }; + } + }, - ts.removeWidget = function(table, name, refreshing){ - table = $(table)[0]; - var i, widget, indx, len, - c = table.config; - // if name === true, add all widgets from $.tablesorter.widgets - if (name === true) { - name = []; - len = ts.widgets.length; - for (indx = 0; indx < len; indx++) { - widget = ts.widgets[indx]; - if (widget && widget.id) { - name.push( widget.id ); - } + refreshWidgets : function( table, doAll, dontapply ) { + table = $( table )[ 0 ]; // see issue #243 + var indx, widget, + c = table.config, + curWidgets = c.widgets, + widgets = ts.widgets, + len = widgets.length, + list = [], + callback = function( table ) { + $( table ).trigger( 'refreshComplete' ); + }; + // remove widgets not defined in config.widgets, unless doAll is true + for ( indx = 0; indx < len; indx++ ) { + widget = widgets[ indx ]; + if ( widget && widget.id && ( doAll || $.inArray( widget.id, curWidgets ) < 0 ) ) { + list.push( widget.id ); + } + } + ts.removeWidget( table, list.join( ',' ), true ); + if ( dontapply !== true ) { + // call widget init if + ts.applyWidget( table, doAll || false, callback ); + if ( doAll ) { + // apply widget format + ts.applyWidget( table, false, callback ); + } + } else { + callback( table ); + } + }, + + /* + ██ ██ ██████ ██ ██ ██ ██████ ██ ██████ ▄█████ + ██ ██ ██ ██ ██ ██ ██ ██ ██▄▄ ▀█▄ + ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀█▄ + ▀████▀ ██ ██ ██████ ██ ██ ██ ██████ █████▀ + */ + benchmark : function( diff ) { + return ( ' ( ' + ( new Date().getTime() - diff.getTime() ) + 'ms )' ); + }, + // deprecated ts.log + log : function() { + console.log( arguments ); + }, + + // $.isEmptyObject from jQuery v1.4 + isEmptyObject : function( obj ) { + /*jshint forin: false */ + for ( var name in obj ) { + return false; + } + return true; + }, + + isValueInArray : function( column, arry ) { + var indx, + len = arry && arry.length || 0; + for ( indx = 0; indx < len; indx++ ) { + if ( arry[ indx ][ 0 ] === column ) { + return indx; + } + } + return -1; + }, + + formatFloat : function( str, table ) { + if ( typeof str !== 'string' || str === '' ) { return str; } + // allow using formatFloat without a table; defaults to US number format + var num, + usFormat = table && table.config ? table.config.usNumberFormat !== false : + typeof table !== 'undefined' ? table : true; + if ( usFormat ) { + // US Format - 1,234,567.89 -> 1234567.89 + str = str.replace( ts.regex.comma, '' ); + } else { + // German Format = 1.234.567,89 -> 1234567.89 + // French Format = 1 234 567,89 -> 1234567.89 + str = str.replace( ts.regex.digitNonUS, '' ).replace( ts.regex.comma, '.' ); + } + if ( ts.regex.digitNegativeTest.test( str ) ) { + // make (#) into a negative number -> (10) = -10 + str = str.replace( ts.regex.digitNegativeReplace, '-$1' ); + } + num = parseFloat( str ); + // return the text instead of zero + return isNaN( num ) ? $.trim( str ) : num; + }, + + isDigit : function( str ) { + // replace all unwanted chars and match + return isNaN( str ) ? + ts.regex.digitTest.test( str.toString().replace( ts.regex.digitReplace, '' ) ) : + str !== ''; + }, + + // computeTableHeaderCellIndexes from: + // http://www.javascripttoolbox.com/lib/table/examples.php + // http://www.javascripttoolbox.com/temp/table_cellindex.html + computeColumnIndex : function( $rows ) { + var i, j, k, l, $cell, cell, cells, rowIndex, cellId, rowSpan, colSpan, firstAvailCol, + matrix = [], + matrixrow = []; + for ( i = 0; i < $rows.length; i++ ) { + cells = $rows[ i ].cells; + for ( j = 0; j < cells.length; j++ ) { + cell = cells[ j ]; + $cell = $( cell ); + rowIndex = cell.parentNode.rowIndex; + cellId = rowIndex + '-' + $cell.index(); + rowSpan = cell.rowSpan || 1; + colSpan = cell.colSpan || 1; + if ( typeof matrix[ rowIndex ] === 'undefined' ) { + matrix[ rowIndex ] = []; } - } else { - // name can be either an array of widgets names, - // or a space/comma separated list of widget names - name = ( $.isArray(name) ? name.join(',') : name || '' ).toLowerCase().split( /[\s,]+/ ); - } - len = name.length; - for (i = 0; i < len; i++) { - widget = ts.getWidgetById(name[i]); - indx = $.inArray( name[i], c.widgets ); - if ( widget && 'remove' in widget ) { - if (c.debug && indx >= 0) { console.log( 'Removing "' + name[i] + '" widget' ); } - if ( c.debug ) { - console.log( ( refreshing ? 'Refreshing' : 'Removing' ) + ' "' + name[i] + '" widget' ); + // Find first available column in the first row + for ( k = 0; k < matrix[ rowIndex ].length + 1; k++ ) { + if ( typeof matrix[ rowIndex ][ k ] === 'undefined' ) { + firstAvailCol = k; + break; } - widget.remove(table, c, c.widgetOptions, refreshing); - c.widgetInit[ name[i] ] = false; } - // don't remove the widget from config.widget if refreshing - if (indx >= 0 && refreshing !== true) { - c.widgets.splice( indx, 1 ); + // add data-column (setAttribute = IE8+) + if ( cell.setAttribute ) { + cell.setAttribute( 'data-column', firstAvailCol ); + } else { + $cell.attr( 'data-column', firstAvailCol ); + } + for ( k = rowIndex; k < rowIndex + rowSpan; k++ ) { + if ( typeof matrix[ k ] === 'undefined' ) { + matrix[ k ] = []; + } + matrixrow = matrix[ k ]; + for ( l = firstAvailCol; l < firstAvailCol + colSpan; l++ ) { + matrixrow[ l ] = 'x'; + } } } - }; + } + return matrixrow.length; + }, - ts.refreshWidgets = function(table, doAll, dontapply) { - table = $(table)[0]; // see issue #243 - var indx, - c = table.config, - cw = c.widgets, - widgets = ts.widgets, - len = widgets.length, - list = [], - callback = function(table){ - $(table).trigger('refreshComplete'); - }; - // remove widgets not defined in config.widgets, unless doAll is true - for (indx = 0; indx < len; indx++) { - if (widgets[indx] && widgets[indx].id && (doAll || $.inArray( widgets[indx].id, cw ) < 0)) { - list.push( widgets[indx].id ); - } + // automatically add a colgroup with col elements set to a percentage width + fixColumnWidth : function( table ) { + table = $( table )[ 0 ]; + var overallWidth, percent, $tbodies, len, index, + c = table.config, + $colgroup = c.$table.children( 'colgroup' ); + // remove plugin-added colgroup, in case we need to refresh the widths + if ( $colgroup.length && $colgroup.hasClass( ts.css.colgroup ) ) { + $colgroup.remove(); + } + if ( c.widthFixed && c.$table.children( 'colgroup' ).length === 0 ) { + $colgroup = $( '<colgroup class="' + ts.css.colgroup + '">' ); + overallWidth = c.$table.width(); + // only add col for visible columns - fixes #371 + $tbodies = c.$tbodies.find( 'tr:first' ).children( ':visible' ); + len = $tbodies.length; + for ( index = 0; index < len; index++ ) { + percent = parseInt( ( $tbodies.eq( index ).width() / overallWidth ) * 1000, 10 ) / 10 + '%'; + $colgroup.append( $( '<col>' ).css( 'width', percent ) ); } - ts.removeWidget( table, list.join(','), true ); - if (dontapply !== true) { - // call widget init if - ts.applyWidget(table, doAll || false, callback ); - if (doAll) { - // apply widget format - ts.applyWidget(table, false, callback); + c.$table.prepend( $colgroup ); + } + }, + + // get sorter, string, empty, etc options for each column from + // jQuery data, metadata, header option or header class name ('sorter-false') + // priority = jQuery data > meta > headers option > header class name + getData : function( header, configHeader, key ) { + var meta, cl4ss, + val = '', + $header = $( header ); + if ( !$header.length ) { return ''; } + meta = $.metadata ? $header.metadata() : false; + cl4ss = ' ' + ( $header.attr( 'class' ) || '' ); + if ( typeof $header.data( key ) !== 'undefined' || + typeof $header.data( key.toLowerCase() ) !== 'undefined' ) { + // 'data-lockedOrder' is assigned to 'lockedorder'; but 'data-locked-order' is assigned to 'lockedOrder' + // 'data-sort-initial-order' is assigned to 'sortInitialOrder' + val += $header.data( key ) || $header.data( key.toLowerCase() ); + } else if ( meta && typeof meta[ key ] !== 'undefined' ) { + val += meta[ key ]; + } else if ( configHeader && typeof configHeader[ key ] !== 'undefined' ) { + val += configHeader[ key ]; + } else if ( cl4ss !== ' ' && cl4ss.match( ' ' + key + '-' ) ) { + // include sorter class name 'sorter-text', etc; now works with 'sorter-my-custom-parser' + val = cl4ss.match( new RegExp( '\\s' + key + '-([\\w-]+)' ) )[ 1 ] || ''; + } + return $.trim( val ); + }, + + getColumnData : function( table, obj, indx, getCell, $headers ) { + if ( typeof obj === 'undefined' || obj === null ) { return; } + table = $( table )[ 0 ]; + var $header, key, + c = table.config, + $cells = ( $headers || c.$headers ), + // c.$headerIndexed is not defined initially + $cell = c.$headerIndexed && c.$headerIndexed[ indx ] || + $cells.filter( '[data-column="' + indx + '"]:last' ); + if ( obj[ indx ] ) { + return getCell ? obj[ indx ] : obj[ $cells.index( $cell ) ]; + } + for ( key in obj ) { + if ( typeof key === 'string' ) { + $header = $cell + // header cell with class/id + .filter( key ) + // find elements within the header cell with cell/id + .add( $cell.find( key ) ); + if ( $header.length ) { + return obj[ key ]; } - } else { - callback(table); } - }; + } + return; + }, - ts.getColumnText = function( table, column, callback ) { - table = $( table )[0]; - var tbodyIndex, rowIndex, cache, row, tbodyLen, rowLen, raw, parsed, $cell, result, - hasCallback = typeof callback === 'function', - allColumns = column === 'all', - data = { raw : [], parsed: [], $cell: [] }, - c = table.config; - if ( ts.isEmptyObject( c ) ) { - if ( c.debug ) { - console.warn( 'No cache found - aborting getColumnText function!' ); - } - } else { - tbodyLen = c.$tbodies.length; - for ( tbodyIndex = 0; tbodyIndex < tbodyLen; tbodyIndex++ ) { - cache = c.cache[ tbodyIndex ].normalized; - rowLen = cache.length; - for ( rowIndex = 0; rowIndex < rowLen; rowIndex++ ) { - result = true; - row = cache[ rowIndex ]; - parsed = ( allColumns ) ? row.slice(0, c.columns) : row[ column ]; - row = row[ c.columns ]; - raw = ( allColumns ) ? row.raw : row.raw[ column ]; - $cell = ( allColumns ) ? row.$row.children() : row.$row.children().eq( column ); - if ( hasCallback ) { - result = callback({ - tbodyIndex: tbodyIndex, - rowIndex: rowIndex, - parsed: parsed, - raw: raw, - $row: row.$row, - $cell: $cell - }); - } - if ( result !== false ) { - data.parsed.push( parsed ); - data.raw.push( raw ); - data.$cell.push( $cell ); - } - } - } - // return everything - return data; + // *** Process table *** + // add processing indicator + isProcessing : function( $table, toggle, $ths ) { + $table = $( $table ); + var c = $table[ 0 ].config, + // default to all headers + $header = $ths || $table.find( '.' + ts.css.header ); + if ( toggle ) { + // don't use sortList if custom $ths used + if ( typeof $ths !== 'undefined' && c.sortList.length > 0 ) { + // get headers from the sortList + $header = $header.filter( function() { + // get data-column from attr to keep compatibility with jQuery 1.2.6 + return this.sortDisabled ? + false : + ts.isValueInArray( parseFloat( $( this ).attr( 'data-column' ) ), c.sortList ) >= 0; + }); } - }; + $table.add( $header ).addClass( ts.css.processing + ' ' + c.cssProcessing ); + } else { + $table.add( $header ).removeClass( ts.css.processing + ' ' + c.cssProcessing ); + } + }, - // get sorter, string, empty, etc options for each column from - // jQuery data, metadata, header option or header class name ('sorter-false') - // priority = jQuery data > meta > headers option > header class name - ts.getData = function(h, ch, key) { - var val = '', $h = $(h), m, cl; - if (!$h.length) { return ''; } - m = $.metadata ? $h.metadata() : false; - cl = ' ' + ($h.attr('class') || ''); - if (typeof $h.data(key) !== 'undefined' || typeof $h.data(key.toLowerCase()) !== 'undefined'){ - // 'data-lockedOrder' is assigned to 'lockedorder'; but 'data-locked-order' is assigned to 'lockedOrder' - // 'data-sort-initial-order' is assigned to 'sortInitialOrder' - val += $h.data(key) || $h.data(key.toLowerCase()); - } else if (m && typeof m[key] !== 'undefined') { - val += m[key]; - } else if (ch && typeof ch[key] !== 'undefined') { - val += ch[key]; - } else if (cl !== ' ' && cl.match(' ' + key + '-')) { - // include sorter class name 'sorter-text', etc; now works with 'sorter-my-custom-parser' - val = cl.match( new RegExp('\\s' + key + '-([\\w-]+)') )[1] || ''; - } - return $.trim(val); - }; + // detach tbody but save the position + // don't use tbody because there are portions that look for a tbody index (updateCell) + processTbody : function( table, $tb, getIt ) { + table = $( table )[ 0 ]; + if ( getIt ) { + table.isProcessing = true; + $tb.before( '<colgroup class="tablesorter-savemyplace"/>' ); + return $.fn.detach ? $tb.detach() : $tb.remove(); + } + var holdr = $( table ).find( 'colgroup.tablesorter-savemyplace' ); + $tb.insertAfter( holdr ); + holdr.remove(); + table.isProcessing = false; + }, - ts.regex.comma = /,/g; - ts.regex.digitNonUS = /[\s|\.]/g; - ts.regex.digitNegativeTest = /^\s*\([.\d]+\)/; - ts.regex.digitNegativeReplace = /^\s*\(([.\d]+)\)/; - ts.formatFloat = function(s, table) { - if (typeof s !== 'string' || s === '') { return s; } - // allow using formatFloat without a table; defaults to US number format - var i, - t = table && table.config ? table.config.usNumberFormat !== false : - typeof table !== 'undefined' ? table : true; - if (t) { - // US Format - 1,234,567.89 -> 1234567.89 - s = s.replace(ts.regex.comma, ''); - } else { - // German Format = 1.234.567,89 -> 1234567.89 - // French Format = 1 234 567,89 -> 1234567.89 - s = s.replace(ts.regex.digitNonUS, '').replace(ts.regex.comma, '.'); + clearTableBody : function( table ) { + $( table )[ 0 ].config.$tbodies.children().detach(); + }, + + // used when replacing accented characters during sorting + characterEquivalents : { + 'a' : '\u00e1\u00e0\u00e2\u00e3\u00e4\u0105\u00e5', // áàâãäąå + 'A' : '\u00c1\u00c0\u00c2\u00c3\u00c4\u0104\u00c5', // ÁÀÂÃÄĄÅ + 'c' : '\u00e7\u0107\u010d', // çćč + 'C' : '\u00c7\u0106\u010c', // ÇĆČ + 'e' : '\u00e9\u00e8\u00ea\u00eb\u011b\u0119', // éèêëěę + 'E' : '\u00c9\u00c8\u00ca\u00cb\u011a\u0118', // ÉÈÊËĚĘ + 'i' : '\u00ed\u00ec\u0130\u00ee\u00ef\u0131', // íìİîïı + 'I' : '\u00cd\u00cc\u0130\u00ce\u00cf', // ÍÌİÎÏ + 'o' : '\u00f3\u00f2\u00f4\u00f5\u00f6\u014d', // óòôõöō + 'O' : '\u00d3\u00d2\u00d4\u00d5\u00d6\u014c', // ÓÒÔÕÖŌ + 'ss': '\u00df', // ß (s sharp) + 'SS': '\u1e9e', // ẞ (Capital sharp s) + 'u' : '\u00fa\u00f9\u00fb\u00fc\u016f', // úùûüů + 'U' : '\u00da\u00d9\u00db\u00dc\u016e' // ÚÙÛÜŮ + }, + + replaceAccents : function( str ) { + var chr, + acc = '[', + eq = ts.characterEquivalents; + if ( !ts.characterRegex ) { + ts.characterRegexArray = {}; + for ( chr in eq ) { + if ( typeof chr === 'string' ) { + acc += eq[ chr ]; + ts.characterRegexArray[ chr ] = new RegExp( '[' + eq[ chr ] + ']', 'g' ); + } } - if (ts.regex.digitNegativeTest.test(s)) { - // make (#) into a negative number -> (10) = -10 - s = s.replace(ts.regex.digitNegativeReplace, '-$1'); + ts.characterRegex = new RegExp( acc + ']' ); + } + if ( ts.characterRegex.test( str ) ) { + for ( chr in eq ) { + if ( typeof chr === 'string' ) { + str = str.replace( ts.characterRegexArray[ chr ], chr ); + } } - i = parseFloat(s); - // return the text instead of zero - return isNaN(i) ? $.trim(s) : i; - }; + } + return str; + }, - ts.regex.digitTest = /^[\-+(]?\d+[)]?$/; - ts.regex.digitReplace = /[,.'"\s]/g; - ts.isDigit = function(s) { - // replace all unwanted chars and match - return isNaN(s) ? - ts.regex.digitTest.test( s.toString().replace( ts.regex.digitReplace, '' ) ) : - s !== ''; - }; + // restore headers + restoreHeaders : function( table ) { + var index, $cell, + c = $( table )[ 0 ].config, + $headers = c.$table.find( c.selectorHeaders ), + len = $headers.length; + // don't use c.$headers here in case header cells were swapped + for ( index = 0; index < len; index++ ) { + $cell = $headers.eq( index ); + // only restore header cells if it is wrapped + // because this is also used by the updateAll method + if ( $cell.find( '.' + ts.css.headerIn ).length ) { + $cell.html( c.headerContent[ index ] ); + } + } + }, - }() - }); + destroy : function( table, removeClasses, callback ) { + table = $( table )[ 0 ]; + if ( !table.hasInitialized ) { return; } + // remove all widgets + ts.removeWidget( table, true, false ); + var events, + $t = $( table ), + c = table.config, + debug = c.debug, + $h = $t.find( 'thead:first' ), + $r = $h.find( 'tr.' + ts.css.headerRow ).removeClass( ts.css.headerRow + ' ' + c.cssHeaderRow ), + $f = $t.find( 'tfoot:first > tr' ).children( 'th, td' ); + if ( removeClasses === false && $.inArray( 'uitheme', c.widgets ) >= 0 ) { + // reapply uitheme classes, in case we want to maintain appearance + $t.trigger( 'applyWidgetId', [ 'uitheme' ] ); + $t.trigger( 'applyWidgetId', [ 'zebra' ] ); + } + // remove widget added rows, just in case + $h.find( 'tr' ).not( $r ).remove(); + // disable tablesorter + events = 'sortReset update updateRows updateAll updateHeaders updateCell addRows updateComplete sorton ' + + 'appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress ' + + 'sortBegin sortEnd resetToLoadState '.split( ' ' ) + .join( c.namespace + ' ' ); + $t + .removeData( 'tablesorter' ) + .unbind( events.replace( ts.regex.spaces, ' ' ) ); + c.$headers + .add( $f ) + .removeClass( [ ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone ].join( ' ' ) ) + .removeAttr( 'data-column' ) + .removeAttr( 'aria-label' ) + .attr( 'aria-disabled', 'true' ); + $r + .find( c.selectorSort ) + .unbind( ( 'mousedown mouseup keypress '.split( ' ' ).join( c.namespace + ' ' ) ).replace( ts.regex.spaces, ' ' ) ); + ts.restoreHeaders( table ); + $t.toggleClass( ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme, removeClasses === false ); + // clear flag in case the plugin is initialized again + table.hasInitialized = false; + delete table.config.cache; + if ( typeof callback === 'function' ) { + callback( table ); + } + if ( debug ) { + console.log( 'tablesorter has been removed' ); + } + } - // make shortcut - var ts = $.tablesorter; + }; - // extend plugin scope - $.fn.extend({ - tablesorter: ts.construct - }); + $.fn.tablesorter = function( settings ) { + return this.each( function() { + var table = this, + // merge & extend config options + c = $.extend( true, {}, ts.defaults, settings, ts.instanceMethods ); + // save initial settings + c.originalSettings = settings; + // create a table from data (build table widget) + if ( !table.hasInitialized && ts.buildTable && this.nodeName !== 'TABLE' ) { + // return the table (in case the original target is the table's container) + ts.buildTable( table, c ); + } else { + ts.setup( table, c ); + } + }); + }; // set up debug logs if ( !( window.console && window.console.log ) ) { + // access $.tablesorter.logs for browsers that don't have a console... ts.logs = []; /*jshint -W020 */ console = {}; console.log = console.warn = console.error = console.table = function() { - ts.logs.push( [ Date.now(), arguments ] ); + var arg = arguments.length > 1 ? arguments : arguments[0]; + ts.logs.push({ date: Date.now(), log: arg }); }; } - ts.log = function(){ - console.log( arguments ); - }; - ts.benchmark = function( diff ) { - return ( ' (' + ( new Date().getTime() - diff.getTime() ) + 'ms)' ); - }; - // add default parsers ts.addParser({ - id: 'no-parser', - is: function() { + id : 'no-parser', + is : function() { return false; }, - format: function() { + format : function() { return ''; }, - type: 'text' + type : 'text' }); ts.addParser({ - id: 'text', - is: function() { + id : 'text', + is : function() { return true; }, - format: function(s, table) { + format : function( str, table ) { var c = table.config; - if (s) { - s = $.trim( c.ignoreCase ? s.toLocaleLowerCase() : s ); - s = c.sortLocaleCompare ? ts.replaceAccents(s) : s; + if ( str ) { + str = $.trim( c.ignoreCase ? str.toLocaleLowerCase() : str ); + str = c.sortLocaleCompare ? ts.replaceAccents( str ) : str; } - return s; + return str; }, - type: 'text' + type : 'text' }); ts.regex.nondigit = /[^\w,. \-()]/g; ts.addParser({ - id: 'digit', - is: function(s) { - return ts.isDigit(s); + id : 'digit', + is : function( str ) { + return ts.isDigit( str ); }, - format: function(s, table) { - var n = ts.formatFloat((s || '').replace(ts.regex.nondigit, ''), table); - return s && typeof n === 'number' ? n : - s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; + format : function( str, table ) { + var num = ts.formatFloat( ( str || '' ).replace( ts.regex.nondigit, '' ), table ); + return str && typeof num === 'number' ? num : + str ? $.trim( str && table.config.ignoreCase ? str.toLocaleLowerCase() : str ) : str; }, - type: 'numeric' + type : 'numeric' }); ts.regex.currencyReplace = /[+\-,. ]/g; ts.regex.currencyTest = /^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/; ts.addParser({ - id: 'currency', - is: function(s) { - s = (s || '').replace(ts.regex.currencyReplace, ''); + id : 'currency', + is : function( str ) { + str = ( str || '' ).replace( ts.regex.currencyReplace, '' ); // test for £$€¤¥¢ - return ts.regex.currencyTest.test(s); + return ts.regex.currencyTest.test( str ); }, - format: function(s, table) { - var n = ts.formatFloat((s || '').replace(ts.regex.nondigit, ''), table); - return s && typeof n === 'number' ? n : - s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; + format : function( str, table ) { + var num = ts.formatFloat( ( str || '' ).replace( ts.regex.nondigit, '' ), table ); + return str && typeof num === 'number' ? num : + str ? $.trim( str && table.config.ignoreCase ? str.toLocaleLowerCase() : str ) : str; }, - type: 'numeric' + type : 'numeric' }); // too many protocols to add them all https://en.wikipedia.org/wiki/URI_scheme @@ -2195,72 +2428,72 @@ ts.regex.urlProtocolTest = /^(https?|ftp|file):\/\//; ts.regex.urlProtocolReplace = /(https?|ftp|file):\/\//; ts.addParser({ - id: 'url', - is: function(s) { - return ts.regex.urlProtocolTest.test(s); + id : 'url', + is : function( str ) { + return ts.regex.urlProtocolTest.test( str ); }, - format: function(s) { - return s ? $.trim(s.replace(ts.regex.urlProtocolReplace, '')) : s; + format : function( str ) { + return str ? $.trim( str.replace( ts.regex.urlProtocolReplace, '' ) ) : str; }, parsed : true, // filter widget flag - type: 'text' + type : 'text' }); ts.regex.dash = /-/g; ts.regex.isoDate = /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}/; ts.addParser({ - id: 'isoDate', - is: function(s) { - return ts.regex.isoDate.test(s); + id : 'isoDate', + is : function( str ) { + return ts.regex.isoDate.test( str ); }, - format: function(s, table) { - var date = s ? new Date( s.replace(ts.regex.dash, '/') ) : s; - return date instanceof Date && isFinite(date) ? date.getTime() : s; + format : function( str, table ) { + var date = str ? new Date( str.replace( ts.regex.dash, '/' ) ) : str; + return date instanceof Date && isFinite( date ) ? date.getTime() : str; }, - type: 'numeric' + type : 'numeric' }); ts.regex.percent = /%/g; ts.regex.percentTest = /(\d\s*?%|%\s*?\d)/; ts.addParser({ - id: 'percent', - is: function(s) { - return ts.regex.percentTest.test(s) && s.length < 15; + id : 'percent', + is : function( str ) { + return ts.regex.percentTest.test( str ) && str.length < 15; }, - format: function(s, table) { - return s ? ts.formatFloat(s.replace(ts.regex.percent, ''), table) : s; + format : function( str, table ) { + return str ? ts.formatFloat( str.replace( ts.regex.percent, '' ), table ) : str; }, - type: 'numeric' + type : 'numeric' }); // added image parser to core v2.17.9 ts.addParser({ - id: 'image', - is: function(s, table, node, $node){ - return $node.find('img').length > 0; + id : 'image', + is : function( str, table, node, $node ) { + return $node.find( 'img' ).length > 0; }, - format: function(s, table, cell) { - return $(cell).find('img').attr(table.config.imgAttr || 'alt') || s; + format : function( str, table, cell ) { + return $( cell ).find( 'img' ).attr( table.config.imgAttr || 'alt' ) || str; }, parsed : true, // filter widget flag - type: 'text' + type : 'text' }); ts.regex.dateReplace = /(\S)([AP]M)$/i; // used by usLongDate & time parser ts.regex.usLongDateTest1 = /^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i; ts.regex.usLongDateTest2 = /^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i; ts.addParser({ - id: 'usLongDate', - is: function(s) { + id : 'usLongDate', + is : function( str ) { // two digit years are not allowed cross-browser // Jan 01, 2013 12:34:56 PM or 01 Jan 2013 - return ts.regex.usLongDateTest1.test(s) || ts.regex.usLongDateTest2.test(s); + return ts.regex.usLongDateTest1.test( str ) || ts.regex.usLongDateTest2.test( str ); }, - format: function(s, table) { - var date = s ? new Date( s.replace(ts.regex.dateReplace, '$1 $2') ) : s; - return date instanceof Date && isFinite(date) ? date.getTime() : s; + format : function( str, table ) { + var date = str ? new Date( str.replace( ts.regex.dateReplace, '$1 $2' ) ) : str; + return date instanceof Date && isFinite( date ) ? date.getTime() : str; }, - type: 'numeric' + type : 'numeric' }); // testing for ##-##-#### or ####-##-##, so it's not perfect; time can be included @@ -2270,103 +2503,129 @@ // XXY covers MDY & DMY formats ts.regex.shortDateXXY = /(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/; ts.regex.shortDateYMD = /(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/; + ts.convertFormat = function( dateString, format ) { + dateString = ( dateString || '' ) + .replace( ts.regex.spaces, ' ' ) + .replace( ts.regex.shortDateReplace, '/' ); + if ( format === 'mmddyyyy' ) { + dateString = dateString.replace( ts.regex.shortDateXXY, '$3/$1/$2' ); + } else if ( format === 'ddmmyyyy' ) { + dateString = dateString.replace( ts.regex.shortDateXXY, '$3/$2/$1' ); + } else if ( format === 'yyyymmdd' ) { + dateString = dateString.replace( ts.regex.shortDateYMD, '$1/$2/$3' ); + } + var date = new Date( dateString ); + return date instanceof Date && isFinite( date ) ? date.getTime() : ''; + }; + ts.addParser({ - id: 'shortDate', // 'mmddyyyy', 'ddmmyyyy' or 'yyyymmdd' - is: function(s) { - s = (s || '').replace(ts.regex.spaces, ' ').replace(ts.regex.shortDateReplace, '/'); - return ts.regex.shortDateTest.test(s); - }, - format: function(s, table, cell, cellIndex) { - if (s) { - var date, d, - c = table.config, - ci = c.$headerIndexed[ cellIndex ], - format = ci.length && ci[0].dateFormat || - ts.getData( ci, ts.getColumnData( table, c.headers, cellIndex ), 'dateFormat') || + id : 'shortDate', // 'mmddyyyy', 'ddmmyyyy' or 'yyyymmdd' + is : function( str ) { + str = ( str || '' ).replace( ts.regex.spaces, ' ' ).replace( ts.regex.shortDateReplace, '/' ); + return ts.regex.shortDateTest.test( str ); + }, + format : function( str, table, cell, cellIndex ) { + if ( str ) { + var c = table.config, + $header = c.$headerIndexed[ cellIndex ], + format = $header.length && $header.data( 'dateFormat' ) || + ts.getData( $header, ts.getColumnData( table, c.headers, cellIndex ), 'dateFormat' ) || c.dateFormat; - d = s.replace(ts.regex.spaces, ' ').replace(ts.regex.shortDateReplace, '/'); - if (format === 'mmddyyyy') { - d = d.replace(ts.regex.shortDateXXY, '$3/$1/$2'); - } else if (format === 'ddmmyyyy') { - d = d.replace(ts.regex.shortDateXXY, '$3/$2/$1'); - } else if (format === 'yyyymmdd') { - d = d.replace(ts.regex.shortDateYMD, '$1/$2/$3'); + // save format because getData can be slow... + if ( $header.length ) { + $header.data( 'dateFormat', format ); } - date = new Date(d); - return date instanceof Date && isFinite(date) ? date.getTime() : s; + return ts.convertFormat( str, format ) || str; } - return s; + return str; }, - type: 'numeric' + type : 'numeric' }); - ts.regex.timeTest = /^(([0-2]?\d:[0-5]\d)|([0-1]?\d:[0-5]\d\s?([AP]M)))$/i; + // match 24 hour time & 12 hours time + am/pm - see http://regexr.com/3c3tk + ts.regex.timeTest = /^([1-9]|1[0-2]):([0-5]\d)(\s[AP]M)|((?:[01]\d|[2][0-4]):[0-5]\d)$/i; + ts.regex.timeMatch = /([1-9]|1[0-2]):([0-5]\d)(\s[AP]M)|((?:[01]\d|[2][0-4]):[0-5]\d)/i; ts.addParser({ - id: 'time', - is: function(s) { - return ts.regex.timeTest.test(s); + id : 'time', + is : function( str ) { + return ts.regex.timeTest.test( str ); }, - format: function(s, table) { - var date = s ? new Date( '2000/01/01 ' + s.replace(ts.regex.dateReplace, '$1 $2') ) : s; - return date instanceof Date && isFinite(date) ? date.getTime() : s; + format : function( str, table ) { + // isolate time... ignore month, day and year + var temp, + timePart = ( str || '' ).match( ts.regex.timeMatch ), + orig = new Date( str ), + // no time component? default to 00:00 by leaving it out, but only if str is defined + time = str && ( timePart !== null ? timePart[ 0 ] : '00:00 AM' ), + date = time ? new Date( '2000/01/01 ' + time.replace( ts.regex.dateReplace, '$1 $2' ) ) : time; + if ( date instanceof Date && isFinite( date ) ) { + temp = orig instanceof Date && isFinite( orig ) ? orig.getTime() : 0; + // if original string was a valid date, add it to the decimal so the column sorts in some kind of order + // luckily new Date() ignores the decimals + return temp ? parseFloat( date.getTime() + '.' + orig.getTime() ) : date.getTime(); + } + return str; }, - type: 'numeric' + type : 'numeric' }); ts.addParser({ - id: 'metadata', - is: function() { + id : 'metadata', + is : function() { return false; }, - format: function(s, table, cell) { + format : function( str, table, cell ) { var c = table.config, - p = (!c.parserMetadataName) ? 'sortValue' : c.parserMetadataName; - return $(cell).metadata()[p]; + p = ( !c.parserMetadataName ) ? 'sortValue' : c.parserMetadataName; + return $( cell ).metadata()[ p ]; }, - type: 'numeric' + type : 'numeric' }); + /* + ██████ ██████ █████▄ █████▄ ▄████▄ + ▄█▀ ██▄▄ ██▄▄██ ██▄▄██ ██▄▄██ + ▄█▀ ██▀▀ ██▀▀██ ██▀▀█ ██▀▀██ + ██████ ██████ █████▀ ██ ██ ██ ██ + */ // add default widgets ts.addWidget({ - id: 'zebra', - priority: 90, - format: function(table, c, wo) { - var $tv, $tr, row, even, time, k, i, len, - child = new RegExp(c.cssChildRow, 'i'), - b = c.$tbodies.add( $( c.namespace + '_extra_table' ).children( 'tbody:not(.' + c.cssInfoBlock + ')' ) ); - if (c.debug) { - time = new Date(); - } - for (k = 0; k < b.length; k++ ) { + id : 'zebra', + priority : 90, + format : function( table, c, wo ) { + var $visibleRows, $row, count, isEven, tbodyIndex, rowIndex, len, + child = new RegExp( c.cssChildRow, 'i' ), + $tbodies = c.$tbodies.add( $( c.namespace + '_extra_table' ).children( 'tbody:not(.' + c.cssInfoBlock + ')' ) ); + for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { // loop through the visible rows - row = 0; - $tv = b.eq( k ).children( 'tr:visible' ).not( c.selectorRemove ); - len = $tv.length; - for ( i = 0; i < len; i++ ) { - $tr = $tv.eq( i ); + count = 0; + $visibleRows = $tbodies.eq( tbodyIndex ).children( 'tr:visible' ).not( c.selectorRemove ); + len = $visibleRows.length; + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { + $row = $visibleRows.eq( rowIndex ); // style child rows the same way the parent row was styled - if ( !child.test( $tr[0].className ) ) { row++; } - even = ( row % 2 === 0 ); - $tr - .removeClass( wo.zebra[ even ? 1 : 0 ] ) - .addClass( wo.zebra[ even ? 0 : 1 ] ); + if ( !child.test( $row[ 0 ].className ) ) { count++; } + isEven = ( count % 2 === 0 ); + $row + .removeClass( wo.zebra[ isEven ? 1 : 0 ] ) + .addClass( wo.zebra[ isEven ? 0 : 1 ] ); } } }, - remove: function(table, c, wo, refreshing){ - if (refreshing) { return; } - var k, $tb, - b = c.$tbodies, - rmv = (wo.zebra || [ 'even', 'odd' ]).join(' '); - for (k = 0; k < b.length; k++ ){ - $tb = ts.processTbody(table, b.eq(k), true); // remove tbody - $tb.children().removeClass(rmv); - ts.processTbody(table, $tb, false); // restore tbody + remove : function( table, c, wo, refreshing ) { + if ( refreshing ) { return; } + var tbodyIndex, $tbody, + $tbodies = c.$tbodies, + toRemove = ( wo.zebra || [ 'even', 'odd' ] ).join( ' ' ); + for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ){ + $tbody = ts.processTbody( table, $tbodies.eq( tbodyIndex ), true ); // remove tbody + $tbody.children().removeClass( toRemove ); + ts.processTbody( table, $tbody, false ); // restore tbody } } }); -})(jQuery); +})( jQuery ); /*! Widget: storage - updated 3/26/2015 (v2.21.3) */ /*global JSON:false */ @@ -2724,7 +2983,7 @@ })(jQuery); -/*! Widget: filter - updated 10/4/2015 (v2.23.5) *//* +/*! Widget: filter - updated 10/31/2015 (v2.24.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3293,8 +3552,9 @@ // $cell parameter, but not the config, is passed to the filter_formatters, // so we have to work with it instead formatterUpdated: function( $cell, column ) { - var wo = $cell.closest( 'table' )[0].config.widgetOptions; - if ( !wo.filter_initialized ) { + // prevent error if $cell is undefined - see #1056 + var wo = $cell && $cell.closest( 'table' )[0].config.widgetOptions; + if ( wo && !wo.filter_initialized ) { // add updates by column since this function // may be called numerous times before initialization wo.filter_formatterInit[ column ] = 1; @@ -3331,6 +3591,16 @@ } } }, + // encode or decode filters for storage; see #1026 + processFilters: function( filters, encode ) { + var indx, + mode = encode ? encodeURIComponent : decodeURIComponent, + len = filters.length; + for ( indx = 0; indx < len; indx++ ) { + filters[ indx ] = mode( filters[ indx ] ); + } + return filters; + }, setDefaults: function( table, c, wo ) { var isArray, saved, indx, col, $filters, // get current ( default ) filters @@ -3340,7 +3610,7 @@ isArray = $.isArray( saved ); // make sure we're not just getting an empty array if ( !( isArray && saved.join( '' ) === '' || !isArray ) ) { - filters = saved; + filters = tsf.processFilters( saved ); } } // if no filters saved, then check default settings @@ -3363,71 +3633,81 @@ return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter; }, buildRow: function( table, c, wo ) { - var col, column, $header, makeSelect, disabled, name, ffxn, tmp, + var $filter, col, column, $header, makeSelect, disabled, name, ffxn, tmp, // c.columns defined in computeThIndexes() cellFilter = wo.filter_cellFilter, columns = c.columns, arry = $.isArray( cellFilter ), buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; for ( column = 0; column < columns; column++ ) { - buildFilter += '<td'; - if ( arry ) { - buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' ); - } else { - buildFilter += ( cellFilter !== '' ? ' class="' + cellFilter + '"' : '' ); + if ( c.$headerIndexed[ column ].length ) { + buildFilter += '<td data-column="' + column + '"'; + // account for entire column set with colspan. See #1047 + tmp = c.$headerIndexed[ column ] && c.$headerIndexed[ column ][0].colSpan || 0; + if ( tmp > 1 ) { + buildFilter += ' colspan="' + tmp + '"'; + } + if ( arry ) { + buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' ); + } else { + buildFilter += ( cellFilter !== '' ? ' class="' + cellFilter + '"' : '' ); + } + buildFilter += '></td>'; } - buildFilter += '></td>'; } c.$filters = $( buildFilter += '</tr>' ) .appendTo( c.$table.children( 'thead' ).eq( 0 ) ) - .find( 'td' ); + .children( 'td' ); // build each filter input for ( column = 0; column < columns; column++ ) { disabled = false; // assuming last cell of a column is the main column $header = c.$headerIndexed[ column ]; - ffxn = ts.getColumnData( table, wo.filter_functions, column ); - makeSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || - $header.hasClass( 'filter-select' ); - // get data from jQuery data, metadata, headers option or header class name - col = ts.getColumnData( table, c.headers, column ); - disabled = ts.getData( $header[0], col, 'filter' ) === 'false' || - ts.getData( $header[0], col, 'parser' ) === 'false'; - - if ( makeSelect ) { - buildFilter = $( '<select>' ).appendTo( c.$filters.eq( column ) ); - } else { - ffxn = ts.getColumnData( table, wo.filter_formatter, column ); - if ( ffxn ) { - wo.filter_formatterCount++; - buildFilter = ffxn( c.$filters.eq( column ), column ); - // no element returned, so lets go find it - if ( buildFilter && buildFilter.length === 0 ) { - buildFilter = c.$filters.eq( column ).children( 'input' ); + if ( $header && $header.length ) { + $filter = c.$filters.filter( '[data-column="' + column + '"]' ); + ffxn = ts.getColumnData( table, wo.filter_functions, column ); + makeSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || + $header.hasClass( 'filter-select' ); + // get data from jQuery data, metadata, headers option or header class name + col = ts.getColumnData( table, c.headers, column ); + disabled = ts.getData( $header[0], col, 'filter' ) === 'false' || + ts.getData( $header[0], col, 'parser' ) === 'false'; + + if ( makeSelect ) { + buildFilter = $( '<select>' ).appendTo( $filter ); + } else { + ffxn = ts.getColumnData( table, wo.filter_formatter, column ); + if ( ffxn ) { + wo.filter_formatterCount++; + buildFilter = ffxn( $filter, column ); + // no element returned, so lets go find it + if ( buildFilter && buildFilter.length === 0 ) { + buildFilter = $filter.children( 'input' ); + } + // element not in DOM, so lets attach it + if ( buildFilter && ( buildFilter.parent().length === 0 || + ( buildFilter.parent().length && buildFilter.parent()[0] !== $filter[0] ) ) ) { + $filter.append( buildFilter ); + } + } else { + buildFilter = $( '<input type="search">' ).appendTo( $filter ); } - // element not in DOM, so lets attach it - if ( buildFilter && ( buildFilter.parent().length === 0 || - ( buildFilter.parent().length && buildFilter.parent()[0] !== c.$filters[column] ) ) ) { - c.$filters.eq( column ).append( buildFilter ); + if ( buildFilter ) { + tmp = $header.data( 'placeholder' ) || + $header.attr( 'data-placeholder' ) || + wo.filter_placeholder.search || ''; + buildFilter.attr( 'placeholder', tmp ); } - } else { - buildFilter = $( '<input type="search">' ).appendTo( c.$filters.eq( column ) ); } if ( buildFilter ) { - tmp = $header.data( 'placeholder' ) || - $header.attr( 'data-placeholder' ) || - wo.filter_placeholder.search || ''; - buildFilter.attr( 'placeholder', tmp ); - } - } - if ( buildFilter ) { - // add filter class name - name = ( $.isArray( wo.filter_cssFilter ) ? - ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : - wo.filter_cssFilter ) || ''; - buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column ); - if ( disabled ) { - buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; + // add filter class name + name = ( $.isArray( wo.filter_cssFilter ) ? + ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : + wo.filter_cssFilter ) || ''; + buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column ); + if ( disabled ) { + buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; + } } } } @@ -3517,9 +3797,9 @@ if ( $.isEmptyObject( c.cache ) ) { // update cache if delayInit set & pager has initialized ( after user initiates a search ) if ( c.delayInit && c.pager && c.pager.initialized ) { - c.$table.trigger( 'updateCache', [ function() { + ts.updateCache( c, function() { tsf.checkFilters( table, false, skipFirst ); - } ] ); + }); } return; } @@ -3557,10 +3837,10 @@ return false; } }, - hideFilters: function( c ) { - var timer; - c.$table - .find( '.' + tscss.filterRow ) + hideFilters: function( c, $table ) { + var timer, + $row = ( $table || c.$table ).find( '.' + tscss.filterRow ).addClass( tscss.filterRowHide ); + $row .bind( 'mouseenter mouseleave', function( e ) { // save event object - http://bugs.jquery.com/ticket/12140 var event = e, @@ -3977,7 +4257,7 @@ // ( '> -10' => '> -100' will ignore hidden rows ) !( regex.isNeg1.test( val ) || regex.isNeg2.test( val ) ) && // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 - !( val !== '' && c.$filters && c.$filters.eq( indx ).find( 'select' ).length && + !( val !== '' && c.$filters && c.$filters.filter( '[data-column="' + indx + '"]' ).find( 'select' ).length && !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); } } @@ -4084,7 +4364,7 @@ c.lastSearch = storedFilters; c.$table.data( 'lastSearch', storedFilters ); if ( wo.filter_saveFilters && ts.storage ) { - ts.storage( table, 'tablesorter-filters', storedFilters ); + ts.storage( table, 'tablesorter-filters', tsf.processFilters( storedFilters, true ) ); } if ( c.debug ) { console.log( 'Completed filter widget search' + ts.benchmark(time) ); @@ -4093,7 +4373,7 @@ c.$table.trigger( 'filterEnd', c ); } setTimeout( function() { - c.$table.trigger( 'applyWidgets' ); // make sure zebra widget is applied + ts.applyWidget( c.table ); // make sure zebra widget is applied }, 0 ); }, getOptionSource: function( table, column, onlyAvail ) { @@ -4425,7 +4705,7 @@ })( jQuery ); -/*! Widget: stickyHeaders - updated 3/26/2015 (v2.21.3) *//* +/*! Widget: stickyHeaders - updated 10/31/2015 (v2.24.0) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -4689,7 +4969,7 @@ ts.filter.bindSearch( $table, $stickyCells.find('.' + ts.css.filter) ); // support hideFilters if (wo.filter_hideFilters) { - ts.filter.hideFilters($stickyTable, c); + ts.filter.hideFilters(c, $stickyTable); } } @@ -5109,7 +5389,10 @@ })( jQuery, window ); -/*! Widget: saveSort */ +/*! Widget: saveSort - updated 10/31/2015 (v2.24.0) *//* +* Requires tablesorter v2.16+ +* by Rob Garrison +*/ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; @@ -5165,7 +5448,7 @@ c.sortList = sortList; } else if (table.hasInitialized && sortList && sortList.length > 0) { // update sort change - $table.trigger('sorton', [ sortList ]); + ts.sortOn( c, sortList ); } } }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 4243076..9fbe71f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.23.5 *//* +/*! TableSorter (FORK) v2.24.2 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -17,2159 +17,2392 @@ * @contributor Rob Garrison - https://github.com/Mottie/tablesorter */ /*jshint browser:true, jquery:true, unused:false, expr: true */ -;(function($){ +;( function( $ ) { 'use strict'; - $.extend({ - /*jshint supernew:true */ - tablesorter: new function() { - - var ts = this; - - ts.version = '2.23.5'; - - ts.parsers = []; - ts.widgets = []; - ts.defaults = { - - // *** appearance - theme : 'default', // adds tablesorter-{theme} to the table for styling - widthFixed : false, // adds colgroup to fix widths of columns - showProcessing : false, // show an indeterminate timer icon in the header when the table is sorted or filtered. - - headerTemplate : '{content}',// header layout template (HTML ok); {content} = innerHTML, {icon} = <i/> (class from cssIcon) - onRenderTemplate : null, // function(index, template){ return template; }, (template is a string) - onRenderHeader : null, // function(index){}, (nothing to return) - - // *** functionality - cancelSelection : true, // prevent text selection in the header - tabIndex : true, // add tabindex to header for keyboard accessibility - dateFormat : 'mmddyyyy', // other options: 'ddmmyyy' or 'yyyymmdd' - sortMultiSortKey : 'shiftKey', // key used to select additional columns - sortResetKey : 'ctrlKey', // key used to remove sorting on a column - usNumberFormat : true, // false for German '1.234.567,89' or French '1 234 567,89' - delayInit : false, // if false, the parsed table contents will not update until the first sort - serverSideSorting: false, // if true, server-side sorting should be performed because client-side sorting will be disabled, but the ui and events will still be used. - resort : true, // default setting to trigger a resort after an 'update', 'addRows', 'updateCell', etc has completed - - // *** sort options - headers : {}, // set sorter, string, empty, locked order, sortInitialOrder, filter, etc. - ignoreCase : true, // ignore case while sorting - sortForce : null, // column(s) first sorted; always applied - sortList : [], // Initial sort order; applied initially; updated when manually sorted - sortAppend : null, // column(s) sorted last; always applied - sortStable : false, // when sorting two rows with exactly the same content, the original sort order is maintained - - sortInitialOrder : 'asc', // sort direction on first click - sortLocaleCompare: false, // replace equivalent character (accented characters) - sortReset : false, // third click on the header will reset column to default - unsorted - sortRestart : false, // restart sort to 'sortInitialOrder' when clicking on previously unsorted columns - - emptyTo : 'bottom', // sort empty cell to bottom, top, none, zero, emptyMax, emptyMin - stringTo : 'max', // sort strings in numerical column as max, min, top, bottom, zero - textExtraction : 'basic', // text extraction method/function - function(node, table, cellIndex){} - textAttribute : 'data-text',// data-attribute that contains alternate cell text (used in default textExtraction function) - textSorter : null, // choose overall or specific column sorter function(a, b, direction, table, columnIndex) [alt: ts.sortText] - numberSorter : null, // choose overall numeric sorter function(a, b, direction, maxColumnValue) - - // *** widget options - widgets: [], // method to add widgets, e.g. widgets: ['zebra'] - widgetOptions : { - zebra : [ 'even', 'odd' ] // zebra widget alternating row class names - }, - initWidgets : true, // apply widgets on tablesorter initialization - widgetClass : 'widget-{name}', // table class name template to match to include a widget - - // *** callbacks - initialized : null, // function(table){}, - - // *** extra css class names - tableClass : '', - cssAsc : '', - cssDesc : '', - cssNone : '', - cssHeader : '', - cssHeaderRow : '', - cssProcessing : '', // processing icon applied to header during sort/filter - - cssChildRow : 'tablesorter-childRow', // class name indiciating that a row is to be attached to the its parent - cssIcon : 'tablesorter-icon', // if this class does not exist, the {icon} will not be added from the headerTemplate - cssIconNone : '', // class name added to the icon when there is no column sort - cssIconAsc : '', // class name added to the icon when the column has an ascending sort - cssIconDesc : '', // class name added to the icon when the column has a descending sort - cssInfoBlock : 'tablesorter-infoOnly', // don't sort tbody with this class name (only one class name allowed here!) - cssNoSort : 'tablesorter-noSort', // class name added to element inside header; clicking on it won't cause a sort - cssIgnoreRow : 'tablesorter-ignoreRow', // header row to ignore; cells within this row will not be added to c.$headers - - // *** events - pointerClick : 'click', - pointerDown : 'mousedown', - pointerUp : 'mouseup', - - // *** selectors - selectorHeaders : '> thead th, > thead td', - selectorSort : 'th, td', // jQuery selector of content within selectorHeaders that is clickable to trigger a sort - selectorRemove : '.remove-me', - - // *** advanced - debug : false, - - // *** Internal variables - headerList: [], - empties: {}, - strings: {}, - parsers: [] + var ts = $.tablesorter = { + + version : '2.24.2', + + parsers : [], + widgets : [], + defaults : { + + // *** appearance + theme : 'default', // adds tablesorter-{theme} to the table for styling + widthFixed : false, // adds colgroup to fix widths of columns + showProcessing : false, // show an indeterminate timer icon in the header when the table is sorted or filtered. + + headerTemplate : '{content}',// header layout template (HTML ok); {content} = innerHTML, {icon} = <i/> // class from cssIcon + onRenderTemplate : null, // function( index, template ){ return template; }, // template is a string + onRenderHeader : null, // function( index ){}, // nothing to return + + // *** functionality + cancelSelection : true, // prevent text selection in the header + tabIndex : true, // add tabindex to header for keyboard accessibility + dateFormat : 'mmddyyyy', // other options: 'ddmmyyy' or 'yyyymmdd' + sortMultiSortKey : 'shiftKey', // key used to select additional columns + sortResetKey : 'ctrlKey', // key used to remove sorting on a column + usNumberFormat : true, // false for German '1.234.567,89' or French '1 234 567,89' + delayInit : false, // if false, the parsed table contents will not update until the first sort + serverSideSorting: false, // if true, server-side sorting should be performed because client-side sorting will be disabled, but the ui and events will still be used. + resort : true, // default setting to trigger a resort after an 'update', 'addRows', 'updateCell', etc has completed + + // *** sort options + headers : {}, // set sorter, string, empty, locked order, sortInitialOrder, filter, etc. + ignoreCase : true, // ignore case while sorting + sortForce : null, // column(s) first sorted; always applied + sortList : [], // Initial sort order; applied initially; updated when manually sorted + sortAppend : null, // column(s) sorted last; always applied + sortStable : false, // when sorting two rows with exactly the same content, the original sort order is maintained + + sortInitialOrder : 'asc', // sort direction on first click + sortLocaleCompare: false, // replace equivalent character (accented characters) + sortReset : false, // third click on the header will reset column to default - unsorted + sortRestart : false, // restart sort to 'sortInitialOrder' when clicking on previously unsorted columns + + emptyTo : 'bottom', // sort empty cell to bottom, top, none, zero, emptyMax, emptyMin + stringTo : 'max', // sort strings in numerical column as max, min, top, bottom, zero + textExtraction : 'basic', // text extraction method/function - function( node, table, cellIndex ){} + textAttribute : 'data-text',// data-attribute that contains alternate cell text (used in default textExtraction function) + textSorter : null, // choose overall or specific column sorter function( a, b, direction, table, columnIndex ) [alt: ts.sortText] + numberSorter : null, // choose overall numeric sorter function( a, b, direction, maxColumnValue ) + + // *** widget options + widgets: [], // method to add widgets, e.g. widgets: ['zebra'] + widgetOptions : { + zebra : [ 'even', 'odd' ] // zebra widget alternating row class names + }, + initWidgets : true, // apply widgets on tablesorter initialization + widgetClass : 'widget-{name}', // table class name template to match to include a widget + + // *** callbacks + initialized : null, // function( table ){}, + + // *** extra css class names + tableClass : '', + cssAsc : '', + cssDesc : '', + cssNone : '', + cssHeader : '', + cssHeaderRow : '', + cssProcessing : '', // processing icon applied to header during sort/filter + + cssChildRow : 'tablesorter-childRow', // class name indiciating that a row is to be attached to the its parent + cssInfoBlock : 'tablesorter-infoOnly', // don't sort tbody with this class name (only one class name allowed here!) + cssNoSort : 'tablesorter-noSort', // class name added to element inside header; clicking on it won't cause a sort + cssIgnoreRow : 'tablesorter-ignoreRow', // header row to ignore; cells within this row will not be added to c.$headers + + cssIcon : 'tablesorter-icon', // if this class does not exist, the {icon} will not be added from the headerTemplate + cssIconNone : '', // class name added to the icon when there is no column sort + cssIconAsc : '', // class name added to the icon when the column has an ascending sort + cssIconDesc : '', // class name added to the icon when the column has a descending sort + + // *** events + pointerClick : 'click', + pointerDown : 'mousedown', + pointerUp : 'mouseup', + + // *** selectors + selectorHeaders : '> thead th, > thead td', + selectorSort : 'th, td', // jQuery selector of content within selectorHeaders that is clickable to trigger a sort + selectorRemove : '.remove-me', + + // *** advanced + debug : false, + + // *** Internal variables + headerList: [], + empties: {}, + strings: {}, + parsers: [] + + // removed: widgetZebra: { css: ['even', 'odd'] } - // removed: widgetZebra: { css: ['even', 'odd'] } + }, - }; + // internal css classes - these will ALWAYS be added to + // the table and MUST only contain one class name - fixes #381 + css : { + table : 'tablesorter', + cssHasChild: 'tablesorter-hasChildRow', + childRow : 'tablesorter-childRow', + colgroup : 'tablesorter-colgroup', + header : 'tablesorter-header', + headerRow : 'tablesorter-headerRow', + headerIn : 'tablesorter-header-inner', + icon : 'tablesorter-icon', + processing : 'tablesorter-processing', + sortAsc : 'tablesorter-headerAsc', + sortDesc : 'tablesorter-headerDesc', + sortNone : 'tablesorter-headerUnSorted' + }, - // internal css classes - these will ALWAYS be added to - // the table and MUST only contain one class name - fixes #381 - ts.css = { - table : 'tablesorter', - cssHasChild: 'tablesorter-hasChildRow', - childRow : 'tablesorter-childRow', - colgroup : 'tablesorter-colgroup', - header : 'tablesorter-header', - headerRow : 'tablesorter-headerRow', - headerIn : 'tablesorter-header-inner', - icon : 'tablesorter-icon', - processing : 'tablesorter-processing', - sortAsc : 'tablesorter-headerAsc', - sortDesc : 'tablesorter-headerDesc', - sortNone : 'tablesorter-headerUnSorted' - }; + // labels applied to sortable headers for accessibility (aria) support + language : { + sortAsc : 'Ascending sort applied, ', + sortDesc : 'Descending sort applied, ', + sortNone : 'No sort applied, ', + nextAsc : 'activate to apply an ascending sort', + nextDesc : 'activate to apply a descending sort', + nextNone : 'activate to remove the sort' + }, - // labels applied to sortable headers for accessibility (aria) support - ts.language = { - sortAsc : 'Ascending sort applied, ', - sortDesc : 'Descending sort applied, ', - sortNone : 'No sort applied, ', - nextAsc : 'activate to apply an ascending sort', - nextDesc : 'activate to apply a descending sort', - nextNone : 'activate to remove the sort' - }; + regex : { + templateContent : /\{content\}/g, + templateIcon : /\{icon\}/g, + templateName : /\{name\}/i, + spaces : /\s+/g, + nonWord : /\W/g, + formElements : /(input|select|button|textarea)/i, - ts.regex = { - templateContent : /\{content\}/g, - templateIcon : /\{icon\}/g, - templateName : /\{name\}/i, - spaces : /\s+/g, - nonWord : /\W/g, - formElements : /(input|select|button|textarea)/i - }; + // *** sort functions *** + // regex used in natural sort + // chunk/tokenize numbers & letters + chunk : /(^([+\-]?(?:\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi, + // replace chunks @ ends + chunks : /(^\\0|\\0$)/, + hex : /^0x[0-9a-f]+$/i, + + // *** formatFloat *** + comma : /,/g, + digitNonUS : /[\s|\.]/g, + digitNegativeTest : /^\s*\([.\d]+\)/, + digitNegativeReplace : /^\s*\(([.\d]+)\)/, + + // *** isDigit *** + digitTest : /^[\-+(]?\d+[)]?$/, + digitReplace : /[,.'"\s]/g - // These methods can be applied on table.config instance - ts.instanceMethods = {}; + }, - // $.isEmptyObject from jQuery v1.4 - ts.isEmptyObject = function( obj ) { - /*jshint forin: false */ - for ( var name in obj ) { - return false; - } - return true; - }; + // digit sort text location; keeping max+/- for backwards compatibility + string : { + max : 1, + min : -1, + emptymin : 1, + emptymax : -1, + zero : 0, + none : 0, + 'null' : 0, + top : true, + bottom : false + }, - ts.getElementText = function(c, node, cellIndex) { - if (!node) { return ''; } - var te, - t = c.textExtraction || '', - // node could be a jquery object - // http://jsperf.com/jquery-vs-instanceof-jquery/2 - $node = node.jquery ? node : $(node); - if (typeof t === 'string') { - // check data-attribute first when set to 'basic'; don't use node.innerText - it's really slow! - // http://www.kellegous.com/j/2013/02/27/innertext-vs-textcontent/ - if ( t === 'basic' && typeof ( te = $node.attr(c.textAttribute) ) !== 'undefined' ) { - return $.trim( te ); - } - return $.trim( node.textContent || $node.text() ); - } else { - if (typeof t === 'function') { - return $.trim( t($node[0], c.table, cellIndex) ); - } else if (typeof (te = ts.getColumnData( c.table, t, cellIndex )) === 'function') { - return $.trim( te($node[0], c.table, cellIndex) ); - } - } - // fallback - return $.trim( $node[0].textContent || $node.text() ); - }; + // These methods can be applied on table.config instance + instanceMethods : {}, - function detectParserForColumn(c, rows, rowIndex, cellIndex) { - var cur, $node, - i = ts.parsers.length, - node = false, - nodeValue = '', - keepLooking = true; - while (nodeValue === '' && keepLooking) { - rowIndex++; - if (rows[rowIndex]) { - node = rows[rowIndex].cells[cellIndex]; - nodeValue = ts.getElementText(c, node, cellIndex); - $node = $(node); - if (c.debug) { - console.log('Checking if value was empty on row ' + rowIndex + ', column: ' + cellIndex + ': "' + nodeValue + '"'); - } + /* + ▄█████ ██████ ██████ ██ ██ █████▄ + ▀█▄ ██▄▄ ██ ██ ██ ██▄▄██ + ▀█▄ ██▀▀ ██ ██ ██ ██▀▀▀ + █████▀ ██████ ██ ▀████▀ ██ + */ + + setup : function( table, c ) { + // if no thead or tbody, or tablesorter is already present, quit + if ( !table || !table.tHead || table.tBodies.length === 0 || table.hasInitialized === true ) { + if ( c.debug ) { + if ( table.hasInitialized ) { + console.warn( 'Stopping initialization. Tablesorter has already been initialized' ); } else { - keepLooking = false; + console.error( 'Stopping initialization! No table, thead or tbody' ); } } - while (--i >= 0) { - cur = ts.parsers[i]; - // ignore the default text parser because it will always be true - if (cur && cur.id !== 'text' && cur.is && cur.is(nodeValue, c.table, node, $node)) { - return cur; - } - } - // nothing found, return the generic parser (text) - return ts.getParserById('text'); + return; + } + + var tmp = '', + $table = $( table ), + meta = $.metadata; + // initialization flag + table.hasInitialized = false; + // table is being processed flag + table.isProcessing = true; + // make sure to store the config object + table.config = c; + // save the settings where they read + $.data( table, 'tablesorter', c ); + if ( c.debug ) { + console[ console.group ? 'group' : 'log' ]( 'Initializing tablesorter' ); + $.data( table, 'startoveralltimer', new Date() ); + } + + // removing this in version 3 (only supports jQuery 1.7+) + c.supportsDataObject = ( function( version ) { + version[ 0 ] = parseInt( version[ 0 ], 10 ); + return ( version[ 0 ] > 1 ) || ( version[ 0 ] === 1 && parseInt( version[ 1 ], 10 ) >= 4 ); + })( $.fn.jquery.split( '.' ) ); + // ensure case insensitivity + c.emptyTo = c.emptyTo.toLowerCase(); + c.stringTo = c.stringTo.toLowerCase(); + c.last = { sortList : [], clickedIndex : -1 }; + // add table theme class only if there isn't already one there + if ( !/tablesorter\-/.test( $table.attr( 'class' ) ) ) { + tmp = ( c.theme !== '' ? ' tablesorter-' + c.theme : '' ); + } + c.table = table; + c.$table = $table + .addClass( ts.css.table + ' ' + c.tableClass + tmp ) + .attr( 'role', 'grid' ); + c.$headers = $table.find( c.selectorHeaders ); + + // give the table a unique id, which will be used in namespace binding + if ( !c.namespace ) { + c.namespace = '.tablesorter' + Math.random().toString( 16 ).slice( 2 ); + } else { + // make sure namespace starts with a period & doesn't have weird characters + c.namespace = '.' + c.namespace.replace( ts.regex.nonWord, '' ); } - // centralized function to extract/parse cell contents - ts.getParsedText = function( c, cell, colIndex, txt ) { - if ( typeof txt === 'undefined' ) { - txt = ts.getElementText( c, cell, colIndex ); + c.$table.children().children( 'tr' ).attr( 'role', 'row' ); + c.$tbodies = $table.children( 'tbody:not(.' + c.cssInfoBlock + ')' ).attr({ + 'aria-live' : 'polite', + 'aria-relevant' : 'all' + }); + if ( c.$table.children( 'caption' ).length ) { + tmp = c.$table.children( 'caption' )[ 0 ]; + if ( !tmp.id ) { tmp.id = c.namespace.slice( 1 ) + 'caption'; } + c.$table.attr( 'aria-labelledby', tmp.id ); + } + c.widgetInit = {}; // keep a list of initialized widgets + // change textExtraction via data-attribute + c.textExtraction = c.$table.attr( 'data-text-extraction' ) || c.textExtraction || 'basic'; + // build headers + ts.buildHeaders( c ); + // fixate columns if the users supplies the fixedWidth option + // do this after theme has been applied + ts.fixColumnWidth( table ); + // add widgets from class name + ts.addWidgetFromClass( table ); + // add widget options before parsing (e.g. grouping widget has parser settings) + ts.applyWidgetOptions( table ); + // try to auto detect column type, and store in tables config + ts.setupParsers( c ); + // start total row count at zero + c.totalRows = 0; + // build the cache for the tbody cells + // delayInit will delay building the cache until the user starts a sort + if ( !c.delayInit ) { ts.buildCache( c ); } + // bind all header events and methods + ts.bindEvents( table, c.$headers, true ); + ts.bindMethods( c ); + // get sort list from jQuery data or metadata + // in jQuery < 1.4, an error occurs when calling $table.data() + if ( c.supportsDataObject && typeof $table.data().sortlist !== 'undefined' ) { + c.sortList = $table.data().sortlist; + } else if ( meta && ( $table.metadata() && $table.metadata().sortlist ) ) { + c.sortList = $table.metadata().sortlist; + } + // apply widget init code + ts.applyWidget( table, true ); + // if user has supplied a sort list to constructor + if ( c.sortList.length > 0 ) { + ts.sortOn( c, c.sortList, {}, !c.initWidgets ); + } else { + ts.setHeadersCss( c ); + if ( c.initWidgets ) { + // apply widget format + ts.applyWidget( table, false ); } - // if no parser, make sure to return the txt - var val = '' + txt, - parser = c.parsers[ colIndex ], - extractor = c.extractors[ colIndex ]; - if ( parser ) { - // do extract before parsing, if there is one - if ( extractor && typeof extractor.format === 'function' ) { - txt = extractor.format( txt, c.table, cell, colIndex ); - } - // allow parsing if the string is empty, previously parsing would change it to zero, - // in case the parser needs to extract data from the table cell attributes - val = parser.id === 'no-parser' ? '' : - // make sure txt is a string (extractor may have converted it) - parser.format( '' + txt, c.table, cell, colIndex ); - if ( c.ignoreCase && typeof val === 'string' ) { - val = val.toLowerCase(); + } + + // show processesing icon + if ( c.showProcessing ) { + $table + .unbind( 'sortBegin' + c.namespace + ' sortEnd' + c.namespace ) + .bind( 'sortBegin' + c.namespace + ' sortEnd' + c.namespace, function( e ) { + clearTimeout( c.processTimer ); + ts.isProcessing( table ); + if ( e.type === 'sortBegin' ) { + c.processTimer = setTimeout( function() { + ts.isProcessing( table, true ); + }, 500 ); } + }); + } + + // initialized + table.hasInitialized = true; + table.isProcessing = false; + if ( c.debug ) { + console.log( 'Overall initialization time: ' + ts.benchmark( $.data( table, 'startoveralltimer' ) ) ); + if ( c.debug && console.groupEnd ) { console.groupEnd(); } + } + $table.trigger( 'tablesorter-initialized', table ); + if ( typeof c.initialized === 'function' ) { + c.initialized( table ); + } + }, + + bindMethods : function( c ) { + var $table = c.$table, + namespace = c.namespace, + events = ( 'sortReset update updateRows updateAll updateHeaders addRows updateCell updateComplete ' + + 'sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup ' + + 'mouseleave ' ).split( ' ' ) + .join( namespace + ' ' ); + // apply easy methods that trigger bound events + $table + .unbind( events.replace( ts.regex.spaces, ' ' ) ) + .bind( 'sortReset' + namespace, function( e, callback ) { + e.stopPropagation(); + // using this.config to ensure functions are getting a non-cached version of the config + ts.sortReset( this.config, callback ); + }) + .bind( 'updateAll' + namespace, function( e, resort, callback ) { + e.stopPropagation(); + ts.updateAll( this.config, resort, callback ); + }) + .bind( 'update' + namespace + ' updateRows' + namespace, function( e, resort, callback ) { + e.stopPropagation(); + ts.update( this.config, resort, callback ); + }) + .bind( 'updateHeaders' + namespace, function( e, callback ) { + e.stopPropagation(); + ts.updateHeaders( this.config, callback ); + }) + .bind( 'updateCell' + namespace, function( e, cell, resort, callback ) { + e.stopPropagation(); + ts.updateCell( this.config, cell, resort, callback ); + }) + .bind( 'addRows' + namespace, function( e, $row, resort, callback ) { + e.stopPropagation(); + ts.addRows( this.config, $row, resort, callback ); + }) + .bind( 'updateComplete' + namespace, function() { + this.isUpdating = false; + }) + .bind( 'sorton' + namespace, function( e, list, callback, init ) { + e.stopPropagation(); + ts.sortOn( this.config, list, callback, init ); + }) + .bind( 'appendCache' + namespace, function( e, callback, init ) { + e.stopPropagation(); + ts.appendCache( this.config, init ); + if ( $.isFunction( callback ) ) { + callback( this ); + } + }) + // $tbodies variable is used by the tbody sorting widget + .bind( 'updateCache' + namespace, function( e, callback, $tbodies ) { + e.stopPropagation(); + ts.updateCache( this.config, callback, $tbodies ); + }) + .bind( 'applyWidgetId' + namespace, function( e, id ) { + e.stopPropagation(); + ts.getWidgetById( id ).format( this, this.config, this.config.widgetOptions ); + }) + .bind( 'applyWidgets' + namespace, function( e, init ) { + e.stopPropagation(); + // apply widgets + ts.applyWidget( this, init ); + }) + .bind( 'refreshWidgets' + namespace, function( e, all, dontapply ) { + e.stopPropagation(); + ts.refreshWidgets( this, all, dontapply ); + }) + .bind( 'destroy' + namespace, function( e, removeClasses, callback ) { + e.stopPropagation(); + ts.destroy( this, removeClasses, callback ); + }) + .bind( 'resetToLoadState' + namespace, function( e ) { + e.stopPropagation(); + // remove all widgets + ts.removeWidget( this, true, false ); + // restore original settings; this clears out current settings, but does not clear + // values saved to storage. + c = $.extend( true, ts.defaults, c.originalSettings ); + this.hasInitialized = false; + // setup the entire table again + ts.setup( this, c ); + }); + }, + + bindEvents : function( table, $headers, core ) { + table = $( table )[ 0 ]; + var tmp, + c = table.config, + namespace = c.namespace, + downTarget = null; + if ( core !== true ) { + $headers.addClass( namespace.slice( 1 ) + '_extra_headers' ); + tmp = $.fn.closest ? $headers.closest( 'table' )[ 0 ] : $headers.parents( 'table' )[ 0 ]; + if ( tmp && tmp.nodeName === 'TABLE' && tmp !== table ) { + $( tmp ).addClass( namespace.slice( 1 ) + '_extra_table' ); } - return val; - }; + } + tmp = ( c.pointerDown + ' ' + c.pointerUp + ' ' + c.pointerClick + ' sort keyup ' ) + .replace( ts.regex.spaces, ' ' ) + .split( ' ' ) + .join( namespace + ' ' ); + // apply event handling to headers and/or additional headers (stickyheaders, scroller, etc) + $headers + // http://stackoverflow.com/questions/5312849/jquery-find-self; + .find( c.selectorSort ) + .add( $headers.filter( c.selectorSort ) ) + .unbind( tmp ) + .bind( tmp, function( e, external ) { + var $cell, cell, temp, + $target = $( e.target ), + // wrap event type in spaces, so the match doesn't trigger on inner words + type = ' ' + e.type + ' '; + // only recognize left clicks + if ( ( ( e.which || e.button ) !== 1 && !type.match( ' ' + c.pointerClick + ' | sort | keyup ' ) ) || + // allow pressing enter + ( type === ' keyup ' && e.which !== 13 ) || + // allow triggering a click event (e.which is undefined) & ignore physical clicks + ( type.match( ' ' + c.pointerClick + ' ' ) && typeof e.which !== 'undefined' ) ) { + return; + } + // ignore mouseup if mousedown wasn't on the same target + if ( type.match( ' ' + c.pointerUp + ' ' ) && downTarget !== e.target && external !== true ) { + return; + } + // set target on mousedown + if ( type.match( ' ' + c.pointerDown + ' ' ) ) { + downTarget = e.target; + // preventDefault needed or jQuery v1.3.2 and older throws an + // "Uncaught TypeError: handler.apply is not a function" error + temp = $target.jquery.split( '.' ); + if ( temp[ 0 ] === '1' && temp[ 1 ] < 4 ) { e.preventDefault(); } + return; + } + downTarget = null; + // prevent sort being triggered on form elements + if ( ts.regex.formElements.test( e.target.nodeName ) || + // nosort class name, or elements within a nosort container + $target.hasClass( c.cssNoSort ) || $target.parents( '.' + c.cssNoSort ).length > 0 || + // elements within a button + $target.parents( 'button' ).length > 0 ) { + return !c.cancelSelection; + } + if ( c.delayInit && ts.isEmptyObject( c.cache ) ) { + ts.buildCache( c ); + } + // jQuery v1.2.6 doesn't have closest() + $cell = $.fn.closest ? $( this ).closest( 'th, td' ) : + /TH|TD/.test( this.nodeName ) ? $( this ) : $( this ).parents( 'th, td' ); + // reference original table headers and find the same cell + // don't use $headers or IE8 throws an error - see #987 + temp = $headers.index( $cell ); + c.last.clickedIndex = ( temp < 0 ) ? $cell.attr( 'data-column' ) : temp; + // use column index if $headers is undefined + cell = c.$headers[ c.last.clickedIndex ]; + if ( cell && !cell.sortDisabled ) { + ts.initSort( c, cell, e ); + } + }); + if ( c.cancelSelection ) { + // cancel selection + $headers + .attr( 'unselectable', 'on' ) + .bind( 'selectstart', false ) + .css({ + 'user-select' : 'none', + 'MozUserSelect' : 'none' // not needed for jQuery 1.8+ + }); + } + }, - function buildParserCache( c, $tbodies ) { - var rows, list, l, i, h, ch, np, p, e, time, tb, len, - table = c.table, - j = 0, - debug = {}; - // update table bodies in case we start with an empty table - c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'); - tb = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies; - len = tb.length; - if ( len === 0) { - return c.debug ? console.warn('Warning: *Empty table!* Not building a parser cache') : ''; - } else if (c.debug) { - time = new Date(); - console[ console.group ? 'group' : 'log' ]('Detecting parsers for each column'); - } - list = { - extractors: [], - parsers: [] + buildHeaders : function( c ) { + var $temp, icon, timer, indx; + c.headerList = []; + c.headerContent = []; + c.sortVars = []; + if ( c.debug ) { + timer = new Date(); + } + // children tr in tfoot - see issue #196 & #547 + c.columns = ts.computeColumnIndex( c.$table.children( 'thead, tfoot' ).children( 'tr' ) ); + // add icon if cssIcon option exists + icon = c.cssIcon ? + '<i class="' + ( c.cssIcon === ts.css.icon ? ts.css.icon : c.cssIcon + ' ' + ts.css.icon ) + '"></i>' : + ''; + // redefine c.$headers here in case of an updateAll that replaces or adds an entire header cell - see #683 + c.$headers = $( $.map( c.$table.find( c.selectorHeaders ), function( elem, index ) { + var configHeaders, header, column, template, tmp, + $elem = $( elem ); + // ignore cell (don't add it to c.$headers) if row has ignoreRow class + if ( $elem.parent().hasClass( c.cssIgnoreRow ) ) { return; } + // make sure to get header cell & not column indexed cell + configHeaders = ts.getColumnData( c.table, c.headers, index, true ); + // save original header content + c.headerContent[ index ] = $elem.html(); + // if headerTemplate is empty, don't reformat the header cell + if ( c.headerTemplate !== '' && !$elem.find( '.' + ts.css.headerIn ).length ) { + // set up header template + template = c.headerTemplate + .replace( ts.regex.templateContent, $elem.html() ) + .replace( ts.regex.templateIcon, $elem.find( '.' + ts.css.icon ).length ? '' : icon ); + if ( c.onRenderTemplate ) { + header = c.onRenderTemplate.apply( $elem, [ index, template ] ); + // only change t if something is returned + if ( header && typeof header === 'string' ) { + template = header; + } + } + $elem.html( '<div class="' + ts.css.headerIn + '">' + template + '</div>' ); // faster than wrapInner + } + if ( c.onRenderHeader ) { + c.onRenderHeader.apply( $elem, [ index, c, c.$table ] ); + } + column = parseInt( $elem.attr( 'data-column' ), 10 ); + elem.column = column; + tmp = ts.getData( $elem, configHeaders, 'sortInitialOrder' ) || c.sortInitialOrder; + // this may get updated numerous times if there are multiple rows + c.sortVars[ column ] = { + count : -1, // set to -1 because clicking on the header automatically adds one + order: ts.formatSortingOrder( tmp ) ? + [ 1, 0, 2 ] : // desc, asc, unsorted + [ 0, 1, 2 ], // asc, desc, unsorted + lockedOrder : false }; - while (j < len) { - rows = tb[j].rows; - if (rows.length) { - l = c.columns; // rows[j].cells.length; - for (i = 0; i < l; i++) { - h = c.$headerIndexed[i]; + tmp = ts.getData( $elem, configHeaders, 'lockedOrder' ) || false; + if ( typeof tmp !== 'undefined' && tmp !== false ) { + c.sortVars[ column ].lockedOrder = true; + c.sortVars[ column ].order = ts.formatSortingOrder( tmp ) ? [ 1, 1, 1 ] : [ 0, 0, 0 ]; + } + // add cell to headerList + c.headerList[ index ] = elem; + // add to parent in case there are multiple rows + $elem + .addClass( ts.css.header + ' ' + c.cssHeader ) + .parent() + .addClass( ts.css.headerRow + ' ' + c.cssHeaderRow ) + .attr( 'role', 'row' ); + // allow keyboard cursor to focus on element + if ( c.tabIndex ) { + $elem.attr( 'tabindex', 0 ); + } + return elem; + }) ); + // cache headers per column + c.$headerIndexed = []; + for ( indx = 0; indx < c.columns; indx++ ) { + // colspan in header making a column undefined + if ( ts.isEmptyObject( c.sortVars[ indx ] ) ) { + c.sortVars[ indx ] = {}; + } + $temp = c.$headers.filter( '[data-column="' + indx + '"]' ); + // target sortable column cells, unless there are none, then use non-sortable cells + // .last() added in jQuery 1.4; use .filter(':last') to maintain compatibility with jQuery v1.2.6 + c.$headerIndexed[ indx ] = $temp.length ? + $temp.not( '.sorter-false' ).length ? + $temp.not( '.sorter-false' ).filter( ':last' ) : + $temp.filter( ':last' ) : + $(); + } + c.$table.find( c.selectorHeaders ).attr({ + scope: 'col', + role : 'columnheader' + }); + // enable/disable sorting + ts.updateHeader( c ); + if ( c.debug ) { + console.log( 'Built headers:' + ts.benchmark( timer ) ); + console.log( c.$headers ); + } + }, + + // Use it to add a set of methods to table.config which will be available for all tables. + // This should be done before table initialization + addInstanceMethods : function( methods ) { + $.extend( ts.instanceMethods, methods ); + }, + + /* + █████▄ ▄████▄ █████▄ ▄█████ ██████ █████▄ ▄█████ + ██▄▄██ ██▄▄██ ██▄▄██ ▀█▄ ██▄▄ ██▄▄██ ▀█▄ + ██▀▀▀ ██▀▀██ ██▀██ ▀█▄ ██▀▀ ██▀██ ▀█▄ + ██ ██ ██ ██ ██ █████▀ ██████ ██ ██ █████▀ + */ + setupParsers : function( c, $tbodies ) { + var rows, list, span, max, colIndex, indx, header, configHeaders, + noParser, parser, extractor, time, tbody, len, + table = c.table, + tbodyIndex = 0, + debug = {}; + // update table bodies in case we start with an empty table + c.$tbodies = c.$table.children( 'tbody:not(.' + c.cssInfoBlock + ')' ); + tbody = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies; + len = tbody.length; + if ( len === 0 ) { + return c.debug ? console.warn( 'Warning: *Empty table!* Not building a parser cache' ) : ''; + } else if ( c.debug ) { + time = new Date(); + console[ console.group ? 'group' : 'log' ]( 'Detecting parsers for each column' ); + } + list = { + extractors: [], + parsers: [] + }; + while ( tbodyIndex < len ) { + rows = tbody[ tbodyIndex ].rows; + if ( rows.length ) { + colIndex = 0; + max = c.columns; + for ( indx = 0; indx < max; indx++ ) { + header = c.$headerIndexed[ colIndex ]; + if ( header && header.length ) { // get column indexed table cell - ch = ts.getColumnData( table, c.headers, i ); + configHeaders = ts.getColumnData( table, c.headers, colIndex ); // get column parser/extractor - e = ts.getParserById( ts.getData(h, ch, 'extractor') ); - p = ts.getParserById( ts.getData(h, ch, 'sorter') ); - np = ts.getData(h, ch, 'parser') === 'false'; + extractor = ts.getParserById( ts.getData( header, configHeaders, 'extractor' ) ); + parser = ts.getParserById( ts.getData( header, configHeaders, 'sorter' ) ); + noParser = ts.getData( header, configHeaders, 'parser' ) === 'false'; // empty cells behaviour - keeping emptyToBottom for backwards compatibility - c.empties[i] = ( ts.getData(h, ch, 'empty') || c.emptyTo || (c.emptyToBottom ? 'bottom' : 'top' ) ).toLowerCase(); + c.empties[colIndex] = ( + ts.getData( header, configHeaders, 'empty' ) || + c.emptyTo || ( c.emptyToBottom ? 'bottom' : 'top' ) ).toLowerCase(); // text strings behaviour in numerical sorts - c.strings[i] = ( ts.getData(h, ch, 'string') || c.stringTo || 'max' ).toLowerCase(); - if (np) { - p = ts.getParserById('no-parser'); + c.strings[colIndex] = ( + ts.getData( header, configHeaders, 'string' ) || + c.stringTo || + 'max' ).toLowerCase(); + if ( noParser ) { + parser = ts.getParserById( 'no-parser' ); } - if (!e) { + if ( !extractor ) { // For now, maybe detect someday - e = false; + extractor = false; } - if (!p) { - p = detectParserForColumn(c, rows, -1, i); + if ( !parser ) { + parser = ts.detectParserForColumn( c, rows, -1, colIndex ); } - if (c.debug) { - debug[ '(' + i + ') ' + h.text() ] = { - parser : p.id, - extractor : e ? e.id : 'none', - string : c.strings[i], - empty : c.empties[i] + if ( c.debug ) { + debug[ '(' + colIndex + ') ' + header.text() ] = { + parser : parser.id, + extractor : extractor ? extractor.id : 'none', + string : c.strings[ colIndex ], + empty : c.empties[ colIndex ] }; } - list.parsers[i] = p; - list.extractors[i] = e; - } - } - j += (list.parsers.length) ? len : 1; - } - if ( c.debug ) { - if ( !ts.isEmptyObject( debug ) ) { - console[ console.table ? 'table' : 'log' ]( debug ); - } else { - console.warn( ' No parsers detected!' ); - } - console.log( 'Completed detecting parsers' + ts.benchmark( time ) ); - if ( console.groupEnd ) { console.groupEnd(); } - } - c.parsers = list.parsers; - c.extractors = list.extractors; - } - - /* utils */ - function buildCache(table, callback, $tbodies) { - var cc, t, v, i, j, k, $tb, $row, cols, cacheTime, - totalRows, rowData, prevRowData, colMax, - c = table.config, - parsers = c.parsers; - // update tbody variable - c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'); - $tb = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies, - c.cache = {}; - c.totalRows = 0; - // if no parsers found, return - it's an empty table. - if (!parsers) { - return c.debug ? console.warn('Warning: *Empty table!* Not building a cache') : ''; - } - if (c.debug) { - cacheTime = new Date(); - } - // processing icon - if (c.showProcessing) { - ts.isProcessing(table, true); - } - for (k = 0; k < $tb.length; k++) { - colMax = []; // column max value per tbody - cc = c.cache[k] = { - normalized: [] // array of normalized row data; last entry contains 'rowData' above - // colMax: # // added at the end - }; - - totalRows = ($tb[k] && $tb[k].rows.length) || 0; - for (i = 0; i < totalRows; ++i) { - rowData = { - // order: original row order # - // $row : jQuery Object[] - child: [], // child row text (filter widget) - raw: [] // original row text - }; - /** Add the table data to main data array */ - $row = $( $tb[ k ].rows[ i ] ); - cols = []; - // if this is a child row, add it to the last row's children and continue to the next row - // ignore child row class, if it is the first row - if ( $row.hasClass( c.cssChildRow ) && i !== 0 ) { - t = cc.normalized.length - 1; - prevRowData = cc.normalized[ t ][ c.columns ]; - prevRowData.$row = prevRowData.$row.add( $row ); - // add 'hasChild' class name to parent row - if ( !$row.prev().hasClass( c.cssChildRow ) ) { - $row.prev().addClass( ts.css.cssHasChild ); - } - // save child row content (un-parsed!) - v = $row.children( 'th, td' ); - t = prevRowData.child.length; - prevRowData.child[ t ] = []; - // child row content does not account for colspans/rowspans; so indexing may be off - for ( j = 0; j < c.columns; j++ ) { - prevRowData.child[ t ][ j ] = ts.getParsedText( c, v[ j ], j ); - } - // go to the next for loop - continue; - } - rowData.$row = $row; - rowData.order = i; // add original row position to rowCache - for ( j = 0; j < c.columns; ++j ) { - if (typeof parsers[ j ] === 'undefined') { - if ( c.debug ) { - console.warn( 'No parser found for cell:', $row[ 0 ].cells[ j ], 'does it have a header?' ); - } - continue; - } - t = ts.getElementText( c, $row[ 0 ].cells[j], j ); - rowData.raw.push( t ); // save original row text - v = ts.getParsedText( c, $row[ 0 ].cells[ j ], j, t ); - cols.push( v ); - if ( ( parsers[ j ].type || '' ).toLowerCase() === 'numeric' ) { - // determine column max value (ignore sign) - colMax[ j ] = Math.max( Math.abs( v ) || 0, colMax[ j ] || 0 ); + list.parsers[ colIndex ] = parser; + list.extractors[ colIndex ] = extractor; + span = header[ 0 ].colSpan - 1; + if ( span > 0 ) { + colIndex += span; + max += span; } } - // ensure rowData is always in the same location (after the last column) - cols[ c.columns ] = rowData; - cc.normalized.push( cols ); + colIndex++; } - cc.colMax = colMax; - // total up rows, not including child rows - c.totalRows += cc.normalized.length; - } - if ( c.showProcessing ) { - ts.isProcessing( table ); // remove processing icon - } - if ( c.debug ) { - console.log( 'Building cache for ' + totalRows + ' rows' + ts.benchmark( cacheTime ) ); - } - if ( $.isFunction( callback ) ) { - callback( table ); + tbodyIndex += ( list.parsers.length ) ? len : 1; + } + if ( c.debug ) { + if ( !ts.isEmptyObject( debug ) ) { + console[ console.table ? 'table' : 'log' ]( debug ); + } else { + console.warn( ' No parsers detected!' ); } + console.log( 'Completed detecting parsers' + ts.benchmark( time ) ); + if ( console.groupEnd ) { console.groupEnd(); } } + c.parsers = list.parsers; + c.extractors = list.extractors; + }, - function formatSortingOrder(v) { - // look for 'd' in 'desc' order; return true - return (/^d/i.test(v) || v === 1); - } - - function buildHeaders( c ) { - var ch, $t, h, i, t, lock, time, indx; - c.headerList = []; - c.headerContent = []; - if (c.debug) { - time = new Date(); - } - // children tr in tfoot - see issue #196 & #547 - c.columns = ts.computeColumnIndex( c.$table.children('thead, tfoot').children('tr') ); - // add icon if cssIcon option exists - i = c.cssIcon ? '<i class="' + ( c.cssIcon === ts.css.icon ? ts.css.icon : c.cssIcon + ' ' + ts.css.icon ) + '"></i>' : ''; - // redefine c.$headers here in case of an updateAll that replaces or adds an entire header cell - see #683 - c.$headers = $( $.map( c.$table.find(c.selectorHeaders), function(elem, index) { - $t = $(elem); - // ignore cell (don't add it to c.$headers) if row has ignoreRow class - if ($t.parent().hasClass(c.cssIgnoreRow)) { return; } - // make sure to get header cell & not column indexed cell - ch = ts.getColumnData( c.table, c.headers, index, true ); - // save original header content - c.headerContent[index] = $t.html(); - // if headerTemplate is empty, don't reformat the header cell - if ( c.headerTemplate !== '' && !$t.find('.' + ts.css.headerIn).length ) { - // set up header template - t = c.headerTemplate - .replace(ts.regex.templateContent, $t.html()) - .replace(ts.regex.templateIcon, $t.find('.' + ts.css.icon).length ? '' : i); - if (c.onRenderTemplate) { - h = c.onRenderTemplate.apply( $t, [ index, t ] ); - if (h && typeof h === 'string') { t = h; } // only change t if something is returned - } - $t.html('<div class="' + ts.css.headerIn + '">' + t + '</div>'); // faster than wrapInner - } - if (c.onRenderHeader) { c.onRenderHeader.apply( $t, [ index, c, c.$table ] ); } - // *** remove this.column value if no conflicts found - elem.column = parseInt( $t.attr('data-column'), 10); - elem.order = formatSortingOrder( ts.getData( $t, ch, 'sortInitialOrder' ) || c.sortInitialOrder ) ? - [ 1, 0, 2 ] : // desc, asc, unsorted - [ 0, 1, 2 ]; // asc, desc, unsorted - elem.count = -1; // set to -1 because clicking on the header automatically adds one - elem.lockedOrder = false; - lock = ts.getData($t, ch, 'lockedOrder') || false; - if (typeof lock !== 'undefined' && lock !== false) { - elem.order = elem.lockedOrder = formatSortingOrder(lock) ? [ 1, 1, 1 ] : [ 0, 0, 0 ]; - } - $t.addClass(ts.css.header + ' ' + c.cssHeader); - // add cell to headerList - c.headerList[index] = elem; - // add to parent in case there are multiple rows - $t.parent().addClass(ts.css.headerRow + ' ' + c.cssHeaderRow).attr('role', 'row'); - // allow keyboard cursor to focus on element - if (c.tabIndex) { $t.attr('tabindex', 0); } - return elem; - })); - // cache headers per column - c.$headerIndexed = []; - for (indx = 0; indx < c.columns; indx++) { - $t = c.$headers.filter('[data-column="' + indx + '"]'); - // target sortable column cells, unless there are none, then use non-sortable cells - // .last() added in jQuery 1.4; use .filter(':last') to maintain compatibility with jQuery v1.2.6 - c.$headerIndexed[indx] = $t.not('.sorter-false').length ? $t.not('.sorter-false').filter(':last') : $t.filter(':last'); - } - c.$table.find(c.selectorHeaders).attr({ - scope: 'col', - role : 'columnheader' - }); - // enable/disable sorting - updateHeader(c.table); - if (c.debug) { - console.log( 'Built headers:' + ts.benchmark( time ) ); - console.log( c.$headers ); + addParser : function( parser ) { + var indx, + len = ts.parsers.length, + add = true; + for ( indx = 0; indx < len; indx++ ) { + if ( ts.parsers[ indx ].id.toLowerCase() === parser.id.toLowerCase() ) { + add = false; } } - - function commonUpdate(table, resort, callback) { - var c = table.config; - // remove rows/elements before update - c.$table.find(c.selectorRemove).remove(); - // rebuild parsers - buildParserCache(c); - // rebuild the cache map - buildCache(table); - checkResort(c, resort, callback); + if ( add ) { + ts.parsers.push( parser ); } + }, - function updateHeader(table) { - var index, isDisabled, $th, col, - c = table.config, - len = c.$headers.length; - for ( index = 0; index < len; index++ ) { - $th = c.$headers.eq( index ); - col = ts.getColumnData( table, c.headers, index, true ); - // add 'sorter-false' class if 'parser-false' is set - isDisabled = ts.getData( $th, col, 'sorter' ) === 'false' || ts.getData( $th, col, 'parser' ) === 'false'; - $th[0].sortDisabled = isDisabled; - $th[ isDisabled ? 'addClass' : 'removeClass' ]( 'sorter-false' ).attr( 'aria-disabled', '' + isDisabled ); - // disable tab index on disabled cells - if ( c.tabIndex ) { - if ( isDisabled ) { - $th.removeAttr( 'tabindex' ); - } else { - $th.attr( 'tabindex', '0' ); - } - } - // aria-controls - requires table ID - if (table.id) { - if ( isDisabled ) { - $th.removeAttr('aria-controls'); - } else { - $th.attr('aria-controls', table.id); - } - } + getParserById : function( name ) { + /*jshint eqeqeq:false */ + if ( name == 'false' ) { return false; } + var indx, + len = ts.parsers.length; + for ( indx = 0; indx < len; indx++ ) { + if ( ts.parsers[ indx ].id.toLowerCase() === ( name.toString() ).toLowerCase() ) { + return ts.parsers[ indx ]; } } + return false; + }, - function setHeadersCss(table) { - var f, h, i, j, $headers, $h, nextSort, txt, - c = table.config, - list = c.sortList, - len = list.length, - none = ts.css.sortNone + ' ' + c.cssNone, - css = [ ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc ], - cssIcon = [ c.cssIconAsc, c.cssIconDesc, c.cssIconNone ], - aria = [ 'ascending', 'descending' ], - // find the footer - $t = $(table).find('tfoot tr').children() - .add( $( c.namespace + '_extra_headers' ) ) - .removeClass( css.join( ' ' ) ); - // remove all header information - c.$headers - .removeClass(css.join(' ')) - .addClass(none).attr('aria-sort', 'none') - .find('.' + ts.css.icon) - .removeClass(cssIcon.join(' ')) - .addClass(cssIcon[2]); - for (i = 0; i < len; i++) { - // direction = 2 means reset! - if (list[i][1] !== 2) { - // multicolumn sorting updating - see #1005 - f = c.lastClickedIndex > 0 ? c.$headers.filter(':gt(' + ( c.lastClickedIndex - 1 ) + ')') : c.$headers; - // choose the :last in case there are nested columns - f = f.not('.sorter-false').filter('[data-column="' + list[i][0] + '"]' + (len === 1 ? ':last' : '') ); - if (f.length) { - for (j = 0; j < f.length; j++) { - if (!f[j].sortDisabled) { - f.eq(j) - .removeClass(none) - .addClass(css[list[i][1]]) - .attr('aria-sort', aria[list[i][1]]) - .find('.' + ts.css.icon) - .removeClass(cssIcon[2]) - .addClass(cssIcon[list[i][1]]); - } - } - // add sorted class to footer & extra headers, if they exist - if ($t.length) { - $t.filter('[data-column="' + list[i][0] + '"]').removeClass(none).addClass(css[list[i][1]]); - } - } + detectParserForColumn : function( c, rows, rowIndex, cellIndex ) { + var cur, $node, + indx = ts.parsers.length, + node = false, + nodeValue = '', + keepLooking = true; + while ( nodeValue === '' && keepLooking ) { + rowIndex++; + if ( rows[ rowIndex ] ) { + node = rows[ rowIndex ].cells[ cellIndex ]; + nodeValue = ts.getElementText( c, node, cellIndex ); + $node = $( node ); + if ( c.debug ) { + console.log( 'Checking if value was empty on row ' + rowIndex + ', column: ' + + cellIndex + ': "' + nodeValue + '"' ); } + } else { + keepLooking = false; } - // add verbose aria labels - len = c.$headers.length; - $headers = c.$headers.not('.sorter-false'); - for ( i = 0; i < len; i++ ) { - $h = $headers.eq( i ); - if ( $h.length ) { - h = $headers[ i ]; - nextSort = h.order[ ( h.count + 1 ) % ( c.sortReset ? 3 : 2 ) ], - txt = $.trim( $h.text() ) + ': ' + - ts.language[ $h.hasClass( ts.css.sortAsc ) ? 'sortAsc' : $h.hasClass( ts.css.sortDesc ) ? 'sortDesc' : 'sortNone' ] + - ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ]; - $h.attr( 'aria-label', txt ); - } + } + while ( --indx >= 0 ) { + cur = ts.parsers[ indx ]; + // ignore the default text parser because it will always be true + if ( cur && cur.id !== 'text' && cur.is && cur.is( nodeValue, c.table, node, $node ) ) { + return cur; } } + // nothing found, return the generic parser (text) + return ts.getParserById( 'text' ); + }, - function updateHeaderSortCount( table, list ) { - var col, dir, group, header, indx, primary, temp, val, - c = table.config, - sortList = list || c.sortList, - len = sortList.length; - c.sortList = []; - for (indx = 0; indx < len; indx++) { - val = sortList[indx]; - // ensure all sortList values are numeric - fixes #127 - col = parseInt(val[0], 10); - // prevents error if sorton array is wrong - if ( col < c.columns && c.$headerIndexed[col] ) { - // make sure header exists - header = c.$headerIndexed[col][0]; - // o.count = o.count + 1; - dir = ('' + val[1]).match(/^(1|d|s|o|n)/); - dir = dir ? dir[0] : ''; - // 0/(a)sc (default), 1/(d)esc, (s)ame, (o)pposite, (n)ext - switch (dir) { - case '1': case 'd': // descending - dir = 1; - break; - case 's': // same direction (as primary column) - // if primary sort is set to 's', make it ascending - dir = primary || 0; - break; - case 'o': - temp = header.order[(primary || 0) % (c.sortReset ? 3 : 2)]; - // opposite of primary column; but resets if primary resets - dir = temp === 0 ? 1 : temp === 1 ? 0 : 2; - break; - case 'n': - header.count = header.count + 1; - dir = header.order[(header.count) % (c.sortReset ? 3 : 2)]; - break; - default: // ascending - dir = 0; - break; - } - primary = indx === 0 ? dir : primary; - group = [ col, parseInt(dir, 10) || 0 ]; - c.sortList.push(group); - dir = $.inArray(group[1], header.order); // fixes issue #167 - header.count = dir >= 0 ? dir : group[1] % (c.sortReset ? 3 : 2); - } + getElementText : function( c, node, cellIndex ) { + if ( !node ) { return ''; } + var tmp, + extract = c.textExtraction || '', + // node could be a jquery object + // http://jsperf.com/jquery-vs-instanceof-jquery/2 + $node = node.jquery ? node : $( node ); + if ( typeof extract === 'string' ) { + // check data-attribute first when set to 'basic'; don't use node.innerText - it's really slow! + // http://www.kellegous.com/j/2013/02/27/innertext-vs-textcontent/ + if ( extract === 'basic' && typeof ( tmp = $node.attr( c.textAttribute ) ) !== 'undefined' ) { + return $.trim( tmp ); + } + return $.trim( node.textContent || $node.text() ); + } else { + if ( typeof extract === 'function' ) { + return $.trim( extract( $node[ 0 ], c.table, cellIndex ) ); + } else if ( typeof ( tmp = ts.getColumnData( c.table, extract, cellIndex ) ) === 'function' ) { + return $.trim( tmp( $node[ 0 ], c.table, cellIndex ) ); } } + // fallback + return $.trim( $node[ 0 ].textContent || $node.text() ); + }, - function getCachedSortType(parsers, i) { - return (parsers && parsers[i]) ? parsers[i].type || '' : ''; - } - - function initSort(table, cell, event){ - if (table.isUpdating) { - // let any updates complete before initializing a sort - return setTimeout(function(){ initSort(table, cell, event); }, 50); - } - var arry, indx, i, col, order, s, $header, - c = table.config, - key = !event[c.sortMultiSortKey], - $table = c.$table, - len = c.$headers.length; - // Only call sortStart if sorting is enabled - $table.trigger('sortStart', table); - // get current column sort order - cell.count = event[c.sortResetKey] ? 2 : (cell.count + 1) % (c.sortReset ? 3 : 2); - // reset all sorts on non-current column - issue #30 - if (c.sortRestart) { - indx = cell; - for ( i = 0; i < len; i++ ) { - $header = c.$headers.eq( i ); - // only reset counts on columns that weren't just clicked on and if not included in a multisort - if ( $header[0] !== indx && ( key || !$header.is('.' + ts.css.sortDesc + ',.' + ts.css.sortAsc) ) ) { - $header[0].count = -1; - } - } + // centralized function to extract/parse cell contents + getParsedText : function( c, cell, colIndex, txt ) { + if ( typeof txt === 'undefined' ) { + txt = ts.getElementText( c, cell, colIndex ); + } + // if no parser, make sure to return the txt + var val = '' + txt, + parser = c.parsers[ colIndex ], + extractor = c.extractors[ colIndex ]; + if ( parser ) { + // do extract before parsing, if there is one + if ( extractor && typeof extractor.format === 'function' ) { + txt = extractor.format( txt, c.table, cell, colIndex ); + } + // allow parsing if the string is empty, previously parsing would change it to zero, + // in case the parser needs to extract data from the table cell attributes + val = parser.id === 'no-parser' ? '' : + // make sure txt is a string (extractor may have converted it) + parser.format( '' + txt, c.table, cell, colIndex ); + if ( c.ignoreCase && typeof val === 'string' ) { + val = val.toLowerCase(); } - // get current column index - indx = parseInt( $(cell).attr('data-column'), 10 ); - // user only wants to sort on one column - if (key) { - // flush the sort list - c.sortList = []; - if (c.sortForce !== null) { - arry = c.sortForce; - for (col = 0; col < arry.length; col++) { - if (arry[col][0] !== indx) { - c.sortList.push(arry[col]); - } + } + return val; + }, + + /* + ▄████▄ ▄████▄ ▄████▄ ██ ██ ██████ + ██ ▀▀ ██▄▄██ ██ ▀▀ ██▄▄██ ██▄▄ + ██ ▄▄ ██▀▀██ ██ ▄▄ ██▀▀██ ██▀▀ + ▀████▀ ██ ██ ▀████▀ ██ ██ ██████ + */ + buildCache : function( c, callback, $tbodies ) { + var cache, val, txt, rowIndex, colIndex, tbodyIndex, $tbody, $row, + cols, $cells, cell, cacheTime, totalRows, rowData, prevRowData, + colMax, span, cacheIndex, max, len, + table = c.table, + parsers = c.parsers; + // update tbody variable + c.$tbodies = c.$table.children( 'tbody:not(.' + c.cssInfoBlock + ')' ); + $tbody = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies, + c.cache = {}; + c.totalRows = 0; + // if no parsers found, return - it's an empty table. + if ( !parsers ) { + return c.debug ? console.warn( 'Warning: *Empty table!* Not building a cache' ) : ''; + } + if ( c.debug ) { + cacheTime = new Date(); + } + // processing icon + if ( c.showProcessing ) { + ts.isProcessing( table, true ); + } + for ( tbodyIndex = 0; tbodyIndex < $tbody.length; tbodyIndex++ ) { + colMax = []; // column max value per tbody + cache = c.cache[ tbodyIndex ] = { + normalized: [] // array of normalized row data; last entry contains 'rowData' above + // colMax: # // added at the end + }; + + totalRows = ( $tbody[ tbodyIndex ] && $tbody[ tbodyIndex ].rows.length ) || 0; + for ( rowIndex = 0; rowIndex < totalRows; ++rowIndex ) { + rowData = { + // order: original row order # + // $row : jQuery Object[] + child: [], // child row text (filter widget) + raw: [] // original row text + }; + /** Add the table data to main data array */ + $row = $( $tbody[ tbodyIndex ].rows[ rowIndex ] ); + cols = []; + // if this is a child row, add it to the last row's children and continue to the next row + // ignore child row class, if it is the first row + if ( $row.hasClass( c.cssChildRow ) && rowIndex !== 0 ) { + len = cache.normalized.length - 1; + prevRowData = cache.normalized[ len ][ c.columns ]; + prevRowData.$row = prevRowData.$row.add( $row ); + // add 'hasChild' class name to parent row + if ( !$row.prev().hasClass( c.cssChildRow ) ) { + $row.prev().addClass( ts.css.cssHasChild ); } - } - // add column to sort list - order = cell.order[cell.count]; - if (order < 2) { - c.sortList.push([ indx, order ]); - // add other columns if header spans across multiple - if (cell.colSpan > 1) { - for (col = 1; col < cell.colSpan; col++) { - c.sortList.push([ indx + col, order ]); + // save child row content (un-parsed!) + $cells = $row.children( 'th, td' ); + len = prevRowData.child.length; + prevRowData.child[ len ] = []; + // child row content does not account for colspans/rowspans; so indexing may be off + cacheIndex = 0; + max = c.columns; + for ( colIndex = 0; colIndex < max; colIndex++ ) { + cell = $cells[ colIndex ]; + if ( cell ) { + prevRowData.child[ len ][ colIndex ] = ts.getParsedText( c, cell, colIndex ); + span = $cells[ colIndex ].colSpan - 1; + if ( span > 0 ) { + cacheIndex += span; + max += span; + } } + cacheIndex++; } - } - // multi column sorting - } else { - // get rid of the sortAppend before adding more - fixes issue #115 & #523 - if (c.sortAppend && c.sortList.length > 1) { - for (col = 0; col < c.sortAppend.length; col++) { - s = ts.isValueInArray(c.sortAppend[col][0], c.sortList); - if (s >= 0) { - c.sortList.splice(s, 1); + // go to the next for loop + continue; + } + rowData.$row = $row; + rowData.order = rowIndex; // add original row position to rowCache + cacheIndex = 0; + max = c.columns; + for ( colIndex = 0; colIndex < max; ++colIndex ) { + cell = $row[ 0 ].cells[ colIndex ]; + if ( typeof parsers[ cacheIndex ] === 'undefined' ) { + if ( c.debug ) { + console.warn( 'No parser found for column ' + colIndex + '; cell:', cell, 'does it have a header?' ); } - } - } - // the user has clicked on an already sorted column - if (ts.isValueInArray(indx, c.sortList) >= 0) { - // reverse the sorting direction - for (col = 0; col < c.sortList.length; col++) { - s = c.sortList[col]; - order = c.$headerIndexed[ s[0] ][0]; - if (s[0] === indx) { - // order.count seems to be incorrect when compared to cell.count - s[1] = order.order[cell.count]; - if (s[1] === 2) { - c.sortList.splice(col, 1); - order.count = -1; - } + } else if ( cell ) { + val = ts.getElementText( c, cell, cacheIndex ); + rowData.raw[ cacheIndex ] = val; // save original row text + txt = ts.getParsedText( c, cell, cacheIndex, val ); + cols[ cacheIndex ] = txt; + if ( ( parsers[ cacheIndex ].type || '' ).toLowerCase() === 'numeric' ) { + // determine column max value (ignore sign) + colMax[ cacheIndex ] = Math.max( Math.abs( txt ) || 0, colMax[ cacheIndex ] || 0 ); } - } - } else { - // add column to sort list array - order = cell.order[cell.count]; - if (order < 2) { - c.sortList.push([ indx, order ]); - // add other columns if header spans across multiple - if (cell.colSpan > 1) { - for (col = 1; col < cell.colSpan; col++) { - c.sortList.push([ indx + col, order ]); - } + // allow colSpan in tbody + span = cell.colSpan - 1; + if ( span > 0 ) { + cacheIndex += span; + max += span; } } + cacheIndex++; } + // ensure rowData is always in the same location (after the last column) + cols[ c.columns ] = rowData; + cache.normalized.push( cols ); } - if (c.sortAppend !== null) { - arry = c.sortAppend; - for (col = 0; col < arry.length; col++) { - if (arry[col][0] !== indx) { - c.sortList.push(arry[col]); + cache.colMax = colMax; + // total up rows, not including child rows + c.totalRows += cache.normalized.length; + + } + if ( c.showProcessing ) { + ts.isProcessing( table ); // remove processing icon + } + if ( c.debug ) { + console.log( 'Building cache for ' + totalRows + ' rows' + ts.benchmark( cacheTime ) ); + } + if ( $.isFunction( callback ) ) { + callback( table ); + } + }, + + getColumnText : function( table, column, callback, rowFilter ) { + table = $( table )[0]; + var tbodyIndex, rowIndex, cache, row, tbodyLen, rowLen, raw, parsed, $cell, result, + hasCallback = typeof callback === 'function', + allColumns = column === 'all', + data = { raw : [], parsed: [], $cell: [] }, + c = table.config; + if ( ts.isEmptyObject( c ) ) { + if ( c.debug ) { + console.warn( 'No cache found - aborting getColumnText function!' ); + } + } else { + tbodyLen = c.$tbodies.length; + for ( tbodyIndex = 0; tbodyIndex < tbodyLen; tbodyIndex++ ) { + cache = c.cache[ tbodyIndex ].normalized; + rowLen = cache.length; + for ( rowIndex = 0; rowIndex < rowLen; rowIndex++ ) { + row = cache[ rowIndex ]; + if ( rowFilter && !row[ c.columns ].$row.is( rowFilter ) ) { + continue; + } + result = true; + parsed = ( allColumns ) ? row.slice( 0, c.columns ) : row[ column ]; + row = row[ c.columns ]; + raw = ( allColumns ) ? row.raw : row.raw[ column ]; + $cell = ( allColumns ) ? row.$row.children() : row.$row.children().eq( column ); + if ( hasCallback ) { + result = callback({ + tbodyIndex : tbodyIndex, + rowIndex : rowIndex, + parsed : parsed, + raw : raw, + $row : row.$row, + $cell : $cell + }); + } + if ( result !== false ) { + data.parsed.push( parsed ); + data.raw.push( raw ); + data.$cell.push( $cell ); } } } - // sortBegin event triggered immediately before the sort - $table.trigger('sortBegin', table); - // setTimeout needed so the processing icon shows up - setTimeout(function(){ - // set css for headers - setHeadersCss(table); - multisort(table); - ts.appendCache( c ); - $table.trigger('sortEnd', table); - }, 1); - } - - // sort multiple columns - function multisort(table) { /*jshint loopfunc:true */ - var i, k, num, col, sortTime, colMax, - rows, order, sort, x, y, - dir = 0, - c = table.config, - cts = c.textSorter || '', - sortList = c.sortList, - l = sortList.length, - bl = c.$tbodies.length; - if (c.serverSideSorting || ts.isEmptyObject(c.cache)) { // empty table - fixes #206/#346 - return; - } - if (c.debug) { sortTime = new Date(); } - for (k = 0; k < bl; k++) { - colMax = c.cache[k].colMax; - rows = c.cache[k].normalized; - - rows.sort(function(a, b) { - // rows is undefined here in IE, so don't use it! - for (i = 0; i < l; i++) { - col = sortList[i][0]; - order = sortList[i][1]; - // sort direction, true = asc, false = desc - dir = order === 0; - - if (c.sortStable && a[col] === b[col] && l === 1) { - return a[c.columns].order - b[c.columns].order; - } + // return everything + return data; + } + }, - // fallback to natural sort since it is more robust - num = /n/i.test(getCachedSortType(c.parsers, col)); - if (num && c.strings[col]) { - // sort strings in numerical columns - if (typeof (c.string[c.strings[col]]) === 'boolean') { - num = (dir ? 1 : -1) * (c.string[c.strings[col]] ? -1 : 1); - } else { - num = (c.strings[col]) ? c.string[c.strings[col]] || 0 : 0; - } - // fall back to built-in numeric sort - // var sort = $.tablesorter['sort' + s]( a[c], b[c], dir, colMax[c], table); - sort = c.numberSorter ? c.numberSorter(a[col], b[col], dir, colMax[col], table) : - ts[ 'sortNumeric' + (dir ? 'Asc' : 'Desc') ](a[col], b[col], num, colMax[col], col, table); - } else { - // set a & b depending on sort direction - x = dir ? a : b; - y = dir ? b : a; - // text sort function - if (typeof cts === 'function') { - // custom OVERALL text sorter - sort = cts(x[col], y[col], dir, col, table); - } else if (typeof cts === 'object' && cts.hasOwnProperty(col)) { - // custom text sorter for a SPECIFIC COLUMN - sort = cts[col](x[col], y[col], dir, col, table); - } else { - // fall back to natural sort - sort = ts[ 'sortNatural' + (dir ? 'Asc' : 'Desc') ](a[col], b[col], col, table, c); - } - } - if (sort) { return sort; } + /* + ██ ██ █████▄ █████▄ ▄████▄ ██████ ██████ + ██ ██ ██▄▄██ ██ ██ ██▄▄██ ██ ██▄▄ + ██ ██ ██▀▀▀ ██ ██ ██▀▀██ ██ ██▀▀ + ▀████▀ ██ █████▀ ██ ██ ██ ██████ + */ + setHeadersCss : function( c ) { + var $sorted, header, indx, column, $header, nextSort, txt, tmp, + list = c.sortList, + len = list.length, + none = ts.css.sortNone + ' ' + c.cssNone, + css = [ ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc ], + cssIcon = [ c.cssIconAsc, c.cssIconDesc, c.cssIconNone ], + aria = [ 'ascending', 'descending' ], + // find the footer + $headers = c.$table + .find( 'tfoot tr' ) + .children() + .add( $( c.namespace + '_extra_headers' ) ) + .removeClass( css.join( ' ' ) ); + // remove all header information + c.$headers + .removeClass( css.join( ' ' ) ) + .addClass( none ) + .attr( 'aria-sort', 'none' ) + .find( '.' + ts.css.icon ) + .removeClass( cssIcon.join( ' ' ) ) + .addClass( cssIcon[ 2 ] ); + for ( indx = 0; indx < len; indx++ ) { + // direction = 2 means reset! + if ( list[ indx ][ 1 ] !== 2 ) { + // multicolumn sorting updating - see #1005 + // .not(function(){}) needs jQuery 1.4 + $sorted = c.$headers.filter( function( i, el ) { + // only include headers that are in the sortList (this includes colspans) + var include = true, + $el = $( el ), + col = parseInt( $el.attr( 'data-column' ), 10 ), + end = col + el.colSpan; + for ( ; col < end; col++ ) { + include = include ? ts.isValueInArray( col, c.sortList ) > -1 : false; } - return a[c.columns].order - b[c.columns].order; + return include; }); - } - if (c.debug) { - console.log( 'Sorting on ' + sortList.toString() + ' and dir ' + order + ' time' + ts.benchmark(sortTime) ); - } - } - function resortComplete(c, callback){ - if (c.table.isUpdating) { - c.$table.trigger('updateComplete', c.table); + // choose the :last in case there are nested columns + $sorted = $sorted + .not( '.sorter-false' ) + .filter( '[data-column="' + list[ indx ][ 0 ] + '"]' + ( len === 1 ? ':last' : '' ) ); + if ( $sorted.length ) { + for ( column = 0; column < $sorted.length; column++ ) { + if ( !$sorted[ column ].sortDisabled ) { + $sorted + .eq( column ) + .removeClass( none ) + .addClass( css[ list[ indx ][ 1 ] ] ) + .attr( 'aria-sort', aria[ list[ indx ][ 1 ] ] ) + .find( '.' + ts.css.icon ) + .removeClass( cssIcon[ 2 ] ) + .addClass( cssIcon[ list[ indx ][ 1 ] ] ); + } + } + // add sorted class to footer & extra headers, if they exist + if ( $headers.length ) { + $headers + .filter( '[data-column="' + list[ indx ][ 0 ] + '"]' ) + .removeClass( none ) + .addClass( css[ list[ indx ][ 1 ] ] ); + } + } } - if ($.isFunction(callback)) { - callback(c.table); + } + // add verbose aria labels + len = c.$headers.length; + $headers = c.$headers.not( '.sorter-false' ); + for ( indx = 0; indx < len; indx++ ) { + $header = $headers.eq( indx ); + if ( $header.length ) { + header = $headers[ indx ]; + column = parseInt( $header.attr( 'data-column' ), 10 ); + nextSort = c.sortVars[ column ].order[ ( c.sortVars[ column ].count + 1 ) % ( c.sortReset ? 3 : 2 ) ]; + tmp = $header.hasClass( ts.css.sortAsc ) ? + 'sortAsc' : + $header.hasClass( ts.css.sortDesc ) ? 'sortDesc' : 'sortNone'; + txt = $.trim( $header.text() ) + ': ' + + ts.language[ tmp ] + + ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ]; + $header.attr( 'aria-label', txt ); } } + }, - function checkResort(c, resort, callback) { - var sl = $.isArray(resort) ? resort : c.sortList, - // if no resort parameter is passed, fallback to config.resort (true by default) - resrt = typeof resort === 'undefined' ? c.resort : resort; - // don't try to resort if the table is still processing - // this will catch spamming of the updateCell method - if (resrt !== false && !c.serverSideSorting && !c.table.isProcessing) { - if (sl.length) { - c.$table.trigger('sorton', [ sl, function(){ - resortComplete(c, callback); - }, true ]); + updateHeader : function( c ) { + var index, isDisabled, $th, col, + table = c.table, + len = c.$headers.length; + for ( index = 0; index < len; index++ ) { + $th = c.$headers.eq( index ); + col = ts.getColumnData( table, c.headers, index, true ); + // add 'sorter-false' class if 'parser-false' is set + isDisabled = ts.getData( $th, col, 'sorter' ) === 'false' || ts.getData( $th, col, 'parser' ) === 'false'; + $th[ 0 ].sortDisabled = isDisabled; + $th[ isDisabled ? 'addClass' : 'removeClass' ]( 'sorter-false' ).attr( 'aria-disabled', '' + isDisabled ); + // disable tab index on disabled cells + if ( c.tabIndex ) { + if ( isDisabled ) { + $th.removeAttr( 'tabindex' ); } else { - c.$table.trigger('sortReset', [ function(){ - resortComplete(c, callback); - ts.applyWidget(c.table, false); - } ]); + $th.attr( 'tabindex', '0' ); + } + } + // aria-controls - requires table ID + if ( table.id ) { + if ( isDisabled ) { + $th.removeAttr( 'aria-controls' ); + } else { + $th.attr( 'aria-controls', table.id ); } - } else { - resortComplete(c, callback); - ts.applyWidget(c.table, false); } } + }, - function bindMethods( table ){ - var c = table.config, - $table = c.$table, - events = ( 'sortReset update updateRows updateAll updateHeaders addRows updateCell updateComplete ' + - 'sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup ' + - 'mouseleave ' ).split( ' ' ) - .join( c.namespace + ' ' ); - // apply easy methods that trigger bound events - $table - .unbind( events.replace( ts.regex.spaces, ' ' ) ) - .bind( 'sortReset' + c.namespace, function( e, callback ) { - e.stopPropagation(); - // using this.config to ensure functions are getting a non-cached version of the config - ts.sortReset( this.config, callback ); - }) - .bind( 'updateAll' + c.namespace, function( e, resort, callback ) { - e.stopPropagation(); - ts.updateAll( this.config, resort, callback ); - }) - .bind( 'update' + c.namespace + ' updateRows' + c.namespace, function( e, resort, callback ) { - e.stopPropagation(); - ts.update( this.config, resort, callback ); - }) - .bind( 'updateHeaders' + c.namespace, function( e, callback ) { - e.stopPropagation(); - ts.updateHeaders( this.config, callback ); - }) - .bind( 'updateCell' + c.namespace, function(e, cell, resort, callback ) { - e.stopPropagation(); - ts.updateCell( this.config, cell, resort, callback ); - }) - .bind( 'addRows' + c.namespace, function(e, $row, resort, callback) { - e.stopPropagation(); - ts.addRows( this.config, $row, resort, callback ); - }) - .bind( 'updateComplete' + c.namespace, function() { - table.isUpdating = false; - }) - .bind( 'sorton' + c.namespace, function( e, list, callback, init ) { - e.stopPropagation(); - ts.sortOn( this.config, list, callback, init ); - }) - .bind( 'appendCache' + c.namespace, function( e, callback, init ) { - e.stopPropagation(); - ts.appendCache( this.config, init ); - if ( $.isFunction( callback ) ) { - callback( table ); - } - }) - // $tbodies variable is used by the tbody sorting widget - .bind( 'updateCache' + c.namespace, function( e, callback, $tbodies ){ - e.stopPropagation(); - ts.updateCache( this.config, callback, $tbodies ); - }) - .bind( 'applyWidgetId' + c.namespace, function( e, id ) { - e.stopPropagation(); - ts.getWidgetById( id ).format( table, this.config, this.config.widgetOptions ); - }) - .bind( 'applyWidgets' + c.namespace, function( e, init ) { - e.stopPropagation(); - // apply widgets - ts.applyWidget( table, init ); - }) - .bind( 'refreshWidgets' + c.namespace, function( e, all, dontapply ) { - e.stopPropagation(); - ts.refreshWidgets( table, all, dontapply ); - }) - .bind( 'destroy' + c.namespace, function( e, removeClasses, callback ) { - e.stopPropagation(); - ts.destroy( table, removeClasses, callback ); - }) - .bind( 'resetToLoadState' + c.namespace, function( e ) { - e.stopPropagation(); - // remove all widgets - ts.removeWidget( table, true, false ); - // restore original settings; this clears out current settings, but does not clear - // values saved to storage. - c = $.extend( true, ts.defaults, c.originalSettings ); - table.hasInitialized = false; - // setup the entire table again - ts.setup( table, c ); - }); + updateHeaderSortCount : function( c, list ) { + var col, dir, group, indx, primary, temp, val, order, + sortList = list || c.sortList, + len = sortList.length; + c.sortList = []; + for ( indx = 0; indx < len; indx++ ) { + val = sortList[ indx ]; + // ensure all sortList values are numeric - fixes #127 + col = parseInt( val[ 0 ], 10 ); + // prevents error if sorton array is wrong + if ( col < c.columns ) { + order = c.sortVars[ col ].order; + dir = ( '' + val[ 1 ] ).match( /^(1|d|s|o|n)/ ); + dir = dir ? dir[ 0 ] : ''; + // 0/(a)sc (default), 1/(d)esc, (s)ame, (o)pposite, (n)ext + switch ( dir ) { + case '1' : case 'd' : // descending + dir = 1; + break; + case 's' : // same direction (as primary column) + // if primary sort is set to 's', make it ascending + dir = primary || 0; + break; + case 'o' : + temp = order[ ( primary || 0 ) % ( c.sortReset ? 3 : 2 ) ]; + // opposite of primary column; but resets if primary resets + dir = temp === 0 ? 1 : temp === 1 ? 0 : 2; + break; + case 'n' : + dir = order[ ( ++c.sortVars[ col ].count ) % ( c.sortReset ? 3 : 2 ) ]; + break; + default : // ascending + dir = 0; + break; + } + primary = indx === 0 ? dir : primary; + group = [ col, parseInt( dir, 10 ) || 0 ]; + c.sortList.push( group ); + dir = $.inArray( group[ 1 ], order ); // fixes issue #167 + c.sortVars[ col ].count = dir >= 0 ? dir : group[ 1 ] % ( c.sortReset ? 3 : 2 ); + } } + }, - /* public methods */ - ts.construct = function(settings) { - return this.each(function() { - var table = this, - // merge & extend config options - c = $.extend(true, {}, ts.defaults, settings, ts.instanceMethods); - // save initial settings - c.originalSettings = settings; - // create a table from data (build table widget) - if (!table.hasInitialized && ts.buildTable && this.nodeName !== 'TABLE') { - // return the table (in case the original target is the table's container) - ts.buildTable(table, c); - } else { - ts.setup(table, c); - } - }); - }; + updateAll : function( c, resort, callback ) { + var table = c.table; + table.isUpdating = true; + ts.refreshWidgets( table, true, true ); + ts.buildHeaders( c ); + ts.bindEvents( table, c.$headers, true ); + ts.bindMethods( c ); + ts.commonUpdate( c, resort, callback ); + }, - ts.setup = function(table, c) { - // if no thead or tbody, or tablesorter is already present, quit - if (!table || !table.tHead || table.tBodies.length === 0 || table.hasInitialized === true) { - if ( c.debug ) { - if ( table.hasInitialized ) { - console.warn( 'Stopping initialization. Tablesorter has already been initialized' ); + update : function( c, resort, callback ) { + var table = c.table; + table.isUpdating = true; + // update sorting (if enabled/disabled) + ts.updateHeader( c ); + ts.commonUpdate( c, resort, callback ); + }, + + // simple header update - see #989 + updateHeaders : function( c, callback ) { + c.table.isUpdating = true; + ts.buildHeaders( c ); + ts.bindEvents( c.table, c.$headers, true ); + ts.resortComplete( c, callback ); + }, + + updateCell : function( c, cell, resort, callback ) { + c.table.isUpdating = true; + c.$table.find( c.selectorRemove ).remove(); + // get position from the dom + var tmp, indx, row, icell, cache, len, + $tbodies = c.$tbodies, + $cell = $( cell ), + // update cache - format: function( s, table, cell, cellIndex ) + // no closest in jQuery v1.2.6 + tbodyIndex = $tbodies + .index( $.fn.closest ? $cell.closest( 'tbody' ) : $cell.parents( 'tbody' ).filter( ':first' ) ), + tbcache = c.cache[ tbodyIndex ], + $row = $.fn.closest ? $cell.closest( 'tr' ) : $cell.parents( 'tr' ).filter( ':first' ); + cell = $cell[ 0 ]; // in case cell is a jQuery object + // tbody may not exist if update is initialized while tbody is removed for processing + if ( $tbodies.length && tbodyIndex >= 0 ) { + row = $tbodies.eq( tbodyIndex ).find( 'tr' ).index( $row ); + cache = tbcache.normalized[ row ]; + len = $row[ 0 ].cells.length; + if ( len !== c.columns ) { + // colspan in here somewhere! + icell = 0; + tmp = false; + for ( indx = 0; indx < len; indx++ ) { + if ( !tmp && $row[ 0 ].cells[ indx ] !== cell ) { + icell += $row[ 0 ].cells[ indx ].colSpan; } else { - console.error( 'Stopping initialization! No table, thead or tbody' ); + tmp = true; } } - return; - } - - var k = '', - $table = $(table), - m = $.metadata; - // initialization flag - table.hasInitialized = false; - // table is being processed flag - table.isProcessing = true; - // make sure to store the config object - table.config = c; - // save the settings where they read - $.data(table, 'tablesorter', c); - if (c.debug) { - console[ console.group ? 'group' : 'log' ]( 'Initializing tablesorter' ); - $.data( table, 'startoveralltimer', new Date()); - } - - // removing this in version 3 (only supports jQuery 1.7+) - c.supportsDataObject = (function(version) { - version[0] = parseInt(version[0], 10); - return (version[0] > 1) || (version[0] === 1 && parseInt(version[1], 10) >= 4); - })($.fn.jquery.split('.')); - // digit sort text location; keeping max+/- for backwards compatibility - c.string = { 'max': 1, 'min': -1, 'emptymin': 1, 'emptymax': -1, 'zero': 0, 'none': 0, 'null': 0, 'top': true, 'bottom': false }; - // ensure case insensitivity - c.emptyTo = c.emptyTo.toLowerCase(); - c.stringTo = c.stringTo.toLowerCase(); - // add table theme class only if there isn't already one there - if (!/tablesorter\-/.test($table.attr('class'))) { - k = (c.theme !== '' ? ' tablesorter-' + c.theme : ''); - } - c.table = table; - c.$table = $table - .addClass(ts.css.table + ' ' + c.tableClass + k) - .attr('role', 'grid'); - c.$headers = $table.find(c.selectorHeaders); - - // give the table a unique id, which will be used in namespace binding - if (!c.namespace) { - c.namespace = '.tablesorter' + Math.random().toString(16).slice(2); } else { - // make sure namespace starts with a period & doesn't have weird characters - c.namespace = '.' + c.namespace.replace(ts.regex.nonWord, ''); + icell = $cell.index(); } - - c.$table.children().children('tr').attr('role', 'row'); - c.$tbodies = $table.children('tbody:not(.' + c.cssInfoBlock + ')').attr({ - 'aria-live' : 'polite', - 'aria-relevant' : 'all' - }); - if (c.$table.children('caption').length) { - k = c.$table.children('caption')[0]; - if (!k.id) { k.id = c.namespace.slice(1) + 'caption'; } - c.$table.attr('aria-labelledby', k.id); - } - c.widgetInit = {}; // keep a list of initialized widgets - // change textExtraction via data-attribute - c.textExtraction = c.$table.attr('data-text-extraction') || c.textExtraction || 'basic'; - // build headers - buildHeaders( c ); - // fixate columns if the users supplies the fixedWidth option - // do this after theme has been applied - ts.fixColumnWidth(table); - // add widget options before parsing (e.g. grouping widget has parser settings) - ts.applyWidgetOptions(table, c); - // try to auto detect column type, and store in tables config - buildParserCache(c); - // start total row count at zero - c.totalRows = 0; - // build the cache for the tbody cells - // delayInit will delay building the cache until the user starts a sort - if (!c.delayInit) { buildCache(table); } - // bind all header events and methods - ts.bindEvents(table, c.$headers, true); - bindMethods(table); - // get sort list from jQuery data or metadata - // in jQuery < 1.4, an error occurs when calling $table.data() - if (c.supportsDataObject && typeof $table.data().sortlist !== 'undefined') { - c.sortList = $table.data().sortlist; - } else if (m && ($table.metadata() && $table.metadata().sortlist)) { - c.sortList = $table.metadata().sortlist; - } - // apply widget init code - ts.applyWidget(table, true); - // if user has supplied a sort list to constructor - if (c.sortList.length > 0) { - $table.trigger('sorton', [ c.sortList, {}, !c.initWidgets, true ]); + tmp = ts.getElementText( c, cell, icell ); // raw + cache[ c.columns ].raw[ icell ] = tmp; + tmp = ts.getParsedText( c, cell, icell, tmp ); + cache[ icell ] = tmp; // parsed + cache[ c.columns ].$row = $row; + if ( ( c.parsers[ icell ].type || '' ).toLowerCase() === 'numeric' ) { + // update column max value (ignore sign) + tbcache.colMax[ icell ] = Math.max( Math.abs( tmp ) || 0, tbcache.colMax[ icell ] || 0 ); + } + tmp = resort !== 'undefined' ? resort : c.resort; + if ( tmp !== false ) { + // widgets will be reapplied + ts.checkResort( c, tmp, callback ); } else { - setHeadersCss(table); - if (c.initWidgets) { - // apply widget format - ts.applyWidget(table, false); - } - } - - // show processesing icon - if (c.showProcessing) { - $table - .unbind('sortBegin' + c.namespace + ' sortEnd' + c.namespace) - .bind('sortBegin' + c.namespace + ' sortEnd' + c.namespace, function(e) { - clearTimeout(c.processTimer); - ts.isProcessing(table); - if (e.type === 'sortBegin') { - c.processTimer = setTimeout(function(){ - ts.isProcessing(table, true); - }, 500); - } - }); - } - - // initialized - table.hasInitialized = true; - table.isProcessing = false; - if (c.debug) { - console.log( 'Overall initialization time: ' + ts.benchmark( $.data( table, 'startoveralltimer') ) ); - if ( c.debug && console.groupEnd ) { console.groupEnd(); } - } - $table.trigger('tablesorter-initialized', table); - if (typeof c.initialized === 'function') { c.initialized(table); } - }; - - // automatically add a colgroup with col elements set to a percentage width - ts.fixColumnWidth = function(table) { - table = $(table)[0]; - var overallWidth, percent, $tbodies, len, index, - c = table.config, - $colgroup = c.$table.children('colgroup'); - // remove plugin-added colgroup, in case we need to refresh the widths - if ($colgroup.length && $colgroup.hasClass(ts.css.colgroup)) { - $colgroup.remove(); - } - if (c.widthFixed && c.$table.children('colgroup').length === 0) { - $colgroup = $('<colgroup class="' + ts.css.colgroup + '">'); - overallWidth = c.$table.width(); - // only add col for visible columns - fixes #371 - $tbodies = c.$tbodies.find('tr:first').children(':visible'); // .each(function() - len = $tbodies.length; - for ( index = 0; index < len; index++ ) { - percent = parseInt( ( $tbodies.eq( index ).width() / overallWidth ) * 1000, 10 ) / 10 + '%'; - $colgroup.append( $('<col>').css('width', percent) ); - } - c.$table.prepend($colgroup); + // don't reapply widgets is resort is false, just in case it causes + // problems with element focus + ts.resortComplete( c, callback ); } - }; + } + }, - ts.getColumnData = function(table, obj, indx, getCell, $headers){ - if (typeof obj === 'undefined' || obj === null) { return; } - table = $(table)[0]; - var $h, k, - c = table.config, - $cells = ( $headers || c.$headers ), - // c.$headerIndexed is not defined initially - $cell = c.$headerIndexed && c.$headerIndexed[indx] || $cells.filter('[data-column="' + indx + '"]:last'); - if (obj[indx]) { - return getCell ? obj[indx] : obj[$cells.index( $cell )]; - } - for (k in obj) { - if (typeof k === 'string') { - $h = $cell - // header cell with class/id - .filter(k) - // find elements within the header cell with cell/id - .add( $cell.find(k) ); - if ($h.length) { - return obj[k]; - } - } + addRows : function( c, $row, resort, callback ) { + var txt, val, tbodyIndex, rowIndex, rows, cellIndex, len, + cacheIndex, rowData, cells, cell, span, + // allow passing a row string if only one non-info tbody exists in the table + valid = typeof $row === 'string' && c.$tbodies.length === 1 && /<tr/.test( $row || '' ), + table = c.table; + if ( valid ) { + $row = $( $row ); + c.$tbodies.append( $row ); + } else if ( !$row || + // row is a jQuery object? + !( $row instanceof jQuery ) || + // row contained in the table? + ( $.fn.closest ? $row.closest( 'table' )[ 0 ] : $row.parents( 'table' )[ 0 ] ) !== c.table ) { + if ( c.debug ) { + console.error( 'addRows method requires (1) a jQuery selector reference to rows that have already ' + + 'been added to the table, or (2) row HTML string to be added to a table with only one tbody' ); } - return; - }; - - // computeTableHeaderCellIndexes from: - // http://www.javascripttoolbox.com/lib/table/examples.php - // http://www.javascripttoolbox.com/temp/table_cellindex.html - ts.computeColumnIndex = function(trs) { - var i, j, k, l, $cell, cell, cells, rowIndex, cellId, rowSpan, colSpan, firstAvailCol, - matrix = [], - matrixrow = [], - lookup = {}; - for (i = 0; i < trs.length; i++) { - cells = trs[i].cells; - for (j = 0; j < cells.length; j++) { - cell = cells[j]; - $cell = $(cell); - rowIndex = cell.parentNode.rowIndex; - cellId = rowIndex + '-' + $cell.index(); - rowSpan = cell.rowSpan || 1; - colSpan = cell.colSpan || 1; - if (typeof matrix[rowIndex] === 'undefined') { - matrix[rowIndex] = []; - } - // Find first available column in the first row - for (k = 0; k < matrix[rowIndex].length + 1; k++) { - if (typeof matrix[rowIndex][k] === 'undefined') { - firstAvailCol = k; - break; - } + return false; + } + table.isUpdating = true; + if ( ts.isEmptyObject( c.cache ) ) { + // empty table, do an update instead - fixes #450 + ts.updateHeader( c ); + ts.commonUpdate( c, resort, callback ); + } else { + rows = $row.filter( 'tr' ).attr( 'role', 'row' ).length; + tbodyIndex = c.$tbodies.index( $row.parents( 'tbody' ).filter( ':first' ) ); + // fixes adding rows to an empty table - see issue #179 + if ( !( c.parsers && c.parsers.length ) ) { + ts.setupParsers( c ); + } + // add each row + for ( rowIndex = 0; rowIndex < rows; rowIndex++ ) { + cacheIndex = 0; + len = $row[ rowIndex ].cells.length; + cells = []; + rowData = { + child : [], + raw : [], + $row : $row.eq( rowIndex ), + order : c.cache[ tbodyIndex ].normalized.length + }; + // add each cell + for ( cellIndex = 0; cellIndex < len; cellIndex++ ) { + cell = $row[ rowIndex ].cells[ cellIndex ]; + txt = ts.getElementText( c, cell, cacheIndex ); + rowData.raw[ cacheIndex ] = txt; + val = ts.getParsedText( c, cell, cacheIndex, txt ); + cells[ cacheIndex ] = val; + if ( ( c.parsers[ cacheIndex ].type || '' ).toLowerCase() === 'numeric' ) { + // update column max value (ignore sign) + c.cache[ tbodyIndex ].colMax[ cacheIndex ] = + Math.max( Math.abs( val ) || 0, c.cache[ tbodyIndex ].colMax[ cacheIndex ] || 0 ); } - lookup[cellId] = firstAvailCol; - // add data-column - $cell.attr({ 'data-column' : firstAvailCol }); // 'data-row' : rowIndex - for (k = rowIndex; k < rowIndex + rowSpan; k++) { - if (typeof matrix[k] === 'undefined') { - matrix[k] = []; - } - matrixrow = matrix[k]; - for (l = firstAvailCol; l < firstAvailCol + colSpan; l++) { - matrixrow[l] = 'x'; - } + span = cell.colSpan - 1; + if ( span > 0 ) { + cacheIndex += span; } + cacheIndex++; } + // add the row data to the end + cells[ c.columns ] = rowData; + // update cache + c.cache[ tbodyIndex ].normalized.push( cells ); } - return matrixrow.length; - }; - - // *** Process table *** - // add processing indicator - ts.isProcessing = function( $table, toggle, $ths ) { - $table = $( $table ); - var c = $table[0].config, - // default to all headers - $h = $ths || $table.find('.' + ts.css.header); - if (toggle) { - // don't use sortList if custom $ths used - if (typeof $ths !== 'undefined' && c.sortList.length > 0) { - // get headers from the sortList - $h = $h.filter(function(){ - // get data-column from attr to keep compatibility with jQuery 1.2.6 - return this.sortDisabled ? false : ts.isValueInArray( parseFloat($(this).attr('data-column')), c.sortList) >= 0; - }); - } - $table.add($h).addClass(ts.css.processing + ' ' + c.cssProcessing); - } else { - $table.add($h).removeClass(ts.css.processing + ' ' + c.cssProcessing); - } - }; - - // detach tbody but save the position - // don't use tbody because there are portions that look for a tbody index (updateCell) - ts.processTbody = function(table, $tb, getIt){ - table = $(table)[0]; - var holdr; - if (getIt) { - table.isProcessing = true; - $tb.before('<colgroup class="tablesorter-savemyplace"/>'); - holdr = ($.fn.detach) ? $tb.detach() : $tb.remove(); - return holdr; - } - holdr = $(table).find('colgroup.tablesorter-savemyplace'); - $tb.insertAfter( holdr ); - holdr.remove(); - table.isProcessing = false; - }; + // resort using current settings + ts.checkResort( c, resort, callback ); + } + }, - ts.clearTableBody = function(table) { - $(table)[0].config.$tbodies.children().detach(); - }; + updateCache : function( c, callback, $tbodies ) { + // rebuild parsers + if ( !( c.parsers && c.parsers.length ) ) { + ts.setupParsers( c, $tbodies ); + } + // rebuild the cache map + ts.buildCache( c, callback, $tbodies ); + }, - ts.bindEvents = function(table, $headers, core) { - table = $(table)[0]; - var t, downTarget = null, - c = table.config; - if (core !== true) { - $headers.addClass( c.namespace.slice(1) + '_extra_headers' ); - t = $.fn.closest ? $headers.closest('table')[0] : $headers.parents('table')[0]; - if (t && t.nodeName === 'TABLE' && t !== table) { - $(t).addClass( c.namespace.slice(1) + '_extra_table' ); - } - } - t = ( c.pointerDown + ' ' + c.pointerUp + ' ' + c.pointerClick + ' sort keyup ' ) - .replace(ts.regex.spaces, ' ') - .split(' ') - .join(c.namespace + ' '); - // apply event handling to headers and/or additional headers (stickyheaders, scroller, etc) - $headers - // http://stackoverflow.com/questions/5312849/jquery-find-self; - .find(c.selectorSort).add( $headers.filter(c.selectorSort) ) - .unbind(t) - .bind(t, function(e, external) { - var $cell, cell, temp, - $target = $(e.target), - // wrap event type in spaces, so the match doesn't trigger on inner words - type = ' ' + e.type + ' '; - // only recognize left clicks - if ( ( ( e.which || e.button ) !== 1 && !type.match( ' ' + c.pointerClick + ' | sort | keyup ' ) ) || - // allow pressing enter - ( type === ' keyup ' && e.which !== 13 ) || - // allow triggering a click event (e.which is undefined) & ignore physical clicks - ( type.match(' ' + c.pointerClick + ' ') && typeof e.which !== 'undefined' ) ) { - return; - } - // ignore mouseup if mousedown wasn't on the same target - if ( type.match(' ' + c.pointerUp + ' ') && downTarget !== e.target && external !== true ) { return; } - // set target on mousedown - if ( type.match(' ' + c.pointerDown + ' ') ) { - downTarget = e.target; - // preventDefault needed or jQuery v1.3.2 and older throws an - // "Uncaught TypeError: handler.apply is not a function" error - temp = $target.jquery.split( '.' ); - if ( temp[0] === '1' && temp[1] < 4 ) { e.preventDefault(); } - return; - } - downTarget = null; - // prevent sort being triggered on form elements - if ( ts.regex.formElements.test(e.target.nodeName) || - // nosort class name, or elements within a nosort container - $target.hasClass(c.cssNoSort) || $target.parents('.' + c.cssNoSort).length > 0 || - // elements within a button - $target.parents('button').length > 0 ) { - return !c.cancelSelection; - } - if (c.delayInit && ts.isEmptyObject(c.cache)) { buildCache(table); } - // jQuery v1.2.6 doesn't have closest() - $cell = $.fn.closest ? $(this).closest('th, td') : /TH|TD/.test(this.nodeName) ? $(this) : $(this).parents('th, td'); - // reference original table headers and find the same cell - // don't use $headers or IE8 throws an error - see #987 - temp = $headers.index( $cell ); - c.lastClickedIndex = ( temp < 0 ) ? $cell.attr('data-column') : temp; - // use column index if $headers is undefined - cell = c.$headers[ c.lastClickedIndex ]; - if (cell && !cell.sortDisabled) { - initSort(table, cell, e); + // init flag (true) used by pager plugin to prevent widget application + // renamed from appendToTable + appendCache : function( c, init ) { + var parsed, totalRows, $tbody, $curTbody, rowIndex, tbodyIndex, appendTime, + table = c.table, + wo = c.widgetOptions, + $tbodies = c.$tbodies, + rows = [], + cache = c.cache; + // empty table - fixes #206/#346 + if ( ts.isEmptyObject( cache ) ) { + // run pager appender in case the table was just emptied + return c.appender ? c.appender( table, rows ) : + table.isUpdating ? c.$table.trigger( 'updateComplete', table ) : ''; // Fixes #532 + } + if ( c.debug ) { + appendTime = new Date(); + } + for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = $tbodies.eq( tbodyIndex ); + if ( $tbody.length ) { + // detach tbody for manipulation + $curTbody = ts.processTbody( table, $tbody, true ); + parsed = cache[ tbodyIndex ].normalized; + totalRows = parsed.length; + for ( rowIndex = 0; rowIndex < totalRows; rowIndex++ ) { + rows.push( parsed[ rowIndex ][ c.columns ].$row ); + // removeRows used by the pager plugin; don't render if using ajax - fixes #411 + if ( !c.appender || ( c.pager && ( !c.pager.removeRows || !wo.pager_removeRows ) && !c.pager.ajax ) ) { + $curTbody.append( parsed[ rowIndex ][ c.columns ].$row ); + } } - }); - if (c.cancelSelection) { - // cancel selection - $headers - .attr('unselectable', 'on') - .bind('selectstart', false) - .css({ - 'user-select': 'none', - 'MozUserSelect': 'none' // not needed for jQuery 1.8+ - }); - } - }; - - ts.sortReset = function( c, callback ) { - var table = c.table; - c.sortList = []; - setHeadersCss( table ); - multisort( table ); - ts.appendCache( c ); - if ( $.isFunction( callback ) ) { - callback( table ); + // restore tbody + ts.processTbody( table, $curTbody, false ); } - }; - - ts.updateAll = function( c, resort, callback ) { - var table = c.table; - table.isUpdating = true; - ts.refreshWidgets( table, true, true ); - buildHeaders( c ); - ts.bindEvents( table, c.$headers, true ); - bindMethods( table); - commonUpdate( table, resort, callback ); - }; - - ts.update = function( c, resort, callback ) { - var table = c.table; - table.isUpdating = true; - // update sorting (if enabled/disabled) - updateHeader( table ); - commonUpdate( table, resort, callback ); - }; + } + if ( c.appender ) { + c.appender( table, rows ); + } + if ( c.debug ) { + console.log( 'Rebuilt table' + ts.benchmark( appendTime ) ); + } + // apply table widgets; but not before ajax completes + if ( !init && !c.appender ) { + ts.applyWidget( table ); + } + if ( table.isUpdating ) { + c.$table.trigger( 'updateComplete', table ); + } + }, - // simple header update - see #989 - ts.updateHeaders = function( c, callback ) { - c.table.isUpdating = true; - buildHeaders( c ); - ts.bindEvents( c.table, c.$headers, true ); - resortComplete( c, callback ); - }; + commonUpdate : function( c, resort, callback ) { + // remove rows/elements before update + c.$table.find( c.selectorRemove ).remove(); + // rebuild parsers + ts.setupParsers( c ); + // rebuild the cache map + ts.buildCache( c ); + ts.checkResort( c, resort, callback ); + }, - ts.updateCell = function( c, cell, resort, callback ) { - c.table.isUpdating = true; - c.$table.find( c.selectorRemove ).remove(); - // get position from the dom - var t, row, icell, cache, + /* + ▄█████ ▄████▄ █████▄ ██████ ██ █████▄ ▄████▄ + ▀█▄ ██ ██ ██▄▄██ ██ ██ ██ ██ ██ ▄▄▄ + ▀█▄ ██ ██ ██▀██ ██ ██ ██ ██ ██ ▀██ + █████▀ ▀████▀ ██ ██ ██ ██ ██ ██ ▀████▀ + */ + initSort : function( c, cell, event ) { + if ( c.table.isUpdating ) { + // let any updates complete before initializing a sort + return setTimeout( function(){ + ts.initSort( c, cell, event ); + }, 50 ); + } + var arry, indx, headerIndx, dir, temp, tmp, $header, + notMultiSort = !event[ c.sortMultiSortKey ], table = c.table, - $tb = c.$tbodies, - $cell = $(cell), - // update cache - format: function(s, table, cell, cellIndex) - // no closest in jQuery v1.2.6 - tbdy = $tb.index( $(cell).closest('tbody') ),$row = $(cell).closest('tr'); - tbdy = $tb.index( $.fn.closest ? $cell.closest( 'tbody' ) : $cell.parents( 'tbody' ).filter( ':first' ) ), - tbcache = c.cache[ tbdy ], - $row = $.fn.closest ? $cell.closest( 'tr' ) : $cell.parents( 'tr' ).filter( ':first' ); - cell = $cell[ 0 ]; // in case cell is a jQuery object - // tbody may not exist if update is initialized while tbody is removed for processing - if ( $tb.length && tbdy >= 0 ) { - row = $tb.eq( tbdy ).find( 'tr' ).index( $row ); - cache = tbcache.normalized[ row ]; - icell = $cell.index(); - t = ts.getParsedText( c, cell, icell ); - cache[ icell ] = t; - cache[ c.columns ].$row = $row; - if ( ( c.parsers[ icell ].type || '' ).toLowerCase() === 'numeric' ) { - // update column max value (ignore sign) - tbcache.colMax[ icell ] = Math.max( Math.abs( t ) || 0, tbcache.colMax[ icell ] || 0 ); - } - t = resort !== 'undefined' ? resort : c.resort; - if ( t !== false ) { - // widgets will be reapplied - checkResort( c, t, callback ); - } else { - // don't reapply widgets is resort is false, just in case it causes - // problems with element focus - resortComplete( c, callback ); + len = c.$headers.length, + // get current column index + col = parseInt( $( cell ).attr( 'data-column' ), 10 ), + order = c.sortVars[ col ].order; + + // Only call sortStart if sorting is enabled + c.$table.trigger( 'sortStart', table ); + // get current column sort order + c.sortVars[ col ].count = + event[ c.sortResetKey ] ? 2 : ( c.sortVars[ col ].count + 1 ) % ( c.sortReset ? 3 : 2 ); + // reset all sorts on non-current column - issue #30 + if ( c.sortRestart ) { + tmp = cell; + for ( headerIndx = 0; headerIndx < len; headerIndx++ ) { + $header = c.$headers.eq( headerIndx ); + // only reset counts on columns that weren't just clicked on and if not included in a multisort + if ( $header[ 0 ] !== tmp && + ( notMultiSort || !$header.is( '.' + ts.css.sortDesc + ',.' + ts.css.sortAsc ) ) ) { + c.sortVars[ col ].count = -1; } } - }; - - ts.addRows = function( c, $row, resort, callback ) { - var i, j, l, rowData, cells, rows, tbdy, - // allow passing a row string if only one non-info tbody exists in the table - valid = typeof $row === 'string' && c.$tbodies.length === 1 && /<tr/.test( $row || '' ), - table = c.table; - if ( valid ) { - $row = $( $row ); - c.$tbodies.append( $row ); - } else if ( !$row || - // row is a jQuery object? - !( $row instanceof jQuery ) || - // row contained in the table? - ( $.fn.closest ? $row.closest( 'table' )[ 0 ] : $row.parents( 'table' )[ 0 ] ) !== c.table ) { - if ( c.debug ) { - console.error( 'addRows method requires (1) a jQuery selector reference to rows that have already ' + - 'been added to the table, or (2) row HTML string to be added to a table with only one tbody' ); + } + // user only wants to sort on one column + if ( notMultiSort ) { + // flush the sort list + c.sortList = []; + c.last.sortList = []; + if ( c.sortForce !== null ) { + arry = c.sortForce; + for ( indx = 0; indx < arry.length; indx++ ) { + if ( arry[ indx ][ 0 ] !== col ) { + c.sortList.push( arry[ indx ] ); + } } - return false; } - table.isUpdating = true; - if ( ts.isEmptyObject( c.cache ) ) { - // empty table, do an update instead - fixes #450 - updateHeader( table ); - commonUpdate( table, resort, callback ); - } else { - rows = $row.filter( 'tr' ).attr( 'role', 'row' ).length; - tbdy = c.$tbodies.index( $row.parents( 'tbody' ).filter( ':first' ) ); - // fixes adding rows to an empty table - see issue #179 - if ( !( c.parsers && c.parsers.length ) ) { - buildParserCache( c ); + // add column to sort list + dir = order[ c.sortVars[ col ].count ]; + if ( dir < 2 ) { + c.sortList.push( [ col, dir ] ); + // add other columns if header spans across multiple + if ( cell.colSpan > 1 ) { + for ( indx = 1; indx < cell.colSpan; indx++ ) { + c.sortList.push( [ col + indx, dir ] ); + // update count on columns in colSpan + c.sortVars[ col + indx ].count = $.inArray( dir, order ); + } } - // add each row - for ( i = 0; i < rows; i++ ) { - l = $row[ i ].cells.length; - cells = []; - rowData = { - child: [], - $row : $row.eq( i ), - order: c.cache[ tbdy ].normalized.length - }; - // add each cell - for ( j = 0; j < l; j++ ) { - cells[ j ] = ts.getParsedText( c, $row[ i ].cells[ j ], j ); - if ( ( c.parsers[ j ].type || '' ).toLowerCase() === 'numeric' ) { - // update column max value (ignore sign) - c.cache[ tbdy ].colMax[ j ] = Math.max( Math.abs( cells[ j ] ) || 0, c.cache[ tbdy ].colMax[ j ] || 0 ); + } + // multi column sorting + } else { + // get rid of the sortAppend before adding more - fixes issue #115 & #523 + c.sortList = $.extend( [], c.last.sortList ); + + // the user has clicked on an already sorted column + if ( ts.isValueInArray( col, c.sortList ) >= 0 ) { + // reverse the sorting direction + for ( indx = 0; indx < c.sortList.length; indx++ ) { + tmp = c.sortList[ indx ]; + if ( tmp[ 0 ] === col ) { + // order.count seems to be incorrect when compared to cell.count + tmp[ 1 ] = order[ c.sortVars[ col ].count ]; + if ( tmp[1] === 2 ) { + c.sortList.splice( indx, 1 ); + c.sortVars[ col ].count = -1; } } - // add the row data to the end - cells.push( rowData ); - // update cache - c.cache[ tbdy ].normalized.push( cells ); } - // resort using current settings - checkResort( c, resort, callback ); - } - }; - - ts.updateCache = function( c, callback, $tbodies ) { - // rebuild parsers - if ( !( c.parsers && c.parsers.length ) ) { - buildParserCache( c, $tbodies ); - } - // rebuild the cache map - buildCache( c.table, callback, $tbodies ); - }; - - // init flag (true) used by pager plugin to prevent widget application - // renamed from appendToTable - ts.appendCache = function( c, init ) { - var n, totalRows, $bk, $tb, i, k, appendTime, - table = c.table, - wo = c.widgetOptions, - $tbodies = c.$tbodies, - rows = [], - cc = c.cache; - // empty table - fixes #206/#346 - if ( ts.isEmptyObject( cc ) ) { - // run pager appender in case the table was just emptied - return c.appender ? c.appender( table, rows ) : - table.isUpdating ? c.$table.trigger( 'updateComplete', table ) : ''; // Fixes #532 - } - if ( c.debug ) { - appendTime = new Date(); - } - for ( k = 0; k < $tbodies.length; k++ ) { - $bk = $tbodies.eq( k ); - if ( $bk.length ) { - // get tbody - $tb = ts.processTbody( table, $bk, true ); - n = cc[ k ].normalized; - totalRows = n.length; - for ( i = 0; i < totalRows; i++ ) { - rows.push( n[ i ][ c.columns ].$row ); - // removeRows used by the pager plugin; don't render if using ajax - fixes #411 - if ( !c.appender || ( c.pager && ( !c.pager.removeRows || !wo.pager_removeRows ) && !c.pager.ajax ) ) { - $tb.append( n[ i ][ c.columns ].$row ); + } else { + // add column to sort list array + dir = order[ c.sortVars[ col ].count ]; + if ( dir < 2 ) { + c.sortList.push( [ col, dir ] ); + // add other columns if header spans across multiple + if ( cell.colSpan > 1 ) { + for ( indx = 1; indx < cell.colSpan; indx++ ) { + c.sortList.push( [ col + indx, dir ] ); + // update count on columns in colSpan + c.sortVars[ col + indx ].count = $.inArray( dir, order ); } } - // restore tbody - ts.processTbody( table, $tb, false ); } } - if ( c.appender ) { - c.appender( table, rows ); - } - if ( c.debug ) { - console.log( 'Rebuilt table' + ts.benchmark( appendTime ) ); - } - // apply table widgets; but not before ajax completes - if ( !init && !c.appender ) { ts.applyWidget( table ); } - if ( table.isUpdating ) { - c.$table.trigger( 'updateComplete', table ); + } + // save sort before applying sortAppend + c.last.sortList = $.extend( [], c.sortList ); + if ( c.sortList.length && c.sortAppend ) { + arry = $.isArray( c.sortAppend ) ? c.sortAppend : c.sortAppend[ c.sortList[ 0 ][ 0 ] ]; + if ( !ts.isEmptyObject( arry ) ) { + for ( indx = 0; indx < arry.length; indx++ ) { + if ( arry[ indx ][ 0 ] !== col && ts.isValueInArray( arry[ indx ][ 0 ], c.sortList ) < 0 ) { + dir = arry[ indx ][ 1 ]; + temp = ( '' + dir ).match( /^(a|d|s|o|n)/ ); + if ( temp ) { + tmp = c.sortList[ 0 ][ 1 ]; + switch ( temp[ 0 ] ) { + case 'd' : + dir = 1; + break; + case 's' : + dir = tmp; + break; + case 'o' : + dir = tmp === 0 ? 1 : 0; + break; + case 'n' : + dir = ( tmp + 1 ) % ( c.sortReset ? 3 : 2 ); + break; + default: + dir = 0; + break; + } + } + c.sortList.push( [ arry[ indx ][ 0 ], dir ] ); + } + } } - }; - - ts.sortOn = function( c, list, callback, init ) { - var table = c.table; - c.$table.trigger( 'sortStart', table ); - // update header count index - updateHeaderSortCount( table, list ); + } + // sortBegin event triggered immediately before the sort + c.$table.trigger( 'sortBegin', table ); + // setTimeout needed so the processing icon shows up + setTimeout( function() { // set css for headers - setHeadersCss( table ); - // fixes #346 - if ( c.delayInit && ts.isEmptyObject( c.cache ) ) { - buildCache( table ); - } - c.$table.trigger( 'sortBegin', table ); - // sort the table and append it to the dom - multisort( table ); - ts.appendCache( c, init ); + ts.setHeadersCss( c ); + ts.multisort( c ); + ts.appendCache( c ); c.$table.trigger( 'sortEnd', table ); - ts.applyWidget( table ); - if ( $.isFunction( callback ) ) { - callback( table ); - } - }; + }, 1 ); + }, - // restore headers - ts.restoreHeaders = function(table){ - var index, $cell, - c = $(table)[0].config, - $headers = c.$table.find( c.selectorHeaders ), - len = $headers.length; - // don't use c.$headers here in case header cells were swapped - for ( index = 0; index < len; index++ ) { - // c.$table.find(c.selectorHeaders).each(function(i){ - $cell = $headers.eq( index ); - // only restore header cells if it is wrapped - // because this is also used by the updateAll method - if ( $cell.find( '.' + ts.css.headerIn ).length ) { - $cell.html( c.headerContent[ index ] ); + // sort multiple columns + multisort : function( c ) { /*jshint loopfunc:true */ + var tbodyIndex, sortTime, colMax, rows, + table = c.table, + dir = 0, + textSorter = c.textSorter || '', + sortList = c.sortList, + sortLen = sortList.length, + len = c.$tbodies.length; + if ( c.serverSideSorting || ts.isEmptyObject( c.cache ) ) { + // empty table - fixes #206/#346 + return; + } + if ( c.debug ) { sortTime = new Date(); } + for ( tbodyIndex = 0; tbodyIndex < len; tbodyIndex++ ) { + colMax = c.cache[ tbodyIndex ].colMax; + rows = c.cache[ tbodyIndex ].normalized; + + rows.sort( function( a, b ) { + var sortIndex, num, col, order, sort, x, y; + // rows is undefined here in IE, so don't use it! + for ( sortIndex = 0; sortIndex < sortLen; sortIndex++ ) { + col = sortList[ sortIndex ][ 0 ]; + order = sortList[ sortIndex ][ 1 ]; + // sort direction, true = asc, false = desc + dir = order === 0; + + if ( c.sortStable && a[ col ] === b[ col ] && sortLen === 1 ) { + return a[ c.columns ].order - b[ c.columns ].order; + } + + // fallback to natural sort since it is more robust + num = /n/i.test( ts.getSortType( c.parsers, col ) ); + if ( num && c.strings[ col ] ) { + // sort strings in numerical columns + if ( typeof ( ts.string[ c.strings[ col ] ] ) === 'boolean' ) { + num = ( dir ? 1 : -1 ) * ( ts.string[ c.strings[ col ] ] ? -1 : 1 ); + } else { + num = ( c.strings[ col ] ) ? ts.string[ c.strings[ col ] ] || 0 : 0; + } + // fall back to built-in numeric sort + // var sort = $.tablesorter['sort' + s]( a[col], b[col], dir, colMax[col], table ); + sort = c.numberSorter ? c.numberSorter( a[ col ], b[ col ], dir, colMax[ col ], table ) : + ts[ 'sortNumeric' + ( dir ? 'Asc' : 'Desc' ) ]( a[ col ], b[ col ], num, colMax[ col ], col, c ); + } else { + // set a & b depending on sort direction + x = dir ? a : b; + y = dir ? b : a; + // text sort function + if ( typeof textSorter === 'function' ) { + // custom OVERALL text sorter + sort = textSorter( x[ col ], y[ col ], dir, col, table ); + } else if ( typeof textSorter === 'object' && textSorter.hasOwnProperty( col ) ) { + // custom text sorter for a SPECIFIC COLUMN + sort = textSorter[ col ]( x[ col ], y[ col ], dir, col, table ); + } else { + // fall back to natural sort + sort = ts[ 'sortNatural' + ( dir ? 'Asc' : 'Desc' ) ]( a[ col ], b[ col ], col, c ); + } + } + if ( sort ) { return sort; } } - } - }; + return a[ c.columns ].order - b[ c.columns ].order; + }); + } + if ( c.debug ) { + console.log( 'Applying sort ' + sortList.toString() + ts.benchmark( sortTime ) ); + } + }, - ts.destroy = function(table, removeClasses, callback){ - table = $(table)[0]; - if (!table.hasInitialized) { return; } - // remove all widgets - ts.removeWidget(table, true, false); - var events, - $t = $(table), - c = table.config, - debug = c.debug, - $h = $t.find('thead:first'), - $r = $h.find('tr.' + ts.css.headerRow).removeClass(ts.css.headerRow + ' ' + c.cssHeaderRow), - $f = $t.find('tfoot:first > tr').children('th, td'); - if (removeClasses === false && $.inArray('uitheme', c.widgets) >= 0) { - // reapply uitheme classes, in case we want to maintain appearance - $t.trigger('applyWidgetId', [ 'uitheme' ]); - $t.trigger('applyWidgetId', [ 'zebra' ]); - } - // remove widget added rows, just in case - $h.find('tr').not($r).remove(); - // disable tablesorter - events = 'sortReset update updateRows updateAll updateHeaders updateCell addRows updateComplete sorton ' + - 'appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress ' + - 'sortBegin sortEnd resetToLoadState '.split(' ') - .join(c.namespace + ' '); - $t - .removeData('tablesorter') - .unbind( events.replace(ts.regex.spaces, ' ') ); - c.$headers.add($f) - .removeClass( [ ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone ].join(' ') ) - .removeAttr('data-column') - .removeAttr('aria-label') - .attr('aria-disabled', 'true'); - $r.find(c.selectorSort).unbind( ('mousedown mouseup keypress '.split(' ').join(c.namespace + ' ')).replace(ts.regex.spaces, ' ') ); - ts.restoreHeaders(table); - $t.toggleClass(ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme, removeClasses === false); - // clear flag in case the plugin is initialized again - table.hasInitialized = false; - delete table.config.cache; - if (typeof callback === 'function') { - callback(table); - } - if (debug) { - console.log( 'tablesorter has been removed' ); - } - }; + resortComplete : function( c, callback ) { + if ( c.table.isUpdating ) { + c.$table.trigger( 'updateComplete', c.table ); + } + if ( $.isFunction( callback ) ) { + callback( c.table ); + } + }, - // *** sort functions *** - // regex used in natural sort - ts.regex.chunk = /(^([+\-]?(?:\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi; // chunk/tokenize numbers & letters - ts.regex.chunks = /(^\\0|\\0$)/; // replace chunks @ ends - ts.regex.hex = /^0x[0-9a-f]+$/i; // hex - - // Natural sort - https://github.com/overset/javascript-natural-sort (date sorting removed) - // this function will only accept strings, or you'll see 'TypeError: undefined is not a function' - // I could add a = a.toString(); b = b.toString(); but it'll slow down the sort overall - ts.sortNatural = function(a, b) { - if (a === b) { return 0; } - var xN, xD, yN, yD, xF, yF, i, mx, - r = ts.regex; - // first try and sort Hex codes - if (r.hex.test(b)) { - xD = parseInt(a.match(r.hex), 16); - yD = parseInt(b.match(r.hex), 16); - if ( xD < yD ) { return -1; } - if ( xD > yD ) { return 1; } - } - // chunk/tokenize - xN = a.replace(r.chunk, '\\0$1\\0').replace(r.chunks, '').split('\\0'); - yN = b.replace(r.chunk, '\\0$1\\0').replace(r.chunks, '').split('\\0'); - mx = Math.max(xN.length, yN.length); - // natural sorting through split numeric strings and default strings - for (i = 0; i < mx; i++) { - // find floats not starting with '0', string or 0 if not defined - xF = isNaN(xN[i]) ? xN[i] || 0 : parseFloat(xN[i]) || 0; - yF = isNaN(yN[i]) ? yN[i] || 0 : parseFloat(yN[i]) || 0; - // handle numeric vs string comparison - number < string - (Kyle Adams) - if (isNaN(xF) !== isNaN(yF)) { return (isNaN(xF)) ? 1 : -1; } - // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2' - if (typeof xF !== typeof yF) { - xF += ''; - yF += ''; - } - if (xF < yF) { return -1; } - if (xF > yF) { return 1; } - } - return 0; - }; + checkResort : function( c, resort, callback ) { + var sortList = $.isArray( resort ) ? resort : c.sortList, + // if no resort parameter is passed, fallback to config.resort (true by default) + resrt = typeof resort === 'undefined' ? c.resort : resort; + // don't try to resort if the table is still processing + // this will catch spamming of the updateCell method + if ( resrt !== false && !c.serverSideSorting && !c.table.isProcessing ) { + if ( sortList.length ) { + ts.sortOn( c, sortList, function() { + ts.resortComplete( c, callback ); + }, true ); + } else { + ts.sortReset( c, function() { + ts.resortComplete( c, callback ); + ts.applyWidget( c.table, false ); + } ); + } + } else { + ts.resortComplete( c, callback ); + ts.applyWidget( c.table, false ); + } + }, - ts.sortNaturalAsc = function(a, b, col, table, c) { - if (a === b) { return 0; } - var e = c.string[ (c.empties[col] || c.emptyTo ) ]; - if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : -e || -1; } - if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : e || 1; } - return ts.sortNatural(a, b); - }; + sortOn : function( c, list, callback, init ) { + var table = c.table; + c.$table.trigger( 'sortStart', table ); + // update header count index + ts.updateHeaderSortCount( c, list ); + // set css for headers + ts.setHeadersCss( c ); + // fixes #346 + if ( c.delayInit && ts.isEmptyObject( c.cache ) ) { + ts.buildCache( c ); + } + c.$table.trigger( 'sortBegin', table ); + // sort the table and append it to the dom + ts.multisort( c ); + ts.appendCache( c, init ); + c.$table.trigger( 'sortEnd', table ); + ts.applyWidget( table ); + if ( $.isFunction( callback ) ) { + callback( table ); + } + }, - ts.sortNaturalDesc = function(a, b, col, table, c) { - if (a === b) { return 0; } - var e = c.string[ (c.empties[col] || c.emptyTo ) ]; - if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : e || 1; } - if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : -e || -1; } - return ts.sortNatural(b, a); - }; + sortReset : function( c, callback ) { + c.sortList = []; + ts.setHeadersCss( c ); + ts.multisort( c ); + ts.appendCache( c ); + if ( $.isFunction( callback ) ) { + callback( c.table ); + } + }, - // basic alphabetical sort - ts.sortText = function(a, b) { - return a > b ? 1 : (a < b ? -1 : 0); - }; + getSortType : function( parsers, column ) { + return ( parsers && parsers[ column ] ) ? parsers[ column ].type || '' : ''; + }, - // return text string value by adding up ascii value - // so the text is somewhat sorted when using a digital sort - // this is NOT an alphanumeric sort - ts.getTextValue = function(a, num, mx) { - if (mx) { - // make sure the text value is greater than the max numerical value (mx) - var i, l = a ? a.length : 0, n = mx + num; - for (i = 0; i < l; i++) { - n += a.charCodeAt(i); - } - return num * n; - } - return 0; - }; + formatSortingOrder : function( val ) { + // look for 'd' in 'desc' order; return true + return ( /^d/i.test( val ) || val === 1 ); + }, - ts.sortNumericAsc = function(a, b, num, mx, col, table) { - if (a === b) { return 0; } - var c = table.config, - e = c.string[ (c.empties[col] || c.emptyTo ) ]; - if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : -e || -1; } - if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : e || 1; } - if (isNaN(a)) { a = ts.getTextValue(a, num, mx); } - if (isNaN(b)) { b = ts.getTextValue(b, num, mx); } - return a - b; - }; + // Natural sort - https://github.com/overset/javascript-natural-sort (date sorting removed) + // this function will only accept strings, or you'll see 'TypeError: undefined is not a function' + // I could add a = a.toString(); b = b.toString(); but it'll slow down the sort overall + sortNatural : function( a, b ) { + if ( a === b ) { return 0; } + var aNum, bNum, aFloat, bFloat, indx, max, + regex = ts.regex; + // first try and sort Hex codes + if ( regex.hex.test( b ) ) { + aNum = parseInt( a.match( regex.hex ), 16 ); + bNum = parseInt( b.match( regex.hex ), 16 ); + if ( aNum < bNum ) { return -1; } + if ( aNum > bNum ) { return 1; } + } + // chunk/tokenize + aNum = a.replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); + bNum = b.replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); + max = Math.max( aNum.length, bNum.length ); + // natural sorting through split numeric strings and default strings + for ( indx = 0; indx < max; indx++ ) { + // find floats not starting with '0', string or 0 if not defined + aFloat = isNaN( aNum[ indx ] ) ? aNum[ indx ] || 0 : parseFloat( aNum[ indx ] ) || 0; + bFloat = isNaN( bNum[ indx ] ) ? bNum[ indx ] || 0 : parseFloat( bNum[ indx ] ) || 0; + // handle numeric vs string comparison - number < string - (Kyle Adams) + if ( isNaN( aFloat ) !== isNaN( bFloat ) ) { return isNaN( aFloat ) ? 1 : -1; } + // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2' + if ( typeof aFloat !== typeof bFloat ) { + aFloat += ''; + bFloat += ''; + } + if ( aFloat < bFloat ) { return -1; } + if ( aFloat > bFloat ) { return 1; } + } + return 0; + }, - ts.sortNumericDesc = function(a, b, num, mx, col, table) { - if (a === b) { return 0; } - var c = table.config, - e = c.string[ (c.empties[col] || c.emptyTo ) ]; - if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : e || 1; } - if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : -e || -1; } - if (isNaN(a)) { a = ts.getTextValue(a, num, mx); } - if (isNaN(b)) { b = ts.getTextValue(b, num, mx); } - return b - a; - }; + sortNaturalAsc : function( a, b, col, c ) { + if ( a === b ) { return 0; } + var empty = ts.string[ ( c.empties[ col ] || c.emptyTo ) ]; + if ( a === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? -1 : 1 ) : -empty || -1; } + if ( b === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? 1 : -1 ) : empty || 1; } + return ts.sortNatural( a, b ); + }, - ts.sortNumeric = function(a, b) { - return a - b; - }; + sortNaturalDesc : function( a, b, col, c ) { + if ( a === b ) { return 0; } + var empty = ts.string[ ( c.empties[ col ] || c.emptyTo ) ]; + if ( a === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? -1 : 1 ) : empty || 1; } + if ( b === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? 1 : -1 ) : -empty || -1; } + return ts.sortNatural( b, a ); + }, - // used when replacing accented characters during sorting - ts.characterEquivalents = { - 'a' : '\u00e1\u00e0\u00e2\u00e3\u00e4\u0105\u00e5', // áàâãäąå - 'A' : '\u00c1\u00c0\u00c2\u00c3\u00c4\u0104\u00c5', // ÁÀÂÃÄĄÅ - 'c' : '\u00e7\u0107\u010d', // çćč - 'C' : '\u00c7\u0106\u010c', // ÇĆČ - 'e' : '\u00e9\u00e8\u00ea\u00eb\u011b\u0119', // éèêëěę - 'E' : '\u00c9\u00c8\u00ca\u00cb\u011a\u0118', // ÉÈÊËĚĘ - 'i' : '\u00ed\u00ec\u0130\u00ee\u00ef\u0131', // íìİîïı - 'I' : '\u00cd\u00cc\u0130\u00ce\u00cf', // ÍÌİÎÏ - 'o' : '\u00f3\u00f2\u00f4\u00f5\u00f6\u014d', // óòôõöō - 'O' : '\u00d3\u00d2\u00d4\u00d5\u00d6\u014c', // ÓÒÔÕÖŌ - 'ss': '\u00df', // ß (s sharp) - 'SS': '\u1e9e', // ẞ (Capital sharp s) - 'u' : '\u00fa\u00f9\u00fb\u00fc\u016f', // úùûüů - 'U' : '\u00da\u00d9\u00db\u00dc\u016e' // ÚÙÛÜŮ - }; - ts.replaceAccents = function(s) { - var a, acc = '[', eq = ts.characterEquivalents; - if (!ts.characterRegex) { - ts.characterRegexArray = {}; - for (a in eq) { - if (typeof a === 'string') { - acc += eq[a]; - ts.characterRegexArray[a] = new RegExp('[' + eq[a] + ']', 'g'); - } - } - ts.characterRegex = new RegExp(acc + ']'); - } - if (ts.characterRegex.test(s)) { - for (a in eq) { - if (typeof a === 'string') { - s = s.replace( ts.characterRegexArray[a], a ); - } - } - } - return s; - }; + // basic alphabetical sort + sortText : function( a, b ) { + return a > b ? 1 : ( a < b ? -1 : 0 ); + }, - // *** utilities *** - ts.isValueInArray = function( column, arry ) { + // return text string value by adding up ascii value + // so the text is somewhat sorted when using a digital sort + // this is NOT an alphanumeric sort + getTextValue : function( val, num, max ) { + if ( max ) { + // make sure the text value is greater than the max numerical value (max) var indx, - len = arry && arry.length || 0; + len = val ? val.length : 0, + n = max + num; for ( indx = 0; indx < len; indx++ ) { - if ( arry[ indx ][ 0 ] === column ) { - return indx; - } + n += val.charCodeAt( indx ); } - return -1; - }; + return num * n; + } + return 0; + }, - ts.addParser = function(parser) { - var i, l = ts.parsers.length, a = true; - for (i = 0; i < l; i++) { - if (ts.parsers[i].id.toLowerCase() === parser.id.toLowerCase()) { - a = false; - } - } - if (a) { - ts.parsers.push(parser); - } - }; + sortNumericAsc : function( a, b, num, max, col, c ) { + if ( a === b ) { return 0; } + var empty = ts.string[ ( c.empties[ col ] || c.emptyTo ) ]; + if ( a === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? -1 : 1 ) : -empty || -1; } + if ( b === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? 1 : -1 ) : empty || 1; } + if ( isNaN( a ) ) { a = ts.getTextValue( a, num, max ); } + if ( isNaN( b ) ) { b = ts.getTextValue( b, num, max ); } + return a - b; + }, - // Use it to add a set of methods to table.config which will be available for all tables. - // This should be done before table initialization - ts.addInstanceMethods = function(methods) { - $.extend(ts.instanceMethods, methods); - }; + sortNumericDesc : function( a, b, num, max, col, c ) { + if ( a === b ) { return 0; } + var empty = ts.string[ ( c.empties[ col ] || c.emptyTo ) ]; + if ( a === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? -1 : 1 ) : empty || 1; } + if ( b === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? 1 : -1 ) : -empty || -1; } + if ( isNaN( a ) ) { a = ts.getTextValue( a, num, max ); } + if ( isNaN( b ) ) { b = ts.getTextValue( b, num, max ); } + return b - a; + }, - ts.getParserById = function(name) { - /*jshint eqeqeq:false */ - if (name == 'false') { return false; } - var i, l = ts.parsers.length; - for (i = 0; i < l; i++) { - if (ts.parsers[i].id.toLowerCase() === (name.toString()).toLowerCase()) { - return ts.parsers[i]; - } - } - return false; - }; + sortNumeric : function( a, b ) { + return a - b; + }, - ts.addWidget = function(widget) { - ts.widgets.push(widget); - }; + /* + ██ ██ ██ ██ █████▄ ▄████▄ ██████ ██████ ▄█████ + ██ ██ ██ ██ ██ ██ ██ ▄▄▄ ██▄▄ ██ ▀█▄ + ██ ██ ██ ██ ██ ██ ██ ▀██ ██▀▀ ██ ▀█▄ + ███████▀ ██ █████▀ ▀████▀ ██████ ██ █████▀ + */ + addWidget : function( widget ) { + ts.widgets.push( widget ); + }, - ts.hasWidget = function( $table, name ) { - $table = $( $table ); - return $table.length && $table[0].config && $table[0].config.widgetInit[name] || false; - }; + hasWidget : function( $table, name ) { + $table = $( $table ); + return $table.length && $table[ 0 ].config && $table[ 0 ].config.widgetInit[ name ] || false; + }, - ts.getWidgetById = function(name) { - var i, w, l = ts.widgets.length; - for (i = 0; i < l; i++) { - w = ts.widgets[i]; - if (w && w.hasOwnProperty('id') && w.id.toLowerCase() === name.toLowerCase()) { - return w; - } + getWidgetById : function( name ) { + var indx, widget, + len = ts.widgets.length; + for ( indx = 0; indx < len; indx++ ) { + widget = ts.widgets[ indx ]; + if ( widget && widget.id && widget.id.toLowerCase() === name.toLowerCase() ) { + return widget; } - }; + } + }, - ts.applyWidgetOptions = function( table, c ){ - var indx, widget, - len = c.widgets.length; - if (len) { - for (indx = 0; indx < len; indx++) { - widget = ts.getWidgetById( c.widgets[indx] ); - if ( widget && 'options' in widget ) { - c.widgetOptions = $.extend( true, {}, widget.options, c.widgetOptions ); - } + applyWidgetOptions : function( table ) { + var indx, widget, + c = table.config, + len = c.widgets.length; + if ( len ) { + for ( indx = 0; indx < len; indx++ ) { + widget = ts.getWidgetById( c.widgets[ indx ] ); + if ( widget && widget.options ) { + c.widgetOptions = $.extend( true, {}, widget.options, c.widgetOptions ); } } - }; + } + }, - ts.applyWidget = function(table, init, callback) { - table = $(table)[0]; // in case this is called externally - var indx, len, names, widget, name, applied, - c = table.config, - tableClass = ' ' + c.table.className + ' ', - widgets = [], - time, time2, w, wd; - // prevent numerous consecutive widget applications - if (init !== false && table.hasInitialized && (table.isApplyingWidgets || table.isUpdating)) { return; } - if (c.debug) { time = new Date(); } - // look for widgets to apply from in table class + addWidgetFromClass : function( table ) { + var len, indx, + c = table.config, + // look for widgets to apply from table class // stop using \b otherwise this matches 'ui-widget-content' & adds 'content' widget - wd = new RegExp( '\\s' + c.widgetClass.replace( ts.regex.templateName, '([\\w-]+)' ) + '\\s', 'g' ); - if ( tableClass.match( wd ) ) { - // extract out the widget id from the table class (widget id's can include dashes) - w = tableClass.match( wd ); - if ( w ) { - len = w.length; - for (indx = 0; indx < len; indx++) { - c.widgets.push( w[indx].replace( wd, '$1' ) ); - } - } + regex = '\\s' + c.widgetClass.replace( ts.regex.templateName, '([\\w-]+)' ) + '\\s', + widgetClass = new RegExp( regex, 'g' ), + // extract out the widget id from the table class (widget id's can include dashes) + widget = ( ' ' + c.table.className + ' ' ).match( widgetClass ); + if ( widget ) { + len = widget.length; + for ( indx = 0; indx < len; indx++ ) { + c.widgets.push( widget[ indx ].replace( widgetClass, '$1' ) ); } - if (c.widgets.length) { - table.isApplyingWidgets = true; - // ensure unique widget ids - c.widgets = $.grep(c.widgets, function(v, k){ - return $.inArray(v, c.widgets) === k; - }); - names = c.widgets || []; - len = names.length; - // build widget array & add priority as needed - for (indx = 0; indx < len; indx++) { - wd = ts.getWidgetById(names[indx]); - if (wd && wd.id) { - // set priority to 10 if not defined - if (!wd.priority) { wd.priority = 10; } - widgets[indx] = wd; - } - } - // sort widgets by priority - widgets.sort(function(a, b){ - return a.priority < b.priority ? -1 : a.priority === b.priority ? 0 : 1; - }); - // add/update selected widgets - len = widgets.length; - if (c.debug) { - console[ console.group ? 'group' : 'log' ]( 'Start ' + ( init ? 'initializing' : 'applying' ) + ' widgets' ); + } + }, + + applyWidget : function( table, init, callback ) { + table = $( table )[ 0 ]; // in case this is called externally + var indx, len, names, widget, name, applied, time, time2, + c = table.config, + widgets = []; + // prevent numerous consecutive widget applications + if ( init !== false && table.hasInitialized && ( table.isApplyingWidgets || table.isUpdating ) ) { + return; + } + if ( c.debug ) { time = new Date(); } + ts.addWidgetFromClass( table ); + if ( c.widgets.length ) { + table.isApplyingWidgets = true; + // ensure unique widget ids + c.widgets = $.grep( c.widgets, function( val, index ) { + return $.inArray( val, c.widgets ) === index; + }); + names = c.widgets || []; + len = names.length; + // build widget array & add priority as needed + for ( indx = 0; indx < len; indx++ ) { + widget = ts.getWidgetById( names[ indx ] ); + if ( widget && widget.id ) { + // set priority to 10 if not defined + if ( !widget.priority ) { widget.priority = 10; } + widgets[ indx ] = widget; } - for (indx = 0; indx < len; indx++) { - widget = widgets[indx]; - if (widget) { - name = widget.id; - applied = false; - if (c.debug) { time2 = new Date(); } - - if ( init || !( c.widgetInit[ name ] ) ) { - // set init flag first to prevent calling init more than once (e.g. pager) - c.widgetInit[ name ] = true; - if (table.hasInitialized) { - // don't reapply widget options on tablesorter init - ts.applyWidgetOptions( table, table.config ); - } - if ( 'init' in widget ) { - applied = true; - if (c.debug) { - console[ console.group ? 'group' : 'log' ]( 'Initializing ' + name + ' widget' ); - } - widget.init(table, widget, table.config, table.config.widgetOptions); - } + } + // sort widgets by priority + widgets.sort( function( a, b ) { + return a.priority < b.priority ? -1 : a.priority === b.priority ? 0 : 1; + }); + // add/update selected widgets + len = widgets.length; + if ( c.debug ) { + console[ console.group ? 'group' : 'log' ]( 'Start ' + ( init ? 'initializing' : 'applying' ) + ' widgets' ); + } + for ( indx = 0; indx < len; indx++ ) { + widget = widgets[ indx ]; + if ( widget ) { + name = widget.id; + applied = false; + if ( c.debug ) { time2 = new Date(); } + + if ( init || !( c.widgetInit[ name ] ) ) { + // set init flag first to prevent calling init more than once (e.g. pager) + c.widgetInit[ name ] = true; + if ( table.hasInitialized ) { + // don't reapply widget options on tablesorter init + ts.applyWidgetOptions( table ); } - if ( !init && 'format' in widget ) { + if ( typeof widget.init === 'function' ) { applied = true; - if (c.debug) { - console[ console.group ? 'group' : 'log' ]( 'Updating ' + name + ' widget' ); + if ( c.debug ) { + console[ console.group ? 'group' : 'log' ]( 'Initializing ' + name + ' widget' ); } - widget.format(table, table.config, table.config.widgetOptions, false); + widget.init( table, widget, table.config, table.config.widgetOptions ); } - if (c.debug) { - if (applied) { - console.log( 'Completed ' + ( init ? 'initializing ' : 'applying ' ) + name + ' widget' + ts.benchmark( time2 ) ); - if ( console.groupEnd ) { console.groupEnd(); } - } + } + if ( !init && typeof widget.format === 'function' ) { + applied = true; + if ( c.debug ) { + console[ console.group ? 'group' : 'log' ]( 'Updating ' + name + ' widget' ); + } + widget.format( table, table.config, table.config.widgetOptions, false ); + } + if ( c.debug ) { + if ( applied ) { + console.log( 'Completed ' + ( init ? 'initializing ' : 'applying ' ) + name + ' widget' + ts.benchmark( time2 ) ); + if ( console.groupEnd ) { console.groupEnd(); } } } } - if ( c.debug && console.groupEnd ) { console.groupEnd(); } - // callback executed on init only - if (!init && typeof callback === 'function') { - callback(table); + } + if ( c.debug && console.groupEnd ) { console.groupEnd(); } + // callback executed on init only + if ( !init && typeof callback === 'function' ) { + callback( table ); + } + } + setTimeout( function() { + table.isApplyingWidgets = false; + $.data( table, 'lastWidgetApplication', new Date() ); + c.$table.trigger('tablesorter-ready'); + }, 0 ); + if ( c.debug ) { + widget = c.widgets.length; + console.log( 'Completed ' + + ( init === true ? 'initializing ' : 'applying ' ) + widget + + ' widget' + ( widget !== 1 ? 's' : '' ) + ts.benchmark( time ) ); + } + }, + + removeWidget : function( table, name, refreshing ) { + table = $( table )[ 0 ]; + var index, widget, indx, len, + c = table.config; + // if name === true, add all widgets from $.tablesorter.widgets + if ( name === true ) { + name = []; + len = ts.widgets.length; + for ( indx = 0; indx < len; indx++ ) { + widget = ts.widgets[ indx ]; + if ( widget && widget.id ) { + name.push( widget.id ); + } + } + } else { + // name can be either an array of widgets names, + // or a space/comma separated list of widget names + name = ( $.isArray( name ) ? name.join( ',' ) : name || '' ).toLowerCase().split( /[\s,]+/ ); + } + len = name.length; + for ( index = 0; index < len; index++ ) { + widget = ts.getWidgetById( name[ index ] ); + indx = $.inArray( name[ index ], c.widgets ); + if ( widget && widget.remove ) { + if ( c.debug ) { + console.log( ( refreshing ? 'Refreshing' : 'Removing' ) + ' "' + name[ index ] + '" widget' ); } + widget.remove( table, c, c.widgetOptions, refreshing ); + c.widgetInit[ name[ index ] ] = false; } - setTimeout(function(){ - table.isApplyingWidgets = false; - $.data(table, 'lastWidgetApplication', new Date()); - }, 0); - if (c.debug) { - w = c.widgets.length; - console.log( 'Completed ' + (init === true ? 'initializing ' : 'applying ') + w + ' widget' + (w !== 1 ? 's' : '') + ts.benchmark(time) ); + // don't remove the widget from config.widget if refreshing + if ( indx >= 0 && refreshing !== true ) { + c.widgets.splice( indx, 1 ); } - }; + } + }, + + refreshWidgets : function( table, doAll, dontapply ) { + table = $( table )[ 0 ]; // see issue #243 + var indx, widget, + c = table.config, + curWidgets = c.widgets, + widgets = ts.widgets, + len = widgets.length, + list = [], + callback = function( table ) { + $( table ).trigger( 'refreshComplete' ); + }; + // remove widgets not defined in config.widgets, unless doAll is true + for ( indx = 0; indx < len; indx++ ) { + widget = widgets[ indx ]; + if ( widget && widget.id && ( doAll || $.inArray( widget.id, curWidgets ) < 0 ) ) { + list.push( widget.id ); + } + } + ts.removeWidget( table, list.join( ',' ), true ); + if ( dontapply !== true ) { + // call widget init if + ts.applyWidget( table, doAll || false, callback ); + if ( doAll ) { + // apply widget format + ts.applyWidget( table, false, callback ); + } + } else { + callback( table ); + } + }, + + /* + ██ ██ ██████ ██ ██ ██ ██████ ██ ██████ ▄█████ + ██ ██ ██ ██ ██ ██ ██ ██ ██▄▄ ▀█▄ + ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀█▄ + ▀████▀ ██ ██ ██████ ██ ██ ██ ██████ █████▀ + */ + benchmark : function( diff ) { + return ( ' ( ' + ( new Date().getTime() - diff.getTime() ) + 'ms )' ); + }, + // deprecated ts.log + log : function() { + console.log( arguments ); + }, + + // $.isEmptyObject from jQuery v1.4 + isEmptyObject : function( obj ) { + /*jshint forin: false */ + for ( var name in obj ) { + return false; + } + return true; + }, + + isValueInArray : function( column, arry ) { + var indx, + len = arry && arry.length || 0; + for ( indx = 0; indx < len; indx++ ) { + if ( arry[ indx ][ 0 ] === column ) { + return indx; + } + } + return -1; + }, + + formatFloat : function( str, table ) { + if ( typeof str !== 'string' || str === '' ) { return str; } + // allow using formatFloat without a table; defaults to US number format + var num, + usFormat = table && table.config ? table.config.usNumberFormat !== false : + typeof table !== 'undefined' ? table : true; + if ( usFormat ) { + // US Format - 1,234,567.89 -> 1234567.89 + str = str.replace( ts.regex.comma, '' ); + } else { + // German Format = 1.234.567,89 -> 1234567.89 + // French Format = 1 234 567,89 -> 1234567.89 + str = str.replace( ts.regex.digitNonUS, '' ).replace( ts.regex.comma, '.' ); + } + if ( ts.regex.digitNegativeTest.test( str ) ) { + // make (#) into a negative number -> (10) = -10 + str = str.replace( ts.regex.digitNegativeReplace, '-$1' ); + } + num = parseFloat( str ); + // return the text instead of zero + return isNaN( num ) ? $.trim( str ) : num; + }, + + isDigit : function( str ) { + // replace all unwanted chars and match + return isNaN( str ) ? + ts.regex.digitTest.test( str.toString().replace( ts.regex.digitReplace, '' ) ) : + str !== ''; + }, - ts.removeWidget = function(table, name, refreshing){ - table = $(table)[0]; - var i, widget, indx, len, - c = table.config; - // if name === true, add all widgets from $.tablesorter.widgets - if (name === true) { - name = []; - len = ts.widgets.length; - for (indx = 0; indx < len; indx++) { - widget = ts.widgets[indx]; - if (widget && widget.id) { - name.push( widget.id ); + // computeTableHeaderCellIndexes from: + // http://www.javascripttoolbox.com/lib/table/examples.php + // http://www.javascripttoolbox.com/temp/table_cellindex.html + computeColumnIndex : function( $rows ) { + var i, j, k, l, $cell, cell, cells, rowIndex, cellId, rowSpan, colSpan, firstAvailCol, + matrix = [], + matrixrow = []; + for ( i = 0; i < $rows.length; i++ ) { + cells = $rows[ i ].cells; + for ( j = 0; j < cells.length; j++ ) { + cell = cells[ j ]; + $cell = $( cell ); + rowIndex = cell.parentNode.rowIndex; + cellId = rowIndex + '-' + $cell.index(); + rowSpan = cell.rowSpan || 1; + colSpan = cell.colSpan || 1; + if ( typeof matrix[ rowIndex ] === 'undefined' ) { + matrix[ rowIndex ] = []; + } + // Find first available column in the first row + for ( k = 0; k < matrix[ rowIndex ].length + 1; k++ ) { + if ( typeof matrix[ rowIndex ][ k ] === 'undefined' ) { + firstAvailCol = k; + break; } } - } else { - // name can be either an array of widgets names, - // or a space/comma separated list of widget names - name = ( $.isArray(name) ? name.join(',') : name || '' ).toLowerCase().split( /[\s,]+/ ); - } - len = name.length; - for (i = 0; i < len; i++) { - widget = ts.getWidgetById(name[i]); - indx = $.inArray( name[i], c.widgets ); - if ( widget && 'remove' in widget ) { - if (c.debug && indx >= 0) { console.log( 'Removing "' + name[i] + '" widget' ); } - if ( c.debug ) { - console.log( ( refreshing ? 'Refreshing' : 'Removing' ) + ' "' + name[i] + '" widget' ); - } - widget.remove(table, c, c.widgetOptions, refreshing); - c.widgetInit[ name[i] ] = false; + // add data-column (setAttribute = IE8+) + if ( cell.setAttribute ) { + cell.setAttribute( 'data-column', firstAvailCol ); + } else { + $cell.attr( 'data-column', firstAvailCol ); } - // don't remove the widget from config.widget if refreshing - if (indx >= 0 && refreshing !== true) { - c.widgets.splice( indx, 1 ); + for ( k = rowIndex; k < rowIndex + rowSpan; k++ ) { + if ( typeof matrix[ k ] === 'undefined' ) { + matrix[ k ] = []; + } + matrixrow = matrix[ k ]; + for ( l = firstAvailCol; l < firstAvailCol + colSpan; l++ ) { + matrixrow[ l ] = 'x'; + } } } - }; + } + return matrixrow.length; + }, - ts.refreshWidgets = function(table, doAll, dontapply) { - table = $(table)[0]; // see issue #243 - var indx, - c = table.config, - cw = c.widgets, - widgets = ts.widgets, - len = widgets.length, - list = [], - callback = function(table){ - $(table).trigger('refreshComplete'); - }; - // remove widgets not defined in config.widgets, unless doAll is true - for (indx = 0; indx < len; indx++) { - if (widgets[indx] && widgets[indx].id && (doAll || $.inArray( widgets[indx].id, cw ) < 0)) { - list.push( widgets[indx].id ); - } + // automatically add a colgroup with col elements set to a percentage width + fixColumnWidth : function( table ) { + table = $( table )[ 0 ]; + var overallWidth, percent, $tbodies, len, index, + c = table.config, + $colgroup = c.$table.children( 'colgroup' ); + // remove plugin-added colgroup, in case we need to refresh the widths + if ( $colgroup.length && $colgroup.hasClass( ts.css.colgroup ) ) { + $colgroup.remove(); + } + if ( c.widthFixed && c.$table.children( 'colgroup' ).length === 0 ) { + $colgroup = $( '<colgroup class="' + ts.css.colgroup + '">' ); + overallWidth = c.$table.width(); + // only add col for visible columns - fixes #371 + $tbodies = c.$tbodies.find( 'tr:first' ).children( ':visible' ); + len = $tbodies.length; + for ( index = 0; index < len; index++ ) { + percent = parseInt( ( $tbodies.eq( index ).width() / overallWidth ) * 1000, 10 ) / 10 + '%'; + $colgroup.append( $( '<col>' ).css( 'width', percent ) ); } - ts.removeWidget( table, list.join(','), true ); - if (dontapply !== true) { - // call widget init if - ts.applyWidget(table, doAll || false, callback ); - if (doAll) { - // apply widget format - ts.applyWidget(table, false, callback); + c.$table.prepend( $colgroup ); + } + }, + + // get sorter, string, empty, etc options for each column from + // jQuery data, metadata, header option or header class name ('sorter-false') + // priority = jQuery data > meta > headers option > header class name + getData : function( header, configHeader, key ) { + var meta, cl4ss, + val = '', + $header = $( header ); + if ( !$header.length ) { return ''; } + meta = $.metadata ? $header.metadata() : false; + cl4ss = ' ' + ( $header.attr( 'class' ) || '' ); + if ( typeof $header.data( key ) !== 'undefined' || + typeof $header.data( key.toLowerCase() ) !== 'undefined' ) { + // 'data-lockedOrder' is assigned to 'lockedorder'; but 'data-locked-order' is assigned to 'lockedOrder' + // 'data-sort-initial-order' is assigned to 'sortInitialOrder' + val += $header.data( key ) || $header.data( key.toLowerCase() ); + } else if ( meta && typeof meta[ key ] !== 'undefined' ) { + val += meta[ key ]; + } else if ( configHeader && typeof configHeader[ key ] !== 'undefined' ) { + val += configHeader[ key ]; + } else if ( cl4ss !== ' ' && cl4ss.match( ' ' + key + '-' ) ) { + // include sorter class name 'sorter-text', etc; now works with 'sorter-my-custom-parser' + val = cl4ss.match( new RegExp( '\\s' + key + '-([\\w-]+)' ) )[ 1 ] || ''; + } + return $.trim( val ); + }, + + getColumnData : function( table, obj, indx, getCell, $headers ) { + if ( typeof obj === 'undefined' || obj === null ) { return; } + table = $( table )[ 0 ]; + var $header, key, + c = table.config, + $cells = ( $headers || c.$headers ), + // c.$headerIndexed is not defined initially + $cell = c.$headerIndexed && c.$headerIndexed[ indx ] || + $cells.filter( '[data-column="' + indx + '"]:last' ); + if ( obj[ indx ] ) { + return getCell ? obj[ indx ] : obj[ $cells.index( $cell ) ]; + } + for ( key in obj ) { + if ( typeof key === 'string' ) { + $header = $cell + // header cell with class/id + .filter( key ) + // find elements within the header cell with cell/id + .add( $cell.find( key ) ); + if ( $header.length ) { + return obj[ key ]; } - } else { - callback(table); } - }; + } + return; + }, - ts.getColumnText = function( table, column, callback ) { - table = $( table )[0]; - var tbodyIndex, rowIndex, cache, row, tbodyLen, rowLen, raw, parsed, $cell, result, - hasCallback = typeof callback === 'function', - allColumns = column === 'all', - data = { raw : [], parsed: [], $cell: [] }, - c = table.config; - if ( ts.isEmptyObject( c ) ) { - if ( c.debug ) { - console.warn( 'No cache found - aborting getColumnText function!' ); - } - } else { - tbodyLen = c.$tbodies.length; - for ( tbodyIndex = 0; tbodyIndex < tbodyLen; tbodyIndex++ ) { - cache = c.cache[ tbodyIndex ].normalized; - rowLen = cache.length; - for ( rowIndex = 0; rowIndex < rowLen; rowIndex++ ) { - result = true; - row = cache[ rowIndex ]; - parsed = ( allColumns ) ? row.slice(0, c.columns) : row[ column ]; - row = row[ c.columns ]; - raw = ( allColumns ) ? row.raw : row.raw[ column ]; - $cell = ( allColumns ) ? row.$row.children() : row.$row.children().eq( column ); - if ( hasCallback ) { - result = callback({ - tbodyIndex: tbodyIndex, - rowIndex: rowIndex, - parsed: parsed, - raw: raw, - $row: row.$row, - $cell: $cell - }); - } - if ( result !== false ) { - data.parsed.push( parsed ); - data.raw.push( raw ); - data.$cell.push( $cell ); - } - } - } - // return everything - return data; + // *** Process table *** + // add processing indicator + isProcessing : function( $table, toggle, $ths ) { + $table = $( $table ); + var c = $table[ 0 ].config, + // default to all headers + $header = $ths || $table.find( '.' + ts.css.header ); + if ( toggle ) { + // don't use sortList if custom $ths used + if ( typeof $ths !== 'undefined' && c.sortList.length > 0 ) { + // get headers from the sortList + $header = $header.filter( function() { + // get data-column from attr to keep compatibility with jQuery 1.2.6 + return this.sortDisabled ? + false : + ts.isValueInArray( parseFloat( $( this ).attr( 'data-column' ) ), c.sortList ) >= 0; + }); } - }; + $table.add( $header ).addClass( ts.css.processing + ' ' + c.cssProcessing ); + } else { + $table.add( $header ).removeClass( ts.css.processing + ' ' + c.cssProcessing ); + } + }, - // get sorter, string, empty, etc options for each column from - // jQuery data, metadata, header option or header class name ('sorter-false') - // priority = jQuery data > meta > headers option > header class name - ts.getData = function(h, ch, key) { - var val = '', $h = $(h), m, cl; - if (!$h.length) { return ''; } - m = $.metadata ? $h.metadata() : false; - cl = ' ' + ($h.attr('class') || ''); - if (typeof $h.data(key) !== 'undefined' || typeof $h.data(key.toLowerCase()) !== 'undefined'){ - // 'data-lockedOrder' is assigned to 'lockedorder'; but 'data-locked-order' is assigned to 'lockedOrder' - // 'data-sort-initial-order' is assigned to 'sortInitialOrder' - val += $h.data(key) || $h.data(key.toLowerCase()); - } else if (m && typeof m[key] !== 'undefined') { - val += m[key]; - } else if (ch && typeof ch[key] !== 'undefined') { - val += ch[key]; - } else if (cl !== ' ' && cl.match(' ' + key + '-')) { - // include sorter class name 'sorter-text', etc; now works with 'sorter-my-custom-parser' - val = cl.match( new RegExp('\\s' + key + '-([\\w-]+)') )[1] || ''; - } - return $.trim(val); - }; + // detach tbody but save the position + // don't use tbody because there are portions that look for a tbody index (updateCell) + processTbody : function( table, $tb, getIt ) { + table = $( table )[ 0 ]; + if ( getIt ) { + table.isProcessing = true; + $tb.before( '<colgroup class="tablesorter-savemyplace"/>' ); + return $.fn.detach ? $tb.detach() : $tb.remove(); + } + var holdr = $( table ).find( 'colgroup.tablesorter-savemyplace' ); + $tb.insertAfter( holdr ); + holdr.remove(); + table.isProcessing = false; + }, - ts.regex.comma = /,/g; - ts.regex.digitNonUS = /[\s|\.]/g; - ts.regex.digitNegativeTest = /^\s*\([.\d]+\)/; - ts.regex.digitNegativeReplace = /^\s*\(([.\d]+)\)/; - ts.formatFloat = function(s, table) { - if (typeof s !== 'string' || s === '') { return s; } - // allow using formatFloat without a table; defaults to US number format - var i, - t = table && table.config ? table.config.usNumberFormat !== false : - typeof table !== 'undefined' ? table : true; - if (t) { - // US Format - 1,234,567.89 -> 1234567.89 - s = s.replace(ts.regex.comma, ''); - } else { - // German Format = 1.234.567,89 -> 1234567.89 - // French Format = 1 234 567,89 -> 1234567.89 - s = s.replace(ts.regex.digitNonUS, '').replace(ts.regex.comma, '.'); + clearTableBody : function( table ) { + $( table )[ 0 ].config.$tbodies.children().detach(); + }, + + // used when replacing accented characters during sorting + characterEquivalents : { + 'a' : '\u00e1\u00e0\u00e2\u00e3\u00e4\u0105\u00e5', // áàâãäąå + 'A' : '\u00c1\u00c0\u00c2\u00c3\u00c4\u0104\u00c5', // ÁÀÂÃÄĄÅ + 'c' : '\u00e7\u0107\u010d', // çćč + 'C' : '\u00c7\u0106\u010c', // ÇĆČ + 'e' : '\u00e9\u00e8\u00ea\u00eb\u011b\u0119', // éèêëěę + 'E' : '\u00c9\u00c8\u00ca\u00cb\u011a\u0118', // ÉÈÊËĚĘ + 'i' : '\u00ed\u00ec\u0130\u00ee\u00ef\u0131', // íìİîïı + 'I' : '\u00cd\u00cc\u0130\u00ce\u00cf', // ÍÌİÎÏ + 'o' : '\u00f3\u00f2\u00f4\u00f5\u00f6\u014d', // óòôõöō + 'O' : '\u00d3\u00d2\u00d4\u00d5\u00d6\u014c', // ÓÒÔÕÖŌ + 'ss': '\u00df', // ß (s sharp) + 'SS': '\u1e9e', // ẞ (Capital sharp s) + 'u' : '\u00fa\u00f9\u00fb\u00fc\u016f', // úùûüů + 'U' : '\u00da\u00d9\u00db\u00dc\u016e' // ÚÙÛÜŮ + }, + + replaceAccents : function( str ) { + var chr, + acc = '[', + eq = ts.characterEquivalents; + if ( !ts.characterRegex ) { + ts.characterRegexArray = {}; + for ( chr in eq ) { + if ( typeof chr === 'string' ) { + acc += eq[ chr ]; + ts.characterRegexArray[ chr ] = new RegExp( '[' + eq[ chr ] + ']', 'g' ); + } } - if (ts.regex.digitNegativeTest.test(s)) { - // make (#) into a negative number -> (10) = -10 - s = s.replace(ts.regex.digitNegativeReplace, '-$1'); + ts.characterRegex = new RegExp( acc + ']' ); + } + if ( ts.characterRegex.test( str ) ) { + for ( chr in eq ) { + if ( typeof chr === 'string' ) { + str = str.replace( ts.characterRegexArray[ chr ], chr ); + } } - i = parseFloat(s); - // return the text instead of zero - return isNaN(i) ? $.trim(s) : i; - }; + } + return str; + }, - ts.regex.digitTest = /^[\-+(]?\d+[)]?$/; - ts.regex.digitReplace = /[,.'"\s]/g; - ts.isDigit = function(s) { - // replace all unwanted chars and match - return isNaN(s) ? - ts.regex.digitTest.test( s.toString().replace( ts.regex.digitReplace, '' ) ) : - s !== ''; - }; + // restore headers + restoreHeaders : function( table ) { + var index, $cell, + c = $( table )[ 0 ].config, + $headers = c.$table.find( c.selectorHeaders ), + len = $headers.length; + // don't use c.$headers here in case header cells were swapped + for ( index = 0; index < len; index++ ) { + $cell = $headers.eq( index ); + // only restore header cells if it is wrapped + // because this is also used by the updateAll method + if ( $cell.find( '.' + ts.css.headerIn ).length ) { + $cell.html( c.headerContent[ index ] ); + } + } + }, - }() - }); + destroy : function( table, removeClasses, callback ) { + table = $( table )[ 0 ]; + if ( !table.hasInitialized ) { return; } + // remove all widgets + ts.removeWidget( table, true, false ); + var events, + $t = $( table ), + c = table.config, + debug = c.debug, + $h = $t.find( 'thead:first' ), + $r = $h.find( 'tr.' + ts.css.headerRow ).removeClass( ts.css.headerRow + ' ' + c.cssHeaderRow ), + $f = $t.find( 'tfoot:first > tr' ).children( 'th, td' ); + if ( removeClasses === false && $.inArray( 'uitheme', c.widgets ) >= 0 ) { + // reapply uitheme classes, in case we want to maintain appearance + $t.trigger( 'applyWidgetId', [ 'uitheme' ] ); + $t.trigger( 'applyWidgetId', [ 'zebra' ] ); + } + // remove widget added rows, just in case + $h.find( 'tr' ).not( $r ).remove(); + // disable tablesorter + events = 'sortReset update updateRows updateAll updateHeaders updateCell addRows updateComplete sorton ' + + 'appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress ' + + 'sortBegin sortEnd resetToLoadState '.split( ' ' ) + .join( c.namespace + ' ' ); + $t + .removeData( 'tablesorter' ) + .unbind( events.replace( ts.regex.spaces, ' ' ) ); + c.$headers + .add( $f ) + .removeClass( [ ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone ].join( ' ' ) ) + .removeAttr( 'data-column' ) + .removeAttr( 'aria-label' ) + .attr( 'aria-disabled', 'true' ); + $r + .find( c.selectorSort ) + .unbind( ( 'mousedown mouseup keypress '.split( ' ' ).join( c.namespace + ' ' ) ).replace( ts.regex.spaces, ' ' ) ); + ts.restoreHeaders( table ); + $t.toggleClass( ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme, removeClasses === false ); + // clear flag in case the plugin is initialized again + table.hasInitialized = false; + delete table.config.cache; + if ( typeof callback === 'function' ) { + callback( table ); + } + if ( debug ) { + console.log( 'tablesorter has been removed' ); + } + } - // make shortcut - var ts = $.tablesorter; + }; - // extend plugin scope - $.fn.extend({ - tablesorter: ts.construct - }); + $.fn.tablesorter = function( settings ) { + return this.each( function() { + var table = this, + // merge & extend config options + c = $.extend( true, {}, ts.defaults, settings, ts.instanceMethods ); + // save initial settings + c.originalSettings = settings; + // create a table from data (build table widget) + if ( !table.hasInitialized && ts.buildTable && this.nodeName !== 'TABLE' ) { + // return the table (in case the original target is the table's container) + ts.buildTable( table, c ); + } else { + ts.setup( table, c ); + } + }); + }; // set up debug logs if ( !( window.console && window.console.log ) ) { + // access $.tablesorter.logs for browsers that don't have a console... ts.logs = []; /*jshint -W020 */ console = {}; console.log = console.warn = console.error = console.table = function() { - ts.logs.push( [ Date.now(), arguments ] ); + var arg = arguments.length > 1 ? arguments : arguments[0]; + ts.logs.push({ date: Date.now(), log: arg }); }; } - ts.log = function(){ - console.log( arguments ); - }; - ts.benchmark = function( diff ) { - return ( ' (' + ( new Date().getTime() - diff.getTime() ) + 'ms)' ); - }; - // add default parsers ts.addParser({ - id: 'no-parser', - is: function() { + id : 'no-parser', + is : function() { return false; }, - format: function() { + format : function() { return ''; }, - type: 'text' + type : 'text' }); ts.addParser({ - id: 'text', - is: function() { + id : 'text', + is : function() { return true; }, - format: function(s, table) { + format : function( str, table ) { var c = table.config; - if (s) { - s = $.trim( c.ignoreCase ? s.toLocaleLowerCase() : s ); - s = c.sortLocaleCompare ? ts.replaceAccents(s) : s; + if ( str ) { + str = $.trim( c.ignoreCase ? str.toLocaleLowerCase() : str ); + str = c.sortLocaleCompare ? ts.replaceAccents( str ) : str; } - return s; + return str; }, - type: 'text' + type : 'text' }); ts.regex.nondigit = /[^\w,. \-()]/g; ts.addParser({ - id: 'digit', - is: function(s) { - return ts.isDigit(s); + id : 'digit', + is : function( str ) { + return ts.isDigit( str ); }, - format: function(s, table) { - var n = ts.formatFloat((s || '').replace(ts.regex.nondigit, ''), table); - return s && typeof n === 'number' ? n : - s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; + format : function( str, table ) { + var num = ts.formatFloat( ( str || '' ).replace( ts.regex.nondigit, '' ), table ); + return str && typeof num === 'number' ? num : + str ? $.trim( str && table.config.ignoreCase ? str.toLocaleLowerCase() : str ) : str; }, - type: 'numeric' + type : 'numeric' }); ts.regex.currencyReplace = /[+\-,. ]/g; ts.regex.currencyTest = /^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/; ts.addParser({ - id: 'currency', - is: function(s) { - s = (s || '').replace(ts.regex.currencyReplace, ''); + id : 'currency', + is : function( str ) { + str = ( str || '' ).replace( ts.regex.currencyReplace, '' ); // test for £$€¤¥¢ - return ts.regex.currencyTest.test(s); + return ts.regex.currencyTest.test( str ); }, - format: function(s, table) { - var n = ts.formatFloat((s || '').replace(ts.regex.nondigit, ''), table); - return s && typeof n === 'number' ? n : - s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s; + format : function( str, table ) { + var num = ts.formatFloat( ( str || '' ).replace( ts.regex.nondigit, '' ), table ); + return str && typeof num === 'number' ? num : + str ? $.trim( str && table.config.ignoreCase ? str.toLocaleLowerCase() : str ) : str; }, - type: 'numeric' + type : 'numeric' }); // too many protocols to add them all https://en.wikipedia.org/wiki/URI_scheme @@ -2177,72 +2410,72 @@ ts.regex.urlProtocolTest = /^(https?|ftp|file):\/\//; ts.regex.urlProtocolReplace = /(https?|ftp|file):\/\//; ts.addParser({ - id: 'url', - is: function(s) { - return ts.regex.urlProtocolTest.test(s); + id : 'url', + is : function( str ) { + return ts.regex.urlProtocolTest.test( str ); }, - format: function(s) { - return s ? $.trim(s.replace(ts.regex.urlProtocolReplace, '')) : s; + format : function( str ) { + return str ? $.trim( str.replace( ts.regex.urlProtocolReplace, '' ) ) : str; }, parsed : true, // filter widget flag - type: 'text' + type : 'text' }); ts.regex.dash = /-/g; ts.regex.isoDate = /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}/; ts.addParser({ - id: 'isoDate', - is: function(s) { - return ts.regex.isoDate.test(s); + id : 'isoDate', + is : function( str ) { + return ts.regex.isoDate.test( str ); }, - format: function(s, table) { - var date = s ? new Date( s.replace(ts.regex.dash, '/') ) : s; - return date instanceof Date && isFinite(date) ? date.getTime() : s; + format : function( str, table ) { + var date = str ? new Date( str.replace( ts.regex.dash, '/' ) ) : str; + return date instanceof Date && isFinite( date ) ? date.getTime() : str; }, - type: 'numeric' + type : 'numeric' }); ts.regex.percent = /%/g; ts.regex.percentTest = /(\d\s*?%|%\s*?\d)/; ts.addParser({ - id: 'percent', - is: function(s) { - return ts.regex.percentTest.test(s) && s.length < 15; + id : 'percent', + is : function( str ) { + return ts.regex.percentTest.test( str ) && str.length < 15; }, - format: function(s, table) { - return s ? ts.formatFloat(s.replace(ts.regex.percent, ''), table) : s; + format : function( str, table ) { + return str ? ts.formatFloat( str.replace( ts.regex.percent, '' ), table ) : str; }, - type: 'numeric' + type : 'numeric' }); // added image parser to core v2.17.9 ts.addParser({ - id: 'image', - is: function(s, table, node, $node){ - return $node.find('img').length > 0; + id : 'image', + is : function( str, table, node, $node ) { + return $node.find( 'img' ).length > 0; }, - format: function(s, table, cell) { - return $(cell).find('img').attr(table.config.imgAttr || 'alt') || s; + format : function( str, table, cell ) { + return $( cell ).find( 'img' ).attr( table.config.imgAttr || 'alt' ) || str; }, parsed : true, // filter widget flag - type: 'text' + type : 'text' }); ts.regex.dateReplace = /(\S)([AP]M)$/i; // used by usLongDate & time parser ts.regex.usLongDateTest1 = /^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i; ts.regex.usLongDateTest2 = /^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i; ts.addParser({ - id: 'usLongDate', - is: function(s) { + id : 'usLongDate', + is : function( str ) { // two digit years are not allowed cross-browser // Jan 01, 2013 12:34:56 PM or 01 Jan 2013 - return ts.regex.usLongDateTest1.test(s) || ts.regex.usLongDateTest2.test(s); + return ts.regex.usLongDateTest1.test( str ) || ts.regex.usLongDateTest2.test( str ); }, - format: function(s, table) { - var date = s ? new Date( s.replace(ts.regex.dateReplace, '$1 $2') ) : s; - return date instanceof Date && isFinite(date) ? date.getTime() : s; + format : function( str, table ) { + var date = str ? new Date( str.replace( ts.regex.dateReplace, '$1 $2' ) ) : str; + return date instanceof Date && isFinite( date ) ? date.getTime() : str; }, - type: 'numeric' + type : 'numeric' }); // testing for ##-##-#### or ####-##-##, so it's not perfect; time can be included @@ -2252,100 +2485,126 @@ // XXY covers MDY & DMY formats ts.regex.shortDateXXY = /(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/; ts.regex.shortDateYMD = /(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/; + ts.convertFormat = function( dateString, format ) { + dateString = ( dateString || '' ) + .replace( ts.regex.spaces, ' ' ) + .replace( ts.regex.shortDateReplace, '/' ); + if ( format === 'mmddyyyy' ) { + dateString = dateString.replace( ts.regex.shortDateXXY, '$3/$1/$2' ); + } else if ( format === 'ddmmyyyy' ) { + dateString = dateString.replace( ts.regex.shortDateXXY, '$3/$2/$1' ); + } else if ( format === 'yyyymmdd' ) { + dateString = dateString.replace( ts.regex.shortDateYMD, '$1/$2/$3' ); + } + var date = new Date( dateString ); + return date instanceof Date && isFinite( date ) ? date.getTime() : ''; + }; + ts.addParser({ - id: 'shortDate', // 'mmddyyyy', 'ddmmyyyy' or 'yyyymmdd' - is: function(s) { - s = (s || '').replace(ts.regex.spaces, ' ').replace(ts.regex.shortDateReplace, '/'); - return ts.regex.shortDateTest.test(s); - }, - format: function(s, table, cell, cellIndex) { - if (s) { - var date, d, - c = table.config, - ci = c.$headerIndexed[ cellIndex ], - format = ci.length && ci[0].dateFormat || - ts.getData( ci, ts.getColumnData( table, c.headers, cellIndex ), 'dateFormat') || + id : 'shortDate', // 'mmddyyyy', 'ddmmyyyy' or 'yyyymmdd' + is : function( str ) { + str = ( str || '' ).replace( ts.regex.spaces, ' ' ).replace( ts.regex.shortDateReplace, '/' ); + return ts.regex.shortDateTest.test( str ); + }, + format : function( str, table, cell, cellIndex ) { + if ( str ) { + var c = table.config, + $header = c.$headerIndexed[ cellIndex ], + format = $header.length && $header.data( 'dateFormat' ) || + ts.getData( $header, ts.getColumnData( table, c.headers, cellIndex ), 'dateFormat' ) || c.dateFormat; - d = s.replace(ts.regex.spaces, ' ').replace(ts.regex.shortDateReplace, '/'); - if (format === 'mmddyyyy') { - d = d.replace(ts.regex.shortDateXXY, '$3/$1/$2'); - } else if (format === 'ddmmyyyy') { - d = d.replace(ts.regex.shortDateXXY, '$3/$2/$1'); - } else if (format === 'yyyymmdd') { - d = d.replace(ts.regex.shortDateYMD, '$1/$2/$3'); + // save format because getData can be slow... + if ( $header.length ) { + $header.data( 'dateFormat', format ); } - date = new Date(d); - return date instanceof Date && isFinite(date) ? date.getTime() : s; + return ts.convertFormat( str, format ) || str; } - return s; + return str; }, - type: 'numeric' + type : 'numeric' }); - ts.regex.timeTest = /^(([0-2]?\d:[0-5]\d)|([0-1]?\d:[0-5]\d\s?([AP]M)))$/i; + // match 24 hour time & 12 hours time + am/pm - see http://regexr.com/3c3tk + ts.regex.timeTest = /^([1-9]|1[0-2]):([0-5]\d)(\s[AP]M)|((?:[01]\d|[2][0-4]):[0-5]\d)$/i; + ts.regex.timeMatch = /([1-9]|1[0-2]):([0-5]\d)(\s[AP]M)|((?:[01]\d|[2][0-4]):[0-5]\d)/i; ts.addParser({ - id: 'time', - is: function(s) { - return ts.regex.timeTest.test(s); + id : 'time', + is : function( str ) { + return ts.regex.timeTest.test( str ); }, - format: function(s, table) { - var date = s ? new Date( '2000/01/01 ' + s.replace(ts.regex.dateReplace, '$1 $2') ) : s; - return date instanceof Date && isFinite(date) ? date.getTime() : s; + format : function( str, table ) { + // isolate time... ignore month, day and year + var temp, + timePart = ( str || '' ).match( ts.regex.timeMatch ), + orig = new Date( str ), + // no time component? default to 00:00 by leaving it out, but only if str is defined + time = str && ( timePart !== null ? timePart[ 0 ] : '00:00 AM' ), + date = time ? new Date( '2000/01/01 ' + time.replace( ts.regex.dateReplace, '$1 $2' ) ) : time; + if ( date instanceof Date && isFinite( date ) ) { + temp = orig instanceof Date && isFinite( orig ) ? orig.getTime() : 0; + // if original string was a valid date, add it to the decimal so the column sorts in some kind of order + // luckily new Date() ignores the decimals + return temp ? parseFloat( date.getTime() + '.' + orig.getTime() ) : date.getTime(); + } + return str; }, - type: 'numeric' + type : 'numeric' }); ts.addParser({ - id: 'metadata', - is: function() { + id : 'metadata', + is : function() { return false; }, - format: function(s, table, cell) { + format : function( str, table, cell ) { var c = table.config, - p = (!c.parserMetadataName) ? 'sortValue' : c.parserMetadataName; - return $(cell).metadata()[p]; + p = ( !c.parserMetadataName ) ? 'sortValue' : c.parserMetadataName; + return $( cell ).metadata()[ p ]; }, - type: 'numeric' + type : 'numeric' }); + /* + ██████ ██████ █████▄ █████▄ ▄████▄ + ▄█▀ ██▄▄ ██▄▄██ ██▄▄██ ██▄▄██ + ▄█▀ ██▀▀ ██▀▀██ ██▀▀█ ██▀▀██ + ██████ ██████ █████▀ ██ ██ ██ ██ + */ // add default widgets ts.addWidget({ - id: 'zebra', - priority: 90, - format: function(table, c, wo) { - var $tv, $tr, row, even, time, k, i, len, - child = new RegExp(c.cssChildRow, 'i'), - b = c.$tbodies.add( $( c.namespace + '_extra_table' ).children( 'tbody:not(.' + c.cssInfoBlock + ')' ) ); - if (c.debug) { - time = new Date(); - } - for (k = 0; k < b.length; k++ ) { + id : 'zebra', + priority : 90, + format : function( table, c, wo ) { + var $visibleRows, $row, count, isEven, tbodyIndex, rowIndex, len, + child = new RegExp( c.cssChildRow, 'i' ), + $tbodies = c.$tbodies.add( $( c.namespace + '_extra_table' ).children( 'tbody:not(.' + c.cssInfoBlock + ')' ) ); + for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { // loop through the visible rows - row = 0; - $tv = b.eq( k ).children( 'tr:visible' ).not( c.selectorRemove ); - len = $tv.length; - for ( i = 0; i < len; i++ ) { - $tr = $tv.eq( i ); + count = 0; + $visibleRows = $tbodies.eq( tbodyIndex ).children( 'tr:visible' ).not( c.selectorRemove ); + len = $visibleRows.length; + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { + $row = $visibleRows.eq( rowIndex ); // style child rows the same way the parent row was styled - if ( !child.test( $tr[0].className ) ) { row++; } - even = ( row % 2 === 0 ); - $tr - .removeClass( wo.zebra[ even ? 1 : 0 ] ) - .addClass( wo.zebra[ even ? 0 : 1 ] ); + if ( !child.test( $row[ 0 ].className ) ) { count++; } + isEven = ( count % 2 === 0 ); + $row + .removeClass( wo.zebra[ isEven ? 1 : 0 ] ) + .addClass( wo.zebra[ isEven ? 0 : 1 ] ); } } }, - remove: function(table, c, wo, refreshing){ - if (refreshing) { return; } - var k, $tb, - b = c.$tbodies, - rmv = (wo.zebra || [ 'even', 'odd' ]).join(' '); - for (k = 0; k < b.length; k++ ){ - $tb = ts.processTbody(table, b.eq(k), true); // remove tbody - $tb.children().removeClass(rmv); - ts.processTbody(table, $tb, false); // restore tbody + remove : function( table, c, wo, refreshing ) { + if ( refreshing ) { return; } + var tbodyIndex, $tbody, + $tbodies = c.$tbodies, + toRemove = ( wo.zebra || [ 'even', 'odd' ] ).join( ' ' ); + for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ){ + $tbody = ts.processTbody( table, $tbodies.eq( tbodyIndex ), true ); // remove tbody + $tbody.children().removeClass( toRemove ); + ts.processTbody( table, $tbody, false ); // restore tbody } } }); -})(jQuery); +})( jQuery ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 6f5e545..599b497 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -1,10 +1,10 @@ /*** This file is dynamically generated *** █████▄ ▄████▄ █████▄ ▄████▄ ██████ ███████▄ ▄████▄ █████▄ ██ ██████ ██ ██ -██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ +██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▄▄ ██▄▄██ +██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 10-04-2015 (v2.23.5)*/ +/*! tablesorter (FORK) - updated 11-02-2015 (v2.24.2)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -372,7 +372,7 @@ })(jQuery); -/*! Widget: filter - updated 10/4/2015 (v2.23.5) *//* +/*! Widget: filter - updated 10/31/2015 (v2.24.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -941,8 +941,9 @@ // $cell parameter, but not the config, is passed to the filter_formatters, // so we have to work with it instead formatterUpdated: function( $cell, column ) { - var wo = $cell.closest( 'table' )[0].config.widgetOptions; - if ( !wo.filter_initialized ) { + // prevent error if $cell is undefined - see #1056 + var wo = $cell && $cell.closest( 'table' )[0].config.widgetOptions; + if ( wo && !wo.filter_initialized ) { // add updates by column since this function // may be called numerous times before initialization wo.filter_formatterInit[ column ] = 1; @@ -979,6 +980,16 @@ } } }, + // encode or decode filters for storage; see #1026 + processFilters: function( filters, encode ) { + var indx, + mode = encode ? encodeURIComponent : decodeURIComponent, + len = filters.length; + for ( indx = 0; indx < len; indx++ ) { + filters[ indx ] = mode( filters[ indx ] ); + } + return filters; + }, setDefaults: function( table, c, wo ) { var isArray, saved, indx, col, $filters, // get current ( default ) filters @@ -988,7 +999,7 @@ isArray = $.isArray( saved ); // make sure we're not just getting an empty array if ( !( isArray && saved.join( '' ) === '' || !isArray ) ) { - filters = saved; + filters = tsf.processFilters( saved ); } } // if no filters saved, then check default settings @@ -1011,71 +1022,81 @@ return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter; }, buildRow: function( table, c, wo ) { - var col, column, $header, makeSelect, disabled, name, ffxn, tmp, + var $filter, col, column, $header, makeSelect, disabled, name, ffxn, tmp, // c.columns defined in computeThIndexes() cellFilter = wo.filter_cellFilter, columns = c.columns, arry = $.isArray( cellFilter ), buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; for ( column = 0; column < columns; column++ ) { - buildFilter += '<td'; - if ( arry ) { - buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' ); - } else { - buildFilter += ( cellFilter !== '' ? ' class="' + cellFilter + '"' : '' ); + if ( c.$headerIndexed[ column ].length ) { + buildFilter += '<td data-column="' + column + '"'; + // account for entire column set with colspan. See #1047 + tmp = c.$headerIndexed[ column ] && c.$headerIndexed[ column ][0].colSpan || 0; + if ( tmp > 1 ) { + buildFilter += ' colspan="' + tmp + '"'; + } + if ( arry ) { + buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' ); + } else { + buildFilter += ( cellFilter !== '' ? ' class="' + cellFilter + '"' : '' ); + } + buildFilter += '></td>'; } - buildFilter += '></td>'; } c.$filters = $( buildFilter += '</tr>' ) .appendTo( c.$table.children( 'thead' ).eq( 0 ) ) - .find( 'td' ); + .children( 'td' ); // build each filter input for ( column = 0; column < columns; column++ ) { disabled = false; // assuming last cell of a column is the main column $header = c.$headerIndexed[ column ]; - ffxn = ts.getColumnData( table, wo.filter_functions, column ); - makeSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || - $header.hasClass( 'filter-select' ); - // get data from jQuery data, metadata, headers option or header class name - col = ts.getColumnData( table, c.headers, column ); - disabled = ts.getData( $header[0], col, 'filter' ) === 'false' || - ts.getData( $header[0], col, 'parser' ) === 'false'; - - if ( makeSelect ) { - buildFilter = $( '<select>' ).appendTo( c.$filters.eq( column ) ); - } else { - ffxn = ts.getColumnData( table, wo.filter_formatter, column ); - if ( ffxn ) { - wo.filter_formatterCount++; - buildFilter = ffxn( c.$filters.eq( column ), column ); - // no element returned, so lets go find it - if ( buildFilter && buildFilter.length === 0 ) { - buildFilter = c.$filters.eq( column ).children( 'input' ); + if ( $header && $header.length ) { + $filter = c.$filters.filter( '[data-column="' + column + '"]' ); + ffxn = ts.getColumnData( table, wo.filter_functions, column ); + makeSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || + $header.hasClass( 'filter-select' ); + // get data from jQuery data, metadata, headers option or header class name + col = ts.getColumnData( table, c.headers, column ); + disabled = ts.getData( $header[0], col, 'filter' ) === 'false' || + ts.getData( $header[0], col, 'parser' ) === 'false'; + + if ( makeSelect ) { + buildFilter = $( '<select>' ).appendTo( $filter ); + } else { + ffxn = ts.getColumnData( table, wo.filter_formatter, column ); + if ( ffxn ) { + wo.filter_formatterCount++; + buildFilter = ffxn( $filter, column ); + // no element returned, so lets go find it + if ( buildFilter && buildFilter.length === 0 ) { + buildFilter = $filter.children( 'input' ); + } + // element not in DOM, so lets attach it + if ( buildFilter && ( buildFilter.parent().length === 0 || + ( buildFilter.parent().length && buildFilter.parent()[0] !== $filter[0] ) ) ) { + $filter.append( buildFilter ); + } + } else { + buildFilter = $( '<input type="search">' ).appendTo( $filter ); } - // element not in DOM, so lets attach it - if ( buildFilter && ( buildFilter.parent().length === 0 || - ( buildFilter.parent().length && buildFilter.parent()[0] !== c.$filters[column] ) ) ) { - c.$filters.eq( column ).append( buildFilter ); + if ( buildFilter ) { + tmp = $header.data( 'placeholder' ) || + $header.attr( 'data-placeholder' ) || + wo.filter_placeholder.search || ''; + buildFilter.attr( 'placeholder', tmp ); } - } else { - buildFilter = $( '<input type="search">' ).appendTo( c.$filters.eq( column ) ); } if ( buildFilter ) { - tmp = $header.data( 'placeholder' ) || - $header.attr( 'data-placeholder' ) || - wo.filter_placeholder.search || ''; - buildFilter.attr( 'placeholder', tmp ); - } - } - if ( buildFilter ) { - // add filter class name - name = ( $.isArray( wo.filter_cssFilter ) ? - ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : - wo.filter_cssFilter ) || ''; - buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column ); - if ( disabled ) { - buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; + // add filter class name + name = ( $.isArray( wo.filter_cssFilter ) ? + ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : + wo.filter_cssFilter ) || ''; + buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column ); + if ( disabled ) { + buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; + } } } } @@ -1165,9 +1186,9 @@ if ( $.isEmptyObject( c.cache ) ) { // update cache if delayInit set & pager has initialized ( after user initiates a search ) if ( c.delayInit && c.pager && c.pager.initialized ) { - c.$table.trigger( 'updateCache', [ function() { + ts.updateCache( c, function() { tsf.checkFilters( table, false, skipFirst ); - } ] ); + }); } return; } @@ -1205,10 +1226,10 @@ return false; } }, - hideFilters: function( c ) { - var timer; - c.$table - .find( '.' + tscss.filterRow ) + hideFilters: function( c, $table ) { + var timer, + $row = ( $table || c.$table ).find( '.' + tscss.filterRow ).addClass( tscss.filterRowHide ); + $row .bind( 'mouseenter mouseleave', function( e ) { // save event object - http://bugs.jquery.com/ticket/12140 var event = e, @@ -1625,7 +1646,7 @@ // ( '> -10' => '> -100' will ignore hidden rows ) !( regex.isNeg1.test( val ) || regex.isNeg2.test( val ) ) && // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 - !( val !== '' && c.$filters && c.$filters.eq( indx ).find( 'select' ).length && + !( val !== '' && c.$filters && c.$filters.filter( '[data-column="' + indx + '"]' ).find( 'select' ).length && !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); } } @@ -1732,7 +1753,7 @@ c.lastSearch = storedFilters; c.$table.data( 'lastSearch', storedFilters ); if ( wo.filter_saveFilters && ts.storage ) { - ts.storage( table, 'tablesorter-filters', storedFilters ); + ts.storage( table, 'tablesorter-filters', tsf.processFilters( storedFilters, true ) ); } if ( c.debug ) { console.log( 'Completed filter widget search' + ts.benchmark(time) ); @@ -1741,7 +1762,7 @@ c.$table.trigger( 'filterEnd', c ); } setTimeout( function() { - c.$table.trigger( 'applyWidgets' ); // make sure zebra widget is applied + ts.applyWidget( c.table ); // make sure zebra widget is applied }, 0 ); }, getOptionSource: function( table, column, onlyAvail ) { @@ -2073,7 +2094,7 @@ })( jQuery ); -/*! Widget: stickyHeaders - updated 3/26/2015 (v2.21.3) *//* +/*! Widget: stickyHeaders - updated 10/31/2015 (v2.24.0) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -2337,7 +2358,7 @@ ts.filter.bindSearch( $table, $stickyCells.find('.' + ts.css.filter) ); // support hideFilters if (wo.filter_hideFilters) { - ts.filter.hideFilters($stickyTable, c); + ts.filter.hideFilters(c, $stickyTable); } } @@ -2757,7 +2778,10 @@ })( jQuery, window ); -/*! Widget: saveSort */ +/*! Widget: saveSort - updated 10/31/2015 (v2.24.0) *//* +* Requires tablesorter v2.16+ +* by Rob Garrison +*/ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; @@ -2813,7 +2837,7 @@ c.sortList = sortList; } else if (table.hasInitialized && sortList && sortList.length > 0) { // update sort change - $table.trigger('sorton', [ sortList ]); + ts.sortOn( c, sortList ); } } }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js index 6eb8679..17dfd94 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js @@ -1,36 +1,61 @@ -/*! Parser: Month - updated 10/26/2014 (v2.18.0) */ +/*! Parser: Month - updated 11/2/2015 (v2.24.1) */ /* Demo: http://jsfiddle.net/Mottie/abkNM/4169/ */ /*jshint jquery:true */ ;(function($){ 'use strict'; var ts = $.tablesorter; - ts.dates = $.extend({}, ts.dates, { - // *** modify this array to match the desired language *** - monthCased : [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] - }); - ts.dates.monthLower = ts.dates.monthCased.join(',').toLocaleLowerCase().split(','); + ts.dates = $.extend( {}, { + // See http://mottie.github.io/tablesorter/docs/example-widget-grouping.html + // for details on how to use CLDR data for a locale to add data for this parser + // CLDR returns an object { 1: "Jan", 2: "Feb", 3: "Mar", ..., 12: "Dec" } + months : { + 'en' : { + 1 : 'Jan', + 2 : 'Feb', + 3 : 'Mar', + 4 : 'Apr', + 5 : 'May', + 6 : 'Jun', + 7 : 'Jul', + 8 : 'Aug', + 9 : 'Sep', + 10: 'Oct', + 11: 'Nov', + 12: 'Dec' + } + } + }, ts.dates ); ts.addParser({ id: 'month', - is: function(){ + is: function() { return false; }, - format: function(s, table) { - if (s) { - var j = -1, c = table.config, - n = c.ignoreCase ? s.toLocaleLowerCase() : s; - $.each(ts.dates[ 'month' + (c.ignoreCase ? 'Lower' : 'Cased') ], function(i, v){ - if (j < 0 && n.match(v)) { - j = i; - return false; + format: function( str, table, cell, cellIndex ) { + if ( str ) { + var m, month, + c = table.config, + // add options to 'config.globalize' for all columns --> globalize : { lang: 'en' } + // or per column by using the column index --> globalize : { 0 : { lang: 'fr' } } + options = c.globalize && ( c.globalize[ cellIndex ] || c.globalize ) || {}, + months = ts.dates.months[ options.lang || 'en' ]; + if ( c.ignoreCase ) { + str = str.toLowerCase(); + } + for ( month in months ) { + if ( typeof month === 'string' ) { + m = months[ month ]; + if ( c.ignoreCase ) { + m = m.toLowerCase(); + } + if ( str.match( m ) ) { + return parseInt( month, 10 ); + } } - }); - // return s (original string) if there isn't a match - // (non-weekdays will sort separately and empty cells will sort as expected) - return j < 0 ? s : j; + } } - return s; + return str; }, type: 'numeric' }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js index 0a00cda..c746df8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js @@ -1,36 +1,94 @@ -/*! Parser: weekday - updated 10/26/2014 (v2.18.0) */ +/*! Parser: weekday - updated 11/2/2015 (v2.24.1) */ /* Demo: http://jsfiddle.net/Mottie/abkNM/4169/ */ /*jshint jquery:true */ ;(function($){ 'use strict'; var ts = $.tablesorter; - ts.dates = $.extend({}, ts.dates, { - // *** modify this array to change match the language *** - weekdayCased : [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ] - }); - ts.dates.weekdayLower = ts.dates.weekdayCased.join(',').toLocaleLowerCase().split(','); + ts.dates = $.extend( true, {}, { + // See http://mottie.github.io/tablesorter/docs/example-widget-grouping.html + // for details on how to use CLDR data for a locale to add data for this parser + // CLDR returns { sun: "Sun", mon: "Mon", tue: "Tue", wed: "Wed", thu: "Thu", ... } + weekdays : { + 'en' : { + 'sun' : 'Sun', + 'mon' : 'Mon', + 'tue' : 'Tue', + 'wed' : 'Wed', + 'thu' : 'Thu', + 'fri' : 'Fri', + 'sat' : 'Sat' + } + }, + + // set table.config.weekStarts to change weekday start date for your locale + // cross-reference of a date on which the week starts on a... + // https://github.com/unicode-cldr/cldr-core/blob/master/supplemental/weekData.json + weekStartList : { + 'sun' : '1995', // Sun 1/1/1995 + 'mon' : '1996', // Mon 1/1/1996 + 'fri' : '1999', // Friday 1/1/1999 + 'sat' : '2000' // Sat 1/1/2000 + }, + + // do not modify this array; it is used for cross referencing + weekdaysXref : [ 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat' ] + + }, ts.dates ); ts.addParser({ id: 'weekday', - is: function(){ + is: function() { return false; }, - format: function(s, table) { - if (s) { - var j = -1, c = table.config; - s = c.ignoreCase ? s.toLocaleLowerCase() : s; - $.each(ts.dates[ 'weekday' + (c.ignoreCase ? 'Lower' : 'Cased') ], function(i, v){ - if (j < 0 && s.match(v)) { - j = i; - return false; + format: function( str, table, cell, cellIndex ) { + if ( str ) { + var d, day, num, + c = table.config, + // add options to 'config.globalize' for all columns --> globalize : { lang: 'en' } + // or per column by using the column index --> globalize : { 0 : { lang: 'fr' } } + options = c.globalize && ( c.globalize[ cellIndex ] || c.globalize ) || {}, + days = ts.dates.weekdays[ options.lang || 'en' ], + xref = ts.dates.weekdaysXref; + if ( c.ignoreCase ) { + str = str.toLowerCase(); + } + for ( day in days ) { + if ( typeof day === 'string' ) { + d = days[ day ]; + if ( c.ignoreCase ) { + d = d.toLowerCase(); + } + if ( str.match( d ) ) { + num = $.inArray( day, xref ); + return num > -1 ? num : str; + } } - }); - // return s (original string) if there isn't a match - // (non-weekdays will sort separately and empty cells will sort as expected) - return j < 0 ? s : j; + } + } + return str; + }, + type: 'numeric' + }); + + // useful when a group widget date column is set to "group-date-week" + // and you want to only sort on the day of the week ignore the actual date, month and year + ts.addParser({ + id: 'weekday-index', + is: function() { + return false; + }, + format: function( str, table ) { + if ( str ) { + var c = table.config, + date = new Date( str ); + if ( date instanceof Date && isFinite( date ) ) { + // use a specific date that started with that weekday so sorting is only going to be + // based on the day of the week and not the date, month or year + return new Date( '1/' + ( date.getDay() + 1 ) + '/' + ts.dates.weekStartList[ c.weekStarts || 'sun' ] ); + } } - return s; + return str; }, type: 'numeric' }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-globalize.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-globalize.js index aa64fc7..fddf043 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-globalize.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-globalize.js @@ -1,24 +1,35 @@ -/*! Parser: jQuery Globalize - updated 5/17/2015 (v2.22.0) */ +/*! Parser: jQuery Globalize - updated 11/2/2015 (v2.24.1) */ /* Extract localized data using jQuery's Globalize parsers; set - Globalize.locale( 'xx' ) prior to initializing tablesorter! */ -/*jshint jquery:true */ + Globalize.locale( 'xx' ) in the globalize settings */ +/*jshint jquery:true, newcap: false */ /*global Globalize:false */ ;( function( $ ) { 'use strict'; /*! jQuery Globalize date parser (https://github.com/jquery/globalize#date-module) */ - /* demo: http://jsfiddle.net/Mottie/0j18Lw8r/ */ $.tablesorter.addParser({ id: 'globalize-date', is: function () { return false; }, format: function ( str, table, cell, cellIndex ) { - var c = table.config, - // add options to 'config.globalize' for all columns --> globalize : { skeleton: 'GyMMMd' } - // or per column by using the column index --> globalize : { 0 : { datetime: 'medium' } } - options = c.globalize && ( c.globalize[ cellIndex ] || c.globalize ) || {}, - date = Globalize && Globalize.dateParser ? Globalize.dateParser( options )( str ) : + var globalize, date, + c = table.config, + // add options to 'config.globalize' for all columns --> globalize : { skeleton: 'GyMMMd' } + // or per column by using the column index --> globalize : { 0 : { datetime: 'medium' } } + options = c.globalize && ( c.globalize[ cellIndex ] || c.globalize ) || {}; + if ( Globalize ) { + globalize = typeof options.Globalize === 'object' ? + // initialized Globalize object + options.Globalize : + // Globalize initialized from "lang" option + Globalize( options.lang || 'en' ); + if ( !options.Globalize ) { + // cache the object + options.Globalize = globalize; + } + } + date = globalize && globalize.dateParser ? globalize.dateParser( options )( str ) : str ? new Date( str ) : str; return date instanceof Date && isFinite( date ) ? date.getTime() : str; }, @@ -26,18 +37,29 @@ }); /*! jQuery Globalize number parser (https://github.com/jquery/globalize#number-module) */ - /* demo: http://jsfiddle.net/Mottie/0j18Lw8r/ */ $.tablesorter.addParser({ id: 'globalize-number', is: function () { return false; }, format: function ( str, table, cell, cellIndex ) { - var c = table.config, - // add options to 'config.globalize' for all columns --> globalize : { skeleton: 'GyMMMd' } - // or per column by using the column index --> globalize : { 0 : { datetime: 'medium' } } - options = c.globalize && ( c.globalize[ cellIndex ] || c.globalize ) || {}, - num = Globalize && Globalize.numberParser ? Globalize.numberParser( options )( str ) : + var globalize, num, + c = table.config, + // add options to 'config.globalize' for all columns --> globalize : { skeleton: 'GyMMMd' } + // or per column by using the column index --> globalize : { 0 : { datetime: 'medium' } } + options = c.globalize && ( c.globalize[ cellIndex ] || c.globalize ) || {}; + if ( Globalize ) { + globalize = typeof options.Globalize === 'object' ? + // initialized Globalize object + options.Globalize : + // Globalize initialized from "lang" option + Globalize( options.lang || 'en' ); + if ( !options.Globalize ) { + // cache the object + options.Globalize = globalize; + } + } + num = globalize && globalize.numberParser ? globalize.numberParser( options )( str ) : str ? $.tablesorter.formatFloat( ( str || '' ).replace( /[^\w,. \-()]/g, '' ), table ) : str; return str && typeof num === 'number' ? num : str; }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index f78d9b1..38621a5 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,4 +1,4 @@ -/*! Parser: input & select - updated 8/17/2015 (v2.23.0) *//* +/*! Parser: input & select - updated 10/31/2015 (v2.24.0) *//* * for jQuery 1.7+ & tablesorter 2.7.11+ * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ @@ -175,12 +175,12 @@ return; } // ignore change event if nothing changed - if ( val !== $target.data( 'ts-original-value' ) || event.target.type === 'checkbox' ) { + if ( c && val !== $target.data( 'ts-original-value' ) || event.target.type === 'checkbox' ) { $target.data( 'ts-original-value', val ); // pass undefined resort value so it falls back to config.resort setting - $table.trigger( 'updateCell', [ $cell, undef, function() { + $.tablesorter.updateCell( c, $cell, undef, function() { updateServer( event, $table, $target ); - } ]); + }); } } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js index b85fa96..a28f2cb 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js @@ -1,4 +1,4 @@ -/* Widget: chart (beta) - updated 2/7/2015 (v2.19.0) *//* +/* Widget: chart (beta) - updated 10/31/2015 (v2.24.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ */ /*jshint browser:true, jquery:true, unused:false */ @@ -213,7 +213,7 @@ } if (value !== false) { - if ( /s/i.test( '' + wo.chart_layout[row.length] ) ) { + if ( /s/i.test( '' + wo.chart_layout[indx] ) ) { row.push( value ); chart_series[objIndex].data.push( value ); chart_dataset[objIndex].data.push( value ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index 368af1c..f113ca5 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Widget: columnSelector (responsive table widget) - updated 8/23/2015 (v2.23.2) *//* +/* Widget: columnSelector (responsive table widget) - updated 10/31/2015 (v2.24.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -35,6 +35,7 @@ colSel.$breakpoints = $('<style></style>').prop('disabled', true).appendTo('head'); colSel.isInitializing = true; + tsColSel.setUpColspan(c, wo); tsColSel.setupSelector(c, wo); if (wo.columnSelector_mediaquery) { @@ -103,10 +104,11 @@ tsColSel.updateBreakpoints(c, wo); tsColSel.updateCols(c, wo); } + tsColSel.adjustColspans( c, wo ); }, setupSelector: function(c, wo) { - var name, + var index, name, $header, priority, col, colId, colSel = c.selector, $container = colSel.$container, useStorage = wo.columnSelector_saveColumns && ts.storage, @@ -121,18 +123,19 @@ colSel.$wrapper = []; colSel.$checkbox = []; // populate the selector container - c.$table.children('thead').find('tr:first th', c.table).each(function() { - var $this = $(this), - // if no data-priority is assigned, default to 1, but don't remove it from the selector list - priority = $this.attr(wo.columnSelector_priority) || 1, - colId = $this.attr('data-column'), - state = ts.getData(this, c.headers[colId], 'columnSelector'); + for ( index = 0; index < c.columns; index++ ) { + $header = c.$headerIndexed[ index ]; + // if no data-priority is assigned, default to 1, but don't remove it from the selector list + priority = $header.attr(wo.columnSelector_priority) || 1; + colId = $header.attr('data-column'); + col = ts.getColumnData( c.table, c.headers, colId ); + state = ts.getData( $header, col, 'columnSelector'); // if this column not hidable at all // include getData check (includes 'columnSelector-false' class, data attribute, etc) if ( isNaN(priority) && priority.length > 0 || state === 'disable' || ( wo.columnSelector_columns[colId] && wo.columnSelector_columns[colId] === 'disable') ) { - return true; // goto next + continue; // goto next } // set default state; storage takes priority @@ -142,7 +145,7 @@ colSel.$column[colId] = $(this); // set default col title - name = $this.attr(wo.columnSelector_name) || $this.text(); + name = $header.attr(wo.columnSelector_name) || $header.text(); if ($container.length) { colSel.$wrapper[colId] = $(wo.columnSelector_layout.replace(/\{name\}/g, name)).appendTo($container); colSel.$checkbox[colId] = colSel.$wrapper[colId] @@ -152,15 +155,17 @@ .toggleClass( wo.columnSelector_cssChecked, colSel.states[colId] ) .prop('checked', colSel.states[colId]) .on('change', function(){ - colSel.states[colId] = this.checked; + // ensure states is accurate + var colId = $(this).attr('data-column'); + c.selector.states[colId] = this.checked; tsColSel.updateCols(c, wo); }).change(); } - }); + } }, - setupBreakpoints: function(c, wo){ + setupBreakpoints: function(c, wo) { var colSel = c.selector; // add responsive breakpoints @@ -222,14 +227,25 @@ if (wo.columnSelector_saveColumns && ts.storage) { ts.storage( c.$table[0], 'tablesorter-columnSelector-auto', { auto : colSel.auto } ); } + tsColSel.adjustColspans( c, wo ); // trigger columnUpdate if auto is true (it gets skipped in updateCols() if (colSel.auto) { c.$table.trigger(wo.columnSelector_updated); } }, - + addSelectors: function( prefix, column ) { + var array = [], + temp = ' col:nth-child(' + column + ')'; + array.push(prefix + temp + ',' + prefix + '_extra_table' + temp); + temp = ' tr th:nth-child(' + column + ')'; + array.push(prefix + temp + ',' + prefix + '_extra_table' + temp); + temp = ' tr td:nth-child(' + column + ')'; + array.push(prefix + temp + ',' + prefix + '_extra_table' + temp); + return array; + }, updateBreakpoints: function(c, wo) { - var priority, column, breaks, temp, + var priority, col, column, breaks, + isHidden = [], colSel = c.selector, prefix = c.namespace + 'columnselector', mediaAll = [], @@ -239,19 +255,27 @@ colSel.$style.prop('disabled', false); return; } - + if (wo.columnSelector_mediaqueryHidden) { + // add columns to be hidden; even when "auto" is set - see #964 + for ( column = 0; column < c.columns; column++ ) { + col = ts.getColumnData( c.table, c.headers, column ); + isHidden[ column + 1 ] = ts.getData( c.$headerIndexed[ column ], col, 'columnSelector' ) === 'false'; + if ( isHidden[ column + 1 ] ) { + // hide columnSelector false column (in auto mode) + mediaAll = mediaAll.concat( tsColSel.addSelectors( prefix, column + 1 ) ); + } + } + } // only 6 breakpoints (same as jQuery Mobile) for (priority = 0; priority < 6; priority++){ /*jshint loopfunc:true */ breaks = []; c.$headers.filter('[' + wo.columnSelector_priority + '=' + (priority + 1) + ']').each(function(){ column = parseInt($(this).attr('data-column'), 10) + 1; - temp = ' col:nth-child(' + column + ')'; - breaks.push(prefix + temp + ',' + prefix + '_extra_table' + temp); - temp = ' tr th:nth-child(' + column + ')'; - breaks.push(prefix + temp + ',' + prefix + '_extra_table' + temp); - temp = ' tr td:nth-child(' + column + ')'; - breaks.push(prefix + temp + ',' + prefix + '_extra_table' + temp); + // don't reveal columnSelector false columns + if ( !isHidden[ column ] ) { + breaks = breaks.concat( tsColSel.addSelectors( prefix, column ) ); + } }); if (breaks.length) { mediaAll = mediaAll.concat( breaks ); @@ -269,24 +293,18 @@ .html( tsColSel.queryAll.replace(/\[columns\]/g, mediaAll.join(',')) + breakpts ); } }, - updateCols: function(c, wo) { if (wo.columnSelector_mediaquery && c.selector.auto || c.selector.isInitializing) { return; } - var column, temp, + var column, colSel = c.selector, styles = [], prefix = c.namespace + 'columnselector'; colSel.$container.find('input[data-column]').filter('[data-column!="auto"]').each(function(){ if (!this.checked) { column = parseInt( $(this).attr('data-column'), 10 ) + 1; - temp = ' col:nth-child(' + column + ')'; - styles.push(prefix + temp + ',' + prefix + '_extra_table' + temp); - temp = ' tr th:nth-child(' + column + ')'; - styles.push(prefix + temp + ',' + prefix + '_extra_table' + temp); - temp = ' tr td:nth-child(' + column + ')'; - styles.push(prefix + temp + ',' + prefix + '_extra_table' + temp); + styles = styles.concat( tsColSel.addSelectors( prefix, column ) ); } $(this).toggleClass( wo.columnSelector_cssChecked, this.checked ); }); @@ -299,9 +317,71 @@ if (wo.columnSelector_saveColumns && ts.storage) { ts.storage( c.$table[0], 'tablesorter-columnSelector', colSel.states ); } + tsColSel.adjustColspans( c, wo ); c.$table.trigger(wo.columnSelector_updated); }, + setUpColspan: function(c, wo) { + var index, span, nspace, + $window = $( window ), + hasSpans = false, + $cells = c.$table + .add( $(c.namespace + '_extra_table') ) + .children('thead, tfoot') + .children('tr') + .children('th, td'), + len = $cells.length; + for ( index = 0; index < len; index++ ) { + span = $cells[ index ].colSpan; + if ( span > 1 ) { + hasSpans = true; + $cells.eq( index ) + .addClass( c.namespace.slice( 1 ) + 'columnselectorHasSpan' ) + .attr( 'data-col-span', span ); + } + } + // only add resize end if using media queries + if ( hasSpans && wo.columnSelector_mediaquery ) { + nspace = c.namespace.slice( 1 ) + 'columnselector'; + // Setup window.resizeEnd event + $window + .off( nspace ) + .on( 'resize' + nspace, ts.window_resize ) + .on( 'resizeEnd' + nspace, function() { + // IE calls resize when you modify content, so we have to unbind the resize event + // so we don't end up with an infinite loop. we can rebind after we're done. + $window.off( 'resize' + nspace, ts.window_resize ); + tsColSel.adjustColspans( c, wo ); + $window.on( 'resize' + nspace, ts.window_resize ); + }); + } + }, + adjustColspans: function(c, wo) { + var index, cols, col, span, end, + colSel = c.selector, + autoModeOn = colSel.auto, + $colspans = $( c.namespace + 'columnselectorHasSpan' ), + len = $colspans.length; + if ( len ) { + for ( index = 0; index < len; index++ ) { + col = parseInt( $colspans.eq(index).attr('data-column'), 10 ); + span = parseInt( $colspans.eq(index).attr('data-col-span'), 10 ); + end = col + span; + for ( cols = col; cols < end; cols++ ) { + if ( !autoModeOn && colSel.states[ cols ] === false || + autoModeOn && c.$headerIndexed[ cols ] && !c.$headerIndexed[ cols ].is(':visible') ) { + span--; + } + } + if ( span ) { + $colspans.eq(index).show()[0].colSpan = span; + } else { + $colspans.eq(index).hide(); + } + } + } + }, + attachTo : function(table, elm) { table = $(table)[0]; var colSel, wo, indx, @@ -336,6 +416,16 @@ }; + /* Add window resizeEnd event (also used by scroller widget) */ + ts.window_resize = function() { + if ( ts.timer_resize ) { + clearTimeout( ts.timer_resize ); + } + ts.timer_resize = setTimeout( function() { + $( window ).trigger( 'resizeEnd' ); + }, 250 ); + }; + ts.addWidget({ id: 'columnSelector', priority: 10, @@ -360,6 +450,8 @@ columnSelector_mediaqueryName: 'Auto: ', // breakpoints checkbox initial setting columnSelector_mediaqueryState: true, + // hide columnSelector false columns while in auto mode + columnSelector_mediaqueryHidden: false, // responsive table hides columns with priority 1-6 at these breakpoints // see http://view.jquerymobile.com/1.3.2/dist/demos/widgets/table-column-toggle/#Applyingapresetbreakpoint // *** set to false to disable *** diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js index 470fdc6..62d25be 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js @@ -1,4 +1,4 @@ -/*! Widget: editable - updated 8/17/2015 (v2.23.0) *//* +/*! Widget: editable - updated 10/31/2015 (v2.24.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -193,7 +193,9 @@ } if ( e.which === 27 ) { // user cancelled - $this.html( $this.data( 'original' ) ).trigger( 'blur' + namespace ); + $this + .html( $this.data( 'original' ) ) + .trigger( 'blur' + namespace ); c.$table.data( 'contentFocused', false ); c.table.isUpdating = false; return false; @@ -220,17 +222,17 @@ .data( 'before', valid ) .data( 'original', valid ) .trigger( 'change' ); - c.$table.trigger( 'updateCell', [ $this.closest( 'td' ), false, function() { + $.tablesorter.updateCell( c, $this.closest( 'td' ), false, function() { if ( wo.editable_autoResort ) { setTimeout( function() { - c.$table.trigger( 'sorton', [ c.sortList, function() { + $.tablesorter.sortOn( c, c.sortList, function() { tse.editComplete( c, wo, c.$table.data( 'contentFocused' ), true ); - }, true ] ); + }, true ); }, 10 ); } else { tse.editComplete( c, wo, c.$table.data( 'contentFocused' ) ); } - } ] ); + }); return false; } } else if ( !valid && e.type !== 'keydown' ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 739f0b0..3b88349 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 10/4/2015 (v2.23.5) *//* +/*! Widget: filter - updated 10/31/2015 (v2.24.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -567,8 +567,9 @@ // $cell parameter, but not the config, is passed to the filter_formatters, // so we have to work with it instead formatterUpdated: function( $cell, column ) { - var wo = $cell.closest( 'table' )[0].config.widgetOptions; - if ( !wo.filter_initialized ) { + // prevent error if $cell is undefined - see #1056 + var wo = $cell && $cell.closest( 'table' )[0].config.widgetOptions; + if ( wo && !wo.filter_initialized ) { // add updates by column since this function // may be called numerous times before initialization wo.filter_formatterInit[ column ] = 1; @@ -605,6 +606,16 @@ } } }, + // encode or decode filters for storage; see #1026 + processFilters: function( filters, encode ) { + var indx, + mode = encode ? encodeURIComponent : decodeURIComponent, + len = filters.length; + for ( indx = 0; indx < len; indx++ ) { + filters[ indx ] = mode( filters[ indx ] ); + } + return filters; + }, setDefaults: function( table, c, wo ) { var isArray, saved, indx, col, $filters, // get current ( default ) filters @@ -614,7 +625,7 @@ isArray = $.isArray( saved ); // make sure we're not just getting an empty array if ( !( isArray && saved.join( '' ) === '' || !isArray ) ) { - filters = saved; + filters = tsf.processFilters( saved ); } } // if no filters saved, then check default settings @@ -637,71 +648,81 @@ return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter; }, buildRow: function( table, c, wo ) { - var col, column, $header, makeSelect, disabled, name, ffxn, tmp, + var $filter, col, column, $header, makeSelect, disabled, name, ffxn, tmp, // c.columns defined in computeThIndexes() cellFilter = wo.filter_cellFilter, columns = c.columns, arry = $.isArray( cellFilter ), buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; for ( column = 0; column < columns; column++ ) { - buildFilter += '<td'; - if ( arry ) { - buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' ); - } else { - buildFilter += ( cellFilter !== '' ? ' class="' + cellFilter + '"' : '' ); + if ( c.$headerIndexed[ column ].length ) { + buildFilter += '<td data-column="' + column + '"'; + // account for entire column set with colspan. See #1047 + tmp = c.$headerIndexed[ column ] && c.$headerIndexed[ column ][0].colSpan || 0; + if ( tmp > 1 ) { + buildFilter += ' colspan="' + tmp + '"'; + } + if ( arry ) { + buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' ); + } else { + buildFilter += ( cellFilter !== '' ? ' class="' + cellFilter + '"' : '' ); + } + buildFilter += '></td>'; } - buildFilter += '></td>'; } c.$filters = $( buildFilter += '</tr>' ) .appendTo( c.$table.children( 'thead' ).eq( 0 ) ) - .find( 'td' ); + .children( 'td' ); // build each filter input for ( column = 0; column < columns; column++ ) { disabled = false; // assuming last cell of a column is the main column $header = c.$headerIndexed[ column ]; - ffxn = ts.getColumnData( table, wo.filter_functions, column ); - makeSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || - $header.hasClass( 'filter-select' ); - // get data from jQuery data, metadata, headers option or header class name - col = ts.getColumnData( table, c.headers, column ); - disabled = ts.getData( $header[0], col, 'filter' ) === 'false' || - ts.getData( $header[0], col, 'parser' ) === 'false'; + if ( $header && $header.length ) { + $filter = c.$filters.filter( '[data-column="' + column + '"]' ); + ffxn = ts.getColumnData( table, wo.filter_functions, column ); + makeSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || + $header.hasClass( 'filter-select' ); + // get data from jQuery data, metadata, headers option or header class name + col = ts.getColumnData( table, c.headers, column ); + disabled = ts.getData( $header[0], col, 'filter' ) === 'false' || + ts.getData( $header[0], col, 'parser' ) === 'false'; - if ( makeSelect ) { - buildFilter = $( '<select>' ).appendTo( c.$filters.eq( column ) ); - } else { - ffxn = ts.getColumnData( table, wo.filter_formatter, column ); - if ( ffxn ) { - wo.filter_formatterCount++; - buildFilter = ffxn( c.$filters.eq( column ), column ); - // no element returned, so lets go find it - if ( buildFilter && buildFilter.length === 0 ) { - buildFilter = c.$filters.eq( column ).children( 'input' ); + if ( makeSelect ) { + buildFilter = $( '<select>' ).appendTo( $filter ); + } else { + ffxn = ts.getColumnData( table, wo.filter_formatter, column ); + if ( ffxn ) { + wo.filter_formatterCount++; + buildFilter = ffxn( $filter, column ); + // no element returned, so lets go find it + if ( buildFilter && buildFilter.length === 0 ) { + buildFilter = $filter.children( 'input' ); + } + // element not in DOM, so lets attach it + if ( buildFilter && ( buildFilter.parent().length === 0 || + ( buildFilter.parent().length && buildFilter.parent()[0] !== $filter[0] ) ) ) { + $filter.append( buildFilter ); + } + } else { + buildFilter = $( '<input type="search">' ).appendTo( $filter ); } - // element not in DOM, so lets attach it - if ( buildFilter && ( buildFilter.parent().length === 0 || - ( buildFilter.parent().length && buildFilter.parent()[0] !== c.$filters[column] ) ) ) { - c.$filters.eq( column ).append( buildFilter ); + if ( buildFilter ) { + tmp = $header.data( 'placeholder' ) || + $header.attr( 'data-placeholder' ) || + wo.filter_placeholder.search || ''; + buildFilter.attr( 'placeholder', tmp ); } - } else { - buildFilter = $( '<input type="search">' ).appendTo( c.$filters.eq( column ) ); } if ( buildFilter ) { - tmp = $header.data( 'placeholder' ) || - $header.attr( 'data-placeholder' ) || - wo.filter_placeholder.search || ''; - buildFilter.attr( 'placeholder', tmp ); - } - } - if ( buildFilter ) { - // add filter class name - name = ( $.isArray( wo.filter_cssFilter ) ? - ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : - wo.filter_cssFilter ) || ''; - buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column ); - if ( disabled ) { - buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; + // add filter class name + name = ( $.isArray( wo.filter_cssFilter ) ? + ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : + wo.filter_cssFilter ) || ''; + buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column ); + if ( disabled ) { + buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; + } } } } @@ -791,9 +812,9 @@ if ( $.isEmptyObject( c.cache ) ) { // update cache if delayInit set & pager has initialized ( after user initiates a search ) if ( c.delayInit && c.pager && c.pager.initialized ) { - c.$table.trigger( 'updateCache', [ function() { + ts.updateCache( c, function() { tsf.checkFilters( table, false, skipFirst ); - } ] ); + }); } return; } @@ -831,10 +852,10 @@ return false; } }, - hideFilters: function( c ) { - var timer; - c.$table - .find( '.' + tscss.filterRow ) + hideFilters: function( c, $table ) { + var timer, + $row = ( $table || c.$table ).find( '.' + tscss.filterRow ).addClass( tscss.filterRowHide ); + $row .bind( 'mouseenter mouseleave', function( e ) { // save event object - http://bugs.jquery.com/ticket/12140 var event = e, @@ -1251,7 +1272,7 @@ // ( '> -10' => '> -100' will ignore hidden rows ) !( regex.isNeg1.test( val ) || regex.isNeg2.test( val ) ) && // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 - !( val !== '' && c.$filters && c.$filters.eq( indx ).find( 'select' ).length && + !( val !== '' && c.$filters && c.$filters.filter( '[data-column="' + indx + '"]' ).find( 'select' ).length && !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); } } @@ -1358,7 +1379,7 @@ c.lastSearch = storedFilters; c.$table.data( 'lastSearch', storedFilters ); if ( wo.filter_saveFilters && ts.storage ) { - ts.storage( table, 'tablesorter-filters', storedFilters ); + ts.storage( table, 'tablesorter-filters', tsf.processFilters( storedFilters, true ) ); } if ( c.debug ) { console.log( 'Completed filter widget search' + ts.benchmark(time) ); @@ -1367,7 +1388,7 @@ c.$table.trigger( 'filterEnd', c ); } setTimeout( function() { - c.$table.trigger( 'applyWidgets' ); // make sure zebra widget is applied + ts.applyWidget( c.table ); // make sure zebra widget is applied }, 0 ); }, getOptionSource: function( table, column, onlyAvail ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index b33eaf0..9321ac1 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -1,4 +1,4 @@ -/*! Widget: grouping - updated 9/1/2015 (v2.23.3) *//* +/*! Widget: grouping - updated 11/2/2015 (v2.24.1) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -6,60 +6,97 @@ /*global jQuery: false */ ;(function($){ 'use strict'; - var ts = $.tablesorter; + var ts = $.tablesorter, - ts.grouping = { + tsg = ts.grouping = { types : { - number : function(c, $column, txt, num, group){ - var value, word; - if (num > 1 && txt !== '') { - if ($column.hasClass(ts.css.sortAsc)) { - value = Math.floor(parseFloat(txt) / num) * num; - return value > parseFloat(group || 0) ? value : parseFloat(group || 0); + number : function(c, $column, txt, num) { + var word, result, + ascSort = $column.hasClass( ts.css.sortAsc ); + if ( num > 1 && txt !== '' ) { + if ( ascSort ) { + result = Math.floor( parseFloat( txt ) / num ) * num; } else { - value = Math.ceil(parseFloat(txt) / num) * num; - return value < parseFloat(group || num) - value ? parseFloat(group || num) - value : value; + result = Math.ceil( parseFloat( txt ) / num ) * num; } + // show range + result += ' - ' + ( result + ( num - 1 ) * ( ascSort ? 1 : -1 ) ); } else { - word = (txt + '').match(/\d+/g); - return word && word.length >= num ? word[num - 1] : txt || ''; + result = parseFloat( txt ) || txt; } + return result; }, - separator : function(c, $column, txt, num){ + separator : function(c, $column, txt, num) { var word = (txt + '').split(c.widgetOptions.group_separator); - return $.trim(word && num > 0 && word.length >= num ? word[(num || 1) - 1] : ''); + // return $.trim(word && num > 0 && word.length >= num ? word[(num || 1) - 1] : ''); + return $.trim( word[ num - 1 ] || '' ); }, - word : function(c, $column, txt, num){ - var word = (txt + ' ').match(/\w+/g); - return word && word.length >= num ? word[num - 1] : txt || ''; + text : function( c, $column, txt ) { + return txt; }, - letter : function(c, $column, txt, num){ + word : function(c, $column, txt, num) { + var word = (txt + ' ').match(/\w+/g) || []; + // return word && word.length >= num ? word[num - 1] : txt || ''; + return word[ num - 1 ] || ''; + }, + letter : function(c, $column, txt, num) { return txt ? (txt + ' ').substring(0, num) : ''; }, - date : function(c, $column, txt, part, group){ - var wo = c.widgetOptions, - time = new Date(txt || ''), - hours = time.getHours(); - return part === 'year' ? time.getFullYear() : - part === 'month' ? wo.group_months[time.getMonth()] : - part === 'monthyear' ? wo.group_months[time.getMonth()] + ' ' + time.getFullYear() : - part === 'day' ? wo.group_months[time.getMonth()] + ' ' + time.getDate() : - part === 'week' ? wo.group_week[time.getDay()] : - part === 'time' ? ('00' + (hours > 12 ? hours - 12 : hours === 0 ? hours + 12 : hours)).slice(-2) + ':' + - ('00' + time.getMinutes()).slice(-2) + ' ' + ('00' + wo.group_time[hours >= 12 ? 1 : 0]).slice(-2) : - wo.group_dateString(time); + date : function(c, $column, txt, part, group) { + var year, month, + wo = c.widgetOptions, + time = new Date(txt || ''); + // check for valid date + if ( time instanceof Date && isFinite( time ) ) { + year = time.getFullYear(); + month = tsg.findMonth( wo, time.getMonth() ); + return part === 'year' ? year : + part === 'month' ? month : + part === 'monthyear' ? month + ' ' + year : + part === 'day' ? month + ' ' + time.getDate() : + part === 'week' ? tsg.findWeek( wo, time.getDay() ) : + part === 'time' ? tsg.findTime( wo, time ) : + wo.group_dateString( time, c, $column ); + } else { + return wo.group_dateInvalid; + } + } + }, + + // group date type functions to allow using this widget with Globalize + findMonth : function( wo, month ) { + // CLDR returns an object { 1: "Jan", 2: "Feb", 3: "Mar", ..., 12: "Dec" } + return wo.group_months[ month + ( ( wo.group_months[0] || '' ) === '' ? 1 : 0 ) ]; + }, + findWeek : function( wo, day ) { + if ( $.isArray( wo.group_week ) ) { + return wo.group_week[ day ]; + } else if ( !$.isEmptyObject( wo.group_week ) ) { + // CLDR returns { sun: "Sun", mon: "Mon", tue: "Tue", wed: "Wed", thu: "Thu", ... } + var cldrWeek = [ 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat' ]; + return wo.group_week[ cldrWeek[ day ] ]; } }, + findTime : function( wo, time ) { + var suffix, + // CLDR returns { am: "AM", pm: "PM", ... } + isObj = wo.group_time.am && wo.group_time.pm, + h = time.getHours(), + period = h >= 12 ? 1 : 0, + p24 = wo.group_time24Hour && h > 12 ? h - 12 : + wo.group_time24Hour && h === 0 ? h + 12 : h, + hours = ( '00' + p24 ).slice(-2), + min = ( '00' + time.getMinutes() ).slice(-2); + suffix = wo.group_time[ isObj ? [ 'am', 'pm' ][ period ] : period ]; + return hours + ':' + min + ( wo.group_time24Hour ? '' : ' ' + ( suffix || '' ) ); + }, - update : function(table, c, wo){ + update : function(table, c, wo) { if ($.isEmptyObject(c.cache)) { return; } - var rowIndex, tbodyIndex, currentGroup, $rows, groupClass, grouping, norm_rows, saveName, direction, - hasSort = typeof c.sortList[0] !== 'undefined', - group = '', - groupIndex = 0, - savedGroup = false, - column = typeof wo.group_forceColumn[0] !== 'undefined' ? + var hasSort = typeof c.sortList[0] !== 'undefined', + data = {}, + column = $.isArray( wo.group_forceColumn ) && typeof wo.group_forceColumn[0] !== 'undefined' ? ( wo.group_enforceSort && !hasSort ? -1 : wo.group_forceColumn[0] ) : ( hasSort ? c.sortList[0][0] : -1 ); c.$table @@ -70,97 +107,135 @@ c.$table.data('pagerSavedHeight', 0); } if (column >= 0 && column < c.columns && !c.$headerIndexed[column].hasClass('group-false')) { - wo.group_currentGroup = ''; // save current groups - wo.group_currentGroups = {}; + wo.group_collapsedGroup = ''; // save current groups + wo.group_collapsedGroups = {}; + data.column = column; // group class finds 'group-{word/separator/letter/number/date/false}-{optional:#/year/month/day/week/time}' - groupClass = (c.$headerIndexed[column].attr('class') || '').match(/(group-\w+(-\w+)?)/g); + data.groupClass = (c.$headerIndexed[column].attr('class') || '').match(/(group-\w+(-\w+)?)/g); // grouping = [ 'group', '{word/separator/letter/number/date/false}', '{#/year/month/day/week/time}' ] - grouping = groupClass ? groupClass[0].split('-') : [ 'group', 'letter', 1 ]; // default to letter 1 + data.grouping = data.groupClass ? data.groupClass[0].split('-') : [ 'group', 'letter', 1 ]; // default to letter 1 // save current grouping - if (wo.group_collapsible && wo.group_saveGroups && ts.storage) { - wo.group_currentGroups = ts.storage( table, 'tablesorter-groups' ) || {}; - // include direction when saving groups (reversed numbers shows different range values) - direction = 'dir' + c.sortList[0][1]; - // combine column, sort direction & grouping as save key - saveName = wo.group_currentGroup = '' + c.sortList[0][0] + direction + grouping.join(''); - if (!wo.group_currentGroups[saveName]) { - wo.group_currentGroups[saveName] = []; - } else { - savedGroup = true; - } - } - for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++) { - norm_rows = c.cache[tbodyIndex].normalized; - group = ''; // clear grouping across tbodies - $rows = c.$tbodies.eq(tbodyIndex).children('tr').not('.' + c.cssChildRow); - for (rowIndex = 0; rowIndex < $rows.length; rowIndex++) { - if ( $rows.eq(rowIndex).is(':visible') ) { - // fixes #438 - if (ts.grouping.types[grouping[1]]) { - currentGroup = norm_rows[rowIndex] ? - ts.grouping.types[grouping[1]]( c, c.$headerIndexed[column], norm_rows[rowIndex][column], /date/.test(groupClass) ? - grouping[2] : parseInt(grouping[2] || 1, 10) || 1, group ) : currentGroup; - if (group !== currentGroup) { - group = currentGroup; - // show range if number > 1 - if (grouping[1] === 'number' && grouping[2] > 1 && currentGroup !== '') { - currentGroup += ' - ' + (parseInt(currentGroup, 10) + - ((parseInt(grouping[2], 10) - 1) * (c.$headerIndexed[column].hasClass(ts.css.sortAsc) ? 1 : -1))); - } - if ($.isFunction(wo.group_formatter)) { - currentGroup = wo.group_formatter((currentGroup || '').toString(), column, table, c, wo) || currentGroup; - } - $rows.eq(rowIndex).before('<tr class="group-header ' + c.selectorRemove.slice(1) + - '" unselectable="on" ' + ( c.tabIndex ? 'tabindex="0" ' : '' ) + 'data-group-index="' + - ( groupIndex++ ) + '"><td colspan="' + c.columns + '">' + - ( wo.group_collapsible ? '<i/>' : '' ) + - '<span class="group-name">' + currentGroup + '</span>' + - '<span class="group-count"></span></td></tr>'); - if (wo.group_saveGroups && !savedGroup && wo.group_collapsed && wo.group_collapsible) { - // all groups start collapsed - wo.group_currentGroups[wo.group_currentGroup].push(currentGroup); - } - } - } + data.savedGroup = tsg.saveCurrentGrouping( c, wo, data ); + + // find column groups + tsg.findColumnGroups( c, wo, data ); + tsg.processHeaders( c, wo, data ); + + c.$table.trigger(wo.group_complete); + } + }, + + processHeaders : function( c, wo, data ) { + var index, isHidden, $label, name, $rows, $row, + $headers = c.$table.find( 'tr.group-header' ), + len = $headers.length; + + $headers.bind( 'selectstart', false ); + for ( index = 0; index < len; index++ ) { + $row = $headers.eq( index ); + $rows = $row.nextUntil( 'tr.group-header' ).filter( ':visible' ); + + // add group count (only visible rows!) + if ( wo.group_count || $.isFunction( wo.group_callback ) ) { + $label = $row.find( '.group-count' ); + if ( $label.length ) { + if ( wo.group_count ) { + $label.html( wo.group_count.replace( /\{num\}/g, $rows.length ) ); } - } - } - c.$table.find('tr.group-header') - .bind('selectstart', false) - .each(function(){ - var isHidden, $label, name, - $row = $(this), - $rows = $row.nextUntil('tr.group-header').filter(':visible'); - if (wo.group_count || $.isFunction(wo.group_callback)) { - $label = $row.find('.group-count'); - if ($label.length) { - if (wo.group_count) { - $label.html( wo.group_count.replace(/\{num\}/g, $rows.length) ); - } - if ($.isFunction(wo.group_callback)) { - wo.group_callback($row.find('td'), $rows, column, table); - } + if ( $.isFunction( wo.group_callback ) ) { + wo.group_callback( $row.find( 'td' ), $rows, data.column, c.table ); } } - if (wo.group_saveGroups && !$.isEmptyObject(wo.group_currentGroups) && wo.group_currentGroups[wo.group_currentGroup].length) { - name = $row.find('.group-name').text().toLowerCase() + $row.attr('data-group-index'); - isHidden = $.inArray( name, wo.group_currentGroups[wo.group_currentGroup] ) > -1; - $row.toggleClass('collapsed', isHidden); - $rows.toggleClass('group-hidden', isHidden); - } else if (wo.group_collapsed && wo.group_collapsible) { - $row.addClass('collapsed'); - $rows.addClass('group-hidden'); + } + + // save collapsed groups + if ( wo.group_saveGroups && + !$.isEmptyObject( wo.group_collapsedGroups ) && + wo.group_collapsedGroups[ wo.group_collapsedGroup ].length ) { + + name = $row.find( '.group-name' ).text().toLowerCase() + $row.attr( 'data-group-index' ); + isHidden = $.inArray( name, wo.group_collapsedGroups[ wo.group_collapsedGroup ] ) > -1; + $row.toggleClass( 'collapsed', isHidden ); + $rows.toggleClass( 'group-hidden', isHidden ); + } else if ( wo.group_collapsed && wo.group_collapsible ) { + $row.addClass( 'collapsed' ); + $rows.addClass( 'group-hidden' ); + } + } + }, + + groupHeaderHTML : function( c, wo, data ) { + return '<tr class="group-header ' + c.selectorRemove.slice(1) + + '" unselectable="on" ' + ( c.tabIndex ? 'tabindex="0" ' : '' ) + 'data-group-index="' + + ( data.groupIndex++ ) + '">' + + '<td colspan="' + c.columns + '">' + + ( wo.group_collapsible ? '<i/>' : '' ) + + '<span class="group-name">' + data.currentGroup + '</span>' + + '<span class="group-count"></span>' + + '</td></tr>'; + }, + saveCurrentGrouping : function( c, wo, data ) { + // save current grouping + var saveName, direction, + savedGroup = false; + if (wo.group_collapsible && wo.group_saveGroups && ts.storage) { + wo.group_collapsedGroups = ts.storage( c.table, 'tablesorter-groups' ) || {}; + // include direction when saving groups (reversed numbers shows different range values) + direction = 'dir' + c.sortList[0][1]; + // combine column, sort direction & grouping as save key + saveName = wo.group_collapsedGroup = '' + c.sortList[0][0] + direction + data.grouping.join(''); + if (!wo.group_collapsedGroups[saveName]) { + wo.group_collapsedGroups[saveName] = []; + } else { + savedGroup = true; + } + } + return savedGroup; + }, + findColumnGroups : function( c, wo, data ) { + var tbodyIndex, norm_rows, $row, rowIndex, end, + hasPager = ts.hasWidget( c.table, 'pager' ); + data.groupIndex = 0; + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + norm_rows = c.cache[ tbodyIndex ].normalized; + data.group = ''; // clear grouping across tbodies + rowIndex = hasPager ? c.pager.startRow - 1 : 0; + end = hasPager ? c.pager.endRow : norm_rows.length; + for ( ; rowIndex < end; rowIndex++ ) { + data.rowData = norm_rows[ rowIndex ]; + data.$row = data.rowData[ c.columns ].$row; + // fixes #438 + if ( data.$row.is( ':visible' ) && tsg.types[ data.grouping[ 1 ] ] ) { + tsg.insertGroupHeader( c, wo, data ); } - }); - c.$table.trigger(wo.group_complete); + } + } + }, + insertGroupHeader: function( c, wo, data ) { + var $header = c.$headerIndexed[ data.column ], + txt = data.rowData[ data.column ], + num = /date/.test( data.groupClass ) ? data.grouping[ 2 ] : parseInt( data.grouping[ 2 ] || 1, 10 ) || 1; + data.currentGroup = data.rowData ? + tsg.types[ data.grouping[ 1 ] ]( c, $header, txt, num, data.group ) : + data.currentGroup; + if ( data.group !== data.currentGroup ) { + data.group = data.currentGroup; + if ( $.isFunction( wo.group_formatter ) ) { + data.currentGroup = wo.group_formatter( ( data.group || '' ).toString(), data.column, c.table, c, wo ) || data.group; + } + data.$row.before( tsg.groupHeaderHTML( c, wo, data ) ); + if ( wo.group_saveGroups && !data.savedGroup && wo.group_collapsed && wo.group_collapsible ) { + // all groups start collapsed + wo.group_collapsedGroups[ wo.group_collapsedGroup ].push( data.currentGroup ); + } } }, bindEvents : function(table, c, wo){ if (wo.group_collapsible) { - wo.group_currentGroups = []; + wo.group_collapsedGroups = []; // .on() requires jQuery 1.7+ c.$table.on('click toggleGroup keyup', 'tr.group-header', function(event){ event.stopPropagation(); @@ -180,33 +255,33 @@ if (wo.group_saveGroups && ts.storage) { $groups = c.$table.find('.group-header'); isCollapsed = $this.hasClass('collapsed'); - if (!wo.group_currentGroups[wo.group_currentGroup]) { - wo.group_currentGroups[wo.group_currentGroup] = []; + if (!wo.group_collapsedGroups[wo.group_collapsedGroup]) { + wo.group_collapsedGroups[wo.group_collapsedGroup] = []; } - if (isCollapsed && wo.group_currentGroup) { - wo.group_currentGroups[wo.group_currentGroup].push( name ); - } else if (wo.group_currentGroup) { - indx = $.inArray( name, wo.group_currentGroups[wo.group_currentGroup] ); + if (isCollapsed && wo.group_collapsedGroup) { + wo.group_collapsedGroups[wo.group_collapsedGroup].push( name ); + } else if (wo.group_collapsedGroup) { + indx = $.inArray( name, wo.group_collapsedGroups[wo.group_collapsedGroup] ); if (indx > -1) { - wo.group_currentGroups[wo.group_currentGroup].splice( indx, 1 ); + wo.group_collapsedGroups[wo.group_collapsedGroup].splice( indx, 1 ); } } - ts.storage( table, 'tablesorter-groups', wo.group_currentGroups ); + ts.storage( table, 'tablesorter-groups', wo.group_collapsedGroups ); } }); } $(wo.group_saveReset).on('click', function(){ - ts.grouping.clearSavedGroups(table); + tsg.clearSavedGroups(table); }); c.$table.on('pagerChange.tsgrouping', function(){ - ts.grouping.update(table, c, wo); + tsg.update(table, c, wo); }); }, clearSavedGroups: function(table){ if (table && ts.storage) { ts.storage(table, 'tablesorter-groups', ''); - ts.grouping.update(table, table.config, table.config.widgetOptions); + tsg.update(table, table.config, table.config.widgetOptions); } } @@ -236,16 +311,22 @@ group_months : [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ], group_week : [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ], group_time : [ 'AM', 'PM' ], + + // use 12 vs 24 hour time + group_time24Hour : false, + // group header text added for invalid dates + group_dateInvalid : 'Invalid Date', + // this function is used when 'group-date' is set to create the date string // you can just return date, date.toLocaleString(), date.toLocaleDateString() or d.toLocaleTimeString() // reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#Conversion_getter group_dateString : function(date) { return date.toLocaleString(); } }, init: function(table, thisWidget, c, wo){ - ts.grouping.bindEvents(table, c, wo); + tsg.bindEvents(table, c, wo); }, format: function(table, c, wo) { - ts.grouping.update(table, c, wo); + tsg.update(table, c, wo); }, remove : function(table, c, wo){ c.$table diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js index dfa738b..f9c2ba7 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js @@ -1,4 +1,4 @@ -/*! Widget: headerTitles - updated 3/5/2014 (v2.15.6) *//* +/*! Widget: headerTitles - updated 10/31/2015 (v2.24.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -54,15 +54,16 @@ c.$headers.each(function(){ var t = this, $this = $(this), - sortType = wo.headerTitle_type[t.column] || c.parsers[ t.column ].type || 'text', + col = parseInt( $this.attr( 'data-column' ), 10 ), + sortType = wo.headerTitle_type[ col ] || c.parsers[ col ].type || 'text', sortDirection = $this.hasClass(ts.css.sortAsc) ? 0 : $this.hasClass(ts.css.sortDesc) ? 1 : 2, - sortNext = t.order[(t.count + 1) % (c.sortReset ? 3 : 2)]; + sortNext = c.sortVars[ col ].order[ ( c.sortVars[ col ].count + 1 ) % ( c.sortReset ? 3 : 2 ) ]; if (wo.headerTitle_useAria) { txt = $this.hasClass('sorter-false') ? wo.headerTitle_output_nosort : $this.attr('aria-label') || ''; } else { txt = (wo.headerTitle_prefix || '') + // now deprecated ($this.hasClass('sorter-false') ? wo.headerTitle_output_nosort : - ts.isValueInArray( t.column, c.sortList ) >= 0 ? wo.headerTitle_output_sorted : wo.headerTitle_output_unsorted); + ts.isValueInArray( col, c.sortList ) >= 0 ? wo.headerTitle_output_sorted : wo.headerTitle_output_unsorted); txt = txt.replace(/\{(current|next|name)\}/gi, function(m){ return { '{name}' : $this.text(), diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-lazyload.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-lazyload.js new file mode 100644 index 0000000..b072de9 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-lazyload.js @@ -0,0 +1,367 @@ +/*! Widget: lazyload (BETA) - 10/31/2015 (v2.24.0) *//* + * Requires tablesorter v2.8+ and jQuery 1.7+ + * by Rob Garrison + */ +/*jshint browser:true, jquery:true, unused:false */ +;( function( $, window ) { + 'use strict'; + var ts = $.tablesorter; + + ts.lazyload = { + init : function( c, wo ) { + if ( wo.lazyload_event === 'scrollstop' && !ts.addScrollStopDone ) { + ts.addScrollStop(); + ts.addScrollStopDone = true; + $.event.special.scrollstop.latency = wo.lazyload_latency || 250; + } + ts.lazyload.update( c, wo ); + var events = [ wo.lazyload_update, 'pagerUpdate', wo.columnSelector_updated || 'columnUpdate', '' ] + .join( c.namespace + 'lazyload ' ); + c.$table.on( events, function() { + ts.lazyload.update( c, c.widgetOptions ); + }); + }, + update : function( c, wo ) { + // add '.' if not already included + var sel = ( /(\.|#)/.test( wo.lazyload_imageClass ) ? '' : '.' ) + wo.lazyload_imageClass; + c.$table.find( sel ).lazyload({ + threshold : wo.lazyload_threshold, + failure_limit : wo.lazyload_failure_limit, + event : wo.lazyload_event, + effect : wo.lazyload_effect, + container : wo.lazyload_container, + data_attribute : wo.lazyload_data_attribute, + skip_invisible : wo.lazyload_skip_invisible, + appear : wo.lazyload_appear, + load : wo.lazyload_load, + placeholder : wo.lazyload_placeholder + }); + }, + remove : function( c, wo ) { + c.$table.off( c.namespace + 'lazyload' ); + } + }; + + ts.addWidget({ + id: 'lazyload', + options: { + // widget options + lazyload_imageClass : 'lazy', + lazyload_update : 'lazyloadUpdate', + // scrollStop option (https://github.com/ssorallen/jquery-scrollstop) + lazyload_latency : 250, + // lazyload options (see http://www.appelsiini.net/projects/lazyload) + lazyload_threshold : 0, + lazyload_failure_limit : 0, + lazyload_event : 'scrollstop', + lazyload_effect : 'show', + lazyload_container : window, + lazyload_data_attribute : 'original', + lazyload_skip_invisible : false, + lazyload_appear : null, + lazyload_load : null, + lazyload_placeholder : 'data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' + }, + init: function( table, thisWidget, c, wo ) { + ts.lazyload.init( c, wo ); + }, + remove : function( table, c, wo ) { + ts.lazyload.remove( c, wo ); + } + }); + + // jscs:disable + ts.addScrollStop = function() { + // jQuery Scrollstop Plugin v1.2.0 + // https://github.com/ssorallen/jquery-scrollstop + /* + (function (factory) { + // UMD[2] wrapper for jQuery plugins to work in AMD or in CommonJS. + // + // [2] https://github.com/umdjs/umd + + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], factory); + } else if (typeof exports === 'object') { + // Node/CommonJS + module.exports = factory(require('jquery')); + } else { + // Browser globals + factory(jQuery); + } + }(function ($) { */ + // $.event.dispatch was undocumented and was deprecated in jQuery 1.7[1]. It + // was replaced by $.event.handle in jQuery 1.9. + // + // Use the first of the available functions to support jQuery <1.8. + // + // [1] https://github.com/jquery/jquery-migrate/blob/master/src/event.js#L25 + var dispatch = $.event.dispatch || $.event.handle; + + var special = $.event.special, + uid1 = 'D' + (+new Date()), + uid2 = 'D' + (+new Date() + 1); + + special.scrollstart = { + setup: function(data) { + var _data = $.extend({ + latency: special.scrollstop.latency + }, data); + + var timer, + handler = function(evt) { + var _self = this, + _args = arguments; + if (timer) { + clearTimeout(timer); + } else { + evt.type = 'scrollstart'; + dispatch.apply(_self, _args); + } + timer = setTimeout(function() { + timer = null; + }, _data.latency); + }; + $(this).bind('scroll', handler).data(uid1, handler); + }, + teardown: function() { + $(this).unbind('scroll', $(this).data(uid1)); + } + }; + special.scrollstop = { + latency: 250, + setup: function(data) { + var _data = $.extend({ + latency: special.scrollstop.latency + }, data); + var timer, + handler = function(evt) { + var _self = this, + _args = arguments; + if (timer) { + clearTimeout(timer); + } + timer = setTimeout(function() { + timer = null; + evt.type = 'scrollstop'; + dispatch.apply(_self, _args); + }, _data.latency); + }; + $(this).bind('scroll', handler).data(uid2, handler); + }, + teardown: function() { + $(this).unbind('scroll', $(this).data(uid2)); + } + }; + /* + })); + */ + + }; + // jscs:enable + +})( jQuery, window ); + +// jscs:disable +/*! +* Lazy Load - jQuery plugin for lazy loading images +* +* Copyright (c) 2007-2015 Mika Tuupola +* +* Licensed under the MIT license: +* http://www.opensource.org/licenses/mit-license.php +* +* Project home: +* http://www.appelsiini.net/projects/lazyload +* +* Version: 1.9.7 +* +*/ +(function($, window, document, undefined) { + var $window = $(window); + $.fn.lazyload = function(options) { + var elements = this; + var $container; + var settings = { + threshold : 0, + failure_limit : 0, + event : "scroll", + effect : "show", + container : window, + data_attribute : "original", + skip_invisible : false, + appear : null, + load : null, + placeholder : "data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" + }; + function update() { + var counter = 0; + elements.each(function() { + var $this = $(this); + if (settings.skip_invisible && !$this.is(":visible")) { + return; + } + if ($.abovethetop(this, settings) || $.leftofbegin(this, settings)) { + /* Nothing. */ + } else if (!$.belowthefold(this, settings) && !$.rightoffold(this, settings)) { + $this.trigger("appear"); + /* if we found an image we'll load, reset the counter */ + counter = 0; + } else { + if (++counter > settings.failure_limit) { + return false; + } + } + }); + } + if (options) { + /* Maintain BC for a couple of versions. */ + if (undefined !== options.failurelimit) { + options.failure_limit = options.failurelimit; + delete options.failurelimit; + } + if (undefined !== options.effectspeed) { + options.effect_speed = options.effectspeed; + delete options.effectspeed; + } + $.extend(settings, options); + } + /* Cache container as jQuery as object. */ + $container = (settings.container === undefined || + settings.container === window) ? $window : $(settings.container); + /* Fire one scroll event per scroll. Not one scroll event per image. */ + if (0 === settings.event.indexOf("scroll")) { + $container.bind(settings.event, function() { + return update(); + }); + } + this.each(function() { + var self = this; + var $self = $(self); + self.loaded = false; + /* If no src attribute given use data:uri. */ + if ($self.attr("src") === undefined || $self.attr("src") === false) { + if ($self.is("img")) { + $self.attr("src", settings.placeholder); + } + } + /* When appear is triggered load original image. */ + $self.one("appear", function() { + if (!this.loaded) { + if (settings.appear) { + var elements_left = elements.length; + settings.appear.call(self, elements_left, settings); + } + $("<img />") + .bind("load", function() { + var original = $self.attr("data-" + settings.data_attribute); + $self.hide(); + if ($self.is("img")) { + $self.attr("src", original); + } else { + $self.css("background-image", "url('" + original + "')"); + } + $self[settings.effect](settings.effect_speed); + self.loaded = true; + /* Remove image from array so it is not looped next time. */ + var temp = $.grep(elements, function(element) { + return !element.loaded; + }); + elements = $(temp); + if (settings.load) { + var elements_left = elements.length; + settings.load.call(self, elements_left, settings); + } + }) + .attr("src", $self.attr("data-" + settings.data_attribute)); + } + }); + /* When wanted event is triggered load original image */ + /* by triggering appear. */ + if (0 !== settings.event.indexOf("scroll")) { + $self.bind(settings.event, function() { + if (!self.loaded) { + $self.trigger("appear"); + } + }); + } + }); + /* Check if something appears when window is resized. */ + $window.bind("resize", function() { + update(); + }); + /* With IOS5 force loading images when navigating with back button. */ + /* Non optimal workaround. */ + if ((/(?:iphone|ipod|ipad).*os 5/gi).test(navigator.appVersion)) { + $window.bind("pageshow", function(event) { + if (event.originalEvent && event.originalEvent.persisted) { + elements.each(function() { + $(this).trigger("appear"); + }); + } + }); + } + /* Force initial check if images should appear. */ + $(document).ready(function() { + update(); + }); + return this; + }; + /* Convenience methods in jQuery namespace. */ + /* Use as $.belowthefold(element, {threshold : 100, container : window}) */ + $.belowthefold = function(element, settings) { + var fold; + if (settings.container === undefined || settings.container === window) { + fold = (window.innerHeight ? window.innerHeight : $window.height()) + $window.scrollTop(); + } else { + fold = $(settings.container).offset().top + $(settings.container).height(); + } + return fold <= $(element).offset().top - settings.threshold; + }; + $.rightoffold = function(element, settings) { + var fold; + if (settings.container === undefined || settings.container === window) { + fold = $window.width() + $window.scrollLeft(); + } else { + fold = $(settings.container).offset().left + $(settings.container).width(); + } + return fold <= $(element).offset().left - settings.threshold; + }; + $.abovethetop = function(element, settings) { + var fold; + if (settings.container === undefined || settings.container === window) { + fold = $window.scrollTop(); + } else { + fold = $(settings.container).offset().top; + } + return fold >= $(element).offset().top + settings.threshold + $(element).height(); + }; + $.leftofbegin = function(element, settings) { + var fold; + if (settings.container === undefined || settings.container === window) { + fold = $window.scrollLeft(); + } else { + fold = $(settings.container).offset().left; + } + return fold >= $(element).offset().left + settings.threshold + $(element).width(); + }; + $.inviewport = function(element, settings) { + return !$.rightoffold(element, settings) && !$.leftofbegin(element, settings) && + !$.belowthefold(element, settings) && !$.abovethetop(element, settings); + }; + /* Custom selectors for your convenience. */ + /* Use as $("img:below-the-fold").something() or */ + /* $("img").filter(":below-the-fold").something() which is faster */ + $.extend($.expr[":"], { + "below-the-fold" : function(a) { return $.belowthefold(a, {threshold : 0}); }, + "above-the-top" : function(a) { return !$.belowthefold(a, {threshold : 0}); }, + "right-of-screen": function(a) { return $.rightoffold(a, {threshold : 0}); }, + "left-of-screen" : function(a) { return !$.rightoffold(a, {threshold : 0}); }, + "in-viewport" : function(a) { return $.inviewport(a, {threshold : 0}); }, + /* Maintain BC for couple of versions. */ + "above-the-fold" : function(a) { return !$.belowthefold(a, {threshold : 0}); }, + "right-of-fold" : function(a) { return $.rightoffold(a, {threshold : 0}); }, + "left-of-fold" : function(a) { return !$.rightoffold(a, {threshold : 0}); } + }); +})(jQuery, window, document); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index d28edae..b4b4ce2 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -1,4 +1,4 @@ -/*! Widget: math - updated 7/28/2015 (v2.22.4) *//* +/*! Widget: math - updated 10/31/2015 (v2.24.0) *//* * Requires tablesorter v2.16+ and jQuery 1.7+ * by Rob Garrison */ @@ -18,11 +18,11 @@ }, // value returned when calculation is not possible, e.g. no values, dividing by zero, etc. - invalid : function( name, errorIndex ) { + invalid : function( c, name, errorIndex ) { // name = function returning invalid results // errorIndex = math.error index with an explanation of the error console.log( name, math.error[ errorIndex ] ); - return 'none'; // text for cell + return c && c.widgetOptions.math_none || 'none'; // text for cell }, events : ( 'tablesorter-initialized update updateAll updateRows addRows updateCell ' + @@ -60,6 +60,7 @@ var index, $t, len, $mathRows, mathAbove, arry = [], wo = c.widgetOptions, + mathAttr = wo.math_dataAttrib, filtered = wo.filter_filteredRow || 'filtered', cIndex = parseInt( $el.attr( 'data-column' ), 10 ), $rows = c.$table.children( 'tbody' ).children(), @@ -71,10 +72,10 @@ index = len; while ( index >= 0 ) { $t = $rows.eq( index ).children().filter( '[data-column=' + cIndex + ']' ); - mathAbove = $t.filter( '[' + wo.math_dataAttrib + '^=above]' ).length; + mathAbove = $t.filter( '[' + mathAttr + '^=above]' ).length; // ignore filtered rows & rows with data-math="ignore" (and starting row) if ( ( !$rows.eq( index ).hasClass( filtered ) && - $rows.eq( index ).not( '[' + wo.math_dataAttrib + '=ignore]' ).length && + $rows.eq( index ).not( '[' + mathAttr + '=ignore]' ).length && index !== len ) || mathAbove && index !== len ) { // stop calculating 'above', when encountering another 'above' @@ -86,13 +87,28 @@ } index--; } + } else if ( type === 'below' ) { + len = $rows.length; + // index + 1 to ignore starting node + for ( index = $rows.index( $row ) + 1; index < len; index++ ) { + $t = $rows.eq( index ).children().filter( '[data-column=' + cIndex + ']' ); + if ( $t.filter( '[' + mathAttr + '^=below]' ).length ) { + break; + } + if ( !$rows.eq( index ).hasClass( filtered ) && + $rows.eq( index ).not( '[' + mathAttr + '=ignore]' ).length && + $t.length ) { + arry.push( math.processText( c, $t ) ); + } + } + } else { - $mathRows = $rows.not( '[' + wo.math_dataAttrib + '=ignore]' ); // .each(function(){ + $mathRows = $rows.not( '[' + mathAttr + '=ignore]' ); len = $mathRows.length; for ( index = 0; index < len; index++ ) { $t = $mathRows.eq( index ).children().filter( '[data-column=' + cIndex + ']' ); if ( !$mathRows.eq( index ).hasClass( filtered ) && - $t.not( '[' + wo.math_dataAttrib + '^=above],[' + wo.math_dataAttrib + '^=col]' ).length && + $t.not( '[' + mathAttr + '^=above],[' + mathAttr + '^=below],[' + mathAttr + '^=col]' ).length && !$t.is( $el ) ) { arry.push( math.processText( c, $t ) ); } @@ -106,19 +122,20 @@ var $t, col, $row, rowIndex, rowLen, $cells, cellIndex, cellLen, arry = [], wo = c.widgetOptions, + mathAttr = wo.math_dataAttrib, filtered = wo.filter_filteredRow || 'filtered', - $rows = c.$table.children( 'tbody' ).children().not( '[' + wo.math_dataAttrib + '=ignore]' ); + $rows = c.$table.children( 'tbody' ).children().not( '[' + mathAttr + '=ignore]' ); rowLen = $rows.length; for ( rowIndex = 0; rowIndex < rowLen; rowIndex++ ) { $row = $rows.eq( rowIndex ); if ( !$row.hasClass( filtered ) ) { - $cells = $row.children().not( '[' + wo.math_dataAttrib + '=ignore]' ); + $cells = $row.children().not( '[' + mathAttr + '=ignore]' ); cellLen = $cells.length; // $row.children().each(function(){ for ( cellIndex = 0; cellIndex < cellLen; cellIndex++ ) { $t = $cells.eq( cellIndex ); col = parseInt( $t.attr( 'data-column' ), 10); - if ( !$t.filter( '[' + wo.math_dataAttrib + ']' ).length && $.inArray( col, wo.math_ignore ) < 0 ) { + if ( !$t.filter( '[' + mathAttr + ']' ).length && $.inArray( col, wo.math_ignore ) < 0 ) { arry.push( math.processText( c, $t ) ); } } @@ -127,36 +144,58 @@ return arry; }, + setColumnIndexes : function( c ) { + c.$table.after( '<div id="_tablesorter_table_placeholder"></div>' ); + // detach table from DOM to speed up column indexing + var $table = c.$table.detach(); + ts.computeColumnIndex( $table.children( 'tbody' ).children() ); + $( '#_tablesorter_table_placeholder' ) + .after( $table ) + .remove(); + }, + recalculate : function(c, wo, init) { if ( c && ( !wo.math_isUpdating || init ) ) { + var time, mathAttr, $mathCells; + if ( c.debug ) { + time = new Date(); + } + // add data-column attributes to all table cells if ( init ) { - ts.computeColumnIndex( c.$table.children( 'tbody' ).children() ); + math.setColumnIndexes( c ) ; } // data-attribute name (defaults to data-math) wo.math_dataAttrib = 'data-' + (wo.math_data || 'math'); // all non-info tbody cells - var $mathCells = c.$tbodies.find( '[' + wo.math_dataAttrib + ']' ); + mathAttr = wo.math_dataAttrib; + $mathCells = c.$tbodies.find( '[' + mathAttr + ']' ); math.mathType( c, $mathCells, wo.math_priority ); // only info tbody cells $mathCells = c.$table .children( '.' + c.cssInfoBlock + ', tfoot' ) - .find( '[' + wo.math_dataAttrib + ']' ); + .find( '[' + mathAttr + ']' ); math.mathType( c, $mathCells, wo.math_priority ); // find the 'all' total - $mathCells = c.$table.find( '[' + wo.math_dataAttrib + '^=all]' ); + $mathCells = c.$table.find( '[' + mathAttr + '^=all]' ); math.mathType( c, $mathCells, [ 'all' ] ); wo.math_isUpdating = true; if ( c.debug ) { console[ console.group ? 'group' : 'log' ]( 'Math widget triggering an update after recalculation' ); } - c.$table.trigger( 'update' ); + + // update internal cache + ts.update( c ); + + if ( c.debug ) { + console.log( 'Math widget update completed' + ts.benchmark( time ) ); + } } }, @@ -164,6 +203,7 @@ if ( $cells.length ) { var formula, result, $el, arry, getAll, $targetCells, index, len, wo = c.widgetOptions, + mathAttr = wo.math_dataAttrib, equations = ts.equations; if ( priority[0] === 'all' ) { // no need to get all cells more than once @@ -172,9 +212,9 @@ if (c.debug) { console[ console.group ? 'group' : 'log' ]( 'Tablesorter Math widget recalculation' ); } - // $.each is okay here... only 3 priorities + // $.each is okay here... only 4 priorities $.each( priority, function( i, type ) { - $targetCells = $cells.filter( '[' + wo.math_dataAttrib + '^=' + type + ']' ); + $targetCells = $cells.filter( '[' + mathAttr + '^=' + type + ']' ); len = $targetCells.length; if ( len ) { if (c.debug) { @@ -182,18 +222,22 @@ } for ( index = 0; index < len; index++ ) { $el = $targetCells.eq( index ); - formula = ( $el.attr( wo.math_dataAttrib ) || '' ).replace( type + '-', '' ); + // Row is filtered, no need to do further checking + if ( $el.parent().hasClass( wo.filter_filteredRow || 'filtered' ) ) { + continue; + } + formula = ( $el.attr( mathAttr ) || '' ).replace( type + '-', '' ); arry = ( type === 'row' ) ? math.getRow( c, $el ) : ( type === 'all' ) ? getAll : math.getColumn( c, $el, type ); if ( equations[ formula ] ) { if ( arry.length ) { - result = equations[ formula ]( arry ); + result = equations[ formula ]( arry, c ); if ( c.debug ) { - console.log( $el.attr( wo.math_dataAttrib ), arry, '=', result ); + console.log( $el.attr( mathAttr ), arry, '=', result ); } } else { // mean will return a divide by zero error, everything else shows an undefined error - result = math.invalid( formula, formula === 'mean' ? 0 : 'undef' ); + result = math.invalid( c, formula, formula === 'mean' ? 0 : 'undef' ); } math.output( $el, wo, result, arry ); } @@ -343,7 +387,7 @@ var total = ts.equations.sum( arry ); return total / arry.length; }, - median : function( arry ) { + median : function( arry, c ) { var half, len = arry.length; if ( len > 1 ) { @@ -352,7 +396,7 @@ half = Math.floor( len / 2 ); return ( len % 2 ) ? arry[ half ] : ( arry[ half - 1 ] + arry[ half ] ) / 2; } - return math.invalid( 'median', 1 ); + return math.invalid( c, 'median', 1 ); }, mode : function( arry ) { // http://stackoverflow.com/a/3451640/145346 @@ -387,7 +431,7 @@ }, // common variance equation // (not accessible via data-attribute setting) - variance: function( arry, population ) { + variance: function( arry, population, c ) { var divisor, avg = ts.equations.mean( arry ), v = 0, @@ -397,27 +441,27 @@ } divisor = ( arry.length - ( population ? 0 : 1 ) ); if ( divisor === 0 ) { - return math.invalid( 'variance', 0 ); + return math.invalid( c, 'variance', 0 ); } v /= divisor; return v; }, // variance (population) - varp : function( arry ) { - return ts.equations.variance( arry, true ); + varp : function( arry, c ) { + return ts.equations.variance( arry, true, c ); }, // variance (sample) - vars : function( arry ) { - return ts.equations.variance( arry ); + vars : function( arry, c ) { + return ts.equations.variance( arry, false, c ); }, // standard deviation (sample) - stdevs : function( arry ) { - var vars = ts.equations.variance( arry ); + stdevs : function( arry, c ) { + var vars = ts.equations.variance( arry, false, c ); return Math.sqrt( vars ); }, // standard deviation (population) - stdevp : function( arry ) { - var varp = ts.equations.variance( arry, true ); + stdevp : function( arry, c ) { + var varp = ts.equations.variance( arry, true, c ); return Math.sqrt( varp ); } }; @@ -436,11 +480,13 @@ // complete executed after each fucntion math_complete : null, // function($cell, wo, result, value, arry){ return result; }, // order of calculation; 'all' is last - math_priority : [ 'row', 'above', 'col' ], + math_priority : [ 'row', 'above', 'below', 'col' ], // template for or just prepend the mask prefix & suffix with this HTML // e.g. '<span class="red">{content}</span>' math_prefix : '', math_suffix : '', + // no matching math elements found (text added to cell) + math_none : 'N/A', math_event : 'recalculate' }, init : function( table, thisWidget, c, wo ) { @@ -453,7 +499,7 @@ if ( !wo.math_isUpdating || init ) { if ( !/filter/.test( e.type ) ) { // redo data-column indexes on update - ts.computeColumnIndex( c.$table.children('tbody').children() ); + math.setColumnIndexes( c ) ; } math.recalculate( c, wo, init ); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index cbe107b..1a4e081 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -1,4 +1,4 @@ -/*! Widget: output - updated 7/28/2015 (v2.22.4) *//* +/*! Widget: output - updated 10/31/2015 (v2.24.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * Modified from: * HTML Table to CSV: http://www.kunalbabre.com/projects/table2CSV.php (License unknown?) @@ -138,7 +138,7 @@ // get header cells $this = $el - .find('thead tr') + .children('thead').children('tr') .not('.' + (ts.css.filterRow || 'tablesorter-filter-row') ) .filter( function() { return wo.output_hiddenColumns || $(this).css('display') !== 'none'; @@ -355,7 +355,7 @@ output_popupStyle : 'width=500,height=300', output_saveFileName : 'mytable.csv', // format $cell content callback - output_formatContent : null, // function(config, data){ return data.content; } + output_formatContent : null, // function(config, widgetOptions, data){ return data.content; } // callback executed when processing completes // return true to continue download/output // return false to stop delivery & do something else with the data diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index c66e4e3..48c27b3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 10/4/2015 (v2.23.5) */ +/*! Widget: Pager - updated 10/31/2015 (v2.24.0) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -129,7 +129,7 @@ init: function(table) { // check if tablesorter has initialized - if (table.hasInitialized && table.config.pager.initialized) { return; } + if (table.hasInitialized && table.config.pager && table.config.pager.initialized) { return; } var t, c = table.config, wo = c.widgetOptions, @@ -171,7 +171,6 @@ p.oldAjaxSuccess = p.oldAjaxSuccess || wo.pager_ajaxObject.success; c.appender = tsp.appender; p.initializing = true; - p.showAll = false; if (wo.pager_savePages && ts.storage) { t = ts.storage(table, wo.pager_storageKey) || {}; // fixes #387 p.page = ( isNaN(t.page) ? p.page : t.page ) || p.setPage || 0; @@ -203,7 +202,7 @@ } else { p.ajax = false; // Regular pager; all rows stored in memory - c.$table.trigger('appendCache', [ {}, true ]); + ts.appendCache( c, true ); // true = don't apply widgets } }, @@ -211,8 +210,7 @@ initComplete: function(table, c){ var p = c.pager; tsp.bindEvents(table, c); - tsp.setPageSize(table, 0, c); // page size 0 is ignored - + tsp.setPageSize(c, 0); // page size 0 is ignored if (!p.ajax) { tsp.hideRowsSetup(table, c); } @@ -224,7 +222,7 @@ if (c.debug) { console.log('Pager: Triggering pagerInitialized'); } - c.$table.trigger('pagerInitialized', c); + c.$table.trigger( 'pagerInitialized', c ); // filter widget not initialized; it will update the output display & fire off the pagerComplete event if ( !( c.widgetOptions.filter_initialized && ts.hasWidget(table, 'filter') ) ) { // if ajax, then don't fire off pagerComplete @@ -258,7 +256,7 @@ } tsp.updatePageDisplay(table, c, false); // tsp.moveToPage(table, p, false); <-- called when applyWidgets is triggered - c.$table.trigger('applyWidgets'); + ts.applyWidget( table ); } }) .on('disablePager' + namespace, function(e){ @@ -271,7 +269,8 @@ }) .on('destroyPager' + namespace, function(e, refreshing){ e.stopPropagation(); - tsp.destroyPager(table, c, refreshing); + // call removeWidget to make sure internal flags are modified. + ts.removeWidget( table, 'pager', false ); }) .on('updateComplete' + namespace, function(e, table, triggered){ e.stopPropagation(); @@ -293,30 +292,30 @@ // update without triggering pagerComplete tsp.updatePageDisplay(table, c, false); // make sure widgets are applied - fixes #450 - c.$table.trigger('applyWidgets'); + ts.applyWidget( table ); tsp.updatePageDisplay(table, c); }) - .on('pageSize refreshComplete '.split(' ').join(namespace + ' '), function(e, v){ + .on('pageSize refreshComplete '.split(' ').join(namespace + ' '), function(e, size){ e.stopPropagation(); - tsp.setPageSize(table, parseInt(v, 10) || p.setSize || 10, c); + tsp.setPageSize(c, tsp.parsePageSize( c, size, 'get' )); tsp.hideRows(table, c); tsp.updatePageDisplay(table, c, false); }) - .on('pageSet pagerUpdate '.split(' ').join(namespace + ' '), function(e, v){ + .on('pageSet pagerUpdate '.split(' ').join(namespace + ' '), function(e, num){ e.stopPropagation(); // force pager refresh if (e.type === 'pagerUpdate') { - v = typeof v === 'undefined' ? p.page + 1 : v; + num = typeof num === 'undefined' ? p.page + 1 : num; p.last.page = true; } - p.page = (parseInt(v, 10) || 1) - 1; + p.page = (parseInt(num, 10) || 1) - 1; tsp.moveToPage(table, p, true); tsp.updatePageDisplay(table, c, false); }) .on('pageAndSize' + namespace, function(e, page, size){ e.stopPropagation(); p.page = (parseInt(page, 10) || 1) - 1; - tsp.setPageSize(table, parseInt(size, 10) || p.setSize || 10, c); + tsp.setPageSize(c, tsp.parsePageSize( c, size, 'get' )); tsp.moveToPage(table, p, true); tsp.hideRows(table, c); tsp.updatePageDisplay(table, c, false); @@ -364,9 +363,10 @@ p.$size .off('change' + namespace) .on('change' + namespace, function() { - p.$size.val( $(this).val() ); // in case there are more than one pagers if ( !$(this).hasClass(wo.pager_css.disabled) ) { - tsp.setPageSize(table, parseInt( $(this).val(), 10 ), c); + var size = $(this).val(); + p.$size.val( size ); // in case there are more than one pagers + tsp.setPageSize(c, size); tsp.changeHeight(table, c); } return false; @@ -420,11 +420,12 @@ wo = c.widgetOptions, p = c.pager, namespace = c.namespace + 'pager', - sz = p.size || p.setSize || 10; // don't allow dividing by zero + sz = tsp.parsePageSize( c, p.size, 'get' ); // don't allow dividing by zero if (wo.pager_countChildRows) { t.push(c.cssChildRow); } p.$size.add(p.$goto).removeClass(wo.pager_css.disabled).removeAttr('disabled').attr('aria-disabled', 'false'); p.totalPages = Math.ceil( p.totalRows / sz ); // needed for 'pageSize' method c.totalRows = p.totalRows; + tsp.parsePageNumber( p ); tsp.calcFilters(table, c); c.filteredRows = p.filteredRows; p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; @@ -634,8 +635,10 @@ hideRowsSetup: function(table, c){ var p = c.pager, - namespace = c.namespace + 'pager'; - p.size = parseInt( p.$size.val(), 10 ) || p.size || p.setSize || 10; + namespace = c.namespace + 'pager', + size = p.$size.val(); + p.size = tsp.parsePageSize( c, size, 'get' ); + p.$size.val( tsp.parsePageSize( c, p.size, 'set' ) ); $.data(table, 'pagerLastSize', p.size); tsp.pagerArrows(c); if ( !c.widgetOptions.pager_removeRows ) { @@ -692,7 +695,7 @@ if (d instanceof jQuery) { if (wo.pager_processAjaxOnInit) { // append jQuery object - c.$tbodies.eq(0).children('tr').detach(); + c.$tbodies.eq(0).empty(); c.$tbodies.eq(0).append(d); } } else if (l) { @@ -709,6 +712,9 @@ if (wo.pager_processAjaxOnInit) { c.$tbodies.eq(0).html( tds ); } + } else { + // nothing returned by ajax, empty out the table; see #1032 + c.$tbodies.eq(0).empty(); } wo.pager_processAjaxOnInit = true; // only add new header text if the length matches @@ -744,14 +750,15 @@ } // make sure last pager settings are saved, prevents multiple server side calls with // the same parameters - p.totalPages = Math.ceil( p.totalRows / ( p.size || p.setSize || 10 ) ); + p.totalPages = Math.ceil( p.totalRows / tsp.parsePageSize( c, p.size, 'get' ) ); p.last.totalRows = p.totalRows; p.last.currentFilters = p.currentFilters; p.last.sortList = (c.sortList || []).join(','); p.initializing = false; // update display without triggering pager complete... before updating cache tsp.updatePageDisplay(table, c, false); - $table.trigger('updateCache', [ function(){ + // tablesorter core updateCache (not pager) + ts.updateCache( c, function(){ if (p.initialized) { // apply widgets after table has rendered & after a delay to prevent // multiple applyWidget blocking code from blocking this trigger @@ -759,16 +766,15 @@ if (c.debug) { console.log('Pager: Triggering pagerChange'); } - $table - .trigger('applyWidgets') - .trigger('pagerChange', p); + $table.trigger( 'pagerChange', p ); + ts.applyWidget( table ); tsp.updatePageDisplay(table, c); }, 0); } - } ]); + }); } if (!p.initialized) { - c.$table.trigger('applyWidgets'); + ts.applyWidget( table ); } }, @@ -877,9 +883,9 @@ if (c.debug) { console.log('Pager: Triggering pagerChange'); } - c.$table.trigger('pagerChange', c); + c.$table.trigger( 'pagerChange', c ); } - if ( !wo.pager_removeRows && !p.showAll ) { + if ( !wo.pager_removeRows ) { tsp.hideRows(table, c); } else { ts.clearTableBody(table); @@ -927,14 +933,13 @@ p.page = 0; p.size = p.totalRows; p.totalPages = 1; - p.showAll = true; c.$table .addClass('pagerDisabled') .removeAttr('aria-describedby') .find('tr.pagerSavedHeightSpacer').remove(); tsp.renderTable(table, c.rowsCopy); p.isDisabled = true; - c.$table.trigger('applyWidgets'); + ts.applyWidget( table ); if (c.debug) { console.log('Pager: Disabled'); } @@ -956,7 +961,8 @@ updateCache: function(table) { var c = table.config, p = c.pager; - c.$table.trigger('updateCache', [ function(){ + // tablesorter core updateCache (not pager) + ts.updateCache( c, function(){ if ( !$.isEmptyObject(table.config.cache) ) { var i, rows = [], @@ -970,7 +976,7 @@ // clear out last search to force an update p.last.currentFilters = [ ' ' ]; } - } ]); + }); }, moveToPage: function(table, p, pageMoved) { @@ -978,17 +984,15 @@ if ( pageMoved !== false && p.initialized && $.isEmptyObject(table.config.cache)) { return tsp.updateCache(table); } - var pg, c = table.config, + var c = table.config, wo = c.widgetOptions, l = p.last; // abort page move if the table has filters and has not been initialized if (p.ajax && !wo.filter_initialized && ts.hasWidget(table, 'filter')) { return; } + tsp.parsePageNumber( p ); tsp.calcFilters(table, c); - pg = Math.min( p.totalPages, p.filteredPages ); - if ( p.page < 0 ) { p.page = 0; } - if ( p.page > ( pg - 1 ) && pg !== 0 ) { p.page = pg - 1; } // fixes issue where one current filter is [] and the other is [ '', '', '' ], // making the next if comparison think the filters as different. Fixes #202. @@ -1027,9 +1031,8 @@ if (c.debug) { console.log('Pager: Triggering pageMoved'); } - c.$table - .trigger('pageMoved', c) - .trigger('applyWidgets'); + c.$table.trigger('pageMoved', c); + ts.applyWidget( table ); if (!p.ajax && table.isUpdating) { if (c.debug) { console.log('Pager: Triggering updateComplete'); @@ -1039,11 +1042,32 @@ } }, - setPageSize: function(table, size, c) { - var p = c.pager; - p.size = size || p.size || p.setSize || 10; - p.$size.val(p.size); - $.data(table, 'pagerLastPage', p.page); + // set to either set or get value + parsePageSize: function( c, size, mode ) { + var p = c.pager, + s = parseInt( size, 10 ) || p.size || c.widgetOptions.pager_size || 10, + // if select does not contain an "all" option, use size + setAll = p.$size.find( 'option[value="all"]' ).length ? 'all' : p.totalRows; + return /all/i.test( size ) || s === p.totalRows ? + // "get" to set `p.size` or "set" to set `p.$size.val()` + ( mode === 'get' ? p.totalRows : setAll ) : + ( mode === 'get' ? s : p.size ); + }, + + parsePageNumber: function( p ) { + var min = Math.min( p.totalPages, p.filteredPages ) - 1; + p.page = parseInt( p.page, 10 ); + if ( p.page < 0 || isNaN( p.page ) ) { p.page = 0; } + if ( p.page > min && p.page !== 0 ) { p.page = min; } + return p.page; + }, + + setPageSize: function(c, size) { + var p = c.pager, + table = c.table; + p.size = tsp.parsePageSize( c, size, 'get' ); + p.$size.val( tsp.parsePageSize( c, p.size, 'set' ) ); + $.data(table, 'pagerLastPage', tsp.parsePageNumber( p ) ); $.data(table, 'pagerLastSize', p.size); p.totalPages = Math.ceil( p.totalRows / p.size ); p.filteredPages = Math.ceil( p.filteredRows / p.size ); @@ -1100,12 +1124,13 @@ }, enablePager: function(table, c, triggered){ - var info, p = c.pager; + var info, size, + p = c.pager; p.isDisabled = false; - p.showAll = false; p.page = $.data(table, 'pagerLastPage') || p.page || 0; - p.size = $.data(table, 'pagerLastSize') || parseInt(p.$size.find('option[selected]').val(), 10) || p.size || p.setSize || 10; - p.$size.val(p.size); // set page size + size = p.$size.find('option[selected]').val(); + p.size = $.data(table, 'pagerLastSize') || tsp.parsePageSize( c, size, 'get' ); + p.$size.val( tsp.parsePageSize( c, p.size, 'set' ) ); // set page size p.totalPages = Math.ceil( Math.min( p.totalRows, p.filteredRows ) / p.size ); c.$table.removeClass('pagerDisabled'); // if table id exists, include page display with aria info @@ -1116,8 +1141,9 @@ } tsp.changeHeight(table, c); if ( triggered ) { - c.$table.trigger('updateRows'); - tsp.setPageSize(table, p.size, c); + // tablesorter core update table + ts.update( c ); + tsp.setPageSize(c, p.size); tsp.hideRowsSetup(table, c); if (c.debug) { console.log('Pager: Enabled'); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js index eca57fa..5bb2795 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js @@ -1,4 +1,4 @@ -/* Widget: print - updated 2/7/2015 (v2.19.0) *//* +/* Widget: print - updated 11/2/2015 (v2.24.1) *//* * Requires tablesorter v2.8+ and jQuery 1.2.6+ */ /*jshint browser:true, jquery:true, unused:false */ @@ -12,6 +12,7 @@ event : 'printTable', basicStyle : 'table, tr, td, th { border : solid 1px black; border-collapse : collapse; } td, th { padding: 2px; }', + popupStyle : 'width=500,height=300', init : function(c) { c.$table @@ -26,11 +27,13 @@ process : function(c, wo) { var $this, $table = $('<div/>').append(c.$table.clone()), - printStyle = printTable.basicStyle + 'table { width: 100% }' + + printStyle = printTable.basicStyle + 'table { width: 100%; }' + // hide filter row - '.tablesorter-filter-row { display: none }' + + '.' + ( ts.css.filterRow || 'tablesorter-filter-row' ) + + // hide filtered rows + ', .' + ( wo.filter_filteredRow || 'filtered' ) + ' { display: none; }' + // hide sort arrows - '.tablesorter-header { background-image: none !important; }'; + '.' + ( ts.css.header || 'tablesorter-header' ) + ' { background-image: none !important; }'; // replace content with data-attribute content $table.find('[' + wo.print_dataAttrib + ']').each(function(){ @@ -41,12 +44,16 @@ // === rows === // Assume 'visible' means rows hidden by the pager (rows set to 'display:none') // or hidden by a class name which is added to the wo.print_extraCSS definition - if (/a/i.test(wo.print_rows)) { - // force show of all rows + // look for jQuery filter selector in wo.print_rows & use if found + if ( /^f/i.test( wo.print_rows ) ) { + printStyle += 'tbody tr:not(.' + ( wo.filter_filteredRow || 'filtered' ) + ') { display: table-row !important; }'; + } else if ( /^a/i.test( wo.print_rows ) ) { + // default force show of all rows printStyle += 'tbody tr { display: table-row !important; }'; - } else if (/f/i.test(wo.print_rows)) { - // add definition to show all non-filtered rows (cells hidden by the pager) - printStyle += 'tbody tr:not(.' + (wo.filter_filteredRow || 'filtered') + ') { display: table-row !important; }'; + } else if ( /^[.#:\[]/.test( wo.print_rows ) ) { + // look for '.' (class selector), '#' (id selector), + // ':' (basic filters, e.g. ':not()') or '[' (attribute selector start) + printStyle += 'tbody tr' + wo.print_rows + ' { display: table-row !important; }'; } // === columns === @@ -74,7 +81,7 @@ printOutput : function(c, data, style) { var wo = c.widgetOptions, - generator = window.open('', wo.print_title, 'width=500,height=300'), + generator = window.open( '', wo.print_title, printTable.popupStyle ), t = wo.print_title || c.$table.find('caption').text() || c.$table[0].id || document.title || 'table'; generator.document.write( '<html><head><title>' + t + '</title>' + @@ -83,8 +90,12 @@ '</head><body>' + data + '</body></html>' ); generator.document.close(); - generator.print(); - generator.close(); + // use timeout to allow browser to build DOM before printing + // Print preview in Chrome doesn't work without this code + setTimeout( function() { + generator.print(); + generator.close(); + }, 10 ); return true; }, @@ -99,8 +110,8 @@ options: { print_title : '', // this option > caption > table id > 'table' print_dataAttrib : 'data-name', // header attrib containing modified header name - print_rows : 'filtered', // (a)ll, (v)isible or (f)iltered - print_columns : 'selected', // (a)ll or (s)elected (if columnSelector widget is added) + print_rows : 'filtered', // (a)ll, (v)isible, (f)iltered or custom css selector + print_columns : 'selected', // (a)ll, (v)isbible or (s)elected (if columnSelector widget is added) print_extraCSS : '', // add any extra css definitions for the popup window here print_styleSheet : '', // add the url of your print stylesheet // callback executed when processing completes diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js index 5cd0deb..1278345 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js @@ -1,4 +1,7 @@ -/*! Widget: saveSort */ +/*! Widget: saveSort - updated 10/31/2015 (v2.24.0) *//* +* Requires tablesorter v2.16+ +* by Rob Garrison +*/ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; @@ -54,7 +57,7 @@ c.sortList = sortList; } else if (table.hasInitialized && sortList && sortList.length > 0) { // update sort change - $table.trigger('sorton', [ sortList ]); + ts.sortOn( c, sortList ); } } }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index 823b040..6126eba 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -1,4 +1,4 @@ -/*! Widget: scroller - updated 7/28/2015 (v2.22.4) *//* +/*! Widget: scroller - updated 10/31/2015 (v2.24.0) *//* Copyright (C) 2011 T. Connell & Associates, Inc. Dual-licensed under the MIT and GPL licenses @@ -88,7 +88,7 @@ } }); - /* Add window resizeEnd event */ + /* Add window resizeEnd event (also used by columnSelector widget) */ ts.window_resize = function() { if ( ts.timer_resize ) { clearTimeout( ts.timer_resize ); @@ -186,7 +186,7 @@ }, setup : function( c, wo ) { - var maxHt, tbHt, $hdr, $t, $hCells, $fCells, $tableWrap, events, tmp, + var maxHt, tbHt, $hdr, $t, $hCells, $fCells, $tableWrap, events, tmp, detectedWidth, $win = $( window ), tsScroller = ts.scroller, namespace = c.namespace + 'tsscroller', @@ -202,10 +202,14 @@ wo.scroller_saved = [ 0, 0 ]; wo.scroller_isBusy = true; - // set scrollbar width & allow setting width to zero - wo.scroller_barSetWidth = wo.scroller_barWidth !== null ? - wo.scroller_barWidth : - ( tsScroller.getBarWidth() || 15 ); + // set scrollbar width to one of the following (1) explicitly set scroller_barWidth option, + // (2) detected scrollbar width or (3) fallback of 15px + if ( wo.scroller_barWidth !== null ) { + wo.scroller_barSetWidth = wo.scroller_barWidth; + } else { + detectedWidth = tsScroller.getBarWidth(); + wo.scroller_barSetWidth = detectedWidth !== null ? detectedWidth : 15; + } maxHt = wo.scroller_height || 300; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js index 86db9a1..1467fb5 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js @@ -1,44 +1,61 @@ -/*! Widget: sort2Hash - updated 7/28/2015 (v2.22.4) */ +/*! Widget: sort2Hash (BETA) - updated 11/2/2015 (v2.24.1) */ +/* Requires tablesorter v2.8+ and jQuery 1.7+ + * by Rob Garrison + */ ;( function( $ ) { 'use strict'; var ts = $.tablesorter || {}, s2h = { init : function( c, wo ) { - var hasSaveSort = ts.hasWidget( c.table, 'saveSort' ), - sort = s2h.getSort( c, wo ); + var filter, temp, page, size, + table = c.table, + pager = c.pager, + hasSaveSort = ts.hasWidget( table, 'saveSort' ), + sort = s2h.decodeHash( c, wo, 'sort' ); if ( ( sort && !hasSaveSort ) || ( sort && hasSaveSort && wo.sort2Hash_overrideSaveSort ) ) { - s2h.processHash( c, wo, sort ); + s2h.convertString2Sort( c, wo, sort ); } - c.$table.on( 'sortEnd.sort2hash', function() { - s2h.setHash( c, wo ); + if ( ts.hasWidget( c.table, 'pager' ) ) { + temp = parseInt( s2h.decodeHash( c, wo, 'page' ), 10 ); + page = pager.page = ( temp < 0 ? 0 : ( temp > pager.totalPages ? pager.totalPages - 1 : temp ) ) + 1; + size = pager.size = parseInt( s2h.decodeHash( c, wo, 'size' ), 10 ); + } + if ( ts.hasWidget( table, 'filter' ) ) { + filter = s2h.decodeHash( c, wo, 'filter' ); + if ( filter ) { + filter = filter.split( wo.sort2Hash_separator ); + c.$table.one( 'tablesorter-ready', function() { + setTimeout(function(){ + c.$table.one( 'filterEnd', function(){ + $(this).trigger( 'pageAndSize', [ page, size ] ); + }); + $.tablesorter.setFilters( table, filter, true ); + }, 100 ); + }); + } + } else { + c.$table.trigger( 'pageAndSize', [ page, size ] ); + } + c.$table.on( 'sortEnd.sort2hash filterEnd.sort2hash pagerComplete.sort2Hash', function() { + if ( this.hasInitialized ) { + s2h.setHash( this.config, this.config.widgetOptions ); + } }); }, + getTableId : function( c, wo ) { // option > table id > table index on page return wo.sort2Hash_tableId || c.table.id || 'table' + $( 'table' ).index( c.$table ); }, - getSort : function( c, wo, clean ) { - // modified original code from http://www.netlobo.com/url_query_string_javascript.html - var value, - name = s2h.getTableId( c, wo ).replace( /[\[]/, '\\[' ).replace( /[\]]/, '\\]' ), - sort = ( new RegExp( '[\\#&]' + name + '=([^&]*)' ) ).exec( window.location.hash ); - if ( sort === null ) { - return ''; - } else { - value = s2h.processSort( c, wo, sort[ 1 ] ); - if ( clean ) { - window.location.hash = window.location.hash.replace( '&' + name + '=' + sort[ 1 ], '' ); - return value; - } - return sort[ 1 ]; - } + regexEscape : function( v ) { + return v.replace( /([\.\^\$\*\+\-\?\(\)\[\]\{\}\\\|])/g, '\\$1'); }, // convert 'first%20name,asc,last%20name,desc' into [[0,0], [1,1]] - processHash : function( c, wo, sortHash ) { - var regex, column, direction, temp, - arry = decodeURI( sortHash || '' ).split( wo.sort2Hash_separator ), + convertString2Sort : function( c, wo, sortHash ) { + var regex, column, direction, temp, index, $cell, + arry = sortHash.split( wo.sort2Hash_separator ), indx = 0, len = arry.length, sort = []; @@ -49,14 +66,18 @@ // ignore wo.sort2Hash_useHeaderText setting & // just see if column contains a number if ( isNaN( temp ) || temp > c.columns ) { - regex = new RegExp( '(' + column + ')', 'i' ); - column = c.$headers.filter( function( index ) { - return regex.test( c.$headers[ index ].textContent || '' ); - }).attr( 'data-column' ); + regex = new RegExp( '(' + s2h.regexEscape( column ) + ')', 'i' ); + for ( index = 0; index < c.columns; index++ ) { + $cell = c.$headerIndexed[ index ]; + if ( regex.test( $cell.attr( wo.sort2Hash_headerTextAttr ) ) ) { + column = index; + index = c.columns; + } + } } direction = arry[ indx++ ]; // ignore unpaired values - if ( typeof direction !== 'undefined' ) { + if ( typeof column !== 'undefined' && typeof direction !== 'undefined' ) { // convert text to 0, 1 if ( isNaN( direction ) ) { // default to ascending sort @@ -71,56 +92,134 @@ }, // convert [[0,0],[1,1]] to 'first%20name,asc,last%20name,desc' - processSort : function( c, wo ) { + convertSort2String : function( c, wo ) { var index, txt, column, direction, sort = [], arry = c.sortList || [], len = arry.length; for ( index = 0; index < len; index++ ) { column = arry[ index ][ 0 ]; - if ( wo.sort2Hash_useHeaderText ) { - txt = $.trim( c.$headerIndexed[ column ].text() ); - if ( typeof wo.sort2Hash_processHeaderText === 'function' ) { - txt = wo.sort2Hash_processHeaderText( txt, c, column ); - } - column = txt; - } - sort.push( column ); + txt = $.trim( c.$headerIndexed[ column ].attr( wo.sort2Hash_headerTextAttr ) ); + sort.push( txt !== '' ? encodeURIComponent( txt ) : column ); direction = wo.sort2Hash_directionText[ arry[ index ][ 1 ] ]; sort.push( direction ); + } + // join with separator + return sort.join( wo.sort2Hash_separator ); + }, + convertFilter2String : function( c, wo ) { + var index, txt, column, direction, + sort = [], + arry = c.sortList || [], + len = arry.length; + for ( index = 0; index < len; index++ ) { + column = arry[ index ][ 0 ]; + txt = $.trim( c.$headerIndexed[ column ].attr( wo.sort2Hash_headerTextAttr ) ); + column = typeof txt !== 'undefined' ? encodeURIComponent( txt ) : column; + sort.push( column ); + direction = wo.sort2Hash_directionText[ arry[ index ][ 1 ] ]; + sort.push( direction ); } // join with separator return sort.join( wo.sort2Hash_separator ); }, - setHash : function( c, wo ) { - var sort = s2h.processSort( c, wo ); - if ( sort.length ) { - // remove old hash - s2h.getSort( c, wo, true ); - window.location.hash += ( window.location.hash.length ? '' : wo.sort2Hash_hash ) + - '&' + s2h.getTableId( c, wo ) + '=' + encodeURI( sort ); + + encodeHash : function( c, wo, component, value, rawValue ) { + var result = false, + tableId = s2h.getTableId( c, wo ); + if ( typeof wo.sort2Hash_encodeHash === 'function' ) { + result = wo.sort2Hash_encodeHash( c, tableId, component, value, rawValue || value ); + } + if ( result === false ) { + result = '&' + component + '[' + tableId + ']=' + value; + } + return result; + }, + + decodeHash : function( c, wo, component ) { + var regex, + result = false, + tableId = s2h.getTableId( c, wo ); + if ( typeof wo.sort2Hash_decodeHash === 'function' ) { + result = wo.sort2Hash_decodeHash( c, tableId, component ); + } + if ( result === false ) { + regex = new RegExp( '[\\#&]' + component + '\\[' + s2h.regexEscape( tableId ) + '\\]=([^&]*)' ), + /*jshint -W030 */ + result = regex.exec( window.location.hash ); } + return result ? decodeURIComponent( result[ 1 ] ) : ''; + }, + + cleanHash : function( c, wo, component, hash ) { + var index, len, parts, regex, + result = false, + tableId = s2h.getTableId( c, wo ); + if ( typeof wo.sort2Hash_cleanHash === 'function' ) { + result = wo.sort2Hash_cleanHash( c, tableId, component, hash ); + } + if ( result === false ) { + result = []; + parts = ( hash || '' ).slice(1).split( '&' ); + len = parts.length; + regex = new RegExp( component + '\\[' + s2h.regexEscape( tableId ) + '\\]=([^&]*)' ); + for ( index = 0; index < len; index++ ) { + if ( !regex.test( parts[ index ] ) ) { + result.push( parts[ index ] ); + } + } + } + return result.length ? '#' + result.join( '&' ) : ''; + }, + + setHash : function( c, wo ) { + var str = '', + hash = window.location.hash, + hasPager = ts.hasWidget( c.table, 'pager' ), + hasFilter = ts.hasWidget( c.table, 'filter' ), + sortList = s2h.convertSort2String( c, wo ), + filters = ( hasFilter && c.lastSearch.join('') !== '' ? c.lastSearch : [] ), + filtersStr = encodeURIComponent( filters.join( c.widgetOptions.sort2Hash_separator ) ), + components = { + 'sort' : sortList ? s2h.encodeHash( c, wo, 'sort', sortList, c.sortList ) : '', + 'page' : hasPager ? s2h.encodeHash( c, wo, 'page', c.pager.page + 1 ) : '', + 'size' : hasPager ? s2h.encodeHash( c, wo, 'size', c.pager.size ) : '', + 'filter' : filtersStr ? s2h.encodeHash( c, wo, 'filter', filtersStr, filters ) : '' + }; + // remove old hash + $.each( components, function( component, value ) { + hash = s2h.cleanHash( c, wo, component, hash ); + str += value; + }); + // add updated hash + window.location.hash = ( ( window.location.hash || '' ).replace( '#', '' ).length ? hash : wo.sort2Hash_hash ) + str; } }; ts.addWidget({ id: 'sort2Hash', - priority: 30, // after saveSort + priority: 60, // after saveSort & pager options: { sort2Hash_hash : '#', // hash prefix sort2Hash_separator : '-', // don't '#' or '=' here - sort2Hash_tableId : null, // this option > table ID > table index on page, - sort2Hash_useHeaderText : false, // use column header text (true) or zero-based column index - sort2Hash_processHeaderText : null, // function( text, config, columnIndex ) {}, + sort2Hash_headerTextAttr : 'data-header', // data attribute containing alternate header text sort2Hash_directionText : [ 0, 1 ], // [ 'asc', 'desc' ], sort2Hash_overrideSaveSort : false // if true, override saveSort widget if saved sort available + + // Options below commented out for improved compression + // ****************** + // sort2Hash_tableId : null, // this option > table ID > table index on page, + // custom hash processing functions + // sort2Hash_encodeHash : null, + // sort2Hash_decodeHash : null, + // sort2Hash_cleanHash : null }, init: function(table, thisWidget, c, wo) { s2h.init( c, wo ); }, remove: function(table, c) { - c.$table.off( 'sortEnd.sort2hash' ); + c.$table.off( '.sort2hash' ); } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js index 73af3e2..d7cc09f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js @@ -1,4 +1,4 @@ -/*! tablesorter tbody sorting widget (BETA) - 6/10/2015 (core v2.22.2) +/*! tablesorter tbody sorting widget (BETA) - 10/31/2015 (v2.24.0) * Requires tablesorter v2.22.2+ and jQuery 1.4+ * by Rob Garrison */ @@ -37,13 +37,13 @@ .bind( 'updateComplete' + namespace, function() { // find parsers for each column ts.sortTbodies.setTbodies( c, wo ); - c.$table.trigger( 'updateCache', [ null, c.$tbodies ] ); + ts.updateCache( c, null, c.$tbodies ); }); // detect parsers - in case the table contains only info-only tbodies if ( $.isEmptyObject( c.parsers ) || c.$tbodies.length !== $tbodies.length ) { ts.sortTbodies.setTbodies( c, wo ); - c.$table.trigger( 'updateCache', [ null, c.$tbodies ] ); + ts.updateCache( c, null, c.$tbodies ); } // find colMax; this only matter for numeric columns @@ -116,10 +116,10 @@ colMax = c.$headerIndexed[ col ].attr( 'data-ts-col-max-value' ) || 1.79E+308; // close to Number.MAX_VALUE // sort strings in numerical columns - if ( typeof ( c.string[ c.strings[ col ] ] ) === 'boolean' ) { - num = ( dir ? 1 : -1 ) * ( c.string[ c.strings[ col ] ] ? -1 : 1 ); + if ( typeof ( ts.string[ c.strings[ col ] ] ) === 'boolean' ) { + num = ( dir ? 1 : -1 ) * ( ts.string[ c.strings[ col ] ] ? -1 : 1 ); } else { - num = ( c.strings[ col ] ) ? c.string[ c.strings[ col ] ] || 0 : 0; + num = ( c.strings[ col ] ) ? ts.string[ c.strings[ col ] ] || 0 : 0; } // fall back to built-in numeric sort // var sort = $.tablesorter['sort' + s](a, b, dir, colMax, table); @@ -138,7 +138,7 @@ sort = cts[ col ]( x, y, dir, col, table ); } else { // fall back to natural sort - sort = ts[ 'sortNatural' + ( dir ? 'Asc' : 'Desc' ) ]( colA, colB, col, table, c ); + sort = ts[ 'sortNatural' + ( dir ? 'Asc' : 'Desc' ) ]( colA, colB, col, c ); } } if ( sort ) { return sort; } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js index 7c34b80..d72a220 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js @@ -1,4 +1,4 @@ -/*! widget: staticRow - updated 2/9/2015 (v2.19.1) *//* +/*! widget: staticRow - updated 10/31/2015 (v2.24.0) *//* * Version 1.2 mod by Rob Garrison (requires tablesorter v2.16+) * Requires: * jQuery v1.4+ @@ -62,7 +62,7 @@ .unbind( ('updateComplete.tsstaticrows ' + wo.staticRow_event).replace(/\s+/g, ' ') ) .bind('updateComplete.tsstaticrows ' + wo.staticRow_event, function(){ addIndexes(table); - c.$table.trigger('applyWidgets'); + ts.applyWidget( table ); }); }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js index e545dc6..b697f1a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js @@ -1,4 +1,4 @@ -/*! Widget: stickyHeaders - updated 3/26/2015 (v2.21.3) *//* +/*! Widget: stickyHeaders - updated 10/31/2015 (v2.24.0) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -262,7 +262,7 @@ ts.filter.bindSearch( $table, $stickyCells.find('.' + ts.css.filter) ); // support hideFilters if (wo.filter_hideFilters) { - ts.filter.hideFilters($stickyTable, c); + ts.filter.hideFilters(c, $stickyTable); } } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-view.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-view.js new file mode 100644 index 0000000..4169675 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-view.js @@ -0,0 +1,192 @@ +/* Widget: view (beta) - updated 10/31/2015 (v2.24.0) */ +/* By Justin F. Hallett (https://github.com/TheSin-) + * Requires tablesorter v2.8+ and jQuery 1.7+ + */ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;(function($) { + 'use strict'; + + var ts = $.tablesorter, + is_hidden = false, + tpos, ttop, tleft, + + view = ts.view = { + copyCaption: function(c, wo) { + view.removeCaption(c, wo); + + if (c.$table.find('caption').length > 0) { + $(wo.view_caption).text(c.$table.find('caption').text()); + } + }, + + removeCaption: function(c, wo) { + $(wo.view_caption).empty(); + }, + + buildToolBar: function(c, wo) { + view.removeToolBar(c, wo); + view.copyCaption(c, wo); + var $toolbar = $(wo.view_toolbar); + + $.each(wo.view_layouts, function(k, v) { + var classes = wo.view_switcher_class; + if (k == wo.view_layout) { + classes += ' active'; + } + + var $switcher = $('<a>', { + 'href': '#', + 'class': classes, + 'data-view-type': k, + 'title': v.title + }); + + $switcher.append($('<i>', { + 'class': v.icon + })); + + $toolbar.append($switcher); + }); + + $toolbar.find('.' + wo.view_switcher_class).on('click', function(e) { + e.preventDefault(); + if ($(this).hasClass('active')) { + // if currently clicked button has the active class + // then we do nothing! + return false; + } else { + // otherwise we are clicking on the inactive button + // and in the process of switching views! + $toolbar.find('.' + wo.view_switcher_class).removeClass('active'); + $(this).addClass('active'); + wo.view_layout = $(this).attr('data-view-type'); + + if (wo.view_layouts[wo.view_layout].raw === true) { + view.remove(c, wo); + view.buildToolBar(c, wo); + } else { + if (is_hidden === false) { + view.hideTable(c, wo); + } + view.buildView(c, wo); + } + } + }); + }, + + removeToolBar: function(c, wo) { + $(wo.view_toolbar).empty(); + view.removeCaption(c, wo); + }, + + buildView: function(c, wo) { + view.removeView(c, wo); + + var myview = wo.view_layouts[wo.view_layout]; + var $container = $(myview.container, { + 'class': wo.view_layout + }); + + ts.getColumnText(c.$table, 0, function(data) { + var tmpl = myview.tmpl; + + $.each($(data.$row).find('td'), function(k, v) { + var attrs = {}; + var reg = '{col' + k + '}'; + $.each(v.attributes, function(idx, attr) { + attrs[attr.nodeName] = attr.nodeValue; + }); + var content = $(v).html(); + // Add 2 spans, one is dropped when using .html() + var span = $('<span />').append($('<span/>', attrs).append(content)); + tmpl = tmpl.replace(reg, span.html()); + }); + + var $tmpl = $(tmpl); + $.each(data.$row[0].attributes, function(idx, attr) { + if (attr.nodeName == 'class') { + $tmpl.attr(attr.nodeName, $tmpl.attr(attr.nodeName) + ' ' + attr.nodeValue); + } else { + $tmpl.attr(attr.nodeName, attr.nodeValue); + } + }); + $container.append($tmpl); + }); + + $(wo.view_container).append($container); + }, + + removeView: function(c, wo) { + $(wo.view_container).empty(); + }, + + hideTable: function(c, wo) { + tpos = c.$table.css('position'); + ttop = c.$table.css('bottom'); + tleft = c.$table.css('left'); + + c.$table.css({ + 'position': 'absolute', + 'top': '-10000px', + 'left': '-10000px' + }); + + is_hidden = true; + }, + + init: function(c, wo) { + if (wo.view_layout === false) { + return; + } + + if (typeof wo.view_layouts[wo.view_layout] === 'undefined') { + return; + } + + if (is_hidden === false) { + view.hideTable(c, wo); + } + + $(c.$table).on('tablesorter-ready', function() { + view.buildToolBar(c, wo); + view.buildView(c, wo); + $(c.$table).trigger('viewComplete'); + }); + }, + + remove: function(c, wo) { + view.removeToolBar(c, wo); + view.removeView(c, wo); + + c.$table.css({ + 'position': tpos, + 'top': ttop, + 'left': tleft + }); + + is_hidden = false; + } + }; + + ts.addWidget({ + id: 'view', + options: { + view_toolbar: '#ts-view-toolbar', + view_container: '#ts-view', + view_caption: '#ts-view-caption', + view_switcher_class: 'ts-view-switcher', + view_layout: false, + view_layouts: {} + }, + + init: function(table, thisWidget, c, wo) { + view.init(c, wo); + }, + + remove: function(table, c, wo) { + view.remove(c, wo); + } + }); + +})(jQuery); From 8931f6e240f1001cf15bd019f8664574ae21e57f Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Fri, 6 Nov 2015 00:14:57 +0100 Subject: [PATCH 070/138] Update tablesorter to latest version (2.24.3) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 43 +++++++++++-------- .../jquery-tablesorter/jquery.tablesorter.js | 15 ++++--- .../jquery.tablesorter.widgets.js | 28 ++++++------ .../widgets/widget-filter.js | 8 +++- .../widgets/widget-resizable.js | 18 ++++---- 9 files changed, 70 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 164a672..c2467b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.19.1 (2015-11-06) + +* Upgrade tablesorter to v2.24.3 + #### v1.19.0 (2015-11-03) * Upgrade tablesorter to v2.24.2 diff --git a/README.md b/README.md index f4b8d91..b214a94 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.24.2 (11/2/2015), [documentation] +Current tablesorter version: 2.24.3 (11/4/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 8901b9e..dbedb3c 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.19.0' + VERSION = '1.19.1' end diff --git a/tablesorter b/tablesorter index 73153f2..0c99d0c 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 73153f2bdf4c4bcc7c228374f066034a4bffa9c0 +Subproject commit 0c99d0c6e3f688d98cfcdfb04e24e90aa6163241 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index fa8c302..eca4f52 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 11-02-2015 (v2.24.2)*/ +/*! tablesorter (FORK) - updated 11-04-2015 (v2.24.3)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.24.2 *//* +/*! TableSorter (FORK) v2.24.3 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -39,7 +39,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.24.2', + version : '2.24.3', parsers : [], widgets : [], @@ -1037,12 +1037,13 @@ if ( list[ indx ][ 1 ] !== 2 ) { // multicolumn sorting updating - see #1005 // .not(function(){}) needs jQuery 1.4 - $sorted = c.$headers.filter( function( i, el ) { + // filter(function(i, el){}) <- el is undefined in jQuery v1.2.6 + $sorted = c.$headers.filter( function( i ) { // only include headers that are in the sortList (this includes colspans) var include = true, - $el = $( el ), + $el = c.$headers.eq( i ), col = parseInt( $el.attr( 'data-column' ), 10 ), - end = col + el.colSpan; + end = col + c.$headers[ i ].colSpan; for ( ; col < end; col++ ) { include = include ? ts.isValueInArray( col, c.sortList ) > -1 : false; } @@ -1424,8 +1425,8 @@ $header = c.$headers.eq( headerIndx ); // only reset counts on columns that weren't just clicked on and if not included in a multisort if ( $header[ 0 ] !== tmp && - ( notMultiSort || !$header.is( '.' + ts.css.sortDesc + ',.' + ts.css.sortAsc ) ) ) { - c.sortVars[ col ].count = -1; + ( notMultiSort || $header.hasClass( ts.css.sortNone ) ) ) { + c.sortVars[ $header.attr( 'data-column' ) ].count = -1; } } } @@ -2983,7 +2984,7 @@ })(jQuery); -/*! Widget: filter - updated 10/31/2015 (v2.24.0) *//* +/*! Widget: filter - updated 11/4/2015 (v2.24.3) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3823,6 +3824,8 @@ c.lastCombinedFilter = null; c.lastSearch = []; } + // convert filters to strings (maybe not the best method)- see #1070 + filters = filters.join( '\u0000' ).split( '\u0000' ); if ( wo.filter_initialized ) { c.$table.trigger( 'filterStart', [ filters ] ); } @@ -4693,6 +4696,10 @@ ts.setFilters = function( table, filter, apply, skipFirst ) { var c = table ? $( table )[0].config : '', valid = ts.getFilters( table, true, filter, skipFirst ); + // default apply to "true" + if ( typeof apply === 'undefined' ) { + apply = true; + } if ( c && apply ) { // ensure new set filters are applied, even if the search is the same c.lastCombinedFilter = null; @@ -4994,7 +5001,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 6/26/2015 (v2.22.2) */ +/*! Widget: resizable - updated 11/4/2015 (v2.24.3) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -5098,10 +5105,8 @@ .bind( 'selectstart', false ); } } - $table.one('tablesorter-initialized', function() { - ts.resizable.setHandlePosition( c, wo ); - ts.resizable.bindings( this.config, this.config.widgetOptions ); - }); + ts.resizable.setHandlePosition( c, wo ); + ts.resizable.bindings( c, wo ); }, updateStoredSizes : function( c, wo ) { @@ -5184,9 +5189,9 @@ }, // prevent text selection while dragging resize bar - toggleTextSelection : function( c, toggle ) { + toggleTextSelection : function( c, wo, toggle ) { var namespace = c.namespace + 'tsresize'; - c.widgetOptions.resizable_vars.disabled = toggle; + wo.resizable_vars.disabled = toggle; $( 'body' ).toggleClass( ts.css.resizableNoSelect, toggle ); if ( toggle ) { $( 'body' ) @@ -5223,7 +5228,7 @@ vars.mouseXPosition = event.pageX; ts.resizable.updateStoredSizes( c, wo ); - ts.resizable.toggleTextSelection( c, true ); + ts.resizable.toggleTextSelection(c, wo, true ); }); $( document ) @@ -5242,7 +5247,7 @@ }) .bind( 'mouseup' + namespace, function() { if (!wo.resizable_vars.disabled) { return; } - ts.resizable.toggleTextSelection( c, false ); + ts.resizable.toggleTextSelection( c, wo, false ); ts.resizable.stopResize( c, wo ); ts.resizable.setHandlePosition( c, wo ); }); @@ -5347,7 +5352,7 @@ .unbind( 'contextmenu' + namespace ); wo.$resizable_container.remove(); - ts.resizable.toggleTextSelection( c, false ); + ts.resizable.toggleTextSelection( c, wo, false ); ts.resizableReset( table, refreshing ); $( document ).unbind( 'mousemove' + namespace + ' mouseup' + namespace ); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 9fbe71f..b719a6c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.24.2 *//* +/*! TableSorter (FORK) v2.24.3 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -21,7 +21,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.24.2', + version : '2.24.3', parsers : [], widgets : [], @@ -1019,12 +1019,13 @@ if ( list[ indx ][ 1 ] !== 2 ) { // multicolumn sorting updating - see #1005 // .not(function(){}) needs jQuery 1.4 - $sorted = c.$headers.filter( function( i, el ) { + // filter(function(i, el){}) <- el is undefined in jQuery v1.2.6 + $sorted = c.$headers.filter( function( i ) { // only include headers that are in the sortList (this includes colspans) var include = true, - $el = $( el ), + $el = c.$headers.eq( i ), col = parseInt( $el.attr( 'data-column' ), 10 ), - end = col + el.colSpan; + end = col + c.$headers[ i ].colSpan; for ( ; col < end; col++ ) { include = include ? ts.isValueInArray( col, c.sortList ) > -1 : false; } @@ -1406,8 +1407,8 @@ $header = c.$headers.eq( headerIndx ); // only reset counts on columns that weren't just clicked on and if not included in a multisort if ( $header[ 0 ] !== tmp && - ( notMultiSort || !$header.is( '.' + ts.css.sortDesc + ',.' + ts.css.sortAsc ) ) ) { - c.sortVars[ col ].count = -1; + ( notMultiSort || $header.hasClass( ts.css.sortNone ) ) ) { + c.sortVars[ $header.attr( 'data-column' ) ].count = -1; } } } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 599b497..45a8975 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 11-02-2015 (v2.24.2)*/ +/*! tablesorter (FORK) - updated 11-04-2015 (v2.24.3)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -372,7 +372,7 @@ })(jQuery); -/*! Widget: filter - updated 10/31/2015 (v2.24.0) *//* +/*! Widget: filter - updated 11/4/2015 (v2.24.3) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -1212,6 +1212,8 @@ c.lastCombinedFilter = null; c.lastSearch = []; } + // convert filters to strings (maybe not the best method)- see #1070 + filters = filters.join( '\u0000' ).split( '\u0000' ); if ( wo.filter_initialized ) { c.$table.trigger( 'filterStart', [ filters ] ); } @@ -2082,6 +2084,10 @@ ts.setFilters = function( table, filter, apply, skipFirst ) { var c = table ? $( table )[0].config : '', valid = ts.getFilters( table, true, filter, skipFirst ); + // default apply to "true" + if ( typeof apply === 'undefined' ) { + apply = true; + } if ( c && apply ) { // ensure new set filters are applied, even if the search is the same c.lastCombinedFilter = null; @@ -2383,7 +2389,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 6/26/2015 (v2.22.2) */ +/*! Widget: resizable - updated 11/4/2015 (v2.24.3) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -2487,10 +2493,8 @@ .bind( 'selectstart', false ); } } - $table.one('tablesorter-initialized', function() { - ts.resizable.setHandlePosition( c, wo ); - ts.resizable.bindings( this.config, this.config.widgetOptions ); - }); + ts.resizable.setHandlePosition( c, wo ); + ts.resizable.bindings( c, wo ); }, updateStoredSizes : function( c, wo ) { @@ -2573,9 +2577,9 @@ }, // prevent text selection while dragging resize bar - toggleTextSelection : function( c, toggle ) { + toggleTextSelection : function( c, wo, toggle ) { var namespace = c.namespace + 'tsresize'; - c.widgetOptions.resizable_vars.disabled = toggle; + wo.resizable_vars.disabled = toggle; $( 'body' ).toggleClass( ts.css.resizableNoSelect, toggle ); if ( toggle ) { $( 'body' ) @@ -2612,7 +2616,7 @@ vars.mouseXPosition = event.pageX; ts.resizable.updateStoredSizes( c, wo ); - ts.resizable.toggleTextSelection( c, true ); + ts.resizable.toggleTextSelection(c, wo, true ); }); $( document ) @@ -2631,7 +2635,7 @@ }) .bind( 'mouseup' + namespace, function() { if (!wo.resizable_vars.disabled) { return; } - ts.resizable.toggleTextSelection( c, false ); + ts.resizable.toggleTextSelection( c, wo, false ); ts.resizable.stopResize( c, wo ); ts.resizable.setHandlePosition( c, wo ); }); @@ -2736,7 +2740,7 @@ .unbind( 'contextmenu' + namespace ); wo.$resizable_container.remove(); - ts.resizable.toggleTextSelection( c, false ); + ts.resizable.toggleTextSelection( c, wo, false ); ts.resizableReset( table, refreshing ); $( document ).unbind( 'mousemove' + namespace + ' mouseup' + namespace ); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 3b88349..a28059c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 10/31/2015 (v2.24.0) *//* +/*! Widget: filter - updated 11/4/2015 (v2.24.3) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -838,6 +838,8 @@ c.lastCombinedFilter = null; c.lastSearch = []; } + // convert filters to strings (maybe not the best method)- see #1070 + filters = filters.join( '\u0000' ).split( '\u0000' ); if ( wo.filter_initialized ) { c.$table.trigger( 'filterStart', [ filters ] ); } @@ -1708,6 +1710,10 @@ ts.setFilters = function( table, filter, apply, skipFirst ) { var c = table ? $( table )[0].config : '', valid = ts.getFilters( table, true, filter, skipFirst ); + // default apply to "true" + if ( typeof apply === 'undefined' ) { + apply = true; + } if ( c && apply ) { // ensure new set filters are applied, even if the search is the same c.lastCombinedFilter = null; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js index 5a07ea4..eacc3b4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js @@ -1,4 +1,4 @@ -/*! Widget: resizable - updated 6/26/2015 (v2.22.2) */ +/*! Widget: resizable - updated 11/4/2015 (v2.24.3) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -102,10 +102,8 @@ .bind( 'selectstart', false ); } } - $table.one('tablesorter-initialized', function() { - ts.resizable.setHandlePosition( c, wo ); - ts.resizable.bindings( this.config, this.config.widgetOptions ); - }); + ts.resizable.setHandlePosition( c, wo ); + ts.resizable.bindings( c, wo ); }, updateStoredSizes : function( c, wo ) { @@ -188,9 +186,9 @@ }, // prevent text selection while dragging resize bar - toggleTextSelection : function( c, toggle ) { + toggleTextSelection : function( c, wo, toggle ) { var namespace = c.namespace + 'tsresize'; - c.widgetOptions.resizable_vars.disabled = toggle; + wo.resizable_vars.disabled = toggle; $( 'body' ).toggleClass( ts.css.resizableNoSelect, toggle ); if ( toggle ) { $( 'body' ) @@ -227,7 +225,7 @@ vars.mouseXPosition = event.pageX; ts.resizable.updateStoredSizes( c, wo ); - ts.resizable.toggleTextSelection( c, true ); + ts.resizable.toggleTextSelection(c, wo, true ); }); $( document ) @@ -246,7 +244,7 @@ }) .bind( 'mouseup' + namespace, function() { if (!wo.resizable_vars.disabled) { return; } - ts.resizable.toggleTextSelection( c, false ); + ts.resizable.toggleTextSelection( c, wo, false ); ts.resizable.stopResize( c, wo ); ts.resizable.setHandlePosition( c, wo ); }); @@ -351,7 +349,7 @@ .unbind( 'contextmenu' + namespace ); wo.$resizable_container.remove(); - ts.resizable.toggleTextSelection( c, false ); + ts.resizable.toggleTextSelection( c, wo, false ); ts.resizableReset( table, refreshing ); $( document ).unbind( 'mousemove' + namespace + ' mouseup' + namespace ); } From f0edd72f57dc153bfc1f16dbe4014dc44ee10004 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Wed, 11 Nov 2015 02:18:11 +0100 Subject: [PATCH 071/138] Update tablesorter to latest version (2.24.4) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 14 +- .../jquery.tablesorter.combined.js | 369 ++++++++++-------- .../jquery-tablesorter/jquery.tablesorter.js | 106 ++--- .../jquery.tablesorter.widgets.js | 263 +++++++------ .../parsers/parser-file-type.js | 26 +- .../widgets/widget-filter.js | 261 +++++++------ .../widgets/widget-grouping.js | 4 +- .../widgets/widget-headerTitles.js | 4 +- .../jquery-tablesorter/widgets/widget-math.js | 11 +- .../widgets/widget-pager.js | 13 +- .../widgets/widget-sort2Hash.js | 68 ++-- .../widgets/widget-toggle.js | 81 ++++ 16 files changed, 727 insertions(+), 503 deletions(-) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-toggle.js diff --git a/CHANGELOG.md b/CHANGELOG.md index c2467b1..88196c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.19.2 (2015-11-11) + +* Upgrade tablesorter to v2.24.4 + #### v1.19.1 (2015-11-06) * Upgrade tablesorter to v2.24.3 diff --git a/README.md b/README.md index b214a94..514a458 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.24.3 (11/4/2015), [documentation] +Current tablesorter version: 2.24.4 (11/10/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index dbedb3c..7a0472d 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.19.1' + VERSION = '1.19.2' end diff --git a/tablesorter b/tablesorter index 0c99d0c..a203ac5 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 0c99d0c6e3f688d98cfcdfb04e24e90aa6163241 +Subproject commit a203ac5f526e7ca4f6becadcf852e45e6ffdb5aa diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index aeb0aa4..7f9ed05 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin - * updated 10/31/2015 (v2.24.0) + * updated 11/10/2015 (v2.24.4) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -54,7 +54,8 @@ ajaxProcessing: function(ajax){ return [ 0, [], null ]; }, // output default: '{page}/{totalPages}' - // possible variables: {page}, {totalPages}, {filteredPages}, {startRow}, {endRow}, {filteredRows} and {totalRows} + // possible variables: {size}, {page}, {totalPages}, {filteredPages}, {startRow}, + // {endRow}, {filteredRows} and {totalRows} output: '{startRow} to {endRow} of {totalRows} rows', // '{page}/{totalPages}' // apply disabled classname to the pager arrows when the rows at either extreme is visible @@ -384,6 +385,12 @@ renderAjax = function(data, table, p, xhr, settings, exception){ // process data if ( typeof p.ajaxProcessing === 'function' ) { + + // in case nothing is returned by ajax, empty out the table; see #1032 + // but do it before calling pager_ajaxProcessing because that function may add content + // directly to the table + c.$tbodies.eq(0).empty(); + // ajaxProcessing result: [ total, rows, headers ] var i, j, t, hsh, $f, $sh, $headers, $h, icon, th, d, l, rr_count, len, c = table.config, @@ -443,9 +450,6 @@ if (p.processAjaxOnInit) { c.$tbodies.eq(0).html( tds ); } - } else { - // nothing returned by ajax, empty out the table; see #1032 - c.$tbodies.eq(0).empty(); } p.processAjaxOnInit = true; // only add new header text if the length matches diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index eca4f52..1759f4f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 11-04-2015 (v2.24.3)*/ +/*! tablesorter (FORK) - updated 11-10-2015 (v2.24.4)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.24.3 *//* +/*! TableSorter (FORK) v2.24.4 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -39,7 +39,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.24.3', + version : '2.24.4', parsers : [], widgets : [], @@ -157,12 +157,13 @@ // labels applied to sortable headers for accessibility (aria) support language : { - sortAsc : 'Ascending sort applied, ', - sortDesc : 'Descending sort applied, ', - sortNone : 'No sort applied, ', - nextAsc : 'activate to apply an ascending sort', - nextDesc : 'activate to apply a descending sort', - nextNone : 'activate to remove the sort' + sortAsc : 'Ascending sort applied, ', + sortDesc : 'Descending sort applied, ', + sortNone : 'No sort applied, ', + sortDisabled : 'sorting is disabled', + nextAsc : 'activate to apply an ascending sort', + nextDesc : 'activate to apply a descending sort', + nextNone : 'activate to remove the sort' }, regex : { @@ -1011,7 +1012,7 @@ ▀████▀ ██ █████▀ ██ ██ ██ ██████ */ setHeadersCss : function( c ) { - var $sorted, header, indx, column, $header, nextSort, txt, tmp, + var $sorted, indx, column, list = c.sortList, len = list.length, none = ts.css.sortNone + ' ' + c.cssNone, @@ -1079,50 +1080,62 @@ } // add verbose aria labels len = c.$headers.length; - $headers = c.$headers.not( '.sorter-false' ); for ( indx = 0; indx < len; indx++ ) { - $header = $headers.eq( indx ); - if ( $header.length ) { - header = $headers[ indx ]; - column = parseInt( $header.attr( 'data-column' ), 10 ); - nextSort = c.sortVars[ column ].order[ ( c.sortVars[ column ].count + 1 ) % ( c.sortReset ? 3 : 2 ) ]; + ts.setColumnAriaLabel( c, c.$headers.eq( indx ) ); + } + }, + + // nextSort (optional), lets you disable next sort text + setColumnAriaLabel : function( c, $header, nextSort ) { + if ( $header.length ) { + var column = parseInt( $header.attr( 'data-column' ), 10 ), tmp = $header.hasClass( ts.css.sortAsc ) ? 'sortAsc' : - $header.hasClass( ts.css.sortDesc ) ? 'sortDesc' : 'sortNone'; - txt = $.trim( $header.text() ) + ': ' + - ts.language[ tmp ] + - ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ]; - $header.attr( 'aria-label', txt ); + $header.hasClass( ts.css.sortDesc ) ? 'sortDesc' : 'sortNone', + txt = $.trim( $header.text() ) + ': ' + ts.language[ tmp ]; + if ( $header.hasClass( 'sorter-false' ) || nextSort === false ) { + txt += ts.language.sortDisabled; + } else { + nextSort = c.sortVars[ column ].order[ ( c.sortVars[ column ].count + 1 ) % ( c.sortReset ? 3 : 2 ) ]; + // if nextSort + txt += ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ]; } + $header.attr( 'aria-label', txt ); } }, updateHeader : function( c ) { - var index, isDisabled, $th, col, + var index, isDisabled, $header, col, table = c.table, len = c.$headers.length; for ( index = 0; index < len; index++ ) { - $th = c.$headers.eq( index ); + $header = c.$headers.eq( index ); col = ts.getColumnData( table, c.headers, index, true ); // add 'sorter-false' class if 'parser-false' is set - isDisabled = ts.getData( $th, col, 'sorter' ) === 'false' || ts.getData( $th, col, 'parser' ) === 'false'; - $th[ 0 ].sortDisabled = isDisabled; - $th[ isDisabled ? 'addClass' : 'removeClass' ]( 'sorter-false' ).attr( 'aria-disabled', '' + isDisabled ); - // disable tab index on disabled cells - if ( c.tabIndex ) { - if ( isDisabled ) { - $th.removeAttr( 'tabindex' ); - } else { - $th.attr( 'tabindex', '0' ); - } + isDisabled = ts.getData( $header, col, 'sorter' ) === 'false' || ts.getData( $header, col, 'parser' ) === 'false'; + ts.setColumnSort( c, $header, isDisabled ); + } + }, + + setColumnSort : function( c, $header, isDisabled ) { + var id = c.table.id; + $header[ 0 ].sortDisabled = isDisabled; + $header[ isDisabled ? 'addClass' : 'removeClass' ]( 'sorter-false' ) + .attr( 'aria-disabled', '' + isDisabled ); + // disable tab index on disabled cells + if ( c.tabIndex ) { + if ( isDisabled ) { + $header.removeAttr( 'tabindex' ); + } else { + $header.attr( 'tabindex', '0' ); } - // aria-controls - requires table ID - if ( table.id ) { - if ( isDisabled ) { - $th.removeAttr( 'aria-controls' ); - } else { - $th.attr( 'aria-controls', table.id ); - } + } + // aria-controls - requires table ID + if ( id ) { + if ( isDisabled ) { + $header.removeAttr( 'aria-controls' ); + } else { + $header.attr( 'aria-controls', id ); } } }, @@ -1420,13 +1433,12 @@ event[ c.sortResetKey ] ? 2 : ( c.sortVars[ col ].count + 1 ) % ( c.sortReset ? 3 : 2 ); // reset all sorts on non-current column - issue #30 if ( c.sortRestart ) { - tmp = cell; for ( headerIndx = 0; headerIndx < len; headerIndx++ ) { $header = c.$headers.eq( headerIndx ); + tmp = parseInt( $header.attr( 'data-column' ), 10 ); // only reset counts on columns that weren't just clicked on and if not included in a multisort - if ( $header[ 0 ] !== tmp && - ( notMultiSort || $header.hasClass( ts.css.sortNone ) ) ) { - c.sortVars[ $header.attr( 'data-column' ) ].count = -1; + if ( col !== tmp && ( notMultiSort || $header.hasClass( ts.css.sortNone ) ) ) { + c.sortVars[ tmp ].count = -1; } } } @@ -2185,14 +2197,14 @@ // *** Process table *** // add processing indicator - isProcessing : function( $table, toggle, $ths ) { + isProcessing : function( $table, toggle, $headers ) { $table = $( $table ); var c = $table[ 0 ].config, // default to all headers - $header = $ths || $table.find( '.' + ts.css.header ); + $header = $headers || $table.find( '.' + ts.css.header ); if ( toggle ) { - // don't use sortList if custom $ths used - if ( typeof $ths !== 'undefined' && c.sortList.length > 0 ) { + // don't use sortList if custom $headers used + if ( typeof $headers !== 'undefined' && c.sortList.length > 0 ) { // get headers from the sortList $header = $header.filter( function() { // get data-column from attr to keep compatibility with jQuery 1.2.6 @@ -2984,13 +2996,13 @@ })(jQuery); -/*! Widget: filter - updated 11/4/2015 (v2.24.3) *//* +/*! Widget: filter - updated 11/10/2015 (v2.24.4) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ ;( function ( $ ) { 'use strict'; - var tsf, + var tsf, tsfRegex, ts = $.tablesorter || {}, tscss = ts.css; @@ -3096,20 +3108,21 @@ // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class ) types: { or : function( c, data, vars ) { - if ( tsf.regex.orTest.test( data.iFilter ) || tsf.regex.orSplit.test( data.filter ) ) { + // look for "|", but not if it is inside of a regular expression + if ( ( tsfRegex.orTest.test( data.iFilter ) || tsfRegex.orSplit.test( data.filter ) ) && + // this test for regex has potential to slow down the overall search + !tsfRegex.regex.test( data.filter ) ) { var indx, filterMatched, query, regex, // duplicate data but split filter data2 = $.extend( {}, data ), - index = data.index, - parsed = data.parsed[ index ], - filter = data.filter.split( tsf.regex.orSplit ), - iFilter = data.iFilter.split( tsf.regex.orSplit ), + filter = data.filter.split( tsfRegex.orSplit ), + iFilter = data.iFilter.split( tsfRegex.orSplit ), len = filter.length; for ( indx = 0; indx < len; indx++ ) { data2.nestedFilters = true; - data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' ); - data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); - query = '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')'; + data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], data ) || '' ); + data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], data ) || '' ); + query = '(' + ( tsf.parseFilter( c, data2.filter, data ) || '' ) + ')'; try { // use try/catch, because query may not be a valid regex if "|" is contained within a partial regex search, // e.g "/(Alex|Aar" -> Uncaught SyntaxError: Invalid regular expression: /(/(Alex)/: Unterminated group @@ -3131,22 +3144,20 @@ }, // Look for an AND or && operator ( logical and ) and : function( c, data, vars ) { - if ( tsf.regex.andTest.test( data.filter ) ) { + if ( tsfRegex.andTest.test( data.filter ) ) { var indx, filterMatched, result, query, regex, // duplicate data but split filter data2 = $.extend( {}, data ), - index = data.index, - parsed = data.parsed[ index ], - filter = data.filter.split( tsf.regex.andSplit ), - iFilter = data.iFilter.split( tsf.regex.andSplit ), + filter = data.filter.split( tsfRegex.andSplit ), + iFilter = data.iFilter.split( tsfRegex.andSplit ), len = filter.length; for ( indx = 0; indx < len; indx++ ) { data2.nestedFilters = true; - data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' ); - data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); - query = ( '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' ) + data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], data ) || '' ); + data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], data ) || '' ); + query = ( '(' + ( tsf.parseFilter( c, data2.filter, data ) || '' ) + ')' ) // replace wild cards since /(a*)/i will match anything - .replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' ); + .replace( tsfRegex.wild01, '\\S{1}' ).replace( tsfRegex.wild0More, '\\S*' ); try { // use try/catch just in case RegExp is invalid regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); @@ -3168,10 +3179,10 @@ }, // Look for regex regex: function( c, data ) { - if ( tsf.regex.regex.test( data.filter ) ) { + if ( tsfRegex.regex.test( data.filter ) ) { var matches, // cache regex per column for optimal speed - regex = data.filter_regexCache[ data.index ] || tsf.regex.regex.exec( data.filter ), + regex = data.filter_regexCache[ data.index ] || tsfRegex.regex.exec( data.filter ), isRegex = regex instanceof RegExp; try { if ( !isRegex ) { @@ -3190,18 +3201,17 @@ // Look for operators >, >=, < or <= operators: function( c, data ) { // ignore empty strings... because '' < 10 is true - if ( tsf.regex.operTest.test( data.iFilter ) && data.iExact !== '' ) { + if ( tsfRegex.operTest.test( data.iFilter ) && data.iExact !== '' ) { var cachedValue, result, txt, table = c.table, - index = data.index, - parsed = data.parsed[index], - query = ts.formatFloat( data.iFilter.replace( tsf.regex.operators, '' ), table ), - parser = c.parsers[index], + parsed = data.parsed[ data.index ], + query = ts.formatFloat( data.iFilter.replace( tsfRegex.operators, '' ), table ), + parser = c.parsers[ data.index ], savedSearch = query; // parse filter value in case we're comparing numbers ( dates ) if ( parsed || parser.type === 'numeric' ) { - txt = $.trim( '' + data.iFilter.replace( tsf.regex.operators, '' ) ); - result = tsf.parseFilter( c, txt, index, true ); + txt = $.trim( '' + data.iFilter.replace( tsfRegex.operators, '' ) ); + result = tsf.parseFilter( c, txt, data, true ); query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query; } // iExact may be numeric - see issue #149; @@ -3213,10 +3223,10 @@ txt = isNaN( data.iExact ) ? data.iExact.replace( ts.regex.nondigit, '' ) : data.iExact; cachedValue = ts.formatFloat( txt, table ); } - if ( tsf.regex.gtTest.test( data.iFilter ) ) { - result = tsf.regex.gteTest.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; - } else if ( tsf.regex.ltTest.test( data.iFilter ) ) { - result = tsf.regex.lteTest.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; + if ( tsfRegex.gtTest.test( data.iFilter ) ) { + result = tsfRegex.gteTest.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; + } else if ( tsfRegex.ltTest.test( data.iFilter ) ) { + result = tsfRegex.lteTest.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; } // keep showing all rows if nothing follows the operator if ( !result && savedSearch === '' ) { @@ -3228,13 +3238,13 @@ }, // Look for a not match notMatch: function( c, data ) { - if ( tsf.regex.notTest.test( data.iFilter ) ) { + if ( tsfRegex.notTest.test( data.iFilter ) ) { var indx, txt = data.iFilter.replace( '!', '' ), - filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; - if ( tsf.regex.exact.test( filter ) ) { + filter = tsf.parseFilter( c, txt, data ) || ''; + if ( tsfRegex.exact.test( filter ) ) { // look for exact not matches - see #628 - filter = filter.replace( tsf.regex.exact, '' ); + filter = filter.replace( tsfRegex.exact, '' ); return filter === '' ? true : $.trim( filter ) !== data.iExact; } else { indx = data.iExact.search( $.trim( filter ) ); @@ -3246,29 +3256,29 @@ // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric exact: function( c, data ) { /*jshint eqeqeq:false */ - if ( tsf.regex.exact.test( data.iFilter ) ) { - var txt = data.iFilter.replace( tsf.regex.exact, '' ), - filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + if ( tsfRegex.exact.test( data.iFilter ) ) { + var txt = data.iFilter.replace( tsfRegex.exact, '' ), + filter = tsf.parseFilter( c, txt, data ) || ''; return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact; } return null; }, // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu! range : function( c, data ) { - if ( tsf.regex.toTest.test( data.iFilter ) ) { + if ( tsfRegex.toTest.test( data.iFilter ) ) { var result, tmp, range1, range2, table = c.table, index = data.index, parsed = data.parsed[index], // make sure the dash is for a range and not indicating a negative number - query = data.iFilter.split( tsf.regex.toSplit ); + query = data.iFilter.split( tsfRegex.toSplit ); tmp = query[0].replace( ts.regex.nondigit, '' ) || ''; - range1 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table ); + range1 = ts.formatFloat( tsf.parseFilter( c, tmp, data ), table ); tmp = query[1].replace( ts.regex.nondigit, '' ) || ''; - range2 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table ); + range2 = ts.formatFloat( tsf.parseFilter( c, tmp, data ), table ); // parse filter value in case we're comparing numbers ( dates ) - if ( parsed || c.parsers[index].type === 'numeric' ) { + if ( parsed || c.parsers[ index ].type === 'numeric' ) { result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index ); range1 = ( result !== '' && !isNaN( result ) ) ? result : range1; result = c.parsers[ index ].format( '' + query[1], table, c.$headers.eq( index ), index ); @@ -3289,18 +3299,16 @@ }, // Look for wild card: ? = single, * = multiple, or | = logical OR wild : function( c, data ) { - if ( tsf.regex.wildOrTest.test( data.iFilter ) ) { - var index = data.index, - parsed = data.parsed[ index ], - query = '' + ( tsf.parseFilter( c, data.iFilter, index, parsed ) || '' ); + if ( tsfRegex.wildOrTest.test( data.iFilter ) ) { + var query = '' + ( tsf.parseFilter( c, data.iFilter, data ) || '' ); // look for an exact match with the 'or' unless the 'filter-match' class is found - if ( !tsf.regex.wildTest.test( query ) && data.nestedFilters ) { + if ( !tsfRegex.wildTest.test( query ) && data.nestedFilters ) { query = data.isMatch ? query : '^(' + query + ')$'; } // parsing the filter may not work properly when using wildcards =/ try { return new RegExp( - query.replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' ), + query.replace( tsfRegex.wild01, '\\S{1}' ).replace( tsfRegex.wild0More, '\\S*' ), c.widgetOptions.filter_ignoreCase ? 'i' : '' ) .test( data.exact ); @@ -3312,21 +3320,18 @@ }, // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license ) fuzzy: function( c, data ) { - if ( tsf.regex.fuzzyTest.test( data.iFilter ) ) { + if ( tsfRegex.fuzzyTest.test( data.iFilter ) ) { var indx, patternIndx = 0, len = data.iExact.length, txt = data.iFilter.slice( 1 ), - pattern = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + pattern = tsf.parseFilter( c, txt, data ) || ''; for ( indx = 0; indx < len; indx++ ) { if ( data.iExact[ indx ] === pattern[ patternIndx ] ) { patternIndx += 1; } } - if ( patternIndx === pattern.length ) { - return true; - } - return false; + return patternIndx === pattern.length; } return null; } @@ -3339,8 +3344,7 @@ and : 'and' }, ts.language ); - var options, string, txt, $header, column, filters, val, fxn, noSelect, - regex = tsf.regex; + var options, string, txt, $header, column, filters, val, fxn, noSelect; c.$table.addClass( 'hasFilters' ); // define timers so using clearTimeout won't cause an undefined error @@ -3351,8 +3355,8 @@ wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; - val = '\\{' + tsf.regex.query + '\\}'; - $.extend( regex, { + val = '\\{' + tsfRegex.query + '\\}'; + $.extend( tsfRegex, { child : new RegExp( c.cssChildRow ), filtered : new RegExp( wo.filter_filteredRow ), alreadyFiltered : new RegExp( '(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i' ), @@ -3630,8 +3634,10 @@ c.$table.data( 'lastSearch', filters ); return filters; }, - parseFilter: function( c, filter, column, parsed ) { - return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter; + parseFilter: function( c, filter, data, parsed ) { + return parsed || data.parsed[ data.index ] ? + c.parsers[ data.index ].format( filter, c.table, [], data.index ) : + filter; }, buildRow: function( table, c, wo ) { var $filter, col, column, $header, makeSelect, disabled, name, ffxn, tmp, @@ -3824,8 +3830,12 @@ c.lastCombinedFilter = null; c.lastSearch = []; } - // convert filters to strings (maybe not the best method)- see #1070 - filters = filters.join( '\u0000' ).split( '\u0000' ); + // convert filters to strings - see #1070 + filters = Array.prototype.map ? + filters.map( String ) : + // for IE8 & older browsers - maybe not the best method + filters.join( '\u0000' ).split( '\u0000' ); + if ( wo.filter_initialized ) { c.$table.trigger( 'filterStart', [ filters ] ); } @@ -3879,8 +3889,8 @@ }, defaultFilter: function( filter, mask ) { if ( filter === '' ) { return filter; } - var regex = tsf.regex.iQuery, - maskLen = mask.match( tsf.regex.igQuery ).length, + var regex = tsfRegex.iQuery, + maskLen = mask.match( tsfRegex.igQuery ).length, query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ], len = query.length - 1, indx = 0, @@ -3977,9 +3987,8 @@ return filterMatched; }, processRow: function( c, data, vars ) { - var hasSelect, result, val, filterMatched, + var result, filterMatched, fxn, ffxn, txt, - regex = tsf.regex, wo = c.widgetOptions, showRow = true, @@ -4058,7 +4067,7 @@ result = data.rawArray[ columnIndex ] || ''; data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 } - data.iExact = !regex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? + data.iExact = !tsfRegex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; data.isMatch = c.$headerIndexed[ data.index ].hasClass( 'filter-match' ); @@ -4076,21 +4085,13 @@ data.filter = ts.replaceAccents( data.filter ); } - val = true; - if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) { - data.filter = tsf.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); - // val is used to indicate that a filter select is using a default filter; - // so we override the exact & partial matches - val = false; - } // data.iFilter = case insensitive ( if wo.filter_ignoreCase is true ), // data.filter = case sensitive data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter; fxn = vars.functions[ columnIndex ]; - hasSelect = c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' ); filterMatched = null; - if ( fxn || ( hasSelect && val ) ) { - if ( fxn === true || hasSelect ) { + if ( fxn ) { + if ( fxn === true ) { // default selector uses exact match unless 'filter-match' class is found filterMatched = data.isMatch ? // data.iExact may be a number @@ -4116,7 +4117,7 @@ // Look for match, and add child row data for matching } else { txt = ( data.iExact + data.childRowText ) - .indexOf( tsf.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) ); + .indexOf( tsf.parseFilter( c, data.iFilter, data ) ); result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); } } else { @@ -4136,7 +4137,6 @@ isChild, childRow, lastSearch, showRow, showParent, time, val, indx, notFiltered, searchFiltered, query, injected, res, id, txt, storedFilters = $.extend( [], filters ), - regex = tsf.regex, c = table.config, wo = c.widgetOptions, // data object passed to filters; anyMatch is a flag for the filters @@ -4218,7 +4218,7 @@ ); if ( wo.filter_columnAnyMatch ) { // specific columns search - query = data.anyMatchFilter.split( regex.andSplit ); + query = data.anyMatchFilter.split( tsfRegex.andSplit ); injected = false; for ( indx = 0; indx < query.length; indx++ ) { res = query[ indx ].split( ':' ); @@ -4253,12 +4253,12 @@ // there are no changes from beginning of filter val.indexOf( lastSearch[indx] || '' ) === 0 && // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string - !regex.alreadyFiltered.test( val ) && + !tsfRegex.alreadyFiltered.test( val ) && // if we are not doing exact matches, using '|' ( logical or ) or not '!' - !regex.exactTest.test( val ) && + !tsfRegex.exactTest.test( val ) && // don't search only filtered if the value is negative // ( '> -10' => '> -100' will ignore hidden rows ) - !( regex.isNeg1.test( val ) || regex.isNeg2.test( val ) ) && + !( tsfRegex.isNeg1.test( val ) || tsfRegex.isNeg2.test( val ) ) && // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 !( val !== '' && c.$filters && c.$filters.filter( '[data-column="' + indx + '"]' ).find( 'select' ).length && !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); @@ -4276,7 +4276,7 @@ // replace accents data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter ); } - if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultAnyFilter ) ) { + if ( wo.filter_defaultFilter && tsfRegex.iQuery.test( vars.defaultAnyFilter ) ) { data.anyMatchFilter = tsf.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter ); // clear search filtered flag because default filters are not saved to the last search searchFiltered = false; @@ -4293,9 +4293,9 @@ txt = $rows[ rowIndex ].className; // the first row can never be a child row - isChild = rowIndex && regex.child.test( txt ); + isChild = rowIndex && tsfRegex.child.test( txt ); // skip child rows & already filtered rows - if ( isChild || ( searchFiltered && regex.filtered.test( txt ) ) ) { + if ( isChild || ( searchFiltered && tsfRegex.filtered.test( txt ) ) ) { continue; } @@ -4405,7 +4405,6 @@ // custom select source function for a SPECIFIC COLUMN arry = fxn( table, column, onlyAvail ); } - if ( arry === false ) { // fall back to original method arry = tsf.getOptions( table, column, onlyAvail ); @@ -4419,18 +4418,19 @@ return false; } table = $( table )[0]; - var cts, txt, indx, len, + var cts, txt, indx, len, parsedTxt, str, c = table.config, validColumn = typeof column !== 'undefined' && column !== null && column >= 0 && column < c.columns, parsed = []; - // get unique elements and sort the list // if $.tablesorter.sortText exists ( not in the original tablesorter ), // then natural sort the list otherwise use a basic sort arry = $.grep( arry, function( value, indx ) { + if ( value.text ) { + return true; + } return $.inArray( value, arry ) === indx; }); - if ( validColumn && c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) { // unsorted select options return arry; @@ -4439,22 +4439,30 @@ // parse select option values for ( indx = 0; indx < len; indx++ ) { txt = arry[ indx ]; + // check for object + str = txt.text ? txt.text : txt; + // sortNatural breaks if you don't pass it strings + parsedTxt = ( validColumn && c.parsers && c.parsers.length && + c.parsers[ column ].format( str, table, [], column ) || str ).toString(); + parsedTxt = c.widgetOptions.filter_ignoreCase ? parsedTxt.toLowerCase() : parsedTxt; // parse array data using set column parser; this DOES NOT pass the original // table cell to the parser format function - parsed.push({ - t : txt, - // check parser length - fixes #934 - p : validColumn && c.parsers && c.parsers.length && - c.parsers[ column ].format( txt, table, [], column ) || txt - }); + if ( txt.text ) { + txt.parsed = parsedTxt; + parsed.push( txt ); + } else { + parsed.push({ + text : txt, + // check parser length - fixes #934 + parsed : parsedTxt + }); + } } - // sort parsed select options cts = c.textSorter || ''; parsed.sort( function( a, b ) { - // sortNatural breaks if you don't pass it strings - var x = a.p.toString(), - y = b.p.toString(); + var x = a.parsed, + y = b.parsed; if ( validColumn && typeof cts === 'function' ) { // custom OVERALL text sorter return cts( x, y, true, column, table ); @@ -4472,7 +4480,7 @@ arry = []; len = parsed.length; for ( indx = 0; indx < len; indx++ ) { - arry.push( parsed[indx].t ); + arry.push( parsed[indx] ); } return arry; } @@ -4532,7 +4540,7 @@ return; } - var indx, val, txt, t, $filters, $filter, + var indx, val, txt, t, $filters, $filter, option, c = table.config, wo = c.widgetOptions, node = c.$headerIndexed[ column ], @@ -4557,23 +4565,45 @@ if ( $.isArray( arry ) ) { // build option list for ( indx = 0; indx < arry.length; indx++ ) { - txt = arry[indx] = ( '' + arry[indx] ).replace( tsf.regex.quote, '"' ); - val = txt; - // allow including a symbol in the selectSource array - // 'a-z|A through Z' so that 'a-z' becomes the option value - // and 'A through Z' becomes the option text - if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { - t = txt.split( wo.filter_selectSourceSeparator ); - val = t[0]; - txt = t[1]; + option = arry[ indx ]; + if ( option.text ) { + // OBJECT!! add data-function-name in case the value is set in filter_functions + option['data-function-name'] = typeof option.value === 'undefined' ? option.text : option.value; + + // support jQuery < v1.8, otherwise the below code could be shortened to + // options += $( '<option>', option )[ 0 ].outerHTML; + options += '<option'; + for ( val in option ) { + if ( option.hasOwnProperty( val ) && val !== 'text' ) { + options += ' ' + val + '="' + option[ val ] + '"'; + } + } + if ( !option.value ) { + options += ' value="' + option.text + '"'; + } + options += '>' + option.text + '</option>'; + // above code is needed in jQuery < v1.8 + + // make sure we don't turn an object into a string (objects without a "text" property) + } else if ( '' + option !== '[object Object]' ) { + txt = option = ( '' + option ).replace( tsfRegex.quote, '"' ); + val = txt; + // allow including a symbol in the selectSource array + // 'a-z|A through Z' so that 'a-z' becomes the option value + // and 'A through Z' becomes the option text + if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { + t = txt.split( wo.filter_selectSourceSeparator ); + val = t[0]; + txt = t[1]; + } + // replace quotes - fixes #242 & ignore empty strings + // see http://stackoverflow.com/q/14990971/145346 + options += option !== '' ? + '<option ' + + ( val === txt ? '' : 'data-function-name="' + option + '" ' ) + + 'value="' + val + '">' + txt + + '</option>' : ''; } - // replace quotes - fixes #242 & ignore empty strings - // see http://stackoverflow.com/q/14990971/145346 - options += arry[indx] !== '' ? - '<option ' + - ( val === txt ? '' : 'data-function-name="' + arry[indx] + '" ' ) + - 'value="' + val + '">' + txt + - '</option>' : ''; } // clear arry so it doesn't get appended twice arry = []; @@ -4619,6 +4649,9 @@ } }; + // filter regex variable + tsfRegex = tsf.regex; + ts.getFilters = function( table, getRaw, setFilters, skipFirst ) { var i, $filters, $column, cols, filters = false, diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index b719a6c..7efba00 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.24.3 *//* +/*! TableSorter (FORK) v2.24.4 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -21,7 +21,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.24.3', + version : '2.24.4', parsers : [], widgets : [], @@ -139,12 +139,13 @@ // labels applied to sortable headers for accessibility (aria) support language : { - sortAsc : 'Ascending sort applied, ', - sortDesc : 'Descending sort applied, ', - sortNone : 'No sort applied, ', - nextAsc : 'activate to apply an ascending sort', - nextDesc : 'activate to apply a descending sort', - nextNone : 'activate to remove the sort' + sortAsc : 'Ascending sort applied, ', + sortDesc : 'Descending sort applied, ', + sortNone : 'No sort applied, ', + sortDisabled : 'sorting is disabled', + nextAsc : 'activate to apply an ascending sort', + nextDesc : 'activate to apply a descending sort', + nextNone : 'activate to remove the sort' }, regex : { @@ -993,7 +994,7 @@ ▀████▀ ██ █████▀ ██ ██ ██ ██████ */ setHeadersCss : function( c ) { - var $sorted, header, indx, column, $header, nextSort, txt, tmp, + var $sorted, indx, column, list = c.sortList, len = list.length, none = ts.css.sortNone + ' ' + c.cssNone, @@ -1061,50 +1062,62 @@ } // add verbose aria labels len = c.$headers.length; - $headers = c.$headers.not( '.sorter-false' ); for ( indx = 0; indx < len; indx++ ) { - $header = $headers.eq( indx ); - if ( $header.length ) { - header = $headers[ indx ]; - column = parseInt( $header.attr( 'data-column' ), 10 ); - nextSort = c.sortVars[ column ].order[ ( c.sortVars[ column ].count + 1 ) % ( c.sortReset ? 3 : 2 ) ]; + ts.setColumnAriaLabel( c, c.$headers.eq( indx ) ); + } + }, + + // nextSort (optional), lets you disable next sort text + setColumnAriaLabel : function( c, $header, nextSort ) { + if ( $header.length ) { + var column = parseInt( $header.attr( 'data-column' ), 10 ), tmp = $header.hasClass( ts.css.sortAsc ) ? 'sortAsc' : - $header.hasClass( ts.css.sortDesc ) ? 'sortDesc' : 'sortNone'; - txt = $.trim( $header.text() ) + ': ' + - ts.language[ tmp ] + - ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ]; - $header.attr( 'aria-label', txt ); + $header.hasClass( ts.css.sortDesc ) ? 'sortDesc' : 'sortNone', + txt = $.trim( $header.text() ) + ': ' + ts.language[ tmp ]; + if ( $header.hasClass( 'sorter-false' ) || nextSort === false ) { + txt += ts.language.sortDisabled; + } else { + nextSort = c.sortVars[ column ].order[ ( c.sortVars[ column ].count + 1 ) % ( c.sortReset ? 3 : 2 ) ]; + // if nextSort + txt += ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ]; } + $header.attr( 'aria-label', txt ); } }, updateHeader : function( c ) { - var index, isDisabled, $th, col, + var index, isDisabled, $header, col, table = c.table, len = c.$headers.length; for ( index = 0; index < len; index++ ) { - $th = c.$headers.eq( index ); + $header = c.$headers.eq( index ); col = ts.getColumnData( table, c.headers, index, true ); // add 'sorter-false' class if 'parser-false' is set - isDisabled = ts.getData( $th, col, 'sorter' ) === 'false' || ts.getData( $th, col, 'parser' ) === 'false'; - $th[ 0 ].sortDisabled = isDisabled; - $th[ isDisabled ? 'addClass' : 'removeClass' ]( 'sorter-false' ).attr( 'aria-disabled', '' + isDisabled ); - // disable tab index on disabled cells - if ( c.tabIndex ) { - if ( isDisabled ) { - $th.removeAttr( 'tabindex' ); - } else { - $th.attr( 'tabindex', '0' ); - } + isDisabled = ts.getData( $header, col, 'sorter' ) === 'false' || ts.getData( $header, col, 'parser' ) === 'false'; + ts.setColumnSort( c, $header, isDisabled ); + } + }, + + setColumnSort : function( c, $header, isDisabled ) { + var id = c.table.id; + $header[ 0 ].sortDisabled = isDisabled; + $header[ isDisabled ? 'addClass' : 'removeClass' ]( 'sorter-false' ) + .attr( 'aria-disabled', '' + isDisabled ); + // disable tab index on disabled cells + if ( c.tabIndex ) { + if ( isDisabled ) { + $header.removeAttr( 'tabindex' ); + } else { + $header.attr( 'tabindex', '0' ); } - // aria-controls - requires table ID - if ( table.id ) { - if ( isDisabled ) { - $th.removeAttr( 'aria-controls' ); - } else { - $th.attr( 'aria-controls', table.id ); - } + } + // aria-controls - requires table ID + if ( id ) { + if ( isDisabled ) { + $header.removeAttr( 'aria-controls' ); + } else { + $header.attr( 'aria-controls', id ); } } }, @@ -1402,13 +1415,12 @@ event[ c.sortResetKey ] ? 2 : ( c.sortVars[ col ].count + 1 ) % ( c.sortReset ? 3 : 2 ); // reset all sorts on non-current column - issue #30 if ( c.sortRestart ) { - tmp = cell; for ( headerIndx = 0; headerIndx < len; headerIndx++ ) { $header = c.$headers.eq( headerIndx ); + tmp = parseInt( $header.attr( 'data-column' ), 10 ); // only reset counts on columns that weren't just clicked on and if not included in a multisort - if ( $header[ 0 ] !== tmp && - ( notMultiSort || $header.hasClass( ts.css.sortNone ) ) ) { - c.sortVars[ $header.attr( 'data-column' ) ].count = -1; + if ( col !== tmp && ( notMultiSort || $header.hasClass( ts.css.sortNone ) ) ) { + c.sortVars[ tmp ].count = -1; } } } @@ -2167,14 +2179,14 @@ // *** Process table *** // add processing indicator - isProcessing : function( $table, toggle, $ths ) { + isProcessing : function( $table, toggle, $headers ) { $table = $( $table ); var c = $table[ 0 ].config, // default to all headers - $header = $ths || $table.find( '.' + ts.css.header ); + $header = $headers || $table.find( '.' + ts.css.header ); if ( toggle ) { - // don't use sortList if custom $ths used - if ( typeof $ths !== 'undefined' && c.sortList.length > 0 ) { + // don't use sortList if custom $headers used + if ( typeof $headers !== 'undefined' && c.sortList.length > 0 ) { // get headers from the sortList $header = $header.filter( function() { // get data-column from attr to keep compatibility with jQuery 1.2.6 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 45a8975..2f03958 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 11-04-2015 (v2.24.3)*/ +/*! tablesorter (FORK) - updated 11-10-2015 (v2.24.4)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -372,13 +372,13 @@ })(jQuery); -/*! Widget: filter - updated 11/4/2015 (v2.24.3) *//* +/*! Widget: filter - updated 11/10/2015 (v2.24.4) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ ;( function ( $ ) { 'use strict'; - var tsf, + var tsf, tsfRegex, ts = $.tablesorter || {}, tscss = ts.css; @@ -484,20 +484,21 @@ // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class ) types: { or : function( c, data, vars ) { - if ( tsf.regex.orTest.test( data.iFilter ) || tsf.regex.orSplit.test( data.filter ) ) { + // look for "|", but not if it is inside of a regular expression + if ( ( tsfRegex.orTest.test( data.iFilter ) || tsfRegex.orSplit.test( data.filter ) ) && + // this test for regex has potential to slow down the overall search + !tsfRegex.regex.test( data.filter ) ) { var indx, filterMatched, query, regex, // duplicate data but split filter data2 = $.extend( {}, data ), - index = data.index, - parsed = data.parsed[ index ], - filter = data.filter.split( tsf.regex.orSplit ), - iFilter = data.iFilter.split( tsf.regex.orSplit ), + filter = data.filter.split( tsfRegex.orSplit ), + iFilter = data.iFilter.split( tsfRegex.orSplit ), len = filter.length; for ( indx = 0; indx < len; indx++ ) { data2.nestedFilters = true; - data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' ); - data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); - query = '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')'; + data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], data ) || '' ); + data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], data ) || '' ); + query = '(' + ( tsf.parseFilter( c, data2.filter, data ) || '' ) + ')'; try { // use try/catch, because query may not be a valid regex if "|" is contained within a partial regex search, // e.g "/(Alex|Aar" -> Uncaught SyntaxError: Invalid regular expression: /(/(Alex)/: Unterminated group @@ -519,22 +520,20 @@ }, // Look for an AND or && operator ( logical and ) and : function( c, data, vars ) { - if ( tsf.regex.andTest.test( data.filter ) ) { + if ( tsfRegex.andTest.test( data.filter ) ) { var indx, filterMatched, result, query, regex, // duplicate data but split filter data2 = $.extend( {}, data ), - index = data.index, - parsed = data.parsed[ index ], - filter = data.filter.split( tsf.regex.andSplit ), - iFilter = data.iFilter.split( tsf.regex.andSplit ), + filter = data.filter.split( tsfRegex.andSplit ), + iFilter = data.iFilter.split( tsfRegex.andSplit ), len = filter.length; for ( indx = 0; indx < len; indx++ ) { data2.nestedFilters = true; - data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' ); - data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); - query = ( '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' ) + data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], data ) || '' ); + data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], data ) || '' ); + query = ( '(' + ( tsf.parseFilter( c, data2.filter, data ) || '' ) + ')' ) // replace wild cards since /(a*)/i will match anything - .replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' ); + .replace( tsfRegex.wild01, '\\S{1}' ).replace( tsfRegex.wild0More, '\\S*' ); try { // use try/catch just in case RegExp is invalid regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); @@ -556,10 +555,10 @@ }, // Look for regex regex: function( c, data ) { - if ( tsf.regex.regex.test( data.filter ) ) { + if ( tsfRegex.regex.test( data.filter ) ) { var matches, // cache regex per column for optimal speed - regex = data.filter_regexCache[ data.index ] || tsf.regex.regex.exec( data.filter ), + regex = data.filter_regexCache[ data.index ] || tsfRegex.regex.exec( data.filter ), isRegex = regex instanceof RegExp; try { if ( !isRegex ) { @@ -578,18 +577,17 @@ // Look for operators >, >=, < or <= operators: function( c, data ) { // ignore empty strings... because '' < 10 is true - if ( tsf.regex.operTest.test( data.iFilter ) && data.iExact !== '' ) { + if ( tsfRegex.operTest.test( data.iFilter ) && data.iExact !== '' ) { var cachedValue, result, txt, table = c.table, - index = data.index, - parsed = data.parsed[index], - query = ts.formatFloat( data.iFilter.replace( tsf.regex.operators, '' ), table ), - parser = c.parsers[index], + parsed = data.parsed[ data.index ], + query = ts.formatFloat( data.iFilter.replace( tsfRegex.operators, '' ), table ), + parser = c.parsers[ data.index ], savedSearch = query; // parse filter value in case we're comparing numbers ( dates ) if ( parsed || parser.type === 'numeric' ) { - txt = $.trim( '' + data.iFilter.replace( tsf.regex.operators, '' ) ); - result = tsf.parseFilter( c, txt, index, true ); + txt = $.trim( '' + data.iFilter.replace( tsfRegex.operators, '' ) ); + result = tsf.parseFilter( c, txt, data, true ); query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query; } // iExact may be numeric - see issue #149; @@ -601,10 +599,10 @@ txt = isNaN( data.iExact ) ? data.iExact.replace( ts.regex.nondigit, '' ) : data.iExact; cachedValue = ts.formatFloat( txt, table ); } - if ( tsf.regex.gtTest.test( data.iFilter ) ) { - result = tsf.regex.gteTest.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; - } else if ( tsf.regex.ltTest.test( data.iFilter ) ) { - result = tsf.regex.lteTest.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; + if ( tsfRegex.gtTest.test( data.iFilter ) ) { + result = tsfRegex.gteTest.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; + } else if ( tsfRegex.ltTest.test( data.iFilter ) ) { + result = tsfRegex.lteTest.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; } // keep showing all rows if nothing follows the operator if ( !result && savedSearch === '' ) { @@ -616,13 +614,13 @@ }, // Look for a not match notMatch: function( c, data ) { - if ( tsf.regex.notTest.test( data.iFilter ) ) { + if ( tsfRegex.notTest.test( data.iFilter ) ) { var indx, txt = data.iFilter.replace( '!', '' ), - filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; - if ( tsf.regex.exact.test( filter ) ) { + filter = tsf.parseFilter( c, txt, data ) || ''; + if ( tsfRegex.exact.test( filter ) ) { // look for exact not matches - see #628 - filter = filter.replace( tsf.regex.exact, '' ); + filter = filter.replace( tsfRegex.exact, '' ); return filter === '' ? true : $.trim( filter ) !== data.iExact; } else { indx = data.iExact.search( $.trim( filter ) ); @@ -634,29 +632,29 @@ // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric exact: function( c, data ) { /*jshint eqeqeq:false */ - if ( tsf.regex.exact.test( data.iFilter ) ) { - var txt = data.iFilter.replace( tsf.regex.exact, '' ), - filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + if ( tsfRegex.exact.test( data.iFilter ) ) { + var txt = data.iFilter.replace( tsfRegex.exact, '' ), + filter = tsf.parseFilter( c, txt, data ) || ''; return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact; } return null; }, // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu! range : function( c, data ) { - if ( tsf.regex.toTest.test( data.iFilter ) ) { + if ( tsfRegex.toTest.test( data.iFilter ) ) { var result, tmp, range1, range2, table = c.table, index = data.index, parsed = data.parsed[index], // make sure the dash is for a range and not indicating a negative number - query = data.iFilter.split( tsf.regex.toSplit ); + query = data.iFilter.split( tsfRegex.toSplit ); tmp = query[0].replace( ts.regex.nondigit, '' ) || ''; - range1 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table ); + range1 = ts.formatFloat( tsf.parseFilter( c, tmp, data ), table ); tmp = query[1].replace( ts.regex.nondigit, '' ) || ''; - range2 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table ); + range2 = ts.formatFloat( tsf.parseFilter( c, tmp, data ), table ); // parse filter value in case we're comparing numbers ( dates ) - if ( parsed || c.parsers[index].type === 'numeric' ) { + if ( parsed || c.parsers[ index ].type === 'numeric' ) { result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index ); range1 = ( result !== '' && !isNaN( result ) ) ? result : range1; result = c.parsers[ index ].format( '' + query[1], table, c.$headers.eq( index ), index ); @@ -677,18 +675,16 @@ }, // Look for wild card: ? = single, * = multiple, or | = logical OR wild : function( c, data ) { - if ( tsf.regex.wildOrTest.test( data.iFilter ) ) { - var index = data.index, - parsed = data.parsed[ index ], - query = '' + ( tsf.parseFilter( c, data.iFilter, index, parsed ) || '' ); + if ( tsfRegex.wildOrTest.test( data.iFilter ) ) { + var query = '' + ( tsf.parseFilter( c, data.iFilter, data ) || '' ); // look for an exact match with the 'or' unless the 'filter-match' class is found - if ( !tsf.regex.wildTest.test( query ) && data.nestedFilters ) { + if ( !tsfRegex.wildTest.test( query ) && data.nestedFilters ) { query = data.isMatch ? query : '^(' + query + ')$'; } // parsing the filter may not work properly when using wildcards =/ try { return new RegExp( - query.replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' ), + query.replace( tsfRegex.wild01, '\\S{1}' ).replace( tsfRegex.wild0More, '\\S*' ), c.widgetOptions.filter_ignoreCase ? 'i' : '' ) .test( data.exact ); @@ -700,21 +696,18 @@ }, // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license ) fuzzy: function( c, data ) { - if ( tsf.regex.fuzzyTest.test( data.iFilter ) ) { + if ( tsfRegex.fuzzyTest.test( data.iFilter ) ) { var indx, patternIndx = 0, len = data.iExact.length, txt = data.iFilter.slice( 1 ), - pattern = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + pattern = tsf.parseFilter( c, txt, data ) || ''; for ( indx = 0; indx < len; indx++ ) { if ( data.iExact[ indx ] === pattern[ patternIndx ] ) { patternIndx += 1; } } - if ( patternIndx === pattern.length ) { - return true; - } - return false; + return patternIndx === pattern.length; } return null; } @@ -727,8 +720,7 @@ and : 'and' }, ts.language ); - var options, string, txt, $header, column, filters, val, fxn, noSelect, - regex = tsf.regex; + var options, string, txt, $header, column, filters, val, fxn, noSelect; c.$table.addClass( 'hasFilters' ); // define timers so using clearTimeout won't cause an undefined error @@ -739,8 +731,8 @@ wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; - val = '\\{' + tsf.regex.query + '\\}'; - $.extend( regex, { + val = '\\{' + tsfRegex.query + '\\}'; + $.extend( tsfRegex, { child : new RegExp( c.cssChildRow ), filtered : new RegExp( wo.filter_filteredRow ), alreadyFiltered : new RegExp( '(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i' ), @@ -1018,8 +1010,10 @@ c.$table.data( 'lastSearch', filters ); return filters; }, - parseFilter: function( c, filter, column, parsed ) { - return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter; + parseFilter: function( c, filter, data, parsed ) { + return parsed || data.parsed[ data.index ] ? + c.parsers[ data.index ].format( filter, c.table, [], data.index ) : + filter; }, buildRow: function( table, c, wo ) { var $filter, col, column, $header, makeSelect, disabled, name, ffxn, tmp, @@ -1212,8 +1206,12 @@ c.lastCombinedFilter = null; c.lastSearch = []; } - // convert filters to strings (maybe not the best method)- see #1070 - filters = filters.join( '\u0000' ).split( '\u0000' ); + // convert filters to strings - see #1070 + filters = Array.prototype.map ? + filters.map( String ) : + // for IE8 & older browsers - maybe not the best method + filters.join( '\u0000' ).split( '\u0000' ); + if ( wo.filter_initialized ) { c.$table.trigger( 'filterStart', [ filters ] ); } @@ -1267,8 +1265,8 @@ }, defaultFilter: function( filter, mask ) { if ( filter === '' ) { return filter; } - var regex = tsf.regex.iQuery, - maskLen = mask.match( tsf.regex.igQuery ).length, + var regex = tsfRegex.iQuery, + maskLen = mask.match( tsfRegex.igQuery ).length, query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ], len = query.length - 1, indx = 0, @@ -1365,9 +1363,8 @@ return filterMatched; }, processRow: function( c, data, vars ) { - var hasSelect, result, val, filterMatched, + var result, filterMatched, fxn, ffxn, txt, - regex = tsf.regex, wo = c.widgetOptions, showRow = true, @@ -1446,7 +1443,7 @@ result = data.rawArray[ columnIndex ] || ''; data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 } - data.iExact = !regex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? + data.iExact = !tsfRegex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; data.isMatch = c.$headerIndexed[ data.index ].hasClass( 'filter-match' ); @@ -1464,21 +1461,13 @@ data.filter = ts.replaceAccents( data.filter ); } - val = true; - if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) { - data.filter = tsf.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); - // val is used to indicate that a filter select is using a default filter; - // so we override the exact & partial matches - val = false; - } // data.iFilter = case insensitive ( if wo.filter_ignoreCase is true ), // data.filter = case sensitive data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter; fxn = vars.functions[ columnIndex ]; - hasSelect = c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' ); filterMatched = null; - if ( fxn || ( hasSelect && val ) ) { - if ( fxn === true || hasSelect ) { + if ( fxn ) { + if ( fxn === true ) { // default selector uses exact match unless 'filter-match' class is found filterMatched = data.isMatch ? // data.iExact may be a number @@ -1504,7 +1493,7 @@ // Look for match, and add child row data for matching } else { txt = ( data.iExact + data.childRowText ) - .indexOf( tsf.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) ); + .indexOf( tsf.parseFilter( c, data.iFilter, data ) ); result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); } } else { @@ -1524,7 +1513,6 @@ isChild, childRow, lastSearch, showRow, showParent, time, val, indx, notFiltered, searchFiltered, query, injected, res, id, txt, storedFilters = $.extend( [], filters ), - regex = tsf.regex, c = table.config, wo = c.widgetOptions, // data object passed to filters; anyMatch is a flag for the filters @@ -1606,7 +1594,7 @@ ); if ( wo.filter_columnAnyMatch ) { // specific columns search - query = data.anyMatchFilter.split( regex.andSplit ); + query = data.anyMatchFilter.split( tsfRegex.andSplit ); injected = false; for ( indx = 0; indx < query.length; indx++ ) { res = query[ indx ].split( ':' ); @@ -1641,12 +1629,12 @@ // there are no changes from beginning of filter val.indexOf( lastSearch[indx] || '' ) === 0 && // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string - !regex.alreadyFiltered.test( val ) && + !tsfRegex.alreadyFiltered.test( val ) && // if we are not doing exact matches, using '|' ( logical or ) or not '!' - !regex.exactTest.test( val ) && + !tsfRegex.exactTest.test( val ) && // don't search only filtered if the value is negative // ( '> -10' => '> -100' will ignore hidden rows ) - !( regex.isNeg1.test( val ) || regex.isNeg2.test( val ) ) && + !( tsfRegex.isNeg1.test( val ) || tsfRegex.isNeg2.test( val ) ) && // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 !( val !== '' && c.$filters && c.$filters.filter( '[data-column="' + indx + '"]' ).find( 'select' ).length && !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); @@ -1664,7 +1652,7 @@ // replace accents data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter ); } - if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultAnyFilter ) ) { + if ( wo.filter_defaultFilter && tsfRegex.iQuery.test( vars.defaultAnyFilter ) ) { data.anyMatchFilter = tsf.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter ); // clear search filtered flag because default filters are not saved to the last search searchFiltered = false; @@ -1681,9 +1669,9 @@ txt = $rows[ rowIndex ].className; // the first row can never be a child row - isChild = rowIndex && regex.child.test( txt ); + isChild = rowIndex && tsfRegex.child.test( txt ); // skip child rows & already filtered rows - if ( isChild || ( searchFiltered && regex.filtered.test( txt ) ) ) { + if ( isChild || ( searchFiltered && tsfRegex.filtered.test( txt ) ) ) { continue; } @@ -1793,7 +1781,6 @@ // custom select source function for a SPECIFIC COLUMN arry = fxn( table, column, onlyAvail ); } - if ( arry === false ) { // fall back to original method arry = tsf.getOptions( table, column, onlyAvail ); @@ -1807,18 +1794,19 @@ return false; } table = $( table )[0]; - var cts, txt, indx, len, + var cts, txt, indx, len, parsedTxt, str, c = table.config, validColumn = typeof column !== 'undefined' && column !== null && column >= 0 && column < c.columns, parsed = []; - // get unique elements and sort the list // if $.tablesorter.sortText exists ( not in the original tablesorter ), // then natural sort the list otherwise use a basic sort arry = $.grep( arry, function( value, indx ) { + if ( value.text ) { + return true; + } return $.inArray( value, arry ) === indx; }); - if ( validColumn && c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) { // unsorted select options return arry; @@ -1827,22 +1815,30 @@ // parse select option values for ( indx = 0; indx < len; indx++ ) { txt = arry[ indx ]; + // check for object + str = txt.text ? txt.text : txt; + // sortNatural breaks if you don't pass it strings + parsedTxt = ( validColumn && c.parsers && c.parsers.length && + c.parsers[ column ].format( str, table, [], column ) || str ).toString(); + parsedTxt = c.widgetOptions.filter_ignoreCase ? parsedTxt.toLowerCase() : parsedTxt; // parse array data using set column parser; this DOES NOT pass the original // table cell to the parser format function - parsed.push({ - t : txt, - // check parser length - fixes #934 - p : validColumn && c.parsers && c.parsers.length && - c.parsers[ column ].format( txt, table, [], column ) || txt - }); + if ( txt.text ) { + txt.parsed = parsedTxt; + parsed.push( txt ); + } else { + parsed.push({ + text : txt, + // check parser length - fixes #934 + parsed : parsedTxt + }); + } } - // sort parsed select options cts = c.textSorter || ''; parsed.sort( function( a, b ) { - // sortNatural breaks if you don't pass it strings - var x = a.p.toString(), - y = b.p.toString(); + var x = a.parsed, + y = b.parsed; if ( validColumn && typeof cts === 'function' ) { // custom OVERALL text sorter return cts( x, y, true, column, table ); @@ -1860,7 +1856,7 @@ arry = []; len = parsed.length; for ( indx = 0; indx < len; indx++ ) { - arry.push( parsed[indx].t ); + arry.push( parsed[indx] ); } return arry; } @@ -1920,7 +1916,7 @@ return; } - var indx, val, txt, t, $filters, $filter, + var indx, val, txt, t, $filters, $filter, option, c = table.config, wo = c.widgetOptions, node = c.$headerIndexed[ column ], @@ -1945,23 +1941,45 @@ if ( $.isArray( arry ) ) { // build option list for ( indx = 0; indx < arry.length; indx++ ) { - txt = arry[indx] = ( '' + arry[indx] ).replace( tsf.regex.quote, '"' ); - val = txt; - // allow including a symbol in the selectSource array - // 'a-z|A through Z' so that 'a-z' becomes the option value - // and 'A through Z' becomes the option text - if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { - t = txt.split( wo.filter_selectSourceSeparator ); - val = t[0]; - txt = t[1]; + option = arry[ indx ]; + if ( option.text ) { + // OBJECT!! add data-function-name in case the value is set in filter_functions + option['data-function-name'] = typeof option.value === 'undefined' ? option.text : option.value; + + // support jQuery < v1.8, otherwise the below code could be shortened to + // options += $( '<option>', option )[ 0 ].outerHTML; + options += '<option'; + for ( val in option ) { + if ( option.hasOwnProperty( val ) && val !== 'text' ) { + options += ' ' + val + '="' + option[ val ] + '"'; + } + } + if ( !option.value ) { + options += ' value="' + option.text + '"'; + } + options += '>' + option.text + '</option>'; + // above code is needed in jQuery < v1.8 + + // make sure we don't turn an object into a string (objects without a "text" property) + } else if ( '' + option !== '[object Object]' ) { + txt = option = ( '' + option ).replace( tsfRegex.quote, '"' ); + val = txt; + // allow including a symbol in the selectSource array + // 'a-z|A through Z' so that 'a-z' becomes the option value + // and 'A through Z' becomes the option text + if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { + t = txt.split( wo.filter_selectSourceSeparator ); + val = t[0]; + txt = t[1]; + } + // replace quotes - fixes #242 & ignore empty strings + // see http://stackoverflow.com/q/14990971/145346 + options += option !== '' ? + '<option ' + + ( val === txt ? '' : 'data-function-name="' + option + '" ' ) + + 'value="' + val + '">' + txt + + '</option>' : ''; } - // replace quotes - fixes #242 & ignore empty strings - // see http://stackoverflow.com/q/14990971/145346 - options += arry[indx] !== '' ? - '<option ' + - ( val === txt ? '' : 'data-function-name="' + arry[indx] + '" ' ) + - 'value="' + val + '">' + txt + - '</option>' : ''; } // clear arry so it doesn't get appended twice arry = []; @@ -2007,6 +2025,9 @@ } }; + // filter regex variable + tsfRegex = tsf.regex; + ts.getFilters = function( table, getRaw, setFilters, skipFirst ) { var i, $filters, $column, cols, filters = false, diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js index 4180e65..a6d8195 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js @@ -1,4 +1,4 @@ -/*! Parser: filetype *//* +/*! Parser: filetype - updated 11/10/2015 (v2.24.4) *//* * When a file type extension is found, the equivalent name is * prefixed into the parsed data, so sorting occurs in groups */ @@ -43,6 +43,7 @@ var t, c = table.config, wo = c.widgetOptions, + groupSeparator = wo.group_separator || '-', i = s.lastIndexOf('.'), sep = $.tablesorter.fileTypes.separator, m = $.tablesorter.fileTypes.matching, @@ -60,7 +61,8 @@ if (m.indexOf(t) >= 0) { for (i in types) { if ((sep + types[i] + sep).indexOf(t) >= 0) { - return i + (wo.group_separator ? wo.group_separator : '-') + s; + // groupSeparator may use a regular expression! + return i + ( groupSeparator.toString().charAt(0) !== '/' ? wo.group_separator : '-' ) + s; } } } @@ -70,4 +72,24 @@ type: 'text' }); + // sort by file extension + // converts "this.is.an.image.jpg" into "jpg.this.is.an.image" + $.tablesorter.addParser({ + id: 'file-extension', + is: function() { + return false; + }, + format: function( str ) { + var ext, + parts = str.split( '.' ); + if ( parts.length ) { + ext = parts.pop(); + parts.unshift( ext ); + return parts.join( '.' ); + } + return str; + }, + type: 'text' + }); + })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index a28059c..d247f23 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,10 +1,10 @@ -/*! Widget: filter - updated 11/4/2015 (v2.24.3) *//* +/*! Widget: filter - updated 11/10/2015 (v2.24.4) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ ;( function ( $ ) { 'use strict'; - var tsf, + var tsf, tsfRegex, ts = $.tablesorter || {}, tscss = ts.css; @@ -110,20 +110,21 @@ // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class ) types: { or : function( c, data, vars ) { - if ( tsf.regex.orTest.test( data.iFilter ) || tsf.regex.orSplit.test( data.filter ) ) { + // look for "|", but not if it is inside of a regular expression + if ( ( tsfRegex.orTest.test( data.iFilter ) || tsfRegex.orSplit.test( data.filter ) ) && + // this test for regex has potential to slow down the overall search + !tsfRegex.regex.test( data.filter ) ) { var indx, filterMatched, query, regex, // duplicate data but split filter data2 = $.extend( {}, data ), - index = data.index, - parsed = data.parsed[ index ], - filter = data.filter.split( tsf.regex.orSplit ), - iFilter = data.iFilter.split( tsf.regex.orSplit ), + filter = data.filter.split( tsfRegex.orSplit ), + iFilter = data.iFilter.split( tsfRegex.orSplit ), len = filter.length; for ( indx = 0; indx < len; indx++ ) { data2.nestedFilters = true; - data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' ); - data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); - query = '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')'; + data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], data ) || '' ); + data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], data ) || '' ); + query = '(' + ( tsf.parseFilter( c, data2.filter, data ) || '' ) + ')'; try { // use try/catch, because query may not be a valid regex if "|" is contained within a partial regex search, // e.g "/(Alex|Aar" -> Uncaught SyntaxError: Invalid regular expression: /(/(Alex)/: Unterminated group @@ -145,22 +146,20 @@ }, // Look for an AND or && operator ( logical and ) and : function( c, data, vars ) { - if ( tsf.regex.andTest.test( data.filter ) ) { + if ( tsfRegex.andTest.test( data.filter ) ) { var indx, filterMatched, result, query, regex, // duplicate data but split filter data2 = $.extend( {}, data ), - index = data.index, - parsed = data.parsed[ index ], - filter = data.filter.split( tsf.regex.andSplit ), - iFilter = data.iFilter.split( tsf.regex.andSplit ), + filter = data.filter.split( tsfRegex.andSplit ), + iFilter = data.iFilter.split( tsfRegex.andSplit ), len = filter.length; for ( indx = 0; indx < len; indx++ ) { data2.nestedFilters = true; - data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' ); - data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' ); - query = ( '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' ) + data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], data ) || '' ); + data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], data ) || '' ); + query = ( '(' + ( tsf.parseFilter( c, data2.filter, data ) || '' ) + ')' ) // replace wild cards since /(a*)/i will match anything - .replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' ); + .replace( tsfRegex.wild01, '\\S{1}' ).replace( tsfRegex.wild0More, '\\S*' ); try { // use try/catch just in case RegExp is invalid regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); @@ -182,10 +181,10 @@ }, // Look for regex regex: function( c, data ) { - if ( tsf.regex.regex.test( data.filter ) ) { + if ( tsfRegex.regex.test( data.filter ) ) { var matches, // cache regex per column for optimal speed - regex = data.filter_regexCache[ data.index ] || tsf.regex.regex.exec( data.filter ), + regex = data.filter_regexCache[ data.index ] || tsfRegex.regex.exec( data.filter ), isRegex = regex instanceof RegExp; try { if ( !isRegex ) { @@ -204,18 +203,17 @@ // Look for operators >, >=, < or <= operators: function( c, data ) { // ignore empty strings... because '' < 10 is true - if ( tsf.regex.operTest.test( data.iFilter ) && data.iExact !== '' ) { + if ( tsfRegex.operTest.test( data.iFilter ) && data.iExact !== '' ) { var cachedValue, result, txt, table = c.table, - index = data.index, - parsed = data.parsed[index], - query = ts.formatFloat( data.iFilter.replace( tsf.regex.operators, '' ), table ), - parser = c.parsers[index], + parsed = data.parsed[ data.index ], + query = ts.formatFloat( data.iFilter.replace( tsfRegex.operators, '' ), table ), + parser = c.parsers[ data.index ], savedSearch = query; // parse filter value in case we're comparing numbers ( dates ) if ( parsed || parser.type === 'numeric' ) { - txt = $.trim( '' + data.iFilter.replace( tsf.regex.operators, '' ) ); - result = tsf.parseFilter( c, txt, index, true ); + txt = $.trim( '' + data.iFilter.replace( tsfRegex.operators, '' ) ); + result = tsf.parseFilter( c, txt, data, true ); query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query; } // iExact may be numeric - see issue #149; @@ -227,10 +225,10 @@ txt = isNaN( data.iExact ) ? data.iExact.replace( ts.regex.nondigit, '' ) : data.iExact; cachedValue = ts.formatFloat( txt, table ); } - if ( tsf.regex.gtTest.test( data.iFilter ) ) { - result = tsf.regex.gteTest.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; - } else if ( tsf.regex.ltTest.test( data.iFilter ) ) { - result = tsf.regex.lteTest.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; + if ( tsfRegex.gtTest.test( data.iFilter ) ) { + result = tsfRegex.gteTest.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; + } else if ( tsfRegex.ltTest.test( data.iFilter ) ) { + result = tsfRegex.lteTest.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; } // keep showing all rows if nothing follows the operator if ( !result && savedSearch === '' ) { @@ -242,13 +240,13 @@ }, // Look for a not match notMatch: function( c, data ) { - if ( tsf.regex.notTest.test( data.iFilter ) ) { + if ( tsfRegex.notTest.test( data.iFilter ) ) { var indx, txt = data.iFilter.replace( '!', '' ), - filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; - if ( tsf.regex.exact.test( filter ) ) { + filter = tsf.parseFilter( c, txt, data ) || ''; + if ( tsfRegex.exact.test( filter ) ) { // look for exact not matches - see #628 - filter = filter.replace( tsf.regex.exact, '' ); + filter = filter.replace( tsfRegex.exact, '' ); return filter === '' ? true : $.trim( filter ) !== data.iExact; } else { indx = data.iExact.search( $.trim( filter ) ); @@ -260,29 +258,29 @@ // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric exact: function( c, data ) { /*jshint eqeqeq:false */ - if ( tsf.regex.exact.test( data.iFilter ) ) { - var txt = data.iFilter.replace( tsf.regex.exact, '' ), - filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + if ( tsfRegex.exact.test( data.iFilter ) ) { + var txt = data.iFilter.replace( tsfRegex.exact, '' ), + filter = tsf.parseFilter( c, txt, data ) || ''; return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact; } return null; }, // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu! range : function( c, data ) { - if ( tsf.regex.toTest.test( data.iFilter ) ) { + if ( tsfRegex.toTest.test( data.iFilter ) ) { var result, tmp, range1, range2, table = c.table, index = data.index, parsed = data.parsed[index], // make sure the dash is for a range and not indicating a negative number - query = data.iFilter.split( tsf.regex.toSplit ); + query = data.iFilter.split( tsfRegex.toSplit ); tmp = query[0].replace( ts.regex.nondigit, '' ) || ''; - range1 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table ); + range1 = ts.formatFloat( tsf.parseFilter( c, tmp, data ), table ); tmp = query[1].replace( ts.regex.nondigit, '' ) || ''; - range2 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table ); + range2 = ts.formatFloat( tsf.parseFilter( c, tmp, data ), table ); // parse filter value in case we're comparing numbers ( dates ) - if ( parsed || c.parsers[index].type === 'numeric' ) { + if ( parsed || c.parsers[ index ].type === 'numeric' ) { result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index ); range1 = ( result !== '' && !isNaN( result ) ) ? result : range1; result = c.parsers[ index ].format( '' + query[1], table, c.$headers.eq( index ), index ); @@ -303,18 +301,16 @@ }, // Look for wild card: ? = single, * = multiple, or | = logical OR wild : function( c, data ) { - if ( tsf.regex.wildOrTest.test( data.iFilter ) ) { - var index = data.index, - parsed = data.parsed[ index ], - query = '' + ( tsf.parseFilter( c, data.iFilter, index, parsed ) || '' ); + if ( tsfRegex.wildOrTest.test( data.iFilter ) ) { + var query = '' + ( tsf.parseFilter( c, data.iFilter, data ) || '' ); // look for an exact match with the 'or' unless the 'filter-match' class is found - if ( !tsf.regex.wildTest.test( query ) && data.nestedFilters ) { + if ( !tsfRegex.wildTest.test( query ) && data.nestedFilters ) { query = data.isMatch ? query : '^(' + query + ')$'; } // parsing the filter may not work properly when using wildcards =/ try { return new RegExp( - query.replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' ), + query.replace( tsfRegex.wild01, '\\S{1}' ).replace( tsfRegex.wild0More, '\\S*' ), c.widgetOptions.filter_ignoreCase ? 'i' : '' ) .test( data.exact ); @@ -326,21 +322,18 @@ }, // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license ) fuzzy: function( c, data ) { - if ( tsf.regex.fuzzyTest.test( data.iFilter ) ) { + if ( tsfRegex.fuzzyTest.test( data.iFilter ) ) { var indx, patternIndx = 0, len = data.iExact.length, txt = data.iFilter.slice( 1 ), - pattern = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || ''; + pattern = tsf.parseFilter( c, txt, data ) || ''; for ( indx = 0; indx < len; indx++ ) { if ( data.iExact[ indx ] === pattern[ patternIndx ] ) { patternIndx += 1; } } - if ( patternIndx === pattern.length ) { - return true; - } - return false; + return patternIndx === pattern.length; } return null; } @@ -353,8 +346,7 @@ and : 'and' }, ts.language ); - var options, string, txt, $header, column, filters, val, fxn, noSelect, - regex = tsf.regex; + var options, string, txt, $header, column, filters, val, fxn, noSelect; c.$table.addClass( 'hasFilters' ); // define timers so using clearTimeout won't cause an undefined error @@ -365,8 +357,8 @@ wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; - val = '\\{' + tsf.regex.query + '\\}'; - $.extend( regex, { + val = '\\{' + tsfRegex.query + '\\}'; + $.extend( tsfRegex, { child : new RegExp( c.cssChildRow ), filtered : new RegExp( wo.filter_filteredRow ), alreadyFiltered : new RegExp( '(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i' ), @@ -644,8 +636,10 @@ c.$table.data( 'lastSearch', filters ); return filters; }, - parseFilter: function( c, filter, column, parsed ) { - return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter; + parseFilter: function( c, filter, data, parsed ) { + return parsed || data.parsed[ data.index ] ? + c.parsers[ data.index ].format( filter, c.table, [], data.index ) : + filter; }, buildRow: function( table, c, wo ) { var $filter, col, column, $header, makeSelect, disabled, name, ffxn, tmp, @@ -838,8 +832,12 @@ c.lastCombinedFilter = null; c.lastSearch = []; } - // convert filters to strings (maybe not the best method)- see #1070 - filters = filters.join( '\u0000' ).split( '\u0000' ); + // convert filters to strings - see #1070 + filters = Array.prototype.map ? + filters.map( String ) : + // for IE8 & older browsers - maybe not the best method + filters.join( '\u0000' ).split( '\u0000' ); + if ( wo.filter_initialized ) { c.$table.trigger( 'filterStart', [ filters ] ); } @@ -893,8 +891,8 @@ }, defaultFilter: function( filter, mask ) { if ( filter === '' ) { return filter; } - var regex = tsf.regex.iQuery, - maskLen = mask.match( tsf.regex.igQuery ).length, + var regex = tsfRegex.iQuery, + maskLen = mask.match( tsfRegex.igQuery ).length, query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ], len = query.length - 1, indx = 0, @@ -991,9 +989,8 @@ return filterMatched; }, processRow: function( c, data, vars ) { - var hasSelect, result, val, filterMatched, + var result, filterMatched, fxn, ffxn, txt, - regex = tsf.regex, wo = c.widgetOptions, showRow = true, @@ -1072,7 +1069,7 @@ result = data.rawArray[ columnIndex ] || ''; data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 } - data.iExact = !regex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? + data.iExact = !tsfRegex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; data.isMatch = c.$headerIndexed[ data.index ].hasClass( 'filter-match' ); @@ -1090,21 +1087,13 @@ data.filter = ts.replaceAccents( data.filter ); } - val = true; - if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) { - data.filter = tsf.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); - // val is used to indicate that a filter select is using a default filter; - // so we override the exact & partial matches - val = false; - } // data.iFilter = case insensitive ( if wo.filter_ignoreCase is true ), // data.filter = case sensitive data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter; fxn = vars.functions[ columnIndex ]; - hasSelect = c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' ); filterMatched = null; - if ( fxn || ( hasSelect && val ) ) { - if ( fxn === true || hasSelect ) { + if ( fxn ) { + if ( fxn === true ) { // default selector uses exact match unless 'filter-match' class is found filterMatched = data.isMatch ? // data.iExact may be a number @@ -1130,7 +1119,7 @@ // Look for match, and add child row data for matching } else { txt = ( data.iExact + data.childRowText ) - .indexOf( tsf.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) ); + .indexOf( tsf.parseFilter( c, data.iFilter, data ) ); result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); } } else { @@ -1150,7 +1139,6 @@ isChild, childRow, lastSearch, showRow, showParent, time, val, indx, notFiltered, searchFiltered, query, injected, res, id, txt, storedFilters = $.extend( [], filters ), - regex = tsf.regex, c = table.config, wo = c.widgetOptions, // data object passed to filters; anyMatch is a flag for the filters @@ -1232,7 +1220,7 @@ ); if ( wo.filter_columnAnyMatch ) { // specific columns search - query = data.anyMatchFilter.split( regex.andSplit ); + query = data.anyMatchFilter.split( tsfRegex.andSplit ); injected = false; for ( indx = 0; indx < query.length; indx++ ) { res = query[ indx ].split( ':' ); @@ -1267,12 +1255,12 @@ // there are no changes from beginning of filter val.indexOf( lastSearch[indx] || '' ) === 0 && // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string - !regex.alreadyFiltered.test( val ) && + !tsfRegex.alreadyFiltered.test( val ) && // if we are not doing exact matches, using '|' ( logical or ) or not '!' - !regex.exactTest.test( val ) && + !tsfRegex.exactTest.test( val ) && // don't search only filtered if the value is negative // ( '> -10' => '> -100' will ignore hidden rows ) - !( regex.isNeg1.test( val ) || regex.isNeg2.test( val ) ) && + !( tsfRegex.isNeg1.test( val ) || tsfRegex.isNeg2.test( val ) ) && // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 !( val !== '' && c.$filters && c.$filters.filter( '[data-column="' + indx + '"]' ).find( 'select' ).length && !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); @@ -1290,7 +1278,7 @@ // replace accents data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter ); } - if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultAnyFilter ) ) { + if ( wo.filter_defaultFilter && tsfRegex.iQuery.test( vars.defaultAnyFilter ) ) { data.anyMatchFilter = tsf.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter ); // clear search filtered flag because default filters are not saved to the last search searchFiltered = false; @@ -1307,9 +1295,9 @@ txt = $rows[ rowIndex ].className; // the first row can never be a child row - isChild = rowIndex && regex.child.test( txt ); + isChild = rowIndex && tsfRegex.child.test( txt ); // skip child rows & already filtered rows - if ( isChild || ( searchFiltered && regex.filtered.test( txt ) ) ) { + if ( isChild || ( searchFiltered && tsfRegex.filtered.test( txt ) ) ) { continue; } @@ -1419,7 +1407,6 @@ // custom select source function for a SPECIFIC COLUMN arry = fxn( table, column, onlyAvail ); } - if ( arry === false ) { // fall back to original method arry = tsf.getOptions( table, column, onlyAvail ); @@ -1433,18 +1420,19 @@ return false; } table = $( table )[0]; - var cts, txt, indx, len, + var cts, txt, indx, len, parsedTxt, str, c = table.config, validColumn = typeof column !== 'undefined' && column !== null && column >= 0 && column < c.columns, parsed = []; - // get unique elements and sort the list // if $.tablesorter.sortText exists ( not in the original tablesorter ), // then natural sort the list otherwise use a basic sort arry = $.grep( arry, function( value, indx ) { + if ( value.text ) { + return true; + } return $.inArray( value, arry ) === indx; }); - if ( validColumn && c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) { // unsorted select options return arry; @@ -1453,22 +1441,30 @@ // parse select option values for ( indx = 0; indx < len; indx++ ) { txt = arry[ indx ]; + // check for object + str = txt.text ? txt.text : txt; + // sortNatural breaks if you don't pass it strings + parsedTxt = ( validColumn && c.parsers && c.parsers.length && + c.parsers[ column ].format( str, table, [], column ) || str ).toString(); + parsedTxt = c.widgetOptions.filter_ignoreCase ? parsedTxt.toLowerCase() : parsedTxt; // parse array data using set column parser; this DOES NOT pass the original // table cell to the parser format function - parsed.push({ - t : txt, - // check parser length - fixes #934 - p : validColumn && c.parsers && c.parsers.length && - c.parsers[ column ].format( txt, table, [], column ) || txt - }); + if ( txt.text ) { + txt.parsed = parsedTxt; + parsed.push( txt ); + } else { + parsed.push({ + text : txt, + // check parser length - fixes #934 + parsed : parsedTxt + }); + } } - // sort parsed select options cts = c.textSorter || ''; parsed.sort( function( a, b ) { - // sortNatural breaks if you don't pass it strings - var x = a.p.toString(), - y = b.p.toString(); + var x = a.parsed, + y = b.parsed; if ( validColumn && typeof cts === 'function' ) { // custom OVERALL text sorter return cts( x, y, true, column, table ); @@ -1486,7 +1482,7 @@ arry = []; len = parsed.length; for ( indx = 0; indx < len; indx++ ) { - arry.push( parsed[indx].t ); + arry.push( parsed[indx] ); } return arry; } @@ -1546,7 +1542,7 @@ return; } - var indx, val, txt, t, $filters, $filter, + var indx, val, txt, t, $filters, $filter, option, c = table.config, wo = c.widgetOptions, node = c.$headerIndexed[ column ], @@ -1571,23 +1567,45 @@ if ( $.isArray( arry ) ) { // build option list for ( indx = 0; indx < arry.length; indx++ ) { - txt = arry[indx] = ( '' + arry[indx] ).replace( tsf.regex.quote, '"' ); - val = txt; - // allow including a symbol in the selectSource array - // 'a-z|A through Z' so that 'a-z' becomes the option value - // and 'A through Z' becomes the option text - if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { - t = txt.split( wo.filter_selectSourceSeparator ); - val = t[0]; - txt = t[1]; + option = arry[ indx ]; + if ( option.text ) { + // OBJECT!! add data-function-name in case the value is set in filter_functions + option['data-function-name'] = typeof option.value === 'undefined' ? option.text : option.value; + + // support jQuery < v1.8, otherwise the below code could be shortened to + // options += $( '<option>', option )[ 0 ].outerHTML; + options += '<option'; + for ( val in option ) { + if ( option.hasOwnProperty( val ) && val !== 'text' ) { + options += ' ' + val + '="' + option[ val ] + '"'; + } + } + if ( !option.value ) { + options += ' value="' + option.text + '"'; + } + options += '>' + option.text + '</option>'; + // above code is needed in jQuery < v1.8 + + // make sure we don't turn an object into a string (objects without a "text" property) + } else if ( '' + option !== '[object Object]' ) { + txt = option = ( '' + option ).replace( tsfRegex.quote, '"' ); + val = txt; + // allow including a symbol in the selectSource array + // 'a-z|A through Z' so that 'a-z' becomes the option value + // and 'A through Z' becomes the option text + if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { + t = txt.split( wo.filter_selectSourceSeparator ); + val = t[0]; + txt = t[1]; + } + // replace quotes - fixes #242 & ignore empty strings + // see http://stackoverflow.com/q/14990971/145346 + options += option !== '' ? + '<option ' + + ( val === txt ? '' : 'data-function-name="' + option + '" ' ) + + 'value="' + val + '">' + txt + + '</option>' : ''; } - // replace quotes - fixes #242 & ignore empty strings - // see http://stackoverflow.com/q/14990971/145346 - options += arry[indx] !== '' ? - '<option ' + - ( val === txt ? '' : 'data-function-name="' + arry[indx] + '" ' ) + - 'value="' + val + '">' + txt + - '</option>' : ''; } // clear arry so it doesn't get appended twice arry = []; @@ -1633,6 +1651,9 @@ } }; + // filter regex variable + tsfRegex = tsf.regex; + ts.getFilters = function( table, getRaw, setFilters, skipFirst ) { var i, $filters, $column, cols, filters = false, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index 9321ac1..4cbcde6 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -1,4 +1,4 @@ -/*! Widget: grouping - updated 11/2/2015 (v2.24.1) *//* +/*! Widget: grouping - updated 11/10/2015 (v2.24.4) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -223,7 +223,7 @@ if ( data.group !== data.currentGroup ) { data.group = data.currentGroup; if ( $.isFunction( wo.group_formatter ) ) { - data.currentGroup = wo.group_formatter( ( data.group || '' ).toString(), data.column, c.table, c, wo ) || data.group; + data.currentGroup = wo.group_formatter( ( data.group || '' ).toString(), data.column, c.table, c, wo, data ) || data.group; } data.$row.before( tsg.groupHeaderHTML( c, wo, data ) ); if ( wo.group_saveGroups && !data.savedGroup && wo.group_collapsed && wo.group_collapsible ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js index f9c2ba7..599086f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js @@ -1,4 +1,4 @@ -/*! Widget: headerTitles - updated 10/31/2015 (v2.24.0) *//* +/*! Widget: headerTitles - updated 11/10/2015 (v2.24.4) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -59,7 +59,7 @@ sortDirection = $this.hasClass(ts.css.sortAsc) ? 0 : $this.hasClass(ts.css.sortDesc) ? 1 : 2, sortNext = c.sortVars[ col ].order[ ( c.sortVars[ col ].count + 1 ) % ( c.sortReset ? 3 : 2 ) ]; if (wo.headerTitle_useAria) { - txt = $this.hasClass('sorter-false') ? wo.headerTitle_output_nosort : $this.attr('aria-label') || ''; + txt = $this.attr('aria-label') || wo.headerTitle_output_nosort || ''; } else { txt = (wo.headerTitle_prefix || '') + // now deprecated ($this.hasClass('sorter-false') ? wo.headerTitle_output_nosort : diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index b4b4ce2..da3795c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -1,4 +1,4 @@ -/*! Widget: math - updated 10/31/2015 (v2.24.0) *//* +/*! Widget: math - updated 11/10/2015 (v2.24.4) *//* * Requires tablesorter v2.16+ and jQuery 1.7+ * by Rob Garrison */ @@ -172,17 +172,18 @@ // all non-info tbody cells mathAttr = wo.math_dataAttrib; - $mathCells = c.$tbodies.find( '[' + mathAttr + ']' ); + $mathCells = c.$tbodies.children( 'tr' ).children( '[' + mathAttr + ']' ); math.mathType( c, $mathCells, wo.math_priority ); // only info tbody cells $mathCells = c.$table .children( '.' + c.cssInfoBlock + ', tfoot' ) - .find( '[' + mathAttr + ']' ); + .children( 'tr' ) + .children( '[' + mathAttr + ']' ); math.mathType( c, $mathCells, wo.math_priority ); // find the 'all' total - $mathCells = c.$table.find( '[' + mathAttr + '^=all]' ); + $mathCells = c.$table.children().children( 'tr' ).children( '[' + mathAttr + '^=all]' ); math.mathType( c, $mathCells, [ 'all' ] ); wo.math_isUpdating = true; @@ -522,7 +523,7 @@ if ( refreshing ) { return; } c.$table .off( ( math.events + ' updateComplete.tsmath ' + wo.math_event ).replace( /\s+/g, ' ' ) ) - .find( '[data-' + wo.math_data + ']' ).empty(); + .children().children( 'tr' ).children( '[data-' + wo.math_data + ']' ).empty(); } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 48c27b3..ee44b10 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 10/31/2015 (v2.24.0) */ +/*! Widget: Pager - updated 11/10/2015 (v2.24.4) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -13,7 +13,7 @@ priority: 55, // load pager after filter widget options : { // output default: '{page}/{totalPages}' - // possible variables: {page}, {totalPages}, {filteredPages}, {startRow}, {endRow}, {filteredRows} and {totalRows} + // possible variables: {size}, {page}, {totalPages}, {filteredPages}, {startRow}, {endRow}, {filteredRows} and {totalRows} pager_output: '{startRow} to {endRow} of {totalRows} rows', // '{page}/{totalPages}' // apply disabled classname to the pager arrows when the rows at either extreme is visible @@ -654,6 +654,12 @@ wo = c.widgetOptions; // process data if ( $.isFunction(wo.pager_ajaxProcessing) ) { + + // in case nothing is returned by ajax, empty out the table; see #1032 + // but do it before calling pager_ajaxProcessing because that function may add content + // directly to the table + c.$tbodies.eq(0).empty(); + // ajaxProcessing result: [ total, rows, headers ] var i, j, t, hsh, $f, $sh, $headers, $h, icon, th, d, l, rr_count, len, $table = c.$table, @@ -712,9 +718,6 @@ if (wo.pager_processAjaxOnInit) { c.$tbodies.eq(0).html( tds ); } - } else { - // nothing returned by ajax, empty out the table; see #1032 - c.$tbodies.eq(0).empty(); } wo.pager_processAjaxOnInit = true; // only add new header text if the length matches diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js index 1467fb5..58febce 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js @@ -1,11 +1,11 @@ -/*! Widget: sort2Hash (BETA) - updated 11/2/2015 (v2.24.1) */ +/*! Widget: sort2Hash (BETA) - updated 11/10/2015 (v2.24.4) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ ;( function( $ ) { 'use strict'; var ts = $.tablesorter || {}, - s2h = { + s2h = ts.sort2Hash = { init : function( c, wo ) { var filter, temp, page, size, table = c.table, @@ -17,7 +17,7 @@ } if ( ts.hasWidget( c.table, 'pager' ) ) { temp = parseInt( s2h.decodeHash( c, wo, 'page' ), 10 ); - page = pager.page = ( temp < 0 ? 0 : ( temp > pager.totalPages ? pager.totalPages - 1 : temp ) ) + 1; + page = pager.page = ( temp < 0 ? 0 : ( temp > pager.totalPages ? pager.totalPages - 1 : temp ) ); size = pager.size = parseInt( s2h.decodeHash( c, wo, 'size' ), 10 ); } if ( ts.hasWidget( table, 'filter' ) ) { @@ -33,9 +33,13 @@ }, 100 ); }); } - } else { - c.$table.trigger( 'pageAndSize', [ page, size ] ); } + if ( !filter ) { + c.$table.one( 'tablesorter-ready', function() { + c.$table.trigger( 'pageAndSize', [ page, size ] ); + }); + } + c.$table.on( 'sortEnd.sort2hash filterEnd.sort2hash pagerComplete.sort2Hash', function() { if ( this.hasInitialized ) { s2h.setHash( this.config, this.config.widgetOptions ); @@ -125,6 +129,33 @@ return sort.join( wo.sort2Hash_separator ); }, + // Get URL Parameters (getParam) + // modified from http://www.netlobo.com/url_query_string_javascript.html + getParam : function ( name, hash, returnRegex ) { + if ( !hash ) { hash = window.location.hash; } + var regex = new RegExp( '[\\?&]' + s2h.regexEscape( name ) + '=([^&#]*)' ), + match = regex.exec( hash ); + if ( returnRegex ) { return regex; } + return match === null ? '' : decodeURIComponent( match[ 1 ] ); + }, + + // remove parameter from hash + removeParam : function( name, hash ) { + if ( !hash ) { hash = window.location.hash; } + var index, + regex = s2h.getParam( name, hash, true ), + result = [], + parts = hash.split( '&' ), + len = parts.length; + for ( index = 0; index < len; index++ ) { + // regex expects a leading '&'... + if ( !regex.test( '&' + parts[ index ] ) ) { + result.push( parts[ index ] ); + } + } + return result.length ? result.join( '&' ) : ''; + }, + encodeHash : function( c, wo, component, value, rawValue ) { var result = false, tableId = s2h.getTableId( c, wo ); @@ -138,39 +169,30 @@ }, decodeHash : function( c, wo, component ) { - var regex, - result = false, + var result = false, tableId = s2h.getTableId( c, wo ); if ( typeof wo.sort2Hash_decodeHash === 'function' ) { + // return a string result = wo.sort2Hash_decodeHash( c, tableId, component ); } if ( result === false ) { - regex = new RegExp( '[\\#&]' + component + '\\[' + s2h.regexEscape( tableId ) + '\\]=([^&]*)' ), - /*jshint -W030 */ - result = regex.exec( window.location.hash ); + result = s2h.getParam( component + '[' + tableId + ']' ); } - return result ? decodeURIComponent( result[ 1 ] ) : ''; + return result || ''; }, cleanHash : function( c, wo, component, hash ) { - var index, len, parts, regex, - result = false, + var result = false, tableId = s2h.getTableId( c, wo ); if ( typeof wo.sort2Hash_cleanHash === 'function' ) { + // can return an array or string result = wo.sort2Hash_cleanHash( c, tableId, component, hash ); } if ( result === false ) { - result = []; - parts = ( hash || '' ).slice(1).split( '&' ); - len = parts.length; - regex = new RegExp( component + '\\[' + s2h.regexEscape( tableId ) + '\\]=([^&]*)' ); - for ( index = 0; index < len; index++ ) { - if ( !regex.test( parts[ index ] ) ) { - result.push( parts[ index ] ); - } - } + // parameter example: 'sort[table0]=0,0' + result = s2h.removeParam( component + '[' + tableId + ']', hash ); } - return result.length ? '#' + result.join( '&' ) : ''; + return result || ''; }, setHash : function( c, wo ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-toggle.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-toggle.js new file mode 100644 index 0000000..c7f4214 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-toggle.js @@ -0,0 +1,81 @@ +/*! tablesorter enable/disable sort & filter (BETA) - 11/10/2015 (v2.24.4) + * Requires tablesorter v2.24.4+ & jQuery 1.7+ + * by Rob Garrison + */ +;( function( $ ) { + 'use strict'; + var ts = $.tablesorter, + + tst = ts.toggleTS = { + + init : function( c, wo ) { + wo.toggleTS_isEnabled = true; // enabled + wo.toggleTS_areDisabled = { + headers : [], + filters : [] + }; + c.$table.on('enable.toggleTS disable.toggleTS', function( event ) { + tst.toggle( this.config, this.config.widgetOptions, event.type === 'enable' ); + }); + }, + toggle : function( c, wo, isEnabled ) { + if ( wo.toggleTS_isEnabled !== isEnabled ) { + wo.toggleTS_isEnabled = isEnabled; + var indx, $el, + len = c.$headers.length; + + // table headers + for ( indx = 0; indx < len; indx++ ) { + $el = c.$headers.eq( indx ); + // function added in v2.24.4 + ts.setColumnSort( c, $el, !isEnabled ); + // function added in v2.24.4; passing "isEnabled" allows removal of "next sort" labels + ts.setColumnAriaLabel( c, $el, isEnabled ); + } + if ( wo.toggleTS_hideFilterRow ) { + c.$table.find( '.' + ts.css.filterRow ).toggle( isEnabled ); + } else if ( ts.hasWidget( c.$table, 'filter' ) ) { + // c.$filters points to filter CELL + len = c.$filters.length; + for ( indx = 0; indx < len; indx++ ) { + if ( isEnabled && !wo.toggleTS_areDisabled.filters[ indx ] ) { + c.$filters.eq( indx ).find( 'input, select' ) + .removeClass( ts.css.filterDisabled ) + .prop( 'disabled', false ); + } else if ( !isEnabled ) { + $el = c.$filters.eq( indx ).find( 'input, select' ); + if ( $el.hasClass( ts.css.filterDisabled ) ) { + wo.toggleTS_areDisabled.filters[ indx ] = true; + } + $el + .addClass( ts.css.filterDisabled ) + .prop( 'disabled', true ); + } + } + } + // include external filters + wo.filter_$externalFilters + .toggleClass( ts.css.filterDisabled, isEnabled ) + .prop( 'disabled', !isEnabled ); + } + if ( typeof wo.toggleTS_callback === 'function' ) { + wo.toggleTS_callback( c, isEnabled ); + } + } + }; + + ts.addWidget({ + id: 'toggle-ts', + options: { + toggleTS_hideFilterRow : false, + toggleTS_callback : null + }, + init : function( table, thisWidget, c, wo ) { + tst.init( c, wo ); + }, + remove : function( table, c ) { + c.$table.off( 'enable.toggleTS disable.toggleTS' ); + } + }); + +})( jQuery ); From 12cfa784106bdfca9e830e0686141cfaf2caf1f0 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Thu, 12 Nov 2015 20:08:05 +0100 Subject: [PATCH 072/138] Update tablesorter to latest version (2.24.5) --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 4 ++-- .../jquery-tablesorter/jquery.tablesorter.combined.js | 6 +++--- .../javascripts/jquery-tablesorter/jquery.tablesorter.js | 4 ++-- .../jquery-tablesorter/jquery.tablesorter.widgets.js | 2 +- 8 files changed, 15 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88196c3..1cd50ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.19.3 (2015-11-12) + +* Upgrade tablesorter to v2.24.5 + #### v1.19.2 (2015-11-11) * Upgrade tablesorter to v2.24.4 diff --git a/README.md b/README.md index 514a458..deeb367 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.24.4 (11/10/2015), [documentation] +Current tablesorter version: 2.24.5 (11/10/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 7a0472d..73bf366 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,3 @@ module JqueryTablesorter - VERSION = '1.19.2' + VERSION = '1.19.3' end diff --git a/tablesorter b/tablesorter index a203ac5..fc04cea 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit a203ac5f526e7ca4f6becadcf852e45e6ffdb5aa +Subproject commit fc04ceaad6330d27b4f2c03273511c62ba065b4d diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 7f9ed05..8ad7e0d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin - * updated 11/10/2015 (v2.24.4) + * updated 11/10/2015 (v2.24.5) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -389,7 +389,7 @@ // in case nothing is returned by ajax, empty out the table; see #1032 // but do it before calling pager_ajaxProcessing because that function may add content // directly to the table - c.$tbodies.eq(0).empty(); + table.config.$tbodies.eq(0).empty(); // ajaxProcessing result: [ total, rows, headers ] var i, j, t, hsh, $f, $sh, $headers, $h, icon, th, d, l, rr_count, len, diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 1759f4f..f4642a1 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 11-10-2015 (v2.24.4)*/ +/*! tablesorter (FORK) - updated 11-10-2015 (v2.24.5)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.24.4 *//* +/*! TableSorter (FORK) v2.24.5 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -39,7 +39,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.24.4', + version : '2.24.5', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 7efba00..74332a6 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.24.4 *//* +/*! TableSorter (FORK) v2.24.5 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -21,7 +21,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.24.4', + version : '2.24.5', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 2f03958..53b74d3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 11-10-2015 (v2.24.4)*/ +/*! tablesorter (FORK) - updated 11-10-2015 (v2.24.5)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { From 21588258cff5210f30c3ce78b37d60c43154e1de Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Thu, 12 Nov 2015 20:14:18 +0100 Subject: [PATCH 073/138] Build version by using three constants --- lib/jquery-tablesorter/version.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 73bf366..05505dd 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,3 +1,7 @@ module JqueryTablesorter - VERSION = '1.19.3' + MAJOR = 1 + MINOR = 19 + TINY = 3 + + VERSION = [MAJOR, MINOR, TINY].compact.join('.') end From 2fb01d3c417e84e065a4f1d5938495fd4271fd86 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 23 Nov 2015 19:30:57 +0100 Subject: [PATCH 074/138] Update tablesorter to latest version (2.24.6) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 48 +- .../jquery.tablesorter.combined.js | 41 +- .../jquery-tablesorter/jquery.tablesorter.js | 39 +- .../jquery.tablesorter.widgets.js | 2 +- .../parsers/parser-date-month.js | 37 +- .../parsers/parser-date-range.js | 90 +- .../parsers/parser-date-two-digit-year.js | 9 +- .../parsers/parser-date-weekday.js | 58 +- .../parsers/parser-image.js | 1 + .../parsers/parser-input-select.js | 97 +- .../widgets/widget-filter-type-insideRange.js | 10 +- .../jquery-tablesorter/widgets/widget-math.js | 68 +- .../widgets/widget-pager.js | 950 +++++++++--------- .../widgets/widget-print.js | 28 +- .../widgets/widget-sortTbodies.js | 4 +- 19 files changed, 880 insertions(+), 612 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cd50ca..9ca9904 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.19.4 (2015-11-23) + +* Upgrade tablesorter to v2.24.6 + #### v1.19.3 (2015-11-12) * Upgrade tablesorter to v2.24.5 diff --git a/README.md b/README.md index deeb367..8695c82 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.24.5 (11/10/2015), [documentation] +Current tablesorter version: 2.24.6 (11/22/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 05505dd..e5b41c1 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 19 - TINY = 3 + TINY = 4 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index fc04cea..9b02a23 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit fc04ceaad6330d27b4f2c03273511c62ba065b4d +Subproject commit 9b02a23890c5c7dec7362c1b4eebf1a13fec4264 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 8ad7e0d..020df44 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin - * updated 11/10/2015 (v2.24.5) + * updated 11/22/2015 (v2.24.6) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -126,13 +126,13 @@ $this = this, // hide arrows at extremes - pagerArrows = function(p, disable) { + pagerArrows = function( table, p, disable ) { var a = 'addClass', r = 'removeClass', d = p.cssDisabled, dis = !!disable, first = ( dis || p.page === 0 ), - tp = Math.min( p.totalPages, p.filteredPages ), + tp = getTotalPages( table, p ), last = ( dis || (p.page === tp - 1) || tp === 0 ); if ( p.updateArrows ) { p.$container.find(p.cssFirst + ',' + p.cssPrev)[ first ? a : r ](d).attr('aria-disabled', first); @@ -170,11 +170,11 @@ if (p.countChildRows) { t.push(c.cssChildRow); } p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method c.totalRows = p.totalRows; - parsePageNumber( p ); + parsePageNumber( table, p ); calcFilters(table, p); c.filteredRows = p.filteredRows; p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; - if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) { + if ( getTotalPages( table, p ) >= 0 ) { t = (p.size * p.page > p.filteredRows) && completed; p.page = (t) ? p.pageReset || 0 : p.page; p.startRow = (t) ? p.size * p.page + 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1); @@ -203,7 +203,7 @@ }); if ( p.$goto.length ) { t = ''; - options = buildPageSelect(p); + options = buildPageSelect( table, p ); len = options.length; for (indx = 0; indx < len; indx++) { t += '<option value="' + options[indx] + '">' + options[indx] + '</option>'; @@ -221,7 +221,7 @@ }); } } - pagerArrows(p); + pagerArrows( table, p ); fixHeight(table, p); if (p.initialized && completed !== false) { if (c.debug) { @@ -238,11 +238,11 @@ } }, - buildPageSelect = function(p) { + buildPageSelect = function( table, p ) { // Filter the options page number link array if it's larger than 'maxOptionSize' // as large page set links will slow the browser on large dom inserts var i, central_focus_size, focus_option_pages, insert_index, option_length, focus_length, - pg = Math.min( p.totalPages, p.filteredPages ) || 1, + pg = getTotalPages( table, p ) || 1, // make skip set size multiples of 5 skip_set_size = Math.ceil( ( pg / p.maxOptionSize ) / 5 ) * 5, large_collection = pg > p.maxOptionSize, @@ -373,7 +373,7 @@ p.size = parsePageSize( p, p.$size.val(), 'get' ); p.$size.val( parsePageSize( p, p.size, 'set' ) ); $.data(table, 'pagerLastSize', p.size); - pagerArrows(p); + pagerArrows( table, p ); if ( !p.removeRows ) { hideRows(table, p); $(table).bind('sortEnd filterEnd '.split(' ').join(table.config.namespace + 'pager '), function(){ @@ -661,7 +661,7 @@ showAllRows = function(table, p) { var index, $controls, len; if ( p.ajax ) { - pagerArrows(p, true); + pagerArrows( table, p, true ); } else { $.data(table, 'pagerLastPage', p.page); $.data(table, 'pagerLastSize', p.size); @@ -719,8 +719,7 @@ } // abort page move if the table has filters and has not been initialized if (p.ajax && ts.hasWidget(table, 'filter') && !c.widgetOptions.filter_initialized) { return; } - - parsePageNumber( p ); + parsePageNumber( table, p ); calcFilters(table, p); // fixes issue where one currentFilter is [] and the other is ['','',''], // making the next if comparison think the filters are different (joined by commas). Fixes #202. @@ -768,29 +767,33 @@ } }, + getTotalPages = function( table, p ) { + return ts.hasWidget( table, 'filter' ) ? Math.min( p.totalPages, p.filteredPages ) : p.totalPages; + }, + // set to either set or get value parsePageSize = function( p, size, mode ) { var s = parseInt( size, 10 ) || p.size || p.settings.size || 10, // if select does not contain an "all" option, use size setAll = p.$size.find( 'option[value="all"]' ).length ? 'all' : p.totalRows; return /all/i.test( size ) || s === p.totalRows ? - // "get" to set `p.size` or "set" to set `p.$size.val()` + // "get" to get `p.size` or "set" to set `p.$size.val()` ( mode === 'get' ? p.totalRows : setAll ) : ( mode === 'get' ? s : p.size ); }, - parsePageNumber = function( p ) { - var min = Math.min( p.totalPages, p.filteredPages ) - 1; + parsePageNumber = function( table, p ) { + var min = getTotalPages( table, p ) - 1; p.page = parseInt( p.page, 10 ); if ( p.page < 0 || isNaN( p.page ) ) { p.page = 0; } - if ( p.page > min && p.page !== 0 ) { p.page = min; } + if ( p.page > min && min >= 0 ) { p.page = min; } return p.page; }, setPageSize = function(table, size, p) { p.size = parsePageSize( p, size, 'get' ); p.$size.val( parsePageSize( p, p.size, 'set' ) ); - $.data(table, 'pagerLastPage', parsePageNumber( p ) ); + $.data(table, 'pagerLastPage', parsePageNumber( table, p ) ); $.data(table, 'pagerLastSize', p.size); p.totalPages = Math.ceil( p.totalRows / p.size ); p.filteredPages = Math.ceil( p.filteredRows / p.size ); @@ -803,14 +806,15 @@ }, moveToLastPage = function(table, p) { - p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 ); + p.page = getTotalPages( table, p ) - 1; moveToPage(table, p); }, moveToNextPage = function(table, p) { p.page++; - if ( p.page >= ( Math.min( p.totalPages, p.filteredPages ) - 1 ) ) { - p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 ); + var last = getTotalPages( table, p ) - 1; + if ( p.page >= last ) { + p.page = last; } moveToPage(table, p); }, @@ -855,7 +859,7 @@ size = p.$size.find('option[selected]').val(); p.size = $.data(table, 'pagerLastSize') || parsePageSize( p, p.size, 'get' ); p.$size.val( parsePageSize( p, p.size, 'set' ) ); // set page size - p.totalPages = Math.ceil( Math.min( p.totalRows, p.filteredRows ) / p.size ); + p.totalPages = Math.ceil( getTotalPages( table, p ) / p.size ); // if table id exists, include page display with aria info if ( table.id ) { info = table.id + '_pager_info'; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index f4642a1..f85005d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 11-10-2015 (v2.24.5)*/ +/*! tablesorter (FORK) - updated 11-22-2015 (v2.24.6)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.24.5 *//* +/*! TableSorter (FORK) v2.24.6 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -39,7 +39,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.24.5', + version : '2.24.6', parsers : [], widgets : [], @@ -207,6 +207,9 @@ bottom : false }, + // placeholder date parser data (globalize) + dates : {}, + // These methods can be applied on table.config instance instanceMethods : {}, @@ -330,10 +333,10 @@ $table .unbind( 'sortBegin' + c.namespace + ' sortEnd' + c.namespace ) .bind( 'sortBegin' + c.namespace + ' sortEnd' + c.namespace, function( e ) { - clearTimeout( c.processTimer ); + clearTimeout( c.timerProcessing ); ts.isProcessing( table ); if ( e.type === 'sortBegin' ) { - c.processTimer = setTimeout( function() { + c.timerProcessing = setTimeout( function() { ts.isProcessing( table, true ); }, 500 ); } @@ -750,20 +753,24 @@ }, detectParserForColumn : function( c, rows, rowIndex, cellIndex ) { - var cur, $node, + var cur, $node, row, indx = ts.parsers.length, node = false, nodeValue = '', keepLooking = true; while ( nodeValue === '' && keepLooking ) { rowIndex++; - if ( rows[ rowIndex ] ) { - node = rows[ rowIndex ].cells[ cellIndex ]; - nodeValue = ts.getElementText( c, node, cellIndex ); - $node = $( node ); - if ( c.debug ) { - console.log( 'Checking if value was empty on row ' + rowIndex + ', column: ' + - cellIndex + ': "' + nodeValue + '"' ); + row = rows[ rowIndex ]; + // stop looking after 50 empty rows + if ( row && rowIndex < 50 ) { + if ( row.className.indexOf( ts.cssIgnoreRow ) < 0 ) { + node = rows[ rowIndex ].cells[ cellIndex ]; + nodeValue = ts.getElementText( c, node, cellIndex ); + $node = $( node ); + if ( c.debug ) { + console.log( 'Checking if value was empty on row ' + rowIndex + ', column: ' + + cellIndex + ': "' + nodeValue + '"' ); + } } } else { keepLooking = false; @@ -1858,6 +1865,8 @@ } if ( c.debug ) { time = new Date(); } ts.addWidgetFromClass( table ); + // prevent "tablesorter-ready" from firing multiple times in a row + clearTimeout( c.timerReady ); if ( c.widgets.length ) { table.isApplyingWidgets = true; // ensure unique widget ids @@ -1927,11 +1936,11 @@ callback( table ); } } - setTimeout( function() { + c.timerReady = setTimeout( function() { table.isApplyingWidgets = false; $.data( table, 'lastWidgetApplication', new Date() ); - c.$table.trigger('tablesorter-ready'); - }, 0 ); + c.$table.trigger( 'tablesorter-ready' ); + }, 10 ); if ( c.debug ) { widget = c.widgets.length; console.log( 'Completed ' + diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 74332a6..b0ff788 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.24.5 *//* +/*! TableSorter (FORK) v2.24.6 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -21,7 +21,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.24.5', + version : '2.24.6', parsers : [], widgets : [], @@ -189,6 +189,9 @@ bottom : false }, + // placeholder date parser data (globalize) + dates : {}, + // These methods can be applied on table.config instance instanceMethods : {}, @@ -312,10 +315,10 @@ $table .unbind( 'sortBegin' + c.namespace + ' sortEnd' + c.namespace ) .bind( 'sortBegin' + c.namespace + ' sortEnd' + c.namespace, function( e ) { - clearTimeout( c.processTimer ); + clearTimeout( c.timerProcessing ); ts.isProcessing( table ); if ( e.type === 'sortBegin' ) { - c.processTimer = setTimeout( function() { + c.timerProcessing = setTimeout( function() { ts.isProcessing( table, true ); }, 500 ); } @@ -732,20 +735,24 @@ }, detectParserForColumn : function( c, rows, rowIndex, cellIndex ) { - var cur, $node, + var cur, $node, row, indx = ts.parsers.length, node = false, nodeValue = '', keepLooking = true; while ( nodeValue === '' && keepLooking ) { rowIndex++; - if ( rows[ rowIndex ] ) { - node = rows[ rowIndex ].cells[ cellIndex ]; - nodeValue = ts.getElementText( c, node, cellIndex ); - $node = $( node ); - if ( c.debug ) { - console.log( 'Checking if value was empty on row ' + rowIndex + ', column: ' + - cellIndex + ': "' + nodeValue + '"' ); + row = rows[ rowIndex ]; + // stop looking after 50 empty rows + if ( row && rowIndex < 50 ) { + if ( row.className.indexOf( ts.cssIgnoreRow ) < 0 ) { + node = rows[ rowIndex ].cells[ cellIndex ]; + nodeValue = ts.getElementText( c, node, cellIndex ); + $node = $( node ); + if ( c.debug ) { + console.log( 'Checking if value was empty on row ' + rowIndex + ', column: ' + + cellIndex + ': "' + nodeValue + '"' ); + } } } else { keepLooking = false; @@ -1840,6 +1847,8 @@ } if ( c.debug ) { time = new Date(); } ts.addWidgetFromClass( table ); + // prevent "tablesorter-ready" from firing multiple times in a row + clearTimeout( c.timerReady ); if ( c.widgets.length ) { table.isApplyingWidgets = true; // ensure unique widget ids @@ -1909,11 +1918,11 @@ callback( table ); } } - setTimeout( function() { + c.timerReady = setTimeout( function() { table.isApplyingWidgets = false; $.data( table, 'lastWidgetApplication', new Date() ); - c.$table.trigger('tablesorter-ready'); - }, 0 ); + c.$table.trigger( 'tablesorter-ready' ); + }, 10 ); if ( c.debug ) { widget = c.widgets.length; console.log( 'Completed ' + diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 53b74d3..67d2e69 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 11-10-2015 (v2.24.5)*/ +/*! tablesorter (FORK) - updated 11-22-2015 (v2.24.6)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js index 17dfd94..9a9f6e8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js @@ -1,31 +1,30 @@ -/*! Parser: Month - updated 11/2/2015 (v2.24.1) */ +/*! Parser: Month - updated 11/22/2015 (v2.24.6) */ /* Demo: http://jsfiddle.net/Mottie/abkNM/4169/ */ /*jshint jquery:true */ ;(function($){ 'use strict'; var ts = $.tablesorter; - ts.dates = $.extend( {}, { + + if ( !ts.dates ) { ts.dates = {}; } + if ( !ts.dates.months ) { ts.dates.months = {}; } + ts.dates.months.en = { // See http://mottie.github.io/tablesorter/docs/example-widget-grouping.html // for details on how to use CLDR data for a locale to add data for this parser // CLDR returns an object { 1: "Jan", 2: "Feb", 3: "Mar", ..., 12: "Dec" } - months : { - 'en' : { - 1 : 'Jan', - 2 : 'Feb', - 3 : 'Mar', - 4 : 'Apr', - 5 : 'May', - 6 : 'Jun', - 7 : 'Jul', - 8 : 'Aug', - 9 : 'Sep', - 10: 'Oct', - 11: 'Nov', - 12: 'Dec' - } - } - }, ts.dates ); + 1 : 'Jan', + 2 : 'Feb', + 3 : 'Mar', + 4 : 'Apr', + 5 : 'May', + 6 : 'Jun', + 7 : 'Jul', + 8 : 'Aug', + 9 : 'Sep', + 10: 'Oct', + 11: 'Nov', + 12: 'Dec' + }; ts.addParser({ id: 'month', diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-range.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-range.js index dd178be..9c23097 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-range.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-range.js @@ -1,17 +1,25 @@ -/*! Parser: date ranges - updated 2/23/2015 (v2.21.0) */ +/*! Parser: date ranges -updated 11/22/2015 (v2.24.6) */ /* Include the 'widget-filter-type-insideRange.js' to filter ranges */ /*jshint jquery:true */ ;(function($){ 'use strict'; - var regex = { + var ts = $.tablesorter, + getMonthVal, + + regex = { mdy : /(\d{1,2}[-\s]\d{1,2}[-\s]\d{4}(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?)/gi, dmy : /(\d{1,2}[-\s]\d{1,2}[-\s]\d{4}(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?)/gi, dmyreplace : /(\d{1,2})[-\s](\d{1,2})[-\s](\d{4})/, ymd : /(\d{4}[-\s]\d{1,2}[-\s]\d{1,2}(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?)/gi, - ymdreplace : /(\d{4})[-\s](\d{1,2})[-\s](\d{1,2})/ + ymdreplace : /(\d{4})[-\s](\d{1,2})[-\s](\d{1,2})/, + + // extract out date format (dd MMM yyyy hms) e.g. 13 March 2016 12:55 PM + overall_dMMMyyyy : /(\d{1,2}\s+\w+\s+\d{4}(\s+\d{1,2}:\d{2}(:\d{2})?(\s\w+)?)?)/g, + matches_dMMMyyyy : /(\d{1,2})\s+(\w+)\s+(\d{4})/ + }; /*! date-range MMDDYYYY *//* (2/15/2000 - 5/18/2000) */ @@ -87,4 +95,80 @@ type: 'text' }); + if ( !ts.dates ) { ts.dates = {}; } + if ( !ts.dates.months ) { ts.dates.months = {}; } + ts.dates.months.en = { + // See http://mottie.github.io/tablesorter/docs/example-widget-grouping.html + // for details on how to use CLDR data for a locale to add data for this parser + // CLDR returns an object { 1: "Jan", 2: "Feb", 3: "Mar", ..., 12: "Dec" } + 1 : 'Jan', + 2 : 'Feb', + 3 : 'Mar', + 4 : 'Apr', + 5 : 'May', + 6 : 'Jun', + 7 : 'Jul', + 8 : 'Aug', + 9 : 'Sep', + 10: 'Oct', + 11: 'Nov', + 12: 'Dec' + }; + + getMonthVal = function( str, c, cellIndex ) { + var m, month, + // add options to 'config.globalize' for all columns --> globalize : { lang: 'en' } + // or per column by using the column index --> globalize : { 0 : { lang: 'fr' } } + options = c.globalize && ( c.globalize[ cellIndex ] || c.globalize ) || {}, + months = ts.dates.months[ options.lang || 'en' ]; + if ( c.ignoreCase ) { + str = str.toLowerCase(); + } + for ( month in months ) { + if ( typeof month === 'string' ) { + m = months[ month ]; + if ( c.ignoreCase ) { + m = m.toLowerCase(); + } + if ( str.match( m ) ) { + return parseInt( month, 10 ); + } + } + } + return str; + }; + + /*! date-range "dd MMM yyyy - dd MMM yyyy" *//* ( hms optional )*/ + ts.addParser({ + id: 'date-range-dMMMyyyy', + is: function () { + return false; + }, + format: function( text, table, cell, cellIndex ) { + var date, month, matches, i, + parsed = [], + str = text.replace( /\s+/g, ' ' ).match( regex.overall_dMMMyyyy ), + len = str && str.length; + if ( len ) { + for ( i = 0; i < len; i++ ) { + date = ''; + matches = str[ i ].match( regex.matches_dMMMyyyy ); + if ( matches && matches.length >= 4 ) { + matches.shift(); + month = getMonthVal( matches[1], table.config, cellIndex ); + if ( !isNaN( month ) ) { + str[i] = str[i].replace( matches[1], month ); + } + date = new Date( ( '' + str[ i ] ).replace( ts.regex.shortDateXXY, '$3/$2/$1' ) ); + } + parsed.push( date instanceof Date && isFinite(date) ? date.getTime() : str[i] ); + } + // sort from min to max + return parsed.sort().join( ' - ' ); + } + return text; + }, + type: 'text' + }); + })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js index ab08afa..08b8241 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js @@ -1,4 +1,4 @@ -/*! Parser: two digit year - updated 10/26/2014 (v2.18.0) */ +/*! Parser: two digit year - updated 11/22/2015 (v2.24.6) */ /* Demo: http://mottie.github.io/tablesorter/docs/example-parsers-dates.html */ /*jshint jquery:true */ ;(function($){ @@ -13,10 +13,9 @@ ts = $.tablesorter, now = new Date().getFullYear(); - ts.dates = $.extend({}, ts.dates, { - regxxxxyy: /(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{2})/, - regyyxxxx: /(\d{2})[\/\s](\d{1,2})[\/\s](\d{1,2})/ - }); + if ( !ts.dates ) { ts.dates = {}; } + ts.dates.regxxxxyy = /(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{2})/; + ts.dates.regyyxxxx = /(\d{2})[\/\s](\d{1,2})[\/\s](\d{1,2})/; ts.formatDate = function(s, regex, format, table){ if (s) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js index c746df8..734508d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js @@ -1,40 +1,38 @@ -/*! Parser: weekday - updated 11/2/2015 (v2.24.1) */ +/*! Parser: weekday - updated 11/22/2015 (v2.24.6) */ /* Demo: http://jsfiddle.net/Mottie/abkNM/4169/ */ /*jshint jquery:true */ ;(function($){ 'use strict'; var ts = $.tablesorter; - ts.dates = $.extend( true, {}, { - // See http://mottie.github.io/tablesorter/docs/example-widget-grouping.html - // for details on how to use CLDR data for a locale to add data for this parser - // CLDR returns { sun: "Sun", mon: "Mon", tue: "Tue", wed: "Wed", thu: "Thu", ... } - weekdays : { - 'en' : { - 'sun' : 'Sun', - 'mon' : 'Mon', - 'tue' : 'Tue', - 'wed' : 'Wed', - 'thu' : 'Thu', - 'fri' : 'Fri', - 'sat' : 'Sat' - } - }, - - // set table.config.weekStarts to change weekday start date for your locale - // cross-reference of a date on which the week starts on a... - // https://github.com/unicode-cldr/cldr-core/blob/master/supplemental/weekData.json - weekStartList : { - 'sun' : '1995', // Sun 1/1/1995 - 'mon' : '1996', // Mon 1/1/1996 - 'fri' : '1999', // Friday 1/1/1999 - 'sat' : '2000' // Sat 1/1/2000 - }, - - // do not modify this array; it is used for cross referencing - weekdaysXref : [ 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat' ] - }, ts.dates ); + if ( !ts.dates ) { ts.dates = {}; } + if ( !ts.dates.weekdays ) { ts.dates.weekdays = {}; } + // See http://mottie.github.io/tablesorter/docs/example-widget-grouping.html + // for details on how to use CLDR data for a locale to add data for this parser + // CLDR returns { sun: "Sun", mon: "Mon", tue: "Tue", wed: "Wed", thu: "Thu", ... } + ts.dates.weekdays.en = { + 'sun' : 'Sun', + 'mon' : 'Mon', + 'tue' : 'Tue', + 'wed' : 'Wed', + 'thu' : 'Thu', + 'fri' : 'Fri', + 'sat' : 'Sat' + }; + // set table.config.weekStarts to change weekday start date for your locale + // cross-reference of a date on which the week starts on a... + // https://github.com/unicode-cldr/cldr-core/blob/master/supplemental/weekData.json + // locale agnostic + ts.dates.weekStartList = { + 'sun' : '1995', // Sun 1/1/1995 + 'mon' : '1996', // Mon 1/1/1996 + 'fri' : '1999', // Friday 1/1/1999 + 'sat' : '2000' // Sat 1/1/2000 + }; + // do not modify this array; it is used for cross referencing weekdays + // locale agnostic + ts.dates.weekdaysXref = [ 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat' ]; ts.addParser({ id: 'weekday', diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-image.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-image.js index 9157eb0..adba3c4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-image.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-image.js @@ -1,5 +1,6 @@ /*! Parser: image - new 7/17/2014 (v2.17.5) */ /* alt attribute parser for jQuery 1.7+ & tablesorter 2.7.11+ */ +/* NOTE! Moved to jquery.tablesorter.js (core) in v2.18.0 */ /*jshint jquery:true, unused:false */ ;(function($){ 'use strict'; diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index 38621a5..bcddafa 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,4 +1,4 @@ -/*! Parser: input & select - updated 10/31/2015 (v2.24.0) *//* +/*! Parser: input & select - updated 11/22/2015 (v2.24.6) *//* * for jQuery 1.7+ & tablesorter 2.7.11+ * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ @@ -10,7 +10,8 @@ // do something here to update your server, if needed // event = change event object // $table = jQuery object of the table that was just updated - // $input = jQuery object of the input or select that was modified + // $input = jQuery object(s) of the input or select that was modified; in v2.24.6, + // if the thead has a checkbox, $input may include multiple elements }; // Custom parser for parsing input values @@ -125,22 +126,20 @@ // you can change it to use delegate (v1.4.3+) or live (v1.3+) as desired // if this code interferes somehow, target the specific table $('#mytable'), instead of $('table') $( function() { + if ( !$.fn.on ) { return; } $( 'table' ).on( 'tablesorter-initialized updateComplete', function() { - var namespace = '.parser-forms', - restoreValue = function( isTbody ) { + this.tablesorterBusy = false; + var namespace = '.parser-forms'; + // bind to .tablesorter (default class name) + $( this ).children( 'tbody' ) + .off( namespace ) + .on( 'mouseleave' + namespace, function( event ) { // make sure we restore original values (trigger blur) // isTbody is needed to prevent the select from closing in IE // see https://connect.microsoft.com/IE/feedbackdetail/view/962618/ - if ( isTbody ) { + if ( event.target.nodeName === 'TBODY' ) { $( ':focus' ).blur(); } - return; - }; - // bind to .tablesorter (default class name) - $( this ).children( 'tbody' ) - .off( namespace ) - .on( 'mouseleave' + namespace, function( event ) { - restoreValue( event.target.nodeName === 'TBODY' ); }) .on( 'focus' + namespace, 'select, input, textarea', function() { $( this ).data( 'ts-original-value', this.value ); @@ -162,28 +161,92 @@ ( event.target.nodeName === 'INPUT' || event.target.nodeName === 'TEXTAREA' && event.altKey ) ) ) { var undef, $target = $( event.target ), + isCheckbox = event.target.type === 'checkbox', $cell = $target.closest( 'td' ), $table = $cell.closest( 'table' ), indx = $cell[ 0 ].cellIndex, c = $table[ 0 ].config || false, + busy = $table.length && $table[ 0 ].tablesorterBusy, $hdr = c && c.$headerIndexed && c.$headerIndexed[ indx ] || [], - val = $target.val(); - // abort if not a tablesorter table, or don't use updateCell if column is set + val = isCheckbox ? event.target.checked : $target.val(); + // abort if not a tablesorter table, or busy, or don't use updateCell if column is set // to 'sorter-false' and 'filter-false', or column is set to 'parser-false' - if ( $hdr.length && ( $hdr.hasClass( 'parser-false' ) || - ( $hdr.hasClass( 'sorter-false' ) && $hdr.hasClass( 'filter-false' ) ) ) ) { + if ( $.isEmptyObject( c ) || busy !== false || $hdr.length && ( $hdr.hasClass( 'parser-false' ) || + ( $hdr.hasClass( 'sorter-false' ) && $hdr.hasClass( 'filter-false' ) ) ) || + // table already updating; get out of here, we might be in an endless loop (in IE)! See #971 + ( event.type === 'change' && c.table.isUpdating ) ) { return; } // ignore change event if nothing changed - if ( c && val !== $target.data( 'ts-original-value' ) || event.target.type === 'checkbox' ) { + if ( c && val !== $target.data( 'ts-original-value' ) || isCheckbox ) { $target.data( 'ts-original-value', val ); + $table[ 0 ].tablesorterBusy = true; // pass undefined resort value so it falls back to config.resort setting $.tablesorter.updateCell( c, $cell, undef, function() { updateServer( event, $table, $target ); + $table[ 0 ].tablesorterBusy = false; }); } } }); + + // add code for a checkbox in the header to set/unset all checkboxes in a column + if ( $( this ).children( 'thead' ).find( 'input[type="checkbox"]' ) ) { + $( this ) + .off( namespace ) + .on( 'tablesorter-ready' + namespace, function() { + var checkboxClass, $rows, len, + $table = $( this ), + c = $table.length && $table[ 0 ].config; + if ( !$.isEmptyObject( c ) ) { + this.tablesorterBusy = true; + checkboxClass = c && c.checkboxClass || 'checked'; + $rows = $table.children( 'tbody' ).children( ':visible' ); // (include child rows?) + len = $rows.length; + // set indeterminate state on header checkbox + $( this ).children( 'thead' ).find( 'input[type="checkbox"]' ).each( function() { + var column = $( this ).closest( 'td, th' ).attr( 'data-column' ), + vis = $rows.filter( '.' + checkboxClass + '-' + column ).length, + allChecked = vis === len; + if ( vis === 0 || allChecked ) { + this.checked = allChecked; + this.indeterminate = false; + } else { + this.indeterminate = true; + } + }); + this.tablesorterBusy = false; + } + }) + .children( 'thead' ) + .off( namespace ) + // modified from http://jsfiddle.net/abkNM/6163/ + .on( 'change' + namespace, 'input[type="checkbox"]', function( event ) { + var undef, onlyVisible, column, $target, + $checkbox = $( this ), + $table = $checkbox.closest( 'table' ), + c = $table.length && $table[ 0 ].config, + isChecked = this.checked; + if ( $table.length && c && !$table[ 0 ].tablesorterBusy ) { + column = parseInt( $checkbox.closest( 'td, th' ).attr( 'data-column' ), 10 ); + onlyVisible = $table.length && c.checkboxVisible; + $table[ 0 ].tablesorterBusy = true; // prevent "change" event from calling updateCell numerous times (see #971) + $target = $table + .children( 'tbody' ) + .children( 'tr' + ( typeof onlyVisible === 'undefined' || onlyVisible === true ? ':visible' : '' ) ) + .children( ':nth-child(' + ( column + 1 ) + ')' ) + .find( 'input[type="checkbox"]' ) + .prop( 'checked', isChecked ); + $.tablesorter.update( c, undef, function() { + updateServer( event, $table, $target ); + $table[ 0 ].tablesorterBusy = false; + }); + } + // update already going on, don't do anything! + return false; + }); + } + }); }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js index bb39f92..84e556d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js @@ -1,4 +1,4 @@ -/*! Widget: filter, insideRange filter type - updated 2/23/2015 (v2.21.0) */ +/*! Widget: filter, insideRange filter type - updated 11/22/2015 (v2.24.6) */ ;(function($){ 'use strict'; @@ -17,6 +17,8 @@ ts.filter.types.insideRange = function( c, data ) { if ( isDigit.test( data.iFilter ) && range.test( data.iExact ) ) { var t, val, low, high, + index = data.index, + cell = data.$cells[ index ], parts = data.iExact.split( range ), format = c.parsers[data.index].format; // the cell does not contain a range @@ -24,9 +26,9 @@ return null; } // format each side part of the range using the assigned parser - low = parseNumber( format( parts[0], c.table ) ); - high = parseNumber( format( parts[1], c.table ) ); - val = parseNumber( format( data.iFilter, c.table ) ); + low = parseNumber( format( parts[0], c.table, cell, index ) ); + high = parseNumber( format( parts[1], c.table, cell, index ) ); + val = parseNumber( format( data.iFilter, c.table, cell, index ) ); if ( high < low ) { // swap high & low t = high; high = low; low = t; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index da3795c..69cf5c0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -1,4 +1,4 @@ -/*! Widget: math - updated 11/10/2015 (v2.24.4) *//* +/*! Widget: math - updated 11/22/2015 (v2.24.6) *//* * Requires tablesorter v2.16+ and jQuery 1.7+ * by Rob Garrison */ @@ -40,11 +40,17 @@ // get all of the row numerical values in an arry getRow : function( c, $el ) { - var wo = c.widgetOptions, + var $cells, + wo = c.widgetOptions, arry = [], $row = $el.closest( 'tr' ), + isFiltered = $row.hasClass( wo.filter_filteredRow || 'filtered' ), + hasFilter = wo.math_rowFilter; + if ( hasFilter ) { + $row = $row.filter( hasFilter ); + } + if ( !isFiltered || hasFilter ) { $cells = $row.children().not( '[' + wo.math_dataAttrib + '=ignore]' ); - if ( !$row.hasClass( wo.filter_filteredRow || 'filtered' ) ) { if ( wo.math_ignore.length ) { $cells = $cells.not( '[data-column=' + wo.math_ignore.join( '],[data-column=' ) + ']' ); } @@ -57,9 +63,10 @@ // get all of the column numerical values in an arry getColumn : function( c, $el, type ) { - var index, $t, len, $mathRows, mathAbove, + var index, $t, $tr, len, $mathRows, mathAbove, arry = [], wo = c.widgetOptions, + hasFilter = wo.math_rowFilter, mathAttr = wo.math_dataAttrib, filtered = wo.filter_filteredRow || 'filtered', cIndex = parseInt( $el.attr( 'data-column' ), 10 ), @@ -71,11 +78,15 @@ len = $rows.index( $row ); index = len; while ( index >= 0 ) { - $t = $rows.eq( index ).children().filter( '[data-column=' + cIndex + ']' ); + $tr = $rows.eq( index ); + if ( hasFilter ) { + $tr = $tr.filter( wo.math_rowFilter ); + } + $t = $tr.children().filter( '[data-column=' + cIndex + ']' ); mathAbove = $t.filter( '[' + mathAttr + '^=above]' ).length; // ignore filtered rows & rows with data-math="ignore" (and starting row) - if ( ( !$rows.eq( index ).hasClass( filtered ) && - $rows.eq( index ).not( '[' + mathAttr + '=ignore]' ).length && + if ( ( ( !$tr.hasClass( filtered ) || hasFilter ) && + $tr.not( '[' + mathAttr + '=ignore]' ).length && index !== len ) || mathAbove && index !== len ) { // stop calculating 'above', when encountering another 'above' @@ -91,12 +102,16 @@ len = $rows.length; // index + 1 to ignore starting node for ( index = $rows.index( $row ) + 1; index < len; index++ ) { - $t = $rows.eq( index ).children().filter( '[data-column=' + cIndex + ']' ); + $tr = $rows.eq( index ); + if ( hasFilter ) { + $tr = $tr.filter( hasFilter ); + } + $t = $tr.children().filter( '[data-column=' + cIndex + ']' ); if ( $t.filter( '[' + mathAttr + '^=below]' ).length ) { break; } - if ( !$rows.eq( index ).hasClass( filtered ) && - $rows.eq( index ).not( '[' + mathAttr + '=ignore]' ).length && + if ( ( !$tr.hasClass( filtered ) || hasFilter ) && + $tr.not( '[' + mathAttr + '=ignore]' ).length && $t.length ) { arry.push( math.processText( c, $t ) ); } @@ -106,8 +121,12 @@ $mathRows = $rows.not( '[' + mathAttr + '=ignore]' ); len = $mathRows.length; for ( index = 0; index < len; index++ ) { - $t = $mathRows.eq( index ).children().filter( '[data-column=' + cIndex + ']' ); - if ( !$mathRows.eq( index ).hasClass( filtered ) && + $tr = $mathRows.eq( index ); + if ( hasFilter ) { + $tr = $tr.filter( hasFilter ); + } + $t = $tr.children().filter( '[data-column=' + cIndex + ']' ); + if ( ( !$tr.hasClass( filtered ) || hasFilter ) && $t.not( '[' + mathAttr + '^=above],[' + mathAttr + '^=below],[' + mathAttr + '^=col]' ).length && !$t.is( $el ) ) { arry.push( math.processText( c, $t ) ); @@ -124,11 +143,15 @@ wo = c.widgetOptions, mathAttr = wo.math_dataAttrib, filtered = wo.filter_filteredRow || 'filtered', + hasFilter = wo.filter_rowFilter, $rows = c.$table.children( 'tbody' ).children().not( '[' + mathAttr + '=ignore]' ); rowLen = $rows.length; for ( rowIndex = 0; rowIndex < rowLen; rowIndex++ ) { $row = $rows.eq( rowIndex ); - if ( !$row.hasClass( filtered ) ) { + if ( hasFilter ) { + $row = $row.filter( hasFilter ); + } + if ( !$row.hasClass( filtered ) || hasFilter ) { $cells = $row.children().not( '[' + mathAttr + '=ignore]' ); cellLen = $cells.length; // $row.children().each(function(){ @@ -157,7 +180,7 @@ recalculate : function(c, wo, init) { if ( c && ( !wo.math_isUpdating || init ) ) { - var time, mathAttr, $mathCells; + var undef, time, mathAttr, $mathCells; if ( c.debug ) { time = new Date(); } @@ -192,7 +215,9 @@ } // update internal cache - ts.update( c ); + ts.update( c, undef, function(){ + math.updateComplete( c ); + }); if ( c.debug ) { console.log( 'Math widget update completed' + ts.benchmark( time ) ); @@ -200,6 +225,12 @@ } }, + updateComplete : function( c ) { + var wo = c.widgetOptions; + if ( wo.math_isUpdating && c.debug && console.groupEnd ) { console.groupEnd(); } + wo.math_isUpdating = false; + }, + mathType : function( c, $cells, priority ) { if ( $cells.length ) { var formula, result, $el, arry, getAll, $targetCells, index, len, @@ -488,7 +519,9 @@ math_suffix : '', // no matching math elements found (text added to cell) math_none : 'N/A', - math_event : 'recalculate' + math_event : 'recalculate', + // use this filter to target specific rows (e.g. ':visible', or ':not(.empty-row)') + math_rowFilter: '' }, init : function( table, thisWidget, c, wo ) { // filterEnd fires after updateComplete @@ -507,8 +540,7 @@ }) .on( update + '.tsmath', function() { setTimeout( function(){ - if ( wo.math_isUpdating && c.debug && console.groupEnd ) { console.groupEnd(); } - wo.math_isUpdating = false; + math.updateComplete( c ); }, 40 ); }); wo.math_isUpdating = false; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index ee44b10..c0a09b4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 11/10/2015 (v2.24.4) */ +/*! Widget: Pager - updated 11/22/2015 (v2.24.6) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -11,9 +11,10 @@ ts.addWidget({ id: 'pager', priority: 55, // load pager after filter widget - options : { + options: { // output default: '{page}/{totalPages}' - // possible variables: {size}, {page}, {totalPages}, {filteredPages}, {startRow}, {endRow}, {filteredRows} and {totalRows} + // possible variables: {size}, {page}, {totalPages}, {filteredPages}, {startRow}, + // {endRow}, {filteredRows} and {totalRows} pager_output: '{startRow} to {endRow} of {totalRows} rows', // '{page}/{totalPages}' // apply disabled classname to the pager arrows when the rows at either extreme is visible @@ -32,14 +33,15 @@ // Number of options to include in the pager number selector pager_maxOptionSize: 20, - // Save pager page & size if the storage script is loaded (requires $.tablesorter.storage in jquery.tablesorter.widgets.js) + // Save pager page & size if the storage script is loaded (requires $.tablesorter.storage + // in jquery.tablesorter.widgets.js) pager_savePages: true, // defines custom storage key pager_storageKey: 'tablesorter-pager', - // if true, the table will remain the same height no matter how many records are displayed. The space is made up by an empty - // table row set to a height to compensate; default is false + // if true, the table will remain the same height no matter how many records are displayed. + // The space is made up by an empty table row set to a height to compensate; default is false pager_fixedHeight: false, // count child rows towards the set page size? (set true if it is a visible table row within the pager) @@ -48,7 +50,8 @@ pager_countChildRows: false, // remove rows from the table to speed up the sort of large tables. - // setting this to false, only hides the non-visible rows; needed if you plan to add/remove rows with the pager enabled. + // setting this to false, only hides the non-visible rows; needed if you plan to add/remove rows with + // the pager enabled. pager_removeRows: false, // removing rows in larger tables speeds up the sort // use this format: 'http://mydatabase.com?page={page}&size={size}&{sortList:col}&{filterList:fcol}' @@ -60,7 +63,7 @@ pager_ajaxUrl: null, // modify the url after all processing has been applied - pager_customAjaxUrl: function(table, url) { return url; }, + pager_customAjaxUrl: function( table, url ) { return url; }, // ajax error callback from $.tablesorter.showError function // pager_ajaxError: function( config, xhr, settings, exception ){ return exception; }; @@ -88,13 +91,15 @@ // ], // [ "header1", "header2", ... "headerN" ] // optional // ] - pager_ajaxProcessing: function(ajax){ return [ 0, [], null ]; }, + pager_ajaxProcessing: function( ajax ){ return [ 0, [], null ]; }, // css class names of pager arrows pager_css: { container : 'tablesorter-pager', - errorRow : 'tablesorter-errorRow', // error information row (don't include period at beginning) - disabled : 'disabled' // class added to arrows @ extremes (i.e. prev/first arrows 'disabled' on first page) + // error information row (don't include period at beginning) + errorRow : 'tablesorter-errorRow', + // class added to arrows @ extremes (i.e. prev/first arrows 'disabled' on first page) + disabled : 'disabled' }, // jQuery selectors @@ -109,27 +114,27 @@ pageSize : '.pagesize' // page size selector - select dropdown that sets the 'size' option } }, - init: function(table){ - tsp.init(table); + init: function( table ) { + tsp.init( table ); }, // only update to complete sorter initialization - format: function(table, c){ - if (!(c.pager && c.pager.initialized)){ - return tsp.initComplete(table, c); + format: function( table, c ) { + if ( !( c.pager && c.pager.initialized ) ) { + return tsp.initComplete( c ); } - tsp.moveToPage(table, c.pager, false); + tsp.moveToPage( c, c.pager, false ); }, - remove: function(table, c, wo, refreshing){ - tsp.destroyPager(table, c, refreshing); + remove: function( table, c, wo, refreshing ) { + tsp.destroyPager( c, refreshing ); } }); /* pager widget functions */ tsp = ts.pager = { - init: function(table) { + init: function( table ) { // check if tablesorter has initialized - if (table.hasInitialized && table.config.pager && table.config.pager.initialized) { return; } + if ( table.hasInitialized && table.config.pager && table.config.pager.initialized ) { return; } var t, c = table.config, wo = c.widgetOptions, @@ -150,55 +155,60 @@ // save original pager size setSize: wo.pager_size, setPage: wo.pager_startPage - }, c.pager); + }, c.pager ); // pager initializes multiple times before table has completed initialization - if (p.isInitializing) { return; } + if ( p.isInitializing ) { return; } p.isInitializing = true; - if (c.debug) { - console.log('Pager: Initializing'); + if ( c.debug ) { + console.log( 'Pager: Initializing' ); } - p.size = $.data(table, 'pagerLastSize') || wo.pager_size; + p.size = $.data( table, 'pagerLastSize' ) || wo.pager_size; // added in case the pager is reinitialized after being destroyed. - p.$container = $(s.container).addClass(wo.pager_css.container).show(); + p.$container = $( s.container ).addClass( wo.pager_css.container ).show(); // goto selector - p.$goto = p.$container.find(s.gotoPage); // goto is a reserved word #657 + p.$goto = p.$container.find( s.gotoPage ); // goto is a reserved word #657 // page size selector - p.$size = p.$container.find(s.pageSize); - p.totalRows = c.$tbodies.eq(0).children('tr').not( wo.pager_countChildRows ? '' : '.' + c.cssChildRow ).length; + p.$size = p.$container.find( s.pageSize ); + p.totalRows = c.$tbodies.eq( 0 ) + .children( 'tr' ) + .not( wo.pager_countChildRows ? '' : '.' + c.cssChildRow ) + .length; p.oldAjaxSuccess = p.oldAjaxSuccess || wo.pager_ajaxObject.success; c.appender = tsp.appender; p.initializing = true; - if (wo.pager_savePages && ts.storage) { - t = ts.storage(table, wo.pager_storageKey) || {}; // fixes #387 - p.page = ( isNaN(t.page) ? p.page : t.page ) || p.setPage || 0; - p.size = ( isNaN(t.size) ? p.size : t.size ) || p.setSize || 10; - $.data(table, 'pagerLastSize', p.size); + if ( wo.pager_savePages && ts.storage ) { + t = ts.storage( table, wo.pager_storageKey ) || {}; // fixes #387 + p.page = ( isNaN( t.page ) ? p.page : t.page ) || p.setPage || 0; + p.size = ( isNaN( t.size ) ? p.size : t.size ) || p.setSize || 10; + $.data( table, 'pagerLastSize', p.size ); } // skipped rows - p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.slice(1) + '|' + c.cssChildRow + ')'); + p.regexRows = new RegExp( '(' + ( wo.filter_filteredRow || 'filtered' ) + '|' + + c.selectorRemove.slice( 1 ) + '|' + c.cssChildRow + ')' ); // clear initialized flag p.initialized = false; // before initialization event - c.$table.trigger('pagerBeforeInitialized', c); + c.$table.trigger( 'pagerBeforeInitialized', c ); - tsp.enablePager(table, c, false); + tsp.enablePager( c, false ); // p must have ajaxObject - p.ajaxObject = wo.pager_ajaxObject; // $.extend({}, wo.pager_ajaxObject ); + p.ajaxObject = wo.pager_ajaxObject; p.ajaxObject.url = wo.pager_ajaxUrl; if ( typeof wo.pager_ajaxUrl === 'string' ) { // ajax pager; interact with database p.ajax = true; - // When filtering with ajax, allow only custom filtering function, disable default filtering since it will be done server side. + // When filtering with ajax, allow only custom filtering function, disable default filtering + // since it will be done server side. wo.filter_serversideFiltering = true; c.serverSideSorting = true; - tsp.moveToPage(table, p); + tsp.moveToPage( c, p ); } else { p.ajax = false; // Regular pager; all rows stored in memory @@ -207,30 +217,30 @@ }, - initComplete: function(table, c){ + initComplete: function( c ) { var p = c.pager; - tsp.bindEvents(table, c); - tsp.setPageSize(c, 0); // page size 0 is ignored - if (!p.ajax) { - tsp.hideRowsSetup(table, c); + tsp.bindEvents( c ); + tsp.setPageSize( c, 0 ); // page size 0 is ignored + if ( !p.ajax ) { + tsp.hideRowsSetup( c ); } // pager initialized p.initialized = true; p.initializing = false; p.isInitializing = false; - if (c.debug) { - console.log('Pager: Triggering pagerInitialized'); + if ( c.debug ) { + console.log( 'Pager: Triggering pagerInitialized' ); } c.$table.trigger( 'pagerInitialized', c ); // filter widget not initialized; it will update the output display & fire off the pagerComplete event - if ( !( c.widgetOptions.filter_initialized && ts.hasWidget(table, 'filter') ) ) { + if ( !( c.widgetOptions.filter_initialized && ts.hasWidget( c.table, 'filter' ) ) ) { // if ajax, then don't fire off pagerComplete - tsp.updatePageDisplay(table, c, !p.ajax); + tsp.updatePageDisplay( c, !p.ajax ); } }, - bindEvents: function(table, c){ + bindEvents: function( c ) { var ctrls, fxn, p = c.pager, wo = c.widgetOptions, @@ -239,106 +249,108 @@ c.$table .off( namespace ) - .on('filterInit filterStart '.split(' ').join(namespace + ' '), function(e, filters) { - p.currentFilters = $.isArray(filters) ? filters : c.$table.data('lastSearch'); + .on( 'filterInit filterStart '.split( ' ' ).join( namespace + ' ' ), function( e, filters ) { + p.currentFilters = $.isArray( filters ) ? filters : c.$table.data( 'lastSearch' ); // don't change page if filters are the same (pager updating, etc) - if (e.type === 'filterStart' && wo.pager_pageReset !== false && (c.lastCombinedFilter || '') !== (p.currentFilters || []).join('')) { + if ( e.type === 'filterStart' && wo.pager_pageReset !== false && + ( c.lastCombinedFilter || '' ) !== ( p.currentFilters || [] ).join( '' ) ) { p.page = wo.pager_pageReset; // fixes #456 & #565 } }) // update pager after filter widget completes - .on('filterEnd sortEnd '.split(' ').join(namespace + ' '), function() { - p.currentFilters = c.$table.data('lastSearch'); - if (p.initialized || p.initializing) { - if (c.delayInit && c.rowsCopy && c.rowsCopy.length === 0) { + .on( 'filterEnd sortEnd '.split( ' ' ).join( namespace + ' ' ), function() { + p.currentFilters = c.$table.data( 'lastSearch' ); + if ( p.initialized || p.initializing ) { + if ( c.delayInit && c.rowsCopy && c.rowsCopy.length === 0 ) { // make sure we have a copy of all table rows once the cache has been built - tsp.updateCache(table); + tsp.updateCache( c ); } - tsp.updatePageDisplay(table, c, false); - // tsp.moveToPage(table, p, false); <-- called when applyWidgets is triggered - ts.applyWidget( table ); + tsp.updatePageDisplay( c, false ); + // tsp.moveToPage( c, p, false ); <-- called when applyWidgets is triggered + ts.applyWidget( c.table ); } }) - .on('disablePager' + namespace, function(e){ + .on( 'disablePager' + namespace, function( e ) { e.stopPropagation(); - tsp.showAllRows(table, c); + tsp.showAllRows( c ); }) - .on('enablePager' + namespace, function(e){ + .on( 'enablePager' + namespace, function( e ) { e.stopPropagation(); - tsp.enablePager(table, c, true); + tsp.enablePager( c, true ); }) - .on('destroyPager' + namespace, function(e, refreshing){ + .on( 'destroyPager' + namespace, function( e, refreshing ) { e.stopPropagation(); // call removeWidget to make sure internal flags are modified. - ts.removeWidget( table, 'pager', false ); + ts.removeWidget( c.table, 'pager', false ); }) - .on('updateComplete' + namespace, function(e, table, triggered){ + .on( 'updateComplete' + namespace, function( e, table, triggered ) { e.stopPropagation(); // table can be unintentionally undefined in tablesorter v2.17.7 and earlier // don't recalculate total rows/pages if using ajax - if (!table || triggered || p.ajax) { return; } - var $rows = c.$tbodies.eq(0).children('tr').not(c.selectorRemove); - p.totalRows = $rows.length - ( wo.pager_countChildRows ? 0 : $rows.filter('.' + c.cssChildRow).length ); + if ( !table || triggered || p.ajax ) { return; } + var $rows = c.$tbodies.eq( 0 ).children( 'tr' ).not( c.selectorRemove ); + p.totalRows = $rows.length - + ( wo.pager_countChildRows ? 0 : $rows.filter( '.' + c.cssChildRow ).length ); p.totalPages = Math.ceil( p.totalRows / p.size ); - if ($rows.length && c.rowsCopy && c.rowsCopy.length === 0) { + if ( $rows.length && c.rowsCopy && c.rowsCopy.length === 0 ) { // make a copy of all table rows once the cache has been built - tsp.updateCache(table); + tsp.updateCache( c ); } if ( p.page >= p.totalPages ) { - tsp.moveToLastPage(table, p); + tsp.moveToLastPage( c, p ); } - tsp.hideRows(table, c); - tsp.changeHeight(table, c); + tsp.hideRows( c ); + tsp.changeHeight( c ); // update without triggering pagerComplete - tsp.updatePageDisplay(table, c, false); + tsp.updatePageDisplay( c, false ); // make sure widgets are applied - fixes #450 ts.applyWidget( table ); - tsp.updatePageDisplay(table, c); + tsp.updatePageDisplay( c ); }) - .on('pageSize refreshComplete '.split(' ').join(namespace + ' '), function(e, size){ + .on( 'pageSize refreshComplete '.split( ' ' ).join( namespace + ' ' ), function( e, size ) { e.stopPropagation(); - tsp.setPageSize(c, tsp.parsePageSize( c, size, 'get' )); - tsp.hideRows(table, c); - tsp.updatePageDisplay(table, c, false); + tsp.setPageSize( c, tsp.parsePageSize( c, size, 'get' ) ); + tsp.hideRows( c ); + tsp.updatePageDisplay( c, false ); }) - .on('pageSet pagerUpdate '.split(' ').join(namespace + ' '), function(e, num){ + .on( 'pageSet pagerUpdate '.split( ' ' ).join( namespace + ' ' ), function( e, num ) { e.stopPropagation(); // force pager refresh - if (e.type === 'pagerUpdate') { + if ( e.type === 'pagerUpdate' ) { num = typeof num === 'undefined' ? p.page + 1 : num; p.last.page = true; } - p.page = (parseInt(num, 10) || 1) - 1; - tsp.moveToPage(table, p, true); - tsp.updatePageDisplay(table, c, false); + p.page = ( parseInt( num, 10 ) || 1 ) - 1; + tsp.moveToPage( c, p, true ); + tsp.updatePageDisplay( c, false ); }) - .on('pageAndSize' + namespace, function(e, page, size){ + .on( 'pageAndSize' + namespace, function( e, page, size ) { e.stopPropagation(); - p.page = (parseInt(page, 10) || 1) - 1; - tsp.setPageSize(c, tsp.parsePageSize( c, size, 'get' )); - tsp.moveToPage(table, p, true); - tsp.hideRows(table, c); - tsp.updatePageDisplay(table, c, false); + p.page = ( parseInt(page, 10) || 1 ) - 1; + tsp.setPageSize( c, tsp.parsePageSize( c, size, 'get' ) ); + tsp.moveToPage( c, p, true ); + tsp.hideRows( c ); + tsp.updatePageDisplay( c, false ); }); // clicked controls ctrls = [ s.first, s.prev, s.next, s.last ]; fxn = [ 'moveToFirstPage', 'moveToPrevPage', 'moveToNextPage', 'moveToLastPage' ]; - if (c.debug && !p.$container.length) { - console.warn('Pager: >> Container not found'); + if ( c.debug && !p.$container.length ) { + console.warn( 'Pager: >> Container not found' ); } - p.$container.find(ctrls.join(',')) - .attr('tabindex', 0) - .off('click' + namespace) - .on('click' + namespace, function(e){ + p.$container.find( ctrls.join( ',' ) ) + .attr( 'tabindex', 0 ) + .off( 'click' + namespace ) + .on( 'click' + namespace, function( e ) { e.stopPropagation(); var i, - $c = $(this), + $c = $( this ), l = ctrls.length; - if ( !$c.hasClass(wo.pager_css.disabled) ) { - for (i = 0; i < l; i++) { - if ($c.is(ctrls[i])) { - tsp[fxn[i]](table, p); + if ( !$c.hasClass( wo.pager_css.disabled ) ) { + for ( i = 0; i < l; i++ ) { + if ( $c.is( ctrls[ i ] ) ) { + tsp[ fxn[ i ] ]( c, p ); break; } } @@ -347,145 +359,165 @@ if ( p.$goto.length ) { p.$goto - .off('change' + namespace) - .on('change' + namespace, function(){ - p.page = $(this).val() - 1; - tsp.moveToPage(table, p, true); - tsp.updatePageDisplay(table, c, false); + .off( 'change' + namespace ) + .on( 'change' + namespace, function() { + p.page = $( this ).val() - 1; + tsp.moveToPage( c, p, true ); + tsp.updatePageDisplay( c, false ); }); - } else if (c.debug) { - console.warn('Pager: >> Goto selector not found'); + } else if ( c.debug ) { + console.warn( 'Pager: >> Goto selector not found' ); } if ( p.$size.length ) { // setting an option as selected appears to cause issues with initial page size - p.$size.find('option').removeAttr('selected'); + p.$size.find( 'option' ).removeAttr( 'selected' ); p.$size - .off('change' + namespace) - .on('change' + namespace, function() { - if ( !$(this).hasClass(wo.pager_css.disabled) ) { - var size = $(this).val(); + .off( 'change' + namespace ) + .on( 'change' + namespace, function() { + if ( !$( this ).hasClass( wo.pager_css.disabled ) ) { + var size = $( this ).val(); p.$size.val( size ); // in case there are more than one pagers - tsp.setPageSize(c, size); - tsp.changeHeight(table, c); + tsp.setPageSize( c, size ); + tsp.changeHeight( c ); } return false; }); - } else if (c.debug) { + } else if ( c.debug ) { console.warn('Pager: >> Size selector not found'); } }, // hide arrows at extremes - pagerArrows: function(c, disable) { + pagerArrows: function( c, disable ) { var p = c.pager, dis = !!disable, first = dis || p.page === 0, - tp = Math.min( p.totalPages, p.filteredPages ), + tp = tsp.getTotalPages( c, p ), last = dis || p.page === tp - 1 || tp === 0, wo = c.widgetOptions, s = wo.pager_selectors; if ( wo.pager_updateArrows ) { - p.$container.find(s.first + ',' + s.prev).toggleClass(wo.pager_css.disabled, first).attr('aria-disabled', first); - p.$container.find(s.next + ',' + s.last).toggleClass(wo.pager_css.disabled, last).attr('aria-disabled', last); + p.$container + .find( s.first + ',' + s.prev ) + .toggleClass( wo.pager_css.disabled, first ) + .attr( 'aria-disabled', first ); + p.$container + .find( s.next + ',' + s.last ) + .toggleClass( wo.pager_css.disabled, last ) + .attr( 'aria-disabled', last ); } }, - calcFilters: function(table, c) { + calcFilters: function( c ) { var normalized, indx, len, wo = c.widgetOptions, p = c.pager, - hasFilters = c.$table.hasClass('hasFilters'); - if (hasFilters && !wo.pager_ajaxUrl) { - if ($.isEmptyObject(c.cache)) { + hasFilters = c.$table.hasClass( 'hasFilters' ); + if ( hasFilters && !wo.pager_ajaxUrl ) { + if ( $.isEmptyObject( c.cache ) ) { // delayInit: true so nothing is in the cache - p.filteredRows = p.totalRows = c.$tbodies.eq(0).children('tr').not( wo.pager_countChildRows ? '' : '.' + c.cssChildRow ).length; + p.filteredRows = p.totalRows = c.$tbodies.eq( 0 ) + .children( 'tr' ) + .not( wo.pager_countChildRows ? '' : '.' + c.cssChildRow ) + .length; } else { p.filteredRows = 0; - normalized = c.cache[0].normalized; + normalized = c.cache[ 0 ].normalized; len = normalized.length; - for (indx = 0; indx < len; indx++) { - p.filteredRows += p.regexRows.test(normalized[indx][c.columns].$row[0].className) ? 0 : 1; + for ( indx = 0; indx < len; indx++ ) { + p.filteredRows += p.regexRows.test( normalized[ indx ][ c.columns ].$row[ 0 ].className ) ? 0 : 1; } } - } else if (!hasFilters) { + } else if ( !hasFilters ) { p.filteredRows = p.totalRows; } }, - updatePageDisplay: function(table, c, completed) { + updatePageDisplay: function( c, completed ) { if ( c.pager.initializing ) { return; } var s, t, $out, options, indx, len, + table = c.table, wo = c.widgetOptions, p = c.pager, namespace = c.namespace + 'pager', sz = tsp.parsePageSize( c, p.size, 'get' ); // don't allow dividing by zero - if (wo.pager_countChildRows) { t.push(c.cssChildRow); } - p.$size.add(p.$goto).removeClass(wo.pager_css.disabled).removeAttr('disabled').attr('aria-disabled', 'false'); + if ( wo.pager_countChildRows ) { t.push( c.cssChildRow ); } + p.$size + .add( p.$goto ) + .removeClass( wo.pager_css.disabled ) + .removeAttr( 'disabled' ) + .attr( 'aria-disabled', 'false' ); p.totalPages = Math.ceil( p.totalRows / sz ); // needed for 'pageSize' method c.totalRows = p.totalRows; - tsp.parsePageNumber( p ); - tsp.calcFilters(table, c); + tsp.parsePageNumber( c, p ); + tsp.calcFilters( c ); c.filteredRows = p.filteredRows; p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; - if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) { - t = (p.size * p.page > p.filteredRows) && completed; - p.page = (t) ? wo.pager_pageReset || 0 : p.page; - p.startRow = (t) ? p.size * p.page + 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1); + if ( tsp.getTotalPages( c, p ) >= 0 ) { + t = ( p.size * p.page > p.filteredRows ) && completed; + p.page = t ? wo.pager_pageReset || 0 : p.page; + p.startRow = t ? p.size * p.page + 1 : ( p.filteredRows === 0 ? 0 : p.size * p.page + 1 ); p.endRow = Math.min( p.filteredRows, p.totalRows, p.size * ( p.page + 1 ) ); - $out = p.$container.find(wo.pager_selectors.pageDisplay); + $out = p.$container.find( wo.pager_selectors.pageDisplay ); // form the output string (can now get a new output string from the server) s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || wo.pager_output : wo.pager_output ) // {page} = one-based index; {page+#} = zero based index +/- value - .replace(/\{page([\-+]\d+)?\}/gi, function(m, n){ - return p.totalPages ? p.page + (n ? parseInt(n, 10) : 1) : 0; + .replace( /\{page([\-+]\d+)?\}/gi, function( m, n ) { + return p.totalPages ? p.page + ( n ? parseInt( n, 10 ) : 1 ) : 0; }) // {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object) - .replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m){ + .replace( /\{\w+(\s*:\s*\w+)?\}/gi, function( m ) { var len, indx, - str = m.replace(/[{}\s]/g, ''), - extra = str.split(':'), + str = m.replace( /[{}\s]/g, '' ), + extra = str.split( ':' ), data = p.ajaxData, // return zero for default page/row numbers - deflt = /(rows?|pages?)$/i.test(str) ? 0 : ''; - if (/(startRow|page)/.test(extra[0]) && extra[1] === 'input') { - len = ('' + (extra[0] === 'page' ? p.totalPages : p.totalRows)).length; - indx = extra[0] === 'page' ? p.page + 1 : p.startRow; - return '<input type="text" class="ts-' + extra[0] + '" style="max-width:' + len + 'em" value="' + indx + '"/>'; + deflt = /(rows?|pages?)$/i.test( str ) ? 0 : ''; + if ( /(startRow|page)/.test( extra[ 0 ] ) && extra[ 1 ] === 'input' ) { + len = ( '' + ( extra[ 0 ] === 'page' ? p.totalPages : p.totalRows ) ).length; + indx = extra[ 0 ] === 'page' ? p.page + 1 : p.startRow; + return '<input type="text" class="ts-' + extra[ 0 ] + + '" style="max-width:' + len + 'em" value="' + indx + '"/>'; } - return extra.length > 1 && data && data[extra[0]] ? data[extra[0]][extra[1]] : p[str] || (data ? data[str] : deflt) || deflt; + return extra.length > 1 && data && data[ extra[ 0 ] ] ? + data[ extra[ 0 ] ][ extra[ 1 ] ] : + p[ str ] || ( data ? data[ str ] : deflt ) || deflt; }); if ( p.$goto.length ) { t = ''; - options = tsp.buildPageSelect(p, c); + options = tsp.buildPageSelect( c, p ); len = options.length; - for (indx = 0; indx < len; indx++) { - t += '<option value="' + options[indx] + '">' + options[indx] + '</option>'; + for ( indx = 0; indx < len; indx++ ) { + t += '<option value="' + options[ indx ] + '">' + options[ indx ] + '</option>'; } // innerHTML doesn't work in IE9 - http://support2.microsoft.com/kb/276228 - p.$goto.html(t).val( p.page + 1 ); + p.$goto.html( t ).val( p.page + 1 ); } - if ($out.length) { - $out[ ($out[0].nodeName === 'INPUT') ? 'val' : 'html' ](s); + if ( $out.length ) { + $out[ ($out[ 0 ].nodeName === 'INPUT' ) ? 'val' : 'html' ]( s ); // rebind startRow/page inputs - $out.find('.ts-startRow, .ts-page').off('change' + namespace).on('change' + namespace, function(){ - var v = $(this).val(), - pg = $(this).hasClass('ts-startRow') ? Math.floor( v / p.size ) + 1 : v; - c.$table.trigger('pageSet' + namespace, [ pg ]); - }); + $out + .find( '.ts-startRow, .ts-page' ) + .off( 'change' + namespace ) + .on( 'change' + namespace, function() { + var v = $( this ).val(), + pg = $( this ).hasClass( 'ts-startRow' ) ? Math.floor( v / p.size ) + 1 : v; + c.$table.trigger( 'pageSet' + namespace, [ pg ] ); + }); } } - tsp.pagerArrows(c); - tsp.fixHeight(table, c); - if (p.initialized && completed !== false) { - if (c.debug) { - console.log('Pager: Triggering pagerComplete'); + tsp.pagerArrows( c ); + tsp.fixHeight( c ); + if ( p.initialized && completed !== false ) { + if ( c.debug ) { + console.log( 'Pager: Triggering pagerComplete' ); } - c.$table.trigger('pagerComplete', c); + c.$table.trigger( 'pagerComplete', c ); // save pager info to storage - if (wo.pager_savePages && ts.storage) { - ts.storage(table, wo.pager_storageKey, { + if ( wo.pager_savePages && ts.storage ) { + ts.storage( table, wo.pager_storageKey, { page : p.page, size : p.size }); @@ -493,100 +525,107 @@ } }, - buildPageSelect: function(p, c) { + buildPageSelect: function( c, p ) { // Filter the options page number link array if it's larger than 'pager_maxOptionSize' // as large page set links will slow the browser on large dom inserts - var i, central_focus_size, focus_option_pages, insert_index, option_length, focus_length, + var i, centralFocusSize, focusOptionPages, insertIndex, optionLength, focusLength, wo = c.widgetOptions, - pg = Math.min( p.totalPages, p.filteredPages ) || 1, + pg = tsp.getTotalPages( c, p ) || 1, // make skip set size multiples of 5 - skip_set_size = Math.ceil( ( pg / wo.pager_maxOptionSize ) / 5 ) * 5, - large_collection = pg > wo.pager_maxOptionSize, - current_page = p.page + 1, - start_page = skip_set_size, - end_page = pg - skip_set_size, - option_pages = [ 1 ], + skipSetSize = Math.ceil( ( pg / wo.pager_maxOptionSize ) / 5 ) * 5, + largeCollection = pg > wo.pager_maxOptionSize, + currentPage = p.page + 1, + startPage = skipSetSize, + endPage = pg - skipSetSize, + optionPages = [ 1 ], // construct default options pages array - option_pages_start_page = (large_collection) ? skip_set_size : 1; + optionPagesStartPage = largeCollection ? skipSetSize : 1; - for ( i = option_pages_start_page; i <= pg; ) { - option_pages.push(i); - i = i + ( large_collection ? skip_set_size : 1 ); + for ( i = optionPagesStartPage; i <= pg; ) { + optionPages.push( i ); + i = i + ( largeCollection ? skipSetSize : 1 ); } - option_pages.push(pg); + optionPages.push( pg ); - if (large_collection) { - focus_option_pages = []; + if ( largeCollection ) { + focusOptionPages = []; // don't allow central focus size to be > 5 on either side of current page - central_focus_size = Math.max( Math.floor( wo.pager_maxOptionSize / skip_set_size ) - 1, 5 ); + centralFocusSize = Math.max( Math.floor( wo.pager_maxOptionSize / skipSetSize ) - 1, 5 ); - start_page = current_page - central_focus_size; - if (start_page < 1) { start_page = 1; } - end_page = current_page + central_focus_size; - if (end_page > pg) { end_page = pg; } + startPage = currentPage - centralFocusSize; + if ( startPage < 1 ) { startPage = 1; } + endPage = currentPage + centralFocusSize; + if ( endPage > pg ) { endPage = pg; } // construct an array to get a focus set around the current page - for (i = start_page; i <= end_page ; i++) { - focus_option_pages.push(i); + for ( i = startPage; i <= endPage ; i++ ) { + focusOptionPages.push( i ); } // keep unique values - option_pages = $.grep(option_pages, function(value, indx) { - return $.inArray(value, option_pages) === indx; + optionPages = $.grep( optionPages, function( value, indx ) { + return $.inArray( value, optionPages ) === indx; }); - option_length = option_pages.length; - focus_length = focus_option_pages.length; + optionLength = optionPages.length; + focusLength = focusOptionPages.length; - // make sure at all option_pages aren't replaced - if (option_length - focus_length > skip_set_size / 2 && option_length + focus_length > wo.pager_maxOptionSize ) { - insert_index = Math.floor(option_length / 2) - Math.floor(focus_length / 2); - Array.prototype.splice.apply(option_pages, [ insert_index, focus_length ]); + // make sure at all optionPages aren't replaced + if ( optionLength - focusLength > skipSetSize / 2 && optionLength + focusLength > wo.pager_maxOptionSize ) { + insertIndex = Math.floor( optionLength / 2 ) - Math.floor( focusLength / 2 ); + Array.prototype.splice.apply( optionPages, [ insertIndex, focusLength ] ); } - option_pages = option_pages.concat(focus_option_pages); + optionPages = optionPages.concat( focusOptionPages ); } // keep unique values again - option_pages = $.grep(option_pages, function(value, indx) { - return $.inArray(value, option_pages) === indx; + optionPages = $.grep( optionPages, function( value, indx ) { + return $.inArray( value, optionPages ) === indx; }) - .sort(function(a, b) { return a - b; }); + .sort( function( a, b ) { + return a - b; + }); - return option_pages; + return optionPages; }, - fixHeight: function(table, c) { + fixHeight: function( c ) { var d, h, + table = c.table, p = c.pager, wo = c.widgetOptions, - $b = c.$tbodies.eq(0); - $b.find('tr.pagerSavedHeightSpacer').remove(); - if (wo.pager_fixedHeight && !p.isDisabled) { - h = $.data(table, 'pagerSavedHeight'); - if (h) { + $b = c.$tbodies.eq( 0 ); + $b.find( 'tr.pagerSavedHeightSpacer' ).remove(); + if ( wo.pager_fixedHeight && !p.isDisabled ) { + h = $.data( table, 'pagerSavedHeight' ); + if ( h ) { d = h - $b.height(); - if ( d > 5 && $.data(table, 'pagerLastSize') === p.size && $b.children('tr:visible').length < p.size ) { - $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice(1) + '" style="height:' + d + 'px;"></tr>'); + if ( d > 5 && $.data( table, 'pagerLastSize' ) === p.size && $b.children( 'tr:visible' ).length < p.size ) { + $b.append( '<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice( 1 ) + + '" style="height:' + d + 'px;"></tr>' ); } } } }, - changeHeight: function(table, c) { - var h, $b = c.$tbodies.eq(0); - $b.find('tr.pagerSavedHeightSpacer').remove(); - if (!$b.children('tr:visible').length) { - $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice(1) + '"><td> </td></tr>'); + changeHeight: function( c ) { + var h, + table = c.table, + $b = c.$tbodies.eq( 0 ); + $b.find( 'tr.pagerSavedHeightSpacer' ).remove(); + if ( !$b.children( 'tr:visible' ).length ) { + $b.append( '<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice( 1 ) + '"><td> </td></tr>' ); } - h = $b.children('tr').eq(0).height() * c.pager.size; - $.data(table, 'pagerSavedHeight', h); - tsp.fixHeight(table, c); - $.data(table, 'pagerLastSize', c.pager.size); + h = $b.children( 'tr' ).eq( 0 ).height() * c.pager.size; + $.data( table, 'pagerSavedHeight', h ); + tsp.fixHeight( c ); + $.data( table, 'pagerLastSize', c.pager.size ); }, - hideRows: function(table, c){ + hideRows: function( c ) { if ( !c.widgetOptions.pager_ajaxUrl ) { var tbodyIndex, rowIndex, $rows, len, lastIndex, + table = c.table, p = c.pager, wo = c.widgetOptions, tbodyLen = c.$tbodies.length, @@ -633,236 +672,239 @@ } }, - hideRowsSetup: function(table, c){ + hideRowsSetup: function( c ) { var p = c.pager, namespace = c.namespace + 'pager', size = p.$size.val(); p.size = tsp.parsePageSize( c, size, 'get' ); p.$size.val( tsp.parsePageSize( c, p.size, 'set' ) ); - $.data(table, 'pagerLastSize', p.size); - tsp.pagerArrows(c); + $.data( c.table, 'pagerLastSize', p.size ); + tsp.pagerArrows( c ); if ( !c.widgetOptions.pager_removeRows ) { - tsp.hideRows(table, c); - c.$table.on('sortEnd filterEnd '.split(' ').join(namespace + ' '), function(){ - tsp.hideRows(table, c); + tsp.hideRows( c ); + c.$table.on( 'sortEnd filterEnd '.split( ' ' ).join( namespace + ' ' ), function() { + tsp.hideRows( c ); }); } }, - renderAjax: function(data, table, c, xhr, settings, exception) { - var p = c.pager, + renderAjax: function( data, c, xhr, settings, exception ) { + var table = c.table, + p = c.pager, wo = c.widgetOptions; // process data - if ( $.isFunction(wo.pager_ajaxProcessing) ) { + if ( $.isFunction( wo.pager_ajaxProcessing ) ) { // in case nothing is returned by ajax, empty out the table; see #1032 // but do it before calling pager_ajaxProcessing because that function may add content // directly to the table - c.$tbodies.eq(0).empty(); + c.$tbodies.eq( 0 ).empty(); // ajaxProcessing result: [ total, rows, headers ] var i, j, t, hsh, $f, $sh, $headers, $h, icon, th, d, l, rr_count, len, $table = c.$table, tds = '', - result = wo.pager_ajaxProcessing(data, table, xhr) || [ 0, [] ], - hl = $table.find('thead th').length; + result = wo.pager_ajaxProcessing( data, table, xhr ) || [ 0, [] ], + hl = $table.find( 'thead th' ).length; // Clean up any previous error. ts.showError( table ); if ( exception ) { - if (c.debug) { - console.error('Pager: >> Ajax Error', xhr, settings, exception); + if ( c.debug ) { + console.error( 'Pager: >> Ajax Error', xhr, settings, exception ); } ts.showError( table, xhr, settings, exception ); - c.$tbodies.eq(0).children('tr').detach(); + c.$tbodies.eq( 0 ).children( 'tr' ).detach(); p.totalRows = 0; } else { // process ajax object - if (!$.isArray(result)) { + if ( !$.isArray( result ) ) { p.ajaxData = result; c.totalRows = p.totalRows = result.total; - c.filteredRows = p.filteredRows = typeof result.filteredRows !== 'undefined' ? result.filteredRows : result.total; + c.filteredRows = p.filteredRows = typeof result.filteredRows !== 'undefined' ? + result.filteredRows : + result.total; th = result.headers; d = result.rows || []; } else { // allow [ total, rows, headers ] or [ rows, total, headers ] - t = isNaN(result[0]) && !isNaN(result[1]); + t = isNaN( result[ 0 ] ) && !isNaN( result[ 1 ] ); // ensure a zero returned row count doesn't fail the logical || - rr_count = result[t ? 1 : 0]; - p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count; + rr_count = result[ t ? 1 : 0 ]; + p.totalRows = isNaN( rr_count ) ? p.totalRows || 0 : rr_count; // can't set filtered rows when returning an array c.totalRows = c.filteredRows = p.filteredRows = p.totalRows; // set row data to empty array if nothing found - see http://stackoverflow.com/q/30875583/145346 - d = p.totalRows === 0 ? [] : result[t ? 0 : 1] || []; // row data - th = result[2]; // headers + d = p.totalRows === 0 ? [] : result[ t ? 0 : 1 ] || []; // row data + th = result[ 2 ]; // headers } l = d && d.length; - if (d instanceof jQuery) { - if (wo.pager_processAjaxOnInit) { + if ( d instanceof jQuery ) { + if ( wo.pager_processAjaxOnInit ) { // append jQuery object - c.$tbodies.eq(0).empty(); - c.$tbodies.eq(0).append(d); + c.$tbodies.eq( 0 ).empty(); + c.$tbodies.eq( 0 ).append( d ); } - } else if (l) { + } else if ( l ) { // build table from array for ( i = 0; i < l; i++ ) { tds += '<tr>'; for ( j = 0; j < d[i].length; j++ ) { // build tbody cells; watch for data containing HTML markup - see #434 - tds += /^\s*<td/.test(d[i][j]) ? $.trim(d[i][j]) : '<td>' + d[i][j] + '</td>'; + tds += /^\s*<td/.test( d[ i ][ j ] ) ? $.trim( d[ i ][ j ] ) : '<td>' + d[ i ][ j ] + '</td>'; } tds += '</tr>'; } // add rows to first tbody - if (wo.pager_processAjaxOnInit) { - c.$tbodies.eq(0).html( tds ); + if ( wo.pager_processAjaxOnInit ) { + c.$tbodies.eq( 0 ).html( tds ); } } wo.pager_processAjaxOnInit = true; // only add new header text if the length matches if ( th && th.length === hl ) { - hsh = $table.hasClass('hasStickyHeaders'); - $sh = hsh ? wo.$sticky.children('thead:first').children('tr').children() : ''; - $f = $table.find('tfoot tr:first').children(); + hsh = $table.hasClass( 'hasStickyHeaders' ); + $sh = hsh ? wo.$sticky.children( 'thead:first' ).children( 'tr' ).children() : ''; + $f = $table.find( 'tfoot tr:first' ).children(); // don't change td headers (may contain pager) - $headers = c.$headers.filter( 'th '); + $headers = c.$headers.filter( 'th' ); len = $headers.length; for ( j = 0; j < len; j++ ) { $h = $headers.eq( j ); // add new test within the first span it finds, or just in the header - if ( $h.find('.' + ts.css.icon).length ) { - icon = $h.find('.' + ts.css.icon).clone(true); - $h.find('.tablesorter-header-inner').html( th[j] ).append(icon); + if ( $h.find( '.' + ts.css.icon ).length ) { + icon = $h.find( '.' + ts.css.icon ).clone( true ); + $h.find( '.tablesorter-header-inner' ).html( th[ j ] ).append( icon ); if ( hsh && $sh.length ) { - icon = $sh.eq(j).find('.' + ts.css.icon).clone(true); - $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ).append(icon); + icon = $sh.eq( j ).find( '.' + ts.css.icon ).clone( true ); + $sh.eq( j ).find( '.tablesorter-header-inner' ).html( th[ j ] ).append( icon ); } } else { - $h.find('.tablesorter-header-inner').html( th[j] ); - if (hsh && $sh.length) { - $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ); + $h.find( '.tablesorter-header-inner' ).html( th[ j ] ); + if ( hsh && $sh.length ) { + $sh.eq( j ).find( '.tablesorter-header-inner' ).html( th[ j ] ); } } - $f.eq(j).html( th[j] ); + $f.eq( j ).html( th[ j ] ); } } } - if (c.showProcessing) { - ts.isProcessing(table); // remove loading icon + if ( c.showProcessing ) { + ts.isProcessing( table ); // remove loading icon } // make sure last pager settings are saved, prevents multiple server side calls with // the same parameters p.totalPages = Math.ceil( p.totalRows / tsp.parsePageSize( c, p.size, 'get' ) ); p.last.totalRows = p.totalRows; p.last.currentFilters = p.currentFilters; - p.last.sortList = (c.sortList || []).join(','); + p.last.sortList = ( c.sortList || [] ).join( ',' ); p.initializing = false; // update display without triggering pager complete... before updating cache - tsp.updatePageDisplay(table, c, false); + tsp.updatePageDisplay( c, false ); // tablesorter core updateCache (not pager) - ts.updateCache( c, function(){ - if (p.initialized) { + ts.updateCache( c, function() { + if ( p.initialized ) { // apply widgets after table has rendered & after a delay to prevent // multiple applyWidget blocking code from blocking this trigger - setTimeout(function(){ - if (c.debug) { - console.log('Pager: Triggering pagerChange'); + setTimeout( function() { + if ( c.debug ) { + console.log( 'Pager: Triggering pagerChange' ); } $table.trigger( 'pagerChange', p ); ts.applyWidget( table ); - tsp.updatePageDisplay(table, c); - }, 0); + tsp.updatePageDisplay( c ); + }, 0 ); } }); } - if (!p.initialized) { + if ( !p.initialized ) { ts.applyWidget( table ); } }, - getAjax: function(table, c){ + getAjax: function( c ) { var counter, - url = tsp.getAjaxUrl(table, c), - $doc = $(document), + url = tsp.getAjaxUrl( c ), + $doc = $( document ), namespace = c.namespace + 'pager', p = c.pager; if ( url !== '' ) { - if (c.showProcessing) { - ts.isProcessing(table, true); // show loading icon + if ( c.showProcessing ) { + ts.isProcessing( c.table, true ); // show loading icon } - $doc.on('ajaxError' + namespace, function(e, xhr, settings, exception) { - tsp.renderAjax(null, table, c, xhr, settings, exception); - $doc.off('ajaxError' + namespace); + $doc.on( 'ajaxError' + namespace, function( e, xhr, settings, exception ) { + tsp.renderAjax( null, c, xhr, settings, exception ); + $doc.off( 'ajaxError' + namespace ); }); counter = ++p.ajaxCounter; p.last.ajaxUrl = url; // remember processed url p.ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl - p.ajaxObject.success = function(data, status, jqxhr) { + p.ajaxObject.success = function( data, status, jqxhr ) { // Refuse to process old ajax commands that were overwritten by new ones - see #443 - if (counter < p.ajaxCounter){ + if ( counter < p.ajaxCounter ) { return; } - tsp.renderAjax(data, table, c, jqxhr); - $doc.off('ajaxError' + namespace); - if (typeof p.oldAjaxSuccess === 'function') { - p.oldAjaxSuccess(data); + tsp.renderAjax( data, c, jqxhr ); + $doc.off( 'ajaxError' + namespace ); + if ( typeof p.oldAjaxSuccess === 'function' ) { + p.oldAjaxSuccess( data ); } }; - if (c.debug) { - console.log('Pager: Ajax initialized', p.ajaxObject); + if ( c.debug ) { + console.log( 'Pager: Ajax initialized', p.ajaxObject ); } - $.ajax(p.ajaxObject); + $.ajax( p.ajaxObject ); } }, - getAjaxUrl: function(table, c) { + getAjaxUrl: function( c ) { var indx, len, p = c.pager, wo = c.widgetOptions, - url = (wo.pager_ajaxUrl) ? wo.pager_ajaxUrl + url = wo.pager_ajaxUrl ? wo.pager_ajaxUrl // allow using '{page+1}' in the url string to switch to a non-zero based index - .replace(/\{page([\-+]\d+)?\}/, function(s, n){ return p.page + (n ? parseInt(n, 10) : 0); }) - .replace(/\{size\}/g, p.size) : '', + .replace( /\{page([\-+]\d+)?\}/, function( s, n ) { return p.page + ( n ? parseInt( n, 10 ) : 0 ); }) + .replace( /\{size\}/g, p.size ) : '', sortList = c.sortList, - filterList = p.currentFilters || $(table).data('lastSearch') || [], - sortCol = url.match(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/), - filterCol = url.match(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/), + filterList = p.currentFilters || c.$table.data( 'lastSearch' ) || [], + sortCol = url.match( /\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/ ), + filterCol = url.match( /\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/ ), arry = []; - if (sortCol) { - sortCol = sortCol[1]; + if ( sortCol ) { + sortCol = sortCol[ 1 ]; len = sortList.length; - for (indx = 0; indx < len; indx++) { - arry.push(sortCol + '[' + sortList[indx][0] + ']=' + sortList[indx][1]); + for ( indx = 0; indx < len; indx++ ) { + arry.push( sortCol + '[' + sortList[ indx ][ 0 ] + ']=' + sortList[ indx ][ 1 ] ); } // if the arry is empty, just add the col parameter... '&{sortList:col}' becomes '&col' - url = url.replace(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : sortCol ); + url = url.replace( /\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join( '&' ) : sortCol ); arry = []; } - if (filterCol) { - filterCol = filterCol[1]; + if ( filterCol ) { + filterCol = filterCol[ 1 ]; len = filterList.length; - for (indx = 0; indx < len; indx++) { - if (filterList[indx]) { - arry.push(filterCol + '[' + indx + ']=' + encodeURIComponent(filterList[indx])); + for ( indx = 0; indx < len; indx++ ) { + if ( filterList[ indx ] ) { + arry.push( filterCol + '[' + indx + ']=' + encodeURIComponent( filterList[ indx ] ) ); } } // if the arry is empty, just add the fcol parameter... '&{filterList:fcol}' becomes '&fcol' - url = url.replace(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : filterCol ); + url = url.replace( /\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join( '&' ) : filterCol ); p.currentFilters = filterList; } - if ( $.isFunction(wo.pager_customAjaxUrl) ) { - url = wo.pager_customAjaxUrl(table, url); + if ( $.isFunction( wo.pager_customAjaxUrl ) ) { + url = wo.pager_customAjaxUrl( c.table, url ); } - if (c.debug) { - console.log('Pager: Ajax url = ' + url); + if ( c.debug ) { + console.log( 'Pager: Ajax url = ' + url ); } return url; }, - renderTable: function(table, rows) { + renderTable: function( c, rows ) { var $tb, index, count, added, - c = table.config, + table = c.table, p = c.pager, wo = c.widgetOptions, f = c.$table.hasClass('hasFilters'), @@ -870,81 +912,83 @@ s = ( p.page * p.size ), e = p.size; if ( l < 1 ) { - if (c.debug) { - console.warn('Pager: >> No rows for pager to render'); + if ( c.debug ) { + console.warn( 'Pager: >> No rows for pager to render' ); } // empty table, abort! return; } if ( p.page >= p.totalPages ) { // lets not render the table more than once - return tsp.moveToLastPage(table, p); + return tsp.moveToLastPage( c, p ); } p.cacheIndex = []; p.isDisabled = false; // needed because sorting will change the page and re-enable the pager - if (p.initialized) { - if (c.debug) { - console.log('Pager: Triggering pagerChange'); + if ( p.initialized ) { + if ( c.debug ) { + console.log( 'Pager: Triggering pagerChange' ); } c.$table.trigger( 'pagerChange', c ); } if ( !wo.pager_removeRows ) { - tsp.hideRows(table, c); + tsp.hideRows( c ); } else { - ts.clearTableBody(table); - $tb = ts.processTbody(table, c.$tbodies.eq(0), true); + ts.clearTableBody( table ); + $tb = ts.processTbody( table, c.$tbodies.eq(0), true ); // not filtered, start from the calculated starting point (s) // if filtered, start from zero index = f ? 0 : s; count = f ? 0 : s; added = 0; - while (added < e && index < rows.length) { - if (!f || !/filtered/.test(rows[index][0].className)){ + while ( added < e && index < rows.length ) { + if ( !f || !/filtered/.test( rows[ index ][ 0 ].className ) ) { count++; - if (count > s && added <= e) { + if ( count > s && added <= e ) { added++; - p.cacheIndex.push(index); - $tb.append(rows[index]); + p.cacheIndex.push( index ); + $tb.append( rows[ index ] ); } } index++; } - ts.processTbody(table, $tb, false); + ts.processTbody( table, $tb, false ); } - tsp.updatePageDisplay(table, c); + tsp.updatePageDisplay( c ); wo.pager_startPage = p.page; wo.pager_size = p.size; - if (table.isUpdating) { - if (c.debug) { - console.log('Pager: Triggering updateComplete'); + if ( table.isUpdating ) { + if ( c.debug ) { + console.log( 'Pager: Triggering updateComplete' ); } - c.$table.trigger('updateComplete', [ table, true ]); + c.$table.trigger( 'updateComplete', [ table, true ] ); } }, - showAllRows: function(table, c){ + showAllRows: function( c ) { var index, $controls, len, + table = c.table, p = c.pager, wo = c.widgetOptions; if ( p.ajax ) { - tsp.pagerArrows(c, true); + tsp.pagerArrows( c, true ); } else { - $.data(table, 'pagerLastPage', p.page); - $.data(table, 'pagerLastSize', p.size); + $.data( table, 'pagerLastPage', p.page ); + $.data( table, 'pagerLastSize', p.size ); p.page = 0; p.size = p.totalRows; p.totalPages = 1; c.$table - .addClass('pagerDisabled') - .removeAttr('aria-describedby') - .find('tr.pagerSavedHeightSpacer').remove(); - tsp.renderTable(table, c.rowsCopy); + .addClass( 'pagerDisabled' ) + .removeAttr( 'aria-describedby' ) + .find( 'tr.pagerSavedHeightSpacer' ) + .remove(); + tsp.renderTable( c, c.rowsCopy ); p.isDisabled = true; ts.applyWidget( table ); - if (c.debug) { - console.log('Pager: Disabled'); + if ( c.debug ) { + console.log( 'Pager: Disabled' ); } } // disable size selector @@ -955,96 +999,99 @@ for ( index = 0; index < len; index++ ) { $controls.eq( index ) .attr( 'aria-disabled', 'true' ) - .addClass( wo.pager_css.disabled )[0].disabled = true; + .addClass( wo.pager_css.disabled )[ 0 ].disabled = true; } }, // updateCache if delayInit: true // this is normally done by 'appendToTable' function in the tablesorter core AFTER a sort - updateCache: function(table) { - var c = table.config, - p = c.pager; + updateCache: function( c ) { + var p = c.pager; // tablesorter core updateCache (not pager) - ts.updateCache( c, function(){ - if ( !$.isEmptyObject(table.config.cache) ) { - var i, + ts.updateCache( c, function() { + if ( !$.isEmptyObject( c.cache ) ) { + var index, rows = [], - n = table.config.cache[0].normalized; - p.totalRows = n.length; - for (i = 0; i < p.totalRows; i++) { - rows.push(n[i][c.columns].$row); + normalized = c.cache[ 0 ].normalized; + p.totalRows = normalized.length; + for ( index = 0; index < p.totalRows; index++ ) { + rows.push( normalized[ index ][ c.columns ].$row ); } c.rowsCopy = rows; - tsp.moveToPage(table, p, true); + tsp.moveToPage( c, p, true ); // clear out last search to force an update p.last.currentFilters = [ ' ' ]; } }); }, - moveToPage: function(table, p, pageMoved) { + moveToPage: function( c, p, pageMoved ) { if ( p.isDisabled ) { return; } - if ( pageMoved !== false && p.initialized && $.isEmptyObject(table.config.cache)) { - return tsp.updateCache(table); + if ( pageMoved !== false && p.initialized && $.isEmptyObject( c.cache ) ) { + return tsp.updateCache( c ); } - var c = table.config, + var table = c.table, wo = c.widgetOptions, l = p.last; // abort page move if the table has filters and has not been initialized - if (p.ajax && !wo.filter_initialized && ts.hasWidget(table, 'filter')) { return; } + if ( p.ajax && !wo.filter_initialized && ts.hasWidget( table, 'filter' ) ) { return; } - tsp.parsePageNumber( p ); - tsp.calcFilters(table, c); + tsp.parsePageNumber( c, p ); + tsp.calcFilters( c ); // fixes issue where one current filter is [] and the other is [ '', '', '' ], // making the next if comparison think the filters as different. Fixes #202. - l.currentFilters = (l.currentFilters || []).join('') === '' ? [] : l.currentFilters; - p.currentFilters = (p.currentFilters || []).join('') === '' ? [] : p.currentFilters; + l.currentFilters = ( l.currentFilters || [] ).join( '' ) === '' ? [] : l.currentFilters; + p.currentFilters = ( p.currentFilters || [] ).join( '' ) === '' ? [] : p.currentFilters; // don't allow rendering multiple times on the same page/size/totalRows/filters/sorts if ( l.page === p.page && l.size === p.size && l.totalRows === p.totalRows && - (l.currentFilters || []).join(',') === (p.currentFilters || []).join(',') && + ( l.currentFilters || [] ).join( ',' ) === ( p.currentFilters || [] ).join( ',' ) && // check for ajax url changes see #730 - (l.ajaxUrl || '') === (p.ajaxObject.url || '') && + ( l.ajaxUrl || '' ) === ( p.ajaxObject.url || '' ) && // & ajax url option changes (dynamically add/remove/rename sort & filter parameters) - (l.optAjaxUrl || '') === (wo.pager_ajaxUrl || '') && - l.sortList === (c.sortList || []).join(',') ) { + ( l.optAjaxUrl || '' ) === ( wo.pager_ajaxUrl || '' ) && + l.sortList === ( c.sortList || [] ).join( ',' ) ) { return; } - if (c.debug) { - console.log('Pager: Changing to page ' + p.page); + if ( c.debug ) { + console.log( 'Pager: Changing to page ' + p.page ); } p.last = { - page : p.page, - size : p.size, + page: p.page, + size: p.size, // fixes #408; modify sortList otherwise it auto-updates - sortList : (c.sortList || []).join(','), - totalRows : p.totalRows, - currentFilters : p.currentFilters || [], - ajaxUrl : p.ajaxObject.url || '', - optAjaxUrl : wo.pager_ajaxUrl + sortList: ( c.sortList || [] ).join( ',' ), + totalRows: p.totalRows, + currentFilters: p.currentFilters || [], + ajaxUrl: p.ajaxObject.url || '', + optAjaxUrl: wo.pager_ajaxUrl }; - if (p.ajax) { - tsp.getAjax(table, c); - } else if (!p.ajax) { - tsp.renderTable(table, c.rowsCopy); + if ( p.ajax ) { + tsp.getAjax( c ); + } else if ( !p.ajax ) { + tsp.renderTable( c, c.rowsCopy ); } - $.data(table, 'pagerLastPage', p.page); - if (p.initialized && pageMoved !== false) { - if (c.debug) { - console.log('Pager: Triggering pageMoved'); + $.data( table, 'pagerLastPage', p.page ); + if ( p.initialized && pageMoved !== false ) { + if ( c.debug ) { + console.log( 'Pager: Triggering pageMoved' ); } - c.$table.trigger('pageMoved', c); + c.$table.trigger( 'pageMoved', c ); ts.applyWidget( table ); - if (!p.ajax && table.isUpdating) { - if (c.debug) { - console.log('Pager: Triggering updateComplete'); + if ( !p.ajax && table.isUpdating ) { + if ( c.debug ) { + console.log( 'Pager: Triggering updateComplete' ); } - c.$table.trigger('updateComplete', [ table, true ]); + c.$table.trigger( 'updateComplete', [ table, true ] ); } } }, + getTotalPages: function( c, p ) { + return ts.hasWidget( c.table, 'filter' ) ? Math.min( p.totalPages, p.filteredPages ) : p.totalPages; + }, + // set to either set or get value parsePageSize: function( c, size, mode ) { var p = c.pager, @@ -1057,54 +1104,56 @@ ( mode === 'get' ? s : p.size ); }, - parsePageNumber: function( p ) { - var min = Math.min( p.totalPages, p.filteredPages ) - 1; + parsePageNumber: function( c, p ) { + var min = tsp.getTotalPages( c, p ) - 1; p.page = parseInt( p.page, 10 ); if ( p.page < 0 || isNaN( p.page ) ) { p.page = 0; } - if ( p.page > min && p.page !== 0 ) { p.page = min; } + if ( p.page > min && min >= 0 ) { p.page = min; } return p.page; }, - setPageSize: function(c, size) { + setPageSize: function( c, size ) { var p = c.pager, table = c.table; p.size = tsp.parsePageSize( c, size, 'get' ); p.$size.val( tsp.parsePageSize( c, p.size, 'set' ) ); - $.data(table, 'pagerLastPage', tsp.parsePageNumber( p ) ); - $.data(table, 'pagerLastSize', p.size); + $.data( table, 'pagerLastPage', tsp.parsePageNumber( c, p ) ); + $.data( table, 'pagerLastSize', p.size ); p.totalPages = Math.ceil( p.totalRows / p.size ); p.filteredPages = Math.ceil( p.filteredRows / p.size ); - tsp.moveToPage(table, p, true); + tsp.moveToPage( c, p, true ); }, - moveToFirstPage: function(table, p) { + moveToFirstPage: function( c, p ) { p.page = 0; - tsp.moveToPage(table, p, true); + tsp.moveToPage( c, p, true ); }, - moveToLastPage: function(table, p) { - p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 ); - tsp.moveToPage(table, p, true); + moveToLastPage: function( c, p ) { + p.page = tsp.getTotalPages( c, p ) - 1; + tsp.moveToPage( c, p, true ); }, - moveToNextPage: function(table, p) { + moveToNextPage: function( c, p ) { p.page++; - if ( p.page >= ( Math.min( p.totalPages, p.filteredPages ) - 1 ) ) { - p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 ); + var last = tsp.getTotalPages( c, p ) - 1; + if ( p.page >= last ) { + p.page = last; } - tsp.moveToPage(table, p, true); + tsp.moveToPage( c, p, true ); }, - moveToPrevPage: function(table, p) { + moveToPrevPage: function( c, p ) { p.page--; if ( p.page <= 0 ) { p.page = 0; } - tsp.moveToPage(table, p, true); + tsp.moveToPage( c, p, true ); }, - destroyPager: function(table, c, refreshing){ - var p = c.pager, + destroyPager: function( c, refreshing ) { + var table = c.table, + p = c.pager, s = c.widgetOptions.pager_selectors, ctrls = [ s.first, s.prev, s.next, s.last, s.gotoPage, s.pageSize ].join( ',' ), namespace = c.namespace + 'pager'; @@ -1117,57 +1166,58 @@ .find( ctrls ) .off( namespace ); if ( refreshing ) { return; } - tsp.showAllRows(table, c); + tsp.showAllRows( c ); c.appender = null; // remove pager appender function - if (ts.storage) { - ts.storage(table, c.widgetOptions.pager_storageKey, ''); + if ( ts.storage ) { + ts.storage( table, c.widgetOptions.pager_storageKey, '' ); } delete table.config.pager; delete table.config.rowsCopy; }, - enablePager: function(table, c, triggered){ + enablePager: function( c, triggered ) { var info, size, + table = c.table, p = c.pager; p.isDisabled = false; - p.page = $.data(table, 'pagerLastPage') || p.page || 0; - size = p.$size.find('option[selected]').val(); - p.size = $.data(table, 'pagerLastSize') || tsp.parsePageSize( c, size, 'get' ); + p.page = $.data( table, 'pagerLastPage' ) || p.page || 0; + size = p.$size.find( 'option[selected]' ).val(); + p.size = $.data( table, 'pagerLastSize' ) || tsp.parsePageSize( c, size, 'get' ); p.$size.val( tsp.parsePageSize( c, p.size, 'set' ) ); // set page size - p.totalPages = Math.ceil( Math.min( p.totalRows, p.filteredRows ) / p.size ); - c.$table.removeClass('pagerDisabled'); + p.totalPages = Math.ceil( tsp.getTotalPages( c, p ) / p.size ); + c.$table.removeClass( 'pagerDisabled' ); // if table id exists, include page display with aria info if ( table.id ) { info = table.id + '_pager_info'; - p.$container.find(c.widgetOptions.pager_selectors.pageDisplay).attr('id', info); - c.$table.attr('aria-describedby', info); + p.$container.find( c.widgetOptions.pager_selectors.pageDisplay ).attr( 'id', info ); + c.$table.attr( 'aria-describedby', info ); } - tsp.changeHeight(table, c); + tsp.changeHeight( c ); if ( triggered ) { // tablesorter core update table ts.update( c ); - tsp.setPageSize(c, p.size); - tsp.hideRowsSetup(table, c); - if (c.debug) { - console.log('Pager: Enabled'); + tsp.setPageSize( c, p.size ); + tsp.hideRowsSetup( c ); + if ( c.debug ) { + console.log( 'Pager: Enabled' ); } } }, - appender: function(table, rows) { + appender: function( table, rows ) { var c = table.config, wo = c.widgetOptions, p = c.pager; if ( !p.ajax ) { c.rowsCopy = rows; - p.totalRows = wo.pager_countChildRows ? c.$tbodies.eq(0).children('tr').length : rows.length; - p.size = $.data(table, 'pagerLastSize') || p.size || wo.pager_size || p.setSize || 10; + p.totalRows = wo.pager_countChildRows ? c.$tbodies.eq( 0 ).children( 'tr' ).length : rows.length; + p.size = $.data( table, 'pagerLastSize' ) || p.size || wo.pager_size || p.setSize || 10; p.totalPages = Math.ceil( p.totalRows / p.size ); - tsp.moveToPage(table, p); + tsp.moveToPage( c, p ); // update display here in case all rows are removed - tsp.updatePageDisplay(table, c, false); + tsp.updatePageDisplay( c, false ); } else { - tsp.moveToPage(table, p, true); + tsp.moveToPage( c, p, true ); } } @@ -1177,7 +1227,7 @@ ts.showError = function( table, xhr, settings, exception ) { var $row, $table = $( table ), - c = $table[0].config, + c = $table[ 0 ].config, wo = c && c.widgetOptions, errorRow = c.pager && c.pager.cssErrorRow || wo && wo.pager_css && wo.pager_css.errorRow || @@ -1185,12 +1235,12 @@ typ = typeof xhr, valid = true, message = '', - removeRow = function(){ + removeRow = function() { c.$table.find( 'thead' ).find( '.' + errorRow ).remove(); }; if ( !$table.length ) { - console.error('tablesorter showError: no table parameter passed'); + console.error( 'tablesorter showError: no table parameter passed' ); return; } @@ -1231,16 +1281,18 @@ } // allow message to include entire row HTML! - $row = ( /tr\>/.test(message) ? $(message) : $('<tr><td colspan="' + c.columns + '">' + message + '</td></tr>') ) + $row = ( /tr\>/.test( message ) ? + $( message ) : + $( '<tr><td colspan="' + c.columns + '">' + message + '</td></tr>' ) ) .click( function() { $( this ).remove(); }) // add error row to thead instead of tbody, or clicking on the header will result in a parser error .appendTo( c.$table.find( 'thead:first' ) ) - .addClass( errorRow + ' ' + c.selectorRemove.slice(1) ) + .addClass( errorRow + ' ' + c.selectorRemove.slice( 1 ) ) .attr({ - role : 'alert', - 'aria-live' : 'assertive' + role: 'alert', + 'aria-live': 'assertive' }); }; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js index 5bb2795..a3d6785 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js @@ -1,4 +1,4 @@ -/* Widget: print - updated 11/2/2015 (v2.24.1) *//* +/* Widget: print - updated 11/22/2015 (v2.24.6) *//* * Requires tablesorter v2.8+ and jQuery 1.2.6+ */ /*jshint browser:true, jquery:true, unused:false */ @@ -33,7 +33,9 @@ // hide filtered rows ', .' + ( wo.filter_filteredRow || 'filtered' ) + ' { display: none; }' + // hide sort arrows - '.' + ( ts.css.header || 'tablesorter-header' ) + ' { background-image: none !important; }'; + '.' + ( ts.css.header || 'tablesorter-header' ) + ' { background-image: none !important; }' + + + '@media print { .print_widget_hidden { display: none; } }'; // replace content with data-attribute content $table.find('[' + wo.print_dataAttrib + ']').each(function(){ @@ -81,21 +83,27 @@ printOutput : function(c, data, style) { var wo = c.widgetOptions, + lang = ts.language, generator = window.open( '', wo.print_title, printTable.popupStyle ), - t = wo.print_title || c.$table.find('caption').text() || c.$table[0].id || document.title || 'table'; + t = wo.print_title || c.$table.find('caption').text() || c.$table[0].id || document.title || 'table', + button = wo.print_now ? '' : '<div class="print_widget_hidden"><a href="javascript:window.print();">' + + '<button type="button">' + lang.button_print + '</button></a> <a href="javascript:window.close();">' + + '<button type="button">' + lang.button_close + '</button></a><hr></div>'; generator.document.write( '<html><head><title>' + t + '</title>' + ( wo.print_styleSheet ? '<link rel="stylesheet" href="' + wo.print_styleSheet + '">' : '' ) + '<style>' + style + '</style>' + - '</head><body>' + data + '</body></html>' + '</head><body>' + button + data + '</body></html>' ); generator.document.close(); // use timeout to allow browser to build DOM before printing // Print preview in Chrome doesn't work without this code - setTimeout( function() { - generator.print(); - generator.close(); - }, 10 ); + if ( wo.print_now ) { + setTimeout( function() { + generator.print(); + generator.close(); + }, 10 ); + } return true; }, @@ -105,6 +113,9 @@ }; + ts.language.button_close = 'Close'; + ts.language.button_print = 'Print'; + ts.addWidget({ id: 'print', options: { @@ -114,6 +125,7 @@ print_columns : 'selected', // (a)ll, (v)isbible or (s)elected (if columnSelector widget is added) print_extraCSS : '', // add any extra css definitions for the popup window here print_styleSheet : '', // add the url of your print stylesheet + print_now : true, // Open the print dialog immediately if true // callback executed when processing completes // to continue printing, use the following function: // function( config, $table, printStyle ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js index d7cc09f..a2295ce 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js @@ -1,4 +1,4 @@ -/*! tablesorter tbody sorting widget (BETA) - 10/31/2015 (v2.24.0) +/*! tablesorter tbody sorting widget (BETA) - 11/22/2015 (v2.24.6) * Requires tablesorter v2.22.2+ and jQuery 1.4+ * by Rob Garrison */ @@ -124,7 +124,7 @@ // fall back to built-in numeric sort // var sort = $.tablesorter['sort' + s](a, b, dir, colMax, table); sort = c.numberSorter ? c.numberSorter( colA, colB, dir, colMax, table ) : - ts[ 'sortNumeric' + ( dir ? 'Asc' : 'Desc' ) ]( colA, colB, num, colMax, col, table ); + ts[ 'sortNumeric' + ( dir ? 'Asc' : 'Desc' ) ]( colA, colB, num, colMax, col, c ); } else { // set a & b depending on sort direction x = dir ? colA : colB; From 54d7241a9c86799edf379853291e49c4486ccad7 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 14 Dec 2015 17:51:57 +0100 Subject: [PATCH 075/138] Update tablesorter to latest version (2.25.0) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 4 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 20 +- .../jquery.tablesorter.combined.js | 318 ++++++++++++------ .../jquery-tablesorter/jquery.tablesorter.js | 225 +++++++++---- .../jquery.tablesorter.widgets.js | 93 +++-- .../parsers/parser-input-select.js | 94 ++++-- .../widgets/widget-alignChar.js | 2 +- .../widgets/widget-build-table.js | 2 +- .../widgets/widget-columnSelector.js | 4 +- .../widgets/widget-editable.js | 27 +- .../widgets/widget-filter-type-insideRange.js | 11 +- .../widgets/widget-filter.js | 81 +++-- .../widgets/widget-grouping.js | 2 +- .../jquery-tablesorter/widgets/widget-math.js | 230 ++++++++----- .../widgets/widget-pager.js | 18 +- .../widgets/widget-print.js | 4 +- .../widgets/widget-resizable.js | 6 +- .../widgets/widget-scroller.js | 2 +- .../widgets/widget-sort2Hash.js | 4 +- .../widgets/widget-staticRow.js | 2 +- .../widgets/widget-stickyHeaders.js | 4 +- .../jquery-tablesorter/widgets/widget-view.js | 4 +- 25 files changed, 755 insertions(+), 410 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ca9904..1f295fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.20.0 (2015-12-14) + +* Upgrade tablesorter to v2.25.0 + #### v1.19.4 (2015-11-23) * Upgrade tablesorter to v2.24.6 diff --git a/README.md b/README.md index 8695c82..3a08b63 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.24.6 (11/22/2015), [documentation] +Current tablesorter version: 2.25.0 (12/13/2015), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index e5b41c1..bfabedb 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 - MINOR = 19 - TINY = 4 + MINOR = 20 + TINY = 0 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 9b02a23..03db8ee 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 9b02a23890c5c7dec7362c1b4eebf1a13fec4264 +Subproject commit 03db8eec0e10fb6879d1d1bd283c055932fdc495 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 020df44..60ac596 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -217,7 +217,7 @@ $out.find('.ts-startRow, .ts-page').unbind('change' + namespace).bind('change' + namespace, function(){ var v = $(this).val(), pg = $(this).hasClass('ts-startRow') ? Math.floor( v / p.size ) + 1 : v; - c.$table.trigger('pageSet' + namespace, [ pg ]); + c.$table.triggerHandler('pageSet' + namespace, [ pg ]); }); } } @@ -227,7 +227,7 @@ if (c.debug) { console.log('Pager: Triggering pagerComplete'); } - c.$table.trigger('pagerComplete', p); + c.$table.triggerHandler('pagerComplete', p); // save pager info to storage if (p.savePages && ts.storage) { ts.storage(table, p.storageKey, { @@ -499,7 +499,7 @@ if (c.debug) { console.log('Pager: Triggering pagerChange'); } - $table.trigger( 'pagerChange', p ); + $table.triggerHandler( 'pagerChange', p ); ts.applyWidget( table ); updatePageDisplay(table, p, true); }, 0); @@ -513,7 +513,7 @@ if (table.config.debug) { console.log('Pager: Triggering pagerInitialized'); } - $(table).trigger( 'pagerInitialized', p ); + $(table).triggerHandler( 'pagerInitialized', p ); ts.applyWidget( table ); updatePageDisplay(table, p); } @@ -624,7 +624,7 @@ if (c.debug) { console.log('Pager: Triggering pagerChange'); } - $t.trigger( 'pagerChange', p ); + $t.triggerHandler( 'pagerChange', p ); } if ( !p.removeRows ) { hideRows(table, p); @@ -654,7 +654,7 @@ if (c.debug) { console.log('Pager: Triggering updateComplete'); } - $t.trigger('updateComplete', [ table, true ]); + $t.triggerHandler('updateComplete', [ table, true ]); } }, @@ -756,13 +756,13 @@ if (c.debug) { console.log('Pager: Triggering pageMoved'); } - $t.trigger('pageMoved', p); + $t.triggerHandler('pageMoved', p); ts.applyWidget( table ); if (table.isUpdating) { if (c.debug) { console.log('Pager: Triggering updateComplete'); } - $t.trigger('updateComplete', [ table, true ]); + $t.triggerHandler('updateComplete', [ table, true ]); } } }, @@ -1059,7 +1059,7 @@ // clear initialized flag p.initialized = false; // before initialization event - $t.trigger('pagerBeforeInitialized', p); + $t.triggerHandler('pagerBeforeInitialized', p); enablePager(table, p, false); if ( typeof p.ajaxUrl === 'string' ) { @@ -1085,7 +1085,7 @@ if (c.debug) { console.log('Pager: Triggering pagerInitialized'); } - c.$table.trigger( 'pagerInitialized', p ); + c.$table.triggerHandler( 'pagerInitialized', p ); if ( !( c.widgetOptions.filter_initialized && ts.hasWidget(table, 'filter') ) ) { updatePageDisplay(table, p, false); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index f85005d..375be35 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 11-22-2015 (v2.24.6)*/ +/*! tablesorter (FORK) - updated 12-13-2015 (v2.25.0)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.24.6 *//* +/*! TableSorter (FORK) v2.25.0 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -39,7 +39,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.24.6', + version : '2.25.0', parsers : [], widgets : [], @@ -80,6 +80,7 @@ emptyTo : 'bottom', // sort empty cell to bottom, top, none, zero, emptyMax, emptyMin stringTo : 'max', // sort strings in numerical column as max, min, top, bottom, zero + duplicateSpan : true, // colspan cells in the tbody will have duplicated content in the cache for each spanned column textExtraction : 'basic', // text extraction method/function - function( node, table, cellIndex ){} textAttribute : 'data-text',// data-attribute that contains alternate cell text (used in default textExtraction function) textSorter : null, // choose overall or specific column sorter function( a, b, direction, table, columnIndex ) [alt: ts.sortText] @@ -227,7 +228,7 @@ if ( table.hasInitialized ) { console.warn( 'Stopping initialization. Tablesorter has already been initialized' ); } else { - console.error( 'Stopping initialization! No table, thead or tbody' ); + console.error( 'Stopping initialization! No table, thead or tbody', table ); } } return; @@ -350,7 +351,7 @@ console.log( 'Overall initialization time: ' + ts.benchmark( $.data( table, 'startoveralltimer' ) ) ); if ( c.debug && console.groupEnd ) { console.groupEnd(); } } - $table.trigger( 'tablesorter-initialized', table ); + $table.triggerHandler( 'tablesorter-initialized', table ); if ( typeof c.initialized === 'function' ) { c.initialized( table ); } @@ -412,7 +413,7 @@ }) .bind( 'applyWidgetId' + namespace, function( e, id ) { e.stopPropagation(); - ts.getWidgetById( id ).format( this, this.config, this.config.widgetOptions ); + ts.applyWidgetId( this, id ); }) .bind( 'applyWidgets' + namespace, function( e, init ) { e.stopPropagation(); @@ -423,6 +424,10 @@ e.stopPropagation(); ts.refreshWidgets( this, all, dontapply ); }) + .bind( 'removeWidget' + namespace, function( e, name, refreshing ) { + e.stopPropagation(); + ts.removeWidget( this, name, refreshing ); + }) .bind( 'destroy' + namespace, function( e, removeClasses, callback ) { e.stopPropagation(); ts.destroy( this, removeClasses, callback ); @@ -535,6 +540,7 @@ timer = new Date(); } // children tr in tfoot - see issue #196 & #547 + // don't pass table.config to computeColumnIndex here - widgets (math) pass it to "quickly" index tbody cells c.columns = ts.computeColumnIndex( c.$table.children( 'thead, tfoot' ).children( 'tr' ) ); // add icon if cssIcon option exists icon = c.cssIcon ? @@ -574,7 +580,7 @@ // this may get updated numerous times if there are multiple rows c.sortVars[ column ] = { count : -1, // set to -1 because clicking on the header automatically adds one - order: ts.formatSortingOrder( tmp ) ? + order: ts.getOrder( tmp ) ? [ 1, 0, 2 ] : // desc, asc, unsorted [ 0, 1, 2 ], // asc, desc, unsorted lockedOrder : false @@ -582,7 +588,7 @@ tmp = ts.getData( $elem, configHeaders, 'lockedOrder' ) || false; if ( typeof tmp !== 'undefined' && tmp !== false ) { c.sortVars[ column ].lockedOrder = true; - c.sortVars[ column ].order = ts.formatSortingOrder( tmp ) ? [ 1, 1, 1 ] : [ 0, 0, 0 ]; + c.sortVars[ column ].order = ts.getOrder( tmp ) ? [ 1, 1, 1 ] : [ 0, 0, 0 ]; } // add cell to headerList c.headerList[ index ] = elem; @@ -705,6 +711,12 @@ if ( span > 0 ) { colIndex += span; max += span; + while ( span + 1 > 0 ) { + // set colspan columns to use the same parsers & extractors + list.parsers[ colIndex - span ] = parser; + list.extractors[ colIndex - span ] = extractor; + span--; + } } } colIndex++; @@ -847,7 +859,7 @@ buildCache : function( c, callback, $tbodies ) { var cache, val, txt, rowIndex, colIndex, tbodyIndex, $tbody, $row, cols, $cells, cell, cacheTime, totalRows, rowData, prevRowData, - colMax, span, cacheIndex, max, len, + colMax, span, cacheIndex, hasParser, max, len, index, table = c.table, parsers = c.parsers; // update tbody variable @@ -922,22 +934,31 @@ max = c.columns; for ( colIndex = 0; colIndex < max; ++colIndex ) { cell = $row[ 0 ].cells[ colIndex ]; - if ( typeof parsers[ cacheIndex ] === 'undefined' ) { - if ( c.debug ) { - console.warn( 'No parser found for column ' + colIndex + '; cell:', cell, 'does it have a header?' ); + if ( cell && cacheIndex < c.columns ) { + hasParser = typeof parsers[ cacheIndex ] !== 'undefined'; + if ( !hasParser && c.debug ) { + console.warn( 'No parser found for row: ' + rowIndex + ', column: ' + colIndex + + '; cell containing: "' + $(cell).text() + '"; does it have a header?' ); } - } else if ( cell ) { val = ts.getElementText( c, cell, cacheIndex ); rowData.raw[ cacheIndex ] = val; // save original row text + // save raw column text even if there is no parser set txt = ts.getParsedText( c, cell, cacheIndex, val ); cols[ cacheIndex ] = txt; - if ( ( parsers[ cacheIndex ].type || '' ).toLowerCase() === 'numeric' ) { + if ( hasParser && ( parsers[ cacheIndex ].type || '' ).toLowerCase() === 'numeric' ) { // determine column max value (ignore sign) colMax[ cacheIndex ] = Math.max( Math.abs( txt ) || 0, colMax[ cacheIndex ] || 0 ); } // allow colSpan in tbody span = cell.colSpan - 1; if ( span > 0 ) { + index = 0; + while ( index <= span ) { + // duplicate text (or not) to spanned columns + rowData.raw[ cacheIndex + index ] = c.duplicateSpan || index === 0 ? val : ''; + cols[ cacheIndex + index ] = c.duplicateSpan || index === 0 ? val : ''; + index++; + } cacheIndex += span; max += span; } @@ -957,7 +978,21 @@ ts.isProcessing( table ); // remove processing icon } if ( c.debug ) { - console.log( 'Building cache for ' + totalRows + ' rows' + ts.benchmark( cacheTime ) ); + len = Math.min( 5, c.cache[ 0 ].normalized.length ); + console[ console.group ? 'group' : 'log' ]( 'Building cache for ' + c.totalRows + + ' rows (showing ' + len + ' rows in log)' + ts.benchmark( cacheTime ) ); + val = {}; + for ( colIndex = 0; colIndex < c.columns; colIndex++ ) { + for ( cacheIndex = 0; cacheIndex < len; cacheIndex++ ) { + if ( !val[ 'row: ' + cacheIndex ] ) { + val[ 'row: ' + cacheIndex ] = {}; + } + val[ 'row: ' + cacheIndex ][ c.$headerIndexed[ colIndex ].text() ] = + c.cache[ 0 ].normalized[ cacheIndex ][ colIndex ]; + } + } + console[ console.table ? 'table' : 'log' ]( val ); + if ( console.groupEnd ) { console.groupEnd(); } } if ( $.isFunction( callback ) ) { callback( table ); @@ -1053,7 +1088,7 @@ col = parseInt( $el.attr( 'data-column' ), 10 ), end = col + c.$headers[ i ].colSpan; for ( ; col < end; col++ ) { - include = include ? ts.isValueInArray( col, c.sortList ) > -1 : false; + include = include ? include || ts.isValueInArray( col, c.sortList ) > -1 : false; } return include; }); @@ -1158,6 +1193,14 @@ col = parseInt( val[ 0 ], 10 ); // prevents error if sorton array is wrong if ( col < c.columns ) { + + // set order if not already defined - due to colspan header without associated header cell + // adding this check prevents a javascript error + if ( !c.sortVars[ col ].order ) { + order = c.sortVars[ col ].order = ts.getOrder( c.sortInitialOrder ) ? [ 1, 0, 2 ] : [ 0, 1, 2 ]; + c.sortVars[ col ].count = 0; + } + order = c.sortVars[ col ].order; dir = ( '' + val[ 1 ] ).match( /^(1|d|s|o|n)/ ); dir = dir ? dir[ 0 ] : ''; @@ -1218,6 +1261,12 @@ }, updateCell : function( c, cell, resort, callback ) { + if ( ts.isEmptyObject( c.cache ) ) { + // empty table, do an update instead - fixes #1099 + ts.updateHeader( c ); + ts.commonUpdate( c, resort, callback ); + return; + } c.table.isUpdating = true; c.$table.find( c.selectorRemove ).remove(); // get position from the dom @@ -1268,6 +1317,11 @@ // problems with element focus ts.resortComplete( c, callback ); } + } else { + if ( c.debug ) { + console.error( 'updateCell aborted, tbody missing or not within the indicated table' ); + } + c.table.isUpdating = false; } }, @@ -1364,7 +1418,7 @@ if ( ts.isEmptyObject( cache ) ) { // run pager appender in case the table was just emptied return c.appender ? c.appender( table, rows ) : - table.isUpdating ? c.$table.trigger( 'updateComplete', table ) : ''; // Fixes #532 + table.isUpdating ? c.$table.triggerHandler( 'updateComplete', table ) : ''; // Fixes #532 } if ( c.debug ) { appendTime = new Date(); @@ -1398,7 +1452,7 @@ ts.applyWidget( table ); } if ( table.isUpdating ) { - c.$table.trigger( 'updateComplete', table ); + c.$table.triggerHandler( 'updateComplete', table ); } }, @@ -1425,6 +1479,7 @@ ts.initSort( c, cell, event ); }, 50 ); } + var arry, indx, headerIndx, dir, temp, tmp, $header, notMultiSort = !event[ c.sortMultiSortKey ], table = c.table, @@ -1434,7 +1489,7 @@ order = c.sortVars[ col ].order; // Only call sortStart if sorting is enabled - c.$table.trigger( 'sortStart', table ); + c.$table.triggerHandler( 'sortStart', table ); // get current column sort order c.sortVars[ col ].count = event[ c.sortResetKey ] ? 2 : ( c.sortVars[ col ].count + 1 ) % ( c.sortReset ? 3 : 2 ); @@ -1545,14 +1600,15 @@ } } // sortBegin event triggered immediately before the sort - c.$table.trigger( 'sortBegin', table ); + c.$table.triggerHandler( 'sortBegin', table ); // setTimeout needed so the processing icon shows up setTimeout( function() { // set css for headers ts.setHeadersCss( c ); ts.multisort( c ); ts.appendCache( c ); - c.$table.trigger( 'sortEnd', table ); + c.$table.triggerHandler( 'sortBeforeEnd', table ); + c.$table.triggerHandler( 'sortEnd', table ); }, 1 ); }, @@ -1628,7 +1684,7 @@ resortComplete : function( c, callback ) { if ( c.table.isUpdating ) { - c.$table.trigger( 'updateComplete', c.table ); + c.$table.triggerHandler( 'updateComplete', c.table ); } if ( $.isFunction( callback ) ) { callback( c.table ); @@ -1660,7 +1716,7 @@ sortOn : function( c, list, callback, init ) { var table = c.table; - c.$table.trigger( 'sortStart', table ); + c.$table.triggerHandler( 'sortStart', table ); // update header count index ts.updateHeaderSortCount( c, list ); // set css for headers @@ -1669,11 +1725,12 @@ if ( c.delayInit && ts.isEmptyObject( c.cache ) ) { ts.buildCache( c ); } - c.$table.trigger( 'sortBegin', table ); + c.$table.triggerHandler( 'sortBegin', table ); // sort the table and append it to the dom ts.multisort( c ); ts.appendCache( c, init ); - c.$table.trigger( 'sortEnd', table ); + c.$table.triggerHandler( 'sortBeforeEnd', table ); + c.$table.triggerHandler( 'sortEnd', table ); ts.applyWidget( table ); if ( $.isFunction( callback ) ) { callback( table ); @@ -1694,7 +1751,7 @@ return ( parsers && parsers[ column ] ) ? parsers[ column ].type || '' : ''; }, - formatSortingOrder : function( val ) { + getOrder : function( val ) { // look for 'd' in 'desc' order; return true return ( /^d/i.test( val ) || val === 1 ); }, @@ -1854,9 +1911,54 @@ } }, + applyWidgetId : function( table, id, init ) { + var applied, time, name, + c = table.config, + wo = c.widgetOptions, + widget = ts.getWidgetById( id ); + if ( widget ) { + name = widget.id; + applied = false; + // add widget name to option list so it gets reapplied after sorting, filtering, etc + if ( $.inArray( name, c.widgets ) < 0 ) { + c.widgets.push( name ); + } + if ( c.debug ) { time = new Date(); } + + if ( init || !( c.widgetInit[ name ] ) ) { + // set init flag first to prevent calling init more than once (e.g. pager) + c.widgetInit[ name ] = true; + if ( table.hasInitialized ) { + // don't reapply widget options on tablesorter init + ts.applyWidgetOptions( table ); + } + if ( typeof widget.init === 'function' ) { + applied = true; + if ( c.debug ) { + console[ console.group ? 'group' : 'log' ]( 'Initializing ' + name + ' widget' ); + } + widget.init( table, widget, c, wo ); + } + } + if ( !init && typeof widget.format === 'function' ) { + applied = true; + if ( c.debug ) { + console[ console.group ? 'group' : 'log' ]( 'Updating ' + name + ' widget' ); + } + widget.format( table, c, wo, false ); + } + if ( c.debug ) { + if ( applied ) { + console.log( 'Completed ' + ( init ? 'initializing ' : 'applying ' ) + name + ' widget' + ts.benchmark( time ) ); + if ( console.groupEnd ) { console.groupEnd(); } + } + } + } + }, + applyWidget : function( table, init, callback ) { table = $( table )[ 0 ]; // in case this is called externally - var indx, len, names, widget, name, applied, time, time2, + var indx, len, names, widget, time, c = table.config, widgets = []; // prevent numerous consecutive widget applications @@ -1895,39 +1997,8 @@ } for ( indx = 0; indx < len; indx++ ) { widget = widgets[ indx ]; - if ( widget ) { - name = widget.id; - applied = false; - if ( c.debug ) { time2 = new Date(); } - - if ( init || !( c.widgetInit[ name ] ) ) { - // set init flag first to prevent calling init more than once (e.g. pager) - c.widgetInit[ name ] = true; - if ( table.hasInitialized ) { - // don't reapply widget options on tablesorter init - ts.applyWidgetOptions( table ); - } - if ( typeof widget.init === 'function' ) { - applied = true; - if ( c.debug ) { - console[ console.group ? 'group' : 'log' ]( 'Initializing ' + name + ' widget' ); - } - widget.init( table, widget, table.config, table.config.widgetOptions ); - } - } - if ( !init && typeof widget.format === 'function' ) { - applied = true; - if ( c.debug ) { - console[ console.group ? 'group' : 'log' ]( 'Updating ' + name + ' widget' ); - } - widget.format( table, table.config, table.config.widgetOptions, false ); - } - if ( c.debug ) { - if ( applied ) { - console.log( 'Completed ' + ( init ? 'initializing ' : 'applying ' ) + name + ' widget' + ts.benchmark( time2 ) ); - if ( console.groupEnd ) { console.groupEnd(); } - } - } + if ( widget && widget.id ) { + ts.applyWidgetId( table, widget.id, init ); } } if ( c.debug && console.groupEnd ) { console.groupEnd(); } @@ -1939,7 +2010,7 @@ c.timerReady = setTimeout( function() { table.isApplyingWidgets = false; $.data( table, 'lastWidgetApplication', new Date() ); - c.$table.trigger( 'tablesorter-ready' ); + c.$table.triggerHandler( 'tablesorter-ready' ); }, 10 ); if ( c.debug ) { widget = c.widgets.length; @@ -1995,7 +2066,7 @@ len = widgets.length, list = [], callback = function( table ) { - $( table ).trigger( 'refreshComplete' ); + $( table ).triggerHandler( 'refreshComplete' ); }; // remove widgets not defined in config.widgets, unless doAll is true for ( indx = 0; indx < len; indx++ ) { @@ -2084,17 +2155,17 @@ // computeTableHeaderCellIndexes from: // http://www.javascripttoolbox.com/lib/table/examples.php // http://www.javascripttoolbox.com/temp/table_cellindex.html - computeColumnIndex : function( $rows ) { - var i, j, k, l, $cell, cell, cells, rowIndex, cellId, rowSpan, colSpan, firstAvailCol, + computeColumnIndex : function( $rows, c ) { + var i, j, k, l, cell, cells, rowIndex, rowSpan, colSpan, firstAvailCol, + // total columns has been calculated, use it to set the matrixrow + columns = c && c.columns || 0, matrix = [], - matrixrow = []; + matrixrow = new Array( columns ); for ( i = 0; i < $rows.length; i++ ) { cells = $rows[ i ].cells; for ( j = 0; j < cells.length; j++ ) { cell = cells[ j ]; - $cell = $( cell ); rowIndex = cell.parentNode.rowIndex; - cellId = rowIndex + '-' + $cell.index(); rowSpan = cell.rowSpan || 1; colSpan = cell.colSpan || 1; if ( typeof matrix[ rowIndex ] === 'undefined' ) { @@ -2107,11 +2178,16 @@ break; } } - // add data-column (setAttribute = IE8+) - if ( cell.setAttribute ) { + // jscs:disable disallowEmptyBlocks + if ( columns && cell.cellIndex === firstAvailCol ) { + // don't to anything + } else if ( cell.setAttribute ) { + // jscs:enable disallowEmptyBlocks + // add data-column (setAttribute = IE8+) cell.setAttribute( 'data-column', firstAvailCol ); } else { - $cell.attr( 'data-column', firstAvailCol ); + // remove once we drop support for IE7 - 1/12/2016 + $( cell ).attr( 'data-column', firstAvailCol ); } for ( k = rowIndex; k < rowIndex + rowSpan; k++ ) { if ( typeof matrix[ k ] === 'undefined' ) { @@ -2320,15 +2396,16 @@ $f = $t.find( 'tfoot:first > tr' ).children( 'th, td' ); if ( removeClasses === false && $.inArray( 'uitheme', c.widgets ) >= 0 ) { // reapply uitheme classes, in case we want to maintain appearance - $t.trigger( 'applyWidgetId', [ 'uitheme' ] ); - $t.trigger( 'applyWidgetId', [ 'zebra' ] ); + $t.triggerHandler( 'applyWidgetId', [ 'uitheme' ] ); + $t.triggerHandler( 'applyWidgetId', [ 'zebra' ] ); } // remove widget added rows, just in case $h.find( 'tr' ).not( $r ).remove(); - // disable tablesorter + // disable tablesorter - not using .unbind( namespace ) because namespacing was + // added in jQuery v1.4.3 - see http://api.jquery.com/event.namespace/ events = 'sortReset update updateRows updateAll updateHeaders updateCell addRows updateComplete sorton ' + - 'appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress ' + - 'sortBegin sortEnd resetToLoadState '.split( ' ' ) + 'appendCache updateCache applyWidgetId applyWidgets refreshWidgets removeWidget destroy mouseup mouseleave ' + + 'keypress sortBegin sortEnd resetToLoadState '.split( ' ' ) .join( c.namespace + ' ' ); $t .removeData( 'tablesorter' ) @@ -3005,7 +3082,7 @@ })(jQuery); -/*! Widget: filter - updated 11/10/2015 (v2.24.4) *//* +/*! Widget: filter - updated 12/13/2015 (v2.25.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3215,7 +3292,7 @@ table = c.table, parsed = data.parsed[ data.index ], query = ts.formatFloat( data.iFilter.replace( tsfRegex.operators, '' ), table ), - parser = c.parsers[ data.index ], + parser = c.parsers[ data.index ] || {}, savedSearch = query; // parse filter value in case we're comparing numbers ( dates ) if ( parsed || parser.type === 'numeric' ) { @@ -3355,6 +3432,7 @@ var options, string, txt, $header, column, filters, val, fxn, noSelect; c.$table.addClass( 'hasFilters' ); + c.lastSearch = []; // define timers so using clearTimeout won't cause an undefined error wo.filter_searchTimer = null; @@ -3437,7 +3515,7 @@ if ( wo.filter_reset instanceof $ ) { // reset contains a jQuery object, bind to it wo.filter_reset.click( function() { - c.$table.trigger( 'filterReset' ); + c.$table.triggerHandler( 'filterReset' ); }); } else if ( $( wo.filter_reset ).length ) { // reset is a jQuery selector, use event delegation @@ -3445,7 +3523,7 @@ .undelegate( wo.filter_reset, 'click' + c.namespace + 'filter' ) .delegate( wo.filter_reset, 'click' + c.namespace + 'filter', function() { // trigger a reset event, so other functions ( filter_formatter ) know when to reset - c.$table.trigger( 'filterReset' ); + c.$table.triggerHandler( 'filterReset' ); }); } } @@ -3547,7 +3625,7 @@ ts.setFilters( table, filters, true ); } } - c.$table.trigger( 'filterFomatterUpdate' ); + c.$table.triggerHandler( 'filterFomatterUpdate' ); // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers setTimeout( function() { if ( !wo.filter_initialized ) { @@ -3557,7 +3635,7 @@ }); // if filter widget is added after pager has initialized; then set filter init flag if ( c.pager && c.pager.initialized && !wo.filter_initialized ) { - c.$table.trigger( 'filterFomatterUpdate' ); + c.$table.triggerHandler( 'filterFomatterUpdate' ); setTimeout( function() { tsf.filterInitComplete( c ); }, 100 ); @@ -3580,7 +3658,7 @@ count = 0, completed = function() { wo.filter_initialized = true; - c.$table.trigger( 'filterInit', c ); + c.$table.triggerHandler( 'filterInit', c ); tsf.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); }; if ( $.isEmptyObject( wo.filter_formatter ) ) { @@ -3635,7 +3713,7 @@ for ( indx = 0; indx <= c.columns; indx++ ) { // include data-column='all' external filters col = indx === c.columns ? 'all' : indx; - filters[indx] = $filters + filters[ indx ] = $filters .filter( '[data-column="' + col + '"]' ) .attr( wo.filter_defaultAttrib ) || filters[indx] || ''; } @@ -3657,11 +3735,12 @@ buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; for ( column = 0; column < columns; column++ ) { if ( c.$headerIndexed[ column ].length ) { - buildFilter += '<td data-column="' + column + '"'; // account for entire column set with colspan. See #1047 tmp = c.$headerIndexed[ column ] && c.$headerIndexed[ column ][0].colSpan || 0; if ( tmp > 1 ) { - buildFilter += ' colspan="' + tmp + '"'; + buildFilter += '<td data-column="' + column + '-' + ( column + tmp - 1 ) + '" colspan="' + tmp + '"'; + } else { + buildFilter += '<td data-column="' + column + '"'; } if ( arry ) { buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' ); @@ -3680,7 +3759,8 @@ // assuming last cell of a column is the main column $header = c.$headerIndexed[ column ]; if ( $header && $header.length ) { - $filter = c.$filters.filter( '[data-column="' + column + '"]' ); + // $filter = c.$filters.filter( '[data-column="' + column + '"]' ); + $filter = tsf.getColumnElm( c, c.$filters, column ); ffxn = ts.getColumnData( table, wo.filter_functions, column ); makeSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || $header.hasClass( 'filter-select' ); @@ -3720,7 +3800,8 @@ name = ( $.isArray( wo.filter_cssFilter ) ? ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : wo.filter_cssFilter ) || ''; - buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column ); + // copy data-column from table cell (it will include colspan) + buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', $filter.attr( 'data-column' ) ); if ( disabled ) { buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; } @@ -3828,7 +3909,7 @@ // show/hide filter row as needed c.$table .find( '.' + tscss.filterRow ) - .trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); + .triggerHandler( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons @@ -3839,6 +3920,8 @@ c.lastCombinedFilter = null; c.lastSearch = []; } + // define filter inside it is false + filters = filters || []; // convert filters to strings - see #1070 filters = Array.prototype.map ? filters.map( String ) : @@ -3846,7 +3929,7 @@ filters.join( '\u0000' ).split( '\u0000' ); if ( wo.filter_initialized ) { - c.$table.trigger( 'filterStart', [ filters ] ); + c.$table.triggerHandler( 'filterStart', [ filters ] ); } if ( c.showProcessing ) { // give it time for the processing icon to kick in @@ -3927,22 +4010,18 @@ } return $input || $(); }, - multipleColumns: function( c, $input ) { + findRange: function( c, val, ignoreRanges ) { // look for multiple columns '1-3,4-6,8' in data-column var temp, ranges, range, start, end, singles, i, indx, len, - wo = c.widgetOptions, - // only target 'all' column inputs on initialization - // & don't target 'all' column inputs if they don't exist - targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, - columns = [], - val = $.trim( tsf.getLatestSearch( $input ).attr( 'data-column' ) || '' ); - if ( /^[0-9]+$/.test(val)) { - return parseInt( val, 10 ); + columns = []; + if ( /^[0-9]+$/.test( val ) ) { + // always return an array + return [ parseInt( val, 10 ) ]; } // process column range - if ( targets && /-/.test( val ) ) { + if ( !ignoreRanges && /-/.test( val ) ) { ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); - len = ranges.length; + len = ranges ? ranges.length : 0; for ( indx = 0; indx < len; indx++ ) { range = ranges[indx].split( /\s*-\s*/ ); start = parseInt( range[0], 10 ) || 0; @@ -3961,7 +4040,7 @@ } } // process single columns - if ( targets && /,/.test( val ) ) { + if ( !ignoreRanges && /,/.test( val ) ) { singles = val.split( /\s*,\s*/ ); len = singles.length; for ( i = 0; i < len; i++ ) { @@ -3981,6 +4060,23 @@ } return columns; }, + getColumnElm: function( c, $elements, column ) { + // data-column may contain multiple columns '1-3,5-6,8' + // replaces: c.$filters.filter( '[data-column="' + column + '"]' ); + return $elements.filter( function() { + var cols = tsf.findRange( c, $( this ).attr( 'data-column' ) ); + return $.inArray( column, cols ) > -1; + }); + }, + multipleColumns: function( c, $input ) { + // look for multiple columns '1-3,4-6,8' in data-column + var wo = c.widgetOptions, + // only target 'all' column inputs on initialization + // & don't target 'all' column inputs if they don't exist + targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, + val = $.trim( tsf.getLatestSearch( $input ).attr( 'data-column' ) || '' ); + return tsf.findRange( c, val, !targets ); + }, processTypes: function( c, data, vars ) { var ffxn, filterMatched = null, @@ -4094,6 +4190,11 @@ data.filter = ts.replaceAccents( data.filter ); } + // replace column specific default filters - see #1088 + if ( wo.filter_defaultFilter && tsfRegex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) { + data.filter = tsf.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); + } + // data.iFilter = case insensitive ( if wo.filter_ignoreCase is true ), // data.filter = case sensitive data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter; @@ -4382,7 +4483,8 @@ console.log( 'Completed filter widget search' + ts.benchmark(time) ); } if ( wo.filter_initialized ) { - c.$table.trigger( 'filterEnd', c ); + c.$table.triggerHandler( 'filterBeforeEnd', c ); + c.$table.triggerHandler( 'filterEnd', c ); } setTimeout( function() { ts.applyWidget( c.table ); // make sure zebra widget is applied @@ -4747,7 +4849,7 @@ c.lastCombinedFilter = null; c.lastSearch = []; tsf.searching( c.table, filter, skipFirst ); - c.$table.trigger( 'filterFomatterUpdate' ); + c.$table.triggerHandler( 'filterFomatterUpdate' ); } return !!valid; }; @@ -4795,7 +4897,7 @@ } } if ( headers.length && triggerEvent !== false ) { - c.$table.trigger( 'resize', [ headers ] ); + c.$table.triggerHandler( 'resize', [ headers ] ); } wo.resize_flag = false; }; @@ -5022,7 +5124,7 @@ } } - $table.trigger('stickyHeadersInit'); + $table.triggerHandler('stickyHeadersInit'); }, remove: function(table, c, wo) { @@ -5350,7 +5452,7 @@ } vars.mouseXPosition = event.pageX; // dynamically update sticky header widths - c.$table.trigger('stickyHeadersUpdate'); + c.$table.triggerHandler('stickyHeadersUpdate'); }, stopResize : function( c, wo ) { @@ -5364,7 +5466,7 @@ vars.mouseXPosition = 0; vars.$target = vars.$next = null; // will update stickyHeaders, just in case, see #912 - c.$table.trigger('stickyHeadersUpdate'); + c.$table.triggerHandler('stickyHeadersUpdate'); } }; @@ -5426,7 +5528,7 @@ } // reset stickyHeader widths - c.$table.trigger( 'stickyHeadersUpdate' ); + c.$table.triggerHandler( 'stickyHeadersUpdate' ); if ( ts.storage && !refreshing ) { ts.storage( this, ts.css.resizableStorage, {} ); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index b0ff788..584f1f4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.24.6 *//* +/*! TableSorter (FORK) v2.25.0 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -21,7 +21,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.24.6', + version : '2.25.0', parsers : [], widgets : [], @@ -62,6 +62,7 @@ emptyTo : 'bottom', // sort empty cell to bottom, top, none, zero, emptyMax, emptyMin stringTo : 'max', // sort strings in numerical column as max, min, top, bottom, zero + duplicateSpan : true, // colspan cells in the tbody will have duplicated content in the cache for each spanned column textExtraction : 'basic', // text extraction method/function - function( node, table, cellIndex ){} textAttribute : 'data-text',// data-attribute that contains alternate cell text (used in default textExtraction function) textSorter : null, // choose overall or specific column sorter function( a, b, direction, table, columnIndex ) [alt: ts.sortText] @@ -209,7 +210,7 @@ if ( table.hasInitialized ) { console.warn( 'Stopping initialization. Tablesorter has already been initialized' ); } else { - console.error( 'Stopping initialization! No table, thead or tbody' ); + console.error( 'Stopping initialization! No table, thead or tbody', table ); } } return; @@ -332,7 +333,7 @@ console.log( 'Overall initialization time: ' + ts.benchmark( $.data( table, 'startoveralltimer' ) ) ); if ( c.debug && console.groupEnd ) { console.groupEnd(); } } - $table.trigger( 'tablesorter-initialized', table ); + $table.triggerHandler( 'tablesorter-initialized', table ); if ( typeof c.initialized === 'function' ) { c.initialized( table ); } @@ -394,7 +395,7 @@ }) .bind( 'applyWidgetId' + namespace, function( e, id ) { e.stopPropagation(); - ts.getWidgetById( id ).format( this, this.config, this.config.widgetOptions ); + ts.applyWidgetId( this, id ); }) .bind( 'applyWidgets' + namespace, function( e, init ) { e.stopPropagation(); @@ -405,6 +406,10 @@ e.stopPropagation(); ts.refreshWidgets( this, all, dontapply ); }) + .bind( 'removeWidget' + namespace, function( e, name, refreshing ) { + e.stopPropagation(); + ts.removeWidget( this, name, refreshing ); + }) .bind( 'destroy' + namespace, function( e, removeClasses, callback ) { e.stopPropagation(); ts.destroy( this, removeClasses, callback ); @@ -517,6 +522,7 @@ timer = new Date(); } // children tr in tfoot - see issue #196 & #547 + // don't pass table.config to computeColumnIndex here - widgets (math) pass it to "quickly" index tbody cells c.columns = ts.computeColumnIndex( c.$table.children( 'thead, tfoot' ).children( 'tr' ) ); // add icon if cssIcon option exists icon = c.cssIcon ? @@ -556,7 +562,7 @@ // this may get updated numerous times if there are multiple rows c.sortVars[ column ] = { count : -1, // set to -1 because clicking on the header automatically adds one - order: ts.formatSortingOrder( tmp ) ? + order: ts.getOrder( tmp ) ? [ 1, 0, 2 ] : // desc, asc, unsorted [ 0, 1, 2 ], // asc, desc, unsorted lockedOrder : false @@ -564,7 +570,7 @@ tmp = ts.getData( $elem, configHeaders, 'lockedOrder' ) || false; if ( typeof tmp !== 'undefined' && tmp !== false ) { c.sortVars[ column ].lockedOrder = true; - c.sortVars[ column ].order = ts.formatSortingOrder( tmp ) ? [ 1, 1, 1 ] : [ 0, 0, 0 ]; + c.sortVars[ column ].order = ts.getOrder( tmp ) ? [ 1, 1, 1 ] : [ 0, 0, 0 ]; } // add cell to headerList c.headerList[ index ] = elem; @@ -687,6 +693,12 @@ if ( span > 0 ) { colIndex += span; max += span; + while ( span + 1 > 0 ) { + // set colspan columns to use the same parsers & extractors + list.parsers[ colIndex - span ] = parser; + list.extractors[ colIndex - span ] = extractor; + span--; + } } } colIndex++; @@ -829,7 +841,7 @@ buildCache : function( c, callback, $tbodies ) { var cache, val, txt, rowIndex, colIndex, tbodyIndex, $tbody, $row, cols, $cells, cell, cacheTime, totalRows, rowData, prevRowData, - colMax, span, cacheIndex, max, len, + colMax, span, cacheIndex, hasParser, max, len, index, table = c.table, parsers = c.parsers; // update tbody variable @@ -904,22 +916,31 @@ max = c.columns; for ( colIndex = 0; colIndex < max; ++colIndex ) { cell = $row[ 0 ].cells[ colIndex ]; - if ( typeof parsers[ cacheIndex ] === 'undefined' ) { - if ( c.debug ) { - console.warn( 'No parser found for column ' + colIndex + '; cell:', cell, 'does it have a header?' ); + if ( cell && cacheIndex < c.columns ) { + hasParser = typeof parsers[ cacheIndex ] !== 'undefined'; + if ( !hasParser && c.debug ) { + console.warn( 'No parser found for row: ' + rowIndex + ', column: ' + colIndex + + '; cell containing: "' + $(cell).text() + '"; does it have a header?' ); } - } else if ( cell ) { val = ts.getElementText( c, cell, cacheIndex ); rowData.raw[ cacheIndex ] = val; // save original row text + // save raw column text even if there is no parser set txt = ts.getParsedText( c, cell, cacheIndex, val ); cols[ cacheIndex ] = txt; - if ( ( parsers[ cacheIndex ].type || '' ).toLowerCase() === 'numeric' ) { + if ( hasParser && ( parsers[ cacheIndex ].type || '' ).toLowerCase() === 'numeric' ) { // determine column max value (ignore sign) colMax[ cacheIndex ] = Math.max( Math.abs( txt ) || 0, colMax[ cacheIndex ] || 0 ); } // allow colSpan in tbody span = cell.colSpan - 1; if ( span > 0 ) { + index = 0; + while ( index <= span ) { + // duplicate text (or not) to spanned columns + rowData.raw[ cacheIndex + index ] = c.duplicateSpan || index === 0 ? val : ''; + cols[ cacheIndex + index ] = c.duplicateSpan || index === 0 ? val : ''; + index++; + } cacheIndex += span; max += span; } @@ -939,7 +960,21 @@ ts.isProcessing( table ); // remove processing icon } if ( c.debug ) { - console.log( 'Building cache for ' + totalRows + ' rows' + ts.benchmark( cacheTime ) ); + len = Math.min( 5, c.cache[ 0 ].normalized.length ); + console[ console.group ? 'group' : 'log' ]( 'Building cache for ' + c.totalRows + + ' rows (showing ' + len + ' rows in log)' + ts.benchmark( cacheTime ) ); + val = {}; + for ( colIndex = 0; colIndex < c.columns; colIndex++ ) { + for ( cacheIndex = 0; cacheIndex < len; cacheIndex++ ) { + if ( !val[ 'row: ' + cacheIndex ] ) { + val[ 'row: ' + cacheIndex ] = {}; + } + val[ 'row: ' + cacheIndex ][ c.$headerIndexed[ colIndex ].text() ] = + c.cache[ 0 ].normalized[ cacheIndex ][ colIndex ]; + } + } + console[ console.table ? 'table' : 'log' ]( val ); + if ( console.groupEnd ) { console.groupEnd(); } } if ( $.isFunction( callback ) ) { callback( table ); @@ -1035,7 +1070,7 @@ col = parseInt( $el.attr( 'data-column' ), 10 ), end = col + c.$headers[ i ].colSpan; for ( ; col < end; col++ ) { - include = include ? ts.isValueInArray( col, c.sortList ) > -1 : false; + include = include ? include || ts.isValueInArray( col, c.sortList ) > -1 : false; } return include; }); @@ -1140,6 +1175,14 @@ col = parseInt( val[ 0 ], 10 ); // prevents error if sorton array is wrong if ( col < c.columns ) { + + // set order if not already defined - due to colspan header without associated header cell + // adding this check prevents a javascript error + if ( !c.sortVars[ col ].order ) { + order = c.sortVars[ col ].order = ts.getOrder( c.sortInitialOrder ) ? [ 1, 0, 2 ] : [ 0, 1, 2 ]; + c.sortVars[ col ].count = 0; + } + order = c.sortVars[ col ].order; dir = ( '' + val[ 1 ] ).match( /^(1|d|s|o|n)/ ); dir = dir ? dir[ 0 ] : ''; @@ -1200,6 +1243,12 @@ }, updateCell : function( c, cell, resort, callback ) { + if ( ts.isEmptyObject( c.cache ) ) { + // empty table, do an update instead - fixes #1099 + ts.updateHeader( c ); + ts.commonUpdate( c, resort, callback ); + return; + } c.table.isUpdating = true; c.$table.find( c.selectorRemove ).remove(); // get position from the dom @@ -1250,6 +1299,11 @@ // problems with element focus ts.resortComplete( c, callback ); } + } else { + if ( c.debug ) { + console.error( 'updateCell aborted, tbody missing or not within the indicated table' ); + } + c.table.isUpdating = false; } }, @@ -1346,7 +1400,7 @@ if ( ts.isEmptyObject( cache ) ) { // run pager appender in case the table was just emptied return c.appender ? c.appender( table, rows ) : - table.isUpdating ? c.$table.trigger( 'updateComplete', table ) : ''; // Fixes #532 + table.isUpdating ? c.$table.triggerHandler( 'updateComplete', table ) : ''; // Fixes #532 } if ( c.debug ) { appendTime = new Date(); @@ -1380,7 +1434,7 @@ ts.applyWidget( table ); } if ( table.isUpdating ) { - c.$table.trigger( 'updateComplete', table ); + c.$table.triggerHandler( 'updateComplete', table ); } }, @@ -1407,6 +1461,7 @@ ts.initSort( c, cell, event ); }, 50 ); } + var arry, indx, headerIndx, dir, temp, tmp, $header, notMultiSort = !event[ c.sortMultiSortKey ], table = c.table, @@ -1416,7 +1471,7 @@ order = c.sortVars[ col ].order; // Only call sortStart if sorting is enabled - c.$table.trigger( 'sortStart', table ); + c.$table.triggerHandler( 'sortStart', table ); // get current column sort order c.sortVars[ col ].count = event[ c.sortResetKey ] ? 2 : ( c.sortVars[ col ].count + 1 ) % ( c.sortReset ? 3 : 2 ); @@ -1527,14 +1582,15 @@ } } // sortBegin event triggered immediately before the sort - c.$table.trigger( 'sortBegin', table ); + c.$table.triggerHandler( 'sortBegin', table ); // setTimeout needed so the processing icon shows up setTimeout( function() { // set css for headers ts.setHeadersCss( c ); ts.multisort( c ); ts.appendCache( c ); - c.$table.trigger( 'sortEnd', table ); + c.$table.triggerHandler( 'sortBeforeEnd', table ); + c.$table.triggerHandler( 'sortEnd', table ); }, 1 ); }, @@ -1610,7 +1666,7 @@ resortComplete : function( c, callback ) { if ( c.table.isUpdating ) { - c.$table.trigger( 'updateComplete', c.table ); + c.$table.triggerHandler( 'updateComplete', c.table ); } if ( $.isFunction( callback ) ) { callback( c.table ); @@ -1642,7 +1698,7 @@ sortOn : function( c, list, callback, init ) { var table = c.table; - c.$table.trigger( 'sortStart', table ); + c.$table.triggerHandler( 'sortStart', table ); // update header count index ts.updateHeaderSortCount( c, list ); // set css for headers @@ -1651,11 +1707,12 @@ if ( c.delayInit && ts.isEmptyObject( c.cache ) ) { ts.buildCache( c ); } - c.$table.trigger( 'sortBegin', table ); + c.$table.triggerHandler( 'sortBegin', table ); // sort the table and append it to the dom ts.multisort( c ); ts.appendCache( c, init ); - c.$table.trigger( 'sortEnd', table ); + c.$table.triggerHandler( 'sortBeforeEnd', table ); + c.$table.triggerHandler( 'sortEnd', table ); ts.applyWidget( table ); if ( $.isFunction( callback ) ) { callback( table ); @@ -1676,7 +1733,7 @@ return ( parsers && parsers[ column ] ) ? parsers[ column ].type || '' : ''; }, - formatSortingOrder : function( val ) { + getOrder : function( val ) { // look for 'd' in 'desc' order; return true return ( /^d/i.test( val ) || val === 1 ); }, @@ -1836,9 +1893,54 @@ } }, + applyWidgetId : function( table, id, init ) { + var applied, time, name, + c = table.config, + wo = c.widgetOptions, + widget = ts.getWidgetById( id ); + if ( widget ) { + name = widget.id; + applied = false; + // add widget name to option list so it gets reapplied after sorting, filtering, etc + if ( $.inArray( name, c.widgets ) < 0 ) { + c.widgets.push( name ); + } + if ( c.debug ) { time = new Date(); } + + if ( init || !( c.widgetInit[ name ] ) ) { + // set init flag first to prevent calling init more than once (e.g. pager) + c.widgetInit[ name ] = true; + if ( table.hasInitialized ) { + // don't reapply widget options on tablesorter init + ts.applyWidgetOptions( table ); + } + if ( typeof widget.init === 'function' ) { + applied = true; + if ( c.debug ) { + console[ console.group ? 'group' : 'log' ]( 'Initializing ' + name + ' widget' ); + } + widget.init( table, widget, c, wo ); + } + } + if ( !init && typeof widget.format === 'function' ) { + applied = true; + if ( c.debug ) { + console[ console.group ? 'group' : 'log' ]( 'Updating ' + name + ' widget' ); + } + widget.format( table, c, wo, false ); + } + if ( c.debug ) { + if ( applied ) { + console.log( 'Completed ' + ( init ? 'initializing ' : 'applying ' ) + name + ' widget' + ts.benchmark( time ) ); + if ( console.groupEnd ) { console.groupEnd(); } + } + } + } + }, + applyWidget : function( table, init, callback ) { table = $( table )[ 0 ]; // in case this is called externally - var indx, len, names, widget, name, applied, time, time2, + var indx, len, names, widget, time, c = table.config, widgets = []; // prevent numerous consecutive widget applications @@ -1877,39 +1979,8 @@ } for ( indx = 0; indx < len; indx++ ) { widget = widgets[ indx ]; - if ( widget ) { - name = widget.id; - applied = false; - if ( c.debug ) { time2 = new Date(); } - - if ( init || !( c.widgetInit[ name ] ) ) { - // set init flag first to prevent calling init more than once (e.g. pager) - c.widgetInit[ name ] = true; - if ( table.hasInitialized ) { - // don't reapply widget options on tablesorter init - ts.applyWidgetOptions( table ); - } - if ( typeof widget.init === 'function' ) { - applied = true; - if ( c.debug ) { - console[ console.group ? 'group' : 'log' ]( 'Initializing ' + name + ' widget' ); - } - widget.init( table, widget, table.config, table.config.widgetOptions ); - } - } - if ( !init && typeof widget.format === 'function' ) { - applied = true; - if ( c.debug ) { - console[ console.group ? 'group' : 'log' ]( 'Updating ' + name + ' widget' ); - } - widget.format( table, table.config, table.config.widgetOptions, false ); - } - if ( c.debug ) { - if ( applied ) { - console.log( 'Completed ' + ( init ? 'initializing ' : 'applying ' ) + name + ' widget' + ts.benchmark( time2 ) ); - if ( console.groupEnd ) { console.groupEnd(); } - } - } + if ( widget && widget.id ) { + ts.applyWidgetId( table, widget.id, init ); } } if ( c.debug && console.groupEnd ) { console.groupEnd(); } @@ -1921,7 +1992,7 @@ c.timerReady = setTimeout( function() { table.isApplyingWidgets = false; $.data( table, 'lastWidgetApplication', new Date() ); - c.$table.trigger( 'tablesorter-ready' ); + c.$table.triggerHandler( 'tablesorter-ready' ); }, 10 ); if ( c.debug ) { widget = c.widgets.length; @@ -1977,7 +2048,7 @@ len = widgets.length, list = [], callback = function( table ) { - $( table ).trigger( 'refreshComplete' ); + $( table ).triggerHandler( 'refreshComplete' ); }; // remove widgets not defined in config.widgets, unless doAll is true for ( indx = 0; indx < len; indx++ ) { @@ -2066,17 +2137,17 @@ // computeTableHeaderCellIndexes from: // http://www.javascripttoolbox.com/lib/table/examples.php // http://www.javascripttoolbox.com/temp/table_cellindex.html - computeColumnIndex : function( $rows ) { - var i, j, k, l, $cell, cell, cells, rowIndex, cellId, rowSpan, colSpan, firstAvailCol, + computeColumnIndex : function( $rows, c ) { + var i, j, k, l, cell, cells, rowIndex, rowSpan, colSpan, firstAvailCol, + // total columns has been calculated, use it to set the matrixrow + columns = c && c.columns || 0, matrix = [], - matrixrow = []; + matrixrow = new Array( columns ); for ( i = 0; i < $rows.length; i++ ) { cells = $rows[ i ].cells; for ( j = 0; j < cells.length; j++ ) { cell = cells[ j ]; - $cell = $( cell ); rowIndex = cell.parentNode.rowIndex; - cellId = rowIndex + '-' + $cell.index(); rowSpan = cell.rowSpan || 1; colSpan = cell.colSpan || 1; if ( typeof matrix[ rowIndex ] === 'undefined' ) { @@ -2089,11 +2160,16 @@ break; } } - // add data-column (setAttribute = IE8+) - if ( cell.setAttribute ) { + // jscs:disable disallowEmptyBlocks + if ( columns && cell.cellIndex === firstAvailCol ) { + // don't to anything + } else if ( cell.setAttribute ) { + // jscs:enable disallowEmptyBlocks + // add data-column (setAttribute = IE8+) cell.setAttribute( 'data-column', firstAvailCol ); } else { - $cell.attr( 'data-column', firstAvailCol ); + // remove once we drop support for IE7 - 1/12/2016 + $( cell ).attr( 'data-column', firstAvailCol ); } for ( k = rowIndex; k < rowIndex + rowSpan; k++ ) { if ( typeof matrix[ k ] === 'undefined' ) { @@ -2302,15 +2378,16 @@ $f = $t.find( 'tfoot:first > tr' ).children( 'th, td' ); if ( removeClasses === false && $.inArray( 'uitheme', c.widgets ) >= 0 ) { // reapply uitheme classes, in case we want to maintain appearance - $t.trigger( 'applyWidgetId', [ 'uitheme' ] ); - $t.trigger( 'applyWidgetId', [ 'zebra' ] ); + $t.triggerHandler( 'applyWidgetId', [ 'uitheme' ] ); + $t.triggerHandler( 'applyWidgetId', [ 'zebra' ] ); } // remove widget added rows, just in case $h.find( 'tr' ).not( $r ).remove(); - // disable tablesorter + // disable tablesorter - not using .unbind( namespace ) because namespacing was + // added in jQuery v1.4.3 - see http://api.jquery.com/event.namespace/ events = 'sortReset update updateRows updateAll updateHeaders updateCell addRows updateComplete sorton ' + - 'appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress ' + - 'sortBegin sortEnd resetToLoadState '.split( ' ' ) + 'appendCache updateCache applyWidgetId applyWidgets refreshWidgets removeWidget destroy mouseup mouseleave ' + + 'keypress sortBegin sortEnd resetToLoadState '.split( ' ' ) .join( c.namespace + ' ' ); $t .removeData( 'tablesorter' ) diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 67d2e69..4e511e9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 11-22-2015 (v2.24.6)*/ +/*! tablesorter (FORK) - updated 12-13-2015 (v2.25.0)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -372,7 +372,7 @@ })(jQuery); -/*! Widget: filter - updated 11/10/2015 (v2.24.4) *//* +/*! Widget: filter - updated 12/13/2015 (v2.25.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -582,7 +582,7 @@ table = c.table, parsed = data.parsed[ data.index ], query = ts.formatFloat( data.iFilter.replace( tsfRegex.operators, '' ), table ), - parser = c.parsers[ data.index ], + parser = c.parsers[ data.index ] || {}, savedSearch = query; // parse filter value in case we're comparing numbers ( dates ) if ( parsed || parser.type === 'numeric' ) { @@ -722,6 +722,7 @@ var options, string, txt, $header, column, filters, val, fxn, noSelect; c.$table.addClass( 'hasFilters' ); + c.lastSearch = []; // define timers so using clearTimeout won't cause an undefined error wo.filter_searchTimer = null; @@ -804,7 +805,7 @@ if ( wo.filter_reset instanceof $ ) { // reset contains a jQuery object, bind to it wo.filter_reset.click( function() { - c.$table.trigger( 'filterReset' ); + c.$table.triggerHandler( 'filterReset' ); }); } else if ( $( wo.filter_reset ).length ) { // reset is a jQuery selector, use event delegation @@ -812,7 +813,7 @@ .undelegate( wo.filter_reset, 'click' + c.namespace + 'filter' ) .delegate( wo.filter_reset, 'click' + c.namespace + 'filter', function() { // trigger a reset event, so other functions ( filter_formatter ) know when to reset - c.$table.trigger( 'filterReset' ); + c.$table.triggerHandler( 'filterReset' ); }); } } @@ -914,7 +915,7 @@ ts.setFilters( table, filters, true ); } } - c.$table.trigger( 'filterFomatterUpdate' ); + c.$table.triggerHandler( 'filterFomatterUpdate' ); // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers setTimeout( function() { if ( !wo.filter_initialized ) { @@ -924,7 +925,7 @@ }); // if filter widget is added after pager has initialized; then set filter init flag if ( c.pager && c.pager.initialized && !wo.filter_initialized ) { - c.$table.trigger( 'filterFomatterUpdate' ); + c.$table.triggerHandler( 'filterFomatterUpdate' ); setTimeout( function() { tsf.filterInitComplete( c ); }, 100 ); @@ -947,7 +948,7 @@ count = 0, completed = function() { wo.filter_initialized = true; - c.$table.trigger( 'filterInit', c ); + c.$table.triggerHandler( 'filterInit', c ); tsf.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); }; if ( $.isEmptyObject( wo.filter_formatter ) ) { @@ -1002,7 +1003,7 @@ for ( indx = 0; indx <= c.columns; indx++ ) { // include data-column='all' external filters col = indx === c.columns ? 'all' : indx; - filters[indx] = $filters + filters[ indx ] = $filters .filter( '[data-column="' + col + '"]' ) .attr( wo.filter_defaultAttrib ) || filters[indx] || ''; } @@ -1024,11 +1025,12 @@ buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; for ( column = 0; column < columns; column++ ) { if ( c.$headerIndexed[ column ].length ) { - buildFilter += '<td data-column="' + column + '"'; // account for entire column set with colspan. See #1047 tmp = c.$headerIndexed[ column ] && c.$headerIndexed[ column ][0].colSpan || 0; if ( tmp > 1 ) { - buildFilter += ' colspan="' + tmp + '"'; + buildFilter += '<td data-column="' + column + '-' + ( column + tmp - 1 ) + '" colspan="' + tmp + '"'; + } else { + buildFilter += '<td data-column="' + column + '"'; } if ( arry ) { buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' ); @@ -1047,7 +1049,8 @@ // assuming last cell of a column is the main column $header = c.$headerIndexed[ column ]; if ( $header && $header.length ) { - $filter = c.$filters.filter( '[data-column="' + column + '"]' ); + // $filter = c.$filters.filter( '[data-column="' + column + '"]' ); + $filter = tsf.getColumnElm( c, c.$filters, column ); ffxn = ts.getColumnData( table, wo.filter_functions, column ); makeSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || $header.hasClass( 'filter-select' ); @@ -1087,7 +1090,8 @@ name = ( $.isArray( wo.filter_cssFilter ) ? ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : wo.filter_cssFilter ) || ''; - buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column ); + // copy data-column from table cell (it will include colspan) + buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', $filter.attr( 'data-column' ) ); if ( disabled ) { buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; } @@ -1195,7 +1199,7 @@ // show/hide filter row as needed c.$table .find( '.' + tscss.filterRow ) - .trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); + .triggerHandler( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons @@ -1206,6 +1210,8 @@ c.lastCombinedFilter = null; c.lastSearch = []; } + // define filter inside it is false + filters = filters || []; // convert filters to strings - see #1070 filters = Array.prototype.map ? filters.map( String ) : @@ -1213,7 +1219,7 @@ filters.join( '\u0000' ).split( '\u0000' ); if ( wo.filter_initialized ) { - c.$table.trigger( 'filterStart', [ filters ] ); + c.$table.triggerHandler( 'filterStart', [ filters ] ); } if ( c.showProcessing ) { // give it time for the processing icon to kick in @@ -1294,22 +1300,18 @@ } return $input || $(); }, - multipleColumns: function( c, $input ) { + findRange: function( c, val, ignoreRanges ) { // look for multiple columns '1-3,4-6,8' in data-column var temp, ranges, range, start, end, singles, i, indx, len, - wo = c.widgetOptions, - // only target 'all' column inputs on initialization - // & don't target 'all' column inputs if they don't exist - targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, - columns = [], - val = $.trim( tsf.getLatestSearch( $input ).attr( 'data-column' ) || '' ); - if ( /^[0-9]+$/.test(val)) { - return parseInt( val, 10 ); + columns = []; + if ( /^[0-9]+$/.test( val ) ) { + // always return an array + return [ parseInt( val, 10 ) ]; } // process column range - if ( targets && /-/.test( val ) ) { + if ( !ignoreRanges && /-/.test( val ) ) { ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); - len = ranges.length; + len = ranges ? ranges.length : 0; for ( indx = 0; indx < len; indx++ ) { range = ranges[indx].split( /\s*-\s*/ ); start = parseInt( range[0], 10 ) || 0; @@ -1328,7 +1330,7 @@ } } // process single columns - if ( targets && /,/.test( val ) ) { + if ( !ignoreRanges && /,/.test( val ) ) { singles = val.split( /\s*,\s*/ ); len = singles.length; for ( i = 0; i < len; i++ ) { @@ -1348,6 +1350,23 @@ } return columns; }, + getColumnElm: function( c, $elements, column ) { + // data-column may contain multiple columns '1-3,5-6,8' + // replaces: c.$filters.filter( '[data-column="' + column + '"]' ); + return $elements.filter( function() { + var cols = tsf.findRange( c, $( this ).attr( 'data-column' ) ); + return $.inArray( column, cols ) > -1; + }); + }, + multipleColumns: function( c, $input ) { + // look for multiple columns '1-3,4-6,8' in data-column + var wo = c.widgetOptions, + // only target 'all' column inputs on initialization + // & don't target 'all' column inputs if they don't exist + targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, + val = $.trim( tsf.getLatestSearch( $input ).attr( 'data-column' ) || '' ); + return tsf.findRange( c, val, !targets ); + }, processTypes: function( c, data, vars ) { var ffxn, filterMatched = null, @@ -1461,6 +1480,11 @@ data.filter = ts.replaceAccents( data.filter ); } + // replace column specific default filters - see #1088 + if ( wo.filter_defaultFilter && tsfRegex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) { + data.filter = tsf.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); + } + // data.iFilter = case insensitive ( if wo.filter_ignoreCase is true ), // data.filter = case sensitive data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter; @@ -1749,7 +1773,8 @@ console.log( 'Completed filter widget search' + ts.benchmark(time) ); } if ( wo.filter_initialized ) { - c.$table.trigger( 'filterEnd', c ); + c.$table.triggerHandler( 'filterBeforeEnd', c ); + c.$table.triggerHandler( 'filterEnd', c ); } setTimeout( function() { ts.applyWidget( c.table ); // make sure zebra widget is applied @@ -2114,7 +2139,7 @@ c.lastCombinedFilter = null; c.lastSearch = []; tsf.searching( c.table, filter, skipFirst ); - c.$table.trigger( 'filterFomatterUpdate' ); + c.$table.triggerHandler( 'filterFomatterUpdate' ); } return !!valid; }; @@ -2162,7 +2187,7 @@ } } if ( headers.length && triggerEvent !== false ) { - c.$table.trigger( 'resize', [ headers ] ); + c.$table.triggerHandler( 'resize', [ headers ] ); } wo.resize_flag = false; }; @@ -2389,7 +2414,7 @@ } } - $table.trigger('stickyHeadersInit'); + $table.triggerHandler('stickyHeadersInit'); }, remove: function(table, c, wo) { @@ -2717,7 +2742,7 @@ } vars.mouseXPosition = event.pageX; // dynamically update sticky header widths - c.$table.trigger('stickyHeadersUpdate'); + c.$table.triggerHandler('stickyHeadersUpdate'); }, stopResize : function( c, wo ) { @@ -2731,7 +2756,7 @@ vars.mouseXPosition = 0; vars.$target = vars.$next = null; // will update stickyHeaders, just in case, see #912 - c.$table.trigger('stickyHeadersUpdate'); + c.$table.triggerHandler('stickyHeadersUpdate'); } }; @@ -2793,7 +2818,7 @@ } // reset stickyHeader widths - c.$table.trigger( 'stickyHeadersUpdate' ); + c.$table.triggerHandler( 'stickyHeadersUpdate' ); if ( ts.storage && !refreshing ) { ts.storage( this, ts.css.resizableStorage, {} ); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index bcddafa..1f151b0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,4 +1,4 @@ -/*! Parser: input & select - updated 11/22/2015 (v2.24.6) *//* +/*! Parser: input & select - updated 12/13/2015 (v2.25.0) *//* * for jQuery 1.7+ & tablesorter 2.7.11+ * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ @@ -54,23 +54,12 @@ }, format : function( txt, table, cell, cellIndex ) { var $cell = $( cell ), - $row = $cell.closest( 'tr' ), wo = table.config.widgetOptions, - checkedClass = table.config.checkboxClass || 'checked', // returning plain language here because this is what is shown in the // group headers - change it as desired status = wo.group_checkbox ? wo.group_checkbox : [ 'checked', 'unchecked' ], $input = $cell.find( 'input[type="checkbox"]' ), isChecked = $input.length ? $input[ 0 ].checked : ''; - // adding class to row, indicating that a checkbox is checked; includes - // a column index in case more than one checkbox happens to be in a row - $row.toggleClass( checkedClass + '-' + cellIndex, isChecked ); - if ( isChecked ) { - $row.addClass( checkedClass ); - } else if ( $row.length && !( $row[0].className || '' ).match( checkedClass + '-' ) ) { - // don't remove checked class if other columns have a check - $row.removeClass( checkedClass ); - } return $input.length ? status[ isChecked ? 0 : 1 ] : txt; }, parsed : true, // filter widget flag @@ -127,6 +116,34 @@ // if this code interferes somehow, target the specific table $('#mytable'), instead of $('table') $( function() { if ( !$.fn.on ) { return; } + var toggleRowClass = function( $row, checkboxClass, indx, isChecked ) { + // adding class to row, indicating that a checkbox is checked; includes + // a column index in case more than one checkbox happens to be in a row + $row.toggleClass( checkboxClass + '-' + indx, isChecked ); + // don't remove checked class if other columns have a check + if ( ( $row[0].className || '' ).match( checkboxClass + '-' ) ) { + $row.addClass( checkboxClass ); + } else { + $row.removeClass( checkboxClass ); + } + }, + updateHeaderCheckbox = function( $table, checkboxClass ) { + var $rows = $table.children( 'tbody' ).children( ':visible' ), // (include child rows?) + len = $rows.length; + // set indeterminate state on header checkbox + $table.children( 'thead' ).find( 'input[type="checkbox"]' ).each( function() { + var column = $( this ).closest( 'td, th' ).attr( 'data-column' ), + vis = $rows.filter( '.' + checkboxClass + '-' + column ).length, + allChecked = vis === len; + if ( vis === 0 || allChecked ) { + this.checked = allChecked; + this.indeterminate = false; + } else { + this.indeterminate = true; + } + }); + }; + $( 'table' ).on( 'tablesorter-initialized updateComplete', function() { this.tablesorterBusy = false; var namespace = '.parser-forms'; @@ -159,7 +176,7 @@ if ( event.type === 'change' || ( event.type === 'keyup' && event.which === 13 && ( event.target.nodeName === 'INPUT' || event.target.nodeName === 'TEXTAREA' && event.altKey ) ) ) { - var undef, + var undef, checkboxClass, $target = $( event.target ), isCheckbox = event.target.type === 'checkbox', $cell = $target.closest( 'td' ), @@ -169,9 +186,18 @@ busy = $table.length && $table[ 0 ].tablesorterBusy, $hdr = c && c.$headerIndexed && c.$headerIndexed[ indx ] || [], val = isCheckbox ? event.target.checked : $target.val(); - // abort if not a tablesorter table, or busy, or don't use updateCell if column is set - // to 'sorter-false' and 'filter-false', or column is set to 'parser-false' - if ( $.isEmptyObject( c ) || busy !== false || $hdr.length && ( $hdr.hasClass( 'parser-false' ) || + // abort if not a tablesorter table, or busy + if ( $.isEmptyObject( c ) || busy !== false ) { + return; + } + if ( isCheckbox ) { + checkboxClass = c.checkboxClass || 'checked'; + toggleRowClass( $cell.closest( 'tr' ), checkboxClass, indx, val ); + updateHeaderCheckbox( $table, checkboxClass ); + } + // don't use updateCell if column is set to 'sorter-false' and 'filter-false', + // or column is set to 'parser-false' + if ( $hdr.length && ( $hdr.hasClass( 'parser-false' ) || ( $hdr.hasClass( 'sorter-false' ) && $hdr.hasClass( 'filter-false' ) ) ) || // table already updating; get out of here, we might be in an endless loop (in IE)! See #971 ( event.type === 'change' && c.table.isUpdating ) ) { @@ -201,20 +227,8 @@ if ( !$.isEmptyObject( c ) ) { this.tablesorterBusy = true; checkboxClass = c && c.checkboxClass || 'checked'; - $rows = $table.children( 'tbody' ).children( ':visible' ); // (include child rows?) - len = $rows.length; // set indeterminate state on header checkbox - $( this ).children( 'thead' ).find( 'input[type="checkbox"]' ).each( function() { - var column = $( this ).closest( 'td, th' ).attr( 'data-column' ), - vis = $rows.filter( '.' + checkboxClass + '-' + column ).length, - allChecked = vis === len; - if ( vis === 0 || allChecked ) { - this.checked = allChecked; - this.indeterminate = false; - } else { - this.indeterminate = true; - } - }); + updateHeaderCheckbox( $table, checkboxClass ); this.tablesorterBusy = false; } }) @@ -222,13 +236,14 @@ .off( namespace ) // modified from http://jsfiddle.net/abkNM/6163/ .on( 'change' + namespace, 'input[type="checkbox"]', function( event ) { - var undef, onlyVisible, column, $target, + var undef, onlyVisible, column, $target, isParsed, $row, checkboxClass, $checkbox = $( this ), $table = $checkbox.closest( 'table' ), c = $table.length && $table[ 0 ].config, isChecked = this.checked; if ( $table.length && c && !$table[ 0 ].tablesorterBusy ) { column = parseInt( $checkbox.closest( 'td, th' ).attr( 'data-column' ), 10 ); + isParsed = c.parsers[ column ].id === 'checkbox'; onlyVisible = $table.length && c.checkboxVisible; $table[ 0 ].tablesorterBusy = true; // prevent "change" event from calling updateCell numerous times (see #971) $target = $table @@ -237,10 +252,25 @@ .children( ':nth-child(' + ( column + 1 ) + ')' ) .find( 'input[type="checkbox"]' ) .prop( 'checked', isChecked ); - $.tablesorter.update( c, undef, function() { + if ( !isParsed ) { + // add checkbox class names + checkboxClass = c.checkboxClass || 'checked'; + $target.each(function(){ + $row = $(this).closest('tr'); + toggleRowClass( $(this).closest( 'tr' ), checkboxClass, column, isChecked ); + }); + updateHeaderCheckbox( $table, checkboxClass ); updateServer( event, $table, $target ); $table[ 0 ].tablesorterBusy = false; - }); + } else { + // only update cache if checkboxes are being sorted + $.tablesorter.update( c, undef, function() { + updateServer( event, $table, $target ); + $table[ 0 ].tablesorterBusy = false; + }); + } + // needed for IE8 + return true; } // update already going on, don't do anything! return false; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js index 1894481..628f1d0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js @@ -132,7 +132,7 @@ format : function(table, c, wo){ // reinitialize in case table is empty when first initialized if (!wo.alignChar_initialized) { - c.$table.trigger('refreshAlign'); + c.$table.triggerHandler('refreshAlign'); } }, remove : function(table, c, wo, refreshing){ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js index 03dd95d..cf08752 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js @@ -192,7 +192,7 @@ }; bt.buildComplete = function(table, wo){ - $(table).trigger(wo.build_complete); + $(table).triggerHandler(wo.build_complete); ts.setup(table, table.config); }; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index f113ca5..563bb7b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -230,7 +230,7 @@ tsColSel.adjustColspans( c, wo ); // trigger columnUpdate if auto is true (it gets skipped in updateCols() if (colSel.auto) { - c.$table.trigger(wo.columnSelector_updated); + c.$table.triggerHandler(wo.columnSelector_updated); } }, addSelectors: function( prefix, column ) { @@ -318,7 +318,7 @@ ts.storage( c.$table[0], 'tablesorter-columnSelector', colSel.states ); } tsColSel.adjustColspans( c, wo ); - c.$table.trigger(wo.columnSelector_updated); + c.$table.triggerHandler(wo.columnSelector_updated); }, setUpColspan: function(c, wo) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js index 62d25be..c573f38 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js @@ -1,4 +1,4 @@ -/*! Widget: editable - updated 10/31/2015 (v2.24.0) *//* +/*! Widget: editable - updated 12/13/2015 (v2.25.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -222,17 +222,20 @@ .data( 'before', valid ) .data( 'original', valid ) .trigger( 'change' ); - $.tablesorter.updateCell( c, $this.closest( 'td' ), false, function() { - if ( wo.editable_autoResort ) { - setTimeout( function() { - $.tablesorter.sortOn( c, c.sortList, function() { - tse.editComplete( c, wo, c.$table.data( 'contentFocused' ), true ); - }, true ); - }, 10 ); - } else { - tse.editComplete( c, wo, c.$table.data( 'contentFocused' ) ); - } - }); + // prevent error if table was destroyed - see #1099 + if ( c.table.hasInitialized ) { + $.tablesorter.updateCell( c, $this.closest( 'td' ), false, function() { + if ( wo.editable_autoResort ) { + setTimeout( function() { + $.tablesorter.sortOn( c, c.sortList, function() { + tse.editComplete( c, wo, c.$table.data( 'contentFocused' ), true ); + }, true ); + }, 10 ); + } else { + tse.editComplete( c, wo, c.$table.data( 'contentFocused' ) ); + } + }); + } return false; } } else if ( !valid && e.type !== 'keydown' ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js index 84e556d..e412a4b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js @@ -1,4 +1,4 @@ -/*! Widget: filter, insideRange filter type - updated 11/22/2015 (v2.24.6) */ +/*! Widget: filter, insideRange filter type - updated 12/10/2015 (v2.25.0) */ ;(function($){ 'use strict'; @@ -15,14 +15,15 @@ }; ts.filter.types.insideRange = function( c, data ) { - if ( isDigit.test( data.iFilter ) && range.test( data.iExact ) ) { + // don't look for an inside range if "any" match is enabled... multiple "-" really screw things up + if ( !data.anyMatch && isDigit.test( data.iFilter ) && range.test( data.iExact ) ) { var t, val, low, high, index = data.index, cell = data.$cells[ index ], parts = data.iExact.split( range ), - format = c.parsers[data.index].format; - // the cell does not contain a range - if ( parts && parts.length < 2 ) { + format = c.parsers[data.index] && c.parsers[data.index].format; + // the cell does not contain a range or the parser isn't defined + if ( parts && parts.length < 2 || typeof format !== 'function' ) { return null; } // format each side part of the range using the assigned parser diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index d247f23..927c232 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 11/10/2015 (v2.24.4) *//* +/*! Widget: filter - updated 12/13/2015 (v2.25.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -208,7 +208,7 @@ table = c.table, parsed = data.parsed[ data.index ], query = ts.formatFloat( data.iFilter.replace( tsfRegex.operators, '' ), table ), - parser = c.parsers[ data.index ], + parser = c.parsers[ data.index ] || {}, savedSearch = query; // parse filter value in case we're comparing numbers ( dates ) if ( parsed || parser.type === 'numeric' ) { @@ -348,6 +348,7 @@ var options, string, txt, $header, column, filters, val, fxn, noSelect; c.$table.addClass( 'hasFilters' ); + c.lastSearch = []; // define timers so using clearTimeout won't cause an undefined error wo.filter_searchTimer = null; @@ -430,7 +431,7 @@ if ( wo.filter_reset instanceof $ ) { // reset contains a jQuery object, bind to it wo.filter_reset.click( function() { - c.$table.trigger( 'filterReset' ); + c.$table.triggerHandler( 'filterReset' ); }); } else if ( $( wo.filter_reset ).length ) { // reset is a jQuery selector, use event delegation @@ -438,7 +439,7 @@ .undelegate( wo.filter_reset, 'click' + c.namespace + 'filter' ) .delegate( wo.filter_reset, 'click' + c.namespace + 'filter', function() { // trigger a reset event, so other functions ( filter_formatter ) know when to reset - c.$table.trigger( 'filterReset' ); + c.$table.triggerHandler( 'filterReset' ); }); } } @@ -540,7 +541,7 @@ ts.setFilters( table, filters, true ); } } - c.$table.trigger( 'filterFomatterUpdate' ); + c.$table.triggerHandler( 'filterFomatterUpdate' ); // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers setTimeout( function() { if ( !wo.filter_initialized ) { @@ -550,7 +551,7 @@ }); // if filter widget is added after pager has initialized; then set filter init flag if ( c.pager && c.pager.initialized && !wo.filter_initialized ) { - c.$table.trigger( 'filterFomatterUpdate' ); + c.$table.triggerHandler( 'filterFomatterUpdate' ); setTimeout( function() { tsf.filterInitComplete( c ); }, 100 ); @@ -573,7 +574,7 @@ count = 0, completed = function() { wo.filter_initialized = true; - c.$table.trigger( 'filterInit', c ); + c.$table.triggerHandler( 'filterInit', c ); tsf.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); }; if ( $.isEmptyObject( wo.filter_formatter ) ) { @@ -628,7 +629,7 @@ for ( indx = 0; indx <= c.columns; indx++ ) { // include data-column='all' external filters col = indx === c.columns ? 'all' : indx; - filters[indx] = $filters + filters[ indx ] = $filters .filter( '[data-column="' + col + '"]' ) .attr( wo.filter_defaultAttrib ) || filters[indx] || ''; } @@ -650,11 +651,12 @@ buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; for ( column = 0; column < columns; column++ ) { if ( c.$headerIndexed[ column ].length ) { - buildFilter += '<td data-column="' + column + '"'; // account for entire column set with colspan. See #1047 tmp = c.$headerIndexed[ column ] && c.$headerIndexed[ column ][0].colSpan || 0; if ( tmp > 1 ) { - buildFilter += ' colspan="' + tmp + '"'; + buildFilter += '<td data-column="' + column + '-' + ( column + tmp - 1 ) + '" colspan="' + tmp + '"'; + } else { + buildFilter += '<td data-column="' + column + '"'; } if ( arry ) { buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' ); @@ -673,7 +675,8 @@ // assuming last cell of a column is the main column $header = c.$headerIndexed[ column ]; if ( $header && $header.length ) { - $filter = c.$filters.filter( '[data-column="' + column + '"]' ); + // $filter = c.$filters.filter( '[data-column="' + column + '"]' ); + $filter = tsf.getColumnElm( c, c.$filters, column ); ffxn = ts.getColumnData( table, wo.filter_functions, column ); makeSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || $header.hasClass( 'filter-select' ); @@ -713,7 +716,8 @@ name = ( $.isArray( wo.filter_cssFilter ) ? ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : wo.filter_cssFilter ) || ''; - buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column ); + // copy data-column from table cell (it will include colspan) + buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', $filter.attr( 'data-column' ) ); if ( disabled ) { buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; } @@ -821,7 +825,7 @@ // show/hide filter row as needed c.$table .find( '.' + tscss.filterRow ) - .trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); + .triggerHandler( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons @@ -832,6 +836,8 @@ c.lastCombinedFilter = null; c.lastSearch = []; } + // define filter inside it is false + filters = filters || []; // convert filters to strings - see #1070 filters = Array.prototype.map ? filters.map( String ) : @@ -839,7 +845,7 @@ filters.join( '\u0000' ).split( '\u0000' ); if ( wo.filter_initialized ) { - c.$table.trigger( 'filterStart', [ filters ] ); + c.$table.triggerHandler( 'filterStart', [ filters ] ); } if ( c.showProcessing ) { // give it time for the processing icon to kick in @@ -920,22 +926,18 @@ } return $input || $(); }, - multipleColumns: function( c, $input ) { + findRange: function( c, val, ignoreRanges ) { // look for multiple columns '1-3,4-6,8' in data-column var temp, ranges, range, start, end, singles, i, indx, len, - wo = c.widgetOptions, - // only target 'all' column inputs on initialization - // & don't target 'all' column inputs if they don't exist - targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, - columns = [], - val = $.trim( tsf.getLatestSearch( $input ).attr( 'data-column' ) || '' ); - if ( /^[0-9]+$/.test(val)) { - return parseInt( val, 10 ); + columns = []; + if ( /^[0-9]+$/.test( val ) ) { + // always return an array + return [ parseInt( val, 10 ) ]; } // process column range - if ( targets && /-/.test( val ) ) { + if ( !ignoreRanges && /-/.test( val ) ) { ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); - len = ranges.length; + len = ranges ? ranges.length : 0; for ( indx = 0; indx < len; indx++ ) { range = ranges[indx].split( /\s*-\s*/ ); start = parseInt( range[0], 10 ) || 0; @@ -954,7 +956,7 @@ } } // process single columns - if ( targets && /,/.test( val ) ) { + if ( !ignoreRanges && /,/.test( val ) ) { singles = val.split( /\s*,\s*/ ); len = singles.length; for ( i = 0; i < len; i++ ) { @@ -974,6 +976,23 @@ } return columns; }, + getColumnElm: function( c, $elements, column ) { + // data-column may contain multiple columns '1-3,5-6,8' + // replaces: c.$filters.filter( '[data-column="' + column + '"]' ); + return $elements.filter( function() { + var cols = tsf.findRange( c, $( this ).attr( 'data-column' ) ); + return $.inArray( column, cols ) > -1; + }); + }, + multipleColumns: function( c, $input ) { + // look for multiple columns '1-3,4-6,8' in data-column + var wo = c.widgetOptions, + // only target 'all' column inputs on initialization + // & don't target 'all' column inputs if they don't exist + targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, + val = $.trim( tsf.getLatestSearch( $input ).attr( 'data-column' ) || '' ); + return tsf.findRange( c, val, !targets ); + }, processTypes: function( c, data, vars ) { var ffxn, filterMatched = null, @@ -1087,6 +1106,11 @@ data.filter = ts.replaceAccents( data.filter ); } + // replace column specific default filters - see #1088 + if ( wo.filter_defaultFilter && tsfRegex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) { + data.filter = tsf.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); + } + // data.iFilter = case insensitive ( if wo.filter_ignoreCase is true ), // data.filter = case sensitive data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter; @@ -1375,7 +1399,8 @@ console.log( 'Completed filter widget search' + ts.benchmark(time) ); } if ( wo.filter_initialized ) { - c.$table.trigger( 'filterEnd', c ); + c.$table.triggerHandler( 'filterBeforeEnd', c ); + c.$table.triggerHandler( 'filterEnd', c ); } setTimeout( function() { ts.applyWidget( c.table ); // make sure zebra widget is applied @@ -1740,7 +1765,7 @@ c.lastCombinedFilter = null; c.lastSearch = []; tsf.searching( c.table, filter, skipFirst ); - c.$table.trigger( 'filterFomatterUpdate' ); + c.$table.triggerHandler( 'filterFomatterUpdate' ); } return !!valid; }; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index 4cbcde6..8691995 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -123,7 +123,7 @@ tsg.findColumnGroups( c, wo, data ); tsg.processHeaders( c, wo, data ); - c.$table.trigger(wo.group_complete); + c.$table.triggerHandler(wo.group_complete); } }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index 69cf5c0..587c930 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -1,4 +1,4 @@ -/*! Widget: math - updated 11/22/2015 (v2.24.6) *//* +/*! Widget: math - updated 12/13/2015 (v2.25.0) *//* * Requires tablesorter v2.16+ and jQuery 1.7+ * by Rob Garrison */ @@ -22,37 +22,36 @@ // name = function returning invalid results // errorIndex = math.error index with an explanation of the error console.log( name, math.error[ errorIndex ] ); - return c && c.widgetOptions.math_none || 'none'; // text for cell + return c && c.widgetOptions.math_none || ''; // text for cell }, - events : ( 'tablesorter-initialized update updateAll updateRows addRows updateCell ' + - 'filterReset filterEnd ' ).split(' ').join('.tsmath '), + events : ( 'tablesorter-initialized update updateAll updateRows addRows updateCell filterReset ' ) + .split(' ').join('.tsmath '), processText : function( c, $cell ) { - var txt = $cell.attr( c.textAttribute ); - if ( typeof txt === 'undefined' ) { - txt = $cell[0].textContent || $cell.text(); - } + var txt = ts.getElementText( c, $cell, math.getCellIndex( $cell ) ); txt = ts.formatFloat( txt.replace( /[^\w,. \-()]/g, '' ), c.table ) || 0; // isNaN('') => false return isNaN( txt ) ? 0 : txt; }, // get all of the row numerical values in an arry - getRow : function( c, $el ) { + getRow : function( c, $el, hasFilter ) { var $cells, wo = c.widgetOptions, arry = [], $row = $el.closest( 'tr' ), - isFiltered = $row.hasClass( wo.filter_filteredRow || 'filtered' ), - hasFilter = wo.math_rowFilter; + isFiltered = $row.hasClass( wo.filter_filteredRow || 'filtered' ); if ( hasFilter ) { $row = $row.filter( hasFilter ); } - if ( !isFiltered || hasFilter ) { + if ( hasFilter || !isFiltered ) { $cells = $row.children().not( '[' + wo.math_dataAttrib + '=ignore]' ); if ( wo.math_ignore.length ) { - $cells = $cells.not( '[data-column=' + wo.math_ignore.join( '],[data-column=' ) + ']' ); + $cells = $cells.filter( function( indx ) { + // using $.inArray is not optimal (needed for IE8) + return $.inArray( math.getCellIndex( $( this ) ), wo.math_ignore ) === -1; + }); } arry = $cells.not( $el ).map( function() { return math.processText( c, $( this ) ); @@ -62,31 +61,38 @@ }, // get all of the column numerical values in an arry - getColumn : function( c, $el, type ) { + getColumn : function( c, $el, type, hasFilter ) { var index, $t, $tr, len, $mathRows, mathAbove, - arry = [], wo = c.widgetOptions, - hasFilter = wo.math_rowFilter, + arry = [], + $row = $el.closest( 'tr' ), mathAttr = wo.math_dataAttrib, + mathIgnore = '[' + mathAttr + '=ignore]', filtered = wo.filter_filteredRow || 'filtered', - cIndex = parseInt( $el.attr( 'data-column' ), 10 ), + cIndex = math.getCellIndex( $el ), + // get all rows to keep row indexing $rows = c.$table.children( 'tbody' ).children(), - $row = $el.closest( 'tr' ); - // make sure tfoot rows are AFTER the tbody rows - // $rows.add( c.$table.children( 'tfoot' ).children() ); + mathAttrs = [ + '[' + mathAttr + '^=above]', + '[' + mathAttr + '^=below]', + '[' + mathAttr + '^=col]', + '[' + mathAttr + '^=all]' + ]; if ( type === 'above' ) { len = $rows.index( $row ); index = len; while ( index >= 0 ) { $tr = $rows.eq( index ); + mathAbove = $tr.children().filter( mathAttrs[0] ).length; if ( hasFilter ) { - $tr = $tr.filter( wo.math_rowFilter ); + $tr = $tr.filter( hasFilter ); } - $t = $tr.children().filter( '[data-column=' + cIndex + ']' ); - mathAbove = $t.filter( '[' + mathAttr + '^=above]' ).length; + $t = $tr.children().filter( function( indx ) { + return math.getCellIndex( $( this ) ) === cIndex; + }); // ignore filtered rows & rows with data-math="ignore" (and starting row) - if ( ( ( !$tr.hasClass( filtered ) || hasFilter ) && - $tr.not( '[' + mathAttr + '=ignore]' ).length && + if ( ( ( hasFilter || !$tr.hasClass( filtered ) ) && + $tr.not( mathIgnore ).length && index !== len ) || mathAbove && index !== len ) { // stop calculating 'above', when encountering another 'above' @@ -103,31 +109,34 @@ // index + 1 to ignore starting node for ( index = $rows.index( $row ) + 1; index < len; index++ ) { $tr = $rows.eq( index ); + if ( $tr.children().filter( mathAttrs[1] ).length ) { + break; + } if ( hasFilter ) { $tr = $tr.filter( hasFilter ); } - $t = $tr.children().filter( '[data-column=' + cIndex + ']' ); - if ( $t.filter( '[' + mathAttr + '^=below]' ).length ) { - break; - } - if ( ( !$tr.hasClass( filtered ) || hasFilter ) && - $tr.not( '[' + mathAttr + '=ignore]' ).length && - $t.length ) { + $t = $tr.children().filter( function( indx ) { + return math.getCellIndex( $( this ) ) === cIndex; + }); + if ( ( hasFilter || !$tr.hasClass( filtered ) ) && + $tr.not( mathIgnore ).length && + $t.length ) { arry.push( math.processText( c, $t ) ); } } - } else { - $mathRows = $rows.not( '[' + mathAttr + '=ignore]' ); + $mathRows = $rows.not( mathIgnore ); len = $mathRows.length; for ( index = 0; index < len; index++ ) { $tr = $mathRows.eq( index ); if ( hasFilter ) { $tr = $tr.filter( hasFilter ); } - $t = $tr.children().filter( '[data-column=' + cIndex + ']' ); - if ( ( !$tr.hasClass( filtered ) || hasFilter ) && - $t.not( '[' + mathAttr + '^=above],[' + mathAttr + '^=below],[' + mathAttr + '^=col]' ).length && + $t = $tr.children().filter( function( indx ) { + return math.getCellIndex( $( this ) ) === cIndex; + }); + if ( ( hasFilter || !$tr.hasClass( filtered ) ) && + $t.not( mathAttrs.join( ',' ) ).length && !$t.is( $el ) ) { arry.push( math.processText( c, $t ) ); } @@ -137,27 +146,27 @@ }, // get all of the column numerical values in an arry - getAll : function( c ) { + getAll : function( c, hasFilter ) { var $t, col, $row, rowIndex, rowLen, $cells, cellIndex, cellLen, arry = [], wo = c.widgetOptions, mathAttr = wo.math_dataAttrib, + mathIgnore = '[' + mathAttr + '=ignore]', filtered = wo.filter_filteredRow || 'filtered', - hasFilter = wo.filter_rowFilter, - $rows = c.$table.children( 'tbody' ).children().not( '[' + mathAttr + '=ignore]' ); + $rows = c.$table.children( 'tbody' ).children().not( mathIgnore ); rowLen = $rows.length; for ( rowIndex = 0; rowIndex < rowLen; rowIndex++ ) { $row = $rows.eq( rowIndex ); if ( hasFilter ) { $row = $row.filter( hasFilter ); } - if ( !$row.hasClass( filtered ) || hasFilter ) { - $cells = $row.children().not( '[' + mathAttr + '=ignore]' ); + if ( hasFilter || !$row.hasClass( filtered ) ) { + $cells = $row.children().not( mathIgnore ); cellLen = $cells.length; // $row.children().each(function(){ for ( cellIndex = 0; cellIndex < cellLen; cellIndex++ ) { $t = $cells.eq( cellIndex ); - col = parseInt( $t.attr( 'data-column' ), 10); + col = math.getCellIndex( $t ); if ( !$t.filter( '[' + mathAttr + ']' ).length && $.inArray( col, wo.math_ignore ) < 0 ) { arry.push( math.processText( c, $t ) ); } @@ -170,17 +179,48 @@ setColumnIndexes : function( c ) { c.$table.after( '<div id="_tablesorter_table_placeholder"></div>' ); // detach table from DOM to speed up column indexing - var $table = c.$table.detach(); - ts.computeColumnIndex( $table.children( 'tbody' ).children() ); + var $table = c.$table.detach(), + last = 1, + // only target rows with a colspan or rows included in a rowspan + $rows = $table.children( 'tbody' ).children().filter( function() { + var cells, indx, len, + $this = $( this ), + include = $this.children( '[colspan]' ).length > 0; + if ( last > 1 ) { + last--; + include = true; + } else if ( last < 1 ) { + last = 1; + } + if ( $this.children( '[rowspan]' ).length > 0 ) { + cells = this.cells; + // find max rowspan (in case more than one cell has a rowspan) + for ( indx = 0; indx < cells.length; indx++ ) { + last = Math.max( cells[ indx ].rowSpan, last ); + } + } + return include; + }); + // pass `c` (table.config) to computeColumnIndex so it won't add a data-column + // to every tbody cell, just the ones where the .cellIndex property doesn't match + // the calculated cell index - hopefully fixes the lag issue in #1048 + ts.computeColumnIndex( $rows, c ); $( '#_tablesorter_table_placeholder' ) .after( $table ) .remove(); }, + getCellIndex : function( $cell ) { + var indx = $cell.attr( 'data-column' ); + return typeof indx === 'undefined' ? $cell[0].cellIndex : parseInt( indx, 10 ); + }, + recalculate : function(c, wo, init) { if ( c && ( !wo.math_isUpdating || init ) ) { - var undef, time, mathAttr, $mathCells; + var undef, time, mathAttr, $mathCells, indx, len, + changed = false, + filters = {}; if ( c.debug ) { time = new Date(); } @@ -196,7 +236,7 @@ // all non-info tbody cells mathAttr = wo.math_dataAttrib; $mathCells = c.$tbodies.children( 'tr' ).children( '[' + mathAttr + ']' ); - math.mathType( c, $mathCells, wo.math_priority ); + changed = math.mathType( c, $mathCells, wo.math_priority ) || changed; // only info tbody cells $mathCells = c.$table @@ -207,20 +247,33 @@ // find the 'all' total $mathCells = c.$table.children().children( 'tr' ).children( '[' + mathAttr + '^=all]' ); - math.mathType( c, $mathCells, [ 'all' ] ); + len = $mathCells.length; + // get math filter, if any + // hasFilter = $row.attr( mathAttr + '-filter' ) || wo.math_rowFilter; + $mathCells.each( function( indx, cell ) { + var $cell = $( cell ), + filter = $mathCells.eq( indx ).attr( mathAttr + '-filter' ) || wo.math_rowFilter; + filters[ filter ] = filters[ filter ] ? filters[ filter ].add( $cell ) : $cell; + }); + $.each( filters, function( hasFilter, $cells ) { + changed = math.mathType( c, $cells, [ 'all' ], hasFilter ) || changed; + }); - wo.math_isUpdating = true; - if ( c.debug ) { - console[ console.group ? 'group' : 'log' ]( 'Math widget triggering an update after recalculation' ); - } + // trigger an update only if cells inside the tbody changed + if ( changed ) { + wo.math_isUpdating = true; + if ( c.debug ) { + console[ console.group ? 'group' : 'log' ]( 'Math widget triggering an update after recalculation' ); + } - // update internal cache - ts.update( c, undef, function(){ - math.updateComplete( c ); - }); + // update internal cache + ts.update( c, undef, function(){ + math.updateComplete( c ); + }); - if ( c.debug ) { - console.log( 'Math widget update completed' + ts.benchmark( time ) ); + if ( c.debug ) { + console.log( 'Math widget update completed' + ts.benchmark( time ) ); + } } } }, @@ -231,23 +284,25 @@ wo.math_isUpdating = false; }, - mathType : function( c, $cells, priority ) { + mathType : function( c, $cells, priority, hasFilter ) { if ( $cells.length ) { - var formula, result, $el, arry, getAll, $targetCells, index, len, + var getAll, + changed = false, wo = c.widgetOptions, mathAttr = wo.math_dataAttrib, equations = ts.equations; if ( priority[0] === 'all' ) { - // no need to get all cells more than once - getAll = math.getAll( c ); + // mathType is called multiple times if more than one "hasFilter" is used + getAll = math.getAll( c, hasFilter ); } if (c.debug) { console[ console.group ? 'group' : 'log' ]( 'Tablesorter Math widget recalculation' ); } // $.each is okay here... only 4 priorities $.each( priority, function( i, type ) { - $targetCells = $cells.filter( '[' + mathAttr + '^=' + type + ']' ); - len = $targetCells.length; + var index, arry, formula, result, $el, + $targetCells = $cells.filter( '[' + mathAttr + '^=' + type + ']' ), + len = $targetCells.length; if ( len ) { if (c.debug) { console[ console.group ? 'group' : 'log' ]( type ); @@ -258,39 +313,58 @@ if ( $el.parent().hasClass( wo.filter_filteredRow || 'filtered' ) ) { continue; } + hasFilter = $el.attr( mathAttr + '-filter' ) || wo.math_rowFilter; formula = ( $el.attr( mathAttr ) || '' ).replace( type + '-', '' ); - arry = ( type === 'row' ) ? math.getRow( c, $el ) : - ( type === 'all' ) ? getAll : math.getColumn( c, $el, type ); + arry = ( type === 'row' ) ? math.getRow( c, $el, hasFilter ) : + ( type === 'all' ) ? getAll : math.getColumn( c, $el, type, hasFilter ); if ( equations[ formula ] ) { if ( arry.length ) { result = equations[ formula ]( arry, c ); if ( c.debug ) { - console.log( $el.attr( mathAttr ), arry, '=', result ); + console.log( $el.attr( mathAttr ), hasFilter ? '("' + hasFilter + '")' : '', arry, '=', result ); } } else { // mean will return a divide by zero error, everything else shows an undefined error result = math.invalid( c, formula, formula === 'mean' ? 0 : 'undef' ); } - math.output( $el, wo, result, arry ); + changed = math.output( $el, c, result, arry ) || changed; } } if ( c.debug && console.groupEnd ) { console.groupEnd(); } } }); if ( c.debug && console.groupEnd ) { console.groupEnd(); } + return changed; } + return false; }, - output : function( $cell, wo, value, arry ) { + output : function( $cell, c, value, arry ) { // get mask from cell data-attribute: data-math-mask="#,##0.00" - var mask = $cell.attr( 'data-' + wo.math_data + '-mask' ) || wo.math_mask, + var $el, + wo = c.widgetOptions, + changed = false, + prev = $cell.html(), + mask = $cell.attr( 'data-' + wo.math_data + '-mask' ) || wo.math_mask, result = ts.formatMask( mask, value, wo.math_wrapPrefix, wo.math_wrapSuffix ); if ( typeof wo.math_complete === 'function' ) { result = wo.math_complete( $cell, wo, result, value, arry ); } if ( result !== false ) { + changed = prev !== result; $cell.html( result ); } + // check if in a regular tbody, otherwise don't pass a changed flag + // to prevent unnecessary updating of the table cache + if ( changed ) { + $el = $cell.closest( 'tbody' ); + // content was changed in a tfoot, info-only tbody or the resulting tbody is in a nested table + // then don't signal a change + if ( !$el.length || $el.hasClass( c.cssInfoBlock ) || $el.parent()[0] !== c.table ) { + return false; + } + } + return changed; } }; @@ -525,20 +599,24 @@ }, init : function( table, thisWidget, c, wo ) { // filterEnd fires after updateComplete - var update = ts.hasWidget( table, 'filter' ) ? 'filterEnd' : 'updateComplete'; + var update = ( ts.hasWidget( table, 'filter' ) ? 'filterEnd' : 'updateComplete' ) + '.tsmath'; + // filterEnd is when the pager hides rows... so bind to pagerComplete + math.events += ( ts.hasWidget( table, 'pager' ) ? 'pagerComplete' : 'filterEnd' ) + '.tsmath '; c.$table - .off( ( math.events + ' updateComplete.tsmath ' + wo.math_event ).replace( /\s+/g, ' ' ) ) - .on( math.events + ' ' + wo.math_event, function( e ) { + .off( ( math.events + 'updateComplete.tsmath ' + wo.math_event ).replace( /\s+/g, ' ' ) ) + .on( math.events + wo.math_event, function( e ) { + if ( !this.hasInitialized ) { return; } var init = e.type === 'tablesorter-initialized'; if ( !wo.math_isUpdating || init ) { - if ( !/filter/.test( e.type ) ) { + // don't setColumnIndexes on init here, or it gets done twice + if ( !/filter/.test( e.type ) && !init ) { // redo data-column indexes on update - math.setColumnIndexes( c ) ; + math.setColumnIndexes( c ); } math.recalculate( c, wo, init ); } }) - .on( update + '.tsmath', function() { + .on( update, function() { setTimeout( function(){ math.updateComplete( c ); }, 40 ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index c0a09b4..57e0b7a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -193,7 +193,7 @@ // clear initialized flag p.initialized = false; // before initialization event - c.$table.trigger( 'pagerBeforeInitialized', c ); + c.$table.triggerHandler( 'pagerBeforeInitialized', c ); tsp.enablePager( c, false ); @@ -232,7 +232,7 @@ if ( c.debug ) { console.log( 'Pager: Triggering pagerInitialized' ); } - c.$table.trigger( 'pagerInitialized', c ); + c.$table.triggerHandler( 'pagerInitialized', c ); // filter widget not initialized; it will update the output display & fire off the pagerComplete event if ( !( c.widgetOptions.filter_initialized && ts.hasWidget( c.table, 'filter' ) ) ) { // if ajax, then don't fire off pagerComplete @@ -504,7 +504,7 @@ .on( 'change' + namespace, function() { var v = $( this ).val(), pg = $( this ).hasClass( 'ts-startRow' ) ? Math.floor( v / p.size ) + 1 : v; - c.$table.trigger( 'pageSet' + namespace, [ pg ] ); + c.$table.triggerHandler( 'pageSet' + namespace, [ pg ] ); }); } } @@ -514,7 +514,7 @@ if ( c.debug ) { console.log( 'Pager: Triggering pagerComplete' ); } - c.$table.trigger( 'pagerComplete', c ); + c.$table.triggerHandler( 'pagerComplete', c ); // save pager info to storage if ( wo.pager_savePages && ts.storage ) { ts.storage( table, wo.pager_storageKey, { @@ -811,7 +811,7 @@ if ( c.debug ) { console.log( 'Pager: Triggering pagerChange' ); } - $table.trigger( 'pagerChange', p ); + $table.triggerHandler( 'pagerChange', p ); ts.applyWidget( table ); tsp.updatePageDisplay( c ); }, 0 ); @@ -928,7 +928,7 @@ if ( c.debug ) { console.log( 'Pager: Triggering pagerChange' ); } - c.$table.trigger( 'pagerChange', c ); + c.$table.triggerHandler( 'pagerChange', c ); } if ( !wo.pager_removeRows ) { tsp.hideRows( c ); @@ -961,7 +961,7 @@ if ( c.debug ) { console.log( 'Pager: Triggering updateComplete' ); } - c.$table.trigger( 'updateComplete', [ table, true ] ); + c.$table.triggerHandler( 'updateComplete', [ table, true ] ); } }, @@ -1077,13 +1077,13 @@ if ( c.debug ) { console.log( 'Pager: Triggering pageMoved' ); } - c.$table.trigger( 'pageMoved', c ); + c.$table.triggerHandler( 'pageMoved', c ); ts.applyWidget( table ); if ( !p.ajax && table.isUpdating ) { if ( c.debug ) { console.log( 'Pager: Triggering updateComplete' ); } - c.$table.trigger( 'updateComplete', [ table, true ] ); + c.$table.triggerHandler( 'updateComplete', [ table, true ] ); } } }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js index a3d6785..47b7beb 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js @@ -1,4 +1,4 @@ -/* Widget: print - updated 11/22/2015 (v2.24.6) *//* +/* Widget: print - updated 12/13/2015 (v2.25.0) *//* * Requires tablesorter v2.8+ and jQuery 1.2.6+ */ /*jshint browser:true, jquery:true, unused:false */ @@ -12,7 +12,7 @@ event : 'printTable', basicStyle : 'table, tr, td, th { border : solid 1px black; border-collapse : collapse; } td, th { padding: 2px; }', - popupStyle : 'width=500,height=300', + popupStyle : 'width=500,height=300,scrollbars=1,resizable=1', init : function(c) { c.$table diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js index eacc3b4..c2a8dd5 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js @@ -305,7 +305,7 @@ } vars.mouseXPosition = event.pageX; // dynamically update sticky header widths - c.$table.trigger('stickyHeadersUpdate'); + c.$table.triggerHandler('stickyHeadersUpdate'); }, stopResize : function( c, wo ) { @@ -319,7 +319,7 @@ vars.mouseXPosition = 0; vars.$target = vars.$next = null; // will update stickyHeaders, just in case, see #912 - c.$table.trigger('stickyHeadersUpdate'); + c.$table.triggerHandler('stickyHeadersUpdate'); } }; @@ -381,7 +381,7 @@ } // reset stickyHeader widths - c.$table.trigger( 'stickyHeadersUpdate' ); + c.$table.triggerHandler( 'stickyHeadersUpdate' ); if ( ts.storage && !refreshing ) { ts.storage( this, ts.css.resizableStorage, {} ); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index 6126eba..82a8730 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -460,7 +460,7 @@ // update resizable widget handles setTimeout( function() { - c.$table.trigger( 'resizableUpdate' ); + c.$table.triggerHandler( 'resizableUpdate' ); }, 100 ); }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js index 58febce..924e891 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js @@ -27,7 +27,7 @@ c.$table.one( 'tablesorter-ready', function() { setTimeout(function(){ c.$table.one( 'filterEnd', function(){ - $(this).trigger( 'pageAndSize', [ page, size ] ); + $(this).triggerHandler( 'pageAndSize', [ page, size ] ); }); $.tablesorter.setFilters( table, filter, true ); }, 100 ); @@ -36,7 +36,7 @@ } if ( !filter ) { c.$table.one( 'tablesorter-ready', function() { - c.$table.trigger( 'pageAndSize', [ page, size ] ); + c.$table.triggerHandler( 'pageAndSize', [ page, size ] ); }); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js index d72a220..a68abd3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js @@ -109,7 +109,7 @@ $.tablesorter.processTbody(table, $tbody, false); // restore tbody }); - c.$table.trigger('staticRowsComplete', table); + c.$table.triggerHandler('staticRowsComplete', table); }, remove : function(table, c, wo){ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js index b697f1a..695f6d5 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js @@ -39,7 +39,7 @@ } } if ( headers.length && triggerEvent !== false ) { - c.$table.trigger( 'resize', [ headers ] ); + c.$table.triggerHandler( 'resize', [ headers ] ); } wo.resize_flag = false; }; @@ -266,7 +266,7 @@ } } - $table.trigger('stickyHeadersInit'); + $table.triggerHandler('stickyHeadersInit'); }, remove: function(table, c, wo) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-view.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-view.js index 4169675..3cdc96d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-view.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-view.js @@ -148,10 +148,10 @@ view.hideTable(c, wo); } - $(c.$table).on('tablesorter-ready', function() { + c.$table.on('tablesorter-ready', function() { view.buildToolBar(c, wo); view.buildView(c, wo); - $(c.$table).trigger('viewComplete'); + c.$table.triggerHandler('viewComplete'); }); }, From 5b44aab3639ac260b53b8864d410a0c1763f01d4 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Sat, 19 Dec 2015 10:26:38 +0100 Subject: [PATCH 076/138] Dependency: compatible with railties 5 --- jquery-tablesorter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jquery-tablesorter.gemspec b/jquery-tablesorter.gemspec index 5786d02..d8adef5 100644 --- a/jquery-tablesorter.gemspec +++ b/jquery-tablesorter.gemspec @@ -18,5 +18,5 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 1.9.3' - s.add_dependency 'railties', '>= 3.2', '< 5' + s.add_dependency 'railties', '>= 3.2', '< 6' end From c7a4547d3df92985fe873436b9bc6d480e7670e5 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 11 Jan 2016 20:47:49 +0100 Subject: [PATCH 077/138] Update license-file to 2016 --- MIT-LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIT-LICENSE b/MIT-LICENSE index 1c11a4f..241c48d 100644 --- a/MIT-LICENSE +++ b/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright 2015 Jun Lin, Erik-B. Ernst +Copyright 2016 Jun Lin, Erik-B. Ernst Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the From 878098516bfef5ba50b7449730d39faecd12566b Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 11 Jan 2016 20:56:47 +0100 Subject: [PATCH 078/138] Update tablesorter to latest version (2.25.1) --- CHANGELOG.md | 5 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 66 +++++++++---------- .../jquery-tablesorter/jquery.tablesorter.js | 28 ++++---- .../jquery.tablesorter.widgets.js | 38 +++++------ .../widgets/widget-columnSelector.js | 31 +++++---- .../widgets/widget-filter.js | 36 ++++------ .../widgets/widget-output.js | 11 +++- 10 files changed, 112 insertions(+), 109 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f295fc..49b0357 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ Changelog === +#### v1.20.1 (2016-01-11) + +* Upgrade tablesorter to v2.25.1 +* Bump railties dependency to support v5.x + #### v1.20.0 (2015-12-14) * Upgrade tablesorter to v2.25.0 diff --git a/README.md b/README.md index 3a08b63..02295fd 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.25.0 (12/13/2015), [documentation] +Current tablesorter version: 2.25.1 (1/10/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index bfabedb..e441253 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 20 - TINY = 0 + TINY = 1 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 03db8ee..a0181a4 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 03db8eec0e10fb6879d1d1bd283c055932fdc495 +Subproject commit a0181a43376f6b7d72d98eaaf1b84a061ec95cc3 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 375be35..1af8f8f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 12-13-2015 (v2.25.0)*/ +/*! tablesorter (FORK) - updated 01-10-2016 (v2.25.1)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.25.0 *//* +/*! TableSorter (FORK) v2.25.1 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -39,7 +39,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.25.0', + version : '2.25.1', parsers : [], widgets : [], @@ -1064,8 +1064,8 @@ // find the footer $headers = c.$table .find( 'tfoot tr' ) - .children() - .add( $( c.namespace + '_extra_headers' ) ) + .add( $( c.namespace + '_extra_headers' ).children( 'tr' ) ) + .children( 'td, th' ) .removeClass( css.join( ' ' ) ); // remove all header information c.$headers @@ -1898,15 +1898,19 @@ var len, indx, c = table.config, // look for widgets to apply from table class - // stop using \b otherwise this matches 'ui-widget-content' & adds 'content' widget - regex = '\\s' + c.widgetClass.replace( ts.regex.templateName, '([\\w-]+)' ) + '\\s', + // don't match from 'ui-widget-content'; use \S instead of \w to include widgets + // with dashes in the name, e.g. "widget-test-2" extracts out "test-2" + regex = '^' + c.widgetClass.replace( ts.regex.templateName, '(\\S+)+' ) + '$', widgetClass = new RegExp( regex, 'g' ), - // extract out the widget id from the table class (widget id's can include dashes) - widget = ( ' ' + c.table.className + ' ' ).match( widgetClass ); - if ( widget ) { - len = widget.length; + // split up table class (widget id's can include dashes) - stop using match + // otherwise only one widget gets extracted, see #1109 + widgets = ( table.className || '' ).split( ts.regex.spaces ); + if ( widgets.length ) { + len = widgets.length; for ( indx = 0; indx < len; indx++ ) { - c.widgets.push( widget[ indx ].replace( widgetClass, '$1' ) ); + if ( widgets[ indx ].match( widgetClass ) ) { + c.widgets.push( widgets[ indx ].replace( widgetClass, '$1' ) ); + } } } }, @@ -2642,7 +2646,7 @@ }); // match 24 hour time & 12 hours time + am/pm - see http://regexr.com/3c3tk - ts.regex.timeTest = /^([1-9]|1[0-2]):([0-5]\d)(\s[AP]M)|((?:[01]\d|[2][0-4]):[0-5]\d)$/i; + ts.regex.timeTest = /^([1-9]|1[0-2]):([0-5]\d)(\s[AP]M)$|^((?:[01]\d|[2][0-4]):[0-5]\d)$/i; ts.regex.timeMatch = /([1-9]|1[0-2]):([0-5]\d)(\s[AP]M)|((?:[01]\d|[2][0-4]):[0-5]\d)/i; ts.addParser({ id : 'time', @@ -3082,7 +3086,7 @@ })(jQuery); -/*! Widget: filter - updated 12/13/2015 (v2.25.0) *//* +/*! Widget: filter - updated 1/10/2016 (v2.25.1) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3451,7 +3455,7 @@ toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)', 'gi' ), andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), - orTest : /\|/, + orTest : new RegExp( '(\\||\\s+' + ts.language.or + '\\s+)', 'i' ), orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ), iQuery : new RegExp( val, 'i' ), igQuery : new RegExp( val, 'ig' ), @@ -3926,7 +3930,7 @@ filters = Array.prototype.map ? filters.map( String ) : // for IE8 & older browsers - maybe not the best method - filters.join( '\u0000' ).split( '\u0000' ); + filters.join( '\ufffd' ).split( '\ufffd' ); if ( wo.filter_initialized ) { c.$table.triggerHandler( 'filterStart', [ filters ] ); @@ -4132,7 +4136,6 @@ vars.excludeMatch = vars.noAnyMatch; filterMatched = tsf.processTypes( c, data, vars ); - if ( filterMatched !== null ) { showRow = filterMatched; } else { @@ -4165,13 +4168,8 @@ // ignore if filter is empty or disabled if ( data.filter ) { data.cache = data.cacheArray[ columnIndex ]; - // check if column data should be from the cell or from parsed data - if ( wo.filter_useParsedData || data.parsed[ columnIndex ] ) { - data.exact = data.cache; - } else { - result = data.rawArray[ columnIndex ] || ''; - data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 - } + result = data.rawArray[ columnIndex ] || ''; + data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 data.iExact = !tsfRegex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; @@ -4226,8 +4224,7 @@ result = filterMatched; // Look for match, and add child row data for matching } else { - txt = ( data.iExact + data.childRowText ) - .indexOf( tsf.parseFilter( c, data.iFilter, data ) ); + txt = ( data.iExact + data.childRowText ).indexOf( tsf.parseFilter( c, data.iFilter, data ) ); result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); } } else { @@ -4267,18 +4264,17 @@ }; // parse columns after formatter, in case the class is added at that point - data.parsed = c.$headers.map( function( columnIndex ) { - return c.parsers && c.parsers[ columnIndex ] && - // force parsing if parser type is numeric - c.parsers[ columnIndex ].parsed || - // getData won't return 'parsed' if other 'filter-' class names exist + data.parsed = []; + for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { + data.parsed[ columnIndex ] = wo.filter_useParsedData || + // parser has a "parsed" parameter + ( c.parsers && c.parsers[ columnIndex ] && c.parsers[ columnIndex ].parsed || + // getData may not return 'parsed' if other 'filter-' class names exist // ( e.g. <th class="filter-select filter-parsed"> ) ts.getData && ts.getData( c.$headerIndexed[ columnIndex ], ts.getColumnData( table, c.headers, columnIndex ), 'filter' ) === 'parsed' || - $( this ).hasClass( 'filter-parsed' ); - }).get(); + c.$headerIndexed[ columnIndex ].hasClass( 'filter-parsed' ) ); - for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { vars.functions[ columnIndex ] = ts.getColumnData( table, wo.filter_functions, columnIndex ); vars.defaultColFilter[ columnIndex ] = @@ -4436,7 +4432,7 @@ // don't pass reference to val val = showParent ? true : false; - childRow = rowData.$row.filter( ':gt( 0 )' ); + childRow = rowData.$row.filter( ':gt(0)' ); if ( wo.filter_childRows && childRow.length ) { if ( wo.filter_childByColumn ) { if ( !wo.filter_childWithSibs ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 584f1f4..c5acc46 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.25.0 *//* +/*! TableSorter (FORK) v2.25.1 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -21,7 +21,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.25.0', + version : '2.25.1', parsers : [], widgets : [], @@ -1046,8 +1046,8 @@ // find the footer $headers = c.$table .find( 'tfoot tr' ) - .children() - .add( $( c.namespace + '_extra_headers' ) ) + .add( $( c.namespace + '_extra_headers' ).children( 'tr' ) ) + .children( 'td, th' ) .removeClass( css.join( ' ' ) ); // remove all header information c.$headers @@ -1880,15 +1880,19 @@ var len, indx, c = table.config, // look for widgets to apply from table class - // stop using \b otherwise this matches 'ui-widget-content' & adds 'content' widget - regex = '\\s' + c.widgetClass.replace( ts.regex.templateName, '([\\w-]+)' ) + '\\s', + // don't match from 'ui-widget-content'; use \S instead of \w to include widgets + // with dashes in the name, e.g. "widget-test-2" extracts out "test-2" + regex = '^' + c.widgetClass.replace( ts.regex.templateName, '(\\S+)+' ) + '$', widgetClass = new RegExp( regex, 'g' ), - // extract out the widget id from the table class (widget id's can include dashes) - widget = ( ' ' + c.table.className + ' ' ).match( widgetClass ); - if ( widget ) { - len = widget.length; + // split up table class (widget id's can include dashes) - stop using match + // otherwise only one widget gets extracted, see #1109 + widgets = ( table.className || '' ).split( ts.regex.spaces ); + if ( widgets.length ) { + len = widgets.length; for ( indx = 0; indx < len; indx++ ) { - c.widgets.push( widget[ indx ].replace( widgetClass, '$1' ) ); + if ( widgets[ indx ].match( widgetClass ) ) { + c.widgets.push( widgets[ indx ].replace( widgetClass, '$1' ) ); + } } } }, @@ -2624,7 +2628,7 @@ }); // match 24 hour time & 12 hours time + am/pm - see http://regexr.com/3c3tk - ts.regex.timeTest = /^([1-9]|1[0-2]):([0-5]\d)(\s[AP]M)|((?:[01]\d|[2][0-4]):[0-5]\d)$/i; + ts.regex.timeTest = /^([1-9]|1[0-2]):([0-5]\d)(\s[AP]M)$|^((?:[01]\d|[2][0-4]):[0-5]\d)$/i; ts.regex.timeMatch = /([1-9]|1[0-2]):([0-5]\d)(\s[AP]M)|((?:[01]\d|[2][0-4]):[0-5]\d)/i; ts.addParser({ id : 'time', diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 4e511e9..015f4a4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 12-13-2015 (v2.25.0)*/ +/*! tablesorter (FORK) - updated 01-10-2016 (v2.25.1)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -372,7 +372,7 @@ })(jQuery); -/*! Widget: filter - updated 12/13/2015 (v2.25.0) *//* +/*! Widget: filter - updated 1/10/2016 (v2.25.1) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -741,7 +741,7 @@ toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)', 'gi' ), andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), - orTest : /\|/, + orTest : new RegExp( '(\\||\\s+' + ts.language.or + '\\s+)', 'i' ), orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ), iQuery : new RegExp( val, 'i' ), igQuery : new RegExp( val, 'ig' ), @@ -1216,7 +1216,7 @@ filters = Array.prototype.map ? filters.map( String ) : // for IE8 & older browsers - maybe not the best method - filters.join( '\u0000' ).split( '\u0000' ); + filters.join( '\ufffd' ).split( '\ufffd' ); if ( wo.filter_initialized ) { c.$table.triggerHandler( 'filterStart', [ filters ] ); @@ -1422,7 +1422,6 @@ vars.excludeMatch = vars.noAnyMatch; filterMatched = tsf.processTypes( c, data, vars ); - if ( filterMatched !== null ) { showRow = filterMatched; } else { @@ -1455,13 +1454,8 @@ // ignore if filter is empty or disabled if ( data.filter ) { data.cache = data.cacheArray[ columnIndex ]; - // check if column data should be from the cell or from parsed data - if ( wo.filter_useParsedData || data.parsed[ columnIndex ] ) { - data.exact = data.cache; - } else { - result = data.rawArray[ columnIndex ] || ''; - data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 - } + result = data.rawArray[ columnIndex ] || ''; + data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 data.iExact = !tsfRegex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; @@ -1516,8 +1510,7 @@ result = filterMatched; // Look for match, and add child row data for matching } else { - txt = ( data.iExact + data.childRowText ) - .indexOf( tsf.parseFilter( c, data.iFilter, data ) ); + txt = ( data.iExact + data.childRowText ).indexOf( tsf.parseFilter( c, data.iFilter, data ) ); result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); } } else { @@ -1557,18 +1550,17 @@ }; // parse columns after formatter, in case the class is added at that point - data.parsed = c.$headers.map( function( columnIndex ) { - return c.parsers && c.parsers[ columnIndex ] && - // force parsing if parser type is numeric - c.parsers[ columnIndex ].parsed || - // getData won't return 'parsed' if other 'filter-' class names exist + data.parsed = []; + for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { + data.parsed[ columnIndex ] = wo.filter_useParsedData || + // parser has a "parsed" parameter + ( c.parsers && c.parsers[ columnIndex ] && c.parsers[ columnIndex ].parsed || + // getData may not return 'parsed' if other 'filter-' class names exist // ( e.g. <th class="filter-select filter-parsed"> ) ts.getData && ts.getData( c.$headerIndexed[ columnIndex ], ts.getColumnData( table, c.headers, columnIndex ), 'filter' ) === 'parsed' || - $( this ).hasClass( 'filter-parsed' ); - }).get(); + c.$headerIndexed[ columnIndex ].hasClass( 'filter-parsed' ) ); - for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { vars.functions[ columnIndex ] = ts.getColumnData( table, wo.filter_functions, columnIndex ); vars.defaultColFilter[ columnIndex ] = @@ -1726,7 +1718,7 @@ // don't pass reference to val val = showParent ? true : false; - childRow = rowData.$row.filter( ':gt( 0 )' ); + childRow = rowData.$row.filter( ':gt(0)' ); if ( wo.filter_childRows && childRow.length ) { if ( wo.filter_childByColumn ) { if ( !wo.filter_childWithSibs ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index 563bb7b..07eca2d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Widget: columnSelector (responsive table widget) - updated 10/31/2015 (v2.24.0) *//* +/* Widget: columnSelector (responsive table widget) - updated 1/10/2016 (v2.25.1) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -237,9 +237,12 @@ var array = [], temp = ' col:nth-child(' + column + ')'; array.push(prefix + temp + ',' + prefix + '_extra_table' + temp); - temp = ' tr th:nth-child(' + column + ')'; + temp = ' tr:not(.hasSpan) th:nth-child(' + column + ')'; array.push(prefix + temp + ',' + prefix + '_extra_table' + temp); - temp = ' tr td:nth-child(' + column + ')'; + temp = ' tr:not(.hasSpan) td:nth-child(' + column + ')'; + array.push(prefix + temp + ',' + prefix + '_extra_table' + temp); + // for other cells in colspan columns + temp = ' tr td:not(' + prefix + 'HasSpan)[data-column="' + (column - 1) + '"]'; array.push(prefix + temp + ',' + prefix + '_extra_table' + temp); return array; }, @@ -290,7 +293,7 @@ if (mediaAll.length) { colSel.$breakpoints .prop('disabled', false) - .html( tsColSel.queryAll.replace(/\[columns\]/g, mediaAll.join(',')) + breakpts ); + .text( tsColSel.queryAll.replace(/\[columns\]/g, mediaAll.join(',')) + breakpts ); } }, updateCols: function(c, wo) { @@ -312,7 +315,7 @@ colSel.$breakpoints.prop('disabled', true); } if (colSel.$style) { - colSel.$style.prop('disabled', false).html( styles.length ? styles.join(',') + ' { display: none; }' : '' ); + colSel.$style.prop('disabled', false).text( styles.length ? styles.join(',') + ' { display: none; }' : '' ); } if (wo.columnSelector_saveColumns && ts.storage) { ts.storage( c.$table[0], 'tablesorter-columnSelector', colSel.states ); @@ -327,7 +330,7 @@ hasSpans = false, $cells = c.$table .add( $(c.namespace + '_extra_table') ) - .children('thead, tfoot') + .children() .children('tr') .children('th, td'), len = $cells.length; @@ -338,6 +341,8 @@ $cells.eq( index ) .addClass( c.namespace.slice( 1 ) + 'columnselectorHasSpan' ) .attr( 'data-col-span', span ); + // add data-column values + ts.computeColumnIndex( $cells.eq( index ).parent().addClass( 'hasSpan' ) ); } } // only add resize end if using media queries @@ -357,15 +362,16 @@ } }, adjustColspans: function(c, wo) { - var index, cols, col, span, end, + var index, cols, col, span, end, $cell, colSel = c.selector, autoModeOn = colSel.auto, $colspans = $( c.namespace + 'columnselectorHasSpan' ), len = $colspans.length; if ( len ) { for ( index = 0; index < len; index++ ) { - col = parseInt( $colspans.eq(index).attr('data-column'), 10 ); - span = parseInt( $colspans.eq(index).attr('data-col-span'), 10 ); + $cell = $colspans.eq(index); + col = parseInt( $cell.attr('data-column'), 10 ) || $cell[0].cellIndex; + span = parseInt( $cell.attr('data-col-span'), 10 ); end = col + span; for ( cols = col; cols < end; cols++ ) { if ( !autoModeOn && colSel.states[ cols ] === false || @@ -374,9 +380,9 @@ } } if ( span ) { - $colspans.eq(index).show()[0].colSpan = span; + $cell.removeClass( wo.filter_filteredRow )[0].colSpan = span; } else { - $colspans.eq(index).hide(); + $cell.addClass( wo.filter_filteredRow ); } } } @@ -471,12 +477,13 @@ tsColSel.init(table, c, wo); }, remove: function(table, c, wo, refreshing) { - if (refreshing) { return; } var csel = c.selector; + if ( refreshing || !csel ) { return; } csel.$container.empty(); if (csel.$popup) { csel.$popup.empty(); } csel.$style.remove(); csel.$breakpoints.remove(); + $( c.namespace + 'columnselectorHasSpan' ).removeClass( wo.filter_filteredRow ); c.$table.off('updateAll' + namespace + ' update' + namespace); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 927c232..6977e64 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 12/13/2015 (v2.25.0) *//* +/*! Widget: filter - updated 1/10/2016 (v2.25.1) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -367,7 +367,7 @@ toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)', 'gi' ), andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), - orTest : /\|/, + orTest : new RegExp( '(\\||\\s+' + ts.language.or + '\\s+)', 'i' ), orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ), iQuery : new RegExp( val, 'i' ), igQuery : new RegExp( val, 'ig' ), @@ -842,7 +842,7 @@ filters = Array.prototype.map ? filters.map( String ) : // for IE8 & older browsers - maybe not the best method - filters.join( '\u0000' ).split( '\u0000' ); + filters.join( '\ufffd' ).split( '\ufffd' ); if ( wo.filter_initialized ) { c.$table.triggerHandler( 'filterStart', [ filters ] ); @@ -1048,7 +1048,6 @@ vars.excludeMatch = vars.noAnyMatch; filterMatched = tsf.processTypes( c, data, vars ); - if ( filterMatched !== null ) { showRow = filterMatched; } else { @@ -1081,13 +1080,8 @@ // ignore if filter is empty or disabled if ( data.filter ) { data.cache = data.cacheArray[ columnIndex ]; - // check if column data should be from the cell or from parsed data - if ( wo.filter_useParsedData || data.parsed[ columnIndex ] ) { - data.exact = data.cache; - } else { - result = data.rawArray[ columnIndex ] || ''; - data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 - } + result = data.rawArray[ columnIndex ] || ''; + data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 data.iExact = !tsfRegex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; @@ -1142,8 +1136,7 @@ result = filterMatched; // Look for match, and add child row data for matching } else { - txt = ( data.iExact + data.childRowText ) - .indexOf( tsf.parseFilter( c, data.iFilter, data ) ); + txt = ( data.iExact + data.childRowText ).indexOf( tsf.parseFilter( c, data.iFilter, data ) ); result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); } } else { @@ -1183,18 +1176,17 @@ }; // parse columns after formatter, in case the class is added at that point - data.parsed = c.$headers.map( function( columnIndex ) { - return c.parsers && c.parsers[ columnIndex ] && - // force parsing if parser type is numeric - c.parsers[ columnIndex ].parsed || - // getData won't return 'parsed' if other 'filter-' class names exist + data.parsed = []; + for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { + data.parsed[ columnIndex ] = wo.filter_useParsedData || + // parser has a "parsed" parameter + ( c.parsers && c.parsers[ columnIndex ] && c.parsers[ columnIndex ].parsed || + // getData may not return 'parsed' if other 'filter-' class names exist // ( e.g. <th class="filter-select filter-parsed"> ) ts.getData && ts.getData( c.$headerIndexed[ columnIndex ], ts.getColumnData( table, c.headers, columnIndex ), 'filter' ) === 'parsed' || - $( this ).hasClass( 'filter-parsed' ); - }).get(); + c.$headerIndexed[ columnIndex ].hasClass( 'filter-parsed' ) ); - for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { vars.functions[ columnIndex ] = ts.getColumnData( table, wo.filter_functions, columnIndex ); vars.defaultColFilter[ columnIndex ] = @@ -1352,7 +1344,7 @@ // don't pass reference to val val = showParent ? true : false; - childRow = rowData.$row.filter( ':gt( 0 )' ); + childRow = rowData.$row.filter( ':gt(0)' ); if ( wo.filter_childRows && childRow.length ) { if ( wo.filter_childByColumn ) { if ( !wo.filter_childWithSibs ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index 1a4e081..d5c231c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -1,4 +1,4 @@ -/*! Widget: output - updated 10/31/2015 (v2.24.0) *//* +/*! Widget: output - updated 1/10/2016 (v2.25.1) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * Modified from: * HTML Table to CSV: http://www.kunalbabre.com/projects/table2CSV.php (License unknown?) @@ -189,7 +189,14 @@ } // callback; if true returned, continue processing - if ($.isFunction(wo.output_callback) && !wo.output_callback(c, mydata)) { return; } + if ($.isFunction(wo.output_callback)) { + tmp = wo.output_callback(c, mydata); + if ( tmp === false ) { + return; + } else if ( typeof tmp === 'string' ) { + mydata = tmp; + } + } if ( /p/i.test( wo.output_delivery || '' ) ) { output.popup(mydata, wo.output_popupStyle, outputJSON || outputArray); From 139737316ede0002ed5d66be812ad13af455cb95 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Tue, 12 Jan 2016 20:44:44 +0100 Subject: [PATCH 079/138] Update docs: ruby 2.3.x compatibility --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 02295fd..a779aa9 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Or install it yourself as: ## Requirements -It should work with Rails 3.2 and higher (tested up to 4.2) as well as with ruby 1.9.3 - 2.2.x. +It should work with Rails 3.2 and higher (tested up to 4.2) as well as with ruby 1.9.3 - 2.3.x. Each release is always tested with the latest version of both. ## Usage From 319793465bd786197536a0a0fd5bcac6d3e69a43 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Fri, 15 Jan 2016 19:50:09 +0100 Subject: [PATCH 080/138] Update tablesorter to latest version (2.25.2) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 55 +++++++++++++------ .../jquery-tablesorter/jquery.tablesorter.js | 12 ++-- .../jquery.tablesorter.widgets.js | 43 +++++++++++---- .../parsers/parser-input-select.js | 8 +-- .../widgets/widget-filter.js | 41 ++++++++++---- .../widgets/widget-output.js | 9 ++- 10 files changed, 125 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49b0357..da7e190 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.20.2 (2016-01-15) + +* Upgrade tablesorter to v2.25.2 + #### v1.20.1 (2016-01-11) * Upgrade tablesorter to v2.25.1 diff --git a/README.md b/README.md index a779aa9..f5f0287 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.25.1 (1/10/2016), [documentation] +Current tablesorter version: 2.25.2 (1/15/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index e441253..43ec3f3 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 20 - TINY = 1 + TINY = 2 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index a0181a4..01b531e 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit a0181a43376f6b7d72d98eaaf1b84a061ec95cc3 +Subproject commit 01b531ee8e190b5321b484da2cadf9d9ba399b7b diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 1af8f8f..840c07d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 01-10-2016 (v2.25.1)*/ +/*! tablesorter (FORK) - updated 01-15-2016 (v2.25.2)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.25.1 *//* +/*! TableSorter (FORK) v2.25.2 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -39,7 +39,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.25.1', + version : '2.25.2', parsers : [], widgets : [], @@ -208,6 +208,10 @@ bottom : false }, + keyCodes : { + enter : 13 + }, + // placeholder date parser data (globalize) dates : {}, @@ -476,7 +480,7 @@ // only recognize left clicks if ( ( ( e.which || e.button ) !== 1 && !type.match( ' ' + c.pointerClick + ' | sort | keyup ' ) ) || // allow pressing enter - ( type === ' keyup ' && e.which !== 13 ) || + ( type === ' keyup ' && e.which !== ts.keyCodes.enter ) || // allow triggering a click event (e.which is undefined) & ignore physical clicks ( type.match( ' ' + c.pointerClick + ' ' ) && typeof e.which !== 'undefined' ) ) { return; @@ -1064,8 +1068,8 @@ // find the footer $headers = c.$table .find( 'tfoot tr' ) - .add( $( c.namespace + '_extra_headers' ).children( 'tr' ) ) .children( 'td, th' ) + .add( $( c.namespace + '_extra_headers' ) ) .removeClass( css.join( ' ' ) ); // remove all header information c.$headers @@ -3086,7 +3090,7 @@ })(jQuery); -/*! Widget: filter - updated 1/10/2016 (v2.25.1) *//* +/*! Widget: filter - updated 1/15/2016 (v2.25.2) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3094,7 +3098,8 @@ 'use strict'; var tsf, tsfRegex, ts = $.tablesorter || {}, - tscss = ts.css; + tscss = ts.css, + tskeyCodes = ts.keyCodes; $.extend( tscss, { filterRow : 'tablesorter-filter-row', @@ -3103,6 +3108,14 @@ filterRowHide : 'hideme' }); + $.extend( tskeyCodes, { + backSpace : 8, + escape : 27, + space : 32, + left : 37, + down : 40 + }); + ts.addWidget({ id: 'filter', priority: 50, @@ -3127,6 +3140,7 @@ filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available ( visible ) options within the drop down filter_placeholder : { search : '', select : '' }, // default placeholder text ( overridden by any header 'data-placeholder' setting ) filter_reset : null, // jQuery selector string of an element used to reset the filters + filter_resetOnEsc : true, // Reset filter input when the user presses escape - normalized across browsers filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters filter_searchDelay : 300, // typing delay in milliseconds before starting a search filter_searchFiltered: true, // allow searching through already filtered rows in special circumstances; will speed up searching in large tables if true @@ -3835,18 +3849,25 @@ ts.setFilters( table, c.$table.data( 'lastSearch' ) || [], internal === false ); } // unbind events - tmp = ( 'keypress keyup search change '.split( ' ' ).join( namespace + ' ' ) ); + tmp = ( 'keypress keyup keydown search change input '.split( ' ' ).join( namespace + ' ' ) ); $el // use data attribute instead of jQuery data since the head is cloned without including // the data/binding .attr( 'data-lastSearchTime', new Date().getTime() ) .unbind( tmp.replace( ts.regex.spaces, ' ' ) ) - // include change for select - fixes #473 + .bind( 'keydown' + namespace, function( event ) { + if ( event.which === tskeyCodes.escape && !wo.filter_resetOnEsc ) { + // prevent keypress event + return false; + } + }) .bind( 'keyup' + namespace, function( event ) { + var column = parseInt( $( this ).attr( 'data-column' ), 10 ); $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); // emulate what webkit does.... escape clears the filter - if ( event.which === 27 ) { - this.value = ''; + if ( event.which === tskeyCodes.escape ) { + // make sure to restore the last value on escape + this.value = wo.filter_resetOnEsc ? '' : c.lastSearch[column]; // live search } else if ( wo.filter_liveSearch === false ) { return; @@ -3855,19 +3876,21 @@ // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace ( typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch ) || // let return & backspace continue on, but ignore arrows & non-valid characters - ( event.which !== 13 && event.which !== 8 && - ( event.which < 32 || ( event.which >= 37 && event.which <= 40 ) ) ) ) ) { + ( event.which !== tskeyCodes.enter && event.which !== tskeyCodes.backSpace && + ( event.which < tskeyCodes.space || ( event.which >= tskeyCodes.left && event.which <= tskeyCodes.down ) ) ) ) ) { return; } // change event = no delay; last true flag tells getFilters to skip newest timed input tsf.searching( table, true, true ); }) - .bind( 'search change keypress '.split( ' ' ).join( namespace + ' ' ), function( event ) { + // include change for select - fixes #473 + .bind( 'search change keypress input '.split( ' ' ).join( namespace + ' ' ), function( event ) { // don't get cached data, in case data-column changes dynamically var column = parseInt( $( this ).attr( 'data-column' ), 10 ); // don't allow 'change' event to process if the input value is the same - fixes #685 - if ( wo.filter_initialized && ( event.which === 13 || event.type === 'search' || - event.type === 'change' && this.value !== c.lastSearch[column] ) ) { + if ( wo.filter_initialized && ( event.which === tskeyCodes.enter || event.type === 'search' || + // only "input" event fires in MS Edge when clicking the "x" to clear the search + ( event.type === 'change' || event.type === 'input' ) && this.value !== c.lastSearch[column] ) ) { event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index c5acc46..99e5d66 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.25.1 *//* +/*! TableSorter (FORK) v2.25.2 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -21,7 +21,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.25.1', + version : '2.25.2', parsers : [], widgets : [], @@ -190,6 +190,10 @@ bottom : false }, + keyCodes : { + enter : 13 + }, + // placeholder date parser data (globalize) dates : {}, @@ -458,7 +462,7 @@ // only recognize left clicks if ( ( ( e.which || e.button ) !== 1 && !type.match( ' ' + c.pointerClick + ' | sort | keyup ' ) ) || // allow pressing enter - ( type === ' keyup ' && e.which !== 13 ) || + ( type === ' keyup ' && e.which !== ts.keyCodes.enter ) || // allow triggering a click event (e.which is undefined) & ignore physical clicks ( type.match( ' ' + c.pointerClick + ' ' ) && typeof e.which !== 'undefined' ) ) { return; @@ -1046,8 +1050,8 @@ // find the footer $headers = c.$table .find( 'tfoot tr' ) - .add( $( c.namespace + '_extra_headers' ).children( 'tr' ) ) .children( 'td, th' ) + .add( $( c.namespace + '_extra_headers' ) ) .removeClass( css.join( ' ' ) ); // remove all header information c.$headers diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 015f4a4..f456cb4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 01-10-2016 (v2.25.1)*/ +/*! tablesorter (FORK) - updated 01-15-2016 (v2.25.2)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -372,7 +372,7 @@ })(jQuery); -/*! Widget: filter - updated 1/10/2016 (v2.25.1) *//* +/*! Widget: filter - updated 1/15/2016 (v2.25.2) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -380,7 +380,8 @@ 'use strict'; var tsf, tsfRegex, ts = $.tablesorter || {}, - tscss = ts.css; + tscss = ts.css, + tskeyCodes = ts.keyCodes; $.extend( tscss, { filterRow : 'tablesorter-filter-row', @@ -389,6 +390,14 @@ filterRowHide : 'hideme' }); + $.extend( tskeyCodes, { + backSpace : 8, + escape : 27, + space : 32, + left : 37, + down : 40 + }); + ts.addWidget({ id: 'filter', priority: 50, @@ -413,6 +422,7 @@ filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available ( visible ) options within the drop down filter_placeholder : { search : '', select : '' }, // default placeholder text ( overridden by any header 'data-placeholder' setting ) filter_reset : null, // jQuery selector string of an element used to reset the filters + filter_resetOnEsc : true, // Reset filter input when the user presses escape - normalized across browsers filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters filter_searchDelay : 300, // typing delay in milliseconds before starting a search filter_searchFiltered: true, // allow searching through already filtered rows in special circumstances; will speed up searching in large tables if true @@ -1121,18 +1131,25 @@ ts.setFilters( table, c.$table.data( 'lastSearch' ) || [], internal === false ); } // unbind events - tmp = ( 'keypress keyup search change '.split( ' ' ).join( namespace + ' ' ) ); + tmp = ( 'keypress keyup keydown search change input '.split( ' ' ).join( namespace + ' ' ) ); $el // use data attribute instead of jQuery data since the head is cloned without including // the data/binding .attr( 'data-lastSearchTime', new Date().getTime() ) .unbind( tmp.replace( ts.regex.spaces, ' ' ) ) - // include change for select - fixes #473 + .bind( 'keydown' + namespace, function( event ) { + if ( event.which === tskeyCodes.escape && !wo.filter_resetOnEsc ) { + // prevent keypress event + return false; + } + }) .bind( 'keyup' + namespace, function( event ) { + var column = parseInt( $( this ).attr( 'data-column' ), 10 ); $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); // emulate what webkit does.... escape clears the filter - if ( event.which === 27 ) { - this.value = ''; + if ( event.which === tskeyCodes.escape ) { + // make sure to restore the last value on escape + this.value = wo.filter_resetOnEsc ? '' : c.lastSearch[column]; // live search } else if ( wo.filter_liveSearch === false ) { return; @@ -1141,19 +1158,21 @@ // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace ( typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch ) || // let return & backspace continue on, but ignore arrows & non-valid characters - ( event.which !== 13 && event.which !== 8 && - ( event.which < 32 || ( event.which >= 37 && event.which <= 40 ) ) ) ) ) { + ( event.which !== tskeyCodes.enter && event.which !== tskeyCodes.backSpace && + ( event.which < tskeyCodes.space || ( event.which >= tskeyCodes.left && event.which <= tskeyCodes.down ) ) ) ) ) { return; } // change event = no delay; last true flag tells getFilters to skip newest timed input tsf.searching( table, true, true ); }) - .bind( 'search change keypress '.split( ' ' ).join( namespace + ' ' ), function( event ) { + // include change for select - fixes #473 + .bind( 'search change keypress input '.split( ' ' ).join( namespace + ' ' ), function( event ) { // don't get cached data, in case data-column changes dynamically var column = parseInt( $( this ).attr( 'data-column' ), 10 ); // don't allow 'change' event to process if the input value is the same - fixes #685 - if ( wo.filter_initialized && ( event.which === 13 || event.type === 'search' || - event.type === 'change' && this.value !== c.lastSearch[column] ) ) { + if ( wo.filter_initialized && ( event.which === tskeyCodes.enter || event.type === 'search' || + // only "input" event fires in MS Edge when clicking the "x" to clear the search + ( event.type === 'change' || event.type === 'input' ) && this.value !== c.lastSearch[column] ) ) { event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index 1f151b0..7d27621 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,4 +1,4 @@ -/*! Parser: input & select - updated 12/13/2015 (v2.25.0) *//* +/*! Parser: input & select - updated 1/15/2016 (v2.25.2) *//* * for jQuery 1.7+ & tablesorter 2.7.11+ * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ @@ -158,16 +158,16 @@ $( ':focus' ).blur(); } }) - .on( 'focus' + namespace, 'select, input, textarea', function() { + .on( 'focus' + namespace, 'select, input:not([type=checkbox]), textarea', function() { $( this ).data( 'ts-original-value', this.value ); }) - .on( 'blur' + namespace, 'input, textarea', function() { + .on( 'blur' + namespace, 'input:not([type=checkbox]), textarea', function() { // restore input value; // 'change' is triggered before 'blur' so this doesn't replace the new update with the original this.value = $( this ).data( 'ts-original-value' ); }) .on( 'change keyup '.split( ' ' ).join( namespace + ' ' ), 'select, input, textarea', function( event ) { - if ( event.which === 27 ) { + if ( event.which === 27 && !( this.nodeName === 'INPUT' && this.type === 'checkbox' ) ) { // escape: restore original value this.value = $( this ).data( 'ts-original-value' ); return; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 6977e64..66f3696 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 1/10/2016 (v2.25.1) *//* +/*! Widget: filter - updated 1/15/2016 (v2.25.2) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -6,7 +6,8 @@ 'use strict'; var tsf, tsfRegex, ts = $.tablesorter || {}, - tscss = ts.css; + tscss = ts.css, + tskeyCodes = ts.keyCodes; $.extend( tscss, { filterRow : 'tablesorter-filter-row', @@ -15,6 +16,14 @@ filterRowHide : 'hideme' }); + $.extend( tskeyCodes, { + backSpace : 8, + escape : 27, + space : 32, + left : 37, + down : 40 + }); + ts.addWidget({ id: 'filter', priority: 50, @@ -39,6 +48,7 @@ filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available ( visible ) options within the drop down filter_placeholder : { search : '', select : '' }, // default placeholder text ( overridden by any header 'data-placeholder' setting ) filter_reset : null, // jQuery selector string of an element used to reset the filters + filter_resetOnEsc : true, // Reset filter input when the user presses escape - normalized across browsers filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters filter_searchDelay : 300, // typing delay in milliseconds before starting a search filter_searchFiltered: true, // allow searching through already filtered rows in special circumstances; will speed up searching in large tables if true @@ -747,18 +757,25 @@ ts.setFilters( table, c.$table.data( 'lastSearch' ) || [], internal === false ); } // unbind events - tmp = ( 'keypress keyup search change '.split( ' ' ).join( namespace + ' ' ) ); + tmp = ( 'keypress keyup keydown search change input '.split( ' ' ).join( namespace + ' ' ) ); $el // use data attribute instead of jQuery data since the head is cloned without including // the data/binding .attr( 'data-lastSearchTime', new Date().getTime() ) .unbind( tmp.replace( ts.regex.spaces, ' ' ) ) - // include change for select - fixes #473 + .bind( 'keydown' + namespace, function( event ) { + if ( event.which === tskeyCodes.escape && !wo.filter_resetOnEsc ) { + // prevent keypress event + return false; + } + }) .bind( 'keyup' + namespace, function( event ) { + var column = parseInt( $( this ).attr( 'data-column' ), 10 ); $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); // emulate what webkit does.... escape clears the filter - if ( event.which === 27 ) { - this.value = ''; + if ( event.which === tskeyCodes.escape ) { + // make sure to restore the last value on escape + this.value = wo.filter_resetOnEsc ? '' : c.lastSearch[column]; // live search } else if ( wo.filter_liveSearch === false ) { return; @@ -767,19 +784,21 @@ // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace ( typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch ) || // let return & backspace continue on, but ignore arrows & non-valid characters - ( event.which !== 13 && event.which !== 8 && - ( event.which < 32 || ( event.which >= 37 && event.which <= 40 ) ) ) ) ) { + ( event.which !== tskeyCodes.enter && event.which !== tskeyCodes.backSpace && + ( event.which < tskeyCodes.space || ( event.which >= tskeyCodes.left && event.which <= tskeyCodes.down ) ) ) ) ) { return; } // change event = no delay; last true flag tells getFilters to skip newest timed input tsf.searching( table, true, true ); }) - .bind( 'search change keypress '.split( ' ' ).join( namespace + ' ' ), function( event ) { + // include change for select - fixes #473 + .bind( 'search change keypress input '.split( ' ' ).join( namespace + ' ' ), function( event ) { // don't get cached data, in case data-column changes dynamically var column = parseInt( $( this ).attr( 'data-column' ), 10 ); // don't allow 'change' event to process if the input value is the same - fixes #685 - if ( wo.filter_initialized && ( event.which === 13 || event.type === 'search' || - event.type === 'change' && this.value !== c.lastSearch[column] ) ) { + if ( wo.filter_initialized && ( event.which === tskeyCodes.enter || event.type === 'search' || + // only "input" event fires in MS Edge when clicking the "x" to clear the search + ( event.type === 'change' || event.type === 'input' ) && this.value !== c.lastSearch[column] ) ) { event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index d5c231c..1036f24 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -1,4 +1,4 @@ -/*! Widget: output - updated 1/10/2016 (v2.25.1) *//* +/*! Widget: output - updated 1/15/2016 (v2.25.2) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * Modified from: * HTML Table to CSV: http://www.kunalbabre.com/projects/table2CSV.php (License unknown?) @@ -148,8 +148,11 @@ // all tbody rows $rows = $el.children('tbody').children('tr'); - // get (f)iltered, (v)isible, all rows (look for the first letter only), or jQuery filter selector - $rows = /^f/.test(saveRows) ? $rows.not('.' + (wo.filter_filteredRow || 'filtered') ) : + // check for a filter callback function first! because + // /^f/.test(function(){ console.log('test'); }) is TRUE! (function is converted to a string) + $rows = typeof saveRows === 'function' ? $rows.filter(saveRows) : + // get (f)iltered, (v)isible, all rows (look for the first letter only), or jQuery filter selector + /^f/.test(saveRows) ? $rows.not('.' + (wo.filter_filteredRow || 'filtered') ) : /^v/.test(saveRows) ? $rows.filter(':visible') : // look for '.' (class selector), '#' (id selector), // ':' (basic filters, e.g. ':not()') or '[' (attribute selector start) From 2ea83421e532134f9cee13884ae03c0093b24d15 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Fri, 22 Jan 2016 16:24:56 +0100 Subject: [PATCH 081/138] Update tablesorter to latest version (2.25.3) --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 14 ++++++++------ .../jquery-tablesorter/jquery.tablesorter.js | 4 ++-- .../jquery.tablesorter.widgets.js | 10 ++++++---- .../jquery-tablesorter/widgets/widget-filter.js | 8 +++++--- 8 files changed, 28 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da7e190..281ea7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.20.3 (2016-01-22) + +* Upgrade tablesorter to v2.25.3 + #### v1.20.2 (2016-01-15) * Upgrade tablesorter to v2.25.2 diff --git a/README.md b/README.md index f5f0287..d0ddefe 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.25.2 (1/15/2016), [documentation] +Current tablesorter version: 2.25.3 (1/21/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 43ec3f3..5330067 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 20 - TINY = 2 + TINY = 3 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 01b531e..bb7b57d 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 01b531ee8e190b5321b484da2cadf9d9ba399b7b +Subproject commit bb7b57d0a17e035f371e524a7bd6fde2c8c6f024 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 840c07d..d9fb75d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 01-15-2016 (v2.25.2)*/ +/*! tablesorter (FORK) - updated 01-21-2016 (v2.25.3)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.25.2 *//* +/*! TableSorter (FORK) v2.25.3 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -39,7 +39,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.25.2', + version : '2.25.3', parsers : [], widgets : [], @@ -3090,7 +3090,7 @@ })(jQuery); -/*! Widget: filter - updated 1/15/2016 (v2.25.2) *//* +/*! Widget: filter - updated 1/21/2016 (v2.25.3) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3707,7 +3707,9 @@ mode = encode ? encodeURIComponent : decodeURIComponent, len = filters.length; for ( indx = 0; indx < len; indx++ ) { - filters[ indx ] = mode( filters[ indx ] ); + if ( filters[ indx ] ) { + filters[ indx ] = mode( filters[ indx ] ); + } } return filters; }, @@ -3894,7 +3896,7 @@ event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - tsf.searching( table, false, true ); + tsf.searching( table, event.type !== 'keypress', true ); } }); }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 99e5d66..18f6083 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.25.2 *//* +/*! TableSorter (FORK) v2.25.3 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -21,7 +21,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.25.2', + version : '2.25.3', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index f456cb4..1c4ace8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 01-15-2016 (v2.25.2)*/ +/*! tablesorter (FORK) - updated 01-21-2016 (v2.25.3)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -372,7 +372,7 @@ })(jQuery); -/*! Widget: filter - updated 1/15/2016 (v2.25.2) *//* +/*! Widget: filter - updated 1/21/2016 (v2.25.3) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -989,7 +989,9 @@ mode = encode ? encodeURIComponent : decodeURIComponent, len = filters.length; for ( indx = 0; indx < len; indx++ ) { - filters[ indx ] = mode( filters[ indx ] ); + if ( filters[ indx ] ) { + filters[ indx ] = mode( filters[ indx ] ); + } } return filters; }, @@ -1176,7 +1178,7 @@ event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - tsf.searching( table, false, true ); + tsf.searching( table, event.type !== 'keypress', true ); } }); }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 66f3696..52510df 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 1/15/2016 (v2.25.2) *//* +/*! Widget: filter - updated 1/21/2016 (v2.25.3) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -615,7 +615,9 @@ mode = encode ? encodeURIComponent : decodeURIComponent, len = filters.length; for ( indx = 0; indx < len; indx++ ) { - filters[ indx ] = mode( filters[ indx ] ); + if ( filters[ indx ] ) { + filters[ indx ] = mode( filters[ indx ] ); + } } return filters; }, @@ -802,7 +804,7 @@ event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - tsf.searching( table, false, true ); + tsf.searching( table, event.type !== 'keypress', true ); } }); }, From 3b2cb3bb7011fcd35957ad37acdf42916d5705cc Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Thu, 18 Feb 2016 19:43:07 +0100 Subject: [PATCH 082/138] Update tablesorter to latest version (2.25.4) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 35 +++++++---- .../jquery.tablesorter.combined.js | 25 ++++---- .../jquery-tablesorter/jquery.tablesorter.js | 15 ++--- .../jquery.tablesorter.widgets.js | 10 ++-- .../widgets/widget-columnSelector.js | 4 +- .../widgets/widget-editable.js | 33 ++++++----- .../widgets/widget-filter.js | 8 ++- .../widgets/widget-grouping.js | 8 ++- .../widgets/widget-pager.js | 58 ++++++++++++------- 13 files changed, 130 insertions(+), 76 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 281ea7a..3d5571d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.20.4 (2016-02-18) + +* Upgrade tablesorter to v2.25.4 + #### v1.20.3 (2016-01-22) * Upgrade tablesorter to v2.25.3 diff --git a/README.md b/README.md index d0ddefe..41b19e5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.25.3 (1/21/2016), [documentation] +Current tablesorter version: 2.25.4 (2/15/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 5330067..98a9d84 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 20 - TINY = 3 + TINY = 4 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index bb7b57d..a234d31 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit bb7b57d0a17e035f371e524a7bd6fde2c8c6f024 +Subproject commit a234d31cbaa26162e7ec1a020276a1ca347dace4 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 60ac596..9565844 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -508,14 +508,7 @@ } if (!p.initialized) { - p.initialized = true; - p.initializing = false; - if (table.config.debug) { - console.log('Pager: Triggering pagerInitialized'); - } - $(table).triggerHandler( 'pagerInitialized', p ); - ts.applyWidget( table ); - updatePageDisplay(table, p); + pagerInitialized(table, p); } }, @@ -711,7 +704,8 @@ moveToPage = function(table, p, pageMoved) { if ( p.isDisabled ) { return; } - var c = table.config, + var tmp, + c = table.config, $t = $(table), l = p.last; if ( pageMoved !== false && p.initialized && ts.isEmptyObject(c.cache)) { @@ -747,7 +741,17 @@ optAjaxUrl : p.ajaxUrl || '' }; if (p.ajax) { - getAjax(table, p); + if ( !p.processAjaxOnInit && !ts.isEmptyObject(p.initialRows) ) { + p.processAjaxOnInit = true; + tmp = p.initialRows; + p.totalRows = typeof tmp.total !== 'undefined' ? tmp.total : + ( c.debug ? console.error('Pager: no initial total page set!') || 0 : 0 ); + p.filteredRows = typeof tmp.filtered !== 'undefined' ? tmp.filtered : + ( c.debug ? console.error('Pager: no initial filtered page set!') || 0 : 0 ); + pagerInitialized( table, p ); + } else { + getAjax(table, p); + } } else if (!p.ajax) { renderTable(table, c.rowsCopy, p); } @@ -827,6 +831,17 @@ moveToPage(table, p); }, + pagerInitialized = function(table, p) { + p.initialized = true; + p.initializing = false; + if (table.config.debug) { + console.log('Pager: Triggering pagerInitialized'); + } + $(table).triggerHandler( 'pagerInitialized', p ); + ts.applyWidget( table ); + updatePageDisplay(table, p); + }, + destroyPager = function(table, p) { var c = table.config, namespace = c.namespace + 'pager', diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index d9fb75d..a989739 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 01-21-2016 (v2.25.3)*/ +/*! tablesorter (FORK) - updated 02-15-2016 (v2.25.4)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.25.3 *//* +/*! TableSorter (FORK) v2.25.4 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -39,7 +39,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.25.3', + version : '2.25.4', parsers : [], widgets : [], @@ -195,7 +195,7 @@ }, - // digit sort text location; keeping max+/- for backwards compatibility + // digit sort, text location string : { max : 1, min : -1, @@ -1920,6 +1920,7 @@ }, applyWidgetId : function( table, id, init ) { + table = $(table)[0]; var applied, time, name, c = table.config, wo = c.widgetOptions, @@ -2051,6 +2052,10 @@ for ( index = 0; index < len; index++ ) { widget = ts.getWidgetById( name[ index ] ); indx = $.inArray( name[ index ], c.widgets ); + // don't remove the widget from config.widget if refreshing + if ( indx >= 0 && refreshing !== true ) { + c.widgets.splice( indx, 1 ); + } if ( widget && widget.remove ) { if ( c.debug ) { console.log( ( refreshing ? 'Refreshing' : 'Removing' ) + ' "' + name[ index ] + '" widget' ); @@ -2058,10 +2063,6 @@ widget.remove( table, c, c.widgetOptions, refreshing ); c.widgetInit[ name[ index ] ] = false; } - // don't remove the widget from config.widget if refreshing - if ( indx >= 0 && refreshing !== true ) { - c.widgets.splice( indx, 1 ); - } } }, @@ -3090,7 +3091,7 @@ })(jQuery); -/*! Widget: filter - updated 1/21/2016 (v2.25.3) *//* +/*! Widget: filter - updated 2/15/2016 (v2.25.4) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3891,8 +3892,9 @@ var column = parseInt( $( this ).attr( 'data-column' ), 10 ); // don't allow 'change' event to process if the input value is the same - fixes #685 if ( wo.filter_initialized && ( event.which === tskeyCodes.enter || event.type === 'search' || + ( event.type === 'change' ) && this.value !== c.lastSearch[column] ) || // only "input" event fires in MS Edge when clicking the "x" to clear the search - ( event.type === 'change' || event.type === 'input' ) && this.value !== c.lastSearch[column] ) ) { + ( event.type === 'input' && this.value === '' ) ) { event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); @@ -4301,7 +4303,8 @@ c.$headerIndexed[ columnIndex ].hasClass( 'filter-parsed' ) ); vars.functions[ columnIndex ] = - ts.getColumnData( table, wo.filter_functions, columnIndex ); + ts.getColumnData( table, wo.filter_functions, columnIndex ) || + c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' ); vars.defaultColFilter[ columnIndex ] = ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || ''; vars.excludeFilter[ columnIndex ] = diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 18f6083..5152569 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.25.3 *//* +/*! TableSorter (FORK) v2.25.4 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -21,7 +21,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.25.3', + version : '2.25.4', parsers : [], widgets : [], @@ -177,7 +177,7 @@ }, - // digit sort text location; keeping max+/- for backwards compatibility + // digit sort, text location string : { max : 1, min : -1, @@ -1902,6 +1902,7 @@ }, applyWidgetId : function( table, id, init ) { + table = $(table)[0]; var applied, time, name, c = table.config, wo = c.widgetOptions, @@ -2033,6 +2034,10 @@ for ( index = 0; index < len; index++ ) { widget = ts.getWidgetById( name[ index ] ); indx = $.inArray( name[ index ], c.widgets ); + // don't remove the widget from config.widget if refreshing + if ( indx >= 0 && refreshing !== true ) { + c.widgets.splice( indx, 1 ); + } if ( widget && widget.remove ) { if ( c.debug ) { console.log( ( refreshing ? 'Refreshing' : 'Removing' ) + ' "' + name[ index ] + '" widget' ); @@ -2040,10 +2045,6 @@ widget.remove( table, c, c.widgetOptions, refreshing ); c.widgetInit[ name[ index ] ] = false; } - // don't remove the widget from config.widget if refreshing - if ( indx >= 0 && refreshing !== true ) { - c.widgets.splice( indx, 1 ); - } } }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 1c4ace8..e48ade6 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 01-21-2016 (v2.25.3)*/ +/*! tablesorter (FORK) - updated 02-15-2016 (v2.25.4)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -372,7 +372,7 @@ })(jQuery); -/*! Widget: filter - updated 1/21/2016 (v2.25.3) *//* +/*! Widget: filter - updated 2/15/2016 (v2.25.4) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -1173,8 +1173,9 @@ var column = parseInt( $( this ).attr( 'data-column' ), 10 ); // don't allow 'change' event to process if the input value is the same - fixes #685 if ( wo.filter_initialized && ( event.which === tskeyCodes.enter || event.type === 'search' || + ( event.type === 'change' ) && this.value !== c.lastSearch[column] ) || // only "input" event fires in MS Edge when clicking the "x" to clear the search - ( event.type === 'change' || event.type === 'input' ) && this.value !== c.lastSearch[column] ) ) { + ( event.type === 'input' && this.value === '' ) ) { event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); @@ -1583,7 +1584,8 @@ c.$headerIndexed[ columnIndex ].hasClass( 'filter-parsed' ) ); vars.functions[ columnIndex ] = - ts.getColumnData( table, wo.filter_functions, columnIndex ); + ts.getColumnData( table, wo.filter_functions, columnIndex ) || + c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' ); vars.defaultColFilter[ columnIndex ] = ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || ''; vars.excludeFilter[ columnIndex ] = diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index 07eca2d..52a7ca3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Widget: columnSelector (responsive table widget) - updated 1/10/2016 (v2.25.1) *//* +/* Widget: columnSelector (responsive table widget) - updated 2/15/2016 (v2.25.4) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -478,8 +478,8 @@ }, remove: function(table, c, wo, refreshing) { var csel = c.selector; + if ( csel) { csel.$container.empty(); } if ( refreshing || !csel ) { return; } - csel.$container.empty(); if (csel.$popup) { csel.$popup.empty(); } csel.$style.remove(); csel.$breakpoints.remove(); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js index c573f38..9e782bf 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js @@ -1,4 +1,4 @@ -/*! Widget: editable - updated 12/13/2015 (v2.25.0) *//* +/*! Widget: editable - updated 2/15/2016 (v2.25.4) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -27,20 +27,25 @@ selectAll: function( cell ) { setTimeout( function() { - // select all text in contenteditable - // see http://stackoverflow.com/a/6150060/145346 - var range, selection; - if ( document.body.createTextRange ) { - range = document.body.createTextRange(); - range.moveToElementText( cell ); - range.select(); - } else if ( window.getSelection ) { - selection = window.getSelection(); - range = document.createRange(); - range.selectNodeContents( cell ); - selection.removeAllRanges(); - selection.addRange( range ); + if ( document.queryCommandSupported( 'SelectAll' ) ) { + document.execCommand( 'selectAll', false, null ); + } else { + // select all text in contenteditable + // see http://stackoverflow.com/a/6150060/145346 + var range, selection; + if ( document.body.createTextRange ) { + range = document.body.createTextRange(); + range.moveToElementText( cell ); + range.select(); + } else if ( window.getSelection ) { + selection = window.getSelection(); + range = document.createRange(); + range.selectNodeContents( cell ); + selection.removeAllRanges(); + selection.addRange( range ); + } } + // need delay of at least 100ms or last contenteditable will get refocused }, 100 ); }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 52510df..69df97e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 1/21/2016 (v2.25.3) *//* +/*! Widget: filter - updated 2/15/2016 (v2.25.4) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -799,8 +799,9 @@ var column = parseInt( $( this ).attr( 'data-column' ), 10 ); // don't allow 'change' event to process if the input value is the same - fixes #685 if ( wo.filter_initialized && ( event.which === tskeyCodes.enter || event.type === 'search' || + ( event.type === 'change' ) && this.value !== c.lastSearch[column] ) || // only "input" event fires in MS Edge when clicking the "x" to clear the search - ( event.type === 'change' || event.type === 'input' ) && this.value !== c.lastSearch[column] ) ) { + ( event.type === 'input' && this.value === '' ) ) { event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); @@ -1209,7 +1210,8 @@ c.$headerIndexed[ columnIndex ].hasClass( 'filter-parsed' ) ); vars.functions[ columnIndex ] = - ts.getColumnData( table, wo.filter_functions, columnIndex ); + ts.getColumnData( table, wo.filter_functions, columnIndex ) || + c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' ); vars.defaultColFilter[ columnIndex ] = ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || ''; vars.excludeFilter[ columnIndex ] = diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index 8691995..c03c993 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -1,4 +1,4 @@ -/*! Widget: grouping - updated 11/10/2015 (v2.24.4) *//* +/*! Widget: grouping - updated 2/15/2016 (v2.25.4) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -251,10 +251,14 @@ $this.toggleClass('collapsed'); // nextUntil requires jQuery 1.4+ $this.nextUntil('tr.group-header').toggleClass('group-hidden', $this.hasClass('collapsed') ); + isCollapsed = $this.hasClass('collapsed'); + // reapply zebra widget after opening collapsed group - see #1156 + if (!isCollapsed && ts.hasWidget(c.$table, 'zebra')) { + ts.applyWidgetId(c.$table, 'zebra'); + } // save collapsed groups if (wo.group_saveGroups && ts.storage) { $groups = c.$table.find('.group-header'); - isCollapsed = $this.hasClass('collapsed'); if (!wo.group_collapsedGroups[wo.group_collapsedGroup]) { wo.group_collapsedGroups[wo.group_collapsedGroup] = []; } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 57e0b7a..15f8f34 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 11/22/2015 (v2.24.6) */ +/*! Widget: Pager - updated 2/15/2016 (v2.25.4) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -436,7 +436,7 @@ }, updatePageDisplay: function( c, completed ) { - if ( c.pager.initializing ) { return; } + if ( c.pager && c.pager.initializing ) { return; } var s, t, $out, options, indx, len, table = c.table, wo = c.widgetOptions, @@ -1030,7 +1030,8 @@ if ( pageMoved !== false && p.initialized && $.isEmptyObject( c.cache ) ) { return tsp.updateCache( c ); } - var table = c.table, + var tmp, + table = c.table, wo = c.widgetOptions, l = p.last; @@ -1068,7 +1069,17 @@ optAjaxUrl: wo.pager_ajaxUrl }; if ( p.ajax ) { - tsp.getAjax( c ); + if ( !wo.pager_processAjaxOnInit && !$.isEmptyObject(wo.pager_initialRows) ) { + wo.pager_processAjaxOnInit = true; + tmp = wo.pager_initialRows; + p.totalRows = typeof tmp.total !== 'undefined' ? tmp.total : + ( c.debug ? console.error('Pager: no initial total page set!') || 0 : 0 ); + p.filteredRows = typeof tmp.filtered !== 'undefined' ? tmp.filtered : + ( c.debug ? console.error('Pager: no initial filtered page set!') || 0 : 0 ); + tsp.updatePageDisplay( c, false ); + } else { + tsp.getAjax( c ); + } } else if ( !p.ajax ) { tsp.renderTable( c, c.rowsCopy ); } @@ -1154,25 +1165,32 @@ destroyPager: function( c, refreshing ) { var table = c.table, p = c.pager, - s = c.widgetOptions.pager_selectors, + s = c.widgetOptions.pager_selectors || {}, ctrls = [ s.first, s.prev, s.next, s.last, s.gotoPage, s.pageSize ].join( ',' ), namespace = c.namespace + 'pager'; - p.initialized = false; - c.$table.off( namespace ); - p.$container - // hide pager - .hide() - // unbind pager controls - .find( ctrls ) - .off( namespace ); - if ( refreshing ) { return; } - tsp.showAllRows( c ); - c.appender = null; // remove pager appender function - if ( ts.storage ) { - ts.storage( table, c.widgetOptions.pager_storageKey, '' ); + // check pager object in case two successive pager destroys are triggered + // e.g. "destroyPager" then "removeWidget" - see #1155 + if ( p ) { + p.initialized = false; + c.$table.off( namespace ); + p.$container + // hide pager + .hide() + // unbind pager controls + .find( ctrls ) + .off( namespace ); + if ( refreshing ) { return; } + c.appender = null; // remove pager appender function + tsp.showAllRows( c ); + if ( ts.storage ) { + ts.storage( table, c.widgetOptions.pager_storageKey, '' ); + } + p.$container = null; + p.$goto = null; + p.$size = null; + c.pager = null; + c.rowsCopy = null; } - delete table.config.pager; - delete table.config.rowsCopy; }, enablePager: function( c, triggered ) { From 50e5de9ca93693f87168082fe728088db2f7fc4d Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Wed, 2 Mar 2016 22:17:03 +0100 Subject: [PATCH 083/138] Update tablesorter to latest version (2.25.5) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 18 +-- .../jquery.tablesorter.combined.js | 111 ++++++++++-------- .../jquery-tablesorter/jquery.tablesorter.js | 49 ++++---- .../jquery.tablesorter.widgets.js | 62 ++++++---- .../parsers/parser-huge-numbers.js | 21 ++++ .../widgets/widget-editable.js | 26 ++-- .../widgets/widget-filter.js | 50 +++++--- .../widgets/widget-grouping.js | 16 +-- .../jquery-tablesorter/widgets/widget-math.js | 39 +++--- .../widgets/widget-pager.js | 18 +-- .../widgets/widget-print.js | 11 +- .../widgets/widget-scroller.js | 20 ++-- .../widgets/widget-stickyHeaders.js | 6 +- .../widgets/widget-storage.js | 4 +- 18 files changed, 278 insertions(+), 183 deletions(-) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/parsers/parser-huge-numbers.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d5571d..9c21a31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.20.5 (2016-03-02) + +* Upgrade tablesorter to v2.25.5 + #### v1.20.4 (2016-02-18) * Upgrade tablesorter to v2.25.4 diff --git a/README.md b/README.md index 41b19e5..cd16f96 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.25.4 (2/15/2016), [documentation] +Current tablesorter version: 2.25.5 (3/1/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 98a9d84..a33dcfa 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 20 - TINY = 4 + TINY = 5 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index a234d31..d089be2 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit a234d31cbaa26162e7ec1a020276a1ca347dace4 +Subproject commit d089be27345197a498517371e354b591c2ef865d diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 9565844..7723497 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -167,7 +167,7 @@ c = table.config, namespace = c.namespace + 'pager', sz = parsePageSize( p, p.size, 'get' ); // don't allow dividing by zero - if (p.countChildRows) { t.push(c.cssChildRow); } + if (p.countChildRows) { t[ t.length ] = c.cssChildRow; } p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method c.totalRows = p.totalRows; parsePageNumber( table, p ); @@ -254,10 +254,10 @@ option_pages_start_page = (large_collection) ? skip_set_size : 1; for ( i = option_pages_start_page; i <= pg; ) { - option_pages.push(i); + option_pages[ option_pages.length ] = i; i = i + ( large_collection ? skip_set_size : 1 ); } - option_pages.push(pg); + option_pages[ option_pages.length ] = pg; if (large_collection) { focus_option_pages = []; // don't allow central focus size to be > 5 on either side of current page @@ -269,7 +269,7 @@ if (end_page > pg) { end_page = pg; } // construct an array to get a focus set around the current page for (i = start_page; i <= end_page ; i++) { - focus_option_pages.push(i); + focus_option_pages[ focus_option_pages.length ] = i; } // keep unique values @@ -349,7 +349,7 @@ } else { rows[i].style.display = ( j >= s && j < e ) ? '' : 'none'; if (last !== j && j >= s && j < e) { - p.cacheIndex.push(i); + p.cacheIndex[ p.cacheIndex.length ] = i; last = j; } // don't count child rows @@ -565,7 +565,7 @@ sortCol = sortCol[1]; len = sortList.length; for (indx = 0; indx < len; indx++) { - arry.push(sortCol + '[' + sortList[indx][0] + ']=' + sortList[indx][1]); + arry[ arry.length ] = sortCol + '[' + sortList[indx][0] + ']=' + sortList[indx][1]; } // if the arry is empty, just add the col parameter... "&{sortList:col}" becomes "&col" url = url.replace(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : sortCol ); @@ -576,7 +576,7 @@ len = filterList.length; for (indx = 0; indx < len; indx++) { if (filterList[indx]) { - arry.push(filterCol + '[' + indx + ']=' + encodeURIComponent(filterList[indx])); + arry[ arry.length ] = filterCol + '[' + indx + ']=' + encodeURIComponent( filterList[indx] ); } } // if the arry is empty, just add the fcol parameter... "&{filterList:fcol}" becomes "&fcol" @@ -634,7 +634,7 @@ count++; if (count > s && added <= e) { added++; - p.cacheIndex.push(index); + p.cacheIndex[ p.cacheIndex.length ] = index; $tb.append(rows[index]); } } @@ -695,7 +695,7 @@ n = table.config.cache[0].normalized; p.totalRows = n.length; for (i = 0; i < p.totalRows; i++) { - rows.push(n[i][c.columns].$row); + rows[ rows.length ] = n[i][c.columns].$row; } c.rowsCopy = rows; moveToPage(table, p, true); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index a989739..74882d6 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 02-15-2016 (v2.25.4)*/ +/*! tablesorter (FORK) - updated 03-01-2016 (v2.25.5)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.25.4 *//* +/*! TableSorter (FORK) v2.25.5 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -39,7 +39,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.25.4', + version : '2.25.5', parsers : [], widgets : [], @@ -751,7 +751,7 @@ } } if ( add ) { - ts.parsers.push( parser ); + ts.parsers[ ts.parsers.length ] = parser; } }, @@ -971,7 +971,7 @@ } // ensure rowData is always in the same location (after the last column) cols[ c.columns ] = rowData; - cache.normalized.push( cols ); + cache.normalized[ cache.normalized.length ] = cols; } cache.colMax = colMax; // total up rows, not including child rows @@ -1040,9 +1040,9 @@ }); } if ( result !== false ) { - data.parsed.push( parsed ); - data.raw.push( raw ); - data.$cell.push( $cell ); + data.parsed[ data.parsed.length ] = parsed; + data.raw[ data.raw.length ] = raw; + data.$cell[ data.$cell.length ] = $cell; } } } @@ -1231,7 +1231,7 @@ } primary = indx === 0 ? dir : primary; group = [ col, parseInt( dir, 10 ) || 0 ]; - c.sortList.push( group ); + c.sortList[ c.sortList.length ] = group; dir = $.inArray( group[ 1 ], order ); // fixes issue #167 c.sortVars[ col ].count = dir >= 0 ? dir : group[ 1 ] % ( c.sortReset ? 3 : 2 ); } @@ -1330,7 +1330,7 @@ }, addRows : function( c, $row, resort, callback ) { - var txt, val, tbodyIndex, rowIndex, rows, cellIndex, len, + var txt, val, tbodyIndex, rowIndex, rows, cellIndex, len, order, cacheIndex, rowData, cells, cell, span, // allow passing a row string if only one non-info tbody exists in the table valid = typeof $row === 'string' && c.$tbodies.length === 1 && /<tr/.test( $row || '' ), @@ -1365,12 +1365,13 @@ for ( rowIndex = 0; rowIndex < rows; rowIndex++ ) { cacheIndex = 0; len = $row[ rowIndex ].cells.length; + order = c.cache[ tbodyIndex ].normalized.length; cells = []; rowData = { child : [], raw : [], $row : $row.eq( rowIndex ), - order : c.cache[ tbodyIndex ].normalized.length + order : order }; // add each cell for ( cellIndex = 0; cellIndex < len; cellIndex++ ) { @@ -1393,7 +1394,7 @@ // add the row data to the end cells[ c.columns ] = rowData; // update cache - c.cache[ tbodyIndex ].normalized.push( cells ); + c.cache[ tbodyIndex ].normalized[ order ] = cells; } // resort using current settings ts.checkResort( c, resort, callback ); @@ -1435,7 +1436,7 @@ parsed = cache[ tbodyIndex ].normalized; totalRows = parsed.length; for ( rowIndex = 0; rowIndex < totalRows; rowIndex++ ) { - rows.push( parsed[ rowIndex ][ c.columns ].$row ); + rows[rows.length] = parsed[ rowIndex ][ c.columns ].$row; // removeRows used by the pager plugin; don't render if using ajax - fixes #411 if ( !c.appender || ( c.pager && ( !c.pager.removeRows || !wo.pager_removeRows ) && !c.pager.ajax ) ) { $curTbody.append( parsed[ rowIndex ][ c.columns ].$row ); @@ -1517,18 +1518,18 @@ arry = c.sortForce; for ( indx = 0; indx < arry.length; indx++ ) { if ( arry[ indx ][ 0 ] !== col ) { - c.sortList.push( arry[ indx ] ); + c.sortList[ c.sortList.length ] = arry[ indx ]; } } } // add column to sort list dir = order[ c.sortVars[ col ].count ]; if ( dir < 2 ) { - c.sortList.push( [ col, dir ] ); + c.sortList[ c.sortList.length ] = [ col, dir ]; // add other columns if header spans across multiple if ( cell.colSpan > 1 ) { for ( indx = 1; indx < cell.colSpan; indx++ ) { - c.sortList.push( [ col + indx, dir ] ); + c.sortList[ c.sortList.length ] = [ col + indx, dir ]; // update count on columns in colSpan c.sortVars[ col + indx ].count = $.inArray( dir, order ); } @@ -1557,11 +1558,11 @@ // add column to sort list array dir = order[ c.sortVars[ col ].count ]; if ( dir < 2 ) { - c.sortList.push( [ col, dir ] ); + c.sortList[ c.sortList.length ] = [ col, dir ]; // add other columns if header spans across multiple if ( cell.colSpan > 1 ) { for ( indx = 1; indx < cell.colSpan; indx++ ) { - c.sortList.push( [ col + indx, dir ] ); + c.sortList[ c.sortList.length ] = [ col + indx, dir ]; // update count on columns in colSpan c.sortVars[ col + indx ].count = $.inArray( dir, order ); } @@ -1598,7 +1599,7 @@ break; } } - c.sortList.push( [ arry[ indx ][ 0 ], dir ] ); + c.sortList[ c.sortList.length ] = [ arry[ indx ][ 0 ], dir ]; } } } @@ -1865,7 +1866,7 @@ ███████▀ ██ █████▀ ▀████▀ ██████ ██ █████▀ */ addWidget : function( widget ) { - ts.widgets.push( widget ); + ts.widgets[ ts.widgets.length ] = widget; }, hasWidget : function( $table, name ) { @@ -1913,7 +1914,7 @@ len = widgets.length; for ( indx = 0; indx < len; indx++ ) { if ( widgets[ indx ].match( widgetClass ) ) { - c.widgets.push( widgets[ indx ].replace( widgetClass, '$1' ) ); + c.widgets[ c.widgets.length ] = widgets[ indx ].replace( widgetClass, '$1' ); } } } @@ -1930,7 +1931,7 @@ applied = false; // add widget name to option list so it gets reapplied after sorting, filtering, etc if ( $.inArray( name, c.widgets ) < 0 ) { - c.widgets.push( name ); + c.widgets[ c.widgets.length ] = name; } if ( c.debug ) { time = new Date(); } @@ -2040,7 +2041,7 @@ for ( indx = 0; indx < len; indx++ ) { widget = ts.widgets[ indx ]; if ( widget && widget.id ) { - name.push( widget.id ); + name[ name.length ] = widget.id; } } } else { @@ -2081,7 +2082,7 @@ for ( indx = 0; indx < len; indx++ ) { widget = widgets[ indx ]; if ( widget && widget.id && ( doAll || $.inArray( widget.id, curWidgets ) < 0 ) ) { - list.push( widget.id ); + list[ list.length ] = widget.id; } } ts.removeWidget( table, list.join( ',' ), true ); @@ -2468,7 +2469,7 @@ console = {}; console.log = console.warn = console.error = console.table = function() { var arg = arguments.length > 1 ? arguments : arguments[0]; - ts.logs.push({ date: Date.now(), log: arg }); + ts.logs[ ts.logs.length ] = { date: Date.now(), log: arg }; }; } @@ -2735,7 +2736,7 @@ })( jQuery ); -/*! Widget: storage - updated 3/26/2015 (v2.21.3) */ +/*! Widget: storage - updated 3/1/2016 (v2.25.5) */ /*global JSON:false */ ;(function ($, window, document) { 'use strict'; @@ -2805,7 +2806,7 @@ } } // allow value to be an empty string too - if ((value || value === '') && window.JSON && JSON.hasOwnProperty('stringify')) { + if (typeof value !== 'undefined' && window.JSON && JSON.hasOwnProperty('stringify')) { // add unique identifiers = url pathname > table ID/index on page > data if (!values[url]) { values[url] = {}; @@ -3091,7 +3092,7 @@ })(jQuery); -/*! Widget: filter - updated 2/15/2016 (v2.25.4) *//* +/*! Widget: filter - updated 3/1/2016 (v2.25.5) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3138,6 +3139,7 @@ filter_hideFilters : false, // collapse filter row when mouse leaves the area filter_ignoreCase : true, // if true, make all searches case-insensitive filter_liveSearch : true, // if true, search column content while the user types ( with a delay ) + filter_matchType : { 'input': 'exact', 'select': 'exact' }, // global query settings ('exact' or 'match'); overridden by "filter-match" or "filter-exact" class filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available ( visible ) options within the drop down filter_placeholder : { search : '', select : '' }, // default placeholder text ( overridden by any header 'data-placeholder' setting ) filter_reset : null, // jQuery selector string of an element used to reset the filters @@ -4064,7 +4066,7 @@ end = c.columns - 1; } for ( ; start <= end; start++ ) { - columns.push( start ); + columns[ columns.length ] = start; } // remove processed range from val val = val.replace( ranges[ indx ], '' ); @@ -4078,7 +4080,7 @@ if ( singles[ i ] !== '' ) { indx = parseInt( singles[ i ], 10 ); if ( indx < c.columns ) { - columns.push( indx ); + columns[ columns.length ] = indx; } } } @@ -4086,7 +4088,7 @@ // return all columns if ( !columns.length ) { for ( indx = 0; indx < c.columns; indx++ ) { - columns.push( indx ); + columns[ columns.length ] = indx; } } return columns; @@ -4122,6 +4124,24 @@ } return filterMatched; }, + matchType: function( c, columnIndex ) { + var isMatch, + $el = c.$headerIndexed[ columnIndex ]; + // filter-exact > filter-match > filter_matchType for type + if ( $el.hasClass( 'filter-exact' ) ) { + isMatch = false; + } else if ( $el.hasClass( 'filter-match' ) ) { + isMatch = true; + } else { + // filter-select is not applied when filter_functions are used, so look for a select + $el = c.$filters.eq( columnIndex ).find( '.' + tscss.filter ); + isMatch = $el.length ? + c.widgetOptions.filter_matchType[ ( $el[ 0 ].nodeName || '' ).toLowerCase() ] === 'match' : + // default to exact, if no inputs found + false; + } + return isMatch; + }, processRow: function( c, data, vars ) { var result, filterMatched, fxn, ffxn, txt, @@ -4195,12 +4215,11 @@ // ignore if filter is empty or disabled if ( data.filter ) { data.cache = data.cacheArray[ columnIndex ]; - result = data.rawArray[ columnIndex ] || ''; + result = data.parsed[ columnIndex ] ? data.cache : data.rawArray[ columnIndex ] || ''; data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 data.iExact = !tsfRegex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; - - data.isMatch = c.$headerIndexed[ data.index ].hasClass( 'filter-match' ); + data.isMatch = tsf.matchType( c, columnIndex ); result = showRow; // if showRow is true, show that row @@ -4395,7 +4414,7 @@ !( tsfRegex.isNeg1.test( val ) || tsfRegex.isNeg2.test( val ) ) && // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 !( val !== '' && c.$filters && c.$filters.filter( '[data-column="' + indx + '"]' ).find( 'select' ).length && - !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); + !tsf.matchType( c, indx ) ); } } notFiltered = $rows.not( '.' + wo.filter_filteredRow ).length; @@ -4584,13 +4603,13 @@ // table cell to the parser format function if ( txt.text ) { txt.parsed = parsedTxt; - parsed.push( txt ); + parsed[ parsed.length ] = txt; } else { - parsed.push({ + parsed[ parsed.length ] = { text : txt, // check parser length - fixes #934 parsed : parsedTxt - }); + }; } } // sort parsed select options @@ -4615,7 +4634,7 @@ arry = []; len = parsed.length; for ( indx = 0; indx < len; indx++ ) { - arry.push( parsed[indx] ); + arry[ arry.length ] = parsed[indx]; } return arry; } @@ -4644,23 +4663,23 @@ if ( wo.filter_useParsedData || c.parsers[column].parsed || c.$headerIndexed[column].hasClass( 'filter-parsed' ) ) { - arry.push( '' + cache.normalized[ rowIndex ][ column ] ); + arry[ arry.length ] = '' + cache.normalized[ rowIndex ][ column ]; // child row parsed data if ( wo.filter_childRows && wo.filter_childByColumn ) { childLen = cache.normalized[ rowIndex ][ c.columns ].$row.length - 1; for ( indx = 0; indx < childLen; indx++ ) { - arry.push( '' + cache.normalized[ rowIndex ][ c.columns ].child[ indx ][ column ] ); + arry[ arry.length ] = '' + cache.normalized[ rowIndex ][ c.columns ].child[ indx ][ column ]; } } } else { // get raw cached data instead of content directly from the cells - arry.push( cache.normalized[ rowIndex ][ c.columns ].raw[ column ] ); + arry[ arry.length ] = cache.normalized[ rowIndex ][ c.columns ].raw[ column ]; // child row unparsed data if ( wo.filter_childRows && wo.filter_childByColumn ) { childLen = cache.normalized[ rowIndex ][ c.columns ].$row.length; for ( indx = 1; indx < childLen; indx++ ) { child = cache.normalized[ rowIndex ][ c.columns ].$row.eq( indx ).children().eq( column ); - arry.push( '' + ts.getElementText( c, child, column ) ); + arry[ arry.length ] = '' + ts.getElementText( c, child, column ); } } } @@ -4880,7 +4899,7 @@ })( jQuery ); -/*! Widget: stickyHeaders - updated 10/31/2015 (v2.24.0) *//* +/*! Widget: stickyHeaders - updated 3/1/2016 (v2.25.5) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -4925,12 +4944,12 @@ } wo.resize_flag = false; }; - checkSizes( false ); clearInterval(wo.resize_timer); if (disable) { wo.resize_flag = false; return false; } + checkSizes( false ); wo.resize_timer = setInterval(function() { if (wo.resize_flag) { return; } checkSizes(); @@ -5163,7 +5182,7 @@ .add(wo.stickyHeaders_yScroll) .add(wo.stickyHeaders_attachTo) .unbind( ('scroll resize '.split(' ').join(namespace)).replace(/\s+/g, ' ') ); - ts.addHeaderResizeEvent(table, false); + ts.addHeaderResizeEvent(table, true); } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 5152569..d827ab3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.25.4 *//* +/*! TableSorter (FORK) v2.25.5 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -21,7 +21,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.25.4', + version : '2.25.5', parsers : [], widgets : [], @@ -733,7 +733,7 @@ } } if ( add ) { - ts.parsers.push( parser ); + ts.parsers[ ts.parsers.length ] = parser; } }, @@ -953,7 +953,7 @@ } // ensure rowData is always in the same location (after the last column) cols[ c.columns ] = rowData; - cache.normalized.push( cols ); + cache.normalized[ cache.normalized.length ] = cols; } cache.colMax = colMax; // total up rows, not including child rows @@ -1022,9 +1022,9 @@ }); } if ( result !== false ) { - data.parsed.push( parsed ); - data.raw.push( raw ); - data.$cell.push( $cell ); + data.parsed[ data.parsed.length ] = parsed; + data.raw[ data.raw.length ] = raw; + data.$cell[ data.$cell.length ] = $cell; } } } @@ -1213,7 +1213,7 @@ } primary = indx === 0 ? dir : primary; group = [ col, parseInt( dir, 10 ) || 0 ]; - c.sortList.push( group ); + c.sortList[ c.sortList.length ] = group; dir = $.inArray( group[ 1 ], order ); // fixes issue #167 c.sortVars[ col ].count = dir >= 0 ? dir : group[ 1 ] % ( c.sortReset ? 3 : 2 ); } @@ -1312,7 +1312,7 @@ }, addRows : function( c, $row, resort, callback ) { - var txt, val, tbodyIndex, rowIndex, rows, cellIndex, len, + var txt, val, tbodyIndex, rowIndex, rows, cellIndex, len, order, cacheIndex, rowData, cells, cell, span, // allow passing a row string if only one non-info tbody exists in the table valid = typeof $row === 'string' && c.$tbodies.length === 1 && /<tr/.test( $row || '' ), @@ -1347,12 +1347,13 @@ for ( rowIndex = 0; rowIndex < rows; rowIndex++ ) { cacheIndex = 0; len = $row[ rowIndex ].cells.length; + order = c.cache[ tbodyIndex ].normalized.length; cells = []; rowData = { child : [], raw : [], $row : $row.eq( rowIndex ), - order : c.cache[ tbodyIndex ].normalized.length + order : order }; // add each cell for ( cellIndex = 0; cellIndex < len; cellIndex++ ) { @@ -1375,7 +1376,7 @@ // add the row data to the end cells[ c.columns ] = rowData; // update cache - c.cache[ tbodyIndex ].normalized.push( cells ); + c.cache[ tbodyIndex ].normalized[ order ] = cells; } // resort using current settings ts.checkResort( c, resort, callback ); @@ -1417,7 +1418,7 @@ parsed = cache[ tbodyIndex ].normalized; totalRows = parsed.length; for ( rowIndex = 0; rowIndex < totalRows; rowIndex++ ) { - rows.push( parsed[ rowIndex ][ c.columns ].$row ); + rows[rows.length] = parsed[ rowIndex ][ c.columns ].$row; // removeRows used by the pager plugin; don't render if using ajax - fixes #411 if ( !c.appender || ( c.pager && ( !c.pager.removeRows || !wo.pager_removeRows ) && !c.pager.ajax ) ) { $curTbody.append( parsed[ rowIndex ][ c.columns ].$row ); @@ -1499,18 +1500,18 @@ arry = c.sortForce; for ( indx = 0; indx < arry.length; indx++ ) { if ( arry[ indx ][ 0 ] !== col ) { - c.sortList.push( arry[ indx ] ); + c.sortList[ c.sortList.length ] = arry[ indx ]; } } } // add column to sort list dir = order[ c.sortVars[ col ].count ]; if ( dir < 2 ) { - c.sortList.push( [ col, dir ] ); + c.sortList[ c.sortList.length ] = [ col, dir ]; // add other columns if header spans across multiple if ( cell.colSpan > 1 ) { for ( indx = 1; indx < cell.colSpan; indx++ ) { - c.sortList.push( [ col + indx, dir ] ); + c.sortList[ c.sortList.length ] = [ col + indx, dir ]; // update count on columns in colSpan c.sortVars[ col + indx ].count = $.inArray( dir, order ); } @@ -1539,11 +1540,11 @@ // add column to sort list array dir = order[ c.sortVars[ col ].count ]; if ( dir < 2 ) { - c.sortList.push( [ col, dir ] ); + c.sortList[ c.sortList.length ] = [ col, dir ]; // add other columns if header spans across multiple if ( cell.colSpan > 1 ) { for ( indx = 1; indx < cell.colSpan; indx++ ) { - c.sortList.push( [ col + indx, dir ] ); + c.sortList[ c.sortList.length ] = [ col + indx, dir ]; // update count on columns in colSpan c.sortVars[ col + indx ].count = $.inArray( dir, order ); } @@ -1580,7 +1581,7 @@ break; } } - c.sortList.push( [ arry[ indx ][ 0 ], dir ] ); + c.sortList[ c.sortList.length ] = [ arry[ indx ][ 0 ], dir ]; } } } @@ -1847,7 +1848,7 @@ ███████▀ ██ █████▀ ▀████▀ ██████ ██ █████▀ */ addWidget : function( widget ) { - ts.widgets.push( widget ); + ts.widgets[ ts.widgets.length ] = widget; }, hasWidget : function( $table, name ) { @@ -1895,7 +1896,7 @@ len = widgets.length; for ( indx = 0; indx < len; indx++ ) { if ( widgets[ indx ].match( widgetClass ) ) { - c.widgets.push( widgets[ indx ].replace( widgetClass, '$1' ) ); + c.widgets[ c.widgets.length ] = widgets[ indx ].replace( widgetClass, '$1' ); } } } @@ -1912,7 +1913,7 @@ applied = false; // add widget name to option list so it gets reapplied after sorting, filtering, etc if ( $.inArray( name, c.widgets ) < 0 ) { - c.widgets.push( name ); + c.widgets[ c.widgets.length ] = name; } if ( c.debug ) { time = new Date(); } @@ -2022,7 +2023,7 @@ for ( indx = 0; indx < len; indx++ ) { widget = ts.widgets[ indx ]; if ( widget && widget.id ) { - name.push( widget.id ); + name[ name.length ] = widget.id; } } } else { @@ -2063,7 +2064,7 @@ for ( indx = 0; indx < len; indx++ ) { widget = widgets[ indx ]; if ( widget && widget.id && ( doAll || $.inArray( widget.id, curWidgets ) < 0 ) ) { - list.push( widget.id ); + list[ list.length ] = widget.id; } } ts.removeWidget( table, list.join( ',' ), true ); @@ -2450,7 +2451,7 @@ console = {}; console.log = console.warn = console.error = console.table = function() { var arg = arguments.length > 1 ? arguments : arguments[0]; - ts.logs.push({ date: Date.now(), log: arg }); + ts.logs[ ts.logs.length ] = { date: Date.now(), log: arg }; }; } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index e48ade6..4f4d5cb 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 02-15-2016 (v2.25.4)*/ +/*! tablesorter (FORK) - updated 03-01-2016 (v2.25.5)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! Widget: storage - updated 3/26/2015 (v2.21.3) */ +/*! Widget: storage - updated 3/1/2016 (v2.25.5) */ /*global JSON:false */ ;(function ($, window, document) { 'use strict'; @@ -86,7 +86,7 @@ } } // allow value to be an empty string too - if ((value || value === '') && window.JSON && JSON.hasOwnProperty('stringify')) { + if (typeof value !== 'undefined' && window.JSON && JSON.hasOwnProperty('stringify')) { // add unique identifiers = url pathname > table ID/index on page > data if (!values[url]) { values[url] = {}; @@ -372,7 +372,7 @@ })(jQuery); -/*! Widget: filter - updated 2/15/2016 (v2.25.4) *//* +/*! Widget: filter - updated 3/1/2016 (v2.25.5) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -419,6 +419,7 @@ filter_hideFilters : false, // collapse filter row when mouse leaves the area filter_ignoreCase : true, // if true, make all searches case-insensitive filter_liveSearch : true, // if true, search column content while the user types ( with a delay ) + filter_matchType : { 'input': 'exact', 'select': 'exact' }, // global query settings ('exact' or 'match'); overridden by "filter-match" or "filter-exact" class filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available ( visible ) options within the drop down filter_placeholder : { search : '', select : '' }, // default placeholder text ( overridden by any header 'data-placeholder' setting ) filter_reset : null, // jQuery selector string of an element used to reset the filters @@ -1345,7 +1346,7 @@ end = c.columns - 1; } for ( ; start <= end; start++ ) { - columns.push( start ); + columns[ columns.length ] = start; } // remove processed range from val val = val.replace( ranges[ indx ], '' ); @@ -1359,7 +1360,7 @@ if ( singles[ i ] !== '' ) { indx = parseInt( singles[ i ], 10 ); if ( indx < c.columns ) { - columns.push( indx ); + columns[ columns.length ] = indx; } } } @@ -1367,7 +1368,7 @@ // return all columns if ( !columns.length ) { for ( indx = 0; indx < c.columns; indx++ ) { - columns.push( indx ); + columns[ columns.length ] = indx; } } return columns; @@ -1403,6 +1404,24 @@ } return filterMatched; }, + matchType: function( c, columnIndex ) { + var isMatch, + $el = c.$headerIndexed[ columnIndex ]; + // filter-exact > filter-match > filter_matchType for type + if ( $el.hasClass( 'filter-exact' ) ) { + isMatch = false; + } else if ( $el.hasClass( 'filter-match' ) ) { + isMatch = true; + } else { + // filter-select is not applied when filter_functions are used, so look for a select + $el = c.$filters.eq( columnIndex ).find( '.' + tscss.filter ); + isMatch = $el.length ? + c.widgetOptions.filter_matchType[ ( $el[ 0 ].nodeName || '' ).toLowerCase() ] === 'match' : + // default to exact, if no inputs found + false; + } + return isMatch; + }, processRow: function( c, data, vars ) { var result, filterMatched, fxn, ffxn, txt, @@ -1476,12 +1495,11 @@ // ignore if filter is empty or disabled if ( data.filter ) { data.cache = data.cacheArray[ columnIndex ]; - result = data.rawArray[ columnIndex ] || ''; + result = data.parsed[ columnIndex ] ? data.cache : data.rawArray[ columnIndex ] || ''; data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 data.iExact = !tsfRegex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; - - data.isMatch = c.$headerIndexed[ data.index ].hasClass( 'filter-match' ); + data.isMatch = tsf.matchType( c, columnIndex ); result = showRow; // if showRow is true, show that row @@ -1676,7 +1694,7 @@ !( tsfRegex.isNeg1.test( val ) || tsfRegex.isNeg2.test( val ) ) && // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 !( val !== '' && c.$filters && c.$filters.filter( '[data-column="' + indx + '"]' ).find( 'select' ).length && - !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); + !tsf.matchType( c, indx ) ); } } notFiltered = $rows.not( '.' + wo.filter_filteredRow ).length; @@ -1865,13 +1883,13 @@ // table cell to the parser format function if ( txt.text ) { txt.parsed = parsedTxt; - parsed.push( txt ); + parsed[ parsed.length ] = txt; } else { - parsed.push({ + parsed[ parsed.length ] = { text : txt, // check parser length - fixes #934 parsed : parsedTxt - }); + }; } } // sort parsed select options @@ -1896,7 +1914,7 @@ arry = []; len = parsed.length; for ( indx = 0; indx < len; indx++ ) { - arry.push( parsed[indx] ); + arry[ arry.length ] = parsed[indx]; } return arry; } @@ -1925,23 +1943,23 @@ if ( wo.filter_useParsedData || c.parsers[column].parsed || c.$headerIndexed[column].hasClass( 'filter-parsed' ) ) { - arry.push( '' + cache.normalized[ rowIndex ][ column ] ); + arry[ arry.length ] = '' + cache.normalized[ rowIndex ][ column ]; // child row parsed data if ( wo.filter_childRows && wo.filter_childByColumn ) { childLen = cache.normalized[ rowIndex ][ c.columns ].$row.length - 1; for ( indx = 0; indx < childLen; indx++ ) { - arry.push( '' + cache.normalized[ rowIndex ][ c.columns ].child[ indx ][ column ] ); + arry[ arry.length ] = '' + cache.normalized[ rowIndex ][ c.columns ].child[ indx ][ column ]; } } } else { // get raw cached data instead of content directly from the cells - arry.push( cache.normalized[ rowIndex ][ c.columns ].raw[ column ] ); + arry[ arry.length ] = cache.normalized[ rowIndex ][ c.columns ].raw[ column ]; // child row unparsed data if ( wo.filter_childRows && wo.filter_childByColumn ) { childLen = cache.normalized[ rowIndex ][ c.columns ].$row.length; for ( indx = 1; indx < childLen; indx++ ) { child = cache.normalized[ rowIndex ][ c.columns ].$row.eq( indx ).children().eq( column ); - arry.push( '' + ts.getElementText( c, child, column ) ); + arry[ arry.length ] = '' + ts.getElementText( c, child, column ); } } } @@ -2161,7 +2179,7 @@ })( jQuery ); -/*! Widget: stickyHeaders - updated 10/31/2015 (v2.24.0) *//* +/*! Widget: stickyHeaders - updated 3/1/2016 (v2.25.5) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -2206,12 +2224,12 @@ } wo.resize_flag = false; }; - checkSizes( false ); clearInterval(wo.resize_timer); if (disable) { wo.resize_flag = false; return false; } + checkSizes( false ); wo.resize_timer = setInterval(function() { if (wo.resize_flag) { return; } checkSizes(); @@ -2444,7 +2462,7 @@ .add(wo.stickyHeaders_yScroll) .add(wo.stickyHeaders_attachTo) .unbind( ('scroll resize '.split(' ').join(namespace)).replace(/\s+/g, ' ') ); - ts.addHeaderResizeEvent(table, false); + ts.addHeaderResizeEvent(table, true); } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-huge-numbers.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-huge-numbers.js new file mode 100644 index 0000000..7cabf24 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-huge-numbers.js @@ -0,0 +1,21 @@ +/*! Parser: hugeNumbers - updated 3/1/2016 (v2.25.5) *//* + * See https://github.com/Mottie/tablesorter/issues/1161 + */ +/*jshint jquery:true */ +;( function( $ ) { + 'use strict'; + + $.tablesorter.addParser({ + id: 'hugeNumbers', + is : function() { + return false; + }, + format : function( str ) { + // add commas every 12 digits; Number.MAX_SAFE_INTEGER is 16 digits long + // regex modified from: http://stackoverflow.com/a/2901298/145346 + return str.replace(/\B(?=(\d{12})+(?!\d))/g, ','); + }, + type : 'text' + }); + +})( jQuery ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js index 9e782bf..7ea710a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js @@ -1,10 +1,10 @@ -/*! Widget: editable - updated 2/15/2016 (v2.25.4) *//* +/*! Widget: editable - updated 3/1/2016 (v2.25.5) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ -;( function( $ ){ +;( function( $ ) { 'use strict'; var tse = $.tablesorter.editable = { @@ -45,7 +45,7 @@ selection.addRange( range ); } } - // need delay of at least 100ms or last contenteditable will get refocused + // need delay of at least 100ms or last contenteditable will get refocused }, 100 ); }, @@ -165,7 +165,7 @@ // prevent enter from adding into the content $this .off( 'keydown' + namespace ) - .on( 'keydown' + namespace, function( e ){ + .on( 'keydown' + namespace, function( e ) { if ( wo.editable_enterToAccept && e.which === 13 && !e.shiftKey ) { e.preventDefault(); } @@ -256,6 +256,18 @@ // restore original content on blur $this.html( $this.data( 'original' ) ); } + }) + // paste plain text from Excel - fixes #994 + .on('paste' + namespace, '[contenteditable]', function() { + var content, + $this = $(this); + // setTimeout needed to get pasted-in content + setTimeout(function() { + if ($this.is(':focus')) { + content = '<div>' + $this.html() + '</div>'; + $this.html( $(content).text().trim() ); + } + }, 0); }); }, destroy : function( c, wo ) { @@ -265,7 +277,7 @@ tmp = ( 'updateComplete pagerComplete '.split( ' ' ).join( namespace + ' ' ) ).replace( /\s+/g, ' ' ); c.$table.off( tmp ); - tmp = ( 'focus blur focusout keydown '.split( ' ' ).join( namespace + ' ' ) ).replace( /\s+/g, ' ' ); + tmp = ( 'focus blur focusout keydown paste '.split( ' ' ).join( namespace + ' ' ) ).replace( /\s+/g, ' ' ); c.$tbodies .off( tmp ) .find( cols.join( ',' ) ) @@ -284,14 +296,14 @@ editable_autoResort : false, editable_wrapContent : '<div>', // wrap the cell content... makes this widget work in IE, and with autocomplete editable_trimContent : true, // trim content inside of contenteditable ( remove tabs & carriage returns ) - editable_validate : null, // function( text, originalText ){ return text; } + editable_validate : null, // function( text, originalText ) { return text; } editable_focused : null, // function( text, columnIndex, $element ) {} editable_blur : null, // function( text, columnIndex, $element ) { } editable_selectAll : false, // true/false or function( text, columnIndex, $element ) { return true; } editable_noEdit : 'no-edit', editable_editComplete : 'editComplete' }, - init: function( table, thisWidget, c, wo ){ + init: function( table, thisWidget, c, wo ) { if ( !wo.editable_columns.length ) { return; } tse.update( c, wo ); tse.bindEvents( c, wo ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 69df97e..c9b66ed 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 2/15/2016 (v2.25.4) *//* +/*! Widget: filter - updated 3/1/2016 (v2.25.5) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -45,6 +45,7 @@ filter_hideFilters : false, // collapse filter row when mouse leaves the area filter_ignoreCase : true, // if true, make all searches case-insensitive filter_liveSearch : true, // if true, search column content while the user types ( with a delay ) + filter_matchType : { 'input': 'exact', 'select': 'exact' }, // global query settings ('exact' or 'match'); overridden by "filter-match" or "filter-exact" class filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available ( visible ) options within the drop down filter_placeholder : { search : '', select : '' }, // default placeholder text ( overridden by any header 'data-placeholder' setting ) filter_reset : null, // jQuery selector string of an element used to reset the filters @@ -971,7 +972,7 @@ end = c.columns - 1; } for ( ; start <= end; start++ ) { - columns.push( start ); + columns[ columns.length ] = start; } // remove processed range from val val = val.replace( ranges[ indx ], '' ); @@ -985,7 +986,7 @@ if ( singles[ i ] !== '' ) { indx = parseInt( singles[ i ], 10 ); if ( indx < c.columns ) { - columns.push( indx ); + columns[ columns.length ] = indx; } } } @@ -993,7 +994,7 @@ // return all columns if ( !columns.length ) { for ( indx = 0; indx < c.columns; indx++ ) { - columns.push( indx ); + columns[ columns.length ] = indx; } } return columns; @@ -1029,6 +1030,24 @@ } return filterMatched; }, + matchType: function( c, columnIndex ) { + var isMatch, + $el = c.$headerIndexed[ columnIndex ]; + // filter-exact > filter-match > filter_matchType for type + if ( $el.hasClass( 'filter-exact' ) ) { + isMatch = false; + } else if ( $el.hasClass( 'filter-match' ) ) { + isMatch = true; + } else { + // filter-select is not applied when filter_functions are used, so look for a select + $el = c.$filters.eq( columnIndex ).find( '.' + tscss.filter ); + isMatch = $el.length ? + c.widgetOptions.filter_matchType[ ( $el[ 0 ].nodeName || '' ).toLowerCase() ] === 'match' : + // default to exact, if no inputs found + false; + } + return isMatch; + }, processRow: function( c, data, vars ) { var result, filterMatched, fxn, ffxn, txt, @@ -1102,12 +1121,11 @@ // ignore if filter is empty or disabled if ( data.filter ) { data.cache = data.cacheArray[ columnIndex ]; - result = data.rawArray[ columnIndex ] || ''; + result = data.parsed[ columnIndex ] ? data.cache : data.rawArray[ columnIndex ] || ''; data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 data.iExact = !tsfRegex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; - - data.isMatch = c.$headerIndexed[ data.index ].hasClass( 'filter-match' ); + data.isMatch = tsf.matchType( c, columnIndex ); result = showRow; // if showRow is true, show that row @@ -1302,7 +1320,7 @@ !( tsfRegex.isNeg1.test( val ) || tsfRegex.isNeg2.test( val ) ) && // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 !( val !== '' && c.$filters && c.$filters.filter( '[data-column="' + indx + '"]' ).find( 'select' ).length && - !c.$headerIndexed[indx].hasClass( 'filter-match' ) ); + !tsf.matchType( c, indx ) ); } } notFiltered = $rows.not( '.' + wo.filter_filteredRow ).length; @@ -1491,13 +1509,13 @@ // table cell to the parser format function if ( txt.text ) { txt.parsed = parsedTxt; - parsed.push( txt ); + parsed[ parsed.length ] = txt; } else { - parsed.push({ + parsed[ parsed.length ] = { text : txt, // check parser length - fixes #934 parsed : parsedTxt - }); + }; } } // sort parsed select options @@ -1522,7 +1540,7 @@ arry = []; len = parsed.length; for ( indx = 0; indx < len; indx++ ) { - arry.push( parsed[indx] ); + arry[ arry.length ] = parsed[indx]; } return arry; } @@ -1551,23 +1569,23 @@ if ( wo.filter_useParsedData || c.parsers[column].parsed || c.$headerIndexed[column].hasClass( 'filter-parsed' ) ) { - arry.push( '' + cache.normalized[ rowIndex ][ column ] ); + arry[ arry.length ] = '' + cache.normalized[ rowIndex ][ column ]; // child row parsed data if ( wo.filter_childRows && wo.filter_childByColumn ) { childLen = cache.normalized[ rowIndex ][ c.columns ].$row.length - 1; for ( indx = 0; indx < childLen; indx++ ) { - arry.push( '' + cache.normalized[ rowIndex ][ c.columns ].child[ indx ][ column ] ); + arry[ arry.length ] = '' + cache.normalized[ rowIndex ][ c.columns ].child[ indx ][ column ]; } } } else { // get raw cached data instead of content directly from the cells - arry.push( cache.normalized[ rowIndex ][ c.columns ].raw[ column ] ); + arry[ arry.length ] = cache.normalized[ rowIndex ][ c.columns ].raw[ column ]; // child row unparsed data if ( wo.filter_childRows && wo.filter_childByColumn ) { childLen = cache.normalized[ rowIndex ][ c.columns ].$row.length; for ( indx = 1; indx < childLen; indx++ ) { child = cache.normalized[ rowIndex ][ c.columns ].$row.eq( indx ).children().eq( column ); - arry.push( '' + ts.getElementText( c, child, column ) ); + arry[ arry.length ] = '' + ts.getElementText( c, child, column ); } } } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index c03c993..d0a3cd1 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -1,4 +1,4 @@ -/*! Widget: grouping - updated 2/15/2016 (v2.25.4) *//* +/*! Widget: grouping - updated 3/1/2016 (v2.25.5) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -92,9 +92,11 @@ return hours + ':' + min + ( wo.group_time24Hour ? '' : ' ' + ( suffix || '' ) ); }, - update : function(table, c, wo) { - if ($.isEmptyObject(c.cache)) { return; } - var hasSort = typeof c.sortList[0] !== 'undefined', + update : function(table) { + if ($.isEmptyObject(table.config.cache)) { return; } + var c = table.config, + wo = c.widgetOptions, + hasSort = typeof c.sortList[0] !== 'undefined', data = {}, column = $.isArray( wo.group_forceColumn ) && typeof wo.group_forceColumn[0] !== 'undefined' ? ( wo.group_enforceSort && !hasSort ? -1 : wo.group_forceColumn[0] ) : @@ -278,14 +280,14 @@ tsg.clearSavedGroups(table); }); c.$table.on('pagerChange.tsgrouping', function(){ - tsg.update(table, c, wo); + tsg.update(table); }); }, clearSavedGroups: function(table){ if (table && ts.storage) { ts.storage(table, 'tablesorter-groups', ''); - tsg.update(table, table.config, table.config.widgetOptions); + tsg.update(table); } } @@ -330,7 +332,7 @@ tsg.bindEvents(table, c, wo); }, format: function(table, c, wo) { - tsg.update(table, c, wo); + tsg.update(table); }, remove : function(table, c, wo){ c.$table diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index 587c930..8ccc798 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -1,4 +1,4 @@ -/*! Widget: math - updated 12/13/2015 (v2.25.0) *//* +/*! Widget: math - updated 3/1/2016 (v2.25.5) *//* * Requires tablesorter v2.16+ and jQuery 1.7+ * by Rob Garrison */ @@ -21,7 +21,7 @@ invalid : function( c, name, errorIndex ) { // name = function returning invalid results // errorIndex = math.error index with an explanation of the error - console.log( name, math.error[ errorIndex ] ); + console.warn( name, math.error[ errorIndex ] ); return c && c.widgetOptions.math_none || ''; // text for cell }, @@ -48,7 +48,7 @@ if ( hasFilter || !isFiltered ) { $cells = $row.children().not( '[' + wo.math_dataAttrib + '=ignore]' ); if ( wo.math_ignore.length ) { - $cells = $cells.filter( function( indx ) { + $cells = $cells.filter( function() { // using $.inArray is not optimal (needed for IE8) return $.inArray( math.getCellIndex( $( this ) ), wo.math_ignore ) === -1; }); @@ -87,7 +87,7 @@ if ( hasFilter ) { $tr = $tr.filter( hasFilter ); } - $t = $tr.children().filter( function( indx ) { + $t = $tr.children().filter( function() { return math.getCellIndex( $( this ) ) === cIndex; }); // ignore filtered rows & rows with data-math="ignore" (and starting row) @@ -99,7 +99,7 @@ if ( mathAbove ) { index = 0; } else if ( $t.length ) { - arry.push( math.processText( c, $t ) ); + arry[ arry.length ] = math.processText( c, $t ); } } index--; @@ -115,13 +115,13 @@ if ( hasFilter ) { $tr = $tr.filter( hasFilter ); } - $t = $tr.children().filter( function( indx ) { + $t = $tr.children().filter( function() { return math.getCellIndex( $( this ) ) === cIndex; }); if ( ( hasFilter || !$tr.hasClass( filtered ) ) && $tr.not( mathIgnore ).length && $t.length ) { - arry.push( math.processText( c, $t ) ); + arry[ arry.length ] = math.processText( c, $t ); } } } else { @@ -132,13 +132,13 @@ if ( hasFilter ) { $tr = $tr.filter( hasFilter ); } - $t = $tr.children().filter( function( indx ) { + $t = $tr.children().filter( function() { return math.getCellIndex( $( this ) ) === cIndex; }); if ( ( hasFilter || !$tr.hasClass( filtered ) ) && $t.not( mathAttrs.join( ',' ) ).length && !$t.is( $el ) ) { - arry.push( math.processText( c, $t ) ); + arry[ arry.length ] = math.processText( c, $t ); } } } @@ -168,7 +168,7 @@ $t = $cells.eq( cellIndex ); col = math.getCellIndex( $t ); if ( !$t.filter( '[' + mathAttr + ']' ).length && $.inArray( col, wo.math_ignore ) < 0 ) { - arry.push( math.processText( c, $t ) ); + arry[ arry.length ] = math.processText( c, $t ); } } } @@ -177,13 +177,11 @@ }, setColumnIndexes : function( c ) { - c.$table.after( '<div id="_tablesorter_table_placeholder"></div>' ); - // detach table from DOM to speed up column indexing - var $table = c.$table.detach(), + var $table = c.$table, last = 1, // only target rows with a colspan or rows included in a rowspan $rows = $table.children( 'tbody' ).children().filter( function() { - var cells, indx, len, + var cells, indx, $this = $( this ), include = $this.children( '[colspan]' ).length > 0; if ( last > 1 ) { @@ -205,9 +203,6 @@ // to every tbody cell, just the ones where the .cellIndex property doesn't match // the calculated cell index - hopefully fixes the lag issue in #1048 ts.computeColumnIndex( $rows, c ); - $( '#_tablesorter_table_placeholder' ) - .after( $table ) - .remove(); }, getCellIndex : function( $cell ) { @@ -250,11 +245,11 @@ len = $mathCells.length; // get math filter, if any // hasFilter = $row.attr( mathAttr + '-filter' ) || wo.math_rowFilter; - $mathCells.each( function( indx, cell ) { - var $cell = $( cell ), - filter = $mathCells.eq( indx ).attr( mathAttr + '-filter' ) || wo.math_rowFilter; + for (indx = 0; indx < len; indx++) { + var $cell = $mathCells.eq( indx ), + filter = $cell.attr( mathAttr + '-filter' ) || wo.math_rowFilter; filters[ filter ] = filters[ filter ] ? filters[ filter ].add( $cell ) : $cell; - }); + } $.each( filters, function( hasFilter, $cells ) { changed = math.mathType( c, $cells, [ 'all' ], hasFilter ) || changed; }); @@ -518,7 +513,7 @@ modes = [ el ]; maxCount = m; } else if ( m === maxCount ) { - modes.push( el ); + modes[ modes.length ] = el; maxCount = m; } } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 15f8f34..5d98811 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -443,7 +443,7 @@ p = c.pager, namespace = c.namespace + 'pager', sz = tsp.parsePageSize( c, p.size, 'get' ); // don't allow dividing by zero - if ( wo.pager_countChildRows ) { t.push( c.cssChildRow ); } + if ( wo.pager_countChildRows ) { t[ t.length ] = c.cssChildRow; } p.$size .add( p.$goto ) .removeClass( wo.pager_css.disabled ) @@ -542,10 +542,10 @@ optionPagesStartPage = largeCollection ? skipSetSize : 1; for ( i = optionPagesStartPage; i <= pg; ) { - optionPages.push( i ); + optionPages[ optionPages.length ] = i; i = i + ( largeCollection ? skipSetSize : 1 ); } - optionPages.push( pg ); + optionPages[ optionPages.length ] = pg; if ( largeCollection ) { focusOptionPages = []; @@ -558,7 +558,7 @@ if ( endPage > pg ) { endPage = pg; } // construct an array to get a focus set around the current page for ( i = startPage; i <= endPage ; i++ ) { - focusOptionPages.push( i ); + focusOptionPages[ focusOptionPages.length ] = i; } // keep unique values @@ -649,7 +649,7 @@ } else { $rows[ rowIndex ].style.display = ( size >= start && size < end ) ? '' : 'none'; if ( last !== size && size >= start && size < end ) { - p.cacheIndex.push( rowIndex ); + p.cacheIndex[ p.cacheIndex.length ] = rowIndex; last = size; } // don't count child rows @@ -875,7 +875,7 @@ sortCol = sortCol[ 1 ]; len = sortList.length; for ( indx = 0; indx < len; indx++ ) { - arry.push( sortCol + '[' + sortList[ indx ][ 0 ] + ']=' + sortList[ indx ][ 1 ] ); + arry[ arry.length ] = sortCol + '[' + sortList[ indx ][ 0 ] + ']=' + sortList[ indx ][ 1 ]; } // if the arry is empty, just add the col parameter... '&{sortList:col}' becomes '&col' url = url.replace( /\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join( '&' ) : sortCol ); @@ -886,7 +886,7 @@ len = filterList.length; for ( indx = 0; indx < len; indx++ ) { if ( filterList[ indx ] ) { - arry.push( filterCol + '[' + indx + ']=' + encodeURIComponent( filterList[ indx ] ) ); + arry[ arry.length ] = filterCol + '[' + indx + ']=' + encodeURIComponent( filterList[ indx ] ); } } // if the arry is empty, just add the fcol parameter... '&{filterList:fcol}' becomes '&fcol' @@ -945,7 +945,7 @@ count++; if ( count > s && added <= e ) { added++; - p.cacheIndex.push( index ); + p.cacheIndex[ p.cacheIndex.length ] = index; $tb.append( rows[ index ] ); } } @@ -1015,7 +1015,7 @@ normalized = c.cache[ 0 ].normalized; p.totalRows = normalized.length; for ( index = 0; index < p.totalRows; index++ ) { - rows.push( normalized[ index ][ c.columns ].$row ); + rows[ rows.length ] = normalized[ index ][ c.columns ].$row; } c.rowsCopy = rows; tsp.moveToPage( c, p, true ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js index 47b7beb..2e56b7c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js @@ -1,9 +1,9 @@ -/* Widget: print - updated 12/13/2015 (v2.25.0) *//* +/* Widget: print - updated 3/1/2016 (v2.25.5) *//* * Requires tablesorter v2.8+ and jQuery 1.2.6+ */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter, @@ -17,10 +17,11 @@ init : function(c) { c.$table .unbind(printTable.event) - .bind(printTable.event, function(){ + .bind(printTable.event, function() { // explicitly use table.config.widgetOptions because we want // the most up-to-date values; not the 'wo' from initialization printTable.process(c, c.widgetOptions); + return false; }); }, @@ -38,7 +39,7 @@ '@media print { .print_widget_hidden { display: none; } }'; // replace content with data-attribute content - $table.find('[' + wo.print_dataAttrib + ']').each(function(){ + $table.find('[' + wo.print_dataAttrib + ']').each(function() { $this = $(this); $this.text( $this.attr(wo.print_dataAttrib) ); }); @@ -137,7 +138,7 @@ init: function(table, thisWidget, c) { printTable.init(c); }, - remove: function(table, c){ + remove: function(table, c) { printTable.remove(c); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index 82a8730..e6a089d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -1,4 +1,4 @@ -/*! Widget: scroller - updated 10/31/2015 (v2.24.0) *//* +/*! Widget: scroller - updated 3/1/2016 (v2.25.5) *//* Copyright (C) 2011 T. Connell & Associates, Inc. Dual-licensed under the MIT and GPL licenses @@ -186,7 +186,7 @@ }, setup : function( c, wo ) { - var maxHt, tbHt, $hdr, $t, $hCells, $fCells, $tableWrap, events, tmp, detectedWidth, + var tbHt, $hdr, $t, $hCells, $fCells, $tableWrap, events, tmp, detectedWidth, $win = $( window ), tsScroller = ts.scroller, namespace = c.namespace + 'tsscroller', @@ -211,9 +211,10 @@ wo.scroller_barSetWidth = detectedWidth !== null ? detectedWidth : 15; } - maxHt = wo.scroller_height || 300; + tmp = $table.children( 'caption' ); $hdr = $( '<table class="' + $table.attr( 'class' ) + '" cellpadding=0 cellspacing=0>' + + ( tmp.length ? tmp[ 0 ].outerHTML : '' ) + $table.children( 'thead' )[ 0 ].outerHTML + '</table>' ); wo.scroller_$header = $hdr.addClass( c.namespace.slice( 1 ) + '_extra_table' ); @@ -247,8 +248,10 @@ .wrap( '<div class="' + tscss.scrollerHeader + '" />' ) .find( '.' + tscss.header ); - // use max-height, so the height resizes dynamically while filtering - $table.wrap( '<div class="' + tscss.scrollerTable + '" style="max-height:' + maxHt + 'px;" />' ); + // if max-height is greater than 0 use max-height, so the height resizes dynamically while filtering + // else let the table not have a vertical scroll + $table.wrap( '<div class="' + tscss.scrollerTable + + ( wo.scroller_height > 0 ? '" style="max-height:' + wo.scroller_height + 'px;">' : '">' ) ); $tableWrap = $table.parent(); // make scroller header sortable @@ -260,9 +263,8 @@ } $table - .find( 'thead' ) + .children( 'thead, caption' ) .addClass( tscss.scrollerHideElement ); - tbHt = $tableWrap.parent().height(); // The header will always jump into view if scrolling the table body @@ -444,7 +446,7 @@ .width( setWidth + temp ); // hide original table thead - $table.children( 'thead' ).addClass( tscss.scrollerHideElement ); + $table.children( 'thead, caption' ).addClass( tscss.scrollerHideElement ); // update fixed column sizes tsScroller.updateFixed( c, wo ); @@ -479,6 +481,8 @@ .removeClass( tscss.scrollerWrap ) .attr( 'id', '' ); + $fixedColumn.find('caption').html(' '); + if ( wo.scroller_addFixedOverlay ) { $fixedColumn.append( '<div class="' + tscss.scrollerFixedPanel + '">' ); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js index 695f6d5..fecc5a1 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js @@ -1,4 +1,4 @@ -/*! Widget: stickyHeaders - updated 10/31/2015 (v2.24.0) *//* +/*! Widget: stickyHeaders - updated 3/1/2016 (v2.25.5) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -43,12 +43,12 @@ } wo.resize_flag = false; }; - checkSizes( false ); clearInterval(wo.resize_timer); if (disable) { wo.resize_flag = false; return false; } + checkSizes( false ); wo.resize_timer = setInterval(function() { if (wo.resize_flag) { return; } checkSizes(); @@ -281,7 +281,7 @@ .add(wo.stickyHeaders_yScroll) .add(wo.stickyHeaders_attachTo) .unbind( ('scroll resize '.split(' ').join(namespace)).replace(/\s+/g, ' ') ); - ts.addHeaderResizeEvent(table, false); + ts.addHeaderResizeEvent(table, true); } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js index 6c482a3..948356a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js @@ -1,4 +1,4 @@ -/*! Widget: storage - updated 3/26/2015 (v2.21.3) */ +/*! Widget: storage - updated 3/1/2016 (v2.25.5) */ /*global JSON:false */ ;(function ($, window, document) { 'use strict'; @@ -68,7 +68,7 @@ } } // allow value to be an empty string too - if ((value || value === '') && window.JSON && JSON.hasOwnProperty('stringify')) { + if (typeof value !== 'undefined' && window.JSON && JSON.hasOwnProperty('stringify')) { // add unique identifiers = url pathname > table ID/index on page > data if (!values[url]) { values[url] = {}; From 3bd6f512540b8cc203b6b57546faacbe0d1224ea Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 21 Mar 2016 17:53:20 +0100 Subject: [PATCH 084/138] Update tablesorter to latest version (2.25.6) --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 23 ++++++++++++------- .../jquery-tablesorter/jquery.tablesorter.js | 9 ++++++-- .../jquery.tablesorter.widgets.js | 14 ++++++----- .../widgets/widget-filter.js | 12 ++++++---- .../widgets/widget-lazyload.js | 20 ++++++++++------ 9 files changed, 57 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c21a31..cf0b1b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.20.6 (2016-03-21) + +* Upgrade tablesorter to v2.25.6 + #### v1.20.5 (2016-03-02) * Upgrade tablesorter to v2.25.5 diff --git a/README.md b/README.md index cd16f96..16958a4 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.25.5 (3/1/2016), [documentation] +Current tablesorter version: 2.25.6 (3/18/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index a33dcfa..1d3f599 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 20 - TINY = 5 + TINY = 6 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index d089be2..fade388 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit d089be27345197a498517371e354b591c2ef865d +Subproject commit fade388523645c27dc0adaeb8542dc9ea79671a9 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 74882d6..fcda56c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 03-01-2016 (v2.25.5)*/ +/*! tablesorter (FORK) - updated 03-18-2016 (v2.25.6)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.25.5 *//* +/*! TableSorter (FORK) v2.25.6 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -39,7 +39,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.25.5', + version : '2.25.6', parsers : [], widgets : [], @@ -1866,6 +1866,9 @@ ███████▀ ██ █████▀ ▀████▀ ██████ ██ █████▀ */ addWidget : function( widget ) { + if ( widget.id && !ts.isEmptyObject( ts.getWidgetById( widget.id ) ) ) { + console.warn( '"' + widget.id + '" widget was loaded more than once!' ); + } ts.widgets[ ts.widgets.length ] = widget; }, @@ -1994,6 +1997,8 @@ // set priority to 10 if not defined if ( !widget.priority ) { widget.priority = 10; } widgets[ indx ] = widget; + } else if ( c.debug ) { + console.warn( '"' + names[ indx ] + '" widget code does not exist!' ); } } // sort widgets by priority @@ -3092,7 +3097,7 @@ })(jQuery); -/*! Widget: filter - updated 3/1/2016 (v2.25.5) *//* +/*! Widget: filter - updated 3/18/2016 (v2.25.6) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3132,7 +3137,7 @@ filter_defaultFilter : {}, // add a default column filter type '~{query}' to make fuzzy searches default; '{q1} AND {q2}' to make all searches use a logical AND. filter_excludeFilter : {}, // filters to exclude, per column filter_external : '', // jQuery selector string ( or jQuery object ) of external filters - filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin + filter_filteredRow : 'filtered', // class added to filtered rows; define in css with "display:none" to hide the filtered-out rows filter_formatter : null, // add custom filter elements to the filter row filter_functions : null, // add custom filter functions using this option filter_hideEmpty : true, // hide filter row when table is empty @@ -3496,8 +3501,8 @@ tsf.buildRow( table, c, wo ); } - txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' - .split( ' ' ).join( c.namespace + 'filter ' ); + txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset ' + + 'filterResetSaved filterEnd search '.split( ' ' ).join( c.namespace + 'filter ' ); c.$table.bind( txt, function( event, filter ) { val = wo.filter_hideEmpty && $.isEmptyObject( c.cache ) && @@ -3511,6 +3516,8 @@ if ( event.type === 'filterReset' ) { c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' ); tsf.searching( table, [] ); + } else if ( event.type === 'filterResetSaved' ) { + ts.storage( table, 'tablesorter-filters', '' ); } else if ( event.type === 'filterEnd' ) { tsf.buildDefault( table, true ); } else { @@ -3926,7 +3933,7 @@ // prevent errors if delay init is set if ( $.isEmptyObject( c.cache ) ) { // update cache if delayInit set & pager has initialized ( after user initiates a search ) - if ( c.delayInit && c.pager && c.pager.initialized ) { + if ( c.delayInit && ( !c.pager || c.pager && c.pager.initialized ) ) { ts.updateCache( c, function() { tsf.checkFilters( table, false, skipFirst ); }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index d827ab3..f48f54a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.25.5 *//* +/*! TableSorter (FORK) v2.25.6 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -21,7 +21,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.25.5', + version : '2.25.6', parsers : [], widgets : [], @@ -1848,6 +1848,9 @@ ███████▀ ██ █████▀ ▀████▀ ██████ ██ █████▀ */ addWidget : function( widget ) { + if ( widget.id && !ts.isEmptyObject( ts.getWidgetById( widget.id ) ) ) { + console.warn( '"' + widget.id + '" widget was loaded more than once!' ); + } ts.widgets[ ts.widgets.length ] = widget; }, @@ -1976,6 +1979,8 @@ // set priority to 10 if not defined if ( !widget.priority ) { widget.priority = 10; } widgets[ indx ] = widget; + } else if ( c.debug ) { + console.warn( '"' + names[ indx ] + '" widget code does not exist!' ); } } // sort widgets by priority diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 4f4d5cb..d6c33af 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 03-01-2016 (v2.25.5)*/ +/*! tablesorter (FORK) - updated 03-18-2016 (v2.25.6)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -372,7 +372,7 @@ })(jQuery); -/*! Widget: filter - updated 3/1/2016 (v2.25.5) *//* +/*! Widget: filter - updated 3/18/2016 (v2.25.6) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -412,7 +412,7 @@ filter_defaultFilter : {}, // add a default column filter type '~{query}' to make fuzzy searches default; '{q1} AND {q2}' to make all searches use a logical AND. filter_excludeFilter : {}, // filters to exclude, per column filter_external : '', // jQuery selector string ( or jQuery object ) of external filters - filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin + filter_filteredRow : 'filtered', // class added to filtered rows; define in css with "display:none" to hide the filtered-out rows filter_formatter : null, // add custom filter elements to the filter row filter_functions : null, // add custom filter functions using this option filter_hideEmpty : true, // hide filter row when table is empty @@ -776,8 +776,8 @@ tsf.buildRow( table, c, wo ); } - txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' - .split( ' ' ).join( c.namespace + 'filter ' ); + txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset ' + + 'filterResetSaved filterEnd search '.split( ' ' ).join( c.namespace + 'filter ' ); c.$table.bind( txt, function( event, filter ) { val = wo.filter_hideEmpty && $.isEmptyObject( c.cache ) && @@ -791,6 +791,8 @@ if ( event.type === 'filterReset' ) { c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' ); tsf.searching( table, [] ); + } else if ( event.type === 'filterResetSaved' ) { + ts.storage( table, 'tablesorter-filters', '' ); } else if ( event.type === 'filterEnd' ) { tsf.buildDefault( table, true ); } else { @@ -1206,7 +1208,7 @@ // prevent errors if delay init is set if ( $.isEmptyObject( c.cache ) ) { // update cache if delayInit set & pager has initialized ( after user initiates a search ) - if ( c.delayInit && c.pager && c.pager.initialized ) { + if ( c.delayInit && ( !c.pager || c.pager && c.pager.initialized ) ) { ts.updateCache( c, function() { tsf.checkFilters( table, false, skipFirst ); }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index c9b66ed..e7ed37a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 3/1/2016 (v2.25.5) *//* +/*! Widget: filter - updated 3/18/2016 (v2.25.6) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -38,7 +38,7 @@ filter_defaultFilter : {}, // add a default column filter type '~{query}' to make fuzzy searches default; '{q1} AND {q2}' to make all searches use a logical AND. filter_excludeFilter : {}, // filters to exclude, per column filter_external : '', // jQuery selector string ( or jQuery object ) of external filters - filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin + filter_filteredRow : 'filtered', // class added to filtered rows; define in css with "display:none" to hide the filtered-out rows filter_formatter : null, // add custom filter elements to the filter row filter_functions : null, // add custom filter functions using this option filter_hideEmpty : true, // hide filter row when table is empty @@ -402,8 +402,8 @@ tsf.buildRow( table, c, wo ); } - txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' - .split( ' ' ).join( c.namespace + 'filter ' ); + txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset ' + + 'filterResetSaved filterEnd search '.split( ' ' ).join( c.namespace + 'filter ' ); c.$table.bind( txt, function( event, filter ) { val = wo.filter_hideEmpty && $.isEmptyObject( c.cache ) && @@ -417,6 +417,8 @@ if ( event.type === 'filterReset' ) { c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' ); tsf.searching( table, [] ); + } else if ( event.type === 'filterResetSaved' ) { + ts.storage( table, 'tablesorter-filters', '' ); } else if ( event.type === 'filterEnd' ) { tsf.buildDefault( table, true ); } else { @@ -832,7 +834,7 @@ // prevent errors if delay init is set if ( $.isEmptyObject( c.cache ) ) { // update cache if delayInit set & pager has initialized ( after user initiates a search ) - if ( c.delayInit && c.pager && c.pager.initialized ) { + if ( c.delayInit && ( !c.pager || c.pager && c.pager.initialized ) ) { ts.updateCache( c, function() { tsf.checkFilters( table, false, skipFirst ); }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-lazyload.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-lazyload.js index b072de9..decdaca 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-lazyload.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-lazyload.js @@ -1,4 +1,4 @@ -/*! Widget: lazyload (BETA) - 10/31/2015 (v2.24.0) *//* +/*! Widget: lazyload (BETA) - 3/18/2016 (v2.25.6) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -15,11 +15,17 @@ $.event.special.scrollstop.latency = wo.lazyload_latency || 250; } ts.lazyload.update( c, wo ); - var events = [ wo.lazyload_update, 'pagerUpdate', wo.columnSelector_updated || 'columnUpdate', '' ] - .join( c.namespace + 'lazyload ' ); - c.$table.on( events, function() { - ts.lazyload.update( c, c.widgetOptions ); - }); + var namespace = c.namespace + 'lazyload ', + events = [ wo.lazyload_update, 'pagerUpdate', wo.columnSelector_updated || 'columnUpdate', '' ] + .join( namespace ); + c.$table + .on( events, function() { + ts.lazyload.update( c, c.widgetOptions ); + }) + .on( 'filterEnd' + namespace, function() { + // give lazyload a nudge after filtering the table. Fixes #1169 + $(window).scroll(); + }); }, update : function( c, wo ) { // add '.' if not already included @@ -57,7 +63,7 @@ lazyload_effect : 'show', lazyload_container : window, lazyload_data_attribute : 'original', - lazyload_skip_invisible : false, + lazyload_skip_invisible : true, lazyload_appear : null, lazyload_load : null, lazyload_placeholder : 'data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' From ab81b0da415d6e97c4116d06c0b55615b76b3afc Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Fri, 1 Apr 2016 20:50:09 +0200 Subject: [PATCH 085/138] Update tablesorter to latest version (2.25.7) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 31 +++++++++---- .../jquery-tablesorter/jquery.tablesorter.js | 4 +- .../jquery.tablesorter.widgets.js | 27 ++++++++--- .../widgets/widget-filter.js | 14 ++++-- .../widgets/widget-lazyload.js | 14 ++++-- .../widgets/widget-scroller.js | 45 +++++++++++++------ .../widgets/widget-stickyHeaders.js | 11 ++++- 11 files changed, 116 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf0b1b0..b7fd669 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.20.7 (2016-04-01) + +* Upgrade tablesorter to v2.25.7 + #### v1.20.6 (2016-03-21) * Upgrade tablesorter to v2.25.6 diff --git a/README.md b/README.md index 16958a4..7c27dc6 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.25.6 (3/18/2016), [documentation] +Current tablesorter version: 2.25.7 (4/1/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 1d3f599..cfdc840 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 20 - TINY = 6 + TINY = 7 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index fade388..9feb205 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit fade388523645c27dc0adaeb8542dc9ea79671a9 +Subproject commit 9feb20551eb17b6ac02fc8cadb5a468428d83bc6 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index fcda56c..8b6cf12 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 03-18-2016 (v2.25.6)*/ +/*! tablesorter (FORK) - updated 04-01-2016 (v2.25.7)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.25.6 *//* +/*! TableSorter (FORK) v2.25.7 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -39,7 +39,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.25.6', + version : '2.25.7', parsers : [], widgets : [], @@ -3097,7 +3097,7 @@ })(jQuery); -/*! Widget: filter - updated 3/18/2016 (v2.25.6) *//* +/*! Widget: filter - updated 4/1/2016 (v2.25.7) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -4133,6 +4133,7 @@ }, matchType: function( c, columnIndex ) { var isMatch, + wo = c.widgetOptions, $el = c.$headerIndexed[ columnIndex ]; // filter-exact > filter-match > filter_matchType for type if ( $el.hasClass( 'filter-exact' ) ) { @@ -4141,7 +4142,14 @@ isMatch = true; } else { // filter-select is not applied when filter_functions are used, so look for a select - $el = c.$filters.eq( columnIndex ).find( '.' + tscss.filter ); + if ( wo.filter_columnFilters ) { + $el = c.$filters + .find( '.' + tscss.filter ) + .add( wo.filter_$externalFilters ) + .filter( '[data-column="' + columnIndex + '"]' ); + } else if ( wo.filter_$externalFilters ) { + $el = wo.filter_$externalFilters.filter( '[data-column="' + columnIndex + '"]' ); + } isMatch = $el.length ? c.widgetOptions.filter_matchType[ ( $el[ 0 ].nodeName || '' ).toLowerCase() ] === 'match' : // default to exact, if no inputs found @@ -4232,7 +4240,7 @@ // in case select filter option has a different value vs text 'a - z|A through Z' ffxn = wo.filter_columnFilters ? - c.$filters.add( c.$externalFilters ) + c.$filters.add( wo.filter_$externalFilters ) .filter( '[data-column="' + columnIndex + '"]' ) .find( 'select option:selected' ) .attr( 'data-function-name' ) || '' : ''; @@ -4906,7 +4914,7 @@ })( jQuery ); -/*! Widget: stickyHeaders - updated 3/1/2016 (v2.25.5) *//* +/*! Widget: stickyHeaders - updated 4/1/2016 (v2.25.7) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -5174,6 +5182,13 @@ } } + // resize table (Firefox) + if (wo.stickyHeaders_addResizeEvent) { + $table.bind('resize' + c.namespace + 'stickyheaders', function() { + resizeHeader(); + }); + } + $table.triggerHandler('stickyHeadersInit'); }, @@ -5181,7 +5196,7 @@ var namespace = c.namespace + 'stickyheaders '; c.$table .removeClass('hasStickyHeaders') - .unbind( ('pagerComplete filterEnd stickyHeadersUpdate '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) + .unbind( ('pagerComplete resize filterEnd stickyHeadersUpdate '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) .next('.' + ts.css.stickyWrap).remove(); if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table $(window) diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index f48f54a..134978f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.25.6 *//* +/*! TableSorter (FORK) v2.25.7 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -21,7 +21,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.25.6', + version : '2.25.7', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index d6c33af..a832976 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 03-18-2016 (v2.25.6)*/ +/*! tablesorter (FORK) - updated 04-01-2016 (v2.25.7)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -372,7 +372,7 @@ })(jQuery); -/*! Widget: filter - updated 3/18/2016 (v2.25.6) *//* +/*! Widget: filter - updated 4/1/2016 (v2.25.7) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -1408,6 +1408,7 @@ }, matchType: function( c, columnIndex ) { var isMatch, + wo = c.widgetOptions, $el = c.$headerIndexed[ columnIndex ]; // filter-exact > filter-match > filter_matchType for type if ( $el.hasClass( 'filter-exact' ) ) { @@ -1416,7 +1417,14 @@ isMatch = true; } else { // filter-select is not applied when filter_functions are used, so look for a select - $el = c.$filters.eq( columnIndex ).find( '.' + tscss.filter ); + if ( wo.filter_columnFilters ) { + $el = c.$filters + .find( '.' + tscss.filter ) + .add( wo.filter_$externalFilters ) + .filter( '[data-column="' + columnIndex + '"]' ); + } else if ( wo.filter_$externalFilters ) { + $el = wo.filter_$externalFilters.filter( '[data-column="' + columnIndex + '"]' ); + } isMatch = $el.length ? c.widgetOptions.filter_matchType[ ( $el[ 0 ].nodeName || '' ).toLowerCase() ] === 'match' : // default to exact, if no inputs found @@ -1507,7 +1515,7 @@ // in case select filter option has a different value vs text 'a - z|A through Z' ffxn = wo.filter_columnFilters ? - c.$filters.add( c.$externalFilters ) + c.$filters.add( wo.filter_$externalFilters ) .filter( '[data-column="' + columnIndex + '"]' ) .find( 'select option:selected' ) .attr( 'data-function-name' ) || '' : ''; @@ -2181,7 +2189,7 @@ })( jQuery ); -/*! Widget: stickyHeaders - updated 3/1/2016 (v2.25.5) *//* +/*! Widget: stickyHeaders - updated 4/1/2016 (v2.25.7) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -2449,6 +2457,13 @@ } } + // resize table (Firefox) + if (wo.stickyHeaders_addResizeEvent) { + $table.bind('resize' + c.namespace + 'stickyheaders', function() { + resizeHeader(); + }); + } + $table.triggerHandler('stickyHeadersInit'); }, @@ -2456,7 +2471,7 @@ var namespace = c.namespace + 'stickyheaders '; c.$table .removeClass('hasStickyHeaders') - .unbind( ('pagerComplete filterEnd stickyHeadersUpdate '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) + .unbind( ('pagerComplete resize filterEnd stickyHeadersUpdate '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) .next('.' + ts.css.stickyWrap).remove(); if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table $(window) diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index e7ed37a..726beee 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 3/18/2016 (v2.25.6) *//* +/*! Widget: filter - updated 4/1/2016 (v2.25.7) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -1034,6 +1034,7 @@ }, matchType: function( c, columnIndex ) { var isMatch, + wo = c.widgetOptions, $el = c.$headerIndexed[ columnIndex ]; // filter-exact > filter-match > filter_matchType for type if ( $el.hasClass( 'filter-exact' ) ) { @@ -1042,7 +1043,14 @@ isMatch = true; } else { // filter-select is not applied when filter_functions are used, so look for a select - $el = c.$filters.eq( columnIndex ).find( '.' + tscss.filter ); + if ( wo.filter_columnFilters ) { + $el = c.$filters + .find( '.' + tscss.filter ) + .add( wo.filter_$externalFilters ) + .filter( '[data-column="' + columnIndex + '"]' ); + } else if ( wo.filter_$externalFilters ) { + $el = wo.filter_$externalFilters.filter( '[data-column="' + columnIndex + '"]' ); + } isMatch = $el.length ? c.widgetOptions.filter_matchType[ ( $el[ 0 ].nodeName || '' ).toLowerCase() ] === 'match' : // default to exact, if no inputs found @@ -1133,7 +1141,7 @@ // in case select filter option has a different value vs text 'a - z|A through Z' ffxn = wo.filter_columnFilters ? - c.$filters.add( c.$externalFilters ) + c.$filters.add( wo.filter_$externalFilters ) .filter( '[data-column="' + columnIndex + '"]' ) .find( 'select option:selected' ) .attr( 'data-function-name' ) || '' : ''; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-lazyload.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-lazyload.js index decdaca..e9b4b6c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-lazyload.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-lazyload.js @@ -1,4 +1,4 @@ -/*! Widget: lazyload (BETA) - 3/18/2016 (v2.25.6) *//* +/*! Widget: lazyload (BETA) - 4/1/2016 (v2.25.7) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -16,8 +16,12 @@ } ts.lazyload.update( c, wo ); var namespace = c.namespace + 'lazyload ', - events = [ wo.lazyload_update, 'pagerUpdate', wo.columnSelector_updated || 'columnUpdate', '' ] - .join( namespace ); + events = [ + wo.lazyload_update, + 'pagerUpdate', + wo.columnSelector_updated || 'columnUpdate', + '' + ].join( namespace ); c.$table .on( events, function() { ts.lazyload.update( c, c.widgetOptions ); @@ -42,6 +46,10 @@ load : wo.lazyload_load, placeholder : wo.lazyload_placeholder }); + // give lazyload a nudge after updating the table. Fixes #1169 + setTimeout(function() { + $(window).scroll(); + }, 1); }, remove : function( c, wo ) { c.$table.off( c.namespace + 'lazyload' ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index e6a089d..a0e1377 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -1,4 +1,4 @@ -/*! Widget: scroller - updated 3/1/2016 (v2.25.5) *//* +/*! Widget: scroller - updated 4/1/2016 (v2.25.7) *//* Copyright (C) 2011 T. Connell & Associates, Inc. Dual-licensed under the MIT and GPL licenses @@ -363,7 +363,7 @@ resize : function( c, wo ) { if ( wo.scroller_isBusy ) { return; } - var index, borderWidth, setWidth, $hCells, $bCells, $fCells, $headers, $this, temp, + var index, borderWidth, setWidth, $headers, $this, temp, tsScroller = ts.scroller, $container = wo.scroller_$container, $table = c.$table, @@ -563,6 +563,28 @@ }, + // https://remysharp.com/2010/07/21/throttling-function-calls + throttle : function(fn, threshhold, scope) { + threshhold = threshhold || 50; + var last, deferTimer; + return function() { + var context = scope || this, + now = +(new Date()), + args = arguments; + if (last && now < last + threshhold) { + // hold on to it + clearTimeout(deferTimer); + deferTimer = setTimeout(function() { + last = now; + fn.apply(context, args); + }, threshhold); + } else { + last = now; + fn.apply(context, args); + } + }; + }, + bindFixedColumnEvents : function( c, wo ) { // update thead & tbody in fixed column var tsScroller = ts.scroller, @@ -576,10 +598,9 @@ .parent() // *** SCROLL *** scroll fixed column along with main .off( events ) - .on( events, function() { - if ( wo.scroller_isBusy ) { return; } + .on( events, tsScroller.throttle(function() { // using flags to prevent firing the scroll event excessively leading to slow scrolling in Firefox - if ( !wo.scroller_isBusy && ( fixedScroll || !( tsScroller.isFirefox || tsScroller.isIE ) ) ) { + if ( !wo.scroller_isBusy && fixedScroll ) { tableScroll = false; var $this = $( this ); $fixedTbody[0].scrollTop = wo.scroller_saved[1] = $this.scrollTop(); @@ -588,21 +609,20 @@ tableScroll = true; }, 20 ); } - }); + })); // scroll main along with fixed column $fixedTbody .off( events ) - .on( events, function() { + .on( events, tsScroller.throttle(function() { // using flags to prevent firing the scroll event excessively leading to slow scrolling in Firefox - if ( !wo.scroller_isBusy && ( tableScroll || !( tsScroller.isFirefox || tsScroller.isIE ) ) ) { + if ( !wo.scroller_isBusy && tableScroll ) { fixedScroll = false; - var $this = $( this ); - c.$table.parent()[0].scrollTop = wo.scroller_saved[1] = $this.scrollTop(); + c.$table.parent()[0].scrollTop = wo.scroller_saved[1] = $( this ).scrollTop(); setTimeout( function() { fixedScroll = true; }, 20 ); } - }) + })) .scroll(); // *** ROW HIGHLIGHT *** @@ -685,8 +705,7 @@ } // scroller_fixedColumns - var index, tbodyIndex, rowIndex, $tbody, $adjCol, $fb, $fixHead, $fixBody, $fixFoot, - totalRows, row, + var index, tbodyIndex, rowIndex, $tbody, $adjCol, $fb, totalRows, // source cells for measurement $mainTbodies = wo.scroller_$container diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js index fecc5a1..44fd825 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js @@ -1,4 +1,4 @@ -/*! Widget: stickyHeaders - updated 3/1/2016 (v2.25.5) *//* +/*! Widget: stickyHeaders - updated 4/1/2016 (v2.25.7) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -266,6 +266,13 @@ } } + // resize table (Firefox) + if (wo.stickyHeaders_addResizeEvent) { + $table.bind('resize' + c.namespace + 'stickyheaders', function() { + resizeHeader(); + }); + } + $table.triggerHandler('stickyHeadersInit'); }, @@ -273,7 +280,7 @@ var namespace = c.namespace + 'stickyheaders '; c.$table .removeClass('hasStickyHeaders') - .unbind( ('pagerComplete filterEnd stickyHeadersUpdate '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) + .unbind( ('pagerComplete resize filterEnd stickyHeadersUpdate '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) .next('.' + ts.css.stickyWrap).remove(); if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table $(window) From 92f3e52b5ee05ddbdb5a7df8c98442c330dc55b3 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Wed, 13 Apr 2016 19:55:27 +0200 Subject: [PATCH 086/138] Update tablesorter to latest version (2.25.8) --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 17 ++++++++++++----- .../jquery-tablesorter/jquery.tablesorter.js | 15 +++++++++++---- .../jquery.tablesorter.widgets.js | 2 +- .../jquery-tablesorter/widgets/widget-print.js | 11 +++++++++-- .../jquery-tablesorter/theme.bootstrap.css | 3 +++ .../jquery-tablesorter/theme.bootstrap_2.css | 3 +++ 10 files changed, 46 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7fd669..8e02d75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.20.8 (2016-04-13) + +* Upgrade tablesorter to v2.25.8 + #### v1.20.7 (2016-04-01) * Upgrade tablesorter to v2.25.7 diff --git a/README.md b/README.md index 7c27dc6..488375a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.25.7 (4/1/2016), [documentation] +Current tablesorter version: 2.25.8 (4/11/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index cfdc840..2b7acc4 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 20 - TINY = 7 + TINY = 8 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 9feb205..77b5034 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 9feb20551eb17b6ac02fc8cadb5a468428d83bc6 +Subproject commit 77b503431c0bb1ee4289ff22c986237166fb6447 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 8b6cf12..8c4f1b2 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 04-01-2016 (v2.25.7)*/ +/*! tablesorter (FORK) - updated 04-11-2016 (v2.25.8)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.25.7 *//* +/*! TableSorter (FORK) v2.25.8 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -39,7 +39,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.25.7', + version : '2.25.8', parsers : [], widgets : [], @@ -959,8 +959,15 @@ index = 0; while ( index <= span ) { // duplicate text (or not) to spanned columns - rowData.raw[ cacheIndex + index ] = c.duplicateSpan || index === 0 ? val : ''; - cols[ cacheIndex + index ] = c.duplicateSpan || index === 0 ? val : ''; + // instead of setting duplicate span to empty string, use textExtraction to try to get a value + // see http://stackoverflow.com/q/36449711/145346 + txt = c.duplicateSpan || index === 0 ? + val : + typeof c.textExtraction !== 'string' ? + ts.getElementText( c, cell, cacheIndex + index ) || '' : + ''; + rowData.raw[ cacheIndex + index ] = txt; + cols[ cacheIndex + index ] = txt; index++; } cacheIndex += span; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 134978f..734787b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.25.7 *//* +/*! TableSorter (FORK) v2.25.8 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -21,7 +21,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.25.7', + version : '2.25.8', parsers : [], widgets : [], @@ -941,8 +941,15 @@ index = 0; while ( index <= span ) { // duplicate text (or not) to spanned columns - rowData.raw[ cacheIndex + index ] = c.duplicateSpan || index === 0 ? val : ''; - cols[ cacheIndex + index ] = c.duplicateSpan || index === 0 ? val : ''; + // instead of setting duplicate span to empty string, use textExtraction to try to get a value + // see http://stackoverflow.com/q/36449711/145346 + txt = c.duplicateSpan || index === 0 ? + val : + typeof c.textExtraction !== 'string' ? + ts.getElementText( c, cell, cacheIndex + index ) || '' : + ''; + rowData.raw[ cacheIndex + index ] = txt; + cols[ cacheIndex + index ] = txt; index++; } cacheIndex += span; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index a832976..3a06a78 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 04-01-2016 (v2.25.7)*/ +/*! tablesorter (FORK) - updated 04-11-2016 (v2.25.8)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js index 2e56b7c..f57e4b3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js @@ -1,4 +1,4 @@ -/* Widget: print - updated 3/1/2016 (v2.25.5) *//* +/* Widget: print - updated 4/11/2016 (v2.25.8) *//* * Requires tablesorter v2.8+ and jQuery 1.2.6+ */ /*jshint browser:true, jquery:true, unused:false */ @@ -26,7 +26,7 @@ }, process : function(c, wo) { - var $this, + var $this, data, $table = $('<div/>').append(c.$table.clone()), printStyle = printTable.basicStyle + 'table { width: 100%; }' + // hide filter row @@ -44,6 +44,13 @@ $this.text( $this.attr(wo.print_dataAttrib) ); }); + // Make sure all lazy loaded images are visible - see #1169 + data = 'data-' + (wo.lazyload_data_attribute || 'original'); + $table.find('img[' + data + ']').each(function(){ + $this = $(this); + $this.attr('src', $this.attr(data)); + }); + // === rows === // Assume 'visible' means rows hidden by the pager (rows set to 'display:none') // or hidden by a class name which is added to the wo.print_extraCSS definition diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index 11a3ea7..0b8b48f 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -19,6 +19,9 @@ .tablesorter-bootstrap .tablesorter-header { cursor: pointer; } +.tablesorter-bootstrap .sorter-false { + cursor: default; +} .tablesorter-bootstrap .tablesorter-header-inner { position: relative; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css index fe0665b..aed88a6 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css @@ -31,6 +31,9 @@ .tablesorter-bootstrap .tablesorter-header { cursor: pointer; } +.tablesorter-bootstrap .sorter-false { + cursor: default; +} .tablesorter-bootstrap .tablesorter-header-inner { position: relative; From 753ddfd10e1620672de5d1f8ebfda890cee75950 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Sun, 8 May 2016 20:01:04 +0200 Subject: [PATCH 087/138] Update tablesorter to latest version (2.26.0) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 4 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 64 +++++++++--------- .../jquery.tablesorter.combined.js | 66 +++++++++++-------- .../jquery-tablesorter/jquery.tablesorter.js | 4 +- .../jquery.tablesorter.widgets.js | 62 ++++++++++------- .../parsers/parser-input-select.js | 38 ++++++----- .../widgets/widget-columnSelector.js | 54 +++++++++------ .../widgets/widget-filter-formatter-jui.js | 6 +- .../widgets/widget-filter.js | 48 ++++++++------ .../widgets/widget-pager.js | 63 ++++++++++-------- .../widgets/widget-scroller.js | 5 +- .../widgets/widget-stickyHeaders.js | 12 ++-- 15 files changed, 250 insertions(+), 184 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e02d75..26484f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.21.0 (2016-05-08) + +* Upgrade tablesorter to v2.26.0 + #### v1.20.8 (2016-04-13) * Upgrade tablesorter to v2.25.8 diff --git a/README.md b/README.md index 488375a..6ee7fbf 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.25.8 (4/11/2016), [documentation] +Current tablesorter version: 2.26.0 (5/1/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 2b7acc4..a1c2076 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 - MINOR = 20 - TINY = 8 + MINOR = 21 + TINY = 0 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 77b5034..6d76080 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 77b503431c0bb1ee4289ff22c986237166fb6447 +Subproject commit 6d76080f8ed2aea800b055902e3ca24c66f255cd diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 7723497..0e4105f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin - * updated 11/22/2015 (v2.24.6) + * updated 5/1/2016 (v2.26.0) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -167,6 +167,7 @@ c = table.config, namespace = c.namespace + 'pager', sz = parsePageSize( p, p.size, 'get' ); // don't allow dividing by zero + if (sz === 'all') { sz = p.totalRows; } if (p.countChildRows) { t[ t.length ] = c.cssChildRow; } p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method c.totalRows = p.totalRows; @@ -175,10 +176,10 @@ c.filteredRows = p.filteredRows; p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; if ( getTotalPages( table, p ) >= 0 ) { - t = (p.size * p.page > p.filteredRows) && completed; + t = (sz * p.page > p.filteredRows) && completed; p.page = (t) ? p.pageReset || 0 : p.page; - p.startRow = (t) ? p.size * p.page + 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1); - p.endRow = Math.min( p.filteredRows, p.totalRows, p.size * ( p.page + 1 ) ); + p.startRow = (t) ? sz * p.page + 1 : (p.filteredRows === 0 ? 0 : sz * p.page + 1); + p.endRow = Math.min( p.filteredRows, p.totalRows, sz * ( p.page + 1 ) ); $out = p.$container.find(p.cssPageDisplay); // form the output string (can now get a new output string from the server) s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || p.output : p.output ) @@ -216,7 +217,7 @@ // rebind startRow/page inputs $out.find('.ts-startRow, .ts-page').unbind('change' + namespace).bind('change' + namespace, function(){ var v = $(this).val(), - pg = $(this).hasClass('ts-startRow') ? Math.floor( v / p.size ) + 1 : v; + pg = $(this).hasClass('ts-startRow') ? Math.floor( v / sz ) + 1 : v; c.$table.triggerHandler('pageSet' + namespace, [ pg ]); }); } @@ -232,7 +233,7 @@ if (p.savePages && ts.storage) { ts.storage(table, p.storageKey, { page : p.page, - size : p.size + size : sz === p.totalRows ? 'all' : sz }); } } @@ -307,7 +308,8 @@ h = $.data(table, 'pagerSavedHeight'); if (h) { d = h - $b.height(); - if ( d > 5 && $.data(table, 'pagerLastSize') === p.size && $b.children('tr:visible').length < p.size ) { + if ( d > 5 && $.data(table, 'pagerLastSize') === p.size && + $b.children('tr:visible').length < (p.size === 'all' ? p.totalRows : p.size) ) { $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice(1) + '" style="height:' + d + 'px;"></tr>'); } } @@ -322,7 +324,7 @@ if (!$b.children('tr:visible').length) { $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice(1) + '"><td> </td></tr>'); } - h = $b.children('tr').eq(0).height() * p.size; + h = $b.children('tr').eq(0).height() * (p.size === 'all' ? p.totalRows : p.size); $.data(table, 'pagerSavedHeight', h); fixHeight(table, p); $.data(table, 'pagerLastSize', p.size); @@ -335,8 +337,9 @@ c = table.config, rows = c.$tbodies.eq(0).children('tr'), l = rows.length, - s = ( p.page * p.size ), - e = s + p.size, + sz = p.size === 'all' ? p.totalRows : p.size, + s = ( p.page * sz ), + e = s + sz, f = c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered', last = 0, // for cache indexing j = 0; // size counter @@ -371,7 +374,7 @@ hideRowsSetup = function(table, p){ p.size = parsePageSize( p, p.$size.val(), 'get' ); - p.$size.val( parsePageSize( p, p.size, 'set' ) ); + p.$size.val( p.size ); $.data(table, 'pagerLastSize', p.size); pagerArrows( table, p ); if ( !p.removeRows ) { @@ -392,7 +395,7 @@ table.config.$tbodies.eq(0).empty(); // ajaxProcessing result: [ total, rows, headers ] - var i, j, t, hsh, $f, $sh, $headers, $h, icon, th, d, l, rr_count, len, + var i, j, t, hsh, $f, $sh, $headers, $h, icon, th, d, l, rr_count, len, sz, c = table.config, $table = c.$table, tds = '', @@ -483,9 +486,10 @@ if (c.showProcessing) { ts.isProcessing(table); // remove loading icon } + sz = parsePageSize( p, p.size, 'get' ); // make sure last pager settings are saved, prevents multiple server side calls with // the same parameters - p.totalPages = Math.ceil( p.totalRows / parsePageSize( p, p.size, 'get' ) ); + p.totalPages = sz === 'all' ? 1 : Math.ceil( p.totalRows / sz ); p.last.totalRows = p.totalRows; p.last.currentFilters = p.currentFilters; p.last.sortList = (c.sortList || []).join(','); @@ -555,6 +559,7 @@ url = (p.ajaxUrl) ? p.ajaxUrl // allow using "{page+1}" in the url string to switch to a non-zero based index .replace(/\{page([\-+]\d+)?\}/, function(s, n){ return p.page + (n ? parseInt(n, 10) : 0); }) + // this will pass "all" to server when size is set to "all" .replace(/\{size\}/g, p.size) : '', sortList = c.sortList, filterList = p.currentFilters || $(table).data('lastSearch') || [], @@ -598,8 +603,8 @@ c = table.config, f = c.$table.hasClass('hasFilters'), l = rows && rows.length || 0, // rows may be undefined - s = ( p.page * p.size ), - e = p.size; + e = p.size === 'all' ? p.totalRows : p.size, + s = ( p.page * e ); if ( l < 1 ) { if (c.debug) { console.warn('Pager: >> No rows for pager to render'); @@ -659,7 +664,7 @@ $.data(table, 'pagerLastPage', p.page); $.data(table, 'pagerLastSize', p.size); p.page = 0; - p.size = p.totalRows; + p.size = 'all'; p.totalPages = 1; $(table) .addClass('pagerDisabled') @@ -777,13 +782,10 @@ // set to either set or get value parsePageSize = function( p, size, mode ) { - var s = parseInt( size, 10 ) || p.size || p.settings.size || 10, - // if select does not contain an "all" option, use size - setAll = p.$size.find( 'option[value="all"]' ).length ? 'all' : p.totalRows; + var s = parseInt( size, 10 ) || p.size || p.settings.size || 10; return /all/i.test( size ) || s === p.totalRows ? // "get" to get `p.size` or "set" to set `p.$size.val()` - ( mode === 'get' ? p.totalRows : setAll ) : - ( mode === 'get' ? s : p.size ); + 'all' : ( mode === 'get' ? s : p.size ); }, parsePageNumber = function( table, p ) { @@ -799,8 +801,8 @@ p.$size.val( parsePageSize( p, p.size, 'set' ) ); $.data(table, 'pagerLastPage', parsePageNumber( table, p ) ); $.data(table, 'pagerLastSize', p.size); - p.totalPages = Math.ceil( p.totalRows / p.size ); - p.filteredPages = Math.ceil( p.filteredRows / p.size ); + p.totalPages = p.size === 'all' ? 1 : Math.ceil( p.totalRows / p.size ); + p.filteredPages = p.size === 'all' ? 1 : Math.ceil( p.filteredRows / p.size ); moveToPage(table, p); }, @@ -872,9 +874,9 @@ p.isDisabled = false; p.page = $.data(table, 'pagerLastPage') || p.page || 0; size = p.$size.find('option[selected]').val(); - p.size = $.data(table, 'pagerLastSize') || parsePageSize( p, p.size, 'get' ); - p.$size.val( parsePageSize( p, p.size, 'set' ) ); // set page size - p.totalPages = Math.ceil( getTotalPages( table, p ) / p.size ); + p.size = $.data(table, 'pagerLastSize') || parsePageSize( p, size, 'get' ); + p.$size.val( p.size ); // set page size + p.totalPages = p.size === 'all' ? 1 : Math.ceil( getTotalPages( table, p ) / p.size ); // if table id exists, include page display with aria info if ( table.id ) { info = table.id + '_pager_info'; @@ -900,7 +902,7 @@ c.rowsCopy = rows; p.totalRows = p.countChildRows ? c.$tbodies.eq(0).children('tr').length : rows.length; p.size = $.data(table, 'pagerLastSize') || p.size || p.settings.size || 10; - p.totalPages = Math.ceil( p.totalRows / p.size ); + p.totalPages = p.size === 'all' ? 1 : Math.ceil( p.totalRows / p.size ); renderTable(table, rows, p); // update display here in case all rows are removed updatePageDisplay(table, p, false); @@ -911,7 +913,7 @@ return this.each(function() { // check if tablesorter has initialized if (!(this.config && this.hasInitialized)) { return; } - var t, ctrls, fxn, + var t, ctrls, fxn, size, table = this, c = table.config, wo = c.widgetOptions, @@ -931,10 +933,10 @@ if (p.savePages && ts.storage) { t = ts.storage(table, p.storageKey) || {}; // fixes #387 p.page = isNaN(t.page) ? p.page : t.page; - p.size = ( isNaN(t.size) ? p.size : t.size ) || p.settings.size || 10; + p.size = t.size === 'all' ? t.size : ( isNaN( t.size ) ? p.size : t.size ) || p.setSize || 10; $.data(table, 'pagerLastSize', p.size); + pager.find(p.cssPageSize).val(p.size); } - // skipped rows p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.slice(1) + '|' + c.cssChildRow + ')'); @@ -980,7 +982,7 @@ if ( !table || triggered || p.ajax ) { return; } var $rows = c.$tbodies.eq(0).children('tr').not(c.selectorRemove); p.totalRows = $rows.length - ( p.countChildRows ? 0 : $rows.filter('.' + c.cssChildRow).length ); - p.totalPages = Math.ceil( p.totalRows / p.size ); + p.totalPages = p.size === 'all' ? 1 : Math.ceil( p.totalRows / p.size ); if ($rows.length && c.rowsCopy && c.rowsCopy.length === 0) { // make a copy of all table rows once the cache has been built updateCache(table); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 8c4f1b2..3542e61 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 04-11-2016 (v2.25.8)*/ +/*! tablesorter (FORK) - updated 05-01-2016 (v2.26.0)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.25.8 *//* +/*! TableSorter (FORK) v2.26.0 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -39,7 +39,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.25.8', + version : '2.26.0', parsers : [], widgets : [], @@ -3104,7 +3104,7 @@ })(jQuery); -/*! Widget: filter - updated 4/1/2016 (v2.25.7) *//* +/*! Widget: filter - updated 4/29/2016 (v2.25.9) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3183,6 +3183,7 @@ .unbind( events.replace( ts.regex.spaces, ' ' ) ) // remove the filter row even if refreshing, because the column might have been moved .find( '.' + tscss.filterRow ).remove(); + wo.filter_initialized = false; if ( refreshing ) { return; } for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { $tbody = ts.processTbody( table, $tbodies.eq( tbodyIndex ), true ); // remove tbody @@ -3455,7 +3456,7 @@ return null; } }, - init: function( table, c, wo ) { + init: function( table ) { // filter language options ts.language = $.extend( true, {}, { to : 'to', @@ -3463,7 +3464,9 @@ and : 'and' }, ts.language ); - var options, string, txt, $header, column, filters, val, fxn, noSelect; + var options, string, txt, $header, column, val, fxn, noSelect, + c = table.config, + wo = c.widgetOptions; c.$table.addClass( 'hasFilters' ); c.lastSearch = []; @@ -3651,22 +3654,7 @@ c.$table .unbind( txt.replace( ts.regex.spaces, ' ' ) ) .bind( txt, function() { - // redefine 'wo' as it does not update properly inside this callback - var wo = this.config.widgetOptions; - filters = tsf.setDefaults( table, c, wo ) || []; - if ( filters.length ) { - // prevent delayInit from triggering a cache build if filters are empty - if ( !( c.delayInit && filters.join( '' ) === '' ) ) { - ts.setFilters( table, filters, true ); - } - } - c.$table.triggerHandler( 'filterFomatterUpdate' ); - // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers - setTimeout( function() { - if ( !wo.filter_initialized ) { - tsf.filterInitComplete( c ); - } - }, 100 ); + tsf.completeInit( this ); }); // if filter widget is added after pager has initialized; then set filter init flag if ( c.pager && c.pager.initialized && !wo.filter_initialized ) { @@ -3674,8 +3662,30 @@ setTimeout( function() { tsf.filterInitComplete( c ); }, 100 ); + } else if ( !wo.filter_initialized ) { + tsf.completeInit( table ); + } + }, + completeInit: function( table ) { + // redefine 'c' & 'wo' so they update properly inside this callback + var c = table.config, + wo = c.widgetOptions, + filters = tsf.setDefaults( table, c, wo ) || []; + if ( filters.length ) { + // prevent delayInit from triggering a cache build if filters are empty + if ( !( c.delayInit && filters.join( '' ) === '' ) ) { + ts.setFilters( table, filters, true ); + } } + c.$table.triggerHandler( 'filterFomatterUpdate' ); + // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers + setTimeout( function() { + if ( !wo.filter_initialized ) { + tsf.filterInitComplete( c ); + } + }, 100 ); }, + // $cell parameter, but not the config, is passed to the filter_formatters, // so we have to work with it instead formatterUpdated: function( $cell, column ) { @@ -4921,7 +4931,7 @@ })( jQuery ); -/*! Widget: stickyHeaders - updated 4/1/2016 (v2.25.7) *//* +/*! Widget: stickyHeaders - updated 5/1/2016 (v2.26.0) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -5085,12 +5095,14 @@ var offset = $table.offset(), yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 xWindow = $.isWindow( $xScroll[0] ), - // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, - scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, - tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)), + attachTop = $attach.length ? + ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : + $yScroll.scrollTop(), + captionHeight = wo.stickyHeaders_includeCaption ? 0 : $table.children( 'caption' ).height() || 0, + scrollTop = attachTop + stickyOffset + nestedStickyTop - captionHeight, + tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)) - captionHeight, isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', cssSettings = { visibility : isVisible }; - if ($attach.length) { cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 734787b..0f31f57 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.25.8 *//* +/*! TableSorter (FORK) v2.26.0 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -21,7 +21,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.25.8', + version : '2.26.0', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 3a06a78..00b8608 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 04-11-2016 (v2.25.8)*/ +/*! tablesorter (FORK) - updated 05-01-2016 (v2.26.0)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -372,7 +372,7 @@ })(jQuery); -/*! Widget: filter - updated 4/1/2016 (v2.25.7) *//* +/*! Widget: filter - updated 4/29/2016 (v2.25.9) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -451,6 +451,7 @@ .unbind( events.replace( ts.regex.spaces, ' ' ) ) // remove the filter row even if refreshing, because the column might have been moved .find( '.' + tscss.filterRow ).remove(); + wo.filter_initialized = false; if ( refreshing ) { return; } for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { $tbody = ts.processTbody( table, $tbodies.eq( tbodyIndex ), true ); // remove tbody @@ -723,7 +724,7 @@ return null; } }, - init: function( table, c, wo ) { + init: function( table ) { // filter language options ts.language = $.extend( true, {}, { to : 'to', @@ -731,7 +732,9 @@ and : 'and' }, ts.language ); - var options, string, txt, $header, column, filters, val, fxn, noSelect; + var options, string, txt, $header, column, val, fxn, noSelect, + c = table.config, + wo = c.widgetOptions; c.$table.addClass( 'hasFilters' ); c.lastSearch = []; @@ -919,22 +922,7 @@ c.$table .unbind( txt.replace( ts.regex.spaces, ' ' ) ) .bind( txt, function() { - // redefine 'wo' as it does not update properly inside this callback - var wo = this.config.widgetOptions; - filters = tsf.setDefaults( table, c, wo ) || []; - if ( filters.length ) { - // prevent delayInit from triggering a cache build if filters are empty - if ( !( c.delayInit && filters.join( '' ) === '' ) ) { - ts.setFilters( table, filters, true ); - } - } - c.$table.triggerHandler( 'filterFomatterUpdate' ); - // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers - setTimeout( function() { - if ( !wo.filter_initialized ) { - tsf.filterInitComplete( c ); - } - }, 100 ); + tsf.completeInit( this ); }); // if filter widget is added after pager has initialized; then set filter init flag if ( c.pager && c.pager.initialized && !wo.filter_initialized ) { @@ -942,8 +930,30 @@ setTimeout( function() { tsf.filterInitComplete( c ); }, 100 ); + } else if ( !wo.filter_initialized ) { + tsf.completeInit( table ); + } + }, + completeInit: function( table ) { + // redefine 'c' & 'wo' so they update properly inside this callback + var c = table.config, + wo = c.widgetOptions, + filters = tsf.setDefaults( table, c, wo ) || []; + if ( filters.length ) { + // prevent delayInit from triggering a cache build if filters are empty + if ( !( c.delayInit && filters.join( '' ) === '' ) ) { + ts.setFilters( table, filters, true ); + } } + c.$table.triggerHandler( 'filterFomatterUpdate' ); + // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers + setTimeout( function() { + if ( !wo.filter_initialized ) { + tsf.filterInitComplete( c ); + } + }, 100 ); }, + // $cell parameter, but not the config, is passed to the filter_formatters, // so we have to work with it instead formatterUpdated: function( $cell, column ) { @@ -2189,7 +2199,7 @@ })( jQuery ); -/*! Widget: stickyHeaders - updated 4/1/2016 (v2.25.7) *//* +/*! Widget: stickyHeaders - updated 5/1/2016 (v2.26.0) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -2353,12 +2363,14 @@ var offset = $table.offset(), yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 xWindow = $.isWindow( $xScroll[0] ), - // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, - scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, - tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)), + attachTop = $attach.length ? + ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : + $yScroll.scrollTop(), + captionHeight = wo.stickyHeaders_includeCaption ? 0 : $table.children( 'caption' ).height() || 0, + scrollTop = attachTop + stickyOffset + nestedStickyTop - captionHeight, + tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)) - captionHeight, isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', cssSettings = { visibility : isVisible }; - if ($attach.length) { cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index 7d27621..e779e53 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,4 +1,4 @@ -/*! Parser: input & select - updated 1/15/2016 (v2.25.2) *//* +/*! Parser: input & select - updated 4/29/2016 (v2.25.9) *//* * for jQuery 1.7+ & tablesorter 2.7.11+ * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ @@ -52,7 +52,7 @@ is : function() { return false; }, - format : function( txt, table, cell, cellIndex ) { + format : function( txt, table, cell ) { var $cell = $( cell ), wo = table.config.widgetOptions, // returning plain language here because this is what is shown in the @@ -128,7 +128,8 @@ } }, updateHeaderCheckbox = function( $table, checkboxClass ) { - var $rows = $table.children( 'tbody' ).children( ':visible' ), // (include child rows?) + var ua = window.navigator.userAgent, + $rows = $table.children( 'tbody' ).children( ':visible' ), // (include child rows?) len = $rows.length; // set indeterminate state on header checkbox $table.children( 'thead' ).find( 'input[type="checkbox"]' ).each( function() { @@ -139,6 +140,8 @@ this.checked = allChecked; this.indeterminate = false; } else { + // needed for IE + this.checked = !(ua.indexOf('Trident/') > -1 || ua.indexOf('Edge/') > -1); this.indeterminate = true; } }); @@ -221,7 +224,7 @@ $( this ) .off( namespace ) .on( 'tablesorter-ready' + namespace, function() { - var checkboxClass, $rows, len, + var checkboxClass, $table = $( this ), c = $table.length && $table[ 0 ].config; if ( !$.isEmptyObject( c ) ) { @@ -235,8 +238,10 @@ .children( 'thead' ) .off( namespace ) // modified from http://jsfiddle.net/abkNM/6163/ - .on( 'change' + namespace, 'input[type="checkbox"]', function( event ) { - var undef, onlyVisible, column, $target, isParsed, $row, checkboxClass, + // click needed for IE; a change isn't fired when going from an indeterminate checkbox to + // either checked or unchecked + .on( 'click' + namespace + ' change' + namespace, 'input[type="checkbox"]', function( event ) { + var undef, onlyVisible, column, $target, isParsed, checkboxClass, $checkbox = $( this ), $table = $checkbox.closest( 'table' ), c = $table.length && $table[ 0 ].config, @@ -252,22 +257,21 @@ .children( ':nth-child(' + ( column + 1 ) + ')' ) .find( 'input[type="checkbox"]' ) .prop( 'checked', isChecked ); - if ( !isParsed ) { - // add checkbox class names - checkboxClass = c.checkboxClass || 'checked'; - $target.each(function(){ - $row = $(this).closest('tr'); - toggleRowClass( $(this).closest( 'tr' ), checkboxClass, column, isChecked ); - }); - updateHeaderCheckbox( $table, checkboxClass ); - updateServer( event, $table, $target ); - $table[ 0 ].tablesorterBusy = false; - } else { + // add checkbox class names to row + checkboxClass = c.checkboxClass || 'checked'; + $target.each( function() { + toggleRowClass( $( this ).closest( 'tr' ), checkboxClass, column, isChecked ); + }); + updateHeaderCheckbox( $table, checkboxClass ); + if ( isParsed ) { // only update cache if checkboxes are being sorted $.tablesorter.update( c, undef, function() { updateServer( event, $table, $target ); $table[ 0 ].tablesorterBusy = false; }); + } else { + updateServer( event, $table, $target ); + $table[ 0 ].tablesorterBusy = false; } // needed for IE8 return true; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index 52a7ca3..0370f32 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Widget: columnSelector (responsive table widget) - updated 2/15/2016 (v2.25.4) *//* +/* Widget: columnSelector (responsive table widget) - updated 4/29/2016 (v2.25.9) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -67,19 +67,20 @@ }, refreshColumns: function( c, optName, optState ) { - var i, arry, + var i, arry, $el, val, + colSel = c.selector, isArry = $.isArray(optState || optName), wo = c.widgetOptions; // see #798 - if (typeof optName !== 'undefined' && c.selector.$container.length) { + if (typeof optName !== 'undefined' && colSel.$container.length) { // pass "selectors" to update the all of the container contents if ( optName === 'selectors' ) { - c.selector.$container.empty(); + colSel.$container.empty(); tsColSel.setupSelector(c, wo); tsColSel.setupBreakpoints(c, wo); // if optState is undefined, maintain the current "auto" state if ( typeof optState === 'undefined' ) { - optState = c.selector.auto; + optState = colSel.auto; } } // pass an array of column zero-based indexes to turn off auto mode & toggle selected columns @@ -90,20 +91,24 @@ arry[i] = parseInt(v, 10); }); for (i = 0; i < c.columns; i++) { - c.selector.$container - .find('input[data-column=' + i + ']') - .prop('checked', $.inArray( i, arry ) >= 0 ); + val = $.inArray( i, arry ) >= 0; + $el = colSel.$container.find( 'input[data-column=' + i + ']' ); + if ( $el.length ) { + $el.prop( 'checked', val ); + colSel.states[i] = val; + } } } // if passing an array, set auto to false to allow manual column selection & update columns // refreshColumns( c, 'auto', true ) === refreshColumns( c, true ); - tsColSel - .updateAuto( c, wo, c.selector.$container.find('input[data-column="auto"]') - .prop('checked', optState === true || optName === true || optName === 'auto' && optState !== false) ); + val = optState === true || optName === true || optName === 'auto' && optState !== false; + $el = colSel.$container.find( 'input[data-column="auto"]' ).prop( 'checked', val ); + tsColSel.updateAuto( c, wo, $el ); } else { tsColSel.updateBreakpoints(c, wo); tsColSel.updateCols(c, wo); } + tsColSel.saveValues( c, wo ); tsColSel.adjustColspans( c, wo ); }, @@ -224,9 +229,7 @@ $(this).prop( 'checked', indx === 'auto' ? colSel.auto : colSel.states[indx] ); }); } - if (wo.columnSelector_saveColumns && ts.storage) { - ts.storage( c.$table[0], 'tablesorter-columnSelector-auto', { auto : colSel.auto } ); - } + tsColSel.saveValues( c, wo ); tsColSel.adjustColspans( c, wo ); // trigger columnUpdate if auto is true (it gets skipped in updateCols() if (colSel.auto) { @@ -270,7 +273,7 @@ } } // only 6 breakpoints (same as jQuery Mobile) - for (priority = 0; priority < 6; priority++){ + for (priority = 0; priority < wo.columnSelector_maxPriorities; priority++){ /*jshint loopfunc:true */ breaks = []; c.$headers.filter('[' + wo.columnSelector_priority + '=' + (priority + 1) + ']').each(function(){ @@ -317,9 +320,7 @@ if (colSel.$style) { colSel.$style.prop('disabled', false).text( styles.length ? styles.join(',') + ' { display: none; }' : '' ); } - if (wo.columnSelector_saveColumns && ts.storage) { - ts.storage( c.$table[0], 'tablesorter-columnSelector', colSel.states ); - } + tsColSel.saveValues( c, wo ); tsColSel.adjustColspans( c, wo ); c.$table.triggerHandler(wo.columnSelector_updated); }, @@ -380,14 +381,22 @@ } } if ( span ) { - $cell.removeClass( wo.filter_filteredRow )[0].colSpan = span; + $cell.removeClass( wo.filter_filteredRow || 'filtered' )[0].colSpan = span; } else { - $cell.addClass( wo.filter_filteredRow ); + $cell.addClass( wo.filter_filteredRow || 'filtered' ); } } } }, + saveValues : function( c, wo ) { + if ( wo.columnSelector_saveColumns && ts.storage ) { + var colSel = c.selector; + ts.storage( c.$table[0], 'tablesorter-columnSelector-auto', { auto : colSel.auto } ); + ts.storage( c.$table[0], 'tablesorter-columnSelector', colSel.states ); + } + }, + attachTo : function(table, elm) { table = $(table)[0]; var colSel, wo, indx, @@ -462,6 +471,9 @@ // see http://view.jquerymobile.com/1.3.2/dist/demos/widgets/table-column-toggle/#Applyingapresetbreakpoint // *** set to false to disable *** columnSelector_breakpoints : [ '20em', '30em', '40em', '50em', '60em', '70em' ], + // maximum number of priority settings; if this value is changed (especially increased), + // then make sure to modify the columnSelector_breakpoints - see #1204 + columnSelector_maxPriorities : 6, // data attribute containing column priority // duplicates how jQuery mobile uses priorities: // http://view.jquerymobile.com/1.3.2/dist/demos/widgets/table-column-toggle/ @@ -483,7 +495,7 @@ if (csel.$popup) { csel.$popup.empty(); } csel.$style.remove(); csel.$breakpoints.remove(); - $( c.namespace + 'columnselectorHasSpan' ).removeClass( wo.filter_filteredRow ); + $( c.namespace + 'columnselectorHasSpan' ).removeClass( wo.filter_filteredRow || 'filtered' ); c.$table.off('updateAll' + namespace + ' update' + namespace); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js index 6ba2a65..78fbbe0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js @@ -519,10 +519,10 @@ $date.datepicker('setDate', (getdate === '' ? '' : getdate) || null); if (getdate === '') { notrigger = false; } date = $date.datepicker('getDate'); - query = date ? ( o.endOfDay && /<=/.test(compare) ? date.setHours(23, 59, 59) : date.getTime() ) || '' : ''; + query = date ? ( o.endOfDay && /<=/.test(compare) ? date.setHours(23, 59, 59, 999) : date.getTime() ) || '' : ''; if (date && o.endOfDay && compare === '=') { compare = ''; - query += ' - ' + date.setHours(23, 59, 59); + query += ' - ' + date.setHours(23, 59, 59, 999); notrigger = false; } $cell.find('.dateCompare') @@ -670,7 +670,7 @@ from = $cell.find('.dateFrom').datepicker('getDate'), to = $cell.find('.dateTo').datepicker('getDate'); from = validDate(from) ? from.getTime() : ''; - to = validDate(to) ? ( o.endOfDay ? to.setHours(23, 59, 59) : to.getTime() ) || '' : ''; + to = validDate(to) ? ( o.endOfDay ? to.setHours(23, 59, 59, 999) : to.getTime() ) || '' : ''; range = from ? ( to ? from + ' - ' + to : '>=' + from ) : (to ? '<=' + to : ''); $cell.add( $shcell ) .find('.dateRange').val(range) diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 726beee..9153240 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 4/1/2016 (v2.25.7) *//* +/*! Widget: filter - updated 4/29/2016 (v2.25.9) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -77,6 +77,7 @@ .unbind( events.replace( ts.regex.spaces, ' ' ) ) // remove the filter row even if refreshing, because the column might have been moved .find( '.' + tscss.filterRow ).remove(); + wo.filter_initialized = false; if ( refreshing ) { return; } for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { $tbody = ts.processTbody( table, $tbodies.eq( tbodyIndex ), true ); // remove tbody @@ -349,7 +350,7 @@ return null; } }, - init: function( table, c, wo ) { + init: function( table ) { // filter language options ts.language = $.extend( true, {}, { to : 'to', @@ -357,7 +358,9 @@ and : 'and' }, ts.language ); - var options, string, txt, $header, column, filters, val, fxn, noSelect; + var options, string, txt, $header, column, val, fxn, noSelect, + c = table.config, + wo = c.widgetOptions; c.$table.addClass( 'hasFilters' ); c.lastSearch = []; @@ -545,22 +548,7 @@ c.$table .unbind( txt.replace( ts.regex.spaces, ' ' ) ) .bind( txt, function() { - // redefine 'wo' as it does not update properly inside this callback - var wo = this.config.widgetOptions; - filters = tsf.setDefaults( table, c, wo ) || []; - if ( filters.length ) { - // prevent delayInit from triggering a cache build if filters are empty - if ( !( c.delayInit && filters.join( '' ) === '' ) ) { - ts.setFilters( table, filters, true ); - } - } - c.$table.triggerHandler( 'filterFomatterUpdate' ); - // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers - setTimeout( function() { - if ( !wo.filter_initialized ) { - tsf.filterInitComplete( c ); - } - }, 100 ); + tsf.completeInit( this ); }); // if filter widget is added after pager has initialized; then set filter init flag if ( c.pager && c.pager.initialized && !wo.filter_initialized ) { @@ -568,8 +556,30 @@ setTimeout( function() { tsf.filterInitComplete( c ); }, 100 ); + } else if ( !wo.filter_initialized ) { + tsf.completeInit( table ); } }, + completeInit: function( table ) { + // redefine 'c' & 'wo' so they update properly inside this callback + var c = table.config, + wo = c.widgetOptions, + filters = tsf.setDefaults( table, c, wo ) || []; + if ( filters.length ) { + // prevent delayInit from triggering a cache build if filters are empty + if ( !( c.delayInit && filters.join( '' ) === '' ) ) { + ts.setFilters( table, filters, true ); + } + } + c.$table.triggerHandler( 'filterFomatterUpdate' ); + // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers + setTimeout( function() { + if ( !wo.filter_initialized ) { + tsf.filterInitComplete( c ); + } + }, 100 ); + }, + // $cell parameter, but not the config, is passed to the filter_formatters, // so we have to work with it instead formatterUpdated: function( $cell, column ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 5d98811..239c8e2 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 2/15/2016 (v2.25.4) */ +/*! Widget: Pager - updated 5/1/2016 (v2.26.0) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -182,8 +182,9 @@ if ( wo.pager_savePages && ts.storage ) { t = ts.storage( table, wo.pager_storageKey ) || {}; // fixes #387 p.page = ( isNaN( t.page ) ? p.page : t.page ) || p.setPage || 0; - p.size = ( isNaN( t.size ) ? p.size : t.size ) || p.setSize || 10; + p.size = t.size === 'all' ? t.size : ( isNaN( t.size ) ? p.size : t.size ) || p.setSize || 10; $.data( table, 'pagerLastSize', p.size ); + p.$size.val( p.size ); } // skipped rows @@ -291,7 +292,7 @@ var $rows = c.$tbodies.eq( 0 ).children( 'tr' ).not( c.selectorRemove ); p.totalRows = $rows.length - ( wo.pager_countChildRows ? 0 : $rows.filter( '.' + c.cssChildRow ).length ); - p.totalPages = Math.ceil( p.totalRows / p.size ); + p.totalPages = p.size === 'all' ? 1 : Math.ceil( p.totalRows / p.size ); if ( $rows.length && c.rowsCopy && c.rowsCopy.length === 0 ) { // make a copy of all table rows once the cache has been built tsp.updateCache( c ); @@ -443,6 +444,7 @@ p = c.pager, namespace = c.namespace + 'pager', sz = tsp.parsePageSize( c, p.size, 'get' ); // don't allow dividing by zero + if ( sz === 'all' ) { sz = p.totalRows; } if ( wo.pager_countChildRows ) { t[ t.length ] = c.cssChildRow; } p.$size .add( p.$goto ) @@ -456,10 +458,10 @@ c.filteredRows = p.filteredRows; p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; if ( tsp.getTotalPages( c, p ) >= 0 ) { - t = ( p.size * p.page > p.filteredRows ) && completed; + t = ( sz * p.page > p.filteredRows ) && completed; p.page = t ? wo.pager_pageReset || 0 : p.page; - p.startRow = t ? p.size * p.page + 1 : ( p.filteredRows === 0 ? 0 : p.size * p.page + 1 ); - p.endRow = Math.min( p.filteredRows, p.totalRows, p.size * ( p.page + 1 ) ); + p.startRow = t ? sz * p.page + 1 : ( p.filteredRows === 0 ? 0 : sz * p.page + 1 ); + p.endRow = Math.min( p.filteredRows, p.totalRows, sz * ( p.page + 1 ) ); $out = p.$container.find( wo.pager_selectors.pageDisplay ); // form the output string (can now get a new output string from the server) s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || wo.pager_output : wo.pager_output ) @@ -503,7 +505,7 @@ .off( 'change' + namespace ) .on( 'change' + namespace, function() { var v = $( this ).val(), - pg = $( this ).hasClass( 'ts-startRow' ) ? Math.floor( v / p.size ) + 1 : v; + pg = $( this ).hasClass( 'ts-startRow' ) ? Math.floor( v / sz ) + 1 : v; c.$table.triggerHandler( 'pageSet' + namespace, [ pg ] ); }); } @@ -519,7 +521,7 @@ if ( wo.pager_savePages && ts.storage ) { ts.storage( table, wo.pager_storageKey, { page : p.page, - size : p.size + size : sz === p.totalRows ? 'all' : sz }); } } @@ -600,7 +602,8 @@ h = $.data( table, 'pagerSavedHeight' ); if ( h ) { d = h - $b.height(); - if ( d > 5 && $.data( table, 'pagerLastSize' ) === p.size && $b.children( 'tr:visible' ).length < p.size ) { + if ( d > 5 && $.data( table, 'pagerLastSize' ) === p.size && + $b.children( 'tr:visible' ).length < ( p.size === 'all' ? p.totalRows : p.size ) ) { $b.append( '<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice( 1 ) + '" style="height:' + d + 'px;"></tr>' ); } @@ -611,15 +614,17 @@ changeHeight: function( c ) { var h, table = c.table, + p = c.pager, + sz = p.size === 'all' ? p.totalRows : p.size, $b = c.$tbodies.eq( 0 ); $b.find( 'tr.pagerSavedHeightSpacer' ).remove(); if ( !$b.children( 'tr:visible' ).length ) { $b.append( '<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice( 1 ) + '"><td> </td></tr>' ); } - h = $b.children( 'tr' ).eq( 0 ).height() * c.pager.size; + h = $b.children( 'tr' ).eq( 0 ).height() * sz; $.data( table, 'pagerSavedHeight', h ); tsp.fixHeight( c ); - $.data( table, 'pagerLastSize', c.pager.size ); + $.data( table, 'pagerLastSize', p.size ); }, hideRows: function( c ) { @@ -629,8 +634,9 @@ p = c.pager, wo = c.widgetOptions, tbodyLen = c.$tbodies.length, - start = ( p.page * p.size ), - end = start + p.size, + sz = p.size === 'all' ? p.totalRows : p.size, + start = ( p.page * sz ), + end = start + sz, filtr = wo && wo.filter_filteredRow || 'filtered', last = 0, // for cache indexing size = 0; // size counter @@ -677,7 +683,7 @@ namespace = c.namespace + 'pager', size = p.$size.val(); p.size = tsp.parsePageSize( c, size, 'get' ); - p.$size.val( tsp.parsePageSize( c, p.size, 'set' ) ); + p.$size.val( p.size ); $.data( c.table, 'pagerLastSize', p.size ); tsp.pagerArrows( c ); if ( !c.widgetOptions.pager_removeRows ) { @@ -701,7 +707,7 @@ c.$tbodies.eq( 0 ).empty(); // ajaxProcessing result: [ total, rows, headers ] - var i, j, t, hsh, $f, $sh, $headers, $h, icon, th, d, l, rr_count, len, + var i, j, t, hsh, $f, $sh, $headers, $h, icon, th, d, l, rr_count, len, sz, $table = c.$table, tds = '', result = wo.pager_ajaxProcessing( data, table, xhr ) || [ 0, [] ], @@ -793,9 +799,10 @@ if ( c.showProcessing ) { ts.isProcessing( table ); // remove loading icon } + sz = tsp.parsePageSize( c, p.size, 'get' ); // make sure last pager settings are saved, prevents multiple server side calls with // the same parameters - p.totalPages = Math.ceil( p.totalRows / tsp.parsePageSize( c, p.size, 'get' ) ); + p.totalPages = sz === 'all' ? 1 : Math.ceil( p.totalRows / sz ); p.last.totalRows = p.totalRows; p.last.currentFilters = p.currentFilters; p.last.sortList = ( c.sortList || [] ).join( ',' ); @@ -865,6 +872,7 @@ url = wo.pager_ajaxUrl ? wo.pager_ajaxUrl // allow using '{page+1}' in the url string to switch to a non-zero based index .replace( /\{page([\-+]\d+)?\}/, function( s, n ) { return p.page + ( n ? parseInt( n, 10 ) : 0 ); }) + // this will pass "all" to server when size is set to "all" .replace( /\{size\}/g, p.size ) : '', sortList = c.sortList, filterList = p.currentFilters || c.$table.data( 'lastSearch' ) || [], @@ -909,8 +917,8 @@ wo = c.widgetOptions, f = c.$table.hasClass('hasFilters'), l = rows && rows.length || 0, // rows may be undefined - s = ( p.page * p.size ), - e = p.size; + e = p.size === 'all' ? p.totalRows : p.size, + s = ( p.page * e ); if ( l < 1 ) { if ( c.debug ) { console.warn( 'Pager: >> No rows for pager to render' ); @@ -977,7 +985,7 @@ $.data( table, 'pagerLastPage', p.page ); $.data( table, 'pagerLastSize', p.size ); p.page = 0; - p.size = p.totalRows; + p.size = 'all'; p.totalPages = 1; c.$table .addClass( 'pagerDisabled' ) @@ -1106,13 +1114,10 @@ // set to either set or get value parsePageSize: function( c, size, mode ) { var p = c.pager, - s = parseInt( size, 10 ) || p.size || c.widgetOptions.pager_size || 10, - // if select does not contain an "all" option, use size - setAll = p.$size.find( 'option[value="all"]' ).length ? 'all' : p.totalRows; + s = parseInt( size, 10 ) || p.size || c.widgetOptions.pager_size || 10; return /all/i.test( size ) || s === p.totalRows ? // "get" to set `p.size` or "set" to set `p.$size.val()` - ( mode === 'get' ? p.totalRows : setAll ) : - ( mode === 'get' ? s : p.size ); + 'all' : ( mode === 'get' ? s : p.size ); }, parsePageNumber: function( c, p ) { @@ -1130,8 +1135,8 @@ p.$size.val( tsp.parsePageSize( c, p.size, 'set' ) ); $.data( table, 'pagerLastPage', tsp.parsePageNumber( c, p ) ); $.data( table, 'pagerLastSize', p.size ); - p.totalPages = Math.ceil( p.totalRows / p.size ); - p.filteredPages = Math.ceil( p.filteredRows / p.size ); + p.totalPages = p.size === 'all' ? 1 : Math.ceil( p.totalRows / p.size ); + p.filteredPages = p.size === 'all' ? 1 : Math.ceil( p.filteredRows / p.size ); tsp.moveToPage( c, p, true ); }, @@ -1201,8 +1206,8 @@ p.page = $.data( table, 'pagerLastPage' ) || p.page || 0; size = p.$size.find( 'option[selected]' ).val(); p.size = $.data( table, 'pagerLastSize' ) || tsp.parsePageSize( c, size, 'get' ); - p.$size.val( tsp.parsePageSize( c, p.size, 'set' ) ); // set page size - p.totalPages = Math.ceil( tsp.getTotalPages( c, p ) / p.size ); + p.$size.val( p.size ); // set page size + p.totalPages = p.size === 'all' ? 1 : Math.ceil( tsp.getTotalPages( c, p ) / p.size ); c.$table.removeClass( 'pagerDisabled' ); // if table id exists, include page display with aria info if ( table.id ) { @@ -1230,7 +1235,7 @@ c.rowsCopy = rows; p.totalRows = wo.pager_countChildRows ? c.$tbodies.eq( 0 ).children( 'tr' ).length : rows.length; p.size = $.data( table, 'pagerLastSize' ) || p.size || wo.pager_size || p.setSize || 10; - p.totalPages = Math.ceil( p.totalRows / p.size ); + p.totalPages = p.size === 'all' ? 1 : Math.ceil( p.totalRows / p.size ); tsp.moveToPage( c, p ); // update display here in case all rows are removed tsp.updatePageDisplay( c, false ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index a0e1377..88f4666 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -1,4 +1,4 @@ -/*! Widget: scroller - updated 4/1/2016 (v2.25.7) *//* +/*! Widget: scroller - updated 4/29/2016 (v2.25.9) *//* Copyright (C) 2011 T. Connell & Associates, Inc. Dual-licensed under the MIT and GPL licenses @@ -828,6 +828,9 @@ $fixedColumn.removeClass( tscss.scrollerHideElement ); + // adjust caption height, see #1202 + $fixedColumn.find('caption').height( wo.scroller_$header.find( 'caption' ).height() ); + wo.scroller_isBusy = false; }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js index 44fd825..07b95f8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js @@ -1,4 +1,4 @@ -/*! Widget: stickyHeaders - updated 4/1/2016 (v2.25.7) *//* +/*! Widget: stickyHeaders - updated 5/1/2016 (v2.26.0) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -162,12 +162,14 @@ var offset = $table.offset(), yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 xWindow = $.isWindow( $xScroll[0] ), - // scrollTop = ( $attach.length ? $attach.offset().top : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, - scrollTop = ( $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop() ) + stickyOffset + nestedStickyTop, - tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)), + attachTop = $attach.length ? + ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : + $yScroll.scrollTop(), + captionHeight = wo.stickyHeaders_includeCaption ? 0 : $table.children( 'caption' ).height() || 0, + scrollTop = attachTop + stickyOffset + nestedStickyTop - captionHeight, + tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)) - captionHeight, isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', cssSettings = { visibility : isVisible }; - if ($attach.length) { cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); } From 7f1cca39e47a94284a9615c93b3d3d36c1ecb4ae Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Wed, 18 May 2016 21:49:25 +0200 Subject: [PATCH 088/138] Update tablesorter to latest version (2.26.1) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 39 +++++++++---------- .../jquery-tablesorter/jquery.tablesorter.js | 12 +++--- .../jquery.tablesorter.widgets.js | 27 +++++++------ .../widgets/widget-filter.js | 18 ++++----- .../widgets/widget-grouping.js | 7 ++-- .../jquery-tablesorter/widgets/widget-math.js | 15 ++++++- .../widgets/widget-resizable.js | 7 ++-- 11 files changed, 74 insertions(+), 61 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26484f1..2a0d4d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.21.1 (2016-05-18) + +* Upgrade tablesorter to v2.26.1 + #### v1.21.0 (2016-05-08) * Upgrade tablesorter to v2.26.0 diff --git a/README.md b/README.md index 6ee7fbf..463c62d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.26.0 (5/1/2016), [documentation] +Current tablesorter version: 2.26.1 (5/16/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index a1c2076..94f56fb 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 21 - TINY = 0 + TINY = 1 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 6d76080..164fdf9 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 6d76080f8ed2aea800b055902e3ca24c66f255cd +Subproject commit 164fdf9f9afeaa419b04272f50c0d4c9731e9ddd diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 3542e61..efbffb9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 05-01-2016 (v2.26.0)*/ +/*! tablesorter (FORK) - updated 05-16-2016 (v2.26.1)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.26.0 *//* +/*! TableSorter (FORK) v2.26.1 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -39,7 +39,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.26.0', + version : '2.26.1', parsers : [], widgets : [], @@ -87,12 +87,12 @@ numberSorter : null, // choose overall numeric sorter function( a, b, direction, maxColumnValue ) // *** widget options - widgets: [], // method to add widgets, e.g. widgets: ['zebra'] + initWidgets : true, // apply widgets on tablesorter initialization + widgetClass : 'widget-{name}', // table class name template to match to include a widget + widgets : [], // method to add widgets, e.g. widgets: ['zebra'] widgetOptions : { zebra : [ 'even', 'odd' ] // zebra widget alternating row class names }, - initWidgets : true, // apply widgets on tablesorter initialization - widgetClass : 'widget-{name}', // table class name template to match to include a widget // *** callbacks initialized : null, // function( table ){}, @@ -106,7 +106,7 @@ cssHeaderRow : '', cssProcessing : '', // processing icon applied to header during sort/filter - cssChildRow : 'tablesorter-childRow', // class name indiciating that a row is to be attached to the its parent + cssChildRow : 'tablesorter-childRow', // class name indiciating that a row is to be attached to its parent cssInfoBlock : 'tablesorter-infoOnly', // don't sort tbody with this class name (only one class name allowed here!) cssNoSort : 'tablesorter-noSort', // class name added to element inside header; clicking on it won't cause a sort cssIgnoreRow : 'tablesorter-ignoreRow', // header row to ignore; cells within this row will not be added to c.$headers @@ -3134,13 +3134,14 @@ id: 'filter', priority: 50, options : { + filter_cellFilter : '', // css class name added to the filter cell ( string or array ) filter_childRows : false, // if true, filter includes child row content in the search filter_childByColumn : false, // ( filter_childRows must be true ) if true = search child rows by column; false = search all child row text grouped filter_childWithSibs : true, // if true, include matching child row siblings - filter_columnFilters : true, // if true, a filter will be added to the top of each table column filter_columnAnyMatch: true, // if true, allows using '#:{query}' in AnyMatch searches ( column:query ) - filter_cellFilter : '', // css class name added to the filter cell ( string or array ) + filter_columnFilters : true, // if true, a filter will be added to the top of each table column filter_cssFilter : '', // css class name added to the filter row & each input in the row ( tablesorter-filter is ALWAYS added ) + filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value filter_defaultFilter : {}, // add a default column filter type '~{query}' to make fuzzy searches default; '{q1} AND {q2}' to make all searches use a logical AND. filter_excludeFilter : {}, // filters to exclude, per column filter_external : '', // jQuery selector string ( or jQuery object ) of external filters @@ -3160,11 +3161,10 @@ filter_searchDelay : 300, // typing delay in milliseconds before starting a search filter_searchFiltered: true, // allow searching through already filtered rows in special circumstances; will speed up searching in large tables if true filter_selectSource : null, // include a function to return an array of values to be added to the column filter select - filter_startsWith : false, // if true, filter start from the beginning of the cell contents - filter_useParsedData : false, // filter all data using parsed content + filter_selectSourceSeparator : '|', // filter_selectSource array text left of the separator is added to the option value, right into the option text filter_serversideFiltering : false, // if true, must perform server-side filtering b/c client-side filtering is disabled, but the ui and events will still be used. - filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value - filter_selectSourceSeparator : '|' // filter_selectSource array text left of the separator is added to the option value, right into the option text + filter_startsWith : false, // if true, filter start from the beginning of the cell contents + filter_useParsedData : false // filter all data using parsed content }, format: function( table, c, wo ) { if ( !c.$table.hasClass( 'hasFilters' ) ) { @@ -4189,12 +4189,11 @@ []; data.$cells = data.$row.children(); - - if ( data.anyMatchFlag && columnIndex.length > 1 ) { + if ( data.anyMatchFlag && columnIndex.length > 1 || data.anyMatchFilter ) { data.anyMatch = true; data.isMatch = true; data.rowArray = data.$cells.map( function( i ) { - if ( $.inArray( i, columnIndex ) > -1 ) { + if ( $.inArray( i, columnIndex ) > -1 || data.anyMatchFilter ) { if ( data.parsed[ i ] ) { txt = data.cacheArray[ i ]; } else { @@ -4212,7 +4211,6 @@ data.exact = data.rowArray.join( ' ' ); data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' ); - vars.excludeMatch = vars.noAnyMatch; filterMatched = tsf.processTypes( c, data, vars ); if ( filterMatched !== null ) { @@ -5333,7 +5331,6 @@ .bind( 'selectstart', false ); } } - ts.resizable.setHandlePosition( c, wo ); ts.resizable.bindings( c, wo ); }, @@ -5383,12 +5380,11 @@ setHandlePosition : function( c, wo ) { var startPosition, - hasScroller = ts.hasWidget( c.table, 'scroller' ), tableHeight = c.$table.height(), $handles = wo.$resizable_container.children(), handleCenter = Math.floor( $handles.width() / 2 ); - if ( hasScroller ) { + if ( ts.hasWidget( c.table, 'scroller' ) ) { tableHeight = 0; c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function(){ var $this = $(this); @@ -5571,6 +5567,9 @@ init: function(table, thisWidget, c, wo) { ts.resizable.init( c, wo ); }, + format: function( table, c, wo ) { + ts.resizable.setHandlePosition( c, wo ); + }, remove: function( table, c, wo, refreshing ) { if (wo.$resizable_container) { var namespace = c.namespace + 'tsresize'; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 0f31f57..67442db 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.26.0 *//* +/*! TableSorter (FORK) v2.26.1 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -21,7 +21,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.26.0', + version : '2.26.1', parsers : [], widgets : [], @@ -69,12 +69,12 @@ numberSorter : null, // choose overall numeric sorter function( a, b, direction, maxColumnValue ) // *** widget options - widgets: [], // method to add widgets, e.g. widgets: ['zebra'] + initWidgets : true, // apply widgets on tablesorter initialization + widgetClass : 'widget-{name}', // table class name template to match to include a widget + widgets : [], // method to add widgets, e.g. widgets: ['zebra'] widgetOptions : { zebra : [ 'even', 'odd' ] // zebra widget alternating row class names }, - initWidgets : true, // apply widgets on tablesorter initialization - widgetClass : 'widget-{name}', // table class name template to match to include a widget // *** callbacks initialized : null, // function( table ){}, @@ -88,7 +88,7 @@ cssHeaderRow : '', cssProcessing : '', // processing icon applied to header during sort/filter - cssChildRow : 'tablesorter-childRow', // class name indiciating that a row is to be attached to the its parent + cssChildRow : 'tablesorter-childRow', // class name indiciating that a row is to be attached to its parent cssInfoBlock : 'tablesorter-infoOnly', // don't sort tbody with this class name (only one class name allowed here!) cssNoSort : 'tablesorter-noSort', // class name added to element inside header; clicking on it won't cause a sort cssIgnoreRow : 'tablesorter-ignoreRow', // header row to ignore; cells within this row will not be added to c.$headers diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 00b8608..cb7108e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 05-01-2016 (v2.26.0)*/ +/*! tablesorter (FORK) - updated 05-16-2016 (v2.26.1)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -402,13 +402,14 @@ id: 'filter', priority: 50, options : { + filter_cellFilter : '', // css class name added to the filter cell ( string or array ) filter_childRows : false, // if true, filter includes child row content in the search filter_childByColumn : false, // ( filter_childRows must be true ) if true = search child rows by column; false = search all child row text grouped filter_childWithSibs : true, // if true, include matching child row siblings - filter_columnFilters : true, // if true, a filter will be added to the top of each table column filter_columnAnyMatch: true, // if true, allows using '#:{query}' in AnyMatch searches ( column:query ) - filter_cellFilter : '', // css class name added to the filter cell ( string or array ) + filter_columnFilters : true, // if true, a filter will be added to the top of each table column filter_cssFilter : '', // css class name added to the filter row & each input in the row ( tablesorter-filter is ALWAYS added ) + filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value filter_defaultFilter : {}, // add a default column filter type '~{query}' to make fuzzy searches default; '{q1} AND {q2}' to make all searches use a logical AND. filter_excludeFilter : {}, // filters to exclude, per column filter_external : '', // jQuery selector string ( or jQuery object ) of external filters @@ -428,11 +429,10 @@ filter_searchDelay : 300, // typing delay in milliseconds before starting a search filter_searchFiltered: true, // allow searching through already filtered rows in special circumstances; will speed up searching in large tables if true filter_selectSource : null, // include a function to return an array of values to be added to the column filter select - filter_startsWith : false, // if true, filter start from the beginning of the cell contents - filter_useParsedData : false, // filter all data using parsed content + filter_selectSourceSeparator : '|', // filter_selectSource array text left of the separator is added to the option value, right into the option text filter_serversideFiltering : false, // if true, must perform server-side filtering b/c client-side filtering is disabled, but the ui and events will still be used. - filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value - filter_selectSourceSeparator : '|' // filter_selectSource array text left of the separator is added to the option value, right into the option text + filter_startsWith : false, // if true, filter start from the beginning of the cell contents + filter_useParsedData : false // filter all data using parsed content }, format: function( table, c, wo ) { if ( !c.$table.hasClass( 'hasFilters' ) ) { @@ -1457,12 +1457,11 @@ []; data.$cells = data.$row.children(); - - if ( data.anyMatchFlag && columnIndex.length > 1 ) { + if ( data.anyMatchFlag && columnIndex.length > 1 || data.anyMatchFilter ) { data.anyMatch = true; data.isMatch = true; data.rowArray = data.$cells.map( function( i ) { - if ( $.inArray( i, columnIndex ) > -1 ) { + if ( $.inArray( i, columnIndex ) > -1 || data.anyMatchFilter ) { if ( data.parsed[ i ] ) { txt = data.cacheArray[ i ]; } else { @@ -1480,7 +1479,6 @@ data.exact = data.rowArray.join( ' ' ); data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' ); - vars.excludeMatch = vars.noAnyMatch; filterMatched = tsf.processTypes( c, data, vars ); if ( filterMatched !== null ) { @@ -2601,7 +2599,6 @@ .bind( 'selectstart', false ); } } - ts.resizable.setHandlePosition( c, wo ); ts.resizable.bindings( c, wo ); }, @@ -2651,12 +2648,11 @@ setHandlePosition : function( c, wo ) { var startPosition, - hasScroller = ts.hasWidget( c.table, 'scroller' ), tableHeight = c.$table.height(), $handles = wo.$resizable_container.children(), handleCenter = Math.floor( $handles.width() / 2 ); - if ( hasScroller ) { + if ( ts.hasWidget( c.table, 'scroller' ) ) { tableHeight = 0; c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function(){ var $this = $(this); @@ -2839,6 +2835,9 @@ init: function(table, thisWidget, c, wo) { ts.resizable.init( c, wo ); }, + format: function( table, c, wo ) { + ts.resizable.setHandlePosition( c, wo ); + }, remove: function( table, c, wo, refreshing ) { if (wo.$resizable_container) { var namespace = c.namespace + 'tsresize'; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 9153240..2d131ac 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -28,13 +28,14 @@ id: 'filter', priority: 50, options : { + filter_cellFilter : '', // css class name added to the filter cell ( string or array ) filter_childRows : false, // if true, filter includes child row content in the search filter_childByColumn : false, // ( filter_childRows must be true ) if true = search child rows by column; false = search all child row text grouped filter_childWithSibs : true, // if true, include matching child row siblings - filter_columnFilters : true, // if true, a filter will be added to the top of each table column filter_columnAnyMatch: true, // if true, allows using '#:{query}' in AnyMatch searches ( column:query ) - filter_cellFilter : '', // css class name added to the filter cell ( string or array ) + filter_columnFilters : true, // if true, a filter will be added to the top of each table column filter_cssFilter : '', // css class name added to the filter row & each input in the row ( tablesorter-filter is ALWAYS added ) + filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value filter_defaultFilter : {}, // add a default column filter type '~{query}' to make fuzzy searches default; '{q1} AND {q2}' to make all searches use a logical AND. filter_excludeFilter : {}, // filters to exclude, per column filter_external : '', // jQuery selector string ( or jQuery object ) of external filters @@ -54,11 +55,10 @@ filter_searchDelay : 300, // typing delay in milliseconds before starting a search filter_searchFiltered: true, // allow searching through already filtered rows in special circumstances; will speed up searching in large tables if true filter_selectSource : null, // include a function to return an array of values to be added to the column filter select - filter_startsWith : false, // if true, filter start from the beginning of the cell contents - filter_useParsedData : false, // filter all data using parsed content + filter_selectSourceSeparator : '|', // filter_selectSource array text left of the separator is added to the option value, right into the option text filter_serversideFiltering : false, // if true, must perform server-side filtering b/c client-side filtering is disabled, but the ui and events will still be used. - filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value - filter_selectSourceSeparator : '|' // filter_selectSource array text left of the separator is added to the option value, right into the option text + filter_startsWith : false, // if true, filter start from the beginning of the cell contents + filter_useParsedData : false // filter all data using parsed content }, format: function( table, c, wo ) { if ( !c.$table.hasClass( 'hasFilters' ) ) { @@ -1083,12 +1083,11 @@ []; data.$cells = data.$row.children(); - - if ( data.anyMatchFlag && columnIndex.length > 1 ) { + if ( data.anyMatchFlag && columnIndex.length > 1 || data.anyMatchFilter ) { data.anyMatch = true; data.isMatch = true; data.rowArray = data.$cells.map( function( i ) { - if ( $.inArray( i, columnIndex ) > -1 ) { + if ( $.inArray( i, columnIndex ) > -1 || data.anyMatchFilter ) { if ( data.parsed[ i ] ) { txt = data.cacheArray[ i ]; } else { @@ -1106,7 +1105,6 @@ data.exact = data.rowArray.join( ' ' ); data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' ); - vars.excludeMatch = vars.noAnyMatch; filterMatched = tsf.processTypes( c, data, vars ); if ( filterMatched !== null ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index d0a3cd1..44153a9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -169,12 +169,13 @@ }, groupHeaderHTML : function( c, wo, data ) { + var name = ( data.currentGroup || '' ).replace(/</g, '<').replace(/>/g, '>'); return '<tr class="group-header ' + c.selectorRemove.slice(1) + '" unselectable="on" ' + ( c.tabIndex ? 'tabindex="0" ' : '' ) + 'data-group-index="' + ( data.groupIndex++ ) + '">' + '<td colspan="' + c.columns + '">' + ( wo.group_collapsible ? '<i/>' : '' ) + - '<span class="group-name">' + data.currentGroup + '</span>' + + '<span class="group-name">' + name + '</span>' + '<span class="group-count"></span>' + '</td></tr>'; }, @@ -197,12 +198,12 @@ return savedGroup; }, findColumnGroups : function( c, wo, data ) { - var tbodyIndex, norm_rows, $row, rowIndex, end, + var tbodyIndex, norm_rows, $row, rowIndex, end, undef, hasPager = ts.hasWidget( c.table, 'pager' ); data.groupIndex = 0; for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { norm_rows = c.cache[ tbodyIndex ].normalized; - data.group = ''; // clear grouping across tbodies + data.group = undef; // clear grouping across tbodies rowIndex = hasPager ? c.pager.startRow - 1 : 0; end = hasPager ? c.pager.endRow : norm_rows.length; for ( ; rowIndex < end; rowIndex++ ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index 8ccc798..c53846a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -264,10 +264,19 @@ // update internal cache ts.update( c, undef, function(){ math.updateComplete( c ); + if ( !init && typeof wo.math_completed === 'function' ) { + wo.math_completed( c ); + } + if ( c.debug ) { + console.log( 'Math widget update completed' + ts.benchmark( time ) ); + } }); - + } else { + if ( !init && typeof wo.math_completed === 'function' ) { + wo.math_completed( c ); + } if ( c.debug ) { - console.log( 'Math widget update completed' + ts.benchmark( time ) ); + console.log( 'Math widget found no changes in data' + ts.benchmark( time ) ); } } } @@ -580,6 +589,8 @@ math_mask : '#,##0.00', // complete executed after each fucntion math_complete : null, // function($cell, wo, result, value, arry){ return result; }, + // math_completed called after all math calculations have completed + // math_completed : function( config ) {}, // order of calculation; 'all' is last math_priority : [ 'row', 'above', 'below', 'col' ], // template for or just prepend the mask prefix & suffix with this HTML diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js index c2a8dd5..f1afdc9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js @@ -102,7 +102,6 @@ .bind( 'selectstart', false ); } } - ts.resizable.setHandlePosition( c, wo ); ts.resizable.bindings( c, wo ); }, @@ -152,12 +151,11 @@ setHandlePosition : function( c, wo ) { var startPosition, - hasScroller = ts.hasWidget( c.table, 'scroller' ), tableHeight = c.$table.height(), $handles = wo.$resizable_container.children(), handleCenter = Math.floor( $handles.width() / 2 ); - if ( hasScroller ) { + if ( ts.hasWidget( c.table, 'scroller' ) ) { tableHeight = 0; c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function(){ var $this = $(this); @@ -340,6 +338,9 @@ init: function(table, thisWidget, c, wo) { ts.resizable.init( c, wo ); }, + format: function( table, c, wo ) { + ts.resizable.setHandlePosition( c, wo ); + }, remove: function( table, c, wo, refreshing ) { if (wo.$resizable_container) { var namespace = c.namespace + 'tsresize'; From bb99c37a5701827302321cc2525684f17a742d74 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Thu, 16 Jun 2016 19:28:47 +0200 Subject: [PATCH 089/138] Update tablesorter to latest version (2.26.2) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 1714 +++++++++-------- .../jquery.tablesorter.combined.js | 19 +- .../jquery-tablesorter/jquery.tablesorter.js | 4 +- .../jquery.tablesorter.widgets.js | 15 +- .../widget-filter-formatter-select2.js | 10 +- .../widgets/widget-filter.js | 11 +- .../widgets/widget-grouping.js | 2 +- .../widgets/widget-resizable.js | 2 +- .../widgets/widget-scroller.js | 5 +- 13 files changed, 905 insertions(+), 887 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a0d4d2..a6b1cca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.21.2 (2016-06-16) + +* Upgrade tablesorter to v2.26.4 + #### v1.21.1 (2016-05-18) * Upgrade tablesorter to v2.26.1 diff --git a/README.md b/README.md index 463c62d..96c868c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.26.1 (5/16/2016), [documentation] +Current tablesorter version: 2.26.4 (6/15/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 94f56fb..d635503 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 21 - TINY = 1 + TINY = 2 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 164fdf9..ed17d4b 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 164fdf9f9afeaa419b04272f50c0d4c9731e9ddd +Subproject commit ed17d4ba8300927e63bacad9238fc8356e8f50c8 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 0e4105f..fc6a503 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,188 +1,189 @@ /*! - * tablesorter (FORK) pager plugin - * updated 5/1/2016 (v2.26.0) - */ +* tablesorter (FORK) pager plugin +* updated 5/1/2016 (v2.26.0) +*/ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { 'use strict'; /*jshint supernew:true */ var ts = $.tablesorter; - $.extend({ tablesorterPager: new function() { + $.extend({ + tablesorterPager: new function() { + + this.defaults = { + // target the pager markup + container: null, + + // use this format: "http://mydatabase.com?page={page}&size={size}&{sortList:col}&{filterList:fcol}" + // where {page} is replaced by the page number, {size} is replaced by the number of records to show, + // {sortList:col} adds the sortList to the url into a "col" array, and {filterList:fcol} adds + // the filterList to the url into an "fcol" array. + // So a sortList = [[2,0],[3,0]] becomes "&col[2]=0&col[3]=0" in the url + // and a filterList = [[2,Blue],[3,13]] becomes "&fcol[2]=Blue&fcol[3]=13" in the url + ajaxUrl: null, + + // modify the url after all processing has been applied + customAjaxUrl: function(table, url) { return url; }, + + // ajax error callback from $.tablesorter.showError function + // ajaxError: function( config, xhr, settings, exception ){ return exception; }; + // returning false will abort the error message + ajaxError: null, + + // modify the $.ajax object to allow complete control over your ajax requests + ajaxObject: { + dataType: 'json' + }, + + // set this to false if you want to block ajax loading on init + processAjaxOnInit: true, + + // process ajax so that the following information is returned: + // [ total_rows (number), rows (array of arrays), headers (array; optional) ] + // example: + // [ + // 100, // total rows + // [ + // [ "row1cell1", "row1cell2", ... "row1cellN" ], + // [ "row2cell1", "row2cell2", ... "row2cellN" ], + // ... + // [ "rowNcell1", "rowNcell2", ... "rowNcellN" ] + // ], + // [ "header1", "header2", ... "headerN" ] // optional + // ] + ajaxProcessing: function(ajax){ return [ 0, [], null ]; }, + + // output default: '{page}/{totalPages}' + // possible variables: {size}, {page}, {totalPages}, {filteredPages}, {startRow}, + // {endRow}, {filteredRows} and {totalRows} + output: '{startRow} to {endRow} of {totalRows} rows', // '{page}/{totalPages}' + + // apply disabled classname to the pager arrows when the rows at either extreme is visible + updateArrows: true, + + // starting page of the pager (zero based index) + page: 0, + + // reset pager after filtering; set to desired page # + // set to false to not change page at filter start + pageReset: 0, + + // Number of visible rows + size: 10, + + // Number of options to include in the pager number selector + maxOptionSize: 20, + + // Save pager page & size if the storage script is loaded (requires $.tablesorter.storage in jquery.tablesorter.widgets.js) + savePages: true, + + // defines custom storage key + storageKey: 'tablesorter-pager', + + // if true, the table will remain the same height no matter how many records are displayed. The space is made up by an empty + // table row set to a height to compensate; default is false + fixedHeight: false, + + // count child rows towards the set page size? (set true if it is a visible table row within the pager) + // if true, child row(s) may not appear to be attached to its parent row, may be split across pages or + // may distort the table if rowspan or cellspans are included. + countChildRows: false, + + // remove rows from the table to speed up the sort of large tables. + // setting this to false, only hides the non-visible rows; needed if you plan to add/remove rows with the pager enabled. + removeRows: false, // removing rows in larger tables speeds up the sort + + // css class names of pager arrows + cssFirst: '.first', // go to first page arrow + cssPrev: '.prev', // previous page arrow + cssNext: '.next', // next page arrow + cssLast: '.last', // go to last page arrow + cssGoto: '.gotoPage', // go to page selector - select dropdown that sets the current page + cssPageDisplay: '.pagedisplay', // location of where the "output" is displayed + cssPageSize: '.pagesize', // page size selector - select dropdown that sets the "size" option + cssErrorRow: 'tablesorter-errorRow', // error information row + + // class added to arrows when at the extremes (i.e. prev/first arrows are "disabled" when on the first page) + cssDisabled: 'disabled', // Note there is no period "." in front of this class name + + // stuff not set by the user + totalRows: 0, + totalPages: 0, + filteredRows: 0, + filteredPages: 0, + ajaxCounter: 0, + currentFilters: [], + startRow: 0, + endRow: 0, + $size: null, + last: {} - this.defaults = { - // target the pager markup - container: null, - - // use this format: "http://mydatabase.com?page={page}&size={size}&{sortList:col}&{filterList:fcol}" - // where {page} is replaced by the page number, {size} is replaced by the number of records to show, - // {sortList:col} adds the sortList to the url into a "col" array, and {filterList:fcol} adds - // the filterList to the url into an "fcol" array. - // So a sortList = [[2,0],[3,0]] becomes "&col[2]=0&col[3]=0" in the url - // and a filterList = [[2,Blue],[3,13]] becomes "&fcol[2]=Blue&fcol[3]=13" in the url - ajaxUrl: null, - - // modify the url after all processing has been applied - customAjaxUrl: function(table, url) { return url; }, - - // ajax error callback from $.tablesorter.showError function - // ajaxError: function( config, xhr, settings, exception ){ return exception; }; - // returning false will abort the error message - ajaxError: null, - - // modify the $.ajax object to allow complete control over your ajax requests - ajaxObject: { - dataType: 'json' - }, - - // set this to false if you want to block ajax loading on init - processAjaxOnInit: true, - - // process ajax so that the following information is returned: - // [ total_rows (number), rows (array of arrays), headers (array; optional) ] - // example: - // [ - // 100, // total rows - // [ - // [ "row1cell1", "row1cell2", ... "row1cellN" ], - // [ "row2cell1", "row2cell2", ... "row2cellN" ], - // ... - // [ "rowNcell1", "rowNcell2", ... "rowNcellN" ] - // ], - // [ "header1", "header2", ... "headerN" ] // optional - // ] - ajaxProcessing: function(ajax){ return [ 0, [], null ]; }, - - // output default: '{page}/{totalPages}' - // possible variables: {size}, {page}, {totalPages}, {filteredPages}, {startRow}, - // {endRow}, {filteredRows} and {totalRows} - output: '{startRow} to {endRow} of {totalRows} rows', // '{page}/{totalPages}' - - // apply disabled classname to the pager arrows when the rows at either extreme is visible - updateArrows: true, - - // starting page of the pager (zero based index) - page: 0, - - // reset pager after filtering; set to desired page # - // set to false to not change page at filter start - pageReset: 0, - - // Number of visible rows - size: 10, - - // Number of options to include in the pager number selector - maxOptionSize: 20, - - // Save pager page & size if the storage script is loaded (requires $.tablesorter.storage in jquery.tablesorter.widgets.js) - savePages: true, - - // defines custom storage key - storageKey: 'tablesorter-pager', - - // if true, the table will remain the same height no matter how many records are displayed. The space is made up by an empty - // table row set to a height to compensate; default is false - fixedHeight: false, - - // count child rows towards the set page size? (set true if it is a visible table row within the pager) - // if true, child row(s) may not appear to be attached to its parent row, may be split across pages or - // may distort the table if rowspan or cellspans are included. - countChildRows: false, - - // remove rows from the table to speed up the sort of large tables. - // setting this to false, only hides the non-visible rows; needed if you plan to add/remove rows with the pager enabled. - removeRows: false, // removing rows in larger tables speeds up the sort - - // css class names of pager arrows - cssFirst: '.first', // go to first page arrow - cssPrev: '.prev', // previous page arrow - cssNext: '.next', // next page arrow - cssLast: '.last', // go to last page arrow - cssGoto: '.gotoPage', // go to page selector - select dropdown that sets the current page - cssPageDisplay: '.pagedisplay', // location of where the "output" is displayed - cssPageSize: '.pagesize', // page size selector - select dropdown that sets the "size" option - cssErrorRow: 'tablesorter-errorRow', // error information row - - // class added to arrows when at the extremes (i.e. prev/first arrows are "disabled" when on the first page) - cssDisabled: 'disabled', // Note there is no period "." in front of this class name - - // stuff not set by the user - totalRows: 0, - totalPages: 0, - filteredRows: 0, - filteredPages: 0, - ajaxCounter: 0, - currentFilters: [], - startRow: 0, - endRow: 0, - $size: null, - last: {} - - }; + }; - var pagerEvents = 'filterInit filterStart filterEnd sortEnd disablePager enablePager destroyPager updateComplete ' + + var pagerEvents = 'filterInit filterStart filterEnd sortEnd disablePager enablePager destroyPager updateComplete ' + 'pageSize pageSet pageAndSize pagerUpdate refreshComplete ', - $this = this, - - // hide arrows at extremes - pagerArrows = function( table, p, disable ) { - var a = 'addClass', - r = 'removeClass', - d = p.cssDisabled, - dis = !!disable, - first = ( dis || p.page === 0 ), - tp = getTotalPages( table, p ), - last = ( dis || (p.page === tp - 1) || tp === 0 ); - if ( p.updateArrows ) { - p.$container.find(p.cssFirst + ',' + p.cssPrev)[ first ? a : r ](d).attr('aria-disabled', first); - p.$container.find(p.cssNext + ',' + p.cssLast)[ last ? a : r ](d).attr('aria-disabled', last); - } - }, + $this = this, + + // hide arrows at extremes + pagerArrows = function( table, p, disable ) { + var a = 'addClass', + r = 'removeClass', + d = p.cssDisabled, + dis = !!disable, + first = ( dis || p.page === 0 ), + tp = getTotalPages( table, p ), + last = ( dis || (p.page === tp - 1) || tp === 0 ); + if ( p.updateArrows ) { + p.$container.find(p.cssFirst + ',' + p.cssPrev)[ first ? a : r ](d).attr('aria-disabled', first); + p.$container.find(p.cssNext + ',' + p.cssLast)[ last ? a : r ](d).attr('aria-disabled', last); + } + }, - calcFilters = function(table, p) { - var normalized, indx, len, + calcFilters = function(table, p) { + var normalized, indx, len, c = table.config, hasFilters = c.$table.hasClass('hasFilters'); - if (hasFilters && !p.ajaxUrl) { - if (ts.isEmptyObject(c.cache)) { - // delayInit: true so nothing is in the cache - p.filteredRows = p.totalRows = c.$tbodies.eq(0).children('tr').not( p.countChildRows ? '' : '.' + c.cssChildRow ).length; - } else { - p.filteredRows = 0; - normalized = c.cache[0].normalized; - len = normalized.length; - for (indx = 0; indx < len; indx++) { - p.filteredRows += p.regexRows.test(normalized[indx][c.columns].$row[0].className) ? 0 : 1; + if (hasFilters && !p.ajaxUrl) { + if (ts.isEmptyObject(c.cache)) { + // delayInit: true so nothing is in the cache + p.filteredRows = p.totalRows = c.$tbodies.eq(0).children('tr').not( p.countChildRows ? '' : '.' + c.cssChildRow ).length; + } else { + p.filteredRows = 0; + normalized = c.cache[0].normalized; + len = normalized.length; + for (indx = 0; indx < len; indx++) { + p.filteredRows += p.regexRows.test(normalized[indx][c.columns].$row[0].className) ? 0 : 1; + } } + } else if (!hasFilters) { + p.filteredRows = p.totalRows; } - } else if (!hasFilters) { - p.filteredRows = p.totalRows; - } - }, + }, - updatePageDisplay = function(table, p, completed) { - if ( p.initializing ) { return; } - var s, t, $out, indx, len, options, + updatePageDisplay = function(table, p, completed) { + if ( p.initializing ) { return; } + var s, t, $out, indx, len, options, c = table.config, namespace = c.namespace + 'pager', sz = parsePageSize( p, p.size, 'get' ); // don't allow dividing by zero - if (sz === 'all') { sz = p.totalRows; } - if (p.countChildRows) { t[ t.length ] = c.cssChildRow; } - p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method - c.totalRows = p.totalRows; - parsePageNumber( table, p ); - calcFilters(table, p); - c.filteredRows = p.filteredRows; - p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; - if ( getTotalPages( table, p ) >= 0 ) { - t = (sz * p.page > p.filteredRows) && completed; - p.page = (t) ? p.pageReset || 0 : p.page; - p.startRow = (t) ? sz * p.page + 1 : (p.filteredRows === 0 ? 0 : sz * p.page + 1); - p.endRow = Math.min( p.filteredRows, p.totalRows, sz * ( p.page + 1 ) ); - $out = p.$container.find(p.cssPageDisplay); - // form the output string (can now get a new output string from the server) - s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || p.output : p.output ) + if (sz === 'all') { sz = p.totalRows; } + if (p.countChildRows) { t[ t.length ] = c.cssChildRow; } + p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method + c.totalRows = p.totalRows; + parsePageNumber( table, p ); + calcFilters(table, p); + c.filteredRows = p.filteredRows; + p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; + if ( getTotalPages( table, p ) >= 0 ) { + t = (sz * p.page > p.filteredRows) && completed; + p.page = (t) ? p.pageReset || 0 : p.page; + p.startRow = (t) ? sz * p.page + 1 : (p.filteredRows === 0 ? 0 : sz * p.page + 1); + p.endRow = Math.min( p.filteredRows, p.totalRows, sz * ( p.page + 1 ) ); + $out = p.$container.find(p.cssPageDisplay); + // form the output string (can now get a new output string from the server) + s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || p.output : p.output ) // {page} = one-based index; {page+#} = zero based index +/- value .replace(/\{page([\-+]\d+)?\}/gi, function(m, n){ return p.totalPages ? p.page + (n ? parseInt(n, 10) : 1) : 0; @@ -190,11 +191,11 @@ // {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object) .replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m){ var len, indx, - str = m.replace(/[{}\s]/g, ''), - extra = str.split(':'), - data = p.ajaxData, - // return zero for default page/row numbers - deflt = /(rows?|pages?)$/i.test(str) ? 0 : ''; + str = m.replace(/[{}\s]/g, ''), + extra = str.split(':'), + data = p.ajaxData, + // return zero for default page/row numbers + deflt = /(rows?|pages?)$/i.test(str) ? 0 : ''; if (/(startRow|page)/.test(extra[0]) && extra[1] === 'input') { len = ('' + (extra[0] === 'page' ? p.totalPages : p.totalRows)).length; indx = extra[0] === 'page' ? p.page + 1 : p.startRow; @@ -202,47 +203,47 @@ } return extra.length > 1 && data && data[extra[0]] ? data[extra[0]][extra[1]] : p[str] || (data ? data[str] : deflt) || deflt; }); - if ( p.$goto.length ) { - t = ''; - options = buildPageSelect( table, p ); - len = options.length; - for (indx = 0; indx < len; indx++) { - t += '<option value="' + options[indx] + '">' + options[indx] + '</option>'; + if ( p.$goto.length ) { + t = ''; + options = buildPageSelect( table, p ); + len = options.length; + for (indx = 0; indx < len; indx++) { + t += '<option value="' + options[indx] + '">' + options[indx] + '</option>'; + } + // innerHTML doesn't work in IE9 - http://support2.microsoft.com/kb/276228 + p.$goto.html(t).val( p.page + 1 ); } - // innerHTML doesn't work in IE9 - http://support2.microsoft.com/kb/276228 - p.$goto.html(t).val( p.page + 1 ); - } - if ($out.length) { - $out[ ($out[0].nodeName === 'INPUT') ? 'val' : 'html' ](s); - // rebind startRow/page inputs - $out.find('.ts-startRow, .ts-page').unbind('change' + namespace).bind('change' + namespace, function(){ - var v = $(this).val(), + if ($out.length) { + $out[ ($out[0].nodeName === 'INPUT') ? 'val' : 'html' ](s); + // rebind startRow/page inputs + $out.find('.ts-startRow, .ts-page').unbind('change' + namespace).bind('change' + namespace, function(){ + var v = $(this).val(), pg = $(this).hasClass('ts-startRow') ? Math.floor( v / sz ) + 1 : v; - c.$table.triggerHandler('pageSet' + namespace, [ pg ]); - }); - } - } - pagerArrows( table, p ); - fixHeight(table, p); - if (p.initialized && completed !== false) { - if (c.debug) { - console.log('Pager: Triggering pagerComplete'); + c.$table.triggerHandler('pageSet' + namespace, [ pg ]); + }); + } } - c.$table.triggerHandler('pagerComplete', p); - // save pager info to storage - if (p.savePages && ts.storage) { - ts.storage(table, p.storageKey, { - page : p.page, - size : sz === p.totalRows ? 'all' : sz - }); + pagerArrows( table, p ); + fixHeight(table, p); + if (p.initialized && completed !== false) { + if (c.debug) { + console.log('Pager: Triggering pagerComplete'); + } + c.$table.triggerHandler('pagerComplete', p); + // save pager info to storage + if (p.savePages && ts.storage) { + ts.storage(table, p.storageKey, { + page : p.page, + size : sz === p.totalRows ? 'all' : sz + }); + } } - } - }, + }, - buildPageSelect = function( table, p ) { - // Filter the options page number link array if it's larger than 'maxOptionSize' - // as large page set links will slow the browser on large dom inserts - var i, central_focus_size, focus_option_pages, insert_index, option_length, focus_length, + buildPageSelect = function( table, p ) { + // Filter the options page number link array if it's larger than 'maxOptionSize' + // as large page set links will slow the browser on large dom inserts + var i, central_focus_size, focus_option_pages, insert_index, option_length, focus_length, pg = getTotalPages( table, p ) || 1, // make skip set size multiples of 5 skip_set_size = Math.ceil( ( pg / p.maxOptionSize ) / 5 ) * 5, @@ -254,666 +255,666 @@ // construct default options pages array option_pages_start_page = (large_collection) ? skip_set_size : 1; - for ( i = option_pages_start_page; i <= pg; ) { - option_pages[ option_pages.length ] = i; - i = i + ( large_collection ? skip_set_size : 1 ); - } - option_pages[ option_pages.length ] = pg; - if (large_collection) { - focus_option_pages = []; - // don't allow central focus size to be > 5 on either side of current page - central_focus_size = Math.max( Math.floor( p.maxOptionSize / skip_set_size ) - 1, 5 ); - - start_page = current_page - central_focus_size; - if (start_page < 1) { start_page = 1; } - end_page = current_page + central_focus_size; - if (end_page > pg) { end_page = pg; } - // construct an array to get a focus set around the current page - for (i = start_page; i <= end_page ; i++) { - focus_option_pages[ focus_option_pages.length ] = i; + for ( i = option_pages_start_page; i <= pg; ) { + option_pages[ option_pages.length ] = i; + i = i + ( large_collection ? skip_set_size : 1 ); } + option_pages[ option_pages.length ] = pg; + if (large_collection) { + focus_option_pages = []; + // don't allow central focus size to be > 5 on either side of current page + central_focus_size = Math.max( Math.floor( p.maxOptionSize / skip_set_size ) - 1, 5 ); + + start_page = current_page - central_focus_size; + if (start_page < 1) { start_page = 1; } + end_page = current_page + central_focus_size; + if (end_page > pg) { end_page = pg; } + // construct an array to get a focus set around the current page + for (i = start_page; i <= end_page ; i++) { + focus_option_pages[ focus_option_pages.length ] = i; + } - // keep unique values - option_pages = $.grep(option_pages, function(value, indx) { - return $.inArray(value, option_pages) === indx; - }); + // keep unique values + option_pages = $.grep(option_pages, function(value, indx) { + return $.inArray(value, option_pages) === indx; + }); - option_length = option_pages.length; - focus_length = focus_option_pages.length; + option_length = option_pages.length; + focus_length = focus_option_pages.length; - // make sure at all option_pages aren't replaced - if (option_length - focus_length > skip_set_size / 2 && option_length + focus_length > p.maxOptionSize ) { - insert_index = Math.floor(option_length / 2) - Math.floor(focus_length / 2); - Array.prototype.splice.apply(option_pages, [ insert_index, focus_length ]); - } - option_pages = option_pages.concat(focus_option_pages); + // make sure at all option_pages aren't replaced + if (option_length - focus_length > skip_set_size / 2 && option_length + focus_length > p.maxOptionSize ) { + insert_index = Math.floor(option_length / 2) - Math.floor(focus_length / 2); + Array.prototype.splice.apply(option_pages, [ insert_index, focus_length ]); + } + option_pages = option_pages.concat(focus_option_pages); - } + } - // keep unique values again - option_pages = $.grep(option_pages, function(value, indx) { - return $.inArray(value, option_pages) === indx; - }) - .sort(function(a, b) { return a - b; }); + // keep unique values again + option_pages = $.grep(option_pages, function(value, indx) { + return $.inArray(value, option_pages) === indx; + }) + .sort(function(a, b) { return a - b; }); - return option_pages; - }, + return option_pages; + }, - fixHeight = function(table, p) { - var d, h, + fixHeight = function(table, p) { + var d, h, c = table.config, $b = c.$tbodies.eq(0); - $b.find('tr.pagerSavedHeightSpacer').remove(); - if (p.fixedHeight && !p.isDisabled) { - h = $.data(table, 'pagerSavedHeight'); - if (h) { - d = h - $b.height(); - if ( d > 5 && $.data(table, 'pagerLastSize') === p.size && + $b.find('tr.pagerSavedHeightSpacer').remove(); + if (p.fixedHeight && !p.isDisabled) { + h = $.data(table, 'pagerSavedHeight'); + if (h) { + d = h - $b.height(); + if ( d > 5 && $.data(table, 'pagerLastSize') === p.size && $b.children('tr:visible').length < (p.size === 'all' ? p.totalRows : p.size) ) { - $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice(1) + '" style="height:' + d + 'px;"></tr>'); + $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice(1) + '" style="height:' + d + 'px;"></tr>'); + } } } - } - }, + }, - changeHeight = function(table, p) { - var h, + changeHeight = function(table, p) { + var h, c = table.config, $b = c.$tbodies.eq(0); - $b.find('tr.pagerSavedHeightSpacer').remove(); - if (!$b.children('tr:visible').length) { - $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice(1) + '"><td> </td></tr>'); - } - h = $b.children('tr').eq(0).height() * (p.size === 'all' ? p.totalRows : p.size); - $.data(table, 'pagerSavedHeight', h); - fixHeight(table, p); - $.data(table, 'pagerLastSize', p.size); - }, - - hideRows = function(table, p){ - if (!p.ajaxUrl) { - var i, - lastIndex = 0, - c = table.config, - rows = c.$tbodies.eq(0).children('tr'), - l = rows.length, - sz = p.size === 'all' ? p.totalRows : p.size, - s = ( p.page * sz ), - e = s + sz, - f = c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered', - last = 0, // for cache indexing - j = 0; // size counter - p.cacheIndex = []; - for ( i = 0; i < l; i++ ){ - if ( !rows[i].className.match(f) ) { - if (j === s && rows[i].className.match(c.cssChildRow)) { - // hide child rows @ start of pager (if already visible) - rows[i].style.display = 'none'; - } else { - rows[i].style.display = ( j >= s && j < e ) ? '' : 'none'; - if (last !== j && j >= s && j < e) { - p.cacheIndex[ p.cacheIndex.length ] = i; - last = j; - } - // don't count child rows - j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) && !p.countChildRows ? 0 : 1; - if ( j === e && rows[i].style.display !== 'none' && rows[i].className.match(ts.css.cssHasChild) ) { - lastIndex = i; + $b.find('tr.pagerSavedHeightSpacer').remove(); + if (!$b.children('tr:visible').length) { + $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice(1) + '"><td> </td></tr>'); + } + h = $b.children('tr').eq(0).height() * (p.size === 'all' ? p.totalRows : p.size); + $.data(table, 'pagerSavedHeight', h); + fixHeight(table, p); + $.data(table, 'pagerLastSize', p.size); + }, + + hideRows = function(table, p){ + if (!p.ajaxUrl) { + var i, + lastIndex = 0, + c = table.config, + rows = c.$tbodies.eq(0).children('tr'), + l = rows.length, + sz = p.size === 'all' ? p.totalRows : p.size, + s = ( p.page * sz ), + e = s + sz, + f = c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered', + last = 0, // for cache indexing + j = 0; // size counter + p.cacheIndex = []; + for ( i = 0; i < l; i++ ){ + if ( !rows[i].className.match(f) ) { + if (j === s && rows[i].className.match(c.cssChildRow)) { + // hide child rows @ start of pager (if already visible) + rows[i].style.display = 'none'; + } else { + rows[i].style.display = ( j >= s && j < e ) ? '' : 'none'; + if (last !== j && j >= s && j < e) { + p.cacheIndex[ p.cacheIndex.length ] = i; + last = j; + } + // don't count child rows + j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) && !p.countChildRows ? 0 : 1; + if ( j === e && rows[i].style.display !== 'none' && rows[i].className.match(ts.css.cssHasChild) ) { + lastIndex = i; + } } } } - } - // add any attached child rows to last row of pager. Fixes part of issue #396 - if ( lastIndex > 0 && rows[lastIndex].className.match(ts.css.cssHasChild) ) { - while ( ++lastIndex < l && rows[lastIndex].className.match(c.cssChildRow) ) { - rows[lastIndex].style.display = ''; + // add any attached child rows to last row of pager. Fixes part of issue #396 + if ( lastIndex > 0 && rows[lastIndex].className.match(ts.css.cssHasChild) ) { + while ( ++lastIndex < l && rows[lastIndex].className.match(c.cssChildRow) ) { + rows[lastIndex].style.display = ''; + } } } - } - }, - - hideRowsSetup = function(table, p){ - p.size = parsePageSize( p, p.$size.val(), 'get' ); - p.$size.val( p.size ); - $.data(table, 'pagerLastSize', p.size); - pagerArrows( table, p ); - if ( !p.removeRows ) { - hideRows(table, p); - $(table).bind('sortEnd filterEnd '.split(' ').join(table.config.namespace + 'pager '), function(){ + }, + + hideRowsSetup = function(table, p){ + p.size = parsePageSize( p, p.$size.val(), 'get' ); + p.$size.val( p.size ); + $.data(table, 'pagerLastSize', p.size); + pagerArrows( table, p ); + if ( !p.removeRows ) { hideRows(table, p); - }); - } - }, + $(table).bind('sortEnd filterEnd '.split(' ').join(table.config.namespace + 'pager '), function(){ + hideRows(table, p); + }); + } + }, - renderAjax = function(data, table, p, xhr, settings, exception){ - // process data - if ( typeof p.ajaxProcessing === 'function' ) { + renderAjax = function(data, table, p, xhr, settings, exception){ + // process data + if ( typeof p.ajaxProcessing === 'function' ) { - // in case nothing is returned by ajax, empty out the table; see #1032 - // but do it before calling pager_ajaxProcessing because that function may add content - // directly to the table - table.config.$tbodies.eq(0).empty(); + // in case nothing is returned by ajax, empty out the table; see #1032 + // but do it before calling pager_ajaxProcessing because that function may add content + // directly to the table + table.config.$tbodies.eq(0).empty(); - // ajaxProcessing result: [ total, rows, headers ] - var i, j, t, hsh, $f, $sh, $headers, $h, icon, th, d, l, rr_count, len, sz, + // ajaxProcessing result: [ total, rows, headers ] + var i, j, t, hsh, $f, $sh, $headers, $h, icon, th, d, l, rr_count, len, sz, c = table.config, $table = c.$table, tds = '', result = p.ajaxProcessing(data, table, xhr) || [ 0, [] ], hl = $table.find('thead th').length; - // Clean up any previous error. - ts.showError( table ); + // Clean up any previous error. + ts.showError( table ); - if ( exception ) { - if (c.debug) { - console.error('Pager: >> Ajax Error', xhr, settings, exception); - } - ts.showError( table, xhr, settings, exception ); - c.$tbodies.eq(0).children('tr').detach(); - p.totalRows = 0; - } else { - // process ajax object - if (!$.isArray(result)) { - p.ajaxData = result; - c.totalRows = p.totalRows = result.total; - c.filteredRows = p.filteredRows = typeof result.filteredRows !== 'undefined' ? result.filteredRows : result.total; - th = result.headers; - d = result.rows || []; + if ( exception ) { + if (c.debug) { + console.error('Pager: >> Ajax Error', xhr, settings, exception); + } + ts.showError( table, xhr, settings, exception ); + c.$tbodies.eq(0).children('tr').detach(); + p.totalRows = 0; } else { - // allow [ total, rows, headers ] or [ rows, total, headers ] - t = isNaN(result[0]) && !isNaN(result[1]); - // ensure a zero returned row count doesn't fail the logical || - rr_count = result[t ? 1 : 0]; - p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count; - // can't set filtered rows when returning an array - c.totalRows = c.filteredRows = p.filteredRows = p.totalRows; - // set row data to empty array if nothing found - see http://stackoverflow.com/q/30875583/145346 - d = p.totalRows === 0 ? [] : result[t ? 0 : 1] || []; // row data - th = result[2]; // headers - } - l = d && d.length; - if (d instanceof jQuery) { - if (p.processAjaxOnInit) { - // append jQuery object - c.$tbodies.eq(0).empty(); - c.$tbodies.eq(0).append(d); + // process ajax object + if (!$.isArray(result)) { + p.ajaxData = result; + c.totalRows = p.totalRows = result.total; + c.filteredRows = p.filteredRows = typeof result.filteredRows !== 'undefined' ? result.filteredRows : result.total; + th = result.headers; + d = result.rows || []; + } else { + // allow [ total, rows, headers ] or [ rows, total, headers ] + t = isNaN(result[0]) && !isNaN(result[1]); + // ensure a zero returned row count doesn't fail the logical || + rr_count = result[t ? 1 : 0]; + p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count; + // can't set filtered rows when returning an array + c.totalRows = c.filteredRows = p.filteredRows = p.totalRows; + // set row data to empty array if nothing found - see http://stackoverflow.com/q/30875583/145346 + d = p.totalRows === 0 ? [] : result[t ? 0 : 1] || []; // row data + th = result[2]; // headers } - } else if (l) { - // build table from array - for ( i = 0; i < l; i++ ) { - tds += '<tr>'; - for ( j = 0; j < d[i].length; j++ ) { - // build tbody cells; watch for data containing HTML markup - see #434 - tds += /^\s*<td/.test(d[i][j]) ? $.trim(d[i][j]) : '<td>' + d[i][j] + '</td>'; + l = d && d.length; + if (d instanceof jQuery) { + if (p.processAjaxOnInit) { + // append jQuery object + c.$tbodies.eq(0).empty(); + c.$tbodies.eq(0).append(d); } - tds += '</tr>'; - } - // add rows to first tbody - if (p.processAjaxOnInit) { - c.$tbodies.eq(0).html( tds ); - } - } - p.processAjaxOnInit = true; - // only add new header text if the length matches - if ( th && th.length === hl ) { - hsh = $table.hasClass('hasStickyHeaders'); - $sh = hsh ? c.widgetOptions.$sticky.children('thead:first').children('tr').children() : ''; - $f = $table.find('tfoot tr:first').children(); - // don't change td headers (may contain pager) - $headers = c.$headers.filter( 'th ' ); - len = $headers.length; - for ( j = 0; j < len; j++ ) { - $h = $headers.eq( j ); - // add new test within the first span it finds, or just in the header - if ( $h.find('.' + ts.css.icon).length ) { - icon = $h.find('.' + ts.css.icon).clone(true); - $h.find('.tablesorter-header-inner').html( th[j] ).append(icon); - if ( hsh && $sh.length ) { - icon = $sh.eq(j).find('.' + ts.css.icon).clone(true); - $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ).append(icon); + } else if (l) { + // build table from array + for ( i = 0; i < l; i++ ) { + tds += '<tr>'; + for ( j = 0; j < d[i].length; j++ ) { + // build tbody cells; watch for data containing HTML markup - see #434 + tds += /^\s*<td/.test(d[i][j]) ? $.trim(d[i][j]) : '<td>' + d[i][j] + '</td>'; } - } else { - $h.find('.tablesorter-header-inner').html( th[j] ); - if (hsh && $sh.length) { - $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ); + tds += '</tr>'; + } + // add rows to first tbody + if (p.processAjaxOnInit) { + c.$tbodies.eq(0).html( tds ); + } + } + p.processAjaxOnInit = true; + // only add new header text if the length matches + if ( th && th.length === hl ) { + hsh = $table.hasClass('hasStickyHeaders'); + $sh = hsh ? c.widgetOptions.$sticky.children('thead:first').children('tr').children() : ''; + $f = $table.find('tfoot tr:first').children(); + // don't change td headers (may contain pager) + $headers = c.$headers.filter( 'th ' ); + len = $headers.length; + for ( j = 0; j < len; j++ ) { + $h = $headers.eq( j ); + // add new test within the first span it finds, or just in the header + if ( $h.find('.' + ts.css.icon).length ) { + icon = $h.find('.' + ts.css.icon).clone(true); + $h.find('.tablesorter-header-inner').html( th[j] ).append(icon); + if ( hsh && $sh.length ) { + icon = $sh.eq(j).find('.' + ts.css.icon).clone(true); + $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ).append(icon); + } + } else { + $h.find('.tablesorter-header-inner').html( th[j] ); + if (hsh && $sh.length) { + $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ); + } } + $f.eq(j).html( th[j] ); } - $f.eq(j).html( th[j] ); } } - } - if (c.showProcessing) { - ts.isProcessing(table); // remove loading icon - } - sz = parsePageSize( p, p.size, 'get' ); - // make sure last pager settings are saved, prevents multiple server side calls with - // the same parameters - p.totalPages = sz === 'all' ? 1 : Math.ceil( p.totalRows / sz ); - p.last.totalRows = p.totalRows; - p.last.currentFilters = p.currentFilters; - p.last.sortList = (c.sortList || []).join(','); - updatePageDisplay(table, p, false); - // tablesorter core updateCache (not pager) - ts.updateCache( c, function(){ - if (p.initialized) { - // apply widgets after table has rendered & after a delay to prevent - // multiple applyWidget blocking code from blocking this trigger - setTimeout(function(){ - if (c.debug) { - console.log('Pager: Triggering pagerChange'); - } - $table.triggerHandler( 'pagerChange', p ); - ts.applyWidget( table ); - updatePageDisplay(table, p, true); - }, 0); + if (c.showProcessing) { + ts.isProcessing(table); // remove loading icon } - }); + sz = parsePageSize( p, p.size, 'get' ); + // make sure last pager settings are saved, prevents multiple server side calls with + // the same parameters + p.totalPages = sz === 'all' ? 1 : Math.ceil( p.totalRows / sz ); + p.last.totalRows = p.totalRows; + p.last.currentFilters = p.currentFilters; + p.last.sortList = (c.sortList || []).join(','); + updatePageDisplay(table, p, false); + // tablesorter core updateCache (not pager) + ts.updateCache( c, function(){ + if (p.initialized) { + // apply widgets after table has rendered & after a delay to prevent + // multiple applyWidget blocking code from blocking this trigger + setTimeout(function(){ + if (c.debug) { + console.log('Pager: Triggering pagerChange'); + } + $table.triggerHandler( 'pagerChange', p ); + ts.applyWidget( table ); + updatePageDisplay(table, p, true); + }, 0); + } + }); - } - if (!p.initialized) { - pagerInitialized(table, p); - } - }, + } + if (!p.initialized) { + pagerInitialized(table, p); + } + }, - getAjax = function(table, p) { - var url = getAjaxUrl(table, p), + getAjax = function(table, p) { + var url = getAjaxUrl(table, p), $doc = $(document), counter, c = table.config, namespace = c.namespace + 'pager'; - if ( url !== '' ) { - if (c.showProcessing) { - ts.isProcessing(table, true); // show loading icon - } - $doc.bind('ajaxError' + namespace, function(e, xhr, settings, exception) { - renderAjax(null, table, p, xhr, settings, exception); - $doc.unbind('ajaxError' + namespace); - }); + if ( url !== '' ) { + if (c.showProcessing) { + ts.isProcessing(table, true); // show loading icon + } + $doc.bind('ajaxError' + namespace, function(e, xhr, settings, exception) { + renderAjax(null, table, p, xhr, settings, exception); + $doc.unbind('ajaxError' + namespace); + }); - counter = ++p.ajaxCounter; + counter = ++p.ajaxCounter; - p.last.ajaxUrl = url; // remember processed url - p.ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl - p.ajaxObject.success = function(data, status, jqxhr) { - // Refuse to process old ajax commands that were overwritten by new ones - see #443 - if (counter < p.ajaxCounter){ - return; - } - renderAjax(data, table, p, jqxhr); - $doc.unbind('ajaxError' + namespace); - if (typeof p.oldAjaxSuccess === 'function') { - p.oldAjaxSuccess(data); + p.last.ajaxUrl = url; // remember processed url + p.ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl + p.ajaxObject.success = function(data, status, jqxhr) { + // Refuse to process old ajax commands that were overwritten by new ones - see #443 + if (counter < p.ajaxCounter){ + return; + } + renderAjax(data, table, p, jqxhr); + $doc.unbind('ajaxError' + namespace); + if (typeof p.oldAjaxSuccess === 'function') { + p.oldAjaxSuccess(data); + } + }; + if (c.debug) { + console.log('Pager: Ajax initialized', p.ajaxObject); } - }; - if (c.debug) { - console.log('Pager: Ajax initialized', p.ajaxObject); + $.ajax(p.ajaxObject); } - $.ajax(p.ajaxObject); - } - }, + }, - getAjaxUrl = function(table, p) { - var indx, len, + getAjaxUrl = function(table, p) { + var indx, len, c = table.config, url = (p.ajaxUrl) ? p.ajaxUrl // allow using "{page+1}" in the url string to switch to a non-zero based index .replace(/\{page([\-+]\d+)?\}/, function(s, n){ return p.page + (n ? parseInt(n, 10) : 0); }) // this will pass "all" to server when size is set to "all" .replace(/\{size\}/g, p.size) : '', - sortList = c.sortList, - filterList = p.currentFilters || $(table).data('lastSearch') || [], - sortCol = url.match(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/), - filterCol = url.match(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/), - arry = []; - if (sortCol) { - sortCol = sortCol[1]; - len = sortList.length; - for (indx = 0; indx < len; indx++) { - arry[ arry.length ] = sortCol + '[' + sortList[indx][0] + ']=' + sortList[indx][1]; - } - // if the arry is empty, just add the col parameter... "&{sortList:col}" becomes "&col" - url = url.replace(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : sortCol ); + sortList = c.sortList, + filterList = p.currentFilters || $(table).data('lastSearch') || [], + sortCol = url.match(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/), + filterCol = url.match(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/), arry = []; - } - if (filterCol) { - filterCol = filterCol[1]; - len = filterList.length; - for (indx = 0; indx < len; indx++) { - if (filterList[indx]) { - arry[ arry.length ] = filterCol + '[' + indx + ']=' + encodeURIComponent( filterList[indx] ); + if (sortCol) { + sortCol = sortCol[1]; + len = sortList.length; + for (indx = 0; indx < len; indx++) { + arry[ arry.length ] = sortCol + '[' + sortList[indx][0] + ']=' + sortList[indx][1]; } + // if the arry is empty, just add the col parameter... "&{sortList:col}" becomes "&col" + url = url.replace(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : sortCol ); + arry = []; } - // if the arry is empty, just add the fcol parameter... "&{filterList:fcol}" becomes "&fcol" - url = url.replace(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : filterCol ); - p.currentFilters = filterList; - } - if ( typeof p.customAjaxUrl === 'function' ) { - url = p.customAjaxUrl(table, url); - } - if (c.debug) { - console.log('Pager: Ajax url = ' + url); - } - return url; - }, + if (filterCol) { + filterCol = filterCol[1]; + len = filterList.length; + for (indx = 0; indx < len; indx++) { + if (filterList[indx]) { + arry[ arry.length ] = filterCol + '[' + indx + ']=' + encodeURIComponent( filterList[indx] ); + } + } + // if the arry is empty, just add the fcol parameter... "&{filterList:fcol}" becomes "&fcol" + url = url.replace(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : filterCol ); + p.currentFilters = filterList; + } + if ( typeof p.customAjaxUrl === 'function' ) { + url = p.customAjaxUrl(table, url); + } + if (c.debug) { + console.log('Pager: Ajax url = ' + url); + } + return url; + }, - renderTable = function(table, rows, p) { - var $tb, index, count, added, + renderTable = function(table, rows, p) { + var $tb, index, count, added, $t = $(table), c = table.config, f = c.$table.hasClass('hasFilters'), l = rows && rows.length || 0, // rows may be undefined e = p.size === 'all' ? p.totalRows : p.size, s = ( p.page * e ); - if ( l < 1 ) { - if (c.debug) { - console.warn('Pager: >> No rows for pager to render'); + if ( l < 1 ) { + if (c.debug) { + console.warn('Pager: >> No rows for pager to render'); + } + // empty table, abort! + return; } - // empty table, abort! - return; - } - if ( p.page >= p.totalPages ) { - // lets not render the table more than once - moveToLastPage(table, p); - } - p.cacheIndex = []; - p.isDisabled = false; // needed because sorting will change the page and re-enable the pager - if (p.initialized) { - if (c.debug) { - console.log('Pager: Triggering pagerChange'); + if ( p.page >= p.totalPages ) { + // lets not render the table more than once + moveToLastPage(table, p); } - $t.triggerHandler( 'pagerChange', p ); - } - if ( !p.removeRows ) { - hideRows(table, p); - } else { - ts.clearTableBody(table); - $tb = ts.processTbody(table, c.$tbodies.eq(0), true); - // not filtered, start from the calculated starting point (s) - // if filtered, start from zero - index = f ? 0 : s; - count = f ? 0 : s; - added = 0; - while (added < e && index < rows.length) { - if (!f || !/filtered/.test(rows[index][0].className)){ - count++; - if (count > s && added <= e) { - added++; - p.cacheIndex[ p.cacheIndex.length ] = index; - $tb.append(rows[index]); + p.cacheIndex = []; + p.isDisabled = false; // needed because sorting will change the page and re-enable the pager + if (p.initialized) { + if (c.debug) { + console.log('Pager: Triggering pagerChange'); + } + $t.triggerHandler( 'pagerChange', p ); + } + if ( !p.removeRows ) { + hideRows(table, p); + } else { + ts.clearTableBody(table); + $tb = ts.processTbody(table, c.$tbodies.eq(0), true); + // not filtered, start from the calculated starting point (s) + // if filtered, start from zero + index = f ? 0 : s; + count = f ? 0 : s; + added = 0; + while (added < e && index < rows.length) { + if (!f || !/filtered/.test(rows[index][0].className)){ + count++; + if (count > s && added <= e) { + added++; + p.cacheIndex[ p.cacheIndex.length ] = index; + $tb.append(rows[index]); + } } + index++; } - index++; + ts.processTbody(table, $tb, false); } - ts.processTbody(table, $tb, false); - } - updatePageDisplay(table, p); - if (table.isUpdating) { - if (c.debug) { - console.log('Pager: Triggering updateComplete'); + updatePageDisplay(table, p); + if (table.isUpdating) { + if (c.debug) { + console.log('Pager: Triggering updateComplete'); + } + $t.triggerHandler('updateComplete', [ table, true ]); } - $t.triggerHandler('updateComplete', [ table, true ]); - } - }, + }, - showAllRows = function(table, p) { - var index, $controls, len; - if ( p.ajax ) { - pagerArrows( table, p, true ); - } else { - $.data(table, 'pagerLastPage', p.page); - $.data(table, 'pagerLastSize', p.size); - p.page = 0; - p.size = 'all'; - p.totalPages = 1; - $(table) + showAllRows = function(table, p) { + var index, $controls, len; + if ( p.ajax ) { + pagerArrows( table, p, true ); + } else { + $.data(table, 'pagerLastPage', p.page); + $.data(table, 'pagerLastSize', p.size); + p.page = 0; + p.size = 'all'; + p.totalPages = 1; + $(table) .addClass('pagerDisabled') .removeAttr('aria-describedby') .find('tr.pagerSavedHeightSpacer').remove(); - renderTable(table, table.config.rowsCopy, p); - p.isDisabled = true; - ts.applyWidget( table ); - if (table.config.debug) { - console.log('Pager: Disabled'); + renderTable(table, table.config.rowsCopy, p); + p.isDisabled = true; + ts.applyWidget( table ); + if (table.config.debug) { + console.log('Pager: Disabled'); + } } - } - // disable size selector - $controls = p.$size + // disable size selector + $controls = p.$size .add( p.$goto ) .add( p.$container.find( '.ts-startRow, .ts-page' ) ); - len = $controls.length; - for ( index = 0; index < len; index++ ) { - $controls.eq( index ) + len = $controls.length; + for ( index = 0; index < len; index++ ) { + $controls.eq( index ) .attr( 'aria-disabled', 'true' ) .addClass( p.cssDisabled )[0].disabled = true; - } - }, + } + }, - // updateCache if delayInit: true - updateCache = function(table) { - var c = table.config, + // updateCache if delayInit: true + updateCache = function(table) { + var c = table.config, p = c.pager; - // tablesorter core updateCache (not pager) - ts.updateCache( c, function(){ - var i, + // tablesorter core updateCache (not pager) + ts.updateCache( c, function(){ + var i, rows = [], n = table.config.cache[0].normalized; - p.totalRows = n.length; - for (i = 0; i < p.totalRows; i++) { - rows[ rows.length ] = n[i][c.columns].$row; - } - c.rowsCopy = rows; - moveToPage(table, p, true); - }); - }, - - moveToPage = function(table, p, pageMoved) { - if ( p.isDisabled ) { return; } - var tmp, + p.totalRows = n.length; + for (i = 0; i < p.totalRows; i++) { + rows[ rows.length ] = n[i][c.columns].$row; + } + c.rowsCopy = rows; + moveToPage(table, p, true); + }); + }, + + moveToPage = function(table, p, pageMoved) { + if ( p.isDisabled ) { return; } + var tmp, c = table.config, $t = $(table), l = p.last; - if ( pageMoved !== false && p.initialized && ts.isEmptyObject(c.cache)) { - return updateCache(table); - } - // abort page move if the table has filters and has not been initialized - if (p.ajax && ts.hasWidget(table, 'filter') && !c.widgetOptions.filter_initialized) { return; } - parsePageNumber( table, p ); - calcFilters(table, p); - // fixes issue where one currentFilter is [] and the other is ['','',''], - // making the next if comparison think the filters are different (joined by commas). Fixes #202. - l.currentFilters = (l.currentFilters || []).join('') === '' ? [] : l.currentFilters; - p.currentFilters = (p.currentFilters || []).join('') === '' ? [] : p.currentFilters; - // don't allow rendering multiple times on the same page/size/totalRows/filters/sorts - if ( l.page === p.page && l.size === p.size && l.totalRows === p.totalRows && + if ( pageMoved !== false && p.initialized && ts.isEmptyObject(c.cache)) { + return updateCache(table); + } + // abort page move if the table has filters and has not been initialized + if (p.ajax && ts.hasWidget(table, 'filter') && !c.widgetOptions.filter_initialized) { return; } + parsePageNumber( table, p ); + calcFilters(table, p); + // fixes issue where one currentFilter is [] and the other is ['','',''], + // making the next if comparison think the filters are different (joined by commas). Fixes #202. + l.currentFilters = (l.currentFilters || []).join('') === '' ? [] : l.currentFilters; + p.currentFilters = (p.currentFilters || []).join('') === '' ? [] : p.currentFilters; + // don't allow rendering multiple times on the same page/size/totalRows/filters/sorts + if ( l.page === p.page && l.size === p.size && l.totalRows === p.totalRows && (l.currentFilters || []).join(',') === (p.currentFilters || []).join(',') && // check for ajax url changes see #730 (l.ajaxUrl || '') === (p.ajaxObject.url || '') && // & ajax url option changes (dynamically add/remove/rename sort & filter parameters) (l.optAjaxUrl || '') === (p.ajaxUrl || '') && l.sortList === (c.sortList || []).join(',') ) { return; } - if (c.debug) { - console.log('Pager: Changing to page ' + p.page); - } - p.last = { - page : p.page, - size : p.size, - // fixes #408; modify sortList otherwise it auto-updates - sortList : (c.sortList || []).join(','), - totalRows : p.totalRows, - currentFilters : p.currentFilters || [], - ajaxUrl : p.ajaxObject.url || '', - optAjaxUrl : p.ajaxUrl || '' - }; - if (p.ajax) { - if ( !p.processAjaxOnInit && !ts.isEmptyObject(p.initialRows) ) { - p.processAjaxOnInit = true; - tmp = p.initialRows; - p.totalRows = typeof tmp.total !== 'undefined' ? tmp.total : + if (c.debug) { + console.log('Pager: Changing to page ' + p.page); + } + p.last = { + page : p.page, + size : p.size, + // fixes #408; modify sortList otherwise it auto-updates + sortList : (c.sortList || []).join(','), + totalRows : p.totalRows, + currentFilters : p.currentFilters || [], + ajaxUrl : p.ajaxObject.url || '', + optAjaxUrl : p.ajaxUrl || '' + }; + if (p.ajax) { + if ( !p.processAjaxOnInit && !ts.isEmptyObject(p.initialRows) ) { + p.processAjaxOnInit = true; + tmp = p.initialRows; + p.totalRows = typeof tmp.total !== 'undefined' ? tmp.total : ( c.debug ? console.error('Pager: no initial total page set!') || 0 : 0 ); - p.filteredRows = typeof tmp.filtered !== 'undefined' ? tmp.filtered : + p.filteredRows = typeof tmp.filtered !== 'undefined' ? tmp.filtered : ( c.debug ? console.error('Pager: no initial filtered page set!') || 0 : 0 ); - pagerInitialized( table, p ); - } else { - getAjax(table, p); - } - } else if (!p.ajax) { - renderTable(table, c.rowsCopy, p); - } - $.data(table, 'pagerLastPage', p.page); - if (p.initialized && pageMoved !== false) { - if (c.debug) { - console.log('Pager: Triggering pageMoved'); + pagerInitialized( table, p ); + } else { + getAjax(table, p); + } + } else if (!p.ajax) { + renderTable(table, c.rowsCopy, p); } - $t.triggerHandler('pageMoved', p); - ts.applyWidget( table ); - if (table.isUpdating) { + $.data(table, 'pagerLastPage', p.page); + if (p.initialized && pageMoved !== false) { if (c.debug) { - console.log('Pager: Triggering updateComplete'); + console.log('Pager: Triggering pageMoved'); + } + $t.triggerHandler('pageMoved', p); + ts.applyWidget( table ); + if (table.isUpdating) { + if (c.debug) { + console.log('Pager: Triggering updateComplete'); + } + $t.triggerHandler('updateComplete', [ table, true ]); } - $t.triggerHandler('updateComplete', [ table, true ]); } - } - }, + }, - getTotalPages = function( table, p ) { - return ts.hasWidget( table, 'filter' ) ? Math.min( p.totalPages, p.filteredPages ) : p.totalPages; - }, + getTotalPages = function( table, p ) { + return ts.hasWidget( table, 'filter' ) ? Math.min( p.totalPages, p.filteredPages ) : p.totalPages; + }, - // set to either set or get value - parsePageSize = function( p, size, mode ) { - var s = parseInt( size, 10 ) || p.size || p.settings.size || 10; - return /all/i.test( size ) || s === p.totalRows ? + // set to either set or get value + parsePageSize = function( p, size, mode ) { + var s = parseInt( size, 10 ) || p.size || p.settings.size || 10; + return /all/i.test( size ) || s === p.totalRows ? // "get" to get `p.size` or "set" to set `p.$size.val()` 'all' : ( mode === 'get' ? s : p.size ); - }, - - parsePageNumber = function( table, p ) { - var min = getTotalPages( table, p ) - 1; - p.page = parseInt( p.page, 10 ); - if ( p.page < 0 || isNaN( p.page ) ) { p.page = 0; } - if ( p.page > min && min >= 0 ) { p.page = min; } - return p.page; - }, - - setPageSize = function(table, size, p) { - p.size = parsePageSize( p, size, 'get' ); - p.$size.val( parsePageSize( p, p.size, 'set' ) ); - $.data(table, 'pagerLastPage', parsePageNumber( table, p ) ); - $.data(table, 'pagerLastSize', p.size); - p.totalPages = p.size === 'all' ? 1 : Math.ceil( p.totalRows / p.size ); - p.filteredPages = p.size === 'all' ? 1 : Math.ceil( p.filteredRows / p.size ); - moveToPage(table, p); - }, - - moveToFirstPage = function(table, p) { - p.page = 0; - moveToPage(table, p); - }, - - moveToLastPage = function(table, p) { - p.page = getTotalPages( table, p ) - 1; - moveToPage(table, p); - }, - - moveToNextPage = function(table, p) { - p.page++; - var last = getTotalPages( table, p ) - 1; - if ( p.page >= last ) { - p.page = last; - } - moveToPage(table, p); - }, + }, + + parsePageNumber = function( table, p ) { + var min = getTotalPages( table, p ) - 1; + p.page = parseInt( p.page, 10 ); + if ( p.page < 0 || isNaN( p.page ) ) { p.page = 0; } + if ( p.page > min && min >= 0 ) { p.page = min; } + return p.page; + }, + + setPageSize = function(table, size, p) { + p.size = parsePageSize( p, size, 'get' ); + p.$size.val( parsePageSize( p, p.size, 'set' ) ); + $.data(table, 'pagerLastPage', parsePageNumber( table, p ) ); + $.data(table, 'pagerLastSize', p.size); + p.totalPages = p.size === 'all' ? 1 : Math.ceil( p.totalRows / p.size ); + p.filteredPages = p.size === 'all' ? 1 : Math.ceil( p.filteredRows / p.size ); + moveToPage(table, p); + }, - moveToPrevPage = function(table, p) { - p.page--; - if ( p.page <= 0 ) { + moveToFirstPage = function(table, p) { p.page = 0; - } - moveToPage(table, p); - }, - - pagerInitialized = function(table, p) { - p.initialized = true; - p.initializing = false; - if (table.config.debug) { - console.log('Pager: Triggering pagerInitialized'); - } - $(table).triggerHandler( 'pagerInitialized', p ); - ts.applyWidget( table ); - updatePageDisplay(table, p); - }, + moveToPage(table, p); + }, + + moveToLastPage = function(table, p) { + p.page = getTotalPages( table, p ) - 1; + moveToPage(table, p); + }, - destroyPager = function(table, p) { - var c = table.config, + moveToNextPage = function(table, p) { + p.page++; + var last = getTotalPages( table, p ) - 1; + if ( p.page >= last ) { + p.page = last; + } + moveToPage(table, p); + }, + + moveToPrevPage = function(table, p) { + p.page--; + if ( p.page <= 0 ) { + p.page = 0; + } + moveToPage(table, p); + }, + + pagerInitialized = function(table, p) { + p.initialized = true; + p.initializing = false; + if (table.config.debug) { + console.log('Pager: Triggering pagerInitialized'); + } + $(table).triggerHandler( 'pagerInitialized', p ); + ts.applyWidget( table ); + updatePageDisplay(table, p); + }, + + destroyPager = function(table, p) { + var c = table.config, namespace = c.namespace + 'pager', ctrls = [ p.cssFirst, p.cssPrev, p.cssNext, p.cssLast, p.cssGoto, p.cssPageSize ].join( ',' ); - showAllRows(table, p); - p.$container + showAllRows(table, p); + p.$container // hide pager controls .hide() // unbind .find( ctrls ) .unbind( namespace ); - c.appender = null; // remove pager appender function - c.$table.unbind( namespace ); - if (ts.storage) { - ts.storage(table, p.storageKey, ''); - } - delete c.pager; - delete c.rowsCopy; - }, + c.appender = null; // remove pager appender function + c.$table.unbind( namespace ); + if (ts.storage) { + ts.storage(table, p.storageKey, ''); + } + delete c.pager; + delete c.rowsCopy; + }, - enablePager = function(table, p, triggered) { - var info, size, + enablePager = function(table, p, triggered) { + var info, size, c = table.config; - p.$size.add(p.$goto).add(p.$container.find('.ts-startRow, .ts-page')) + p.$size.add(p.$goto).add(p.$container.find('.ts-startRow, .ts-page')) .removeClass(p.cssDisabled) .removeAttr('disabled') .attr('aria-disabled', 'false'); - p.isDisabled = false; - p.page = $.data(table, 'pagerLastPage') || p.page || 0; - size = p.$size.find('option[selected]').val(); - p.size = $.data(table, 'pagerLastSize') || parsePageSize( p, size, 'get' ); - p.$size.val( p.size ); // set page size - p.totalPages = p.size === 'all' ? 1 : Math.ceil( getTotalPages( table, p ) / p.size ); - // if table id exists, include page display with aria info - if ( table.id ) { - info = table.id + '_pager_info'; - p.$container.find(p.cssPageDisplay).attr('id', info); - c.$table.attr('aria-describedby', info); - } - changeHeight(table, p); - if ( triggered ) { - // tablesorter core update table - ts.update( c ); - setPageSize(table, p.size, p); - hideRowsSetup(table, p); - if (c.debug) { - console.log('Pager: Enabled'); + p.isDisabled = false; + p.page = $.data(table, 'pagerLastPage') || p.page || 0; + size = p.$size.find('option[selected]').val(); + p.size = $.data(table, 'pagerLastSize') || parsePageSize( p, size, 'get' ); + p.$size.val( p.size ); // set page size + p.totalPages = p.size === 'all' ? 1 : Math.ceil( getTotalPages( table, p ) / p.size ); + // if table id exists, include page display with aria info + if ( table.id ) { + info = table.id + '_pager_info'; + p.$container.find(p.cssPageDisplay).attr('id', info); + c.$table.attr('aria-describedby', info); } - } - }; + changeHeight(table, p); + if ( triggered ) { + // tablesorter core update table + ts.update( c ); + setPageSize(table, p.size, p); + hideRowsSetup(table, p); + if (c.debug) { + console.log('Pager: Enabled'); + } + } + }; - $this.appender = function(table, rows) { - var c = table.config, + $this.appender = function(table, rows) { + var c = table.config, p = c.pager; - if ( !p.ajax ) { - c.rowsCopy = rows; - p.totalRows = p.countChildRows ? c.$tbodies.eq(0).children('tr').length : rows.length; - p.size = $.data(table, 'pagerLastSize') || p.size || p.settings.size || 10; - p.totalPages = p.size === 'all' ? 1 : Math.ceil( p.totalRows / p.size ); - renderTable(table, rows, p); - // update display here in case all rows are removed - updatePageDisplay(table, p, false); - } - }; + if ( !p.ajax ) { + c.rowsCopy = rows; + p.totalRows = p.countChildRows ? c.$tbodies.eq(0).children('tr').length : rows.length; + p.size = $.data(table, 'pagerLastSize') || p.size || p.settings.size || 10; + p.totalPages = p.size === 'all' ? 1 : Math.ceil( p.totalRows / p.size ); + renderTable(table, rows, p); + // update display here in case all rows are removed + updatePageDisplay(table, p, false); + } + }; - $this.construct = function(settings) { - return this.each(function() { - // check if tablesorter has initialized - if (!(this.config && this.hasInitialized)) { return; } - var t, ctrls, fxn, size, + $this.construct = function(settings) { + return this.each(function() { + // check if tablesorter has initialized + if (!(this.config && this.hasInitialized)) { return; } + var t, ctrls, fxn, size, table = this, c = table.config, wo = c.widgetOptions, @@ -922,25 +923,25 @@ namespace = c.namespace + 'pager', // added in case the pager is reinitialized after being destroyed. pager = p.$container = $(p.container).addClass('tablesorter-pager').show(); - // save a copy of the original settings - p.settings = $.extend( true, {}, $.tablesorterPager.defaults, settings ); - if (c.debug) { - console.log('Pager: Initializing'); - } - p.oldAjaxSuccess = p.oldAjaxSuccess || p.ajaxObject.success; - c.appender = $this.appender; - p.initializing = true; - if (p.savePages && ts.storage) { - t = ts.storage(table, p.storageKey) || {}; // fixes #387 - p.page = isNaN(t.page) ? p.page : t.page; - p.size = t.size === 'all' ? t.size : ( isNaN( t.size ) ? p.size : t.size ) || p.setSize || 10; - $.data(table, 'pagerLastSize', p.size); - pager.find(p.cssPageSize).val(p.size); - } - // skipped rows - p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.slice(1) + '|' + c.cssChildRow + ')'); + // save a copy of the original settings + p.settings = $.extend( true, {}, $.tablesorterPager.defaults, settings ); + if (c.debug) { + console.log('Pager: Initializing'); + } + p.oldAjaxSuccess = p.oldAjaxSuccess || p.ajaxObject.success; + c.appender = $this.appender; + p.initializing = true; + if (p.savePages && ts.storage) { + t = ts.storage(table, p.storageKey) || {}; // fixes #387 + p.page = isNaN(t.page) ? p.page : t.page; + p.size = t.size === 'all' ? t.size : ( isNaN( t.size ) ? p.size : t.size ) || p.setSize || 10; + $.data(table, 'pagerLastSize', p.size); + pager.find(p.cssPageSize).val(p.size); + } + // skipped rows + p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.slice(1) + '|' + c.cssChildRow + ')'); - $t + $t // .unbind( namespace ) adding in jQuery 1.4.3 ( I think ) .unbind( pagerEvents.split(' ').join(namespace + ' ').replace(/\s+/g, ' ') ) .bind('filterInit filterStart '.split(' ').join(namespace + ' '), function(e, filters) { @@ -1020,13 +1021,13 @@ updatePageDisplay(table, p, false); }); - // clicked controls - ctrls = [ p.cssFirst, p.cssPrev, p.cssNext, p.cssLast ]; - fxn = [ moveToFirstPage, moveToPrevPage, moveToNextPage, moveToLastPage ]; - if (c.debug && !pager.length) { - console.warn('Pager: >> Container not found'); - } - pager.find(ctrls.join(',')) + // clicked controls + ctrls = [ p.cssFirst, p.cssPrev, p.cssNext, p.cssLast ]; + fxn = [ moveToFirstPage, moveToPrevPage, moveToNextPage, moveToLastPage ]; + if (c.debug && !pager.length) { + console.warn('Pager: >> Container not found'); + } + pager.find(ctrls.join(',')) .attr('tabindex', 0) .unbind('click' + namespace) .bind('click' + namespace, function(e){ @@ -1042,94 +1043,95 @@ } }); - // goto selector - p.$goto = pager.find(p.cssGoto); - if ( p.$goto.length ) { - p.$goto + // goto selector + p.$goto = pager.find(p.cssGoto); + if ( p.$goto.length ) { + p.$goto .unbind('change' + namespace) .bind('change' + namespace, function(){ p.page = $(this).val() - 1; moveToPage(table, p, true); updatePageDisplay(table, p, false); }); - } else if (c.debug) { - console.warn('Pager: >> Goto selector not found'); - } - // page size selector - p.$size = pager.find(p.cssPageSize); - if ( p.$size.length ) { - // setting an option as selected appears to cause issues with initial page size - p.$size.find('option').removeAttr('selected'); - p.$size.unbind('change' + namespace).bind('change' + namespace, function() { - if ( !$(this).hasClass(p.cssDisabled) ) { - var size = $(this).val(); - p.$size.val( size ); // in case there are more than one pagers - setPageSize(table, size, p); - changeHeight(table, p); - } - return false; - }); - } else if (c.debug) { - console.warn('Pager: >> Size selector not found'); - } - - // clear initialized flag - p.initialized = false; - // before initialization event - $t.triggerHandler('pagerBeforeInitialized', p); - - enablePager(table, p, false); - if ( typeof p.ajaxUrl === 'string' ) { - // ajax pager; interact with database - p.ajax = true; - // When filtering with ajax, allow only custom filtering function, disable default - // filtering since it will be done server side. - c.widgetOptions.filter_serversideFiltering = true; - c.serverSideSorting = true; - moveToPage(table, p); - } else { - p.ajax = false; - // Regular pager; all rows stored in memory - ts.appendCache( c, true ); // true = don't apply widgets - hideRowsSetup(table, p); - } + } else if (c.debug) { + console.warn('Pager: >> Goto selector not found'); + } + // page size selector + p.$size = pager.find(p.cssPageSize); + if ( p.$size.length ) { + // setting an option as selected appears to cause issues with initial page size + p.$size.find('option').removeAttr('selected'); + p.$size.unbind('change' + namespace).bind('change' + namespace, function() { + if ( !$(this).hasClass(p.cssDisabled) ) { + var size = $(this).val(); + p.$size.val( size ); // in case there are more than one pagers + setPageSize(table, size, p); + changeHeight(table, p); + } + return false; + }); + } else if (c.debug) { + console.warn('Pager: >> Size selector not found'); + } - // pager initialized - if (!p.ajax && !p.initialized) { - p.initializing = false; - p.initialized = true; - moveToPage(table, p); - if (c.debug) { - console.log('Pager: Triggering pagerInitialized'); + // clear initialized flag + p.initialized = false; + // before initialization event + $t.triggerHandler('pagerBeforeInitialized', p); + + enablePager(table, p, false); + if ( typeof p.ajaxUrl === 'string' ) { + // ajax pager; interact with database + p.ajax = true; + // When filtering with ajax, allow only custom filtering function, disable default + // filtering since it will be done server side. + c.widgetOptions.filter_serversideFiltering = true; + c.serverSideSorting = true; + moveToPage(table, p); + } else { + p.ajax = false; + // Regular pager; all rows stored in memory + ts.appendCache( c, true ); // true = don't apply widgets + hideRowsSetup(table, p); } - c.$table.triggerHandler( 'pagerInitialized', p ); - if ( !( c.widgetOptions.filter_initialized && ts.hasWidget(table, 'filter') ) ) { - updatePageDisplay(table, p, false); + + // pager initialized + if (!p.ajax && !p.initialized) { + p.initializing = false; + p.initialized = true; + moveToPage(table, p); + if (c.debug) { + console.log('Pager: Triggering pagerInitialized'); + } + c.$table.triggerHandler( 'pagerInitialized', p ); + if ( !( c.widgetOptions.filter_initialized && ts.hasWidget(table, 'filter') ) ) { + updatePageDisplay(table, p, false); + } } - } - // make the hasWidget function think that the pager widget is being used - c.widgetInit.pager = true; - }); - }; + // make the hasWidget function think that the pager widget is being used + c.widgetInit.pager = true; + }); + }; - }() }); + }() + }); // see #486 ts.showError = function( table, xhr, settings, exception ) { var $row, - $table = $( table ), - c = $table[0].config, - wo = c && c.widgetOptions, - errorRow = c.pager && c.pager.cssErrorRow || - wo && wo.pager_css && wo.pager_css.errorRow || - 'tablesorter-errorRow', - typ = typeof xhr, - valid = true, - message = '', - removeRow = function(){ - c.$table.find( 'thead' ).find( '.' + errorRow ).remove(); - }; + $table = $( table ), + c = $table[0].config, + wo = c && c.widgetOptions, + errorRow = c.pager && c.pager.cssErrorRow || + wo && wo.pager_css && wo.pager_css.errorRow || + 'tablesorter-errorRow', + typ = typeof xhr, + valid = true, + message = '', + removeRow = function(){ + c.$table.find( 'thead' ).find( '.' + errorRow ).remove(); + }; if ( !$table.length ) { console.error('tablesorter showError: no table parameter passed'); @@ -1156,13 +1158,13 @@ if ( message === '' ) { if ( typ === 'object' ) { message = - xhr.status === 0 ? 'Not connected, verify Network' : - xhr.status === 404 ? 'Requested page not found [404]' : - xhr.status === 500 ? 'Internal Server Error [500]' : - exception === 'parsererror' ? 'Requested JSON parse failed' : - exception === 'timeout' ? 'Time out error' : - exception === 'abort' ? 'Ajax Request aborted' : - 'Uncaught error: ' + xhr.statusText + ' [' + xhr.status + ']'; + xhr.status === 0 ? 'Not connected, verify Network' : + xhr.status === 404 ? 'Requested page not found [404]' : + xhr.status === 500 ? 'Internal Server Error [500]' : + exception === 'parsererror' ? 'Requested JSON parse failed' : + exception === 'timeout' ? 'Time out error' : + exception === 'abort' ? 'Ajax Request aborted' : + 'Uncaught error: ' + xhr.statusText + ' [' + xhr.status + ']'; } else if ( typ === 'string' ) { // keep backward compatibility (external usage just passes a message string) message = xhr; @@ -1174,16 +1176,16 @@ // allow message to include entire row HTML! $row = ( /tr\>/.test(message) ? $(message) : $('<tr><td colspan="' + c.columns + '">' + message + '</td></tr>') ) - .click( function() { - $( this ).remove(); - }) - // add error row to thead instead of tbody, or clicking on the header will result in a parser error - .appendTo( c.$table.find( 'thead:first' ) ) - .addClass( errorRow + ' ' + c.selectorRemove.slice(1) ) - .attr({ - role : 'alert', - 'aria-live' : 'assertive' - }); + .click( function() { + $( this ).remove(); + }) + // add error row to thead instead of tbody, or clicking on the header will result in a parser error + .appendTo( c.$table.find( 'thead:first' ) ) + .addClass( errorRow + ' ' + c.selectorRemove.slice(1) ) + .attr({ + role : 'alert', + 'aria-live' : 'assertive' + }); }; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index efbffb9..14fff96 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 05-16-2016 (v2.26.1)*/ +/*! tablesorter (FORK) - updated 06-15-2016 (v2.26.4)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.26.1 *//* +/*! TableSorter (FORK) v2.26.4 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -39,7 +39,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.26.1', + version : '2.26.4', parsers : [], widgets : [], @@ -3104,7 +3104,7 @@ })(jQuery); -/*! Widget: filter - updated 4/29/2016 (v2.25.9) *//* +/*! Widget: filter - updated 5/28/2016 (v2.26.2) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3368,7 +3368,11 @@ return filter === '' ? true : $.trim( filter ) !== data.iExact; } else { indx = data.iExact.search( $.trim( filter ) ); - return filter === '' ? true : !( c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0 ); + return filter === '' ? true : + // return true if not found + data.anyMatch ? indx < 0 : + // return false if found + !( c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0 ); } } return null; @@ -4187,7 +4191,6 @@ // look for multiple columns '1-3,4-6,8' tsf.multipleColumns( c, wo.filter_$anyMatch ) : []; - data.$cells = data.$row.children(); if ( data.anyMatchFlag && columnIndex.length > 1 || data.anyMatchFilter ) { data.anyMatch = true; @@ -4331,7 +4334,7 @@ }, vars = { // anyMatch really screws up with these types of filters - noAnyMatch: [ 'range', 'notMatch', 'operators' ], + noAnyMatch: [ 'range', 'operators' ], // cache filter variables that use ts.getColumnData in the main loop functions : [], excludeFilter : [], @@ -5227,7 +5230,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 11/4/2015 (v2.24.3) */ +/*! Widget: resizable - updated 5/16/2015 (v2.26.1) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 67442db..0af6b68 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.26.1 *//* +/*! TableSorter (FORK) v2.26.4 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -21,7 +21,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.26.1', + version : '2.26.4', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index cb7108e..0ce4ac2 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 05-16-2016 (v2.26.1)*/ +/*! tablesorter (FORK) - updated 06-15-2016 (v2.26.4)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -372,7 +372,7 @@ })(jQuery); -/*! Widget: filter - updated 4/29/2016 (v2.25.9) *//* +/*! Widget: filter - updated 5/28/2016 (v2.26.2) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -636,7 +636,11 @@ return filter === '' ? true : $.trim( filter ) !== data.iExact; } else { indx = data.iExact.search( $.trim( filter ) ); - return filter === '' ? true : !( c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0 ); + return filter === '' ? true : + // return true if not found + data.anyMatch ? indx < 0 : + // return false if found + !( c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0 ); } } return null; @@ -1455,7 +1459,6 @@ // look for multiple columns '1-3,4-6,8' tsf.multipleColumns( c, wo.filter_$anyMatch ) : []; - data.$cells = data.$row.children(); if ( data.anyMatchFlag && columnIndex.length > 1 || data.anyMatchFilter ) { data.anyMatch = true; @@ -1599,7 +1602,7 @@ }, vars = { // anyMatch really screws up with these types of filters - noAnyMatch: [ 'range', 'notMatch', 'operators' ], + noAnyMatch: [ 'range', 'operators' ], // cache filter variables that use ts.getColumnData in the main loop functions : [], excludeFilter : [], @@ -2495,7 +2498,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 11/4/2015 (v2.24.3) */ +/*! Widget: resizable - updated 5/16/2015 (v2.26.1) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js index 8139a58..1889352 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js @@ -1,5 +1,6 @@ -/*! Widget: filter, select2 formatter function - updated 3/26/2015 (v2.21.3) *//* - * requires: jQuery 1.7.2+, tableSorter (FORK) 2.16+, filter widget 2.16+ and select2 v3.4.6+ plugin +/*! Widget: filter, select2 formatter function - updated 5/28/2016 (v2.26.2) *//* + * requires: jQuery 1.7.2+, tableSorter (FORK) 2.16+, filter widget 2.16+ + and select2 v3.4.6+ plugin (this code is NOT compatible with select2 v4+) */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ @@ -74,7 +75,8 @@ arry = ts.filter.getOptionSource(c.$table[0], indx, onlyAvail) || []; // build select2 data option $.each(arry, function(i, v){ - data.push({id: v, text: v}); + // getOptionSource returns { parsed: "value", text: "value" } in v2.24.4 + data.push({ id: '' + v.parsed, text: v.text }); }); o.data = data; }; @@ -118,7 +120,7 @@ // has sticky headers? c.$table.bind('stickyHeadersInit', function(){ - $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); + $shcell = c.widgetOptions.$sticky.find('.' + ts.css.filterRow).children().eq(indx).empty(); // add a select2! $('<input class="select2 select2-' + indx + '" type="hidden">') .val(o.value) diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 2d131ac..8fc3dbf 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 4/29/2016 (v2.25.9) *//* +/*! Widget: filter - updated 5/28/2016 (v2.26.2) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -262,7 +262,11 @@ return filter === '' ? true : $.trim( filter ) !== data.iExact; } else { indx = data.iExact.search( $.trim( filter ) ); - return filter === '' ? true : !( c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0 ); + return filter === '' ? true : + // return true if not found + data.anyMatch ? indx < 0 : + // return false if found + !( c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0 ); } } return null; @@ -1081,7 +1085,6 @@ // look for multiple columns '1-3,4-6,8' tsf.multipleColumns( c, wo.filter_$anyMatch ) : []; - data.$cells = data.$row.children(); if ( data.anyMatchFlag && columnIndex.length > 1 || data.anyMatchFilter ) { data.anyMatch = true; @@ -1225,7 +1228,7 @@ }, vars = { // anyMatch really screws up with these types of filters - noAnyMatch: [ 'range', 'notMatch', 'operators' ], + noAnyMatch: [ 'range', 'operators' ], // cache filter variables that use ts.getColumnData in the main loop functions : [], excludeFilter : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index 44153a9..73563a6 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -1,4 +1,4 @@ -/*! Widget: grouping - updated 3/1/2016 (v2.25.5) *//* +/*! Widget: grouping - updated 5/16/2015 (v2.26.1) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js index f1afdc9..926c08a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js @@ -1,4 +1,4 @@ -/*! Widget: resizable - updated 11/4/2015 (v2.24.3) */ +/*! Widget: resizable - updated 5/16/2015 (v2.26.1) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index 88f4666..bb524d1 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -1,4 +1,4 @@ -/*! Widget: scroller - updated 4/29/2016 (v2.25.9) *//* +/*! Widget: scroller - updated 6/15/2016 (v2.26.3) *//* Copyright (C) 2011 T. Connell & Associates, Inc. Dual-licensed under the MIT and GPL licenses @@ -787,10 +787,11 @@ $fixedColumn.removeClass( tscss.scrollerHideElement ); for ( index = 0; index < fixedColumns; index++ ) { + temp = ':nth-child(' + ( index + 1 ) + ')'; $wrapper .children( 'div' ) .children( 'table' ) - .find( 'th:nth-child(' + ( index + 1 ) + '), td:nth-child(' + ( index + 1 ) + ')' ) + .find( 'th' + temp + ', td' + temp + ', col' + temp ) .addClass( tscss.scrollerHideColumn ); } From 869022b1b59ec349aa8a0fb57e24085e791bd2d7 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Tue, 28 Jun 2016 19:52:41 +0200 Subject: [PATCH 090/138] Update tablesorter to latest version (2.26.3) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/icons/first.png | Bin 720 -> 642 bytes .../addons/pager/icons/last.png | Bin 737 -> 661 bytes .../addons/pager/icons/next.png | Bin 736 -> 658 bytes .../addons/pager/icons/prev.png | Bin 745 -> 663 bytes .../bootstrap-black-unsorted.png | Bin 284 -> 281 bytes .../bootstrap-white-unsorted.png | Bin 223 -> 180 bytes .../jquery-tablesorter/dragtable-handle.png | Bin 103 -> 75 bytes .../dropbox-asc-hovered.png | Bin 275 -> 200 bytes .../images/jquery-tablesorter/dropbox-asc.png | Bin 278 -> 204 bytes .../dropbox-desc-hovered.png | Bin 273 -> 195 bytes .../jquery-tablesorter/dropbox-desc.png | Bin 277 -> 206 bytes .../jquery-tablesorter/metro-black-asc.png | Bin 213 -> 161 bytes .../jquery-tablesorter/metro-black-desc.png | Bin 232 -> 172 bytes .../jquery-tablesorter/metro-unsorted.png | Bin 181 -> 151 bytes .../jquery-tablesorter/metro-white-asc.png | Bin 209 -> 168 bytes .../jquery-tablesorter/metro-white-desc.png | Bin 224 -> 179 bytes .../addons/pager/jquery.tablesorter.pager.js | 66 +++++++++--------- .../jquery.tablesorter.combined.js | 18 ++--- .../jquery-tablesorter/jquery.tablesorter.js | 4 +- .../jquery.tablesorter.widgets.js | 14 ++-- .../widgets/widget-filter.js | 8 ++- .../widgets/widget-grouping.js | 20 +++--- .../widgets/widget-pager.js | 8 +-- .../widgets/widget-resizable.js | 4 +- 28 files changed, 83 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6b1cca..ea1ecdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.21.3 (2016-06-28) + +* Upgrade tablesorter to v2.26.5 + #### v1.21.2 (2016-06-16) * Upgrade tablesorter to v2.26.4 diff --git a/README.md b/README.md index 96c868c..8024355 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.26.4 (6/15/2016), [documentation] +Current tablesorter version: 2.26.5 (6/28/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index d635503..4fc3127 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 21 - TINY = 2 + TINY = 3 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index ed17d4b..eb84f87 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit ed17d4ba8300927e63bacad9238fc8356e8f50c8 +Subproject commit eb84f87d5a99070180354a29f63d6450e8c2bb31 diff --git a/vendor/assets/images/jquery-tablesorter/addons/pager/icons/first.png b/vendor/assets/images/jquery-tablesorter/addons/pager/icons/first.png index 6f11fcb086b1bfa51bc5070ca45fac0a988c624c..7e505d68e3ba4d8aed703db339b33d8a29ff9034 100644 GIT binary patch delta 618 zcmV-w0+s#H1%d^TBYy%(Nkl<ZcmZUR1B_e&5QZD8u`jmWiyPK%!;5R%jrwwN-LB5I zZDUogZQH(`zL~V|^pbx$|2I4U0K0Tl2o<a<S)F24MWWC2TgBe^oyLQ#4!s$Q-yPg^ z@+H5G8%nh{tWasf>pyfGy(?D!%f856jKNqAu)}<~d(Fcrw0~y3FBFGlrU1pQGAWVH zgWDeg@eh^0Y1YhOEP6NqQqio^S(4dY5%9_6OAtvGAj~S2E75PsOdi%{f0Ff61_nI@ zz`n3-%>3W%j^w}-&4DkPN73gYiJybaE{XMzI(&E0i-5|(4F?~~#s@QAs$PK`LiH~5 zHNFa*p)AGdtbbnM^-1{XMZhWXFT)1*jHQMW2v{ZnRx-ev&oOcW*n?S!r;8AXW}vxe zd;`4*sQRN+hjCbF4#x9viCKp00u}J5?D!c)0INSkRpg6X(9zWsMlS(=D^v(TGwI^n zMKTDc9lHQ#U*=yG1!!(<=K!Zx<(iFXG6#uF5&SU(EPs%}7RX>#9KhsF(`JmWDDdjL zxE3nA7EOG+I|Su2;NU3u3Xd|Puqyh^;7&uKRE8y~1{l#KH^hpk-NCzyYchQ}p2Yxe zeMV0jg+}~N2~2GEueELZt0E(<kvk;bu!0$zTTA*PDGH3Hir6o<fvY93(G`I&_1($W zyeT@QePgHmzDvfm^^NHze@tNa#c3wkv&I&N=`{ts=QZhHex2BN${Yl#w;F7BtLvuu zABC+OJb~!%-cFb_^=-$*IY%oeuA7=L^#(>}qBuJP0IWX=5|kGItN;K207*qoM6N<$ Ef+%<@4gdfE delta 697 zcmV;q0!ICU1<(bMBYyw{XF*Lt006JZHwB960000PbVXQnQ*UN;cVTj606}DLVr3vn zZDD6+Qe|Oed2z{QJOBU!Vo5|nR5;6}lS^+BQ547f0sI7f4p_Q$!@|Vwx--6F!p6i! zQ&	anYDUW`HQBO=|;{NKLAi)JR+D2+}kbI#7m(KwIcg7=LgW7#N@&=Ug6wy7MOg zWaj4lf9G-UodSTVaow<iPLn~CQPa#o#=)NM&-iiUHBI^_8<NTM^^6G~CE4TXi5THx z;{IWreY2NgX5AGN#$fCOU|_!P@)xMcNtzeR639v$I#)fST0@0=u>yfZj$hhNQy4ph z3;}vHbMYT?vwzhAT#DL-irfZKQ@zzCd$OX!ekd$Ewh{=@69D#wWn<={IbUf(q1pnW zsuJP%MzA=7Z0`j4M{5GMqeFnf!=5r&HZIIKJ--JJY|#nxJ%)p`a+AcwGZ#R+gA95R zU<`auu-G$}TCO2rmjJMmx0?ATbwB{QQWK;`2QI4$EPt=RXVHs*necH`#9^ViB&je> z?ZSO42h3KEe4`MMDJrB2wQ~h)&UF#J1aK7L2msBfTTgw&VD+9(0MddosG<$aE3ZcY z<ClXNTa{Z-Q#w%miGa_<kiAs;Ee!yviv~HF<arfnhjY||!RbpexqKOVJs^X_;5&3b zBMPg+y?;c$0qt%NcD}`cvVR;6QTNzYGWq-=UX+?7V0e7VLW2m6_>T!vT6W8eRsOC$ z*+0hNMWWwDsLfA)S*X-WV71;M{bCz<v<Oa~SHSFW$#eA9?~62kl^2{LYSq2T2g51I z2@;tJ_8iNK;PppffnMTgEg$~A%Hxme;j64^{xYSPd2U-_X{%jav@qf`vxA*8dzjly f{NIo1f1dL%tUn17r!C&s00000NkvXXu0mjf-X28@ diff --git a/vendor/assets/images/jquery-tablesorter/addons/pager/icons/last.png b/vendor/assets/images/jquery-tablesorter/addons/pager/icons/last.png index 72079357614b4254b163a5cac766093d1f99bbe7..41e248ccc5e15522fee21f1a363de72f7988b3de 100644 GIT binary patch delta 637 zcmV-@0)qYF1(gMmBYy&1Nkl<ZcmZTn1B@JD7`<4HeX;G^xM3aDh-=%8YP+*I+qStp z+csxyz0NyhlDGVxeuMznfrCnfN{~r7CgB3H{#nNVcF%7o?voJqD%Wy5M>j1ubPQ{| zd;7(%o<332I-t#{AD2JRG~VV6XSWM%koTvl=iYSnj}9)ZSbt!1df^KM30NH-%q|+C zZ|xb3tr&g7S#q`ugpu4cFlKYPec<Et_+hkpAxf~j{Qw||dZ20Qv_+K;Q6vl$U@yj| z^9H%Fc?pYV4;D;om@~OCYjkglRMhGRA0#iZPACOrIoc^XWAj<IcyRiI8u?9&o8*)4 z>Tylk3#Q>SFn>M|A=4lymjcK1?L%62W>k_+y;n=9N3LNJGonipr~?I7tX^0gJ}jA> zh)XZhl1qULo!z}67mEWFpd)eivjOtbNt}FBhpUPnFx~Wu3m#tp$*JigxgmV%=<1;W zIKZwXpZTN%ofTzMIPtm$SLAB~lglnCg%@!NNt*)48h@Jmv?iMyR;LeiAepXAG`{GR z1gb#+E-ZIuc*X=FDs{7hEVX7tJ~_LBU=VbW$Tc8&bioON<V!*iSkLPZqPDRQZ&W3l zix~PiZTWVMx@l0fxHfl!#F2RihUXk4ZjwhN_rDQ|gO^pqYumVSkc7OhH-(jI+k(*! zCNbH4V0M~`B`3F!WGT84f1=WCFNM;rgS7o9e@|_ClbBgJXy_cl<bnlU6L#*K5K*S? z!gnEd@cG~3?MHd<Wx5ePar<3lnf#|RK`SU!;_tt5@$;W3?aMzo^8ZVidIKXfQJkFt XZ-oeY*o9o?00000NkvXXu0mjfB?l+s delta 714 zcmV;*0yX`W1>psdBYyw{XF*Lt006JZHwB960000PbVXQnQ*UN;cVTj606}DLVr3vn zZDD6+Qe|Oed2z{QJOBU!b4f%&R5;6}lS^+BQ547f0sI7f4p_Q$!%lo8-5FmoVPoQ= zsVgVd*hOQ6%m7hLo7M&@3DuxNYNSOv1!)=!1<Di<Xo2z?5Pzl&&vKk|fe{}&Z}Q*V zod55<?wtaFsYzWjf<lr?l3LRANb1p@;?MX=;{{2|hwF8%edmPn@5kAL$RQsJNBQl& z82iSPVrFbrEyiH%Z-9~cqSc$HLWfDNP^p6;cA<AZAZj!_kSkWfw-@FXH<A>_jv-@! z63yY*ccInp0e>E`(}$|i1zyrwyHD-}u>;$IpkQ_$Aw)?4*cX<Kna5_wZV8G%I#8$$ zm9MoSS8dCXjZGhSf3<+^C=j5sv&RgUji+4iz||*Sco!(jX3d-j?iwSc8a%PoDbO}Q zgI)xvecz5)>={c%)0M|wNgi1D7a_~HB)}E{R2p5VH-CFjs)?|)_MSyA0;Yop5gvzy z0SLg1sO$57V4NAyP+M@v5GK`~R9evKox`eSjYls5?1#bxfC1Pmny)?GA(;)XW4Qdt z4Y!#gAX9EhRdiu#`LzsCf7y+)HK7d+u}3lxvy>GpN)<&3@R<N`u(+9cwg$A>BDbKj zxD!k!SAT)Q07wQ6<tFB2K_o)+O*%~4>-Gn*`85iZ>7zVE#RF?u>-GjXzTT2IfyTsX z6XN-%yiPPHr0n_^E8Hzpe7MH(L!#e?D90zSP^g}fN@{04Qcb!flJ%iEo~?q;Y!zl{ z=dXt}`IHwd0cyqO;QYY^WD0dM6YM#f=E3C+!9xPQ$j#_K{QZ<CKhwQeY3<xg7xT=x w%+dxE|6I@T^Exv-tFtpVnApER)Bk+upKpZ-dgNcVH2?qr07*qoM6N<$f~y2fR{#J2 diff --git a/vendor/assets/images/jquery-tablesorter/addons/pager/icons/next.png b/vendor/assets/images/jquery-tablesorter/addons/pager/icons/next.png index 4a2f9d4e4a81857f509d85bbe46936c99709cd6f..aebf14d9ef7d8a71607a71ead039b56df9520e7b 100644 GIT binary patch delta 634 zcmV-=0)_qH1(F4jBYy%}Nkl<ZcmZrv1FTzN7+tKzezEQM;)QcGBbsg7wyN2-&2rvq z+wJXL&DXPghCANml;6>h9svG8A2m88bV(Q}VKl+d{Eh$pJ^wqgr-aVs*Yh*386ry4 zCy(A@a|mXuUC_OE>My+vD3@H$&EO1Ye-n_&y)J0u>&y<HD}R~IqgZL6+1&<FDAy57 z<zReobsc}?TgO>)WC5y@Y<2pJ)mjsLD)km}#RdckrCN(WNuqV=-h3_|x$mY(Cn&&P zj7{gCWN)I3)R#Jv;&-@XWyEqdS>(}kqigQj<P35t&~;58Rx&o9Oumj`o3td6jJR0C zkPR<kixd&bR)4|A8Aw2J*`y?w0t1a7-FkLrRFZ~n)Jk{>XRL&%P?ZE;fUt&($d{W) zixr%>ct=kz1xA0i*aR+Cw$K0t=tzv%YJw+T#*lR{Bs|0L%?9v$z1fYk=PwH6eiQg) zwo(9$&#oj7-(mn=DLQ!VGuD#cpYpquqJa~q&d34-Uw^%|>&0RXg-R3bNitnI97t7U zG7Y#ho~Q`Lk@IqeuJg~l%1|teb{hy~s$g7_eaQ-l!E2s^yTXNSwAy(7+KzRad-5W7 zEVz)F@l5;1CFHB}PLRm$WM`7OpfvDCMeILx+BHGrnL)06kQ6t}>ux{R7L&Q}gJP-4 zGzg|EJabHRh9{cCz9Sck%M|zJgY?Hy-g*Aby3?0$yG)<G2qp7e6Ly9@DBywCjGc<( zt|j~L%18OXmuc$J;EWB2A1HULPw5pZjj(sWQrNohh<@3=Ys#@o=htCmCW^B&0OLLg UU)@(lasU7T07*qoM6N<$f>e||djJ3c delta 713 zcmV;)0yh1U1>gmcBYyw{XF*Lt006JZHwB960000PbVXQnQ*UN;cVTj606}DLVr3vn zZDD6+Qe|Oed2z{QJOBU!a!Eu%R5;6}Q%i3WQ55bE@E7nqVCm8gyH;z`orxP0HYP5b zy0Me`=%O(~W-L)mo7M&@37XW}rlwS!4j`tnP@oJCf$}I627ep|hR0Bjb1o0@vGXS1 z;@+I^JLfy++z9}f7{|3`(5TR<(5sjkKtFiX{24z^yrM#T|E*|nxHF9RIL4iXPK7`) zEF2z1xHqmOGi$FHFa~3P6Bv-M+Soi5I8E?{vIvsg0p+qsP;0aySFC{ND9EquB`Ay? ziVOv`NDfB6OMlIl0(j(h7b;Q*1QqpGm%K@G8xDPb$+8ng5iJowUsyI~9+Knd4JiI- zL!sJ7G}nM!r8N@SJMi##HwtJ+g917ScgA4ZcuHa$ZaiWM3Ca0t3#Jxa5Xwl9DK|+> zBzX?B-OC^s1@xY8Q4T$0sYts1kX7M=NUjc9p{WXdB7X>;Ngy^9xTwmoy7`_%E(*-} zPeKAVt6b|40nCWFx#)#hz5!E|DnL15`mvkb+sY+uST_aa5`p7DkO*M;=oQJ+PuwIc zicZcuiLJ@`eR3BY9avp^JrdA={u<`0QVVLbLVOZ3)h8#5@`#KHa5AxM6=;ifWI<<j z#+h`k41c{IK)Tc<<svyzkO?Lq?31ZrV>{g*9DE4_W&SW~qUN5hWN@-RUJ#q3Ngxr& ziJp;+4aEd;HM{A>I)B?7>yL4`Nc6h^we^ve3YC6fwXTqAQWr8=Cd?emR={YnNptkh zuSFVP<t3|+TDNcUUVj|Y1(EawJ%>{Q?6Lt^qC!{rS=094t33Xh?z~DF7GCZ$PnXv? v`ng$nW@3azqlKF@I+$Bb<lmp^f4=ii<30#qlV^0>00000NkvXXu0mjfBtJS( diff --git a/vendor/assets/images/jquery-tablesorter/addons/pager/icons/prev.png b/vendor/assets/images/jquery-tablesorter/addons/pager/icons/prev.png index 15d1584bdbb2c26a9fc4c8269aa54615a58a4657..7d1d049e9c0cf58d2a270cdb1d2afa846debc10d 100644 GIT binary patch delta 639 zcmV-_0)YML1(yYoBYy&3Nkl<ZcmZsa1B{#j5QQ77u`jlL7dNbJN40IAZQHhO+qU_y zt=In*=SkjjzBky}0|0jDNHDBK*@!wR>ITKv{EXl2&fg9m6lMJ-TmCewX+E%}XS}h0 za9HRY7!n#fM;g;=rvpExOP(@^x&OKC@S&9YCELE?$<dV!Ie(NI1B_-1D1};&#Z?KK zI|oLi%O-7^OJ=))HQ|AgDWz6t1Vf`YL83H35T(+Yz)))RXlUzIMih_Pk^v1k7u#m# z|M0q8kNFKPR-}3gOHv)CS2dd^W!252uM$_DlBI!7R^wbC+ujb}@aWtDUw3ck=U|LX zFC!qmC6Fu)PJcFck2P{;wmQ46#U+P&8embP!{mwvXTKKEfZsu`HeyYxK}>2vBUu{U z=<OdAxU5x~9vAIvMAd=-E1&t2FRB37+}gnR>_TreBQZHuAp6Ilr*D7;*iaa7)2kO3 zepP}7G`!-}i1R;6an+@T-TdpqfS9<1O@otl?L&=HrGE|zjS&liIKO9)I6^mO)-(iY zCaMz?%WCkK8DEehAR>8l1e@fliNM*#4OlDy0k(PDrw^=1vGrTty%<|m!(g(Yx^)P) zAw`=@w0fJe{<NyTZB&qJ!63xytT?`;20=cvz{G+Seqr&WcSFXXlHEL{fSf+t{PKF` zibMq_N_bU9up1ijm{@syK?2{16y@iDVp}qMPT3>5%{D$gf3&4{0<$Y}a3`F3XhuLu zeIGmmqDMda6>gsLzJI1ikuy*2!%G6ag5w$kf<gozzd*soH=^;gZ`SWkO_+KEBQsH) ZodL{*2|V#tF*E=G002ovPDHLkV1lH3DoOwV delta 722 zcmV;@0xkWQ1?dHlBYyw{XF*Lt006JZHwB960000PbVXQnQ*UN;cVTj606}DLVr3vn zZDD6+Qe|Oed2z{QJOBU!dr3q=R5;6}lTB|DQ51&z1N;U24p_Q$L!$8`YEc&^ZcNyi zxM*4vBdIZs#aaksiDKHcHc&~>1hlor)=Ec^h7>5J3?Bj(D1WpJ2m=GdP>yphA5wMa zP2NoA&Uv46&Yg3I0AOfPXJ$YqNg+uoN!69IyC?fIe$aSIl6>q<S#A5ePq}?jdS`c! z4Fp5%rZ-H#-bhd*)}k6?F!ltXYd&ppW;KDmIFl=sf#aJX9=C}qH32gDBDlOkW_C5M z!Pp+82auyV7=QlG)f*z<;)NCzxhAlZs*M)e<9Go!J$}x#96^Yj0I)AC8#DLJM`Z!_ z3w6kq1fp!I0f{`{hpeu<m^+I(Y)6Iwg^m6}VcCObmDYR0JUqEY_Hl(12Vk(eDfA*h z>DrFa*fW-zI;z8^hfV^>6dMrF@o?^*g8=YbxhBG4iGPQ=B?paO1gQKwyDT17{-*#J z$3IKjAO_5QcFZox0aqV;$U9ach{v#KUSiQp0KPzw01A~RT$}R4g$JJq0D+eu(Qy9$ z2e>lfCe?JRXu{n5t3E*aWh+FNxCT^s5z?&G{}Z3o!ZsuiI|hUePkJGCP$PdPnl6FC zWbU`1Fn_Pbs8pr^?KThtmU&}3Kr*7R^y^Rk5Xsb_*=oc3w-9KI@B4=+yK6a8uQ@#o zTkf1PnlL+NjAm=Zh)+xqld|idFEF=^(avw|4U*w)Kx6;t<Z{I-2`p7bQVoJ|m@soR zT?C!U!i^f1e-F~&C~M6g&4Sg=xcxCm<;vttuuJDql7$s#0JMf#W<>w)?@=DSOt)Vq z)#EQ#sHvHG+VITCPU|UlLT92!bvEiI75?{S`k(9k3(SNGJn76jE&u=k07*qoM6N<$ Eg0a^}6951J diff --git a/vendor/assets/images/jquery-tablesorter/bootstrap-black-unsorted.png b/vendor/assets/images/jquery-tablesorter/bootstrap-black-unsorted.png index 4e161a65ec0d89596124ebd5eeb18014e900fc5f..433ee0226295ccb6d6bedee2765808c0eec4163d 100644 GIT binary patch delta 265 zcmV+k0rvi!0+|Ak8Gi-<008-6=9K^d0N_bPK~#7FwUD(z!~hhA@fqSth11~fO{BqL zHCCXaqsHB#$_DAt(=TrA-6i?;MP6I_CyHWw^HgP7J_6BERwzo6G^lCX*~rL99Yo`o z6^9JNIH5Q;HdY7G_~hxbm@ZCFPgm0e<Z)S5)!V4Jy1M%5d4HY-qVdV&^7i)j@%Z?7 z-Eo``*L7F$Kr}u%UW<RSEFbpu_4(^!zjz=TpFA$}F}z!}U%nnh<CDkb=jZ47gM))R zf^c2mAR3=ME>|j*o|~JS^=N>VfoObkdWC__&CPepU|F#+U|H6AS+N0%B95#wsl0#y P0000<MNUMnLIPld{n>?_ delta 268 zcmbQqG>2(|WIZzj1H-xfV#|S4NPtg>E0A__a*B+Ml$4Y_dGe%!f&!2M1b_bgk&}~? zm6eqOgOHFAX=&*T7cShnbH~TWCnzWg$bJ3#wS|R+mzNh%m#(g^iHV7^v9Y?kdXT`c z3qW%vN`m}?8Lpj{blD-x$aeUYw${d3^)`-IudMd^d*u5{PZ=ekqIgdi#}JO|rN=#) z8Wea~4l-~Zt1TD$_uoJH-M5vq)=cV;>6oM3eEgA2l*X-xvs3ouOxoGrW~@>5S>?9Z zYc=7&=Hch2>^%2O;lYs!32}=aG#)v6$22e`>GH($>vms$Ubb(wGAsA(l1J=7s~9|8 M{a9VrIVCg!0Ccx#!T<mO diff --git a/vendor/assets/images/jquery-tablesorter/bootstrap-white-unsorted.png b/vendor/assets/images/jquery-tablesorter/bootstrap-white-unsorted.png index fb227a150ec672948d40c0989ab4654957d4074c..368c66dd7399b1b7455a6d38017b4c0df3ba9759 100644 GIT binary patch delta 164 zcmV;V09*gx0ki>-7=H)`0002_Vdj+p004VQL_t(|0kzOO0fR6AMbS)X=+^)uvSAV% z%oxZV0daEL?e)L)%=bZ(@(NdMxRzfqqX8P`U*Rrbe;eSKFfwpVOcw(ShUkge5~}46 zH3M7uKdgX;Yu;M}tce+-;fN+iVk)`_I%;AjEXW|?f~iCX7#)C-Fg^`h!ngsSK|v4v Sq0pfK0000<MNUMnLSTX^4MB(i literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^+(697!VF^h|6T^9v;urWT!Hj|;y~N(Q~E&Fyd^<? z!3+;vPqAIm+AC|fS#s9YvlX|165*aMjv*Y^lM@nNWF;^#nME55Os)G~#l*~9{`T(f zt2{hw%3D*`8yOVD&N28X#w_(<=bZ~R?Tw8e*KK|4%Eor?#_n=uNr{M%_i>?&UREc$ gEEzP#QWe-4B7cis^t<g_0<?m`)78&qol`;+09y`Y9smFU diff --git a/vendor/assets/images/jquery-tablesorter/dragtable-handle.png b/vendor/assets/images/jquery-tablesorter/dragtable-handle.png index ff64a7d572cf53cf31ff98cd261c1d21ebe19e97..52a1a5659382b287828bdb8c0f56158a9f342824 100644 GIT binary patch delta 56 zcmYfIo*-e&!N$PA(EMQkM<6BS>Eakt!I*r2ucx>7d_Xh{BSY(7wqL@nL5x6I22WQ% Jmvv4FO#s}v5fK0Y delta 84 zcmeZHpCIAS$jrdNuv1TW6OdvH@Ck7R(#FQdCq6z?0J4}$g8YIR&OC8+UJT^%dAc}; ia9mGjU}V>s!N?#G#ptMWcjA1Y41=eupUXO@geCxBMipHE diff --git a/vendor/assets/images/jquery-tablesorter/dropbox-asc-hovered.png b/vendor/assets/images/jquery-tablesorter/dropbox-asc-hovered.png index bc45223f1dec5faab874457b501249849a5ad67a..4e625e028c315576b4bf47140074ef5a7af2d8f7 100644 GIT binary patch delta 172 zcmV;d08{^y0>}Z7B!7=dL_t(|0b>0B|33pEz=X^R&#GjL$f@oEG(y$}2t;8F&iS(+ zn}GosfJLzOIvh%z4Op1V!=37jRUto)(oE;t%A9;VY7dq*?PMdB8c6z=QKrmZyHZVC z0ZIQb>W<b8WnG>rjVOvi(lft~GHnjZxS~&hAOK0v+>d(WZ6#e<X{MPf`Tzht>6x?t a>!%}ejvI~?KfLGw0000<MNUMnLSTY`^G#>~ delta 247 zcmV<T00{rc0h0odB!3xnMObuGZ)S9NVRB^vL1b@YWgtmyVP|DhWnpA_ami&o0002B zNkl<Zc-mt8|NlP&A;5yn3D2rzV`O4#`Ty@<&B|%*zhL4_$cot5Iny|~c}qbwvN$8m zx~RN5As&9g+Z<fnLO;HJefQ<#d(~x=n%<$9#LmIh!pXxY1aLHo0YroNDAvUkG#GGj z^Q5tJa56A6voL^Y5FaECSH#ZAHJO8p2Pn@9bO0j*hz9XN;vjkEyC0cUxcLQpK-MrZ xGeaB-RLI1{#PIL`Ke-j_w=ZQRCY%5Q3;^4XF8Gf^mEZsX002ovPDHLkV1fila6AA2 diff --git a/vendor/assets/images/jquery-tablesorter/dropbox-asc.png b/vendor/assets/images/jquery-tablesorter/dropbox-asc.png index 0d6ee15054013562d87105ebf9a5915b093b745a..7b6615bc49d1e874cf626a9fac4754e2f15696a8 100644 GIT binary patch delta 176 zcmV;h08jsx0?YxBB!81hL_t(|0b~6C|33pEz=X_+>{!PZ)wQ7uMB`M%#>tb$$tPS2 zqOmH9>D?;C$uHc<D<Z`JqCtENMeJO>EnNJ<LR|bp3?LfBM^O|vVTS=HpHLb*7as#N z3mXH72Ju1Sa7FCge3LnN1(=!HfbL~vWB}11K1dv_D1PED6)8?$Ase7$f%0HIU>d{+ eiG$>c3MT;i<{w||U9uSf0000<MNUMnLSTY3u}TyG delta 250 zcmV<W00sZd0hR)gB!3xnMObuGZ)S9NVRB^vL1b@YWgtmyVP|DhWnpA_ami&o0002E zNkl<Zc-mt8|NlP&A;5yniR@U%#>m9f^8eqzn&lNizhL4_$corFdD1xfgiAp*vN$8m zx|rUrLOj9}w>fwOgnoSa@b1go7phB&z2Bjj#LmUr!o@Ev#B;?j!~mi}d=%^AChRca z<P%C`=i*~vW?^Fh(I7rZ9Il9+n{P4)uK+VM8ynC8j0_+e#0QCk<eBdaTB+~|N%R19 zGB7biJj)1F$i&3V@c-{$xfOfQEoCGooB#q00A{8yymYN>J^%m!07*qoM6N<$g6U#% A#Q*>R diff --git a/vendor/assets/images/jquery-tablesorter/dropbox-desc-hovered.png b/vendor/assets/images/jquery-tablesorter/dropbox-desc-hovered.png index 4930942048212f647f27b8c8f06ec2737a7ee016..806707dddb0b21cae6cddea0a4acdddd20393058 100644 GIT binary patch delta 167 zcmV;Y09gN#0>c52B!7xYL_t(|0b}_8|35*@5Ledi98=KvKd!h11B1jt@?e!YZL>e- zch3D^&@~T^L41%n*d!29IAKM2*~At9E2pl8V-O!C4pRiirIXiOshF|~tN=uV_)s~7 zqWr#P%4L&Q{Hp{i0MQ^mvLZMxnY?CI*~Df4K{SYuRZ&joLLi=^{$(dYG`b>!7y$dE V+FA!A)4%`#002ovPDHLkV1hKbN^1ZB delta 245 zcmV<R01E%Z0g(cbB!3xnMObuGZ)S9NVRB^vL1b@YWgtmyVP|DhWnpA_ami&o00029 zNkl<Zc-mt8|NlP&A;1z}-sAl5*S9@P%*+^)|Nj1EU}EEPVFa0!+dk(bGcyw(BO@bR z0Z9Ds-+!MbSElf@fCN~%c+*%J8CEc}AWZxF=P$$W{|sp$Hf|%#O=VNoUSVeZrwuX< zY|4M8t6e$4Iv_bFn9g6nfBFCW_wPSY1p|l%@nLdsMH31m@BU+CS@ZYr9|jN&;=_H1 v?3*9oz9zA;vC9AY@ihrqoROGt0thew+vQU#xeeWL00000NkvXXu0mjf-BWM6 diff --git a/vendor/assets/images/jquery-tablesorter/dropbox-desc.png b/vendor/assets/images/jquery-tablesorter/dropbox-desc.png index 0bce65a07a86cfaab08ac6bac4959e42fd31ca4e..868a37ccc935c40ec378c3a73b75ea97aa9ef9d4 100644 GIT binary patch delta 189 zcmV;u07Czj0?q-D8Gi!+007rd_96fP069=hR7L;)|5}N#UXZz9lf7%D({iujbFt%X zr`LJ9?|i@YbFt%lzVmCO(|EY;e7^K?uHSjO?q{FOZ>!w2@4e3e000woQchC<B>(@z z|Nk)j|Cav}?f-2528vx^j#>Z!05M5KK~#7FCBeBB0x=LkF*fn%5u5%yb;6z!0G|sS rM@P*}9hsNxQd`uwfc0Al6#zH^d)EeRF*)P}00000NkvXXu0mjfTK`y? delta 260 zcmV+f0sH>W0hI!f8Gi-<007{T1`q%M02y>eSaefwW^{L9a%BKPWN%_+AW3auXJt}l zVPtu6$z?nM006g1L_t(|+G70w|33pEz!KfHQH6z_^EMMR3x?#szkeA1{QR!U2r?;W z*_FfWTzoc+OiXYE|Ns4C`1$SAkttC+wqWf{Of0$V+<a%4S$)}<VT%6#{`L3wk8inP zc@QxvLgT`(?_Xy9|NjqS3XlfzLE>;lAn@b!hnl~?e!T;#U;xn|K1>d-XiBusJD?lt z|Ni*}a!Wmk5BD9iZ@zteJL})yzj8mme3-?+pa2(VBqp2y0t^7x-(G=<F<UVJ0000< KMNUMnLSTZ?j(JA_ diff --git a/vendor/assets/images/jquery-tablesorter/metro-black-asc.png b/vendor/assets/images/jquery-tablesorter/metro-black-asc.png index f7a781be17147df66dfad715f1a977b148fd1153..381ed1e0aec0baf1ae292b418b968774f2a3c004 100644 GIT binary patch delta 145 zcmV;C0B--)0igkq7=H)`0000YVV(y7003x7L_t(|0hQ1x5(F^_K+!R?qT{2nxlf-h zH_K6&FHqe;rGM3PS$QNKZYza3MVVm1+=3u&!cJ!OZc7~TyK5T0yRabPy9*Qd>lmB* zolQNO)JP~Uy^>9e7s4diASi1Xu3@X@F8u+ek2r-EH6ud+015yANkvXXu0mjfx<okz literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eK!VDxY_&1vXDa8Pv5LY0LALL&2I0#h1RTAVE z%wWu|6?cDuf+*XOEM^0sXr!l$V+hA}-7~sE4h$l$4~-?Rm@@@l>{N*qQH@MuUvp6C zPs?_%_h)|`vVF@G*#BDZenOR(_Zq8cwyV-krlwQVQ$FtbV`gaCncnKA!t6Kk+IbhR dGl35-$=XTsix@`i@H_)@oTsaw%Q~loCIIowIb;9; diff --git a/vendor/assets/images/jquery-tablesorter/metro-black-desc.png b/vendor/assets/images/jquery-tablesorter/metro-black-desc.png index ef5f48e07e52c6608e913fda2aa724c3eda3cecd..b20631f2ebd7b14682780e83bb7d47489afcf9f2 100644 GIT binary patch delta 156 zcmV;N0Av5?0jvR#7=H)`0000YVV(y70047IL_t(|0hP`%4Z~0j2H_`fp0gW)rw1fF zT0mhEHUSX^?hRhr_K(t&zWs)1X}5CB=D5s|m@Q$D1?Gp4-<ZOpzcm-%Qo_Wy6!^al zb<>7n*ilN`VWV>#DB*yQj{+y^!-*K8M14qHwxZgKTRrf^R~0XI;WL(w$#)z80000< KMNUMnLSTXu!$NBS literal 232 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eK!VDxY_&1vXDct~{5LX~gJUF;O!w#sLuO!GX zn88R#j+05)@57y~)3P!xeeTBsWnw&C978y+CnqE%cqcG0x(f=NT%{qxuw(`+D{n){ zq6G^in3=m31S||#8Z|=MoCQ?RFtGl<$9OQiO+#Sol*ZJ1yUX=uB_wq2?agNMGTK|c loFORWyUdzt-x-*h87{7oXg<B{)OnD5Jzf1=);T3K0RT~YH)H?+ diff --git a/vendor/assets/images/jquery-tablesorter/metro-unsorted.png b/vendor/assets/images/jquery-tablesorter/metro-unsorted.png index 653b083428357a3a81387e840c003c4d8a88c226..e67ab2a69c4c0b9b90c7c4b1adc782b568c51b9c 100644 GIT binary patch delta 134 zcmV;10D1ql0ha-g7=H)`0000YVV(y7003S|L_t(|0iBY!5x@Wl1L^3QfS!*1XJQ5C zY`>H4z9S>KQdL=xkPgAxkrVnRAvOu|6zm;&!0+Zxz<6#(VQwUNZpP({eszE(4t~3M okR{?O7%T$+fMEK#LD+kHx3JPVNI_EN{Qv*}07*qoM6N<$g5H`q2LJ#7 delta 165 zcmbQvxRr5&L_G^L0|Uba|7H^)B^2Nj;tHhSp@L8ObrwKTwvr&fU<M;0c5MZAwjJ(3 z5g$(%#}JO|$q5MwK?w|u&WQ$3HJFqlSy`DIrYu^pK!BNfvx0zx!G;D6_BICr)l&?N z0dgL;5$%f?D0uAeT(H1+LXsR?CbJ~72}_nBL+}JPJ;RCYc0hv}JYD@<);T3K0RU%_ BG?f4V diff --git a/vendor/assets/images/jquery-tablesorter/metro-white-asc.png b/vendor/assets/images/jquery-tablesorter/metro-white-asc.png index 63327188b8fc240895c3e02d2e02e64e49e297c4..ca8c8e25daecaadb474f5c32c12f1987b8900dca 100644 GIT binary patch delta 152 zcmV;J0B8Tv0jL3x7=H)`0000YVV(y7003`EL_t(|0gaKj5x^i6#oRIgJ@sq753@0! z(+JMl7U@nE`njSv41-_6Pmr}R9VjQ8h4vB9E|^q6xaEtG>U)adx$iXMzL|R->KovB zO84#->Ovd`F%C8rmBm-)Al=u7AP9KK9AqD|MTvu0o&n;zYY~dH`Weaq0000<MNUMn GLSTYD7eC|x literal 209 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eK!VDxY_&1vXDY*cj5LY1m4mYstac=>t;4BI9 z3uZ9p)Dn$*y+GkdYyN(qV5FyuV+hA}-7{<X92i8}9+ua+*(!IK&F)!N$i8Y-=Bi_+ z5@qkMS<Y6k$^YF@ap>#x{0(+XRRizpY<|VNa8}!`%^RxY>Xy%Fx>D?*J&AFu$MbY0 f)wDB(7a8Qwbqb|%)xLTMw1mOa)z4*}Q$iB}B+^)A diff --git a/vendor/assets/images/jquery-tablesorter/metro-white-desc.png b/vendor/assets/images/jquery-tablesorter/metro-white-desc.png index ca7c363e34009bdf9f61bfea526e410a9ecddc49..cda73c170778147826980381a4fd108caf20f668 100644 GIT binary patch delta 163 zcmV;U09^mz0kZ*+7=H)`0000YVV(y7004SPL_t(|0gcf&5kMgf08oxCGdl`0Ap{@a z5MFS^gav-@Y23YybVt43dssQ*QJ-Ov4xoMZ3w#9N!b<^hkv^i-cVYyxZw1hO)9!ib zzG?J4hP{`?Fe>K^)$qA4{Bui9_}msi9c|F~<00`GwI$@o=NxR^+_ZAUn*ly#an|NS Rvs3^8002ovPDHLkV1g8jMZW+5 literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eK!VDxY_&1vXDfIxK5LY1mjtC&A#&`*+l&2)f zFPOndM~;(8IMeUNw4Hf3%JYHZF`h1tAsp9}6A}`<6BroX1qDv7(hy--GJ}<sw;^QF zf&~)H%-sqC76vSh8lh~?0;*>iShLSD9Mo*f5ZF4kG4<R`V|7^xiJWtDEP1A+SQWc5 jE?M!MB|^8Fk(rsHyG_iYB>X`v&>jX)S3j3^P6<r__~la{ diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index fc6a503..219bc32 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin -* updated 5/1/2016 (v2.26.0) +* updated 6/28/2015 (v2.26.5) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -341,12 +341,11 @@ sz = p.size === 'all' ? p.totalRows : p.size, s = ( p.page * sz ), e = s + sz, - f = c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered', last = 0, // for cache indexing j = 0; // size counter p.cacheIndex = []; for ( i = 0; i < l; i++ ){ - if ( !rows[i].className.match(f) ) { + if ( !p.regexFiltered.test(rows[i].className) ) { if (j === s && rows[i].className.match(c.cssChildRow)) { // hide child rows @ start of pager (if already visible) rows[i].style.display = 'none'; @@ -636,7 +635,7 @@ count = f ? 0 : s; added = 0; while (added < e && index < rows.length) { - if (!f || !/filtered/.test(rows[index][0].className)){ + if (!f || !p.regexFiltered.test(rows[index][0].className)){ count++; if (count > s && added <= e) { added++; @@ -940,6 +939,7 @@ } // skipped rows p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.slice(1) + '|' + c.cssChildRow + ')'); + p.regexFiltered = new RegExp(wo.filter_filteredRow || 'filtered'); $t // .unbind( namespace ) adding in jQuery 1.4.3 ( I think ) @@ -1120,18 +1120,18 @@ // see #486 ts.showError = function( table, xhr, settings, exception ) { var $row, - $table = $( table ), - c = $table[0].config, - wo = c && c.widgetOptions, - errorRow = c.pager && c.pager.cssErrorRow || - wo && wo.pager_css && wo.pager_css.errorRow || - 'tablesorter-errorRow', - typ = typeof xhr, - valid = true, - message = '', - removeRow = function(){ - c.$table.find( 'thead' ).find( '.' + errorRow ).remove(); - }; + $table = $( table ), + c = $table[0].config, + wo = c && c.widgetOptions, + errorRow = c.pager && c.pager.cssErrorRow || + wo && wo.pager_css && wo.pager_css.errorRow || + 'tablesorter-errorRow', + typ = typeof xhr, + valid = true, + message = '', + removeRow = function(){ + c.$table.find( 'thead' ).find( '.' + errorRow ).remove(); + }; if ( !$table.length ) { console.error('tablesorter showError: no table parameter passed'); @@ -1158,13 +1158,13 @@ if ( message === '' ) { if ( typ === 'object' ) { message = - xhr.status === 0 ? 'Not connected, verify Network' : - xhr.status === 404 ? 'Requested page not found [404]' : - xhr.status === 500 ? 'Internal Server Error [500]' : - exception === 'parsererror' ? 'Requested JSON parse failed' : - exception === 'timeout' ? 'Time out error' : - exception === 'abort' ? 'Ajax Request aborted' : - 'Uncaught error: ' + xhr.statusText + ' [' + xhr.status + ']'; + xhr.status === 0 ? 'Not connected, verify Network' : + xhr.status === 404 ? 'Requested page not found [404]' : + xhr.status === 500 ? 'Internal Server Error [500]' : + exception === 'parsererror' ? 'Requested JSON parse failed' : + exception === 'timeout' ? 'Time out error' : + exception === 'abort' ? 'Ajax Request aborted' : + 'Uncaught error: ' + xhr.statusText + ' [' + xhr.status + ']'; } else if ( typ === 'string' ) { // keep backward compatibility (external usage just passes a message string) message = xhr; @@ -1176,16 +1176,16 @@ // allow message to include entire row HTML! $row = ( /tr\>/.test(message) ? $(message) : $('<tr><td colspan="' + c.columns + '">' + message + '</td></tr>') ) - .click( function() { - $( this ).remove(); - }) - // add error row to thead instead of tbody, or clicking on the header will result in a parser error - .appendTo( c.$table.find( 'thead:first' ) ) - .addClass( errorRow + ' ' + c.selectorRemove.slice(1) ) - .attr({ - role : 'alert', - 'aria-live' : 'assertive' - }); + .click( function() { + $( this ).remove(); + }) + // add error row to thead instead of tbody, or clicking on the header will result in a parser error + .appendTo( c.$table.find( 'thead:first' ) ) + .addClass( errorRow + ' ' + c.selectorRemove.slice(1) ) + .attr({ + role : 'alert', + 'aria-live' : 'assertive' + }); }; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 14fff96..9e572bc 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 06-15-2016 (v2.26.4)*/ +/*! tablesorter (FORK) - updated 06-28-2016 (v2.26.5)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.26.4 *//* +/*! TableSorter (FORK) v2.26.5 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -39,7 +39,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.26.4', + version : '2.26.5', parsers : [], widgets : [], @@ -3104,7 +3104,7 @@ })(jQuery); -/*! Widget: filter - updated 5/28/2016 (v2.26.2) *//* +/*! Widget: filter - updated 6/28/2015 (v2.26.5) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3735,14 +3735,16 @@ // encode or decode filters for storage; see #1026 processFilters: function( filters, encode ) { var indx, + // fixes #1237; previously returning an encoded "filters" value + result = [], mode = encode ? encodeURIComponent : decodeURIComponent, len = filters.length; for ( indx = 0; indx < len; indx++ ) { if ( filters[ indx ] ) { - filters[ indx ] = mode( filters[ indx ] ); + result[ indx ] = mode( filters[ indx ] ); } } - return filters; + return result; }, setDefaults: function( table, c, wo ) { var isArray, saved, indx, col, $filters, @@ -5230,7 +5232,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 5/16/2015 (v2.26.1) */ +/*! Widget: resizable - updated 6/28/2015 (v2.26.5) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -5486,7 +5488,7 @@ // right click to reset columns to default widths c.$table - .bind( 'columnUpdate' + namespace, function() { + .bind( 'columnUpdate' + namespace + ' pagerComplete' + namespace, function() { ts.resizable.setHandlePosition( c, wo ); }) .find( 'thead:first' ) diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 0af6b68..5beda8c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.26.4 *//* +/*! TableSorter (FORK) v2.26.5 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -21,7 +21,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.26.4', + version : '2.26.5', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 0ce4ac2..c3e5bb5 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 06-15-2016 (v2.26.4)*/ +/*! tablesorter (FORK) - updated 06-28-2016 (v2.26.5)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -372,7 +372,7 @@ })(jQuery); -/*! Widget: filter - updated 5/28/2016 (v2.26.2) *//* +/*! Widget: filter - updated 6/28/2015 (v2.26.5) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -1003,14 +1003,16 @@ // encode or decode filters for storage; see #1026 processFilters: function( filters, encode ) { var indx, + // fixes #1237; previously returning an encoded "filters" value + result = [], mode = encode ? encodeURIComponent : decodeURIComponent, len = filters.length; for ( indx = 0; indx < len; indx++ ) { if ( filters[ indx ] ) { - filters[ indx ] = mode( filters[ indx ] ); + result[ indx ] = mode( filters[ indx ] ); } } - return filters; + return result; }, setDefaults: function( table, c, wo ) { var isArray, saved, indx, col, $filters, @@ -2498,7 +2500,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 5/16/2015 (v2.26.1) */ +/*! Widget: resizable - updated 6/28/2015 (v2.26.5) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -2754,7 +2756,7 @@ // right click to reset columns to default widths c.$table - .bind( 'columnUpdate' + namespace, function() { + .bind( 'columnUpdate' + namespace + ' pagerComplete' + namespace, function() { ts.resizable.setHandlePosition( c, wo ); }) .find( 'thead:first' ) diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 8fc3dbf..8d2d60a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 5/28/2016 (v2.26.2) *//* +/*! Widget: filter - updated 6/28/2015 (v2.26.5) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -629,14 +629,16 @@ // encode or decode filters for storage; see #1026 processFilters: function( filters, encode ) { var indx, + // fixes #1237; previously returning an encoded "filters" value + result = [], mode = encode ? encodeURIComponent : decodeURIComponent, len = filters.length; for ( indx = 0; indx < len; indx++ ) { if ( filters[ indx ] ) { - filters[ indx ] = mode( filters[ indx ] ); + result[ indx ] = mode( filters[ indx ] ); } } - return filters; + return result; }, setDefaults: function( table, c, wo ) { var isArray, saved, indx, col, $filters, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index 73563a6..43713d9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -1,4 +1,4 @@ -/*! Widget: grouping - updated 5/16/2015 (v2.26.1) *//* +/*! Widget: grouping - updated 6/28/2015 (v2.26.5) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -199,19 +199,23 @@ }, findColumnGroups : function( c, wo, data ) { var tbodyIndex, norm_rows, $row, rowIndex, end, undef, - hasPager = ts.hasWidget( c.table, 'pager' ); + hasPager = ts.hasWidget( c.table, 'pager' ), + p = c.pager || {}; data.groupIndex = 0; for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { norm_rows = c.cache[ tbodyIndex ].normalized; data.group = undef; // clear grouping across tbodies - rowIndex = hasPager ? c.pager.startRow - 1 : 0; - end = hasPager ? c.pager.endRow : norm_rows.length; + rowIndex = hasPager && !p.ajax ? p.startRow - 1 : 0; + end = hasPager ? p.endRow - ( p.ajax ? p.startRow : 0 ) : norm_rows.length; for ( ; rowIndex < end; rowIndex++ ) { data.rowData = norm_rows[ rowIndex ]; - data.$row = data.rowData[ c.columns ].$row; - // fixes #438 - if ( data.$row.is( ':visible' ) && tsg.types[ data.grouping[ 1 ] ] ) { - tsg.insertGroupHeader( c, wo, data ); + // fixes #1232 - ajax issue; if endRow > norm_rows.length (after filtering), then data.rowData is undefined + if (data.rowData) { + data.$row = data.rowData[ c.columns ].$row; + // fixes #438 + if ( data.$row.is( ':visible' ) && tsg.types[ data.grouping[ 1 ] ] ) { + tsg.insertGroupHeader( c, wo, data ); + } } } } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 239c8e2..e023642 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 5/1/2016 (v2.26.0) */ +/*! Widget: Pager - updated 6/28/2015 (v2.26.5) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -190,6 +190,7 @@ // skipped rows p.regexRows = new RegExp( '(' + ( wo.filter_filteredRow || 'filtered' ) + '|' + c.selectorRemove.slice( 1 ) + '|' + c.cssChildRow + ')' ); + p.regexFiltered = new RegExp( wo.filter_filteredRow || 'filtered' ); // clear initialized flag p.initialized = false; @@ -637,7 +638,6 @@ sz = p.size === 'all' ? p.totalRows : p.size, start = ( p.page * sz ), end = start + sz, - filtr = wo && wo.filter_filteredRow || 'filtered', last = 0, // for cache indexing size = 0; // size counter p.cacheIndex = []; @@ -648,7 +648,7 @@ last = 0; // for cache indexing size = 0; // size counter for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { - if ( !$rows[ rowIndex ].className.match( filtr ) ) { + if ( !p.regexFiltered.test( $rows[ rowIndex ].className ) ) { if ( size === start && $rows[ rowIndex ].className.match( c.cssChildRow ) ) { // hide child rows @ start of pager (if already visible) $rows[ rowIndex ].style.display = 'none'; @@ -949,7 +949,7 @@ count = f ? 0 : s; added = 0; while ( added < e && index < rows.length ) { - if ( !f || !/filtered/.test( rows[ index ][ 0 ].className ) ) { + if ( !f || !p.regexFiltered.test( rows[ index ][ 0 ].className ) ) { count++; if ( count > s && added <= e ) { added++; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js index 926c08a..ad92f98 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js @@ -1,4 +1,4 @@ -/*! Widget: resizable - updated 5/16/2015 (v2.26.1) */ +/*! Widget: resizable - updated 6/28/2015 (v2.26.5) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -254,7 +254,7 @@ // right click to reset columns to default widths c.$table - .bind( 'columnUpdate' + namespace, function() { + .bind( 'columnUpdate' + namespace + ' pagerComplete' + namespace, function() { ts.resizable.setHandlePosition( c, wo ); }) .find( 'thead:first' ) From 9a7a148b33d8fb420ca8ec2287e174fff5062519 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Tue, 12 Jul 2016 20:54:38 +0200 Subject: [PATCH 091/138] Update tablesorter to latest version (2.26.6) Please note that the two previous commit messages accidentally referenced the wrong tablesorter version number. Please always refer the changelog/readme/submodule. Thanks. --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 4 +- .../jquery.tablesorter.combined.js | 78 ++++++++++++------- .../jquery-tablesorter/jquery.tablesorter.js | 12 +-- .../jquery.tablesorter.widgets.js | 66 ++++++++++------ .../widgets/widget-columnSelector.js | 31 ++++---- .../widget-filter-formatter-select2.js | 64 +++++++++------ .../widgets/widget-filter.js | 40 ++++++---- .../widgets/widget-grouping.js | 15 ++-- .../widgets/widget-pager.js | 6 +- .../widgets/widget-resizable.js | 2 +- .../widgets/widget-scroller.js | 6 +- .../widgets/widget-uitheme.js | 22 ++++-- .../jquery-tablesorter/widgets/widget-view.js | 9 ++- .../jquery-tablesorter/theme.bootstrap.css | 7 ++ .../jquery-tablesorter/theme.bootstrap_2.css | 6 ++ .../jquery-tablesorter/theme.dropbox.css | 6 +- .../jquery-tablesorter/theme.grey.css | 3 +- 21 files changed, 242 insertions(+), 145 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea1ecdb..545caf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.21.4 (2016-07-12) + +* Upgrade tablesorter to v2.26.6 + #### v1.21.3 (2016-06-28) * Upgrade tablesorter to v2.26.5 diff --git a/README.md b/README.md index 8024355..ce82f6a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.26.5 (6/28/2016), [documentation] +Current tablesorter version: 2.26.6 (7/11/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 4fc3127..70321fa 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 21 - TINY = 3 + TINY = 4 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index eb84f87..b015da6 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit eb84f87d5a99070180354a29f63d6450e8c2bb31 +Subproject commit b015da6bbc9c757ffc87a1cfa0c216dfd0ba9725 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 219bc32..b43d7ff 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin -* updated 6/28/2015 (v2.26.5) +* updated 7/11/2016 (v2.26.6) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -783,7 +783,7 @@ // set to either set or get value parsePageSize = function( p, size, mode ) { var s = parseInt( size, 10 ) || p.size || p.settings.size || 10; - return /all/i.test( size ) || s === p.totalRows ? + return p.initialized && (/all/i.test( size ) || s === p.totalRows) ? // "get" to get `p.size` or "set" to set `p.$size.val()` 'all' : ( mode === 'get' ? s : p.size ); }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 9e572bc..2e788c9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 06-28-2016 (v2.26.5)*/ +/*! tablesorter (FORK) - updated 07-11-2016 (v2.26.6)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function($) { -/*! TableSorter (FORK) v2.26.5 *//* +/*! TableSorter (FORK) v2.26.6 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -39,7 +39,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.26.5', + version : '2.26.6', parsers : [], widgets : [], @@ -1777,14 +1777,14 @@ regex = ts.regex; // first try and sort Hex codes if ( regex.hex.test( b ) ) { - aNum = parseInt( a.match( regex.hex ), 16 ); - bNum = parseInt( b.match( regex.hex ), 16 ); + aNum = parseInt( ( a || '' ).match( regex.hex ), 16 ); + bNum = parseInt( ( b || '' ).match( regex.hex ), 16 ); if ( aNum < bNum ) { return -1; } if ( aNum > bNum ) { return 1; } } // chunk/tokenize - aNum = a.replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); - bNum = b.replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); + aNum = ( a || '' ).replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); + bNum = ( b || '' ).replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); max = Math.max( aNum.length, bNum.length ); // natural sorting through split numeric strings and default strings for ( indx = 0; indx < max; indx++ ) { @@ -2839,7 +2839,7 @@ })(jQuery, window, document); -/*! Widget: uitheme - updated 3/26/2015 (v2.21.3) */ +/*! Widget: uitheme - updated 7/11/2016 (v2.26.6) */ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; @@ -2897,7 +2897,7 @@ id: 'uitheme', priority: 10, format: function(table, c, wo) { - var i, hdr, icon, time, $header, $icon, $tfoot, $h, oldtheme, oldremove, oldIconRmv, hasOldTheme, + var i, tmp, hdr, icon, time, $header, $icon, $tfoot, $h, oldtheme, oldremove, oldIconRmv, hasOldTheme, themesAll = ts.themes, $table = c.$table.add( $( c.namespace + '_extra_table' ) ), $headers = c.$headers.add( $( c.namespace + '_extra_headers' ) ), @@ -2964,10 +2964,20 @@ .removeClass(hasOldTheme ? [ oldtheme.icons, oldIconRmv ].join(' ') : '') .addClass(themes.icons || ''); } - if ($table.hasClass('hasFilters')) { - $table.children('thead').children('.' + ts.css.filterRow) - .removeClass(hasOldTheme ? oldtheme.filterRow || '' : '') - .addClass(themes.filterRow || ''); + // filter widget initializes after uitheme + if (c.widgets.indexOf('filter') > -1) { + tmp = function() { + $table.children('thead').children('.' + ts.css.filterRow) + .removeClass(hasOldTheme ? oldtheme.filterRow || '' : '') + .addClass(themes.filterRow || ''); + }; + if (wo.filter_initialized) { + tmp(); + } else { + $table.one('filterInit', function() { + tmp(); + }); + } } } for (i = 0; i < c.columns; i++) { @@ -3104,7 +3114,7 @@ })(jQuery); -/*! Widget: filter - updated 6/28/2015 (v2.26.5) *//* +/*! Widget: filter - updated 7/11/2016 (v2.26.6) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3891,12 +3901,13 @@ .attr( 'data-lastSearchTime', new Date().getTime() ) .unbind( tmp.replace( ts.regex.spaces, ' ' ) ) .bind( 'keydown' + namespace, function( event ) { - if ( event.which === tskeyCodes.escape && !wo.filter_resetOnEsc ) { + if ( event.which === tskeyCodes.escape && !table.config.widgetOptions.filter_resetOnEsc ) { // prevent keypress event return false; } }) .bind( 'keyup' + namespace, function( event ) { + wo = table.config.widgetOptions; // make sure "wo" isn't cached var column = parseInt( $( this ).attr( 'data-column' ), 10 ); $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); // emulate what webkit does.... escape clears the filter @@ -3923,7 +3934,8 @@ // don't get cached data, in case data-column changes dynamically var column = parseInt( $( this ).attr( 'data-column' ), 10 ); // don't allow 'change' event to process if the input value is the same - fixes #685 - if ( wo.filter_initialized && ( event.which === tskeyCodes.enter || event.type === 'search' || + if ( table.config.widgetOptions.filter_initialized && + ( event.which === tskeyCodes.enter || event.type === 'search' || ( event.type === 'change' ) && this.value !== c.lastSearch[column] ) || // only "input" event fires in MS Edge when clicking the "x" to clear the search ( event.type === 'input' && this.value === '' ) ) { @@ -3972,7 +3984,7 @@ // show/hide filter row as needed c.$table .find( '.' + tscss.filterRow ) - .triggerHandler( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); + .triggerHandler( tsf.hideFiltersCheck( c ) ? 'mouseleave' : 'mouseenter' ); } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons @@ -4005,26 +4017,34 @@ return false; } }, + hideFiltersCheck: function( c ) { + if (typeof c.widgetOptions.filter_hideFilters === 'function') { + var val = c.widgetOptions.filter_hideFilters( c ); + if (typeof val === 'boolean') { + return val; + } + } + return ts.getFilters( c.$table ).join( '' ) === ''; + }, hideFilters: function( c, $table ) { - var timer, - $row = ( $table || c.$table ).find( '.' + tscss.filterRow ).addClass( tscss.filterRowHide ); - $row + var timer; + ( $table || c.$table ) + .find( '.' + tscss.filterRow ) + .addClass( tscss.filterRowHide ) .bind( 'mouseenter mouseleave', function( e ) { // save event object - http://bugs.jquery.com/ticket/12140 var event = e, - $filterRow = $( this ); + $row = $( this ); clearTimeout( timer ); timer = setTimeout( function() { if ( /enter|over/.test( event.type ) ) { - $filterRow.removeClass( tscss.filterRowHide ); + $row.removeClass( tscss.filterRowHide ); } else { // don't hide if input has focus // $( ':focus' ) needs jQuery 1.6+ - if ( $( document.activeElement ).closest( 'tr' )[0] !== $filterRow[0] ) { + if ( $( document.activeElement ).closest( 'tr' )[0] !== $row[0] ) { // don't hide row if any filter has a value - if ( c.lastCombinedFilter === '' ) { - $filterRow.addClass( tscss.filterRowHide ); - } + $row.toggleClass( tscss.filterRowHide, tsf.hideFiltersCheck( c ) ); } } }, 200 ); @@ -4036,9 +4056,7 @@ timer = setTimeout( function() { clearTimeout( timer ); // don't hide row if any filter has a value - if ( ts.getFilters( c.$table ).join( '' ) === '' ) { - $row.toggleClass( tscss.filterRowHide, event.type !== 'focus' ); - } + $row.toggleClass( tscss.filterRowHide, tsf.hideFiltersCheck( c ) && event.type !== 'focus' ); }, 200 ); }); }, @@ -5232,7 +5250,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 6/28/2015 (v2.26.5) */ +/*! Widget: resizable - updated 6/28/2016 (v2.26.5) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 5beda8c..95ce670 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.26.5 *//* +/*! TableSorter (FORK) v2.26.6 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -21,7 +21,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.26.5', + version : '2.26.6', parsers : [], widgets : [], @@ -1759,14 +1759,14 @@ regex = ts.regex; // first try and sort Hex codes if ( regex.hex.test( b ) ) { - aNum = parseInt( a.match( regex.hex ), 16 ); - bNum = parseInt( b.match( regex.hex ), 16 ); + aNum = parseInt( ( a || '' ).match( regex.hex ), 16 ); + bNum = parseInt( ( b || '' ).match( regex.hex ), 16 ); if ( aNum < bNum ) { return -1; } if ( aNum > bNum ) { return 1; } } // chunk/tokenize - aNum = a.replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); - bNum = b.replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); + aNum = ( a || '' ).replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); + bNum = ( b || '' ).replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); max = Math.max( aNum.length, bNum.length ); // natural sorting through split numeric strings and default strings for ( indx = 0; indx < max; indx++ ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index c3e5bb5..5261053 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 06-28-2016 (v2.26.5)*/ +/*! tablesorter (FORK) - updated 07-11-2016 (v2.26.6)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -107,7 +107,7 @@ })(jQuery, window, document); -/*! Widget: uitheme - updated 3/26/2015 (v2.21.3) */ +/*! Widget: uitheme - updated 7/11/2016 (v2.26.6) */ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; @@ -165,7 +165,7 @@ id: 'uitheme', priority: 10, format: function(table, c, wo) { - var i, hdr, icon, time, $header, $icon, $tfoot, $h, oldtheme, oldremove, oldIconRmv, hasOldTheme, + var i, tmp, hdr, icon, time, $header, $icon, $tfoot, $h, oldtheme, oldremove, oldIconRmv, hasOldTheme, themesAll = ts.themes, $table = c.$table.add( $( c.namespace + '_extra_table' ) ), $headers = c.$headers.add( $( c.namespace + '_extra_headers' ) ), @@ -232,10 +232,20 @@ .removeClass(hasOldTheme ? [ oldtheme.icons, oldIconRmv ].join(' ') : '') .addClass(themes.icons || ''); } - if ($table.hasClass('hasFilters')) { - $table.children('thead').children('.' + ts.css.filterRow) - .removeClass(hasOldTheme ? oldtheme.filterRow || '' : '') - .addClass(themes.filterRow || ''); + // filter widget initializes after uitheme + if (c.widgets.indexOf('filter') > -1) { + tmp = function() { + $table.children('thead').children('.' + ts.css.filterRow) + .removeClass(hasOldTheme ? oldtheme.filterRow || '' : '') + .addClass(themes.filterRow || ''); + }; + if (wo.filter_initialized) { + tmp(); + } else { + $table.one('filterInit', function() { + tmp(); + }); + } } } for (i = 0; i < c.columns; i++) { @@ -372,7 +382,7 @@ })(jQuery); -/*! Widget: filter - updated 6/28/2015 (v2.26.5) *//* +/*! Widget: filter - updated 7/11/2016 (v2.26.6) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -1159,12 +1169,13 @@ .attr( 'data-lastSearchTime', new Date().getTime() ) .unbind( tmp.replace( ts.regex.spaces, ' ' ) ) .bind( 'keydown' + namespace, function( event ) { - if ( event.which === tskeyCodes.escape && !wo.filter_resetOnEsc ) { + if ( event.which === tskeyCodes.escape && !table.config.widgetOptions.filter_resetOnEsc ) { // prevent keypress event return false; } }) .bind( 'keyup' + namespace, function( event ) { + wo = table.config.widgetOptions; // make sure "wo" isn't cached var column = parseInt( $( this ).attr( 'data-column' ), 10 ); $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); // emulate what webkit does.... escape clears the filter @@ -1191,7 +1202,8 @@ // don't get cached data, in case data-column changes dynamically var column = parseInt( $( this ).attr( 'data-column' ), 10 ); // don't allow 'change' event to process if the input value is the same - fixes #685 - if ( wo.filter_initialized && ( event.which === tskeyCodes.enter || event.type === 'search' || + if ( table.config.widgetOptions.filter_initialized && + ( event.which === tskeyCodes.enter || event.type === 'search' || ( event.type === 'change' ) && this.value !== c.lastSearch[column] ) || // only "input" event fires in MS Edge when clicking the "x" to clear the search ( event.type === 'input' && this.value === '' ) ) { @@ -1240,7 +1252,7 @@ // show/hide filter row as needed c.$table .find( '.' + tscss.filterRow ) - .triggerHandler( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); + .triggerHandler( tsf.hideFiltersCheck( c ) ? 'mouseleave' : 'mouseenter' ); } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons @@ -1273,26 +1285,34 @@ return false; } }, + hideFiltersCheck: function( c ) { + if (typeof c.widgetOptions.filter_hideFilters === 'function') { + var val = c.widgetOptions.filter_hideFilters( c ); + if (typeof val === 'boolean') { + return val; + } + } + return ts.getFilters( c.$table ).join( '' ) === ''; + }, hideFilters: function( c, $table ) { - var timer, - $row = ( $table || c.$table ).find( '.' + tscss.filterRow ).addClass( tscss.filterRowHide ); - $row + var timer; + ( $table || c.$table ) + .find( '.' + tscss.filterRow ) + .addClass( tscss.filterRowHide ) .bind( 'mouseenter mouseleave', function( e ) { // save event object - http://bugs.jquery.com/ticket/12140 var event = e, - $filterRow = $( this ); + $row = $( this ); clearTimeout( timer ); timer = setTimeout( function() { if ( /enter|over/.test( event.type ) ) { - $filterRow.removeClass( tscss.filterRowHide ); + $row.removeClass( tscss.filterRowHide ); } else { // don't hide if input has focus // $( ':focus' ) needs jQuery 1.6+ - if ( $( document.activeElement ).closest( 'tr' )[0] !== $filterRow[0] ) { + if ( $( document.activeElement ).closest( 'tr' )[0] !== $row[0] ) { // don't hide row if any filter has a value - if ( c.lastCombinedFilter === '' ) { - $filterRow.addClass( tscss.filterRowHide ); - } + $row.toggleClass( tscss.filterRowHide, tsf.hideFiltersCheck( c ) ); } } }, 200 ); @@ -1304,9 +1324,7 @@ timer = setTimeout( function() { clearTimeout( timer ); // don't hide row if any filter has a value - if ( ts.getFilters( c.$table ).join( '' ) === '' ) { - $row.toggleClass( tscss.filterRowHide, event.type !== 'focus' ); - } + $row.toggleClass( tscss.filterRowHide, tsf.hideFiltersCheck( c ) && event.type !== 'focus' ); }, 200 ); }); }, @@ -2500,7 +2518,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 6/28/2015 (v2.26.5) */ +/*! Widget: resizable - updated 6/28/2016 (v2.26.5) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index 0370f32..fb4fda7 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Widget: columnSelector (responsive table widget) - updated 4/29/2016 (v2.25.9) *//* +/* Widget: columnSelector (responsive table widget) - updated 7/11/2016 (v2.26.6) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -348,7 +348,7 @@ } // only add resize end if using media queries if ( hasSpans && wo.columnSelector_mediaquery ) { - nspace = c.namespace.slice( 1 ) + 'columnselector'; + nspace = c.namespace + 'columnselector'; // Setup window.resizeEnd event $window .off( nspace ) @@ -365,15 +365,18 @@ adjustColspans: function(c, wo) { var index, cols, col, span, end, $cell, colSel = c.selector, - autoModeOn = colSel.auto, - $colspans = $( c.namespace + 'columnselectorHasSpan' ), - len = $colspans.length; - if ( len ) { - for ( index = 0; index < len; index++ ) { - $cell = $colspans.eq(index); - col = parseInt( $cell.attr('data-column'), 10 ) || $cell[0].cellIndex; - span = parseInt( $cell.attr('data-col-span'), 10 ); - end = col + span; + filtered = wo.filter_filteredRow || 'filtered', + autoModeOn = wo.columnSelector_mediaquery && colSel.auto, + // find all header/footer cells in case a regular column follows a colspan; see #1238 + $headers = c.$table.children( 'thead, tfoot' ).children().children() + .add( $(c.namespace + '_extra_table').children( 'thead, tfoot' ).children().children() ), + len = $headers.length; + for ( index = 0; index < len; index++ ) { + $cell = $headers.eq(index); + col = parseInt( $cell.attr('data-column'), 10 ) || $cell[0].cellIndex; + span = parseInt( $cell.attr('data-col-span'), 10 ) || 1; + end = col + span; + if ( span > 1 ) { for ( cols = col; cols < end; cols++ ) { if ( !autoModeOn && colSel.states[ cols ] === false || autoModeOn && c.$headerIndexed[ cols ] && !c.$headerIndexed[ cols ].is(':visible') ) { @@ -381,10 +384,12 @@ } } if ( span ) { - $cell.removeClass( wo.filter_filteredRow || 'filtered' )[0].colSpan = span; + $cell.removeClass( filtered )[0].colSpan = span; } else { - $cell.addClass( wo.filter_filteredRow || 'filtered' ); + $cell.addClass( filtered ); } + } else if ( typeof colSel.states[ col ] !== 'undefined' ) { + $cell.toggleClass( filtered, !colSel.states[ col ] ); } } }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js index 1889352..cb51791 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js @@ -1,4 +1,4 @@ -/*! Widget: filter, select2 formatter function - updated 5/28/2016 (v2.26.2) *//* +/*! Widget: filter, select2 formatter function - updated 7/11/2016 (v2.26.6) *//* * requires: jQuery 1.7.2+, tableSorter (FORK) 2.16+, filter widget 2.16+ and select2 v3.4.6+ plugin (this code is NOT compatible with select2 v4+) */ @@ -25,28 +25,40 @@ }, select2Def ), arry, data, - c = $cell.closest('table')[0].config, + // add class to $cell since it may point to a removed DOM node + // after a "refreshWidgets"; see #1237 + c = $cell.addClass('select2col' + indx).closest('table')[0].config, wo = c.widgetOptions, // Add a hidden input to hold the range values $input = $('<input class="filter" type="hidden">') .appendTo($cell) // hidden filter update namespace trigger by filter widget .bind('change' + c.namespace + 'filter', function(){ - var val = this.value; - val = val.replace(/[/()$^]/g, '').split('|'); - $cell.find('.select2').select2('val', val); + var val = convertRegex(this.value); + c.$table.find('.select2col' + indx + ' .select2').select2('val', val); updateSelect2(); }), $header = c.$headerIndexed[indx], onlyAvail = $header.hasClass(wo.filter_onlyAvail), - $shcell = [], matchPrefix = o.match ? '' : '^', matchSuffix = o.match ? '' : '$', + flags = wo.filter_ignoreCase ? 'i' : '', + + convertRegex = function(val) { + // value = '/(^x$|^y$)/' => ['x','y'] + return val + .replace(/^\/\(\^?/, '') + .replace(/\$\|\^/g, '|') + .replace(/\$?\)\/i?$/g, '') + // unescape special regex characters + .replace(/\\/g, '') + .split('|'); + }, // this function updates the hidden input and adds the current values to the header cell text updateSelect2 = function() { var arry = false, - v = $cell.find('.select2').select2('val') || o.value || ''; + v = c.$table.find('.select2col' + indx + ' .select2').select2('val') || o.value || ''; // convert array to string if ($.isArray(v)) { arry = true; @@ -60,12 +72,16 @@ } $input // add regex, so we filter exact numbers - .val( $.isArray(v) && v.length && v.join('') !== '' ? '/(' + matchPrefix + (v || []).join(matchSuffix + '|' + matchPrefix) + matchSuffix + ')/' : '' ) + .val( + $.isArray(v) && v.length && v.join('') !== '' ? + '/(' + matchPrefix + (v || []).join(matchSuffix + '|' + matchPrefix) + matchSuffix + ')/' + flags : + '' + ) .trigger('search').end() .find('.select2').select2('val', v); // update sticky header cell - if ($shcell.length) { - $shcell.find('.select2').select2('val', v); + if (c.widgetOptions.$sticky) { + c.widgetOptions.$sticky.find('.select2col' + indx + ' .select2').select2('val', v); } }, @@ -91,12 +107,13 @@ // data options are already defined if (!(o.ajax && !$.isEmptyObject(o.ajax)) && !o.data) { updateOptions(); - if (onlyAvail) { - c.$table.bind('filterEnd', function(){ - updateOptions(); - $cell.add($shcell).find('.select2').select2(o); - }); - } + c.$table.bind('filterEnd', function(){ + updateOptions(); + c.$table + .find('.select2col' + indx) + .add(c.widgetOptions.$sticky && c.widgetOptions.$sticky.find('.select2col' + indx)) + .find('.select2').select2(o); + }); } // add a select2 hidden input! @@ -109,10 +126,10 @@ }); // update select2 from filter hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ + c.$table.bind('filterFomatterUpdate', function() { // value = '/(^x$|^y$)/' => 'x,y' - var val = c.$table.data('lastSearch')[indx] || ''; - val = val.replace(/^\/\(\^?/, '').replace(/\$\|\^/g, '|').replace(/\$?\)\/$/g, '').split('|'); + var val = convertRegex(c.$table.data('lastSearch')[indx] || ''); + $cell = c.$table.find('.select2col' + indx); $cell.find('.select2').select2('val', val); updateSelect2(); ts.filter.formatterUpdated($cell, indx); @@ -120,25 +137,26 @@ // has sticky headers? c.$table.bind('stickyHeadersInit', function(){ - $shcell = c.widgetOptions.$sticky.find('.' + ts.css.filterRow).children().eq(indx).empty(); + var $shcell = c.widgetOptions.$sticky.find('.select2col' + indx).empty(); // add a select2! $('<input class="select2 select2-' + indx + '" type="hidden">') .val(o.value) .appendTo($shcell) .select2(o) .bind('change', function(){ - $cell.find('.select2').select2('val', $shcell.find('.select2').select2('val') ); + c.$table.find('.select2col' + indx) + .find('.select2') + .select2('val', c.widgetOptions.$sticky.find('.select2col' + indx + ' .select2').select2('val') ); updateSelect2(); }); if (o.cellText) { $shcell.prepend('<label>' + o.cellText + '</label>'); } - }); // on reset c.$table.bind('filterReset', function(){ - $cell.find('.select2').select2('val', o.value || ''); + c.$table.find('.select2col' + indx).find('.select2').select2('val', o.value || ''); setTimeout(function(){ updateSelect2(); }, 0); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 8d2d60a..be361e4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 6/28/2015 (v2.26.5) *//* +/*! Widget: filter - updated 7/11/2016 (v2.26.6) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -785,12 +785,13 @@ .attr( 'data-lastSearchTime', new Date().getTime() ) .unbind( tmp.replace( ts.regex.spaces, ' ' ) ) .bind( 'keydown' + namespace, function( event ) { - if ( event.which === tskeyCodes.escape && !wo.filter_resetOnEsc ) { + if ( event.which === tskeyCodes.escape && !table.config.widgetOptions.filter_resetOnEsc ) { // prevent keypress event return false; } }) .bind( 'keyup' + namespace, function( event ) { + wo = table.config.widgetOptions; // make sure "wo" isn't cached var column = parseInt( $( this ).attr( 'data-column' ), 10 ); $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); // emulate what webkit does.... escape clears the filter @@ -817,7 +818,8 @@ // don't get cached data, in case data-column changes dynamically var column = parseInt( $( this ).attr( 'data-column' ), 10 ); // don't allow 'change' event to process if the input value is the same - fixes #685 - if ( wo.filter_initialized && ( event.which === tskeyCodes.enter || event.type === 'search' || + if ( table.config.widgetOptions.filter_initialized && + ( event.which === tskeyCodes.enter || event.type === 'search' || ( event.type === 'change' ) && this.value !== c.lastSearch[column] ) || // only "input" event fires in MS Edge when clicking the "x" to clear the search ( event.type === 'input' && this.value === '' ) ) { @@ -866,7 +868,7 @@ // show/hide filter row as needed c.$table .find( '.' + tscss.filterRow ) - .triggerHandler( combinedFilters === '' ? 'mouseleave' : 'mouseenter' ); + .triggerHandler( tsf.hideFiltersCheck( c ) ? 'mouseleave' : 'mouseenter' ); } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons @@ -899,26 +901,34 @@ return false; } }, + hideFiltersCheck: function( c ) { + if (typeof c.widgetOptions.filter_hideFilters === 'function') { + var val = c.widgetOptions.filter_hideFilters( c ); + if (typeof val === 'boolean') { + return val; + } + } + return ts.getFilters( c.$table ).join( '' ) === ''; + }, hideFilters: function( c, $table ) { - var timer, - $row = ( $table || c.$table ).find( '.' + tscss.filterRow ).addClass( tscss.filterRowHide ); - $row + var timer; + ( $table || c.$table ) + .find( '.' + tscss.filterRow ) + .addClass( tscss.filterRowHide ) .bind( 'mouseenter mouseleave', function( e ) { // save event object - http://bugs.jquery.com/ticket/12140 var event = e, - $filterRow = $( this ); + $row = $( this ); clearTimeout( timer ); timer = setTimeout( function() { if ( /enter|over/.test( event.type ) ) { - $filterRow.removeClass( tscss.filterRowHide ); + $row.removeClass( tscss.filterRowHide ); } else { // don't hide if input has focus // $( ':focus' ) needs jQuery 1.6+ - if ( $( document.activeElement ).closest( 'tr' )[0] !== $filterRow[0] ) { + if ( $( document.activeElement ).closest( 'tr' )[0] !== $row[0] ) { // don't hide row if any filter has a value - if ( c.lastCombinedFilter === '' ) { - $filterRow.addClass( tscss.filterRowHide ); - } + $row.toggleClass( tscss.filterRowHide, tsf.hideFiltersCheck( c ) ); } } }, 200 ); @@ -930,9 +940,7 @@ timer = setTimeout( function() { clearTimeout( timer ); // don't hide row if any filter has a value - if ( ts.getFilters( c.$table ).join( '' ) === '' ) { - $row.toggleClass( tscss.filterRowHide, event.type !== 'focus' ); - } + $row.toggleClass( tscss.filterRowHide, tsf.hideFiltersCheck( c ) && event.type !== 'focus' ); }, 200 ); }); }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index 43713d9..d3d36c4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -1,4 +1,4 @@ -/*! Widget: grouping - updated 6/28/2015 (v2.26.5) *//* +/*! Widget: grouping - updated 7/11/2016 (v2.26.6) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -151,12 +151,10 @@ } } } - // save collapsed groups if ( wo.group_saveGroups && !$.isEmptyObject( wo.group_collapsedGroups ) && wo.group_collapsedGroups[ wo.group_collapsedGroup ].length ) { - name = $row.find( '.group-name' ).text().toLowerCase() + $row.attr( 'data-group-index' ); isHidden = $.inArray( name, wo.group_collapsedGroups[ wo.group_collapsedGroup ] ) > -1; $row.toggleClass( 'collapsed', isHidden ); @@ -172,7 +170,7 @@ var name = ( data.currentGroup || '' ).replace(/</g, '<').replace(/>/g, '>'); return '<tr class="group-header ' + c.selectorRemove.slice(1) + '" unselectable="on" ' + ( c.tabIndex ? 'tabindex="0" ' : '' ) + 'data-group-index="' + - ( data.groupIndex++ ) + '">' + + data.groupIndex + '">' + '<td colspan="' + c.columns + '">' + ( wo.group_collapsible ? '<i/>' : '' ) + '<span class="group-name">' + name + '</span>' + @@ -183,8 +181,8 @@ // save current grouping var saveName, direction, savedGroup = false; - if (wo.group_collapsible && wo.group_saveGroups && ts.storage) { - wo.group_collapsedGroups = ts.storage( c.table, 'tablesorter-groups' ) || {}; + if (wo.group_collapsible && wo.group_saveGroups) { + wo.group_collapsedGroups = ts.storage && ts.storage( c.table, 'tablesorter-groups' ) || {}; // include direction when saving groups (reversed numbers shows different range values) direction = 'dir' + c.sortList[0][1]; // combine column, sort direction & grouping as save key @@ -234,9 +232,10 @@ } data.$row.before( tsg.groupHeaderHTML( c, wo, data ) ); if ( wo.group_saveGroups && !data.savedGroup && wo.group_collapsed && wo.group_collapsible ) { - // all groups start collapsed - wo.group_collapsedGroups[ wo.group_collapsedGroup ].push( data.currentGroup ); + // all groups start collapsed; data.groupIndex is 1 more than the expected index. + wo.group_collapsedGroups[ wo.group_collapsedGroup ].push( data.currentGroup + data.groupIndex ); } + data.groupIndex++; } }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index e023642..d2cc049 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 6/28/2015 (v2.26.5) */ +/*! Widget: Pager - updated 7/11/2016 (v2.26.6) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -1085,7 +1085,7 @@ p.filteredRows = typeof tmp.filtered !== 'undefined' ? tmp.filtered : ( c.debug ? console.error('Pager: no initial filtered page set!') || 0 : 0 ); tsp.updatePageDisplay( c, false ); - } else { + } else if (p.initialized) { tsp.getAjax( c ); } } else if ( !p.ajax ) { @@ -1115,7 +1115,7 @@ parsePageSize: function( c, size, mode ) { var p = c.pager, s = parseInt( size, 10 ) || p.size || c.widgetOptions.pager_size || 10; - return /all/i.test( size ) || s === p.totalRows ? + return p.initialized && (/all/i.test( size ) || s === p.totalRows) ? // "get" to set `p.size` or "set" to set `p.$size.val()` 'all' : ( mode === 'get' ? s : p.size ); }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js index ad92f98..2cc80a3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js @@ -1,4 +1,4 @@ -/*! Widget: resizable - updated 6/28/2015 (v2.26.5) */ +/*! Widget: resizable - updated 6/28/2016 (v2.26.5) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index bb524d1..2dba042 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -1,4 +1,4 @@ -/*! Widget: scroller - updated 6/15/2016 (v2.26.3) *//* +/*! Widget: scroller - updated 7/11/2016 (v2.26.6) *//* Copyright (C) 2011 T. Connell & Associates, Inc. Dual-licensed under the MIT and GPL licenses @@ -370,6 +370,8 @@ $tableWrap = $table.parent(), $hdr = wo.scroller_$header, $foot = wo.scroller_$footer, + $win = $(window), + position = [ $win.scrollLeft(), $win.scrollTop() ], id = c.namespace.slice( 1 ) + 'tsscroller', // Hide other scrollers so we can resize $div = $( 'div.' + tscss.scrollerWrap + '[id!="' + id + '"]' ) @@ -459,6 +461,8 @@ .find( '.' + tscss.scrollerFixed ) .find( '.' + tscss.scrollerTable ) .scrollTop( wo.scroller_saved[1] ); + $win.scrollLeft( position[0] ); + $win.scrollTop( position[1] ); // update resizable widget handles setTimeout( function() { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js index 467fcfc..f76890d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js @@ -1,4 +1,4 @@ -/*! Widget: uitheme - updated 3/26/2015 (v2.21.3) */ +/*! Widget: uitheme - updated 7/11/2016 (v2.26.6) */ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; @@ -56,7 +56,7 @@ id: 'uitheme', priority: 10, format: function(table, c, wo) { - var i, hdr, icon, time, $header, $icon, $tfoot, $h, oldtheme, oldremove, oldIconRmv, hasOldTheme, + var i, tmp, hdr, icon, time, $header, $icon, $tfoot, $h, oldtheme, oldremove, oldIconRmv, hasOldTheme, themesAll = ts.themes, $table = c.$table.add( $( c.namespace + '_extra_table' ) ), $headers = c.$headers.add( $( c.namespace + '_extra_headers' ) ), @@ -123,10 +123,20 @@ .removeClass(hasOldTheme ? [ oldtheme.icons, oldIconRmv ].join(' ') : '') .addClass(themes.icons || ''); } - if ($table.hasClass('hasFilters')) { - $table.children('thead').children('.' + ts.css.filterRow) - .removeClass(hasOldTheme ? oldtheme.filterRow || '' : '') - .addClass(themes.filterRow || ''); + // filter widget initializes after uitheme + if (c.widgets.indexOf('filter') > -1) { + tmp = function() { + $table.children('thead').children('.' + ts.css.filterRow) + .removeClass(hasOldTheme ? oldtheme.filterRow || '' : '') + .addClass(themes.filterRow || ''); + }; + if (wo.filter_initialized) { + tmp(); + } else { + $table.one('filterInit', function() { + tmp(); + }); + } } } for (i = 0; i < c.columns; i++) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-view.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-view.js index 3cdc96d..e84e82c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-view.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-view.js @@ -1,4 +1,4 @@ -/* Widget: view (beta) - updated 10/31/2015 (v2.24.0) */ +/* Widget: view (beta) - updated 7/11/2016 (v2.26.6) */ /* By Justin F. Hallett (https://github.com/TheSin-) * Requires tablesorter v2.8+ and jQuery 1.7+ */ @@ -100,7 +100,10 @@ var content = $(v).html(); // Add 2 spans, one is dropped when using .html() var span = $('<span />').append($('<span/>', attrs).append(content)); - tmpl = tmpl.replace(reg, span.html()); + tmpl = tmpl.replace(new RegExp(reg, 'g'), span.html()); + + reg = '{col' + k + ':raw}'; + tmpl = tmpl.replace(new RegExp(reg, 'g'), $(v).text()); }); var $tmpl = $(tmpl); @@ -115,6 +118,7 @@ }); $(wo.view_container).append($container); + c.$table.triggerHandler('viewComplete'); }, removeView: function(c, wo) { @@ -151,7 +155,6 @@ c.$table.on('tablesorter-ready', function() { view.buildToolBar(c, wo); view.buildView(c, wo); - c.$table.triggerHandler('viewComplete'); }); }, diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index 0b8b48f..07db862 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -23,10 +23,17 @@ cursor: default; } +.tablesorter-bootstrap .tablesorter-header.sorter-false i.tablesorter-icon { + display: none; +} + .tablesorter-bootstrap .tablesorter-header-inner { position: relative; padding: 4px 18px 4px 4px; } +.tablesorter-bootstrap .sorter-false .tablesorter-header-inner { + padding: 4px; +} /* bootstrap uses <i> for icons */ .tablesorter-bootstrap .tablesorter-header i.tablesorter-icon { diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css index aed88a6..fad1ae9 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css @@ -34,11 +34,17 @@ .tablesorter-bootstrap .sorter-false { cursor: default; } +.tablesorter-bootstrap .tablesorter-header.sorter-false i.tablesorter-icon { + display: none; +} .tablesorter-bootstrap .tablesorter-header-inner { position: relative; padding: 4px 18px 4px 4px; } +.tablesorter-bootstrap .sorter-false .tablesorter-header-inner { + padding: 4px; +} /* bootstrap uses <i> for icons */ .tablesorter-bootstrap .tablesorter-header i.tablesorter-icon { diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css index 1e41a37..897ab11 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css @@ -79,10 +79,8 @@ cursor: default; } -.tablesorter-dropbox thead .sorter-false i.tablesorter-icon, -.tablesorter-dropbox thead .sorter-false:hover i.tablesorter-icon { - background-image: none; - padding: 4px; +.tablesorter-dropbox thead .sorter-false i.tablesorter-icon { + display: none; } /* tbody */ diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css index 0b75817..c92a37c 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css @@ -85,8 +85,7 @@ cursor: default; } .tablesorter-grey thead .sorter-false i.tablesorter-icon { - background-image: none; - padding: 4px; + display: none; } /* tfoot */ From 90319e711a9bac279af636ccf53608e1a5316b93 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 1 Aug 2016 20:48:24 +0200 Subject: [PATCH 092/138] Update tablesorter to latest version (2.27.1) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 4 +- tablesorter | 2 +- .../bootstrap-black-unsorted.png | Bin 281 -> 276 bytes .../jquery-tablesorter/metro-black-asc.png | Bin 161 -> 158 bytes .../jquery-tablesorter/metro-black-desc.png | Bin 172 -> 171 bytes .../jquery-tablesorter/metro-white-asc.png | Bin 168 -> 165 bytes .../jquery-tablesorter/metro-white-desc.png | Bin 179 -> 178 bytes .../jquery.tablesorter.combined.js | 106 ++++++----- .../jquery-tablesorter/jquery.tablesorter.js | 54 +++--- .../jquery.tablesorter.widgets.js | 52 +++--- .../widgets/widget-columnSelector.js | 86 ++++++--- .../widgets/widget-currentSort.js | 60 ++++++ .../widgets/widget-filter.js | 23 ++- .../widgets/widget-grouping.js | 6 +- .../jquery-tablesorter/widgets/widget-mark.js | 135 ++++++++++++++ .../jquery-tablesorter/widgets/widget-math.js | 39 ++-- .../widgets/widget-output.js | 8 +- .../widgets/widget-scroller.js | 6 +- .../widgets/widget-stickyHeaders.js | 15 +- .../widgets/widget-uitheme.js | 8 +- .../jquery-tablesorter/theme.bootstrap.css | 4 +- .../jquery-tablesorter/theme.bootstrap_2.css | 4 +- .../jquery-tablesorter/theme.materialize.css | 176 ++++++++++++++++++ 25 files changed, 633 insertions(+), 161 deletions(-) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-currentSort.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-mark.js create mode 100644 vendor/assets/stylesheets/jquery-tablesorter/theme.materialize.css diff --git a/CHANGELOG.md b/CHANGELOG.md index 545caf9..668892d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.22.0 (2016-08-01) + +* Upgrade tablesorter to v2.27.1 + #### v1.21.4 (2016-07-12) * Upgrade tablesorter to v2.26.6 diff --git a/README.md b/README.md index ce82f6a..318a45f 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.26.6 (7/11/2016), [documentation] +Current tablesorter version: 2.27.1 (7/31/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 70321fa..dc62b2c 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 - MINOR = 21 - TINY = 4 + MINOR = 22 + TINY = 0 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index b015da6..8df2059 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit b015da6bbc9c757ffc87a1cfa0c216dfd0ba9725 +Subproject commit 8df205914adb6ae68786685bb3e89902b0e66c19 diff --git a/vendor/assets/images/jquery-tablesorter/bootstrap-black-unsorted.png b/vendor/assets/images/jquery-tablesorter/bootstrap-black-unsorted.png index 433ee0226295ccb6d6bedee2765808c0eec4163d..3190f29accc3207a2f840733addbd8d284459361 100644 GIT binary patch delta 248 zcmV<U00;k>0+a%fB!AmUL_t(|0b)P~l9G~)KrAaQExj603mIr2424k`=p7oH9~fm| z!w@uqYhVU@wca8i018$xRA6tg2MCs1AO36H3((*==X{6VAH{3ufl(A~k|eogS!Pi@ zc?jH!$QdxtbBp51>6h?&36^E~Sq|tO9LMoZ!K$iW(=-`W2XXWcZrk=$6oswp`Z<o{ zo(#p4)06%A``z`Jrm5vz+?Nc+lhZqx$MA1@UtW*m$>|+D48yMLy1NcU<gFk%y@Tg@ yUiE!{&;ch?kenXQ8#GPx07iW<FfhhkL*N&RB95#K<%rDy0000<MNUMnLSTX{%5f+F delta 253 zcmV<Z00RG%0+|AkB!A#ZL_t(|0kx2|LBs$QhVdEVNrltk?oFh@VKr8uqNB#$p~?p7 z(bF$(?cF8$^+jG=`X`EFd-GIfSv~^MQC28Qk~FAk+S$m+NF7AumlcN$!#JThHa1oV z(fH))vY0MTPfu6V1LSd8Rn^<5xVpOf>3N<7qVdV&^7i)j@pkz5c-?WF57%{9@IW*^ zIbMr@vMe9=_4WDdV!wDG8lOBa^D(?zv|qj+MB|gk<>%+;`GbRlJA!as-yj;FJT6x% zm7bfMoAqdbm4Rq{a(ab<&CShs%3xWsFko5Md0DXmiXx7zGO4_P00000NkvXXu0mjf Dk>G;4 diff --git a/vendor/assets/images/jquery-tablesorter/metro-black-asc.png b/vendor/assets/images/jquery-tablesorter/metro-black-asc.png index 381ed1e0aec0baf1ae292b418b968774f2a3c004..61c4f8018b2f198b91d595c26b7dbb9eb24a602c 100644 GIT binary patch delta 129 zcmV-{0Dk|W0iFSnBzk2@L_t(|0gaI%5&$6x07(|J-Aujz_%?p#Q#=BJaC@>dTbyME zMgdk>LfsSUE$;4413TUji!f_{dmnJ|-mJpjV7kXRs@zjxx=+!`Llh?zWBBprD#T5I j{xQ(y!btIuB7@==rH?p;;?s*e00000NkvXXu0mjfxePe; delta 132 zcmV-~0DJ$Q0igkqBz<T}L_t(|0hQ1x5(F^_K+!R?qT{2nxlf-hH_K6&FHqe;rGM3P zS$QNKZYza3MVVm1+=3u&!cJ!OZc7~TyK5T0yRabPy9*Qd>lmB*olQNO)JP~Uy^>9e m7s4diASi1Xu3@X@F8u+ek2r-EH6ud+0000<MNUMnLSTZfbT^m) diff --git a/vendor/assets/images/jquery-tablesorter/metro-black-desc.png b/vendor/assets/images/jquery-tablesorter/metro-black-desc.png index b20631f2ebd7b14682780e83bb7d47489afcf9f2..fc2188c6091bc2e17267df12130876c8f3a34384 100644 GIT binary patch delta 143 zcmV;A0C4}T0jmL!B!6;AL_t(|0hP|N4Z|=D1yO+=YxkUhl?L%1$w1!3+jt0Xz+9n9 z!FQAcUVlxr0c$z>;CRfC=tIIF3-n1qes^Kh-<qp$C1L7Y3H;xIT6CZpPSg)4tc@HO xO1R){DsZDFH=>CWHAy_SqlO(zz3|3YA9mq0mecT^OaK4?00>D%PDHLkV1o0oKJ)+p delta 144 zcmV;B0B`@R0jvR#B!6>BL_t(|0hP`%4Z~0j2H_`fp0gW)rw1fFT0mhEHUSX^?hRhr z_K(t&zWs)1X}5CB=D5s|m@Q$D1?Gp4-<ZOpzcm-%Qo_Wy6!^alb<>7n*ilN`VWV># yDB*yQj{+y^!-*K8M14qHwxZgKTRrf^S1)$qGnS6YcN_o!00{s|MNUMnLSTX{*Fg9H diff --git a/vendor/assets/images/jquery-tablesorter/metro-white-asc.png b/vendor/assets/images/jquery-tablesorter/metro-white-asc.png index ca8c8e25daecaadb474f5c32c12f1987b8900dca..a850fbf1a985ce0039c6ca8907d1b96ec0981abe 100644 GIT binary patch delta 137 zcmV;40CxYV0i^+uB!6s4L_t(|0fmvV4FDks0Bs$>o|^XeaW?leVFjO0C@4;z<hr{A zw9%N^{Ae^Ft8)VVle@E1u$^87E5yvI_C6OT?_Hz3H<Ipwy#c-lhL08JApqfs5qPPq rto+LyDgSmv5U`x0k>r6AM=ZbMx@(GKv(RP$0000<MNUMnLIPldbGtpU delta 140 zcmV;70CWGP0jL3xB!6#7L_t(|0gaKj5x^i6#oRIgJ@sq753@0!(+JMl7U@nE`njSv z41-_6Pmr}R9VjQ8h4vB9E|^q6xaEtG>U)adx$iXMzL|R->KovBO84#->Ovd`F%C8r umBm-)Al=u7AP9KK9AqD|MTvu0o&n;zYl^h`8Oi_v002ovP6b4+LSTYsbv}Xs diff --git a/vendor/assets/images/jquery-tablesorter/metro-white-desc.png b/vendor/assets/images/jquery-tablesorter/metro-white-desc.png index cda73c170778147826980381a4fd108caf20f668..fc05607ef3a2334361b0ba3911f0aa7a3ff7038e 100644 GIT binary patch delta 150 zcmV;H0BQfT0kQ#*B!78HL_t(|0gaKd4FDksMK5;L_D(?MAntJsxyx;2<q94qnoxW} zlUMmjKA#7@-g}(DPau<!7v#x7U^T!(um+NnH_6JK4@8*TfpBhw-UsJa`1|<1S7BUI z_YR*YFDK}hnjtrV(9s6wd^`p+dnFVlm)qvv3Htj1K4fv$Xb+U|eE<Le07*qoM6N<$ Ef@bbQO#lD@ delta 151 zcmV;I0BHZR0kZ*+B!7BIL_t(|0gcf&5kMgf08oxCGdl`0Ap{@a5MFS^gav-@Y23Yy zbVt43dssQ*QJ-Ov4xoMZ3w#9N!b<^hkv^i-cVYyxZw1hO)9!ibzG?J4hP{`?Fe>K^ z)$qA4{Bui9_}msi9c|F~<00`GwI$@o=WN~Fv~t9o0X}4L))3}Gvs3^8002ovPDHLk FV1h1YL|Xs= diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 2e788c9..b15b715 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 07-11-2016 (v2.26.6)*/ +/*! tablesorter (FORK) - updated 07-31-2016 (v2.27.1)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -14,16 +14,16 @@ } else { factory(jQuery); } -}(function($) { +}(function(jQuery) { -/*! TableSorter (FORK) v2.26.6 *//* +/*! TableSorter (FORK) v2.27.1 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach * fork maintained by Rob Garrison * -* Examples and docs at: http://tablesorter.com +* Examples and original docs at: http://tablesorter.com * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html @@ -33,13 +33,14 @@ * @cat Plugins/Tablesorter * @author Christian Bach - christian.bach@polyester.se * @contributor Rob Garrison - https://github.com/Mottie/tablesorter +* @docs (fork) - https://mottie.github.io/tablesorter/docs/ */ /*jshint browser:true, jquery:true, unused:false, expr: true */ ;( function( $ ) { 'use strict'; var ts = $.tablesorter = { - version : '2.26.6', + version : '2.27.1', parsers : [], widgets : [], @@ -580,19 +581,19 @@ } column = parseInt( $elem.attr( 'data-column' ), 10 ); elem.column = column; - tmp = ts.getData( $elem, configHeaders, 'sortInitialOrder' ) || c.sortInitialOrder; + tmp = ts.getOrder( ts.getData( $elem, configHeaders, 'sortInitialOrder' ) || c.sortInitialOrder ); // this may get updated numerous times if there are multiple rows c.sortVars[ column ] = { count : -1, // set to -1 because clicking on the header automatically adds one - order: ts.getOrder( tmp ) ? - [ 1, 0, 2 ] : // desc, asc, unsorted - [ 0, 1, 2 ], // asc, desc, unsorted + order: tmp ? + ( c.sortReset ? [ 1, 0, 2 ] : [ 1, 0 ] ) : // desc, asc, unsorted + ( c.sortReset ? [ 0, 1, 2 ] : [ 0, 1 ] ), // asc, desc, unsorted lockedOrder : false }; tmp = ts.getData( $elem, configHeaders, 'lockedOrder' ) || false; if ( typeof tmp !== 'undefined' && tmp !== false ) { c.sortVars[ column ].lockedOrder = true; - c.sortVars[ column ].order = ts.getOrder( tmp ) ? [ 1, 1, 1 ] : [ 0, 0, 0 ]; + c.sortVars[ column ].order = ts.getOrder( tmp ) ? [ 1, 1 ] : [ 0, 0 ]; } // add cell to headerList c.headerList[ index ] = elem; @@ -900,6 +901,10 @@ /** Add the table data to main data array */ $row = $( $tbody[ tbodyIndex ].rows[ rowIndex ] ); cols = []; + // ignore "remove-me" rows + if ( $row.hasClass( c.selectorRemove.slice(1) ) ) { + continue; + } // if this is a child row, add it to the last row's children and continue to the next row // ignore child row class, if it is the first row if ( $row.hasClass( c.cssChildRow ) && rowIndex !== 0 ) { @@ -991,7 +996,8 @@ if ( c.debug ) { len = Math.min( 5, c.cache[ 0 ].normalized.length ); console[ console.group ? 'group' : 'log' ]( 'Building cache for ' + c.totalRows + - ' rows (showing ' + len + ' rows in log)' + ts.benchmark( cacheTime ) ); + ' rows (showing ' + len + ' rows in log) and ' + c.columns + ' columns' + + ts.benchmark( cacheTime ) ); val = {}; for ( colIndex = 0; colIndex < c.columns; colIndex++ ) { for ( cacheIndex = 0; cacheIndex < len; cacheIndex++ ) { @@ -1142,6 +1148,7 @@ setColumnAriaLabel : function( c, $header, nextSort ) { if ( $header.length ) { var column = parseInt( $header.attr( 'data-column' ), 10 ), + vars = c.sortVars[ column ], tmp = $header.hasClass( ts.css.sortAsc ) ? 'sortAsc' : $header.hasClass( ts.css.sortDesc ) ? 'sortDesc' : 'sortNone', @@ -1149,7 +1156,8 @@ if ( $header.hasClass( 'sorter-false' ) || nextSort === false ) { txt += ts.language.sortDisabled; } else { - nextSort = c.sortVars[ column ].order[ ( c.sortVars[ column ].count + 1 ) % ( c.sortReset ? 3 : 2 ) ]; + tmp = ( vars.count + 1 ) % vars.order.length; + nextSort = vars.order[ tmp ]; // if nextSort txt += ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ]; } @@ -1208,7 +1216,12 @@ // set order if not already defined - due to colspan header without associated header cell // adding this check prevents a javascript error if ( !c.sortVars[ col ].order ) { - order = c.sortVars[ col ].order = ts.getOrder( c.sortInitialOrder ) ? [ 1, 0, 2 ] : [ 0, 1, 2 ]; + if ( ts.getOrder( c.sortInitialOrder ) ) { + order = c.sortReset ? [ 1, 0, 2 ] : [ 1, 0 ]; + } else { + order = c.sortReset ? [ 0, 1, 2 ] : [ 0, 1 ]; + } + c.sortVars[ col ].order = order; c.sortVars[ col ].count = 0; } @@ -1225,12 +1238,12 @@ dir = primary || 0; break; case 'o' : - temp = order[ ( primary || 0 ) % ( c.sortReset ? 3 : 2 ) ]; + temp = order[ ( primary || 0 ) % order.length ]; // opposite of primary column; but resets if primary resets dir = temp === 0 ? 1 : temp === 1 ? 0 : 2; break; case 'n' : - dir = order[ ( ++c.sortVars[ col ].count ) % ( c.sortReset ? 3 : 2 ) ]; + dir = order[ ( ++c.sortVars[ col ].count ) % order.length ]; break; default : // ascending dir = 0; @@ -1240,7 +1253,7 @@ group = [ col, parseInt( dir, 10 ) || 0 ]; c.sortList[ c.sortList.length ] = group; dir = $.inArray( group[ 1 ], order ); // fixes issue #167 - c.sortVars[ col ].count = dir >= 0 ? dir : group[ 1 ] % ( c.sortReset ? 3 : 2 ); + c.sortVars[ col ].count = dir >= 0 ? dir : group[ 1 ] % order.length; } } }, @@ -1503,8 +1516,8 @@ // Only call sortStart if sorting is enabled c.$table.triggerHandler( 'sortStart', table ); // get current column sort order - c.sortVars[ col ].count = - event[ c.sortResetKey ] ? 2 : ( c.sortVars[ col ].count + 1 ) % ( c.sortReset ? 3 : 2 ); + tmp = ( c.sortVars[ col ].count + 1 ) % order.length; + c.sortVars[ col ].count = event[ c.sortResetKey ] ? 2 : tmp; // reset all sorts on non-current column - issue #30 if ( c.sortRestart ) { for ( headerIndx = 0; headerIndx < len; headerIndx++ ) { @@ -1599,7 +1612,7 @@ dir = tmp === 0 ? 1 : 0; break; case 'n' : - dir = ( tmp + 1 ) % ( c.sortReset ? 3 : 2 ); + dir = ( tmp + 1 ) % order.length; break; default: dir = 0; @@ -2117,7 +2130,7 @@ ▀████▀ ██ ██ ██████ ██ ██ ██ ██████ █████▀ */ benchmark : function( diff ) { - return ( ' ( ' + ( new Date().getTime() - diff.getTime() ) + 'ms )' ); + return ( ' (' + ( new Date().getTime() - diff.getTime() ) + ' ms)' ); }, // deprecated ts.log log : function() { @@ -2546,8 +2559,8 @@ // too many protocols to add them all https://en.wikipedia.org/wiki/URI_scheme // now, this regex can be updated before initialization - ts.regex.urlProtocolTest = /^(https?|ftp|file):\/\//; - ts.regex.urlProtocolReplace = /(https?|ftp|file):\/\//; + ts.regex.urlProtocolTest = /^(https?|ftp|file):\/\//; + ts.regex.urlProtocolReplace = /(https?|ftp|file):\/\/(www\.)?/; ts.addParser({ id : 'url', is : function( str ) { @@ -2556,7 +2569,6 @@ format : function( str ) { return str ? $.trim( str.replace( ts.regex.urlProtocolReplace, '' ) ) : str; }, - parsed : true, // filter widget flag type : 'text' }); @@ -2839,7 +2851,7 @@ })(jQuery, window, document); -/*! Widget: uitheme - updated 7/11/2016 (v2.26.6) */ +/*! Widget: uitheme - updated 7/31/2016 (v2.27.0) */ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; @@ -2878,9 +2890,9 @@ hover : 'ui-state-hover', // hover class // icon class names icons : 'ui-icon', // icon class added to the <i> in the header - iconSortNone : 'ui-icon-carat-2-n-s', // class name added to icon when column is not sorted - iconSortAsc : 'ui-icon-carat-1-n', // class name added to icon when column has ascending sort - iconSortDesc : 'ui-icon-carat-1-s', // class name added to icon when column has descending sort + iconSortNone : 'ui-icon-carat-2-n-s ui-icon-caret-2-n-s', // class name added to icon when column is not sorted + iconSortAsc : 'ui-icon-carat-1-n ui-icon-caret-1-n', // class name added to icon when column has ascending sort + iconSortDesc : 'ui-icon-carat-1-s ui-icon-caret-1-s', // class name added to icon when column has descending sort filterRow : '', footerRow : '', footerCells : '', @@ -3114,7 +3126,7 @@ })(jQuery); -/*! Widget: filter - updated 7/11/2016 (v2.26.6) *//* +/*! Widget: filter - updated 7/31/2016 (v2.27.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3210,7 +3222,7 @@ // regex used in filter 'check' functions - not for general use and not documented regex: { - regex : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // regex to test for regex + regex : /^\/((?:\\\/|[^\/])+)\/([migyu]{0,5})?$/, // regex to test for regex child : /tablesorter-childRow/, // child row class name; this gets updated in the script filtered : /filtered/, // filtered (hidden) row class name; updated in the script type : /undefined|number/, // check type @@ -3553,6 +3565,8 @@ // force a new search since content has changed c.lastCombinedFilter = null; c.lastSearch = []; + // update filterFormatters after update - Fixes #1237 + c.$table.triggerHandler( 'filterFomatterUpdate' ); } // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first // input ensures all inputs are updated when a search is triggered on the table @@ -3717,8 +3731,10 @@ count = 0, completed = function() { wo.filter_initialized = true; + // update lastSearch - it gets cleared often + c.lastSearch = c.$table.data( 'lastSearch' ); c.$table.triggerHandler( 'filterInit', c ); - tsf.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); + tsf.findRows( c.table, c.lastSearch || [] ); }; if ( $.isEmptyObject( wo.filter_formatter ) ) { completed(); @@ -4203,6 +4219,7 @@ fxn, ffxn, txt, wo = c.widgetOptions, showRow = true, + hasAnyMatchInput = wo.filter_$anyMatch && wo.filter_$anyMatch.length, // if wo.filter_$anyMatch data-column attribute is changed dynamically // we don't want to do an "anyMatch" search on one column using data @@ -4212,11 +4229,11 @@ tsf.multipleColumns( c, wo.filter_$anyMatch ) : []; data.$cells = data.$row.children(); - if ( data.anyMatchFlag && columnIndex.length > 1 || data.anyMatchFilter ) { + if ( data.anyMatchFlag && columnIndex.length > 1 || ( data.anyMatchFilter && !hasAnyMatchInput ) ) { data.anyMatch = true; data.isMatch = true; data.rowArray = data.$cells.map( function( i ) { - if ( $.inArray( i, columnIndex ) > -1 || data.anyMatchFilter ) { + if ( $.inArray( i, columnIndex ) > -1 || ( data.anyMatchFilter && !hasAnyMatchInput ) ) { if ( data.parsed[ i ] ) { txt = data.cacheArray[ i ]; } else { @@ -4506,6 +4523,7 @@ } data.$row = $rows.eq( rowIndex ); + data.rowIndex = rowIndex; data.cacheArray = norm_rows[ rowIndex ]; rowData = data.cacheArray[ c.columns ]; data.rawArray = rowData.raw; @@ -4861,7 +4879,7 @@ ts.getFilters = function( table, getRaw, setFilters, skipFirst ) { var i, $filters, $column, cols, - filters = false, + filters = [], c = table ? $( table )[0].config : '', wo = c ? c.widgetOptions : ''; if ( ( getRaw !== true && wo && !wo.filter_columnFilters ) || @@ -4927,9 +4945,6 @@ } } } - if ( filters.length === 0 ) { - filters = false; - } return filters; }; @@ -4947,12 +4962,12 @@ tsf.searching( c.table, filter, skipFirst ); c.$table.triggerHandler( 'filterFomatterUpdate' ); } - return !!valid; + return valid.length !== 0; }; })( jQuery ); -/*! Widget: stickyHeaders - updated 5/1/2016 (v2.26.0) *//* +/*! Widget: stickyHeaders - updated 7/31/2016 (v2.27.0) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -5015,10 +5030,11 @@ // ************************** ts.addWidget({ id: 'stickyHeaders', - priority: 60, // sticky widget must be initialized after the filter widget! + priority: 55, // sticky widget must be initialized after the filter widget! options: { stickyHeaders : '', // extra class name added to the sticky header row - stickyHeaders_attachTo : null, // jQuery selector or object to attach sticky header to + stickyHeaders_appendTo : null, // jQuery selector or object to phycially attach the sticky headers + stickyHeaders_attachTo : null, // jQuery selector or object to attach scroll listener to (overridden by xScroll & yScroll settings) stickyHeaders_xScroll : null, // jQuery selector or object to monitor horizontal scroll position (defaults: xScroll > attachTo > window) stickyHeaders_yScroll : null, // jQuery selector or object to monitor vertical scroll position (defaults: yScroll > attachTo > window) stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element @@ -5169,8 +5185,12 @@ ts.bindEvents(table, $stickyThead.children().children('.' + ts.css.header)); - // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. - $table.after( $stickyWrap ); + if (wo.stickyHeaders_appendTo) { + $(wo.stickyHeaders_appendTo).append( $stickyWrap ); + } else { + // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. + $table.after( $stickyWrap ); + } // onRenderHeader is defined, we need to do something about it (fixes #641) if (c.onRenderHeader) { @@ -5716,5 +5736,5 @@ })(jQuery); -return $.tablesorter; +return jQuery.tablesorter; })); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 95ce670..b67075f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,11 +1,11 @@ -/*! TableSorter (FORK) v2.26.6 *//* +/*! TableSorter (FORK) v2.27.1 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * * Copyright (c) 2007 Christian Bach * fork maintained by Rob Garrison * -* Examples and docs at: http://tablesorter.com +* Examples and original docs at: http://tablesorter.com * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html @@ -15,13 +15,14 @@ * @cat Plugins/Tablesorter * @author Christian Bach - christian.bach@polyester.se * @contributor Rob Garrison - https://github.com/Mottie/tablesorter +* @docs (fork) - https://mottie.github.io/tablesorter/docs/ */ /*jshint browser:true, jquery:true, unused:false, expr: true */ ;( function( $ ) { 'use strict'; var ts = $.tablesorter = { - version : '2.26.6', + version : '2.27.1', parsers : [], widgets : [], @@ -562,19 +563,19 @@ } column = parseInt( $elem.attr( 'data-column' ), 10 ); elem.column = column; - tmp = ts.getData( $elem, configHeaders, 'sortInitialOrder' ) || c.sortInitialOrder; + tmp = ts.getOrder( ts.getData( $elem, configHeaders, 'sortInitialOrder' ) || c.sortInitialOrder ); // this may get updated numerous times if there are multiple rows c.sortVars[ column ] = { count : -1, // set to -1 because clicking on the header automatically adds one - order: ts.getOrder( tmp ) ? - [ 1, 0, 2 ] : // desc, asc, unsorted - [ 0, 1, 2 ], // asc, desc, unsorted + order: tmp ? + ( c.sortReset ? [ 1, 0, 2 ] : [ 1, 0 ] ) : // desc, asc, unsorted + ( c.sortReset ? [ 0, 1, 2 ] : [ 0, 1 ] ), // asc, desc, unsorted lockedOrder : false }; tmp = ts.getData( $elem, configHeaders, 'lockedOrder' ) || false; if ( typeof tmp !== 'undefined' && tmp !== false ) { c.sortVars[ column ].lockedOrder = true; - c.sortVars[ column ].order = ts.getOrder( tmp ) ? [ 1, 1, 1 ] : [ 0, 0, 0 ]; + c.sortVars[ column ].order = ts.getOrder( tmp ) ? [ 1, 1 ] : [ 0, 0 ]; } // add cell to headerList c.headerList[ index ] = elem; @@ -882,6 +883,10 @@ /** Add the table data to main data array */ $row = $( $tbody[ tbodyIndex ].rows[ rowIndex ] ); cols = []; + // ignore "remove-me" rows + if ( $row.hasClass( c.selectorRemove.slice(1) ) ) { + continue; + } // if this is a child row, add it to the last row's children and continue to the next row // ignore child row class, if it is the first row if ( $row.hasClass( c.cssChildRow ) && rowIndex !== 0 ) { @@ -973,7 +978,8 @@ if ( c.debug ) { len = Math.min( 5, c.cache[ 0 ].normalized.length ); console[ console.group ? 'group' : 'log' ]( 'Building cache for ' + c.totalRows + - ' rows (showing ' + len + ' rows in log)' + ts.benchmark( cacheTime ) ); + ' rows (showing ' + len + ' rows in log) and ' + c.columns + ' columns' + + ts.benchmark( cacheTime ) ); val = {}; for ( colIndex = 0; colIndex < c.columns; colIndex++ ) { for ( cacheIndex = 0; cacheIndex < len; cacheIndex++ ) { @@ -1124,6 +1130,7 @@ setColumnAriaLabel : function( c, $header, nextSort ) { if ( $header.length ) { var column = parseInt( $header.attr( 'data-column' ), 10 ), + vars = c.sortVars[ column ], tmp = $header.hasClass( ts.css.sortAsc ) ? 'sortAsc' : $header.hasClass( ts.css.sortDesc ) ? 'sortDesc' : 'sortNone', @@ -1131,7 +1138,8 @@ if ( $header.hasClass( 'sorter-false' ) || nextSort === false ) { txt += ts.language.sortDisabled; } else { - nextSort = c.sortVars[ column ].order[ ( c.sortVars[ column ].count + 1 ) % ( c.sortReset ? 3 : 2 ) ]; + tmp = ( vars.count + 1 ) % vars.order.length; + nextSort = vars.order[ tmp ]; // if nextSort txt += ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ]; } @@ -1190,7 +1198,12 @@ // set order if not already defined - due to colspan header without associated header cell // adding this check prevents a javascript error if ( !c.sortVars[ col ].order ) { - order = c.sortVars[ col ].order = ts.getOrder( c.sortInitialOrder ) ? [ 1, 0, 2 ] : [ 0, 1, 2 ]; + if ( ts.getOrder( c.sortInitialOrder ) ) { + order = c.sortReset ? [ 1, 0, 2 ] : [ 1, 0 ]; + } else { + order = c.sortReset ? [ 0, 1, 2 ] : [ 0, 1 ]; + } + c.sortVars[ col ].order = order; c.sortVars[ col ].count = 0; } @@ -1207,12 +1220,12 @@ dir = primary || 0; break; case 'o' : - temp = order[ ( primary || 0 ) % ( c.sortReset ? 3 : 2 ) ]; + temp = order[ ( primary || 0 ) % order.length ]; // opposite of primary column; but resets if primary resets dir = temp === 0 ? 1 : temp === 1 ? 0 : 2; break; case 'n' : - dir = order[ ( ++c.sortVars[ col ].count ) % ( c.sortReset ? 3 : 2 ) ]; + dir = order[ ( ++c.sortVars[ col ].count ) % order.length ]; break; default : // ascending dir = 0; @@ -1222,7 +1235,7 @@ group = [ col, parseInt( dir, 10 ) || 0 ]; c.sortList[ c.sortList.length ] = group; dir = $.inArray( group[ 1 ], order ); // fixes issue #167 - c.sortVars[ col ].count = dir >= 0 ? dir : group[ 1 ] % ( c.sortReset ? 3 : 2 ); + c.sortVars[ col ].count = dir >= 0 ? dir : group[ 1 ] % order.length; } } }, @@ -1485,8 +1498,8 @@ // Only call sortStart if sorting is enabled c.$table.triggerHandler( 'sortStart', table ); // get current column sort order - c.sortVars[ col ].count = - event[ c.sortResetKey ] ? 2 : ( c.sortVars[ col ].count + 1 ) % ( c.sortReset ? 3 : 2 ); + tmp = ( c.sortVars[ col ].count + 1 ) % order.length; + c.sortVars[ col ].count = event[ c.sortResetKey ] ? 2 : tmp; // reset all sorts on non-current column - issue #30 if ( c.sortRestart ) { for ( headerIndx = 0; headerIndx < len; headerIndx++ ) { @@ -1581,7 +1594,7 @@ dir = tmp === 0 ? 1 : 0; break; case 'n' : - dir = ( tmp + 1 ) % ( c.sortReset ? 3 : 2 ); + dir = ( tmp + 1 ) % order.length; break; default: dir = 0; @@ -2099,7 +2112,7 @@ ▀████▀ ██ ██ ██████ ██ ██ ██ ██████ █████▀ */ benchmark : function( diff ) { - return ( ' ( ' + ( new Date().getTime() - diff.getTime() ) + 'ms )' ); + return ( ' (' + ( new Date().getTime() - diff.getTime() ) + ' ms)' ); }, // deprecated ts.log log : function() { @@ -2528,8 +2541,8 @@ // too many protocols to add them all https://en.wikipedia.org/wiki/URI_scheme // now, this regex can be updated before initialization - ts.regex.urlProtocolTest = /^(https?|ftp|file):\/\//; - ts.regex.urlProtocolReplace = /(https?|ftp|file):\/\//; + ts.regex.urlProtocolTest = /^(https?|ftp|file):\/\//; + ts.regex.urlProtocolReplace = /(https?|ftp|file):\/\/(www\.)?/; ts.addParser({ id : 'url', is : function( str ) { @@ -2538,7 +2551,6 @@ format : function( str ) { return str ? $.trim( str.replace( ts.regex.urlProtocolReplace, '' ) ) : str; }, - parsed : true, // filter widget flag type : 'text' }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 5261053..1cc03cc 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 07-11-2016 (v2.26.6)*/ +/*! tablesorter (FORK) - updated 07-31-2016 (v2.27.1)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -14,7 +14,7 @@ } else { factory(jQuery); } -}(function($) { +}(function(jQuery) { /*! Widget: storage - updated 3/1/2016 (v2.25.5) */ /*global JSON:false */ @@ -107,7 +107,7 @@ })(jQuery, window, document); -/*! Widget: uitheme - updated 7/11/2016 (v2.26.6) */ +/*! Widget: uitheme - updated 7/31/2016 (v2.27.0) */ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; @@ -146,9 +146,9 @@ hover : 'ui-state-hover', // hover class // icon class names icons : 'ui-icon', // icon class added to the <i> in the header - iconSortNone : 'ui-icon-carat-2-n-s', // class name added to icon when column is not sorted - iconSortAsc : 'ui-icon-carat-1-n', // class name added to icon when column has ascending sort - iconSortDesc : 'ui-icon-carat-1-s', // class name added to icon when column has descending sort + iconSortNone : 'ui-icon-carat-2-n-s ui-icon-caret-2-n-s', // class name added to icon when column is not sorted + iconSortAsc : 'ui-icon-carat-1-n ui-icon-caret-1-n', // class name added to icon when column has ascending sort + iconSortDesc : 'ui-icon-carat-1-s ui-icon-caret-1-s', // class name added to icon when column has descending sort filterRow : '', footerRow : '', footerCells : '', @@ -382,7 +382,7 @@ })(jQuery); -/*! Widget: filter - updated 7/11/2016 (v2.26.6) *//* +/*! Widget: filter - updated 7/31/2016 (v2.27.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -478,7 +478,7 @@ // regex used in filter 'check' functions - not for general use and not documented regex: { - regex : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // regex to test for regex + regex : /^\/((?:\\\/|[^\/])+)\/([migyu]{0,5})?$/, // regex to test for regex child : /tablesorter-childRow/, // child row class name; this gets updated in the script filtered : /filtered/, // filtered (hidden) row class name; updated in the script type : /undefined|number/, // check type @@ -821,6 +821,8 @@ // force a new search since content has changed c.lastCombinedFilter = null; c.lastSearch = []; + // update filterFormatters after update - Fixes #1237 + c.$table.triggerHandler( 'filterFomatterUpdate' ); } // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first // input ensures all inputs are updated when a search is triggered on the table @@ -985,8 +987,10 @@ count = 0, completed = function() { wo.filter_initialized = true; + // update lastSearch - it gets cleared often + c.lastSearch = c.$table.data( 'lastSearch' ); c.$table.triggerHandler( 'filterInit', c ); - tsf.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); + tsf.findRows( c.table, c.lastSearch || [] ); }; if ( $.isEmptyObject( wo.filter_formatter ) ) { completed(); @@ -1471,6 +1475,7 @@ fxn, ffxn, txt, wo = c.widgetOptions, showRow = true, + hasAnyMatchInput = wo.filter_$anyMatch && wo.filter_$anyMatch.length, // if wo.filter_$anyMatch data-column attribute is changed dynamically // we don't want to do an "anyMatch" search on one column using data @@ -1480,11 +1485,11 @@ tsf.multipleColumns( c, wo.filter_$anyMatch ) : []; data.$cells = data.$row.children(); - if ( data.anyMatchFlag && columnIndex.length > 1 || data.anyMatchFilter ) { + if ( data.anyMatchFlag && columnIndex.length > 1 || ( data.anyMatchFilter && !hasAnyMatchInput ) ) { data.anyMatch = true; data.isMatch = true; data.rowArray = data.$cells.map( function( i ) { - if ( $.inArray( i, columnIndex ) > -1 || data.anyMatchFilter ) { + if ( $.inArray( i, columnIndex ) > -1 || ( data.anyMatchFilter && !hasAnyMatchInput ) ) { if ( data.parsed[ i ] ) { txt = data.cacheArray[ i ]; } else { @@ -1774,6 +1779,7 @@ } data.$row = $rows.eq( rowIndex ); + data.rowIndex = rowIndex; data.cacheArray = norm_rows[ rowIndex ]; rowData = data.cacheArray[ c.columns ]; data.rawArray = rowData.raw; @@ -2129,7 +2135,7 @@ ts.getFilters = function( table, getRaw, setFilters, skipFirst ) { var i, $filters, $column, cols, - filters = false, + filters = [], c = table ? $( table )[0].config : '', wo = c ? c.widgetOptions : ''; if ( ( getRaw !== true && wo && !wo.filter_columnFilters ) || @@ -2195,9 +2201,6 @@ } } } - if ( filters.length === 0 ) { - filters = false; - } return filters; }; @@ -2215,12 +2218,12 @@ tsf.searching( c.table, filter, skipFirst ); c.$table.triggerHandler( 'filterFomatterUpdate' ); } - return !!valid; + return valid.length !== 0; }; })( jQuery ); -/*! Widget: stickyHeaders - updated 5/1/2016 (v2.26.0) *//* +/*! Widget: stickyHeaders - updated 7/31/2016 (v2.27.0) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -2283,10 +2286,11 @@ // ************************** ts.addWidget({ id: 'stickyHeaders', - priority: 60, // sticky widget must be initialized after the filter widget! + priority: 55, // sticky widget must be initialized after the filter widget! options: { stickyHeaders : '', // extra class name added to the sticky header row - stickyHeaders_attachTo : null, // jQuery selector or object to attach sticky header to + stickyHeaders_appendTo : null, // jQuery selector or object to phycially attach the sticky headers + stickyHeaders_attachTo : null, // jQuery selector or object to attach scroll listener to (overridden by xScroll & yScroll settings) stickyHeaders_xScroll : null, // jQuery selector or object to monitor horizontal scroll position (defaults: xScroll > attachTo > window) stickyHeaders_yScroll : null, // jQuery selector or object to monitor vertical scroll position (defaults: yScroll > attachTo > window) stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element @@ -2437,8 +2441,12 @@ ts.bindEvents(table, $stickyThead.children().children('.' + ts.css.header)); - // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. - $table.after( $stickyWrap ); + if (wo.stickyHeaders_appendTo) { + $(wo.stickyHeaders_appendTo).append( $stickyWrap ); + } else { + // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. + $table.after( $stickyWrap ); + } // onRenderHeader is defined, we need to do something about it (fixes #641) if (c.onRenderHeader) { @@ -2984,5 +2992,5 @@ })(jQuery); -return $.tablesorter; +return jQuery.tablesorter; })); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index fb4fda7..eced613 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Widget: columnSelector (responsive table widget) - updated 7/11/2016 (v2.26.6) *//* +/* Widget: columnSelector (responsive table widget) - updated 7/31/2016 (v2.27.1) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -72,14 +72,14 @@ isArry = $.isArray(optState || optName), wo = c.widgetOptions; // see #798 - if (typeof optName !== 'undefined' && colSel.$container.length) { + if (typeof optName !== 'undefined' && optName !== null && colSel.$container.length) { // pass "selectors" to update the all of the container contents if ( optName === 'selectors' ) { colSel.$container.empty(); tsColSel.setupSelector(c, wo); tsColSel.setupBreakpoints(c, wo); // if optState is undefined, maintain the current "auto" state - if ( typeof optState === 'undefined' ) { + if ( typeof optState === 'undefined' && optState !== null ) { optState = colSel.auto; } } @@ -140,12 +140,13 @@ // include getData check (includes 'columnSelector-false' class, data attribute, etc) if ( isNaN(priority) && priority.length > 0 || state === 'disable' || ( wo.columnSelector_columns[colId] && wo.columnSelector_columns[colId] === 'disable') ) { + colSel.states[colId] = null; continue; // goto next } // set default state; storage takes priority - colSel.states[colId] = saved && typeof saved[colId] !== 'undefined' ? - saved[colId] : typeof wo.columnSelector_columns[colId] !== 'undefined' ? + colSel.states[colId] = saved && (typeof saved[colId] !== 'undefined' && saved[colId] !== null) ? + saved[colId] : (typeof wo.columnSelector_columns[colId] !== 'undefined' && wo.columnSelector_columns[colId] !== null) ? wo.columnSelector_columns[colId] : (state === 'true' || state !== 'false'); colSel.$column[colId] = $(this); @@ -159,17 +160,44 @@ .attr('data-column', colId) .toggleClass( wo.columnSelector_cssChecked, colSel.states[colId] ) .prop('checked', colSel.states[colId]) - .on('change', function(){ - // ensure states is accurate - var colId = $(this).attr('data-column'); - c.selector.states[colId] = this.checked; - tsColSel.updateCols(c, wo); + .on('change', function() { + if (!colSel.isInitializing) { + // ensure states is accurate + var colId = $(this).attr('data-column'); + if (tsColSel.checkChange(c, this.checked)) { + // if (wo.columnSelector_maxVisible) + c.selector.states[colId] = this.checked; + tsColSel.updateCols(c, wo); + } else { + this.checked = !this.checked; + return false; + } + } }).change(); } } }, + checkChange: function(c, checked) { + var wo = c.widgetOptions, + max = wo.columnSelector_maxVisible, + min = wo.columnSelector_minVisible, + states = c.selector.states, + indx = states.length, + count = 0; + while (indx-- >= 0) { + if (states[indx]) { + count++; + } + } + if ((checked & max !== null && count >= max) || + (!checked && min !== null && count <= min)) { + return false; + } + return true; + }, + setupBreakpoints: function(c, wo) { var colSel = c.selector; @@ -388,7 +416,7 @@ } else { $cell.addClass( filtered ); } - } else if ( typeof colSel.states[ col ] !== 'undefined' ) { + } else if ( typeof colSel.states[ col ] !== 'undefined' && colSel.states[ col ] !== null ) { $cell.toggleClass( filtered, !colSel.states[ col ] ); } } @@ -423,13 +451,20 @@ .toggleClass( wo.columnSelector_cssChecked, isChecked ) .prop( 'checked', isChecked ); }); - colSel.$popup = $popup.on('change', 'input', function(){ - // data input - indx = $(this).toggleClass( wo.columnSelector_cssChecked, this.checked ).attr('data-column'); - // update original popup - colSel.$container.find('input[data-column="' + indx + '"]') - .prop('checked', this.checked) - .trigger('change'); + colSel.$popup = $popup.on('change', 'input', function() { + if (!colSel.isInitializing) { + if (tsColSel.checkChange(c, this.checked)) { + // data input + indx = $(this).toggleClass( wo.columnSelector_cssChecked, this.checked ).attr('data-column'); + // update original popup + colSel.$container.find('input[data-column="' + indx + '"]') + .prop('checked', this.checked) + .trigger('change'); + } else { + this.checked = !this.checked; + return false; + } + } }); } } @@ -461,17 +496,21 @@ // container layout columnSelector_layout : '<label><input type="checkbox">{name}</label>', // data attribute containing column name to use in the selector container - columnSelector_name : 'data-selector-name', + columnSelector_name : 'data-selector-name', /* Responsive Media Query settings */ // enable/disable mediaquery breakpoints - columnSelector_mediaquery: true, + columnSelector_mediaquery : true, // toggle checkbox name - columnSelector_mediaqueryName: 'Auto: ', + columnSelector_mediaqueryName : 'Auto: ', // breakpoints checkbox initial setting - columnSelector_mediaqueryState: true, + columnSelector_mediaqueryState : true, // hide columnSelector false columns while in auto mode - columnSelector_mediaqueryHidden: false, + columnSelector_mediaqueryHidden : false, + // set the maximum and/or minimum number of visible columns + columnSelector_maxVisible : null, + columnSelector_minVisible : null, + // responsive table hides columns with priority 1-6 at these breakpoints // see http://view.jquerymobile.com/1.3.2/dist/demos/widgets/table-column-toggle/#Applyingapresetbreakpoint // *** set to false to disable *** @@ -488,7 +527,6 @@ columnSelector_cssChecked : 'checked', // event triggered when columnSelector completes columnSelector_updated : 'columnUpdate' - }, init: function(table, thisWidget, c, wo) { tsColSel.init(table, c, wo); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-currentSort.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-currentSort.js new file mode 100644 index 0000000..f4e02f3 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-currentSort.js @@ -0,0 +1,60 @@ +/*! Widget: currentSort - 7/31/2016 (v2.27.0) *//* + * Requires tablesorter v2.8+ and jQuery 1.7+ + * by Rob Garrison + */ +;( function( $ ) { + 'use strict'; + var ts = $.tablesorter; + + ts.currentSortLanguage = { + 0: 'asc', + 1: 'desc', + 2: 'unsorted' + }; + + ts.currentSort = { + init : function( c ) { + c.$table.on( 'sortEnd.tscurrentSort', function() { + ts.currentSort.update( this.config ); + }); + }, + update: function( c ) { + if ( c ) { + var indx, + wo = c.widgetOptions, + lang = ts.currentSortLanguage, + unsort = lang[ 2 ], + // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill + // order = new Array(c.columns).fill(unsort), + // the above ES6 will not work in all browsers, so + // we're stuck with this messy code to fill the array: + order = Array + .apply( null, Array( c.columns ) ) + .map( String.prototype.valueOf, unsort ), + sortList = c.sortList, + len = sortList.length; + for ( indx = 0; indx < len; indx++ ) { + order[ sortList[ indx ][ 0 ] ] = lang[ sortList[ indx ][ 1 ] ]; + } + c.currentSort = order; + if ( typeof wo.currentSort_callback === 'function' ) { + wo.currentSort_callback(c, order); + } + } + } + }; + + ts.addWidget({ + id: 'currentSort', + options: { + currentSort_callback : null + }, + init : function( table, thisWidget, c, wo ) { + ts.currentSort.init( c, wo ); + }, + remove : function( table, c ) { + c.$table.off( 'sortEnd.tscurrentSort' ); + } + }); + +})( jQuery ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index be361e4..c8c85df 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 7/11/2016 (v2.26.6) *//* +/*! Widget: filter - updated 7/31/2016 (v2.27.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -94,7 +94,7 @@ // regex used in filter 'check' functions - not for general use and not documented regex: { - regex : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // regex to test for regex + regex : /^\/((?:\\\/|[^\/])+)\/([migyu]{0,5})?$/, // regex to test for regex child : /tablesorter-childRow/, // child row class name; this gets updated in the script filtered : /filtered/, // filtered (hidden) row class name; updated in the script type : /undefined|number/, // check type @@ -437,6 +437,8 @@ // force a new search since content has changed c.lastCombinedFilter = null; c.lastSearch = []; + // update filterFormatters after update - Fixes #1237 + c.$table.triggerHandler( 'filterFomatterUpdate' ); } // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first // input ensures all inputs are updated when a search is triggered on the table @@ -601,8 +603,10 @@ count = 0, completed = function() { wo.filter_initialized = true; + // update lastSearch - it gets cleared often + c.lastSearch = c.$table.data( 'lastSearch' ); c.$table.triggerHandler( 'filterInit', c ); - tsf.findRows( c.table, c.$table.data( 'lastSearch' ) || [] ); + tsf.findRows( c.table, c.lastSearch || [] ); }; if ( $.isEmptyObject( wo.filter_formatter ) ) { completed(); @@ -1087,6 +1091,7 @@ fxn, ffxn, txt, wo = c.widgetOptions, showRow = true, + hasAnyMatchInput = wo.filter_$anyMatch && wo.filter_$anyMatch.length, // if wo.filter_$anyMatch data-column attribute is changed dynamically // we don't want to do an "anyMatch" search on one column using data @@ -1096,11 +1101,11 @@ tsf.multipleColumns( c, wo.filter_$anyMatch ) : []; data.$cells = data.$row.children(); - if ( data.anyMatchFlag && columnIndex.length > 1 || data.anyMatchFilter ) { + if ( data.anyMatchFlag && columnIndex.length > 1 || ( data.anyMatchFilter && !hasAnyMatchInput ) ) { data.anyMatch = true; data.isMatch = true; data.rowArray = data.$cells.map( function( i ) { - if ( $.inArray( i, columnIndex ) > -1 || data.anyMatchFilter ) { + if ( $.inArray( i, columnIndex ) > -1 || ( data.anyMatchFilter && !hasAnyMatchInput ) ) { if ( data.parsed[ i ] ) { txt = data.cacheArray[ i ]; } else { @@ -1390,6 +1395,7 @@ } data.$row = $rows.eq( rowIndex ); + data.rowIndex = rowIndex; data.cacheArray = norm_rows[ rowIndex ]; rowData = data.cacheArray[ c.columns ]; data.rawArray = rowData.raw; @@ -1745,7 +1751,7 @@ ts.getFilters = function( table, getRaw, setFilters, skipFirst ) { var i, $filters, $column, cols, - filters = false, + filters = [], c = table ? $( table )[0].config : '', wo = c ? c.widgetOptions : ''; if ( ( getRaw !== true && wo && !wo.filter_columnFilters ) || @@ -1811,9 +1817,6 @@ } } } - if ( filters.length === 0 ) { - filters = false; - } return filters; }; @@ -1831,7 +1834,7 @@ tsf.searching( c.table, filter, skipFirst ); c.$table.triggerHandler( 'filterFomatterUpdate' ); } - return !!valid; + return valid.length !== 0; }; })( jQuery ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index d3d36c4..b0b5c60 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -1,4 +1,4 @@ -/*! Widget: grouping - updated 7/11/2016 (v2.26.6) *//* +/*! Widget: grouping - updated 7/31/2016 (v2.27.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -144,7 +144,7 @@ $label = $row.find( '.group-count' ); if ( $label.length ) { if ( wo.group_count ) { - $label.html( wo.group_count.replace( /\{num\}/g, $rows.length ) ); + $label.html( wo.group_count.toString().replace( /\{num\}/g, $rows.length ) ); } if ( $.isFunction( wo.group_callback ) ) { wo.group_callback( $row.find( 'td' ), $rows, data.column, c.table ); @@ -167,7 +167,7 @@ }, groupHeaderHTML : function( c, wo, data ) { - var name = ( data.currentGroup || '' ).replace(/</g, '<').replace(/>/g, '>'); + var name = ( data.currentGroup || '' ).toString().replace(/</g, '<').replace(/>/g, '>'); return '<tr class="group-header ' + c.selectorRemove.slice(1) + '" unselectable="on" ' + ( c.tabIndex ? 'tabindex="0" ' : '' ) + 'data-group-index="' + data.groupIndex + '">' + diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-mark.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-mark.js new file mode 100644 index 0000000..bc0403e --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-mark.js @@ -0,0 +1,135 @@ +/*! Widget: mark.js - updated 7/31/2016 (v2.27.1) *//* + * Requires tablesorter v2.8+ and jQuery 1.7+ + * by Rob Garrison + */ +;( function( $ ) { + 'use strict'; + var ts = $.tablesorter; + + ts.mark = { + init : function( c ) { + if ( typeof $.fn.mark === 'function' ) { + var tmp, + update = c.widgetOptions.mark_tsUpdate; + c.$table.on( 'filterEnd.tsmark' + ( update ? ' ' + update : '' ), function( e, filters ) { + // filterEnd passes "config" as the param + ts.mark.update( c, e.type === 'filterEnd' ? '' : filters ); + }); + // Regex to split up a query + tmp = '(?:<|=|>|\\||\"|' + "\\'|" + + '\\s+(?:&&|-|' + + ( ts.language.and || 'and' ) + '|' + + ( ts.language.or || 'or' ) + '|' + + ( ts.language.to || 'to' ) + ')\\s+)'; + ts.mark.regex.filter = new RegExp(tmp, 'gim'); + } else { + console.warn('Widget-mark not initialized: missing "jquery.mark.js"'); + } + }, + regex : { + mark : /^mark_(.+)$/, + // test for regex (e.g. "/(lorem|ipsum)/gi") + pure : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/ + }, + checkRegex : function( regex ) { + if ( regex instanceof RegExp ) { + // prevent lock up of mark.js (see https://github.com/julmot/mark.js/issues/55) + var result = '\u0001\u0002\u0003\u0004\u0005'.match( regex ); + return result === null || result.length < 5; + } + return false; + }, + cleanMatches : function( matches ) { + var results = [], + indx = matches && matches.length || 0; + while ( indx-- ) { + if ( matches[indx] !== '' ) { + results[ results.length ] = matches[ indx ]; + } + } + return results; + }, + update : function( c, filters ) { + var options = {}, + regex = ts.mark.regex, + $rows = c.$table + .find( 'tbody tr' ) + .unmark() + .not( '.' + ( c.widgetOptions.filter_filteredRow || 'filtered' ) ); + filters = filters || $.tablesorter.getFilters( c.$table ); + // extract & save mark options from widgetOptions (prefixed with "mark_") + // update dynamically + $.each( filters, function( indx, filter ) { + if ( filter ) { + var testRegex = null, + matches = filter, + useRegex = false, + col = indx === c.columns ? '' : ':nth-child(' + ( indx + 1 ) + ')'; + // regular expression entered + if ( regex.pure.test( filter ) ) { + matches = regex.pure.exec( filter ); + // ignore "all" matches (i.e. /.*/) + if (matches[1] === '.*') { + matches[1] = ''; + } + try { + // make sure to include global flag when testing regex + testRegex = new RegExp( matches[ 1 ], 'gim' ); + matches = new RegExp( matches[ 1 ], matches[ 2 ] ); + } catch (err) { + matches = null; + } + if ( ts.mark.checkRegex( testRegex ) ) { + $rows.children( col ).markRegExp( matches, options ); + } + // matches is either null, invalid, or done my markRegExp + return; + } + // all special querys (or, and, wild cards & fuzzy) + // regex seems to not be consistent here; so use string indexOf + // fuzzy or wild card matches + if ( filter.indexOf( '~' ) === 0 ) { + useRegex = true; + // fuzzy search separate each letter + matches = filter.replace( /~/g, '' ).split( '' ); + } else { + // wild card matching + if ( filter.indexOf( '?' ) > -1 ) { + useRegex = true; + filter = filter.replace( /\?/g, '\\S{1}' ); + } + if ( filter.indexOf( '*' ) > -1 ) { + useRegex = true; + filter = filter.replace( /\*/g, '\\S*' ); + } + matches = filter.split( regex.filter ); + } + if ( useRegex && matches && matches.length ) { + matches = new RegExp( ts.mark.cleanMatches( matches ).join( '.*' ), 'gim' ); + if ( ts.mark.checkRegex( matches ) ) { + $rows.children( col ).markRegExp( matches, options ); + } + } else { + // pass an array of matches + $rows.children( col ).mark( ts.mark.cleanMatches( matches ), options ); + } + } + }); + } + }; + + ts.addWidget({ + id: 'mark', + options: { + mark_tsUpdate : 'markUpdate' + }, + init : function( table, thisWidget, c, wo ) { + ts.mark.init( c, wo ); + }, + remove : function( table, c ) { + var update = c.widgetOptions.mark_tsUpdate; + c.$table.off( 'filterEnd.tsmark' + ( update ? ' ' + update : '' ) ); + } + }); + +})( jQuery ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index c53846a..cb20b05 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -1,4 +1,4 @@ -/*! Widget: math - updated 3/1/2016 (v2.25.5) *//* +/*! Widget: math - updated 7/31/2016 (v2.27.0) *//* * Requires tablesorter v2.16+ and jQuery 1.7+ * by Rob Garrison */ @@ -216,7 +216,7 @@ var undef, time, mathAttr, $mathCells, indx, len, changed = false, filters = {}; - if ( c.debug ) { + if ( c.debug || wo.math_debug ) { time = new Date(); } @@ -232,7 +232,6 @@ mathAttr = wo.math_dataAttrib; $mathCells = c.$tbodies.children( 'tr' ).children( '[' + mathAttr + ']' ); changed = math.mathType( c, $mathCells, wo.math_priority ) || changed; - // only info tbody cells $mathCells = c.$table .children( '.' + c.cssInfoBlock + ', tfoot' ) @@ -257,17 +256,17 @@ // trigger an update only if cells inside the tbody changed if ( changed ) { wo.math_isUpdating = true; - if ( c.debug ) { - console[ console.group ? 'group' : 'log' ]( 'Math widget triggering an update after recalculation' ); + if ( c.debug || wo.math_debug ) { + console[ console.group ? 'group' : 'log' ]( 'Math widget updating the cache after recalculation' ); } - // update internal cache - ts.update( c, undef, function(){ + // update internal cache, but ignore "remove-me" rows and do not resort + ts.updateCache( c, function() { math.updateComplete( c ); if ( !init && typeof wo.math_completed === 'function' ) { wo.math_completed( c ); } - if ( c.debug ) { + if ( c.debug || wo.math_debug ) { console.log( 'Math widget update completed' + ts.benchmark( time ) ); } }); @@ -275,7 +274,7 @@ if ( !init && typeof wo.math_completed === 'function' ) { wo.math_completed( c ); } - if ( c.debug ) { + if ( c.debug || wo.math_debug ) { console.log( 'Math widget found no changes in data' + ts.benchmark( time ) ); } } @@ -284,7 +283,9 @@ updateComplete : function( c ) { var wo = c.widgetOptions; - if ( wo.math_isUpdating && c.debug && console.groupEnd ) { console.groupEnd(); } + if ( wo.math_isUpdating && (c.debug || wo.math_debug ) && console.groupEnd ) { + console.groupEnd(); + } wo.math_isUpdating = false; }, @@ -299,7 +300,7 @@ // mathType is called multiple times if more than one "hasFilter" is used getAll = math.getAll( c, hasFilter ); } - if (c.debug) { + if (c.debug || wo.math_debug) { console[ console.group ? 'group' : 'log' ]( 'Tablesorter Math widget recalculation' ); } // $.each is okay here... only 4 priorities @@ -308,7 +309,7 @@ $targetCells = $cells.filter( '[' + mathAttr + '^=' + type + ']' ), len = $targetCells.length; if ( len ) { - if (c.debug) { + if (c.debug || wo.math_debug) { console[ console.group ? 'group' : 'log' ]( type ); } for ( index = 0; index < len; index++ ) { @@ -324,7 +325,7 @@ if ( equations[ formula ] ) { if ( arry.length ) { result = equations[ formula ]( arry, c ); - if ( c.debug ) { + if ( c.debug || wo.math_debug ) { console.log( $el.attr( mathAttr ), hasFilter ? '("' + hasFilter + '")' : '', arry, '=', result ); } } else { @@ -334,10 +335,10 @@ changed = math.output( $el, c, result, arry ) || changed; } } - if ( c.debug && console.groupEnd ) { console.groupEnd(); } + if ( ( c.debug || wo.math_debug ) && console.groupEnd ) { console.groupEnd(); } } }); - if ( c.debug && console.groupEnd ) { console.groupEnd(); } + if ( ( c.debug || wo.math_debug ) && console.groupEnd ) { console.groupEnd(); } return changed; } return false; @@ -350,7 +351,14 @@ changed = false, prev = $cell.html(), mask = $cell.attr( 'data-' + wo.math_data + '-mask' ) || wo.math_mask, + target = $cell.attr( 'data-' + wo.math_data + '-target' ) || '', result = ts.formatMask( mask, value, wo.math_wrapPrefix, wo.math_wrapSuffix ); + if (target) { + $el = $cell.find(target); + if ($el.length) { + $cell = $el; + } + } if ( typeof wo.math_complete === 'function' ) { result = wo.math_complete( $cell, wo, result, value, arry ); } @@ -583,6 +591,7 @@ priority: 100, options: { math_data : 'math', + math_debug : false, // column index to ignore math_ignore : [], // mask info: https://code.google.com/p/javascript-number-formatter/ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index 1036f24..06f6901 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -1,4 +1,4 @@ -/*! Widget: output - updated 1/15/2016 (v2.25.2) *//* +/*! Widget: output - updated 7/31/2016 (v2.27.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * Modified from: * HTML Table to CSV: http://www.kunalbabre.com/projects/table2CSV.php (License unknown?) @@ -145,8 +145,8 @@ }); headers = output.processRow(c, $this, true, outputJSON); - // all tbody rows - $rows = $el.children('tbody').children('tr'); + // all tbody rows - do not include widget added rows (e.g. grouping widget headers) + $rows = $el.children('tbody').children('tr').not(c.selectorRemove); // check for a filter callback function first! because // /^f/.test(function(){ console.log('test'); }) is TRUE! (function is converted to a string) @@ -193,7 +193,7 @@ // callback; if true returned, continue processing if ($.isFunction(wo.output_callback)) { - tmp = wo.output_callback(c, mydata); + tmp = wo.output_callback(c, mydata, c.pager && c.pager.ajaxObject.url || null); if ( tmp === false ) { return; } else if ( typeof tmp === 'string' ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index 2dba042..8280f5c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -1,4 +1,4 @@ -/*! Widget: scroller - updated 7/11/2016 (v2.26.6) *//* +/*! Widget: scroller - updated 7/31/2016 (v2.27.0) *//* Copyright (C) 2011 T. Connell & Associates, Inc. Dual-licensed under the MIT and GPL licenses @@ -77,7 +77,9 @@ scroller_barWidth : null }, format : function( table, c, wo ) { - if ( !c.isScrolling ) { + if ( c.isScrolling ) { + ts.scroller.resize( c, wo ); + } else { // initialize here instead of in widget init to give the // filter widget time to finish building the filter row ts.scroller.setup( c, wo ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js index 07b95f8..8939513 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js @@ -1,4 +1,4 @@ -/*! Widget: stickyHeaders - updated 5/1/2016 (v2.26.0) *//* +/*! Widget: stickyHeaders - updated 7/31/2016 (v2.27.0) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -61,10 +61,11 @@ // ************************** ts.addWidget({ id: 'stickyHeaders', - priority: 60, // sticky widget must be initialized after the filter widget! + priority: 55, // sticky widget must be initialized after the filter widget! options: { stickyHeaders : '', // extra class name added to the sticky header row - stickyHeaders_attachTo : null, // jQuery selector or object to attach sticky header to + stickyHeaders_appendTo : null, // jQuery selector or object to phycially attach the sticky headers + stickyHeaders_attachTo : null, // jQuery selector or object to attach scroll listener to (overridden by xScroll & yScroll settings) stickyHeaders_xScroll : null, // jQuery selector or object to monitor horizontal scroll position (defaults: xScroll > attachTo > window) stickyHeaders_yScroll : null, // jQuery selector or object to monitor vertical scroll position (defaults: yScroll > attachTo > window) stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element @@ -215,8 +216,12 @@ ts.bindEvents(table, $stickyThead.children().children('.' + ts.css.header)); - // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. - $table.after( $stickyWrap ); + if (wo.stickyHeaders_appendTo) { + $(wo.stickyHeaders_appendTo).append( $stickyWrap ); + } else { + // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. + $table.after( $stickyWrap ); + } // onRenderHeader is defined, we need to do something about it (fixes #641) if (c.onRenderHeader) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js index f76890d..bb5f577 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js @@ -1,4 +1,4 @@ -/*! Widget: uitheme - updated 7/11/2016 (v2.26.6) */ +/*! Widget: uitheme - updated 7/31/2016 (v2.27.0) */ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; @@ -37,9 +37,9 @@ hover : 'ui-state-hover', // hover class // icon class names icons : 'ui-icon', // icon class added to the <i> in the header - iconSortNone : 'ui-icon-carat-2-n-s', // class name added to icon when column is not sorted - iconSortAsc : 'ui-icon-carat-1-n', // class name added to icon when column has ascending sort - iconSortDesc : 'ui-icon-carat-1-s', // class name added to icon when column has descending sort + iconSortNone : 'ui-icon-carat-2-n-s ui-icon-caret-2-n-s', // class name added to icon when column is not sorted + iconSortAsc : 'ui-icon-carat-1-n ui-icon-caret-1-n', // class name added to icon when column has ascending sort + iconSortDesc : 'ui-icon-carat-1-s ui-icon-caret-1-s', // class name added to icon when column has descending sort filterRow : '', footerRow : '', footerCells : '', diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index 07db862..3649b9a 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -51,12 +51,12 @@ /* black unsorted icon */ .tablesorter-bootstrap .bootstrap-icon-unsorted { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAMAAADOvxanAAAAVFBMVEUAAABCQkJZWVkZGRnJyckgICAZGRkZGRn8/PweHh4dHR0aGhoaGhpUVFQbGxvQ0NDc3NxMTExSUlIbGxvr6+s4ODhKSkogICAtLS00NDQzMzMnJydSEPrQAAAAGHRSTlMA1ssZRLgdAQbDyisqsZo8QdXUq0r9xPepSRwiAAAAX0lEQVQI13XHSQKAIAwEwQAKxn13Ev7/T2Pu9qmarJKPXIicI4PH4hxaKNrhm2S8bJK5h4YzKHrzJNtK6yYT/TdXzpS5zuYg4MSQYF6i4IHExdw1UVRi05HPrrvT53a+qyMFC9t04gcAAAAASUVORK5CYII=); + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAYAAAD5YeaVAAAA20lEQVR4AWJABpKSkoxALCstLb0aUAsZaCAMhVEY6B0amx8YZWDDEDSBa2AGe7XeIiAAClYwVGBvsAcIllsf/mvcC9DgOOd8h90fxWvngVEUbZIkuWRZZlE8eQjcisgZMM9zi+LJ6ZfwegmWZflZDugdHMfxTcGqql7TNBlUB/QObtv2VBSFrev6OY7jngzFk9OT/fn73fWYpqnlXNyXDMWT0zuYx/Bvel9ej+LJ6R08DMOu67q7DkTkrSA5vYPneV71fX/QASdTkJwezhs0TfMARn0wMDDGXEPgF4oijqwM5YjNAAAAAElFTkSuQmCC); } /* white unsorted icon */ .tablesorter-bootstrap .icon-white.bootstrap-icon-unsorted { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOBAMAAAALT/umAAAAKlBMVEUAAAD///////////////////////////////////////////////////+Gu8ovAAAADXRSTlMA4EXKBtQqvR0+sxmalc142gAAAFdJREFUCNdjYGDoamAAAjZbMxCVfvd6AgMDd+3du9UMDKx3hWSvMjBwXZww8RYDGuC53NB8h4GB8a617UUGBs7Yu3cjGRhYVO9eVQFKOskKOQApFmUgBwBZ+xXRTttNdAAAAABJRU5ErkJggg==); + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAYAAAD5YeaVAAAAe0lEQVR4AbXQoRWDMBiF0Sh2QLAAQ8SxJGugWSA6A2STW1PxTsnB9cnkfuYvv8OGC1t5G3Y0QMP+Bm857keAdQIzWBP3+Bw4MADQE18B6/etRnCV/w9nnGuLezfAmXhABGtAGIkruvk6auIFRwQJDywllsEAjCecB20GP59BQQ+gtlRLAAAAAElFTkSuQmCC); } /* since bootstrap (table-striped) uses nth-child(), we just use this to add a zebra stripe color */ diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css index fad1ae9..ac9e8cb 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css @@ -61,12 +61,12 @@ /* black unsorted icon */ .tablesorter-bootstrap .bootstrap-icon-unsorted { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAYAAAD5YeaVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAWVJREFUeNqUUL9Lw2AUTGP8mqGlpBQkNeCSRcckEBcHq1jImMElToKuDvpHFMGhU0BQcHBwLji6CE1B4uB/INQsDi4d2jQ/fPeZxo764OV6915f7lLJ81xot9tCURXqdVEUr7IsO6ffH9Q5BlEUCaLwWxWqTcbYnaIoh0Dw4gAvcWlxq1qt9hqNxg6hUGAP+uIPUrGs0qXLer2+v/pTX6QpxLtkc2U2m53ACb8sSdIDXerSEms2m6+DweAICA4d89KGbduf9MpEVdXQ9/2LVqv1CASHjjn3iq/x1xKFfxQPqGnada1W86bT6SiO42OS3qk3KPStLMvbk8nkfjwen/LLuq6blFymMB0KdUPSGhAcOualjX6/f0bCiC7NaWGPQr0BwaFjzn0gYJqmLAiCA8/zni3LmhuGkQPBoWPOPwQeaPIqD4fDruu6L6Zp5kBw6IudchmdJAkLw3DXcZwnIPjy/FuAAQCiqqWWCAFKcwAAAABJRU5ErkJggg==); + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAYAAAD5YeaVAAABGUlEQVR4AZWQAUfEYBzGC07fILfrbnfgqG1jmxliDitmYIaxb9Y3CEIEVHDaUGMfYF8gIyBqbd7eH7wWofB6/J7nN+x/IIRQbz6fH8p3slgsrkl4uv8QNU071XX9wTAMQcLTD6bi2WazubUsq3ddV5AwPftU1tbr9Z0UPhGDIHgjYXp2JS+Xy71t2wNCFEV113UxCdOzKznLshvf9z+SJHlp23ZHR8L07Er+6/u/LO96td1u3zmX/BmdjoTp2ZUchmHted4o/16sVqt7KR6TMD27kpumOc/z/EkOvWmaQp7rlYTp2ZU8juOsqqqLoij2UvhyHEeQMD27knl93x+VZXmZpukz9yVh+l+vMQzDrK7rXRzHjyQ83b8BlglBGLw1Kb4AAAAASUVORK5CYII=); } /* white unsorted icon */ .tablesorter-bootstrap .icon-white.bootstrap-icon-unsorted { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOBAMAAAALT/umAAAAKlBMVEUAAAD///////////////////////////////////////////////////+Gu8ovAAAADXRSTlMA4EXKBtQqvR0+sxmalc142gAAAFdJREFUCNdjYGDoamAAAjZbMxCVfvd6AgMDd+3du9UMDKx3hWSvMjBwXZww8RYDGuC53NB8h4GB8a617UUGBs7Yu3cjGRhYVO9eVQFKOskKOQApFmUgBwBZ+xXRTttNdAAAAABJRU5ErkJggg==); + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAYAAAD5YeaVAAAAe0lEQVR4AbXQoRWDMBiF0Sh2QLAAQ8SxJGugWSA6A2STW1PxTsnB9cnkfuYvv8OGC1t5G3Y0QMP+Bm857keAdQIzWBP3+Bw4MADQE18B6/etRnCV/w9nnGuLezfAmXhABGtAGIkruvk6auIFRwQJDywllsEAjCecB20GP59BQQ+gtlRLAAAAAElFTkSuQmCC); } /* since bootstrap (table-striped) uses nth-child(), we just use this to add a zebra stripe color */ diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.materialize.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.materialize.css new file mode 100644 index 0000000..a9fb113 --- /dev/null +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.materialize.css @@ -0,0 +1,176 @@ +/************* + Materialize theme (http://materializecss.com/) + *************/ +/* jQuery materialize Theme */ +.tablesorter-materialize { + width: 100%; +} +.tablesorter-materialize thead th, +.tablesorter-materialize thead td, +.tablesorter-materialize tfoot th, +.tablesorter-materialize tfoot td { + font: 14px/20px Arial, Sans-serif; + font-weight: bold; + padding: 4px; + margin: 0 0 18px; + background-color: #eee; +} + +.tablesorter-materialize .tablesorter-header { + cursor: pointer; +} +.tablesorter-materialize .sorter-false { + cursor: default; +} + +.tablesorter-materialize .tablesorter-header-inner { + position: relative; + padding: 4px 18px 4px 4px; +} + +/* sort icons */ +.tablesorter-materialize thead .tablesorter-header { + background-repeat: no-repeat; + background-position: center right; + padding: 4px 18px 4px 4px; + white-space: normal; + cursor: pointer; +} + +/* black unsorted icon */ +.tablesorter-materialize thead .tablesorter-headerUnSorted { + /* <svg xmlns="http://www.w3.org/2000/svg" width="18" height="12" viewBox="0 0 24 16"><path d="M15 8 1 8 8 0zM15 9 1 9 8 16z" fill="#222"/></svg> */ + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxMiIgdmlld0JveD0iMCAwIDI0IDE2Ij48cGF0aCBkPSJNMTUgOCAxIDggOCAwek0xNSA5IDEgOSA4IDE2eiIgZmlsbD0iIzIyMiIvPjwvc3ZnPg==); +} +/* black asc icon */ +.tablesorter-materialize thead .tablesorter-headerAsc { + /* <svg xmlns="http://www.w3.org/2000/svg" width="18" height="12" viewBox="0 0 24 16"><path d="M15 11 1 11 8 3z" fill="#222"/></svg> */ + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxMiIgdmlld0JveD0iMCAwIDI0IDE2Ij48cGF0aCBkPSJNMTUgMTEgMSAxMSA4IDN6IiBmaWxsPSIjMjIyIi8+PC9zdmc+); +} +/* black desc icon */ +.tablesorter-materialize thead .tablesorter-headerDesc { + /* <svg xmlns="http://www.w3.org/2000/svg" width="18" height="12" viewBox="0 0 24 16"><path d="M15 6 1 6 8 13z" fill="#222"/></svg> */ + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxMiIgdmlld0JveD0iMCAwIDI0IDE2Ij48cGF0aCBkPSJNMTUgNiAxIDYgOCAxM3oiIGZpbGw9IiMyMjIiLz48L3N2Zz4=); +} + +/* white unsorted icon */ +.tablesorter-materialize-dark thead .tablesorter-headerUnSorted { + /* <svg xmlns="http://www.w3.org/2000/svg" width="18" height="12" viewBox="0 0 24 16"><path d="M15 8 1 8 8 0zM15 9 1 9 8 16z" fill="#fff"/></svg> */ + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxMiIgdmlld0JveD0iMCAwIDI0IDE2Ij48cGF0aCBkPSJNMTUgOCAxIDggOCAwek0xNSA5IDEgOSA4IDE2eiIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg==); +} +/* white asc icon */ +.tablesorter-materialize-dark thead .tablesorter-headerAsc { + /* <svg xmlns="http://www.w3.org/2000/svg" width="18" height="12" viewBox="0 0 24 16"><path d="M15 11 1 11 8 3z" fill="#fff"/></svg> */ + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxMiIgdmlld0JveD0iMCAwIDI0IDE2Ij48cGF0aCBkPSJNMTUgMTEgMSAxMSA4IDN6IiBmaWxsPSIjZmZmIi8+PC9zdmc+); +} +/* white desc icon */ +.tablesorter-materialize-dark thead .tablesorter-headerDesc { + /* <svg xmlns="http://www.w3.org/2000/svg" width="18" height="12" viewBox="0 0 24 16"><path d="M15 6 1 6 8 13z" fill="#fff"/></svg> */ + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxMiIgdmlld0JveD0iMCAwIDI0IDE2Ij48cGF0aCBkPSJNMTUgNiAxIDYgOCAxM3oiIGZpbGw9IiNmZmYiLz48L3N2Zz4=); +} + +/* since materialize (table-striped) uses nth-child(), we just use this to add a zebra stripe color */ +.tablesorter-materialize > tbody > tr.odd > td, +.tablesorter-materialize > tbody > tr.tablesorter-hasChildRow.odd:hover ~ tr.tablesorter-hasChildRow.odd ~ .tablesorter-childRow.odd > td { + background-color: #f9f9f9; +} +.tablesorter-materialize > tbody > tr.hover > td, +.tablesorter-materialize > tbody > tr.odd:hover > td, +.tablesorter-materialize > tbody > tr.even:hover > td, +.tablesorter-materialize > tbody > tr.tablesorter-hasChildRow.odd:hover ~ .tablesorter-childRow.odd > td, +.tablesorter-materialize > tbody > tr.tablesorter-hasChildRow.even:hover ~ .tablesorter-childRow.even > td { + background-color: #f5f5f5; +} +.tablesorter-materialize > tbody > tr.even > td, +.tablesorter-materialize > tbody > tr.tablesorter-hasChildRow.even:hover ~ tr.tablesorter-hasChildRow.even ~ .tablesorter-childRow.even > td { + background-color: #fff; +} + +/* processing icon */ +.tablesorter-materialize .tablesorter-processing { + background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs='); + background-position: center center !important; + background-repeat: no-repeat !important; +} + +/* caption */ +.caption { + background-color: #fff; +} + +/* filter widget */ +.tablesorter-materialize .tablesorter-filter-row input.tablesorter-filter, +.tablesorter-materialize .tablesorter-filter-row select.tablesorter-filter { + width: 98%; + margin: 0; + padding: 4px 6px; + color: #333; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition: height 0.1s ease; + -moz-transition: height 0.1s ease; + -o-transition: height 0.1s ease; + transition: height 0.1s ease; +} +.tablesorter-materialize .tablesorter-filter-row .tablesorter-filter.disabled { + background-color: #eee; + color: #555; + cursor: not-allowed; + border: 1px solid #ccc; + border-radius: 4px; + box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.075) inset; + box-sizing: border-box; + transition: height 0.1s ease; +} +.tablesorter-materialize .tablesorter-filter-row { + background-color: #efefef; +} +.tablesorter-materialize .tablesorter-filter-row td { + background-color: #efefef; + line-height: normal; + text-align: center; + padding: 4px 6px; + vertical-align: middle; + -webkit-transition: line-height 0.1s ease; + -moz-transition: line-height 0.1s ease; + -o-transition: line-height 0.1s ease; + transition: line-height 0.1s ease; +} +/* hidden filter row */ +.tablesorter-materialize .tablesorter-filter-row.hideme td { + padding: 2px; /* change this to modify the thickness of the closed border row */ + margin: 0; + line-height: 0; +} +.tablesorter-materialize .tablesorter-filter-row.hideme * { + height: 1px; + min-height: 0; + border: 0; + padding: 0; + margin: 0; + /* don't use visibility: hidden because it disables tabbing */ + opacity: 0; + filter: alpha(opacity=0); +} +/* rows hidden by filtering (needed for child rows) */ +.tablesorter .filtered { + display: none; +} + +/* pager plugin */ +.tablesorter-materialize .tablesorter-pager select { + padding: 4px 6px; + display: inline-block; + width: auto; +} +.tablesorter-materialize .tablesorter-pager .pagedisplay { + border: 0; +} + +/* ajax error row */ +.tablesorter .tablesorter-errorRow td { + text-align: center; + cursor: pointer; + background-color: #e6bf99; +} From 38a2315ad0edbbbfc0430f611490a6c649bf6905 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Tue, 2 Aug 2016 22:57:01 +0200 Subject: [PATCH 093/138] Update tablesorter to latest version (2.27.2) --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 10 +++++----- .../jquery-tablesorter/jquery.tablesorter.js | 8 ++++---- .../jquery.tablesorter.widgets.js | 2 +- .../jquery-tablesorter/widgets/widget-mark.js | 15 +++++++++++++-- 8 files changed, 30 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 668892d..fe6ee44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.22.1 (2016-08-02) + +* Upgrade tablesorter to v2.27.2 + #### v1.22.0 (2016-08-01) * Upgrade tablesorter to v2.27.1 diff --git a/README.md b/README.md index 318a45f..a193d89 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.27.1 (7/31/2016), [documentation] +Current tablesorter version: 2.27.2 (8/2/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index dc62b2c..b19f945 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 22 - TINY = 0 + TINY = 1 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 8df2059..fd15cfa 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 8df205914adb6ae68786685bb3e89902b0e66c19 +Subproject commit fd15cfa7987155911887f5e4d3ca8ee77764a8df diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index b15b715..bc0b9da 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 07-31-2016 (v2.27.1)*/ +/*! tablesorter (FORK) - updated 08-02-2016 (v2.27.2)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.27.1 *//* +/*! TableSorter (FORK) v2.27.2 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.27.1', + version : '2.27.2', parsers : [], widgets : [], @@ -2676,8 +2676,8 @@ }); // match 24 hour time & 12 hours time + am/pm - see http://regexr.com/3c3tk - ts.regex.timeTest = /^([1-9]|1[0-2]):([0-5]\d)(\s[AP]M)$|^((?:[01]\d|[2][0-4]):[0-5]\d)$/i; - ts.regex.timeMatch = /([1-9]|1[0-2]):([0-5]\d)(\s[AP]M)|((?:[01]\d|[2][0-4]):[0-5]\d)/i; + ts.regex.timeTest = /^(0?[1-9]|1[0-2]):([0-5]\d)(\s[AP]M)$|^((?:[01]\d|[2][0-4]):[0-5]\d)$/i; + ts.regex.timeMatch = /(0?[1-9]|1[0-2]):([0-5]\d)(\s[AP]M)|((?:[01]\d|[2][0-4]):[0-5]\d)/i; ts.addParser({ id : 'time', is : function( str ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index b67075f..cfbdf04 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.27.1 *//* +/*! TableSorter (FORK) v2.27.2 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.27.1', + version : '2.27.2', parsers : [], widgets : [], @@ -2658,8 +2658,8 @@ }); // match 24 hour time & 12 hours time + am/pm - see http://regexr.com/3c3tk - ts.regex.timeTest = /^([1-9]|1[0-2]):([0-5]\d)(\s[AP]M)$|^((?:[01]\d|[2][0-4]):[0-5]\d)$/i; - ts.regex.timeMatch = /([1-9]|1[0-2]):([0-5]\d)(\s[AP]M)|((?:[01]\d|[2][0-4]):[0-5]\d)/i; + ts.regex.timeTest = /^(0?[1-9]|1[0-2]):([0-5]\d)(\s[AP]M)$|^((?:[01]\d|[2][0-4]):[0-5]\d)$/i; + ts.regex.timeMatch = /(0?[1-9]|1[0-2]):([0-5]\d)(\s[AP]M)|((?:[01]\d|[2][0-4]):[0-5]\d)/i; ts.addParser({ id : 'time', is : function( str ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 1cc03cc..99653d4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 07-31-2016 (v2.27.1)*/ +/*! tablesorter (FORK) - updated 08-02-2016 (v2.27.2)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-mark.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-mark.js index bc0403e..f311e7d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-mark.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-mark.js @@ -1,4 +1,4 @@ -/*! Widget: mark.js - updated 7/31/2016 (v2.27.1) *//* +/*! Widget: mark.js - updated 8/1/2016 (v2.27.2) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -51,6 +51,8 @@ }, update : function( c, filters ) { var options = {}, + wo = c.widgetOptions, + setIgnoreCase = typeof wo.filter_ignoreCase === 'undefined' ? true : wo.filter_ignoreCase, regex = ts.mark.regex, $rows = c.$table .find( 'tbody tr' ) @@ -59,6 +61,12 @@ filters = filters || $.tablesorter.getFilters( c.$table ); // extract & save mark options from widgetOptions (prefixed with "mark_") // update dynamically + $.each( c.widgetOptions, function( key, val ) { + var matches = key.match( regex.mark ); + if ( matches && typeof matches[1] !== 'undefined' ) { + options[ matches[1] ] = val; + } + }); $.each( filters, function( indx, filter ) { if ( filter ) { var testRegex = null, @@ -105,7 +113,10 @@ matches = filter.split( regex.filter ); } if ( useRegex && matches && matches.length ) { - matches = new RegExp( ts.mark.cleanMatches( matches ).join( '.*' ), 'gim' ); + matches = new RegExp( + ts.mark.cleanMatches( matches ).join( '.*' ), + 'gm' + ( setIgnoreCase ? 'i' : '' ) + ); if ( ts.mark.checkRegex( matches ) ) { $rows.children( col ).markRegExp( matches, options ); } From fb0d5ed1c5c03287a2a3b7479174b30c6aab14cd Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Wed, 17 Aug 2016 21:00:53 +0200 Subject: [PATCH 094/138] Update tablesorter to latest version (2.27.3) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 4 +- .../jquery.tablesorter.combined.js | 48 +++++++++++++------ .../jquery-tablesorter/jquery.tablesorter.js | 6 +-- .../jquery.tablesorter.widgets.js | 42 +++++++++++----- .../widgets/widget-filter.js | 40 ++++++++++++---- .../widgets/widget-pager.js | 4 +- 10 files changed, 109 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe6ee44..8df6aff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.22.2 (2016-08-17) + +* Upgrade tablesorter to v2.27.3 + #### v1.22.1 (2016-08-02) * Upgrade tablesorter to v2.27.2 diff --git a/README.md b/README.md index a193d89..397e625 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.27.2 (8/2/2016), [documentation] +Current tablesorter version: 2.27.3 (8/17/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index b19f945..632b62e 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 22 - TINY = 1 + TINY = 2 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index fd15cfa..fca6ef2 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit fd15cfa7987155911887f5e4d3ca8ee77764a8df +Subproject commit fca6ef265227a5083e5f2ab0864afac253b386db diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index b43d7ff..55a778b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin -* updated 7/11/2016 (v2.26.6) +* updated 8/17/2016 (v2.27.3) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -145,7 +145,7 @@ var normalized, indx, len, c = table.config, hasFilters = c.$table.hasClass('hasFilters'); - if (hasFilters && !p.ajaxUrl) { + if (hasFilters && !p.ajax) { if (ts.isEmptyObject(c.cache)) { // delayInit: true so nothing is in the cache p.filteredRows = p.totalRows = c.$tbodies.eq(0).children('tr').not( p.countChildRows ? '' : '.' + c.cssChildRow ).length; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index bc0b9da..5c27a7a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 08-02-2016 (v2.27.2)*/ +/*! tablesorter (FORK) - updated 08-17-2016 (v2.27.3)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.27.2 *//* +/*! TableSorter (FORK) v2.27.3 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.27.2', + version : '2.27.3', parsers : [], widgets : [], @@ -2297,7 +2297,7 @@ // c.$headerIndexed is not defined initially $cell = c.$headerIndexed && c.$headerIndexed[ indx ] || $cells.filter( '[data-column="' + indx + '"]:last' ); - if ( obj[ indx ] ) { + if ( typeof obj[ indx ] !== 'undefined' ) { return getCell ? obj[ indx ] : obj[ $cells.index( $cell ) ]; } for ( key in obj ) { @@ -3126,7 +3126,7 @@ })(jQuery); -/*! Widget: filter - updated 7/31/2016 (v2.27.0) *//* +/*! Widget: filter - updated 8/17/2016 (v2.27.3) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3565,8 +3565,10 @@ // force a new search since content has changed c.lastCombinedFilter = null; c.lastSearch = []; - // update filterFormatters after update - Fixes #1237 - c.$table.triggerHandler( 'filterFomatterUpdate' ); + // update filterFormatters after update (& small delay) - Fixes #1237 + setTimeout(function(){ + c.$table.triggerHandler( 'filterFomatterUpdate' ); + }, 100); } // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first // input ensures all inputs are updated when a search is triggered on the table @@ -3924,19 +3926,24 @@ }) .bind( 'keyup' + namespace, function( event ) { wo = table.config.widgetOptions; // make sure "wo" isn't cached - var column = parseInt( $( this ).attr( 'data-column' ), 10 ); + var column = parseInt( $( this ).attr( 'data-column' ), 10 ), + liveSearch = typeof wo.filter_liveSearch === 'boolean' ? wo.filter_liveSearch : + ts.getColumnData( table, wo.filter_liveSearch, column ); + if ( typeof liveSearch === 'undefined' ) { + liveSearch = wo.filter_liveSearch.fallback || false; + } $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); // emulate what webkit does.... escape clears the filter if ( event.which === tskeyCodes.escape ) { // make sure to restore the last value on escape this.value = wo.filter_resetOnEsc ? '' : c.lastSearch[column]; // live search - } else if ( wo.filter_liveSearch === false ) { + } else if ( liveSearch === false ) { return; // don't return if the search value is empty ( all rows need to be revealed ) } else if ( this.value !== '' && ( // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace - ( typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch ) || + ( typeof liveSearch === 'number' && this.value.length < liveSearch ) || // let return & backspace continue on, but ignore arrows & non-valid characters ( event.which !== tskeyCodes.enter && event.which !== tskeyCodes.backSpace && ( event.which < tskeyCodes.space || ( event.which >= tskeyCodes.left && event.which <= tskeyCodes.down ) ) ) ) ) { @@ -3958,18 +3965,31 @@ event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - tsf.searching( table, event.type !== 'keypress', true ); + tsf.searching( table, event.type !== 'keypress', true, column ); } }); }, - searching: function( table, filter, skipFirst ) { - var wo = table.config.widgetOptions; + searching: function( table, filter, skipFirst, column ) { + var liveSearch, + wo = table.config.widgetOptions; + if (typeof column === 'undefined') { + // no delay + liveSearch = false; + } else { + liveSearch = typeof wo.filter_liveSearch === 'boolean' ? + wo.filter_liveSearch : + // get column setting, or set to fallback value, or default to false + ts.getColumnData( table, wo.filter_liveSearch, column ); + if ( typeof liveSearch !== 'undefined' ) { + liveSearch = wo.filter_liveSearch.fallback || false; + } + } clearTimeout( wo.filter_searchTimer ); if ( typeof filter === 'undefined' || filter === true ) { // delay filtering wo.filter_searchTimer = setTimeout( function() { tsf.checkFilters( table, filter, skipFirst ); - }, wo.filter_liveSearch ? wo.filter_searchDelay : 10 ); + }, liveSearch ? wo.filter_searchDelay : 10 ); } else { // skip delay tsf.checkFilters( table, filter, skipFirst ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index cfbdf04..7f56de9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.27.2 *//* +/*! TableSorter (FORK) v2.27.3 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.27.2', + version : '2.27.3', parsers : [], widgets : [], @@ -2279,7 +2279,7 @@ // c.$headerIndexed is not defined initially $cell = c.$headerIndexed && c.$headerIndexed[ indx ] || $cells.filter( '[data-column="' + indx + '"]:last' ); - if ( obj[ indx ] ) { + if ( typeof obj[ indx ] !== 'undefined' ) { return getCell ? obj[ indx ] : obj[ $cells.index( $cell ) ]; } for ( key in obj ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 99653d4..f67ff35 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 08-02-2016 (v2.27.2)*/ +/*! tablesorter (FORK) - updated 08-17-2016 (v2.27.3)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -382,7 +382,7 @@ })(jQuery); -/*! Widget: filter - updated 7/31/2016 (v2.27.0) *//* +/*! Widget: filter - updated 8/17/2016 (v2.27.3) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -821,8 +821,10 @@ // force a new search since content has changed c.lastCombinedFilter = null; c.lastSearch = []; - // update filterFormatters after update - Fixes #1237 - c.$table.triggerHandler( 'filterFomatterUpdate' ); + // update filterFormatters after update (& small delay) - Fixes #1237 + setTimeout(function(){ + c.$table.triggerHandler( 'filterFomatterUpdate' ); + }, 100); } // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first // input ensures all inputs are updated when a search is triggered on the table @@ -1180,19 +1182,24 @@ }) .bind( 'keyup' + namespace, function( event ) { wo = table.config.widgetOptions; // make sure "wo" isn't cached - var column = parseInt( $( this ).attr( 'data-column' ), 10 ); + var column = parseInt( $( this ).attr( 'data-column' ), 10 ), + liveSearch = typeof wo.filter_liveSearch === 'boolean' ? wo.filter_liveSearch : + ts.getColumnData( table, wo.filter_liveSearch, column ); + if ( typeof liveSearch === 'undefined' ) { + liveSearch = wo.filter_liveSearch.fallback || false; + } $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); // emulate what webkit does.... escape clears the filter if ( event.which === tskeyCodes.escape ) { // make sure to restore the last value on escape this.value = wo.filter_resetOnEsc ? '' : c.lastSearch[column]; // live search - } else if ( wo.filter_liveSearch === false ) { + } else if ( liveSearch === false ) { return; // don't return if the search value is empty ( all rows need to be revealed ) } else if ( this.value !== '' && ( // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace - ( typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch ) || + ( typeof liveSearch === 'number' && this.value.length < liveSearch ) || // let return & backspace continue on, but ignore arrows & non-valid characters ( event.which !== tskeyCodes.enter && event.which !== tskeyCodes.backSpace && ( event.which < tskeyCodes.space || ( event.which >= tskeyCodes.left && event.which <= tskeyCodes.down ) ) ) ) ) { @@ -1214,18 +1221,31 @@ event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - tsf.searching( table, event.type !== 'keypress', true ); + tsf.searching( table, event.type !== 'keypress', true, column ); } }); }, - searching: function( table, filter, skipFirst ) { - var wo = table.config.widgetOptions; + searching: function( table, filter, skipFirst, column ) { + var liveSearch, + wo = table.config.widgetOptions; + if (typeof column === 'undefined') { + // no delay + liveSearch = false; + } else { + liveSearch = typeof wo.filter_liveSearch === 'boolean' ? + wo.filter_liveSearch : + // get column setting, or set to fallback value, or default to false + ts.getColumnData( table, wo.filter_liveSearch, column ); + if ( typeof liveSearch !== 'undefined' ) { + liveSearch = wo.filter_liveSearch.fallback || false; + } + } clearTimeout( wo.filter_searchTimer ); if ( typeof filter === 'undefined' || filter === true ) { // delay filtering wo.filter_searchTimer = setTimeout( function() { tsf.checkFilters( table, filter, skipFirst ); - }, wo.filter_liveSearch ? wo.filter_searchDelay : 10 ); + }, liveSearch ? wo.filter_searchDelay : 10 ); } else { // skip delay tsf.checkFilters( table, filter, skipFirst ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index c8c85df..6da0854 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 7/31/2016 (v2.27.0) *//* +/*! Widget: filter - updated 8/17/2016 (v2.27.3) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -437,8 +437,10 @@ // force a new search since content has changed c.lastCombinedFilter = null; c.lastSearch = []; - // update filterFormatters after update - Fixes #1237 - c.$table.triggerHandler( 'filterFomatterUpdate' ); + // update filterFormatters after update (& small delay) - Fixes #1237 + setTimeout(function(){ + c.$table.triggerHandler( 'filterFomatterUpdate' ); + }, 100); } // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first // input ensures all inputs are updated when a search is triggered on the table @@ -796,19 +798,24 @@ }) .bind( 'keyup' + namespace, function( event ) { wo = table.config.widgetOptions; // make sure "wo" isn't cached - var column = parseInt( $( this ).attr( 'data-column' ), 10 ); + var column = parseInt( $( this ).attr( 'data-column' ), 10 ), + liveSearch = typeof wo.filter_liveSearch === 'boolean' ? wo.filter_liveSearch : + ts.getColumnData( table, wo.filter_liveSearch, column ); + if ( typeof liveSearch === 'undefined' ) { + liveSearch = wo.filter_liveSearch.fallback || false; + } $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); // emulate what webkit does.... escape clears the filter if ( event.which === tskeyCodes.escape ) { // make sure to restore the last value on escape this.value = wo.filter_resetOnEsc ? '' : c.lastSearch[column]; // live search - } else if ( wo.filter_liveSearch === false ) { + } else if ( liveSearch === false ) { return; // don't return if the search value is empty ( all rows need to be revealed ) } else if ( this.value !== '' && ( // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace - ( typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch ) || + ( typeof liveSearch === 'number' && this.value.length < liveSearch ) || // let return & backspace continue on, but ignore arrows & non-valid characters ( event.which !== tskeyCodes.enter && event.which !== tskeyCodes.backSpace && ( event.which < tskeyCodes.space || ( event.which >= tskeyCodes.left && event.which <= tskeyCodes.down ) ) ) ) ) { @@ -830,18 +837,31 @@ event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - tsf.searching( table, event.type !== 'keypress', true ); + tsf.searching( table, event.type !== 'keypress', true, column ); } }); }, - searching: function( table, filter, skipFirst ) { - var wo = table.config.widgetOptions; + searching: function( table, filter, skipFirst, column ) { + var liveSearch, + wo = table.config.widgetOptions; + if (typeof column === 'undefined') { + // no delay + liveSearch = false; + } else { + liveSearch = typeof wo.filter_liveSearch === 'boolean' ? + wo.filter_liveSearch : + // get column setting, or set to fallback value, or default to false + ts.getColumnData( table, wo.filter_liveSearch, column ); + if ( typeof liveSearch !== 'undefined' ) { + liveSearch = wo.filter_liveSearch.fallback || false; + } + } clearTimeout( wo.filter_searchTimer ); if ( typeof filter === 'undefined' || filter === true ) { // delay filtering wo.filter_searchTimer = setTimeout( function() { tsf.checkFilters( table, filter, skipFirst ); - }, wo.filter_liveSearch ? wo.filter_searchDelay : 10 ); + }, liveSearch ? wo.filter_searchDelay : 10 ); } else { // skip delay tsf.checkFilters( table, filter, skipFirst ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index d2cc049..b8d2728 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 7/11/2016 (v2.26.6) */ +/*! Widget: Pager - updated 8/17/2016 (v2.27.3) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -417,7 +417,7 @@ wo = c.widgetOptions, p = c.pager, hasFilters = c.$table.hasClass( 'hasFilters' ); - if ( hasFilters && !wo.pager_ajaxUrl ) { + if ( hasFilters && !p.ajax ) { if ( $.isEmptyObject( c.cache ) ) { // delayInit: true so nothing is in the cache p.filteredRows = p.totalRows = c.$tbodies.eq( 0 ) From 59ee1838fe69b74760926a940371cd63871462a5 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 22 Aug 2016 21:21:45 +0200 Subject: [PATCH 095/138] Update tablesorter to latest version (2.27.5) --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 20 ++++++++++++++----- .../jquery-tablesorter/jquery.tablesorter.js | 4 ++-- .../jquery.tablesorter.widgets.js | 16 ++++++++++++--- .../jquery-tablesorter/parsers/parser-date.js | 9 ++++++--- .../widgets/widget-filter.js | 14 +++++++++++-- 9 files changed, 55 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8df6aff..bca4380 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.22.3 (2016-08-22) + +* Upgrade tablesorter to v2.27.5 + #### v1.22.2 (2016-08-17) * Upgrade tablesorter to v2.27.3 diff --git a/README.md b/README.md index 397e625..ef42493 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.27.3 (8/17/2016), [documentation] +Current tablesorter version: 2.27.5 (8/22/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 632b62e..ab3aa61 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 22 - TINY = 2 + TINY = 3 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index fca6ef2..27a931e 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit fca6ef265227a5083e5f2ab0864afac253b386db +Subproject commit 27a931e7859bf6ef2bcfd72f4114c6f27e9085eb diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 5c27a7a..01fb5fd 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 08-17-2016 (v2.27.3)*/ +/*! tablesorter (FORK) - updated 08-22-2016 (v2.27.5)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.27.3 *//* +/*! TableSorter (FORK) v2.27.5 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.27.3', + version : '2.27.5', parsers : [], widgets : [], @@ -3126,7 +3126,7 @@ })(jQuery); -/*! Widget: filter - updated 8/17/2016 (v2.27.3) *//* +/*! Widget: filter - updated 8/22/2016 (v2.27.5) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -4467,7 +4467,17 @@ res = query[ indx ].split( ':' ); if ( res.length > 1 ) { // make the column a one-based index ( non-developers start counting from one :P ) - id = parseInt( res[0], 10 ) - 1; + if ( isNaN( res[0] ) ) { + $.each( c.headerContent, function( i, txt ) { + // multiple matches are possible + if ( txt.toLowerCase().indexOf( res[0] ) > -1 ) { + id = i; + filters[ id ] = res[1]; + } + }); + } else { + id = parseInt( res[0], 10 ) - 1; + } if ( id >= 0 && id < c.columns ) { // if id is an integer filters[ id ] = res[1]; query.splice( indx, 1 ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 7f56de9..a85b212 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.27.3 *//* +/*! TableSorter (FORK) v2.27.5 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.27.3', + version : '2.27.5', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index f67ff35..64fb90b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 08-17-2016 (v2.27.3)*/ +/*! tablesorter (FORK) - updated 08-22-2016 (v2.27.5)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -382,7 +382,7 @@ })(jQuery); -/*! Widget: filter - updated 8/17/2016 (v2.27.3) *//* +/*! Widget: filter - updated 8/22/2016 (v2.27.5) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -1723,7 +1723,17 @@ res = query[ indx ].split( ':' ); if ( res.length > 1 ) { // make the column a one-based index ( non-developers start counting from one :P ) - id = parseInt( res[0], 10 ) - 1; + if ( isNaN( res[0] ) ) { + $.each( c.headerContent, function( i, txt ) { + // multiple matches are possible + if ( txt.toLowerCase().indexOf( res[0] ) > -1 ) { + id = i; + filters[ id ] = res[1]; + } + }); + } else { + id = parseInt( res[0], 10 ) - 1; + } if ( id >= 0 && id < c.columns ) { // if id is an integer filters[ id ] = res[1]; query.splice( indx, 1 ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js index a2acef5..d5a0ca7 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js @@ -1,10 +1,11 @@ -/*! Parser: dates - updated 10/26/2014 (v2.18.0) */ +/*! Parser: dates - updated 8/22/2016 (v2.27.5) */ /* Extract dates using popular natural language date parsers */ /*jshint jquery:true */ +/*global Sugar*/ ;(function($){ 'use strict'; - /*! Sugar (http://sugarjs.com/dates#comparing_dates) */ + /*! Sugar (https://sugarjs.com/docs/#/DateParsing) */ /* demo: http://jsfiddle.net/Mottie/abkNM/4163/ */ $.tablesorter.addParser({ id: 'sugar', @@ -12,7 +13,9 @@ return false; }, format: function(s) { - var date = Date.create ? Date.create(s) : s ? new Date(s) : s; + // Add support for sugar v2.0+ + var create = Date.create || Sugar.Date.create, + date = create ? create(s) : s ? new Date(s) : s; return date instanceof Date && isFinite(date) ? date.getTime() : s; }, type: 'numeric' diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 6da0854..20be06c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 8/17/2016 (v2.27.3) *//* +/*! Widget: filter - updated 8/22/2016 (v2.27.5) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -1339,7 +1339,17 @@ res = query[ indx ].split( ':' ); if ( res.length > 1 ) { // make the column a one-based index ( non-developers start counting from one :P ) - id = parseInt( res[0], 10 ) - 1; + if ( isNaN( res[0] ) ) { + $.each( c.headerContent, function( i, txt ) { + // multiple matches are possible + if ( txt.toLowerCase().indexOf( res[0] ) > -1 ) { + id = i; + filters[ id ] = res[1]; + } + }); + } else { + id = parseInt( res[0], 10 ) - 1; + } if ( id >= 0 && id < c.columns ) { // if id is an integer filters[ id ] = res[1]; query.splice( indx, 1 ); From 29fe14231313c8cda21385fbfd0f19e19fd7f8d8 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Thu, 1 Sep 2016 21:02:45 +0200 Subject: [PATCH 096/138] Update tablesorter to latest version (2.27.6) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 23 ++++++--- .../jquery-tablesorter/jquery.tablesorter.js | 21 ++++++-- .../jquery.tablesorter.widgets.js | 2 +- .../parsers/parser-input-select.js | 4 +- .../widgets/widget-grouping.js | 8 ++- .../jquery-tablesorter/widgets/widget-mark.js | 49 +++++++++++++++---- 10 files changed, 88 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bca4380..86ffdb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.22.4 (2016-09-01) + +* Upgrade tablesorter to v2.27.6 + #### v1.22.3 (2016-08-22) * Upgrade tablesorter to v2.27.5 diff --git a/README.md b/README.md index ef42493..91c6e88 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jquery-tablesorter into the asset pipeline. -Current tablesorter version: 2.27.5 (8/22/2016), [documentation] +Current tablesorter version: 2.27.6 (9/1/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index ab3aa61..cbd556b 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 22 - TINY = 3 + TINY = 4 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 27a931e..847b667 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 27a931e7859bf6ef2bcfd72f4114c6f27e9085eb +Subproject commit 847b6676f40fb10f86f1a2b4b4cb727cdd3ccfbc diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 01fb5fd..2e33337 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 08-22-2016 (v2.27.5)*/ +/*! tablesorter (FORK) - updated 09-01-2016 (v2.27.6)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.27.5 *//* +/*! TableSorter (FORK) v2.27.6 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.27.5', + version : '2.27.6', parsers : [], widgets : [], @@ -1639,8 +1639,9 @@ // sort multiple columns multisort : function( c ) { /*jshint loopfunc:true */ - var tbodyIndex, sortTime, colMax, rows, + var tbodyIndex, sortTime, colMax, rows, tmp, table = c.table, + sorter = [], dir = 0, textSorter = c.textSorter || '', sortList = c.sortList, @@ -1651,6 +1652,16 @@ return; } if ( c.debug ) { sortTime = new Date(); } + // cache textSorter to optimize speed + if ( typeof textSorter === 'object' ) { + colMax = c.columns; + while ( colMax-- ) { + tmp = ts.getColumnData( table, textSorter, colMax ); + if ( typeof tmp === 'function' ) { + sorter[ colMax ] = tmp; + } + } + } for ( tbodyIndex = 0; tbodyIndex < len; tbodyIndex++ ) { colMax = c.cache[ tbodyIndex ].colMax; rows = c.cache[ tbodyIndex ].normalized; @@ -1689,9 +1700,9 @@ if ( typeof textSorter === 'function' ) { // custom OVERALL text sorter sort = textSorter( x[ col ], y[ col ], dir, col, table ); - } else if ( typeof textSorter === 'object' && textSorter.hasOwnProperty( col ) ) { + } else if ( typeof sorter[ col ] === 'function' ) { // custom text sorter for a SPECIFIC COLUMN - sort = textSorter[ col ]( x[ col ], y[ col ], dir, col, table ); + sort = sorter[ col ]( x[ col ], y[ col ], dir, col, table ); } else { // fall back to natural sort sort = ts[ 'sortNatural' + ( dir ? 'Asc' : 'Desc' ) ]( a[ col ], b[ col ], col, c ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index a85b212..5279052 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.27.5 *//* +/*! TableSorter (FORK) v2.27.6 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.27.5', + version : '2.27.6', parsers : [], widgets : [], @@ -1621,8 +1621,9 @@ // sort multiple columns multisort : function( c ) { /*jshint loopfunc:true */ - var tbodyIndex, sortTime, colMax, rows, + var tbodyIndex, sortTime, colMax, rows, tmp, table = c.table, + sorter = [], dir = 0, textSorter = c.textSorter || '', sortList = c.sortList, @@ -1633,6 +1634,16 @@ return; } if ( c.debug ) { sortTime = new Date(); } + // cache textSorter to optimize speed + if ( typeof textSorter === 'object' ) { + colMax = c.columns; + while ( colMax-- ) { + tmp = ts.getColumnData( table, textSorter, colMax ); + if ( typeof tmp === 'function' ) { + sorter[ colMax ] = tmp; + } + } + } for ( tbodyIndex = 0; tbodyIndex < len; tbodyIndex++ ) { colMax = c.cache[ tbodyIndex ].colMax; rows = c.cache[ tbodyIndex ].normalized; @@ -1671,9 +1682,9 @@ if ( typeof textSorter === 'function' ) { // custom OVERALL text sorter sort = textSorter( x[ col ], y[ col ], dir, col, table ); - } else if ( typeof textSorter === 'object' && textSorter.hasOwnProperty( col ) ) { + } else if ( typeof sorter[ col ] === 'function' ) { // custom text sorter for a SPECIFIC COLUMN - sort = textSorter[ col ]( x[ col ], y[ col ], dir, col, table ); + sort = sorter[ col ]( x[ col ], y[ col ], dir, col, table ); } else { // fall back to natural sort sort = ts[ 'sortNatural' + ( dir ? 'Asc' : 'Desc' ) ]( a[ col ], b[ col ], col, c ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 64fb90b..ffe31ed 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 08-22-2016 (v2.27.5)*/ +/*! tablesorter (FORK) - updated 09-01-2016 (v2.27.6)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index e779e53..dfcbdee 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,4 +1,4 @@ -/*! Parser: input & select - updated 4/29/2016 (v2.25.9) *//* +/*! Parser: input & select - updated 9/1/2016 (v2.27.6) *//* * for jQuery 1.7+ & tablesorter 2.7.11+ * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ @@ -135,7 +135,7 @@ $table.children( 'thead' ).find( 'input[type="checkbox"]' ).each( function() { var column = $( this ).closest( 'td, th' ).attr( 'data-column' ), vis = $rows.filter( '.' + checkboxClass + '-' + column ).length, - allChecked = vis === len; + allChecked = vis === len && len > 0; if ( vis === 0 || allChecked ) { this.checked = allChecked; this.indeterminate = false; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index b0b5c60..d87b627 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -1,4 +1,4 @@ -/*! Widget: grouping - updated 7/31/2016 (v2.27.0) *//* +/*! Widget: grouping - updated 9/1/2016 (v2.27.6) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -57,6 +57,7 @@ part === 'day' ? month + ' ' + time.getDate() : part === 'week' ? tsg.findWeek( wo, time.getDay() ) : part === 'time' ? tsg.findTime( wo, time ) : + part === 'hour' ? tsg.findTime( wo, time, 'hour' ) : wo.group_dateString( time, c, $column ); } else { return wo.group_dateInvalid; @@ -78,7 +79,7 @@ return wo.group_week[ cldrWeek[ day ] ]; } }, - findTime : function( wo, time ) { + findTime : function( wo, time, part ) { var suffix, // CLDR returns { am: "AM", pm: "PM", ... } isObj = wo.group_time.am && wo.group_time.pm, @@ -89,6 +90,9 @@ hours = ( '00' + p24 ).slice(-2), min = ( '00' + time.getMinutes() ).slice(-2); suffix = wo.group_time[ isObj ? [ 'am', 'pm' ][ period ] : period ]; + if ( part === 'hour' ) { + return hours; + } return hours + ':' + min + ( wo.group_time24Hour ? '' : ' ' + ( suffix || '' ) ); }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-mark.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-mark.js index f311e7d..c09e28c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-mark.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-mark.js @@ -1,4 +1,4 @@ -/*! Widget: mark.js - updated 8/1/2016 (v2.27.2) *//* +/*! Widget: mark.js - updated 9/1/2016 (v2.27.6) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -11,9 +11,10 @@ if ( typeof $.fn.mark === 'function' ) { var tmp, update = c.widgetOptions.mark_tsUpdate; - c.$table.on( 'filterEnd.tsmark' + ( update ? ' ' + update : '' ), function( e, filters ) { + c.$table.on( 'filterEnd.tsmark pagerComplete.tsmark' + + ( update ? ' ' + update : '' ), function( e, filters ) { // filterEnd passes "config" as the param - ts.mark.update( c, e.type === 'filterEnd' ? '' : filters ); + ts.mark.update( c, e.type === update ? filters : '' ); }); // Regex to split up a query tmp = '(?:<|=|>|\\||\"|' + "\\'|" + @@ -33,7 +34,8 @@ }, checkRegex : function( regex ) { if ( regex instanceof RegExp ) { - // prevent lock up of mark.js (see https://github.com/julmot/mark.js/issues/55) + // prevent lock up of mark.js + // (see https://github.com/julmot/mark.js/issues/55) var result = '\u0001\u0002\u0003\u0004\u0005'.match( regex ); return result === null || result.length < 5; } @@ -49,10 +51,27 @@ } return results; }, + // used when "any" match is performed + ignoreColumns : function( c ) { + var wo = c.widgetOptions, + len = c.columns, + cols = []; + while (len--) { + if (wo.mark_tsIgnore[len] || + $( c.$headerIndexed[len] ).hasClass( 'mark-ignore' ) ) { + cols[cols.length] = ':nth-child(' + (len + 1) + ')'; + } + } + if (cols.length) { + return ':not(' + cols.join(',') + ')'; + } + return ''; + }, update : function( c, filters ) { var options = {}, wo = c.widgetOptions, - setIgnoreCase = typeof wo.filter_ignoreCase === 'undefined' ? true : wo.filter_ignoreCase, + setIgnoreCase = typeof wo.filter_ignoreCase === 'undefined' ? true : + wo.filter_ignoreCase, regex = ts.mark.regex, $rows = c.$table .find( 'tbody tr' ) @@ -68,11 +87,16 @@ } }); $.each( filters, function( indx, filter ) { - if ( filter ) { + if ( filter && + !( $(c.$headerIndexed[indx]).hasClass('mark-ignore') || + wo.mark_tsIgnore[indx] + ) ) { var testRegex = null, matches = filter, useRegex = false, - col = indx === c.columns ? '' : ':nth-child(' + ( indx + 1 ) + ')'; + col = indx === c.columns ? + ts.mark.ignoreColumns( c ) : + ':nth-child(' + ( indx + 1 ) + ')'; // regular expression entered if ( regex.pure.test( filter ) ) { matches = regex.pure.exec( filter ); @@ -122,7 +146,10 @@ } } else { // pass an array of matches - $rows.children( col ).mark( ts.mark.cleanMatches( matches ), options ); + $rows.children( col ).mark( + ts.mark.cleanMatches( matches ), + options + ); } } }); @@ -132,14 +159,16 @@ ts.addWidget({ id: 'mark', options: { - mark_tsUpdate : 'markUpdate' + mark_tsUpdate : 'markUpdate', + mark_tsIgnore : {} }, init : function( table, thisWidget, c, wo ) { ts.mark.init( c, wo ); }, remove : function( table, c ) { var update = c.widgetOptions.mark_tsUpdate; - c.$table.off( 'filterEnd.tsmark' + ( update ? ' ' + update : '' ) ); + c.$table.off( 'filterEnd.tsmark pagerComplete.tsmark' + + ( update ? ' ' + update : '' ) ); } }); From a07573bec94bc80349b56eb5a3db827ea65c5d98 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Sat, 17 Sep 2016 09:04:35 +0200 Subject: [PATCH 097/138] Minor updates and changes in Readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 91c6e88..fdbb133 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# jQuery Table Sorter plugin for Rails +# jQuery tablesorter plugin for Rails [](http://badge.fury.io/rb/jquery-tablesorter) -Simple integration of jquery-tablesorter into the asset pipeline. +Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. Current tablesorter version: 2.27.6 (9/1/2016), [documentation] @@ -24,7 +24,7 @@ Or install it yourself as: ## Requirements -It should work with Rails 3.2 and higher (tested up to 4.2) as well as with ruby 1.9.3 - 2.3.x. +It should work with Rails 3.2 and higher (tested up to 5) as well as with ruby 1.9.3 - 2.3.x. Each release is always tested with the latest version of both. ## Usage From 19d99a0f27547d456f4b290fd55b158e9bb625d8 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Fri, 23 Sep 2016 21:52:46 +0200 Subject: [PATCH 098/138] Update tablesorter to latest version (2.27.7) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 65 +++++++++-------- .../jquery.tablesorter.combined.js | 23 ++++--- .../jquery-tablesorter/jquery.tablesorter.js | 8 +-- .../jquery.tablesorter.widgets.js | 15 ++-- .../widgets/widget-filter.js | 13 ++-- .../jquery-tablesorter/widgets/widget-mark.js | 6 +- .../widgets/widget-pager.js | 69 +++++++++++-------- .../widgets/widget-repeatheaders.js | 6 +- .../addons/pager/jquery.tablesorter.pager.css | 2 +- 13 files changed, 122 insertions(+), 95 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86ffdb4..bfd0344 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.22.5 (2016-09-23) + +* Upgrade tablesorter to v2.27.7 + #### v1.22.4 (2016-09-01) * Upgrade tablesorter to v2.27.6 diff --git a/README.md b/README.md index fdbb133..15aa2b7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.27.6 (9/1/2016), [documentation] +Current tablesorter version: 2.27.7 (9/23/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index cbd556b..51c471e 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 22 - TINY = 4 + TINY = 5 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 847b667..570d3c0 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 847b6676f40fb10f86f1a2b4b4cb727cdd3ccfbc +Subproject commit 570d3c0c30cad65eb7da07c0f14ae6650c73a52b diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 55a778b..322262b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin -* updated 8/17/2016 (v2.27.3) +* updated 9/23/2016 (v2.27.7) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -182,27 +182,33 @@ p.startRow = (t) ? sz * p.page + 1 : (p.filteredRows === 0 ? 0 : sz * p.page + 1); p.endRow = Math.min( p.filteredRows, p.totalRows, sz * ( p.page + 1 ) ); $out = p.$container.find(p.cssPageDisplay); - // form the output string (can now get a new output string from the server) - s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || p.output : p.output ) - // {page} = one-based index; {page+#} = zero based index +/- value - .replace(/\{page([\-+]\d+)?\}/gi, function(m, n){ - return p.totalPages ? p.page + (n ? parseInt(n, 10) : 1) : 0; - }) - // {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object) - .replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m){ - var len, indx, - str = m.replace(/[{}\s]/g, ''), - extra = str.split(':'), - data = p.ajaxData, - // return zero for default page/row numbers - deflt = /(rows?|pages?)$/i.test(str) ? 0 : ''; - if (/(startRow|page)/.test(extra[0]) && extra[1] === 'input') { - len = ('' + (extra[0] === 'page' ? p.totalPages : p.totalRows)).length; - indx = extra[0] === 'page' ? p.page + 1 : p.startRow; - return '<input type="text" class="ts-' + extra[0] + '" style="max-width:' + len + 'em" value="' + indx + '"/>'; - } - return extra.length > 1 && data && data[extra[0]] ? data[extra[0]][extra[1]] : p[str] || (data ? data[str] : deflt) || deflt; - }); + + // Output param can be callback for custom rendering or string + if (typeof p.output === 'function') { + s = p.output(table, p); + } else { + // form the output string (can now get a new output string from the server) + s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || p.output : p.output ) + // {page} = one-based index; {page+#} = zero based index +/- value + .replace(/\{page([\-+]\d+)?\}/gi, function(m, n){ + return p.totalPages ? p.page + (n ? parseInt(n, 10) : 1) : 0; + }) + // {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object) + .replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m){ + var len, indx, + str = m.replace(/[{}\s]/g, ''), + extra = str.split(':'), + data = p.ajaxData, + // return zero for default page/row numbers + deflt = /(rows?|pages?)$/i.test(str) ? 0 : ''; + if (/(startRow|page)/.test(extra[0]) && extra[1] === 'input') { + len = ('' + (extra[0] === 'page' ? p.totalPages : p.totalRows)).length; + indx = extra[0] === 'page' ? p.page + 1 : p.startRow; + return '<input type="text" class="ts-' + extra[0] + '" style="max-width:' + len + 'em" value="' + indx + '"/>'; + } + return extra.length > 1 && data && data[extra[0]] ? data[extra[0]][extra[1]] : p[str] || (data ? data[str] : deflt) || deflt; + }); + } if ( p.$goto.length ) { t = ''; options = buildPageSelect( table, p ); @@ -865,7 +871,7 @@ }, enablePager = function(table, p, triggered) { - var info, size, + var info, size, $el, c = table.config; p.$size.add(p.$goto).add(p.$container.find('.ts-startRow, .ts-page')) .removeClass(p.cssDisabled) @@ -878,10 +884,15 @@ p.$size.val( p.size ); // set page size p.totalPages = p.size === 'all' ? 1 : Math.ceil( getTotalPages( table, p ) / p.size ); // if table id exists, include page display with aria info - if ( table.id ) { - info = table.id + '_pager_info'; - p.$container.find(p.cssPageDisplay).attr('id', info); - c.$table.attr('aria-describedby', info); + if ( table.id && !c.$table.attr( 'aria-describedby' ) ) { + $el = p.$container.find( p.cssPageDisplay ); + info = $el.attr( 'id' ); + if ( !info ) { + // only add pageDisplay id if it doesn't exist - see #1288 + info = table.id + '_pager_info'; + $el.attr( 'id', info ); + } + c.$table.attr( 'aria-describedby', info ); } changeHeight(table, p); if ( triggered ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 2e33337..d9d4efb 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 09-01-2016 (v2.27.6)*/ +/*! tablesorter (FORK) - updated 09-23-2016 (v2.27.7)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.27.6 *//* +/*! TableSorter (FORK) v2.27.7 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.27.6', + version : '2.27.7', parsers : [], widgets : [], @@ -251,7 +251,7 @@ // save the settings where they read $.data( table, 'tablesorter', c ); if ( c.debug ) { - console[ console.group ? 'group' : 'log' ]( 'Initializing tablesorter' ); + console[ console.group ? 'group' : 'log' ]( 'Initializing tablesorter v' + ts.version ); $.data( table, 'startoveralltimer', new Date() ); } @@ -353,7 +353,7 @@ table.hasInitialized = true; table.isProcessing = false; if ( c.debug ) { - console.log( 'Overall initialization time: ' + ts.benchmark( $.data( table, 'startoveralltimer' ) ) ); + console.log( 'Overall initialization time:' + ts.benchmark( $.data( table, 'startoveralltimer' ) ) ); if ( c.debug && console.groupEnd ) { console.groupEnd(); } } $table.triggerHandler( 'tablesorter-initialized', table ); @@ -3137,7 +3137,7 @@ })(jQuery); -/*! Widget: filter - updated 8/22/2016 (v2.27.5) *//* +/*! Widget: filter - updated 9/23/2016 (v2.27.7) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3970,9 +3970,9 @@ // don't allow 'change' event to process if the input value is the same - fixes #685 if ( table.config.widgetOptions.filter_initialized && ( event.which === tskeyCodes.enter || event.type === 'search' || - ( event.type === 'change' ) && this.value !== c.lastSearch[column] ) || - // only "input" event fires in MS Edge when clicking the "x" to clear the search - ( event.type === 'input' && this.value === '' ) ) { + ( event.type === 'change' || event.type === 'input' ) && + this.value !== c.lastSearch[column] ) + ) { event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); @@ -4687,6 +4687,7 @@ var cts, txt, indx, len, parsedTxt, str, c = table.config, validColumn = typeof column !== 'undefined' && column !== null && column >= 0 && column < c.columns, + direction = validColumn ? c.$headerIndexed[ column ].hasClass( 'filter-select-sort-desc' ) : false, parsed = []; // get unique elements and sort the list // if $.tablesorter.sortText exists ( not in the original tablesorter ), @@ -4727,8 +4728,8 @@ // sort parsed select options cts = c.textSorter || ''; parsed.sort( function( a, b ) { - var x = a.parsed, - y = b.parsed; + var x = direction ? b.parsed : a.parsed, + y = direction ? a.parsed : b.parsed; if ( validColumn && typeof cts === 'function' ) { // custom OVERALL text sorter return cts( x, y, true, column, table ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 5279052..20b3165 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.27.6 *//* +/*! TableSorter (FORK) v2.27.7 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.27.6', + version : '2.27.7', parsers : [], widgets : [], @@ -233,7 +233,7 @@ // save the settings where they read $.data( table, 'tablesorter', c ); if ( c.debug ) { - console[ console.group ? 'group' : 'log' ]( 'Initializing tablesorter' ); + console[ console.group ? 'group' : 'log' ]( 'Initializing tablesorter v' + ts.version ); $.data( table, 'startoveralltimer', new Date() ); } @@ -335,7 +335,7 @@ table.hasInitialized = true; table.isProcessing = false; if ( c.debug ) { - console.log( 'Overall initialization time: ' + ts.benchmark( $.data( table, 'startoveralltimer' ) ) ); + console.log( 'Overall initialization time:' + ts.benchmark( $.data( table, 'startoveralltimer' ) ) ); if ( c.debug && console.groupEnd ) { console.groupEnd(); } } $table.triggerHandler( 'tablesorter-initialized', table ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index ffe31ed..017e774 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 09-01-2016 (v2.27.6)*/ +/*! tablesorter (FORK) - updated 09-23-2016 (v2.27.7)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -382,7 +382,7 @@ })(jQuery); -/*! Widget: filter - updated 8/22/2016 (v2.27.5) *//* +/*! Widget: filter - updated 9/23/2016 (v2.27.7) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -1215,9 +1215,9 @@ // don't allow 'change' event to process if the input value is the same - fixes #685 if ( table.config.widgetOptions.filter_initialized && ( event.which === tskeyCodes.enter || event.type === 'search' || - ( event.type === 'change' ) && this.value !== c.lastSearch[column] ) || - // only "input" event fires in MS Edge when clicking the "x" to clear the search - ( event.type === 'input' && this.value === '' ) ) { + ( event.type === 'change' || event.type === 'input' ) && + this.value !== c.lastSearch[column] ) + ) { event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); @@ -1932,6 +1932,7 @@ var cts, txt, indx, len, parsedTxt, str, c = table.config, validColumn = typeof column !== 'undefined' && column !== null && column >= 0 && column < c.columns, + direction = validColumn ? c.$headerIndexed[ column ].hasClass( 'filter-select-sort-desc' ) : false, parsed = []; // get unique elements and sort the list // if $.tablesorter.sortText exists ( not in the original tablesorter ), @@ -1972,8 +1973,8 @@ // sort parsed select options cts = c.textSorter || ''; parsed.sort( function( a, b ) { - var x = a.parsed, - y = b.parsed; + var x = direction ? b.parsed : a.parsed, + y = direction ? a.parsed : b.parsed; if ( validColumn && typeof cts === 'function' ) { // custom OVERALL text sorter return cts( x, y, true, column, table ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 20be06c..60a6059 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 8/22/2016 (v2.27.5) *//* +/*! Widget: filter - updated 9/23/2016 (v2.27.7) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -831,9 +831,9 @@ // don't allow 'change' event to process if the input value is the same - fixes #685 if ( table.config.widgetOptions.filter_initialized && ( event.which === tskeyCodes.enter || event.type === 'search' || - ( event.type === 'change' ) && this.value !== c.lastSearch[column] ) || - // only "input" event fires in MS Edge when clicking the "x" to clear the search - ( event.type === 'input' && this.value === '' ) ) { + ( event.type === 'change' || event.type === 'input' ) && + this.value !== c.lastSearch[column] ) + ) { event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); @@ -1548,6 +1548,7 @@ var cts, txt, indx, len, parsedTxt, str, c = table.config, validColumn = typeof column !== 'undefined' && column !== null && column >= 0 && column < c.columns, + direction = validColumn ? c.$headerIndexed[ column ].hasClass( 'filter-select-sort-desc' ) : false, parsed = []; // get unique elements and sort the list // if $.tablesorter.sortText exists ( not in the original tablesorter ), @@ -1588,8 +1589,8 @@ // sort parsed select options cts = c.textSorter || ''; parsed.sort( function( a, b ) { - var x = a.parsed, - y = b.parsed; + var x = direction ? b.parsed : a.parsed, + y = direction ? a.parsed : b.parsed; if ( validColumn && typeof cts === 'function' ) { // custom OVERALL text sorter return cts( x, y, true, column, table ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-mark.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-mark.js index c09e28c..53d5a50 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-mark.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-mark.js @@ -1,4 +1,4 @@ -/*! Widget: mark.js - updated 9/1/2016 (v2.27.6) *//* +/*! Widget: mark.js - updated 9/23/2016 (v2.27.7) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -70,8 +70,6 @@ update : function( c, filters ) { var options = {}, wo = c.widgetOptions, - setIgnoreCase = typeof wo.filter_ignoreCase === 'undefined' ? true : - wo.filter_ignoreCase, regex = ts.mark.regex, $rows = c.$table .find( 'tbody tr' ) @@ -139,7 +137,7 @@ if ( useRegex && matches && matches.length ) { matches = new RegExp( ts.mark.cleanMatches( matches ).join( '.*' ), - 'gm' + ( setIgnoreCase ? 'i' : '' ) + 'gm' ); if ( ts.mark.checkRegex( matches ) ) { $rows.children( col ).markRegExp( matches, options ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index b8d2728..b3cd1b4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 8/17/2016 (v2.27.3) */ +/*! Widget: Pager - updated 9/23/2016 (v2.27.7) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -464,30 +464,36 @@ p.startRow = t ? sz * p.page + 1 : ( p.filteredRows === 0 ? 0 : sz * p.page + 1 ); p.endRow = Math.min( p.filteredRows, p.totalRows, sz * ( p.page + 1 ) ); $out = p.$container.find( wo.pager_selectors.pageDisplay ); - // form the output string (can now get a new output string from the server) - s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || wo.pager_output : wo.pager_output ) - // {page} = one-based index; {page+#} = zero based index +/- value - .replace( /\{page([\-+]\d+)?\}/gi, function( m, n ) { - return p.totalPages ? p.page + ( n ? parseInt( n, 10 ) : 1 ) : 0; - }) - // {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object) - .replace( /\{\w+(\s*:\s*\w+)?\}/gi, function( m ) { - var len, indx, - str = m.replace( /[{}\s]/g, '' ), - extra = str.split( ':' ), - data = p.ajaxData, - // return zero for default page/row numbers - deflt = /(rows?|pages?)$/i.test( str ) ? 0 : ''; - if ( /(startRow|page)/.test( extra[ 0 ] ) && extra[ 1 ] === 'input' ) { - len = ( '' + ( extra[ 0 ] === 'page' ? p.totalPages : p.totalRows ) ).length; - indx = extra[ 0 ] === 'page' ? p.page + 1 : p.startRow; - return '<input type="text" class="ts-' + extra[ 0 ] + - '" style="max-width:' + len + 'em" value="' + indx + '"/>'; - } - return extra.length > 1 && data && data[ extra[ 0 ] ] ? - data[ extra[ 0 ] ][ extra[ 1 ] ] : - p[ str ] || ( data ? data[ str ] : deflt ) || deflt; - }); + + // Output param can be callback for custom rendering or string + if ( typeof wo.pager_output === 'function' ) { + s = wo.pager_output( table, p ); + } else { + // form the output string (can now get a new output string from the server) + s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || wo.pager_output : wo.pager_output ) + // {page} = one-based index; {page+#} = zero based index +/- value + .replace( /\{page([\-+]\d+)?\}/gi, function( m, n ) { + return p.totalPages ? p.page + ( n ? parseInt( n, 10 ) : 1 ) : 0; + }) + // {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object) + .replace( /\{\w+(\s*:\s*\w+)?\}/gi, function( m ) { + var len, indx, + str = m.replace( /[{}\s]/g, '' ), + extra = str.split( ':' ), + data = p.ajaxData, + // return zero for default page/row numbers + deflt = /(rows?|pages?)$/i.test( str ) ? 0 : ''; + if ( /(startRow|page)/.test( extra[ 0 ] ) && extra[ 1 ] === 'input' ) { + len = ( '' + ( extra[ 0 ] === 'page' ? p.totalPages : p.totalRows ) ).length; + indx = extra[ 0 ] === 'page' ? p.page + 1 : p.startRow; + return '<input type="text" class="ts-' + extra[ 0 ] + + '" style="max-width:' + len + 'em" value="' + indx + '"/>'; + } + return extra.length > 1 && data && data[ extra[ 0 ] ] ? + data[ extra[ 0 ] ][ extra[ 1 ] ] : + p[ str ] || ( data ? data[ str ] : deflt ) || deflt; + }); + } if ( p.$goto.length ) { t = ''; options = tsp.buildPageSelect( c, p ); @@ -1199,7 +1205,7 @@ }, enablePager: function( c, triggered ) { - var info, size, + var info, size, $el, table = c.table, p = c.pager; p.isDisabled = false; @@ -1210,9 +1216,14 @@ p.totalPages = p.size === 'all' ? 1 : Math.ceil( tsp.getTotalPages( c, p ) / p.size ); c.$table.removeClass( 'pagerDisabled' ); // if table id exists, include page display with aria info - if ( table.id ) { - info = table.id + '_pager_info'; - p.$container.find( c.widgetOptions.pager_selectors.pageDisplay ).attr( 'id', info ); + if ( table.id && !c.$table.attr( 'aria-describedby' ) ) { + $el = p.$container.find( c.widgetOptions.pager_selectors.pageDisplay ); + info = $el.attr( 'id' ); + if ( !info ) { + // only add pageDisplay id if it doesn't exist - see #1288 + info = table.id + '_pager_info'; + $el.attr( 'id', info ); + } c.$table.attr( 'aria-describedby', info ); } tsp.changeHeight( c ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js index 00c17ea..68a0d7e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js @@ -1,4 +1,4 @@ -/*! Widget: repeatHeaders - updated 2/7/2015 (v2.19.0) *//* +/*! Widget: repeatHeaders - updated 9/23/2016 (v2.27.7) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * Original by Christian Bach from the example-widgets.html demo */ @@ -19,8 +19,8 @@ if (!wo.repeatHeaders) { h = '<tr class="repeated-header ' + c.selectorRemove.slice(1) + '">'; for (i = 0; i < c.columns; i++) { - // only get the headerContent text - h += '<th>' + $.trim( c.$headers.eq(i).text() ) + '</th>'; + // repeat the content of the current header (including child elements) + h += '<th>' + $.trim( c.$headers.eq(i).html() ) + '</th>'; } // 'remove-me' class was added in case the table needs to be updated, the 'remove-me' rows will be // removed prior to the update to prevent including the rows in the update - see 'selectorRemove' option diff --git a/vendor/assets/stylesheets/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.css b/vendor/assets/stylesheets/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.css index e77cd88..da4f2cc 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.css @@ -39,4 +39,4 @@ td.tablesorter-pager { opacity: 0.5; filter: alpha(opacity=50); cursor: default; -} \ No newline at end of file +} From f74c329e83856ee07c1af49f287c4e2beaf9d29a Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Wed, 28 Sep 2016 23:04:52 +0200 Subject: [PATCH 099/138] Add beta-testing directory to gem --- README.md | 3 + Rakefile | 6 + .../beta-testing/pager-custom-controls.js | 143 ++++++++++++++ .../beta-testing/widget-reorder.js | 182 ++++++++++++++++++ 4 files changed, 334 insertions(+) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/beta-testing/pager-custom-controls.js create mode 100644 vendor/assets/javascripts/jquery-tablesorter/beta-testing/widget-reorder.js diff --git a/README.md b/README.md index 15aa2b7..2e4659a 100644 --- a/README.md +++ b/README.md @@ -49,8 +49,11 @@ Or you can include single file with: //= require jquery-tablesorter/widgets/widget-repeatheaders //= require jquery-tablesorter/parsers/parser-metric //= require jquery-tablesorter/extras/jquery.quicksearch +//= require jquery-tablesorter/beta-testing/pager-custom-controls ``` +Please note that files in the beta-testing directory might move into the stable tablesorter or get renamed with upcoming tablesorter releases and thus could have to be required in an updated way after upgrading this gem. + ### Stylesheet files In your `application.css` diff --git a/Rakefile b/Rakefile index ed79d3c..2a3babe 100755 --- a/Rakefile +++ b/Rakefile @@ -44,6 +44,12 @@ namespace :jquery_tablesorter do copy_files(files, folder_javascript_dir) end + # beta-testing + # + beta_dir = File.join(javascript_dir, 'beta-testing') + beta_files = Dir.glob(File.join('tablesorter', 'beta-testing', '*.js')).reject{|file| file =~ /.min.js\Z/} + copy_files(beta_files, beta_dir) + end def copy_files(files, target_dir) diff --git a/vendor/assets/javascripts/jquery-tablesorter/beta-testing/pager-custom-controls.js b/vendor/assets/javascripts/jquery-tablesorter/beta-testing/pager-custom-controls.js new file mode 100644 index 0000000..66b75ac --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/beta-testing/pager-custom-controls.js @@ -0,0 +1,143 @@ +/*! + * custom pager controls (beta) for Tablesorter - updated 9/1/2016 (v2.27.6) + initialize custom pager script BEFORE initializing tablesorter/tablesorter pager + custom pager looks like this: + 1 | 2 … 5 | 6 | 7 … 99 | 100 + _ _ _ _ adjacentSpacer + _ _ distanceSpacer + _____ ________ ends (2 default) + _________ aroundCurrent (1 default) + + */ +/*jshint browser:true, jquery:true, unused:false, loopfunc:true */ +/*global jQuery: false */ + +;(function($) { +"use strict"; + +$.tablesorter = $.tablesorter || {}; + +$.tablesorter.customPagerControls = function(settings) { + var defaults = { + table : 'table', + pager : '.pager', + pageSize : '.left a', + currentPage : '.right a', + ends : 2, // number of pages to show of either end + aroundCurrent : 1, // number of pages surrounding the current page + link : '<a href="#">{page}</a>', // page element; use {page} to include the page number + currentClass : 'current', // current page class name + adjacentSpacer : '<span> | </span>', // spacer for page numbers next to each other + distanceSpacer : '<span> … <span>', // spacer for page numbers away from each other (ellipsis) + addKeyboard : true, // use left,right,up,down,pageUp,pageDown,home, or end to change current page + pageKeyStep : 10 // page step to use for pageUp and pageDown + }, + options = $.extend({}, defaults, settings), + $table = $(options.table), + $pager = $(options.pager); + + $table + .on('pagerInitialized pagerComplete', function (e, c) { + var indx, + p = c.pager ? c.pager : c, // using widget + pages = $('<div/>'), + cur = p.page + 1, + pageArray = [], + max = p.filteredPages, + around = options.aroundCurrent; + for (indx = -around; indx <= around; indx++) { + if (cur + indx >= 1 && cur + indx <= max) { + pageArray.push(cur + indx); + } + } + if (pageArray.length) { + // include first and last pages (ends) in the pagination + for (indx = 0; indx < options.ends; indx++) { + if ((indx + 1 <= max) && $.inArray(indx + 1, pageArray) === -1) { + pageArray.push(indx + 1); + } + if ((max - indx > 0) && $.inArray(max - indx, pageArray) === -1) { + pageArray.push(max - indx); + } + } + // sort the list + pageArray = pageArray.sort(function(a, b) { return a - b; }); + // only include unique pages + pageArray = $.grep(pageArray, function(value, key) { + return $.inArray(value, pageArray) === key; + }); + // make links and spacers + if (pageArray.length) { + max = pageArray.length - 1; + $.each(pageArray, function(indx, value) { + pages + .append( + $(options.link.replace(/\{page\}/g, value)) + .toggleClass(options.currentClass, value === cur) + .attr('data-page', value) + ) + .append((indx < max && (pageArray[ indx + 1 ] - 1 !== value) ? + options.distanceSpacer : + (indx >= max ? '' : options.adjacentSpacer) + )); + }); + } + } + $pager + .find('.pagecount') + .html(pages.html()) + .find('.' + options.currentClass) + .focus(); + }); + + // set up pager controls + $pager + .find(options.pageSize) + .on('click', function () { + $(this) + .addClass(options.currentClass) + .siblings() + .removeClass(options.currentClass); + $table.trigger('pageSize', $(this).html()); + return false; + }) + .end() + .on('click', options.currentPage, function() { + var $el = $(this); + $el + .addClass(options.currentClass) + .siblings() + .removeClass(options.currentClass); + $table.trigger('pageSet', $el.attr('data-page')); + return false; + }); + + // make right/left arrow keys work + if (options.addKeyboard) { + $(document).on('keydown', function(events) { + // ignore arrows inside form elements + if (/input|select|textarea/i.test(events.target.nodeName) || + !(events.which > 32 && events.which < 41)) { + return; + } + // only allow keyboard use if element inside of pager is focused + if ($(document.activeElement).closest(options.pager).is($pager)) { + events.preventDefault(); + var key = events.which, + max = $table[0].config.totalRows, + $el = $pager.find(options.currentPage).filter('.' + options.currentClass), + page = $el.length ? parseInt($el.attr('data-page'), 10) : null; + if (page) { + if (key === 33) { page -= options.pageKeyStep; } // pageUp + if (key === 34) { page += options.pageKeyStep; } // pageDown + if (key === 35) { page = max; } // end + if (key === 36) { page = 1; } // home + if (key === 37 || key === 38) { page -= 1; } // left/up + if (key === 39 || key === 40) { page += 1; } // right/down + $table.trigger('pageSet', page); + } + } + }); + } +}; +})(jQuery); \ No newline at end of file diff --git a/vendor/assets/javascripts/jquery-tablesorter/beta-testing/widget-reorder.js b/vendor/assets/javascripts/jquery-tablesorter/beta-testing/widget-reorder.js new file mode 100644 index 0000000..1a7733f --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/beta-testing/widget-reorder.js @@ -0,0 +1,182 @@ +/*! tablesorter column reorder - beta testing +* Requires tablesorter v2.8+ and jQuery 1.7+ +* by Rob Garrison +*/ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;(function($){ + "use strict"; + +$.tablesorter.addWidget({ + id: 'reorder', + priority: 70, + options : { + reorder_axis : 'xy', // x or xy + reorder_delay : 300, + reorder_helperClass : 'tablesorter-reorder-helper', + reorder_helperBar : 'tablesorter-reorder-helper-bar', + reorder_noReorder : 'reorder-false', + reorder_blocked : 'reorder-block-left reorder-block-end', + reorder_complete : null // callback + }, + init: function(table, thisWidget, c, wo) { + var i, timer, $helper, $bar, clickOffset, + lastIndx = -1, + ts = $.tablesorter, + endIndex = -1, + startIndex = -1, + t = wo.reorder_blocked.split(' '), + noReorderLeft = t[0] || 'reorder-block-left', + noReorderLast = t[1] || 'reorder-block-end', + lastOffset = c.$headers.not('.' + noReorderLeft).first(), + offsets = c.$headers.map(function(i){ + var s, $t = $(this); + if ($t.hasClass(noReorderLeft)) { + s = lastOffset; + $t = s; + //lastOffset = $t; + } + lastOffset = $t; + return $t.offset().left; + }).get(), + len = offsets.length, + startReorder = function(e, $th){ + var p = $th.position(), + r = $th.parent().position(), + i = startIndex = $th.index(); + clickOffset = [ e.pageX - p.left, e.pageY - r.top ]; + $helper = c.$table.clone(); + $helper.find('> thead > tr:first').children('[data-column!=' + i + ']').remove(); + $helper.find('thead tr:gt(0), caption, colgroup, tbody, tfoot').remove(); + $helper + .css({ + position: 'absolute', + zIndex : 1, + left: p.left - clickOffset[0], + top: r.top - clickOffset[1], + width: $th.outerWidth() + }) + .appendTo('body') + .find('th, td').addClass(wo.reorder_helperClass); + $bar = $('<div class="' + wo.reorder_helperBar + '" />') + .css({ + position : 'absolute', + top : c.$table.find('thead').offset().top, + height : $th.closest('thead').outerHeight() + c.$table.find('tbody').height() + }) + .appendTo('body'); + positionBar(e); + lastIndx = endIndex; + }, + positionBar = function(e){ + for (i = 0; i <= len; i++) { + if ( i > 0 && e.pageX < offsets[i-1] + (offsets[i] - offsets[i-1])/2 && !c.$headers.eq(i).hasClass(noReorderLeft) ) { + endIndex = i - 1; + // endIndex = offsets.lastIndexOf( offsets[i-1] ); // lastIndexOf not supported by IE8 and older + if (endIndex >= 0 && lastIndx === endIndex) { return false; } + lastIndx = endIndex; + if (c.debug) { + console.log( endIndex === 0 ? 'target before column 0' : endIndex === len ? 'target after last column' : 'target between columns ' + startIndex + ' and ' + endIndex); + } + $bar.css('left', offsets[i-1]); + return false; + } + } + if (endIndex < 0) { + endIndex = len; + $bar.css('left', offsets[len]); + } + }, + finishReorder = function(){ + $helper.remove(); + $bar.remove(); + // finish reorder + var adj, s = startIndex, + rows = c.$table.find('tr'), + cols; + startIndex = -1; // stop mousemove updates + if ( s > -1 && endIndex > -1 && s != endIndex && s + 1 !== endIndex ) { + adj = endIndex !== 0; + if (c.debug) { + console.log( 'Inserting column ' + s + (adj ? ' after' : ' before') + ' column ' + (endIndex - adj ? 1 : 0) ); + } + rows.each(function() { + cols = $(this).children(); + cols.eq(s)[ adj ? 'insertAfter' : 'insertBefore' ]( cols.eq( endIndex - (adj ? 1 : 0) ) ); + }); + cols = []; + // stored header info needs to be modified too! + for (i = 0; i < len; i++) { + if (i === s) { continue; } + if (i === endIndex - (adj ? 1 : 0)) { + if (!adj) { cols.push(c.headerContent[s]); } + cols.push(c.headerContent[i]); + if (adj) { cols.push(c.headerContent[s]); } + } else { + cols.push(c.headerContent[i]); + } + } + c.headerContent = cols; + // cols = c.headerContent.splice(s, 1); + // c.headerContent.splice(endIndex - (adj ? 1 : 0), 0, cols); + c.$table.trigger('updateAll', [ true, wo.reorder_complete ]); + } + endIndex = -1; + }, + mdown = function(e, el){ + var $t = $(el), evt = e; + if ($t.hasClass(wo.reorder_noReorder)) { return; } + timer = setTimeout(function(){ + $t.addClass('tablesorter-reorder'); + startReorder(evt, $t); + }, wo.reorder_delay); + }; + + console.log( c.$headers.last().hasClass(noReorderLast) ); + + if ( c.$headers.last().hasClass(noReorderLast) ) { + offsets.push( offsets[ offsets.length - 1 ] ); + } else { + offsets.push( c.$table.offset().left + c.$table.outerWidth() ); + } + + c.$headers.not('.' + wo.reorder_noReorder).bind('mousedown.reorder', function(e){ + mdown(e, this); + }); + + $(document) + .bind('mousemove.reorder', function(e){ + if (startIndex !== -1){ + var c = { left : e.pageX - clickOffset[0] }; + endIndex = -1; + if (/y/.test(wo.reorder_axis)) { + c.top = e.pageY - clickOffset[1]; + } + $helper.css(c); + positionBar(e); + } + }) + .add( c.$headers ) + .bind('mouseup.reorder', function(){ + clearTimeout(timer); + if (startIndex !== -1 && endIndex !== -1){ + finishReorder(); + } else { + startIndex = -1; + } + }); + + // has sticky headers? + c.$table.bind('stickyHeadersInit', function(){ + wo.$sticky.find('thead').children().not('.' + wo.reorder_noReorder).bind('mousedown.reorder', function(e){ + mdown(e, this); + }); + }); + + } +}); + +// add mouse coordinates +$x = $('#main h1:last'); $(document).mousemove(function(e){ $x.html( e.pageX ); }); + +})(jQuery); \ No newline at end of file From 929504ce40014906517f68d584342052afe02b95 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Wed, 28 Sep 2016 23:42:18 +0200 Subject: [PATCH 100/138] Version bump --- CHANGELOG.md | 4 ++++ lib/jquery-tablesorter/version.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfd0344..e5b92bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.22.6 (2016-09-28) + +* Include beta-testing files from tablesorter + #### v1.22.5 (2016-09-23) * Upgrade tablesorter to v2.27.7 diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 51c471e..6233412 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 22 - TINY = 5 + TINY = 6 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end From 96d82501248847ac7d05770165f86e26ccfff009 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Thu, 29 Sep 2016 08:03:03 +0200 Subject: [PATCH 101/138] Update tablesorter to latest version (2.27.8) --- CHANGELOG.md | 4 +++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../beta-testing/pager-custom-controls.js | 26 ++++++++++++------- .../jquery.tablesorter.combined.js | 14 +++++----- .../jquery-tablesorter/jquery.tablesorter.js | 12 ++++----- .../jquery.tablesorter.widgets.js | 2 +- .../widgets/widget-columnSelector.js | 6 +++-- 9 files changed, 42 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5b92bd..186df9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.22.7 (2016-09-29) + +* Upgrade tablesorter to v2.27.8 + #### v1.22.6 (2016-09-28) * Include beta-testing files from tablesorter diff --git a/README.md b/README.md index 2e4659a..5c50c55 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.27.7 (9/23/2016), [documentation] +Current tablesorter version: 2.27.8 (9/28/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 6233412..68dc2c5 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 22 - TINY = 6 + TINY = 7 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 570d3c0..d73e951 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 570d3c0c30cad65eb7da07c0f14ae6650c73a52b +Subproject commit d73e9513e0a487467e3b1dd394b1e38bb1af6cf3 diff --git a/vendor/assets/javascripts/jquery-tablesorter/beta-testing/pager-custom-controls.js b/vendor/assets/javascripts/jquery-tablesorter/beta-testing/pager-custom-controls.js index 66b75ac..e6ba676 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/beta-testing/pager-custom-controls.js +++ b/vendor/assets/javascripts/jquery-tablesorter/beta-testing/pager-custom-controls.js @@ -1,5 +1,5 @@ /*! - * custom pager controls (beta) for Tablesorter - updated 9/1/2016 (v2.27.6) + * custom pager controls (beta) for Tablesorter - updated 9/28/2016 (v2.27.8) initialize custom pager script BEFORE initializing tablesorter/tablesorter pager custom pager looks like this: 1 | 2 … 5 | 6 | 7 … 99 | 100 @@ -13,7 +13,7 @@ /*global jQuery: false */ ;(function($) { -"use strict"; +'use strict'; $.tablesorter = $.tablesorter || {}; @@ -34,9 +34,13 @@ $.tablesorter.customPagerControls = function(settings) { }, options = $.extend({}, defaults, settings), $table = $(options.table), - $pager = $(options.pager); + $pager = $(options.pager), + focusOnPager = false; $table + .on('filterStart', function() { + focusOnPager = false; + }) .on('pagerInitialized pagerComplete', function (e, c) { var indx, p = c.pager ? c.pager : c, // using widget @@ -83,11 +87,11 @@ $.tablesorter.customPagerControls = function(settings) { }); } } - $pager - .find('.pagecount') - .html(pages.html()) - .find('.' + options.currentClass) - .focus(); + $pager.find('.pagecount').html(pages.html()); + if (focusOnPager) { + // don't focus on pager when using filter - fixes #1296 + $pager.find('.' + options.currentClass).focus(); + } }); // set up pager controls @@ -103,6 +107,7 @@ $.tablesorter.customPagerControls = function(settings) { }) .end() .on('click', options.currentPage, function() { + focusOnPager = true; var $el = $(this); $el .addClass(options.currentClass) @@ -118,11 +123,13 @@ $.tablesorter.customPagerControls = function(settings) { // ignore arrows inside form elements if (/input|select|textarea/i.test(events.target.nodeName) || !(events.which > 32 && events.which < 41)) { + focusOnPager = false; return; } // only allow keyboard use if element inside of pager is focused if ($(document.activeElement).closest(options.pager).is($pager)) { events.preventDefault(); + focusOnPager = true; var key = events.which, max = $table[0].config.totalRows, $el = $pager.find(options.currentPage).filter('.' + options.currentClass), @@ -140,4 +147,5 @@ $.tablesorter.customPagerControls = function(settings) { }); } }; -})(jQuery); \ No newline at end of file + +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index d9d4efb..16e9fed 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 09-23-2016 (v2.27.7)*/ +/*! tablesorter (FORK) - updated 09-28-2016 (v2.27.8)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.27.7 *//* +/*! TableSorter (FORK) v2.27.8 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.27.7', + version : '2.27.8', parsers : [], widgets : [], @@ -92,7 +92,7 @@ widgetClass : 'widget-{name}', // table class name template to match to include a widget widgets : [], // method to add widgets, e.g. widgets: ['zebra'] widgetOptions : { - zebra : [ 'even', 'odd' ] // zebra widget alternating row class names + zebra : [ 'even', 'odd' ] // zebra widget alternating row class names }, // *** callbacks @@ -109,8 +109,8 @@ cssChildRow : 'tablesorter-childRow', // class name indiciating that a row is to be attached to its parent cssInfoBlock : 'tablesorter-infoOnly', // don't sort tbody with this class name (only one class name allowed here!) - cssNoSort : 'tablesorter-noSort', // class name added to element inside header; clicking on it won't cause a sort - cssIgnoreRow : 'tablesorter-ignoreRow', // header row to ignore; cells within this row will not be added to c.$headers + cssNoSort : 'tablesorter-noSort', // class name added to element inside header; clicking on it won't cause a sort + cssIgnoreRow : 'tablesorter-ignoreRow',// header row to ignore; cells within this row will not be added to c.$headers cssIcon : 'tablesorter-icon', // if this class does not exist, the {icon} will not be added from the headerTemplate cssIconNone : '', // class name added to the icon when there is no column sort @@ -124,7 +124,7 @@ // *** selectors selectorHeaders : '> thead th, > thead td', - selectorSort : 'th, td', // jQuery selector of content within selectorHeaders that is clickable to trigger a sort + selectorSort : 'th, td', // jQuery selector of content within selectorHeaders that is clickable to trigger a sort selectorRemove : '.remove-me', // *** advanced diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 20b3165..10ee4a7 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.27.7 *//* +/*! TableSorter (FORK) v2.27.8 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.27.7', + version : '2.27.8', parsers : [], widgets : [], @@ -74,7 +74,7 @@ widgetClass : 'widget-{name}', // table class name template to match to include a widget widgets : [], // method to add widgets, e.g. widgets: ['zebra'] widgetOptions : { - zebra : [ 'even', 'odd' ] // zebra widget alternating row class names + zebra : [ 'even', 'odd' ] // zebra widget alternating row class names }, // *** callbacks @@ -91,8 +91,8 @@ cssChildRow : 'tablesorter-childRow', // class name indiciating that a row is to be attached to its parent cssInfoBlock : 'tablesorter-infoOnly', // don't sort tbody with this class name (only one class name allowed here!) - cssNoSort : 'tablesorter-noSort', // class name added to element inside header; clicking on it won't cause a sort - cssIgnoreRow : 'tablesorter-ignoreRow', // header row to ignore; cells within this row will not be added to c.$headers + cssNoSort : 'tablesorter-noSort', // class name added to element inside header; clicking on it won't cause a sort + cssIgnoreRow : 'tablesorter-ignoreRow',// header row to ignore; cells within this row will not be added to c.$headers cssIcon : 'tablesorter-icon', // if this class does not exist, the {icon} will not be added from the headerTemplate cssIconNone : '', // class name added to the icon when there is no column sort @@ -106,7 +106,7 @@ // *** selectors selectorHeaders : '> thead th, > thead td', - selectorSort : 'th, td', // jQuery selector of content within selectorHeaders that is clickable to trigger a sort + selectorSort : 'th, td', // jQuery selector of content within selectorHeaders that is clickable to trigger a sort selectorRemove : '.remove-me', // *** advanced diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 017e774..ebb97ae 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 09-23-2016 (v2.27.7)*/ +/*! tablesorter (FORK) - updated 09-28-2016 (v2.27.8)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index eced613..cb21451 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Widget: columnSelector (responsive table widget) - updated 7/31/2016 (v2.27.1) *//* +/* Widget: columnSelector (responsive table widget) - updated 9/28/2016 (v2.27.8) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -208,7 +208,9 @@ tsColSel.updateBreakpoints(c, wo); c.$table .off('updateAll' + namespace) - .on('updateAll' + namespace, function(){ + .on('updateAll' + namespace, function() { + tsColSel.setupSelector(c, wo); + tsColSel.setupBreakpoints(c, wo); tsColSel.updateBreakpoints(c, wo); tsColSel.updateCols(c, wo); }); From b2f1608a970489b359d3f50298c10015f33dbd0d Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 28 Nov 2016 16:21:43 +0100 Subject: [PATCH 102/138] Update tablesorter to latest version (2.28.0) --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 4 ++-- tablesorter | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 186df9a..4765821 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.23.0 (2016-11-28) + +* Upgrade tablesorter to v2.28.0 + #### v1.22.7 (2016-09-29) * Upgrade tablesorter to v2.27.8 diff --git a/README.md b/README.md index 5c50c55..a16dca5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.27.8 (9/28/2016), [documentation] +Current tablesorter version: 2.28.0 (11/27/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 68dc2c5..4d91d82 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 - MINOR = 22 - TINY = 7 + MINOR = 23 + TINY = 0 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index d73e951..920dd01 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit d73e9513e0a487467e3b1dd394b1e38bb1af6cf3 +Subproject commit 920dd01baac9ea281fb26444a9d8489950583ebd From b13967e74696ea30889e85cc670fe8fa4f10d055 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <erik.ernst@sic-software.com> Date: Tue, 29 Nov 2016 08:59:16 +0100 Subject: [PATCH 103/138] Add actual files of v2.28.0 (facepalm!) Bump gem version to 1.23.1. --- CHANGELOG.md | 4 + lib/jquery-tablesorter/version.rb | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 447 +++++++++--------- .../jquery.tablesorter.combined.js | 75 ++- .../jquery-tablesorter/jquery.tablesorter.js | 50 +- .../jquery.tablesorter.widgets.js | 25 +- .../parsers/parser-date-two-digit-year.js | 5 +- .../parsers/parser-input-select.js | 6 +- .../widgets/widget-build-table.js | 5 +- .../widgets/widget-filter.js | 10 +- .../widgets/widget-grouping.js | 5 +- .../jquery-tablesorter/widgets/widget-math.js | 19 +- .../widgets/widget-pager.js | 10 +- .../widgets/widget-sortTbodies.js | 14 +- .../widgets/widget-storage.js | 13 +- 15 files changed, 436 insertions(+), 254 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4765821..4756c26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.23.1 (2016-11-29) + +* Add actual files of v2.28.0 (facepalm!) + #### v1.23.0 (2016-11-28) * Upgrade tablesorter to v2.28.0 diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 4d91d82..cc9ea08 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 23 - TINY = 0 + TINY = 1 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 322262b..e954113 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin -* updated 9/23/2016 (v2.27.7) +* updated 11/26/2016 (v2.28.0) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -128,16 +128,25 @@ // hide arrows at extremes pagerArrows = function( table, p, disable ) { - var a = 'addClass', - r = 'removeClass', - d = p.cssDisabled, - dis = !!disable, - first = ( dis || p.page === 0 ), - tp = getTotalPages( table, p ), - last = ( dis || (p.page === tp - 1) || tp === 0 ); + var tmp, + a = 'addClass', + r = 'removeClass', + d = p.cssDisabled, + dis = !!disable, + first = ( dis || p.page === 0 ), + tp = getTotalPages( table, p ), + last = ( dis || (p.page === tp - 1) || tp === 0 ); if ( p.updateArrows ) { - p.$container.find(p.cssFirst + ',' + p.cssPrev)[ first ? a : r ](d).attr('aria-disabled', first); - p.$container.find(p.cssNext + ',' + p.cssLast)[ last ? a : r ](d).attr('aria-disabled', last); + tmp = p.$container.find(p.cssFirst + ',' + p.cssPrev); + tmp[ first ? a : r ](d); // toggle disabled class + tmp.each(function(){ + this.ariaDisabled = first; + }); + tmp = p.$container.find(p.cssNext + ',' + p.cssLast); + tmp[ last ? a : r ](d); + tmp.each(function(){ + this.ariaDisabled = last; + }); } }, @@ -689,9 +698,8 @@ .add( p.$container.find( '.ts-startRow, .ts-page' ) ); len = $controls.length; for ( index = 0; index < len; index++ ) { - $controls.eq( index ) - .attr( 'aria-disabled', 'true' ) - .addClass( p.cssDisabled )[0].disabled = true; + $controls.eq( index ).addClass( p.cssDisabled )[0].disabled = true; + $controls[ index ].ariaDisabled = true; } }, @@ -850,6 +858,12 @@ updatePageDisplay(table, p); }, + resetState = function(table, p) { + var c = table.config; + c.pager = $.extend( true, {}, $.tablesorterPager.defaults, p.settings ); + init(table, p.settings); + }, + destroyPager = function(table, p) { var c = table.config, namespace = c.namespace + 'pager', @@ -876,7 +890,9 @@ p.$size.add(p.$goto).add(p.$container.find('.ts-startRow, .ts-page')) .removeClass(p.cssDisabled) .removeAttr('disabled') - .attr('aria-disabled', 'false'); + .each(function(){ + this.ariaDisabled = false; + }); p.isDisabled = false; p.page = $.data(table, 'pagerLastPage') || p.page || 0; size = p.$size.find('option[selected]').val(); @@ -904,6 +920,210 @@ console.log('Pager: Enabled'); } } + }, + + init = function(table, settings) { + var t, ctrls, fxn, size, + c = table.config, + wo = c.widgetOptions, + p = c.pager = $.extend( true, {}, $.tablesorterPager.defaults, settings ), + $t = c.$table, + namespace = c.namespace + 'pager', + // added in case the pager is reinitialized after being destroyed. + pager = p.$container = $(p.container).addClass('tablesorter-pager').show(); + // save a copy of the original settings + p.settings = $.extend( true, {}, $.tablesorterPager.defaults, settings ); + if (c.debug) { + console.log('Pager: Initializing'); + } + p.oldAjaxSuccess = p.oldAjaxSuccess || p.ajaxObject.success; + c.appender = $this.appender; + p.initializing = true; + if (p.savePages && ts.storage) { + t = ts.storage(table, p.storageKey) || {}; // fixes #387 + p.page = isNaN(t.page) ? p.page : t.page; + p.size = t.size === 'all' ? t.size : ( isNaN( t.size ) ? p.size : t.size ) || p.setSize || 10; + $.data(table, 'pagerLastSize', p.size); + pager.find(p.cssPageSize).val(p.size); + } + // skipped rows + p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.slice(1) + '|' + c.cssChildRow + ')'); + p.regexFiltered = new RegExp(wo.filter_filteredRow || 'filtered'); + + $t + // .unbind( namespace ) adding in jQuery 1.4.3 ( I think ) + .unbind( pagerEvents.split(' ').join(namespace + ' ').replace(/\s+/g, ' ') ) + .bind('filterInit filterStart '.split(' ').join(namespace + ' '), function(e, filters) { + p.currentFilters = $.isArray(filters) ? filters : c.$table.data('lastSearch'); + // don't change page if filters are the same (pager updating, etc) + if (e.type === 'filterStart' && p.pageReset !== false && (c.lastCombinedFilter || '') !== (p.currentFilters || []).join('')) { + p.page = p.pageReset; // fixes #456 & #565 + } + }) + // update pager after filter widget completes + .bind('filterEnd sortEnd '.split(' ').join(namespace + ' '), function() { + p.currentFilters = c.$table.data('lastSearch'); + if (p.initialized || p.initializing) { + if (c.delayInit && c.rowsCopy && c.rowsCopy.length === 0) { + // make sure we have a copy of all table rows once the cache has been built + updateCache(table); + } + updatePageDisplay(table, p, false); + moveToPage(table, p, false); + ts.applyWidget( table ); + } + }) + .bind('disablePager' + namespace, function(e){ + e.stopPropagation(); + showAllRows(table, p); + }) + .bind('enablePager' + namespace, function(e){ + e.stopPropagation(); + enablePager(table, p, true); + }) + .bind('destroyPager' + namespace, function(e){ + e.stopPropagation(); + destroyPager(table, p); + }) + .bind('resetToLoadState' + namespace, function(e){ + e.stopPropagation(); + resetState(table, p); + }) + .bind('updateComplete' + namespace, function(e, table, triggered){ + e.stopPropagation(); + // table can be unintentionally undefined in tablesorter v2.17.7 and earlier + // don't recalculate total rows/pages if using ajax + if ( !table || triggered || p.ajax ) { return; } + var $rows = c.$tbodies.eq(0).children('tr').not(c.selectorRemove); + p.totalRows = $rows.length - ( p.countChildRows ? 0 : $rows.filter('.' + c.cssChildRow).length ); + p.totalPages = p.size === 'all' ? 1 : Math.ceil( p.totalRows / p.size ); + if ($rows.length && c.rowsCopy && c.rowsCopy.length === 0) { + // make a copy of all table rows once the cache has been built + updateCache(table); + } + if ( p.page >= p.totalPages ) { + moveToLastPage(table, p); + } + hideRows(table, p); + changeHeight(table, p); + updatePageDisplay(table, p, true); + }) + .bind('pageSize refreshComplete '.split(' ').join(namespace + ' '), function(e, size){ + e.stopPropagation(); + setPageSize(table, parsePageSize( p, size, 'get' ), p); + hideRows(table, p); + updatePageDisplay(table, p, false); + }) + .bind('pageSet pagerUpdate '.split(' ').join(namespace + ' '), function(e, num){ + e.stopPropagation(); + // force pager refresh + if (e.type === 'pagerUpdate') { + num = typeof num === 'undefined' ? p.page + 1 : num; + p.last.page = true; + } + p.page = (parseInt(num, 10) || 1) - 1; + moveToPage(table, p, true); + updatePageDisplay(table, p, false); + }) + .bind('pageAndSize' + namespace, function(e, page, size){ + e.stopPropagation(); + p.page = (parseInt(page, 10) || 1) - 1; + setPageSize(table, parsePageSize( p, size, 'get' ), p); + moveToPage(table, p, true); + hideRows(table, p); + updatePageDisplay(table, p, false); + }); + + // clicked controls + ctrls = [ p.cssFirst, p.cssPrev, p.cssNext, p.cssLast ]; + fxn = [ moveToFirstPage, moveToPrevPage, moveToNextPage, moveToLastPage ]; + if (c.debug && !pager.length) { + console.warn('Pager: >> Container not found'); + } + pager.find(ctrls.join(',')) + .attr('tabindex', 0) + .unbind('click' + namespace) + .bind('click' + namespace, function(e){ + e.stopPropagation(); + var i, $t = $(this), l = ctrls.length; + if ( !$t.hasClass(p.cssDisabled) ) { + for (i = 0; i < l; i++) { + if ($t.is(ctrls[i])) { + fxn[i](table, p); + break; + } + } + } + }); + + // goto selector + p.$goto = pager.find(p.cssGoto); + if ( p.$goto.length ) { + p.$goto + .unbind('change' + namespace) + .bind('change' + namespace, function(){ + p.page = $(this).val() - 1; + moveToPage(table, p, true); + updatePageDisplay(table, p, false); + }); + } else if (c.debug) { + console.warn('Pager: >> Goto selector not found'); + } + // page size selector + p.$size = pager.find(p.cssPageSize); + if ( p.$size.length ) { + // setting an option as selected appears to cause issues with initial page size + p.$size.find('option').removeAttr('selected'); + p.$size.unbind('change' + namespace).bind('change' + namespace, function() { + if ( !$(this).hasClass(p.cssDisabled) ) { + var size = $(this).val(); + p.$size.val( size ); // in case there are more than one pagers + setPageSize(table, size, p); + changeHeight(table, p); + } + return false; + }); + } else if (c.debug) { + console.warn('Pager: >> Size selector not found'); + } + + // clear initialized flag + p.initialized = false; + // before initialization event + $t.triggerHandler('pagerBeforeInitialized', p); + + enablePager(table, p, false); + if ( typeof p.ajaxUrl === 'string' ) { + // ajax pager; interact with database + p.ajax = true; + // When filtering with ajax, allow only custom filtering function, disable default + // filtering since it will be done server side. + c.widgetOptions.filter_serversideFiltering = true; + c.serverSideSorting = true; + moveToPage(table, p); + } else { + p.ajax = false; + // Regular pager; all rows stored in memory + ts.appendCache( c, true ); // true = don't apply widgets + hideRowsSetup(table, p); + } + + // pager initialized + if (!p.ajax && !p.initialized) { + p.initializing = false; + p.initialized = true; + moveToPage(table, p); + if (c.debug) { + console.log('Pager: Triggering pagerInitialized'); + } + c.$table.triggerHandler( 'pagerInitialized', p ); + if ( !( c.widgetOptions.filter_initialized && ts.hasWidget(table, 'filter') ) ) { + updatePageDisplay(table, p, false); + } + } + + // make the hasWidget function think that the pager widget is being used + c.widgetInit.pager = true; }; $this.appender = function(table, rows) { @@ -924,204 +1144,7 @@ return this.each(function() { // check if tablesorter has initialized if (!(this.config && this.hasInitialized)) { return; } - var t, ctrls, fxn, size, - table = this, - c = table.config, - wo = c.widgetOptions, - p = c.pager = $.extend( true, {}, $.tablesorterPager.defaults, settings ), - $t = c.$table, - namespace = c.namespace + 'pager', - // added in case the pager is reinitialized after being destroyed. - pager = p.$container = $(p.container).addClass('tablesorter-pager').show(); - // save a copy of the original settings - p.settings = $.extend( true, {}, $.tablesorterPager.defaults, settings ); - if (c.debug) { - console.log('Pager: Initializing'); - } - p.oldAjaxSuccess = p.oldAjaxSuccess || p.ajaxObject.success; - c.appender = $this.appender; - p.initializing = true; - if (p.savePages && ts.storage) { - t = ts.storage(table, p.storageKey) || {}; // fixes #387 - p.page = isNaN(t.page) ? p.page : t.page; - p.size = t.size === 'all' ? t.size : ( isNaN( t.size ) ? p.size : t.size ) || p.setSize || 10; - $.data(table, 'pagerLastSize', p.size); - pager.find(p.cssPageSize).val(p.size); - } - // skipped rows - p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.slice(1) + '|' + c.cssChildRow + ')'); - p.regexFiltered = new RegExp(wo.filter_filteredRow || 'filtered'); - - $t - // .unbind( namespace ) adding in jQuery 1.4.3 ( I think ) - .unbind( pagerEvents.split(' ').join(namespace + ' ').replace(/\s+/g, ' ') ) - .bind('filterInit filterStart '.split(' ').join(namespace + ' '), function(e, filters) { - p.currentFilters = $.isArray(filters) ? filters : c.$table.data('lastSearch'); - // don't change page if filters are the same (pager updating, etc) - if (e.type === 'filterStart' && p.pageReset !== false && (c.lastCombinedFilter || '') !== (p.currentFilters || []).join('')) { - p.page = p.pageReset; // fixes #456 & #565 - } - }) - // update pager after filter widget completes - .bind('filterEnd sortEnd '.split(' ').join(namespace + ' '), function() { - p.currentFilters = c.$table.data('lastSearch'); - if (p.initialized || p.initializing) { - if (c.delayInit && c.rowsCopy && c.rowsCopy.length === 0) { - // make sure we have a copy of all table rows once the cache has been built - updateCache(table); - } - updatePageDisplay(table, p, false); - moveToPage(table, p, false); - ts.applyWidget( table ); - } - }) - .bind('disablePager' + namespace, function(e){ - e.stopPropagation(); - showAllRows(table, p); - }) - .bind('enablePager' + namespace, function(e){ - e.stopPropagation(); - enablePager(table, p, true); - }) - .bind('destroyPager' + namespace, function(e){ - e.stopPropagation(); - destroyPager(table, p); - }) - .bind('updateComplete' + namespace, function(e, table, triggered){ - e.stopPropagation(); - // table can be unintentionally undefined in tablesorter v2.17.7 and earlier - // don't recalculate total rows/pages if using ajax - if ( !table || triggered || p.ajax ) { return; } - var $rows = c.$tbodies.eq(0).children('tr').not(c.selectorRemove); - p.totalRows = $rows.length - ( p.countChildRows ? 0 : $rows.filter('.' + c.cssChildRow).length ); - p.totalPages = p.size === 'all' ? 1 : Math.ceil( p.totalRows / p.size ); - if ($rows.length && c.rowsCopy && c.rowsCopy.length === 0) { - // make a copy of all table rows once the cache has been built - updateCache(table); - } - if ( p.page >= p.totalPages ) { - moveToLastPage(table, p); - } - hideRows(table, p); - changeHeight(table, p); - updatePageDisplay(table, p, true); - }) - .bind('pageSize refreshComplete '.split(' ').join(namespace + ' '), function(e, size){ - e.stopPropagation(); - setPageSize(table, parsePageSize( p, size, 'get' ), p); - hideRows(table, p); - updatePageDisplay(table, p, false); - }) - .bind('pageSet pagerUpdate '.split(' ').join(namespace + ' '), function(e, num){ - e.stopPropagation(); - // force pager refresh - if (e.type === 'pagerUpdate') { - num = typeof num === 'undefined' ? p.page + 1 : num; - p.last.page = true; - } - p.page = (parseInt(num, 10) || 1) - 1; - moveToPage(table, p, true); - updatePageDisplay(table, p, false); - }) - .bind('pageAndSize' + namespace, function(e, page, size){ - e.stopPropagation(); - p.page = (parseInt(page, 10) || 1) - 1; - setPageSize(table, parsePageSize( p, size, 'get' ), p); - moveToPage(table, p, true); - hideRows(table, p); - updatePageDisplay(table, p, false); - }); - - // clicked controls - ctrls = [ p.cssFirst, p.cssPrev, p.cssNext, p.cssLast ]; - fxn = [ moveToFirstPage, moveToPrevPage, moveToNextPage, moveToLastPage ]; - if (c.debug && !pager.length) { - console.warn('Pager: >> Container not found'); - } - pager.find(ctrls.join(',')) - .attr('tabindex', 0) - .unbind('click' + namespace) - .bind('click' + namespace, function(e){ - e.stopPropagation(); - var i, $t = $(this), l = ctrls.length; - if ( !$t.hasClass(p.cssDisabled) ) { - for (i = 0; i < l; i++) { - if ($t.is(ctrls[i])) { - fxn[i](table, p); - break; - } - } - } - }); - - // goto selector - p.$goto = pager.find(p.cssGoto); - if ( p.$goto.length ) { - p.$goto - .unbind('change' + namespace) - .bind('change' + namespace, function(){ - p.page = $(this).val() - 1; - moveToPage(table, p, true); - updatePageDisplay(table, p, false); - }); - } else if (c.debug) { - console.warn('Pager: >> Goto selector not found'); - } - // page size selector - p.$size = pager.find(p.cssPageSize); - if ( p.$size.length ) { - // setting an option as selected appears to cause issues with initial page size - p.$size.find('option').removeAttr('selected'); - p.$size.unbind('change' + namespace).bind('change' + namespace, function() { - if ( !$(this).hasClass(p.cssDisabled) ) { - var size = $(this).val(); - p.$size.val( size ); // in case there are more than one pagers - setPageSize(table, size, p); - changeHeight(table, p); - } - return false; - }); - } else if (c.debug) { - console.warn('Pager: >> Size selector not found'); - } - - // clear initialized flag - p.initialized = false; - // before initialization event - $t.triggerHandler('pagerBeforeInitialized', p); - - enablePager(table, p, false); - if ( typeof p.ajaxUrl === 'string' ) { - // ajax pager; interact with database - p.ajax = true; - // When filtering with ajax, allow only custom filtering function, disable default - // filtering since it will be done server side. - c.widgetOptions.filter_serversideFiltering = true; - c.serverSideSorting = true; - moveToPage(table, p); - } else { - p.ajax = false; - // Regular pager; all rows stored in memory - ts.appendCache( c, true ); // true = don't apply widgets - hideRowsSetup(table, p); - } - - // pager initialized - if (!p.ajax && !p.initialized) { - p.initializing = false; - p.initialized = true; - moveToPage(table, p); - if (c.debug) { - console.log('Pager: Triggering pagerInitialized'); - } - c.$table.triggerHandler( 'pagerInitialized', p ); - if ( !( c.widgetOptions.filter_initialized && ts.hasWidget(table, 'filter') ) ) { - updatePageDisplay(table, p, false); - } - } - - // make the hasWidget function think that the pager widget is being used - c.widgetInit.pager = true; + init(this, settings); }); }; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 16e9fed..63e0443 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 09-28-2016 (v2.27.8)*/ +/*! tablesorter (FORK) - updated 11-26-2016 (v2.28.0)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.27.8 *//* +/*! TableSorter (FORK) v2.28.0 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.27.8', + version : '2.28.0', parsers : [], widgets : [], @@ -134,7 +134,11 @@ headerList: [], empties: {}, strings: {}, - parsers: [] + parsers: [], + + // *** parser options for validator; values must be falsy! + globalize: 0, + imgAttr: 0 // removed: widgetZebra: { css: ['even', 'odd'] } @@ -308,6 +312,7 @@ ts.setupParsers( c ); // start total row count at zero c.totalRows = 0; + ts.validateOptions( c ); // build the cache for the tbody cells // delayInit will delay building the cache until the user starts a sort if ( !c.delayInit ) { ts.buildCache( c ); } @@ -441,9 +446,11 @@ e.stopPropagation(); // remove all widgets ts.removeWidget( this, true, false ); + var tmp = $.extend( true, {}, c.originalSettings ); // restore original settings; this clears out current settings, but does not clear // values saved to storage. - c = $.extend( true, ts.defaults, c.originalSettings ); + c = $.extend( true, ts.defaults, tmp ); + c.originalSettings = tmp; this.hasInitialized = false; // setup the entire table again ts.setup( this, c ); @@ -1086,6 +1093,7 @@ .removeClass( css.join( ' ' ) ); // remove all header information c.$headers + .add( $( 'thead ' + c.namespace + '_extra_headers' ) ) .removeClass( css.join( ' ' ) ) .addClass( none ) .attr( 'aria-sort', 'none' ) @@ -1928,6 +1936,8 @@ widget = ts.getWidgetById( c.widgets[ indx ] ); if ( widget && widget.options ) { c.widgetOptions = $.extend( true, {}, widget.options, c.widgetOptions ); + // add widgetOptions to defaults for option validator + $.extend( true, ts.defaults.widgetOptions, widget.options ); } } } @@ -2300,7 +2310,9 @@ }, getColumnData : function( table, obj, indx, getCell, $headers ) { - if ( typeof obj === 'undefined' || obj === null ) { return; } + if ( typeof obj !== 'object' || obj === null ) { + return obj; + } table = $( table )[ 0 ]; var $header, key, c = table.config, @@ -2411,6 +2423,34 @@ return str; }, + validateOptions : function( c ) { + var setting, setting2, typ, timer, + // ignore options containing an array + ignore = 'sortForce sortList sortAppend widgets'.split( ' ' ), + orig = c.originalSettings; + if ( orig ) { + if ( c.debug ) { + timer = new Date(); + } + for ( setting in orig ) { + typ = typeof ts.defaults[setting]; + if ( typ === 'undefined' ) { + console.warn( 'Tablesorter Warning! "table.config.' + setting + '" option not recognized' ); + } else if ( typ === 'object' ) { + for ( setting2 in orig[setting] ) { + typ = typeof ts.defaults[setting][setting2]; + if ( $.inArray( setting, ignore ) < 0 && typ === 'undefined' ) { + console.warn( 'Tablesorter Warning! "table.config.' + setting + '.' + setting2 + '" option not recognized' ); + } + } + } + } + if ( c.debug ) { + console.log( 'validate options time:' + ts.benchmark( timer ) ); + } + } + }, + // restore headers restoreHeaders : function( table ) { var index, $cell, @@ -2771,7 +2811,7 @@ })( jQuery ); -/*! Widget: storage - updated 3/1/2016 (v2.25.5) */ +/*! Widget: storage - updated 11/26/2016 (v2.28.0) */ /*global JSON:false */ ;(function ($, window, document) { 'use strict'; @@ -2816,6 +2856,17 @@ url = options && options.url || $table.attr(options && options.page || wo && wo.storage_page || 'data-table-page') || wo && wo.storage_fixedUrl || c && c.fixedUrl || window.location.pathname; + // update defaults for validator; these values must be falsy! + $.extend(true, ts.defaults, { + fixedUrl: '', + widgetOptions: { + storage_fixedUrl: '', + storage_group: '', + storage_page: '', + storage_tableId: '', + storage_useSessionStorage: '' + } + }); // https://gist.github.com/paulirish/5558557 if (storageType in window) { try { @@ -3137,7 +3188,7 @@ })(jQuery); -/*! Widget: filter - updated 9/23/2016 (v2.27.7) *//* +/*! Widget: filter - updated 11/26/2016 (v2.28.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3966,11 +4017,15 @@ // include change for select - fixes #473 .bind( 'search change keypress input '.split( ' ' ).join( namespace + ' ' ), function( event ) { // don't get cached data, in case data-column changes dynamically - var column = parseInt( $( this ).attr( 'data-column' ), 10 ); + var column = parseInt( $( this ).attr( 'data-column' ), 10 ), + liveSearch = typeof wo.filter_liveSearch === 'boolean' ? + wo.filter_liveSearch : + ts.getColumnData( table, wo.filter_liveSearch, column ); // don't allow 'change' event to process if the input value is the same - fixes #685 if ( table.config.widgetOptions.filter_initialized && ( event.which === tskeyCodes.enter || event.type === 'search' || - ( event.type === 'change' || event.type === 'input' ) && + ( event.type === 'change' || + ( event.type === 'input' && liveSearch === true ) ) && this.value !== c.lastSearch[column] ) ) { event.preventDefault(); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 10ee4a7..7513942 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.27.8 *//* +/*! TableSorter (FORK) v2.28.0 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.27.8', + version : '2.28.0', parsers : [], widgets : [], @@ -116,7 +116,11 @@ headerList: [], empties: {}, strings: {}, - parsers: [] + parsers: [], + + // *** parser options for validator; values must be falsy! + globalize: 0, + imgAttr: 0 // removed: widgetZebra: { css: ['even', 'odd'] } @@ -290,6 +294,7 @@ ts.setupParsers( c ); // start total row count at zero c.totalRows = 0; + ts.validateOptions( c ); // build the cache for the tbody cells // delayInit will delay building the cache until the user starts a sort if ( !c.delayInit ) { ts.buildCache( c ); } @@ -423,9 +428,11 @@ e.stopPropagation(); // remove all widgets ts.removeWidget( this, true, false ); + var tmp = $.extend( true, {}, c.originalSettings ); // restore original settings; this clears out current settings, but does not clear // values saved to storage. - c = $.extend( true, ts.defaults, c.originalSettings ); + c = $.extend( true, ts.defaults, tmp ); + c.originalSettings = tmp; this.hasInitialized = false; // setup the entire table again ts.setup( this, c ); @@ -1068,6 +1075,7 @@ .removeClass( css.join( ' ' ) ); // remove all header information c.$headers + .add( $( 'thead ' + c.namespace + '_extra_headers' ) ) .removeClass( css.join( ' ' ) ) .addClass( none ) .attr( 'aria-sort', 'none' ) @@ -1910,6 +1918,8 @@ widget = ts.getWidgetById( c.widgets[ indx ] ); if ( widget && widget.options ) { c.widgetOptions = $.extend( true, {}, widget.options, c.widgetOptions ); + // add widgetOptions to defaults for option validator + $.extend( true, ts.defaults.widgetOptions, widget.options ); } } } @@ -2282,7 +2292,9 @@ }, getColumnData : function( table, obj, indx, getCell, $headers ) { - if ( typeof obj === 'undefined' || obj === null ) { return; } + if ( typeof obj !== 'object' || obj === null ) { + return obj; + } table = $( table )[ 0 ]; var $header, key, c = table.config, @@ -2393,6 +2405,34 @@ return str; }, + validateOptions : function( c ) { + var setting, setting2, typ, timer, + // ignore options containing an array + ignore = 'headers sortForce sortList sortAppend widgets'.split( ' ' ), + orig = c.originalSettings; + if ( orig ) { + if ( c.debug ) { + timer = new Date(); + } + for ( setting in orig ) { + typ = typeof ts.defaults[setting]; + if ( typ === 'undefined' ) { + console.warn( 'Tablesorter Warning! "table.config.' + setting + '" option not recognized' ); + } else if ( typ === 'object' ) { + for ( setting2 in orig[setting] ) { + typ = ts.defaults[setting] && typeof ts.defaults[setting][setting2]; + if ( $.inArray( setting, ignore ) < 0 && typ === 'undefined' ) { + console.warn( 'Tablesorter Warning! "table.config.' + setting + '.' + setting2 + '" option not recognized' ); + } + } + } + } + if ( c.debug ) { + console.log( 'validate options time:' + ts.benchmark( timer ) ); + } + } + }, + // restore headers restoreHeaders : function( table ) { var index, $cell, diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index ebb97ae..3ff98bd 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 09-28-2016 (v2.27.8)*/ +/*! tablesorter (FORK) - updated 11-26-2016 (v2.28.0)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! Widget: storage - updated 3/1/2016 (v2.25.5) */ +/*! Widget: storage - updated 11/26/2016 (v2.28.0) */ /*global JSON:false */ ;(function ($, window, document) { 'use strict'; @@ -61,6 +61,17 @@ url = options && options.url || $table.attr(options && options.page || wo && wo.storage_page || 'data-table-page') || wo && wo.storage_fixedUrl || c && c.fixedUrl || window.location.pathname; + // update defaults for validator; these values must be falsy! + $.extend(true, ts.defaults, { + fixedUrl: '', + widgetOptions: { + storage_fixedUrl: '', + storage_group: '', + storage_page: '', + storage_tableId: '', + storage_useSessionStorage: '' + } + }); // https://gist.github.com/paulirish/5558557 if (storageType in window) { try { @@ -382,7 +393,7 @@ })(jQuery); -/*! Widget: filter - updated 9/23/2016 (v2.27.7) *//* +/*! Widget: filter - updated 11/26/2016 (v2.28.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -1211,11 +1222,15 @@ // include change for select - fixes #473 .bind( 'search change keypress input '.split( ' ' ).join( namespace + ' ' ), function( event ) { // don't get cached data, in case data-column changes dynamically - var column = parseInt( $( this ).attr( 'data-column' ), 10 ); + var column = parseInt( $( this ).attr( 'data-column' ), 10 ), + liveSearch = typeof wo.filter_liveSearch === 'boolean' ? + wo.filter_liveSearch : + ts.getColumnData( table, wo.filter_liveSearch, column ); // don't allow 'change' event to process if the input value is the same - fixes #685 if ( table.config.widgetOptions.filter_initialized && ( event.which === tskeyCodes.enter || event.type === 'search' || - ( event.type === 'change' || event.type === 'input' ) && + ( event.type === 'change' || + ( event.type === 'input' && liveSearch === true ) ) && this.value !== c.lastSearch[column] ) ) { event.preventDefault(); diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js index 08b8241..ceb822c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js @@ -1,4 +1,4 @@ -/*! Parser: two digit year - updated 11/22/2015 (v2.24.6) */ +/*! Parser: two digit year - updated 11/26/2016 (v2.28.0) */ /* Demo: http://mottie.github.io/tablesorter/docs/example-parsers-dates.html */ /*jshint jquery:true */ ;(function($){ @@ -13,6 +13,9 @@ ts = $.tablesorter, now = new Date().getFullYear(); + // add dateRange to defaults for validator; value must be falsy + ts.defaults.dataRange = ''; + if ( !ts.dates ) { ts.dates = {}; } ts.dates.regxxxxyy = /(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{2})/; ts.dates.regyyxxxx = /(\d{2})[\/\s](\d{1,2})[\/\s](\d{1,2})/; diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index dfcbdee..bdac971 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,4 +1,4 @@ -/*! Parser: input & select - updated 9/1/2016 (v2.27.6) *//* +/*! Parser: input & select - updated 11/26/2016 (v2.28.0) *//* * for jQuery 1.7+ & tablesorter 2.7.11+ * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ @@ -110,6 +110,10 @@ type : 'text' }); + // update defaults for validator; values must be falsy + $.tablesorter.defaults.checkboxClass = ''; + $.tablesorter.defaults.checkboxVisible = ''; + // update select and all input types in the tablesorter cache when the change event fires. // This method only works with jQuery 1.7+ // you can change it to use delegate (v1.4.3+) or live (v1.3+) as desired diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js index cf08752..9acd0b1 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js @@ -1,4 +1,4 @@ -/*! Widget: Build Table - updated 3/26/2015 (v2.21.3) *//* +/*! Widget: Build Table - updated 11/26/2016 (v2.28.0) *//* * for tableSorter v2.16.0+ * by Rob Garrison */ @@ -79,6 +79,9 @@ } }; + // add data to defaults for validator; value must be falsy! + ts.defaults.data = ''; + bt.defaults = { // *** build widget core *** build_type : '', // array, csv, object, json, html diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 60a6059..0475c02 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 9/23/2016 (v2.27.7) *//* +/*! Widget: filter - updated 11/26/2016 (v2.28.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -827,11 +827,15 @@ // include change for select - fixes #473 .bind( 'search change keypress input '.split( ' ' ).join( namespace + ' ' ), function( event ) { // don't get cached data, in case data-column changes dynamically - var column = parseInt( $( this ).attr( 'data-column' ), 10 ); + var column = parseInt( $( this ).attr( 'data-column' ), 10 ), + liveSearch = typeof wo.filter_liveSearch === 'boolean' ? + wo.filter_liveSearch : + ts.getColumnData( table, wo.filter_liveSearch, column ); // don't allow 'change' event to process if the input value is the same - fixes #685 if ( table.config.widgetOptions.filter_initialized && ( event.which === tskeyCodes.enter || event.type === 'search' || - ( event.type === 'change' || event.type === 'input' ) && + ( event.type === 'change' || + ( event.type === 'input' && liveSearch === true ) ) && this.value !== c.lastSearch[column] ) ) { event.preventDefault(); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index d87b627..b7dc09a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -1,4 +1,4 @@ -/*! Widget: grouping - updated 9/1/2016 (v2.27.6) *//* +/*! Widget: grouping - updated 11/26/2016 (v2.28.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -234,7 +234,8 @@ if ( $.isFunction( wo.group_formatter ) ) { data.currentGroup = wo.group_formatter( ( data.group || '' ).toString(), data.column, c.table, c, wo, data ) || data.group; } - data.$row.before( tsg.groupHeaderHTML( c, wo, data ) ); + // add first() for grouping with childRows + data.$row.first().before( tsg.groupHeaderHTML( c, wo, data ) ); if ( wo.group_saveGroups && !data.savedGroup && wo.group_collapsed && wo.group_collapsible ) { // all groups start collapsed; data.groupIndex is 1 more than the expected index. wo.group_collapsedGroups[ wo.group_collapsedGroup ].push( data.currentGroup + data.groupIndex ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index cb20b05..a2f31f5 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -1,4 +1,4 @@ -/*! Widget: math - updated 7/31/2016 (v2.27.0) *//* +/*! Widget: math - updated 11/26/2016 (v2.28.0) *//* * Requires tablesorter v2.16+ and jQuery 1.7+ * by Rob Garrison */ @@ -29,7 +29,15 @@ .split(' ').join('.tsmath '), processText : function( c, $cell ) { - var txt = ts.getElementText( c, $cell, math.getCellIndex( $cell ) ); + var tmp, + txt = ts.getElementText( c, $cell, math.getCellIndex( $cell ) ), + prefix = c.widgetOptions.math_prefix; + if ( /</.test( prefix ) ) { + // prefix contains HTML; remove it & any text before using formatFloat + tmp = $( '<div>' + prefix + '</div>' ).text() + .replace(/\{content\}/g, '').trim(); + txt = txt.replace( tmp, '' ); + } txt = ts.formatFloat( txt.replace( /[^\w,. \-()]/g, '' ), c.table ) || 0; // isNaN('') => false return isNaN( txt ) ? 0 : txt; @@ -352,7 +360,7 @@ prev = $cell.html(), mask = $cell.attr( 'data-' + wo.math_data + '-mask' ) || wo.math_mask, target = $cell.attr( 'data-' + wo.math_data + '-target' ) || '', - result = ts.formatMask( mask, value, wo.math_wrapPrefix, wo.math_wrapSuffix ); + result = ts.formatMask( mask, value, wo.math_prefix, wo.math_suffix ); if (target) { $el = $cell.find(target); if ($el.length) { @@ -403,8 +411,7 @@ start = mask.search( /[0-9\-\+#]/ ), tmp = start > 0 ? mask.substring( 0, start ) : '', prefix = tmp; - - if ( start > 0 && tmpPrefix ) { + if ( tmpPrefix ) { if ( /\{content\}/.test( tmpPrefix || '' ) ) { prefix = ( tmpPrefix || '' ).replace( /\{content\}/g, tmp || '' ); } else { @@ -418,7 +425,7 @@ index += ( mask.substring( index, index + 1 ) === '.' ) ? 1 : 0; tmp = end > 0 ? mask.substring( index, len ) : ''; suffix = tmp; - if ( tmp !== '' && tmpSuffix ) { + if ( tmpSuffix ) { if ( /\{content\}/.test( tmpSuffix || '' ) ) { suffix = ( tmpSuffix || '' ).replace( /\{content\}/g, tmp || '' ); } else { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index b3cd1b4..f5814a4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 9/23/2016 (v2.27.7) */ +/*! Widget: Pager - updated 11/26/2016 (v2.28.0) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -404,11 +404,11 @@ p.$container .find( s.first + ',' + s.prev ) .toggleClass( wo.pager_css.disabled, first ) - .attr( 'aria-disabled', first ); + .prop( 'aria-disabled', first ); p.$container .find( s.next + ',' + s.last ) .toggleClass( wo.pager_css.disabled, last ) - .attr( 'aria-disabled', last ); + .prop( 'aria-disabled', last ); } }, @@ -451,7 +451,7 @@ .add( p.$goto ) .removeClass( wo.pager_css.disabled ) .removeAttr( 'disabled' ) - .attr( 'aria-disabled', 'false' ); + .prop( 'aria-disabled', 'false' ); p.totalPages = Math.ceil( p.totalRows / sz ); // needed for 'pageSize' method c.totalRows = p.totalRows; tsp.parsePageNumber( c, p ); @@ -1012,7 +1012,7 @@ len = $controls.length; for ( index = 0; index < len; index++ ) { $controls.eq( index ) - .attr( 'aria-disabled', 'true' ) + .prop( 'aria-disabled', 'true' ) .addClass( wo.pager_css.disabled )[ 0 ].disabled = true; } }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js index a2295ce..8bd3cfc 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js @@ -1,6 +1,7 @@ -/*! tablesorter tbody sorting widget (BETA) - 11/22/2015 (v2.24.6) +/*! tablesorter tbody sorting widget (BETA) - 11/26/2016 (v2.28.0) * Requires tablesorter v2.22.2+ and jQuery 1.4+ * by Rob Garrison + * Contributors: Chris Rogers */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ @@ -38,6 +39,15 @@ // find parsers for each column ts.sortTbodies.setTbodies( c, wo ); ts.updateCache( c, null, c.$tbodies ); + }) + .bind('sortEnd', function() { + // Moves the head row back to the top of the tbody + var primaryRow = wo.sortTbody_primaryRow; + if ( wo.sortTbody_lockHead && primaryRow ) { + c.$table.find( primaryRow ).each( function() { + $( this ).parents( 'tbody' ).prepend( this ); + }); + } }); // detect parsers - in case the table contains only info-only tbodies @@ -208,6 +218,8 @@ // priority < 50 (filter widget), so c.$tbodies has the correct elements priority: 40, options: { + // lock primary row as a header when sorting + sortTbody_lockHead : false, // point to a row within the tbody that matches the number of header columns sortTbody_primaryRow : null, // sort tbody internal rows diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js index 948356a..e489d66 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js @@ -1,4 +1,4 @@ -/*! Widget: storage - updated 3/1/2016 (v2.25.5) */ +/*! Widget: storage - updated 11/26/2016 (v2.28.0) */ /*global JSON:false */ ;(function ($, window, document) { 'use strict'; @@ -43,6 +43,17 @@ url = options && options.url || $table.attr(options && options.page || wo && wo.storage_page || 'data-table-page') || wo && wo.storage_fixedUrl || c && c.fixedUrl || window.location.pathname; + // update defaults for validator; these values must be falsy! + $.extend(true, ts.defaults, { + fixedUrl: '', + widgetOptions: { + storage_fixedUrl: '', + storage_group: '', + storage_page: '', + storage_tableId: '', + storage_useSessionStorage: '' + } + }); // https://gist.github.com/paulirish/5558557 if (storageType in window) { try { From 326793c71690427d7f0bed2496da0c8e95e5fef9 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Fri, 9 Dec 2016 23:49:03 +0100 Subject: [PATCH 104/138] Update tablesorter to latest version (2.28.1) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 37 +++++++++++-------- .../jquery-tablesorter/jquery.tablesorter.js | 4 +- .../jquery.tablesorter.widgets.js | 29 +++++++++------ .../widgets/widget-filter.js | 23 ++++++++---- .../widgets/widget-print.js | 4 +- .../widgets/widget-uitheme.js | 4 +- 10 files changed, 68 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4756c26..491b418 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.23.2 (2016-12-09) + +* Upgrade tablesorter to v2.28.1 + #### v1.23.1 (2016-11-29) * Add actual files of v2.28.0 (facepalm!) diff --git a/README.md b/README.md index a16dca5..61e6c1b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.28.0 (11/27/2016), [documentation] +Current tablesorter version: 2.28.1 (11/27/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index cc9ea08..e8211f1 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 23 - TINY = 1 + TINY = 2 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 920dd01..85b8a9e 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 920dd01baac9ea281fb26444a9d8489950583ebd +Subproject commit 85b8a9e4810f53656e1e7f03e6dfd66489e1bd3d diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 63e0443..08d187e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 11-26-2016 (v2.28.0)*/ +/*! tablesorter (FORK) - updated 12-08-2016 (v2.28.1)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.28.0 *//* +/*! TableSorter (FORK) v2.28.1 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.0', + version : '2.28.1', parsers : [], widgets : [], @@ -2426,7 +2426,7 @@ validateOptions : function( c ) { var setting, setting2, typ, timer, // ignore options containing an array - ignore = 'sortForce sortList sortAppend widgets'.split( ' ' ), + ignore = 'headers sortForce sortList sortAppend widgets'.split( ' ' ), orig = c.originalSettings; if ( orig ) { if ( c.debug ) { @@ -2438,7 +2438,7 @@ console.warn( 'Tablesorter Warning! "table.config.' + setting + '" option not recognized' ); } else if ( typ === 'object' ) { for ( setting2 in orig[setting] ) { - typ = typeof ts.defaults[setting][setting2]; + typ = ts.defaults[setting] && typeof ts.defaults[setting][setting2]; if ( $.inArray( setting, ignore ) < 0 && typ === 'undefined' ) { console.warn( 'Tablesorter Warning! "table.config.' + setting + '.' + setting2 + '" option not recognized' ); } @@ -2913,7 +2913,7 @@ })(jQuery, window, document); -/*! Widget: uitheme - updated 7/31/2016 (v2.27.0) */ +/*! Widget: uitheme - updated 12/8/2016 (v2.28.1) */ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; @@ -3039,7 +3039,7 @@ .addClass(themes.icons || ''); } // filter widget initializes after uitheme - if (c.widgets.indexOf('filter') > -1) { + if (ts.hasWidget( c.table, 'filter' )) { tmp = function() { $table.children('thead').children('.' + ts.css.filterRow) .removeClass(hasOldTheme ? oldtheme.filterRow || '' : '') @@ -3188,7 +3188,7 @@ })(jQuery); -/*! Widget: filter - updated 11/26/2016 (v2.28.0) *//* +/*! Widget: filter - updated 12/8/2016 (v2.28.1) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -4012,7 +4012,7 @@ return; } // change event = no delay; last true flag tells getFilters to skip newest timed input - tsf.searching( table, true, true ); + tsf.searching( table, true, true, column ); }) // include change for select - fixes #473 .bind( 'search change keypress input '.split( ' ' ).join( namespace + ' ' ), function( event ) { @@ -4021,12 +4021,19 @@ liveSearch = typeof wo.filter_liveSearch === 'boolean' ? wo.filter_liveSearch : ts.getColumnData( table, wo.filter_liveSearch, column ); - // don't allow 'change' event to process if the input value is the same - fixes #685 if ( table.config.widgetOptions.filter_initialized && - ( event.which === tskeyCodes.enter || event.type === 'search' || - ( event.type === 'change' || - ( event.type === 'input' && liveSearch === true ) ) && - this.value !== c.lastSearch[column] ) + // immediate search if user presses enter + ( event.which === tskeyCodes.enter || + // immediate search if a "search" is triggered on the input + event.type === 'search' || + // change & input events must be ignored if liveSearch !== true + ( event.type === 'change' || event.type === 'input' ) && + // prevent search if liveSearch is a number + liveSearch === true && + // don't allow 'change' or 'input' event to process if the input value + // is the same - fixes #685 + this.value !== c.lastSearch[column] + ) ) { event.preventDefault(); // init search with no delay @@ -4046,7 +4053,7 @@ wo.filter_liveSearch : // get column setting, or set to fallback value, or default to false ts.getColumnData( table, wo.filter_liveSearch, column ); - if ( typeof liveSearch !== 'undefined' ) { + if ( typeof liveSearch === 'undefined' ) { liveSearch = wo.filter_liveSearch.fallback || false; } } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 7513942..0dc065d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.28.0 *//* +/*! TableSorter (FORK) v2.28.1 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.0', + version : '2.28.1', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 3ff98bd..78b8496 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 11-26-2016 (v2.28.0)*/ +/*! tablesorter (FORK) - updated 12-08-2016 (v2.28.1)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -118,7 +118,7 @@ })(jQuery, window, document); -/*! Widget: uitheme - updated 7/31/2016 (v2.27.0) */ +/*! Widget: uitheme - updated 12/8/2016 (v2.28.1) */ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; @@ -244,7 +244,7 @@ .addClass(themes.icons || ''); } // filter widget initializes after uitheme - if (c.widgets.indexOf('filter') > -1) { + if (ts.hasWidget( c.table, 'filter' )) { tmp = function() { $table.children('thead').children('.' + ts.css.filterRow) .removeClass(hasOldTheme ? oldtheme.filterRow || '' : '') @@ -393,7 +393,7 @@ })(jQuery); -/*! Widget: filter - updated 11/26/2016 (v2.28.0) *//* +/*! Widget: filter - updated 12/8/2016 (v2.28.1) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -1217,7 +1217,7 @@ return; } // change event = no delay; last true flag tells getFilters to skip newest timed input - tsf.searching( table, true, true ); + tsf.searching( table, true, true, column ); }) // include change for select - fixes #473 .bind( 'search change keypress input '.split( ' ' ).join( namespace + ' ' ), function( event ) { @@ -1226,12 +1226,19 @@ liveSearch = typeof wo.filter_liveSearch === 'boolean' ? wo.filter_liveSearch : ts.getColumnData( table, wo.filter_liveSearch, column ); - // don't allow 'change' event to process if the input value is the same - fixes #685 if ( table.config.widgetOptions.filter_initialized && - ( event.which === tskeyCodes.enter || event.type === 'search' || - ( event.type === 'change' || - ( event.type === 'input' && liveSearch === true ) ) && - this.value !== c.lastSearch[column] ) + // immediate search if user presses enter + ( event.which === tskeyCodes.enter || + // immediate search if a "search" is triggered on the input + event.type === 'search' || + // change & input events must be ignored if liveSearch !== true + ( event.type === 'change' || event.type === 'input' ) && + // prevent search if liveSearch is a number + liveSearch === true && + // don't allow 'change' or 'input' event to process if the input value + // is the same - fixes #685 + this.value !== c.lastSearch[column] + ) ) { event.preventDefault(); // init search with no delay @@ -1251,7 +1258,7 @@ wo.filter_liveSearch : // get column setting, or set to fallback value, or default to false ts.getColumnData( table, wo.filter_liveSearch, column ); - if ( typeof liveSearch !== 'undefined' ) { + if ( typeof liveSearch === 'undefined' ) { liveSearch = wo.filter_liveSearch.fallback || false; } } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 0475c02..4e571e8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 11/26/2016 (v2.28.0) *//* +/*! Widget: filter - updated 12/8/2016 (v2.28.1) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -822,7 +822,7 @@ return; } // change event = no delay; last true flag tells getFilters to skip newest timed input - tsf.searching( table, true, true ); + tsf.searching( table, true, true, column ); }) // include change for select - fixes #473 .bind( 'search change keypress input '.split( ' ' ).join( namespace + ' ' ), function( event ) { @@ -831,12 +831,19 @@ liveSearch = typeof wo.filter_liveSearch === 'boolean' ? wo.filter_liveSearch : ts.getColumnData( table, wo.filter_liveSearch, column ); - // don't allow 'change' event to process if the input value is the same - fixes #685 if ( table.config.widgetOptions.filter_initialized && - ( event.which === tskeyCodes.enter || event.type === 'search' || - ( event.type === 'change' || - ( event.type === 'input' && liveSearch === true ) ) && - this.value !== c.lastSearch[column] ) + // immediate search if user presses enter + ( event.which === tskeyCodes.enter || + // immediate search if a "search" is triggered on the input + event.type === 'search' || + // change & input events must be ignored if liveSearch !== true + ( event.type === 'change' || event.type === 'input' ) && + // prevent search if liveSearch is a number + liveSearch === true && + // don't allow 'change' or 'input' event to process if the input value + // is the same - fixes #685 + this.value !== c.lastSearch[column] + ) ) { event.preventDefault(); // init search with no delay @@ -856,7 +863,7 @@ wo.filter_liveSearch : // get column setting, or set to fallback value, or default to false ts.getColumnData( table, wo.filter_liveSearch, column ); - if ( typeof liveSearch !== 'undefined' ) { + if ( typeof liveSearch === 'undefined' ) { liveSearch = wo.filter_liveSearch.fallback || false; } } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js index f57e4b3..64c9f17 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js @@ -1,4 +1,4 @@ -/* Widget: print - updated 4/11/2016 (v2.25.8) *//* +/* Widget: print - updated 12/8/2016 (v2.28.1) *//* * Requires tablesorter v2.8+ and jQuery 1.2.6+ */ /*jshint browser:true, jquery:true, unused:false */ @@ -70,7 +70,7 @@ // columnSelector -> c.selector.$style // Assume 'visible' means hidden columns have a 'display:none' style, or a class name // add the definition to the wo.print_extraCSS option - if (/s/i.test(wo.print_columns) && c.selector && c.widgets.indexOf('columnSelector') >= 0) { + if (/s/i.test(wo.print_columns) && c.selector && ts.hasWidget( c.table, 'columnSelector' )) { // show selected (visible) columns; make a copy of the columnSelector widget css (not media queries) printStyle += wo.columnSelector_mediaquery && c.selector.auto ? '' : c.selector.$style.text(); } else if (/a/i.test(wo.print_columns)) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js index bb5f577..42a945e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js @@ -1,4 +1,4 @@ -/*! Widget: uitheme - updated 7/31/2016 (v2.27.0) */ +/*! Widget: uitheme - updated 12/8/2016 (v2.28.1) */ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; @@ -124,7 +124,7 @@ .addClass(themes.icons || ''); } // filter widget initializes after uitheme - if (c.widgets.indexOf('filter') > -1) { + if (ts.hasWidget( c.table, 'filter' )) { tmp = function() { $table.children('thead').children('.' + ts.css.filterRow) .removeClass(hasOldTheme ? oldtheme.filterRow || '' : '') From 8239b4cd0da27c8b2b712d075f6a265ecd32d59c Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 19 Dec 2016 17:09:17 +0100 Subject: [PATCH 105/138] Update tablesorter to latest version (2.28.3) --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../beta-testing/widget-reorder.js | 6 +++--- .../jquery.tablesorter.combined.js | 10 +++++----- .../jquery-tablesorter/jquery.tablesorter.js | 4 ++-- .../jquery.tablesorter.widgets.js | 6 +++--- .../widgets/widget-columnSelector.js | 18 ++++++++++++------ .../widgets/widget-resizable.js | 4 ++-- .../widgets/widget-scroller.js | 2 +- 11 files changed, 35 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 491b418..6ac3bd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.23.3 (2016-12-19) + +* Upgrade tablesorter to v2.28.3 + #### v1.23.2 (2016-12-09) * Upgrade tablesorter to v2.28.1 diff --git a/README.md b/README.md index 61e6c1b..a5b65ba 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.28.1 (11/27/2016), [documentation] +Current tablesorter version: 2.28.3 (12/16/2016), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index e8211f1..28a346c 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 23 - TINY = 2 + TINY = 3 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 85b8a9e..09e5010 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 85b8a9e4810f53656e1e7f03e6dfd66489e1bd3d +Subproject commit 09e5010be1dc94865bcf785e1325d6ddd34781be diff --git a/vendor/assets/javascripts/jquery-tablesorter/beta-testing/widget-reorder.js b/vendor/assets/javascripts/jquery-tablesorter/beta-testing/widget-reorder.js index 1a7733f..5d09f83 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/beta-testing/widget-reorder.js +++ b/vendor/assets/javascripts/jquery-tablesorter/beta-testing/widget-reorder.js @@ -56,7 +56,7 @@ $.tablesorter.addWidget({ top: r.top - clickOffset[1], width: $th.outerWidth() }) - .appendTo('body') + .appendTo('head') .find('th, td').addClass(wo.reorder_helperClass); $bar = $('<div class="' + wo.reorder_helperBar + '" />') .css({ @@ -64,7 +64,7 @@ $.tablesorter.addWidget({ top : c.$table.find('thead').offset().top, height : $th.closest('thead').outerHeight() + c.$table.find('tbody').height() }) - .appendTo('body'); + .appendTo('head'); positionBar(e); lastIndx = endIndex; }, @@ -179,4 +179,4 @@ $.tablesorter.addWidget({ // add mouse coordinates $x = $('#main h1:last'); $(document).mousemove(function(e){ $x.html( e.pageX ); }); -})(jQuery); \ No newline at end of file +})(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 08d187e..c619536 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 12-08-2016 (v2.28.1)*/ +/*! tablesorter (FORK) - updated 12-16-2016 (v2.28.3)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.28.1 *//* +/*! TableSorter (FORK) v2.28.3 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.1', + version : '2.28.3', parsers : [], widgets : [], @@ -5374,7 +5374,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 6/28/2016 (v2.26.5) */ +/*! Widget: resizable - updated 12/15/2016 (v2.28.2) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -5397,7 +5397,7 @@ '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px;' + 'top: 1px; cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' + '</style>'; - $(s).appendTo('body'); + $('head').append(s); }); ts.resizable = { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 0dc065d..c93f759 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.28.1 *//* +/*! TableSorter (FORK) v2.28.3 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.1', + version : '2.28.3', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 78b8496..51877bc 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 12-08-2016 (v2.28.1)*/ +/*! tablesorter (FORK) - updated 12-16-2016 (v2.28.3)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -2579,7 +2579,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 6/28/2016 (v2.26.5) */ +/*! Widget: resizable - updated 12/15/2016 (v2.28.2) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -2602,7 +2602,7 @@ '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px;' + 'top: 1px; cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' + '</style>'; - $(s).appendTo('body'); + $('head').append(s); }); ts.resizable = { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index cb21451..7b55e38 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Widget: columnSelector (responsive table widget) - updated 9/28/2016 (v2.27.8) *//* +/* Widget: columnSelector (responsive table widget) - updated 12/15/2016 (v2.28.2) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -113,7 +113,7 @@ }, setupSelector: function(c, wo) { - var index, name, $header, priority, col, colId, + var index, name, $header, priority, col, colId, $el, colSel = c.selector, $container = colSel.$container, useStorage = wo.columnSelector_saveColumns && ts.storage, @@ -149,10 +149,13 @@ saved[colId] : (typeof wo.columnSelector_columns[colId] !== 'undefined' && wo.columnSelector_columns[colId] !== null) ? wo.columnSelector_columns[colId] : (state === 'true' || state !== 'false'); colSel.$column[colId] = $(this); - - // set default col title - name = $header.attr(wo.columnSelector_name) || $header.text(); if ($container.length) { + // set default col title + name = $header.attr(wo.columnSelector_name) || $header.text().trim(); + if (typeof wo.columnSelector_layoutCustomizer === "function") { + $el = $header.find('.' + ts.css.headerIn); + name = wo.columnSelector_layoutCustomizer( $el.length ? $el : $header, name, parseInt(colId, 10) ); + } colSel.$wrapper[colId] = $(wo.columnSelector_layout.replace(/\{name\}/g, name)).appendTo($container); colSel.$checkbox[colId] = colSel.$wrapper[colId] // input may not be wrapped within the layout template @@ -270,7 +273,7 @@ var array = [], temp = ' col:nth-child(' + column + ')'; array.push(prefix + temp + ',' + prefix + '_extra_table' + temp); - temp = ' tr:not(.hasSpan) th:nth-child(' + column + ')'; + temp = ' tr:not(.hasSpan) th[data-column="' + ( column - 1 ) + '"]'; array.push(prefix + temp + ',' + prefix + '_extra_table' + temp); temp = ' tr:not(.hasSpan) td:nth-child(' + column + ')'; array.push(prefix + temp + ',' + prefix + '_extra_table' + temp); @@ -497,6 +500,9 @@ // container layout columnSelector_layout : '<label><input type="checkbox">{name}</label>', + // layout customizer callback called for each column + // function($cell, name, column){ return name || $cell.html(); } + columnSelector_layoutCustomizer : null, // data attribute containing column name to use in the selector container columnSelector_name : 'data-selector-name', diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js index 2cc80a3..fe47ea7 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js @@ -1,4 +1,4 @@ -/*! Widget: resizable - updated 6/28/2016 (v2.26.5) */ +/*! Widget: resizable - updated 12/15/2016 (v2.28.2) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -21,7 +21,7 @@ '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px;' + 'top: 1px; cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' + '</style>'; - $(s).appendTo('body'); + $('head').append(s); }); ts.resizable = { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index 8280f5c..d796fd7 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -139,7 +139,7 @@ '.' + tscss.scrollerWrap + ' .' + tscss.scrollerFixedPanel + ' { position: absolute; top: 0; bottom: 0; z-index: 2; left: 0; right: 0; } ' + '</style>'; - $( style ).appendTo( 'body' ); + $( 'head' ).append( style ); }); ts.scroller = { From e15327ac0b8584455a3931dbc5fb6a9beba2172a Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Sun, 8 Jan 2017 18:28:34 +0100 Subject: [PATCH 106/138] Update tablesorter to latest version (2.28.4) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 72 ++++--- .../jquery.tablesorter.combined.js | 10 +- .../jquery-tablesorter/jquery.tablesorter.js | 4 +- .../jquery.tablesorter.widgets.js | 6 +- .../widgets/widget-columnSelector.js | 2 +- .../widgets/widget-output.js | 69 +++++-- .../widgets/widget-pager.js | 95 +++++---- .../widgets/widget-stickyHeaders.js | 4 +- .../jquery-tablesorter/theme.bootstrap.css | 35 +++- .../jquery-tablesorter/theme.bootstrap_2.css | 32 ++- .../jquery-tablesorter/theme.bootstrap_3.css | 190 ++++++++++++++++++ .../jquery-tablesorter/theme.bootstrap_4.css | 189 +++++++++++++++++ .../jquery-tablesorter/widget.grouping.css | 38 ++++ 17 files changed, 638 insertions(+), 118 deletions(-) create mode 100644 vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_3.css create mode 100644 vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_4.css create mode 100644 vendor/assets/stylesheets/jquery-tablesorter/widget.grouping.css diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ac3bd4..875ef34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.23.4 (2017-01-08) + +* Upgrade tablesorter to v2.28.4 + #### v1.23.3 (2016-12-19) * Upgrade tablesorter to v2.28.3 diff --git a/README.md b/README.md index a5b65ba..6bdb398 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.28.3 (12/16/2016), [documentation] +Current tablesorter version: 2.28.4 (1/6/2017), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 28a346c..caa5789 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 23 - TINY = 3 + TINY = 4 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 09e5010..245c951 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 09e5010be1dc94865bcf785e1325d6ddd34781be +Subproject commit 245c951659789908fe3df20b7481acd2a361a42a diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index e954113..ad8599e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin -* updated 11/26/2016 (v2.28.0) +* updated 1/6/2017 (v2.28.4) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -173,7 +173,7 @@ updatePageDisplay = function(table, p, completed) { if ( p.initializing ) { return; } - var s, t, $out, indx, len, options, + var s, t, $out, $el, indx, len, options, output, c = table.config, namespace = c.namespace + 'pager', sz = parsePageSize( p, p.size, 'get' ); // don't allow dividing by zero @@ -196,8 +196,12 @@ if (typeof p.output === 'function') { s = p.output(table, p); } else { + output = $out + // get output template from data-pager-output or data-pager-output-filtered + .attr('data-pager-output' + (p.filteredRows < p.totalRows ? '-filtered' : '')) || + p.output; // form the output string (can now get a new output string from the server) - s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || p.output : p.output ) + s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || output : output ) // {page} = one-based index; {page+#} = zero based index +/- value .replace(/\{page([\-+]\d+)?\}/gi, function(m, n){ return p.totalPages ? p.page + (n ? parseInt(n, 10) : 1) : 0; @@ -218,7 +222,8 @@ return extra.length > 1 && data && data[extra[0]] ? data[extra[0]][extra[1]] : p[str] || (data ? data[str] : deflt) || deflt; }); } - if ( p.$goto.length ) { + $el = p.$container.find(p.cssGoto); + if ( $el.length ) { t = ''; options = buildPageSelect( table, p ); len = options.length; @@ -226,7 +231,7 @@ t += '<option value="' + options[indx] + '">' + options[indx] + '</option>'; } // innerHTML doesn't work in IE9 - http://support2.microsoft.com/kb/276228 - p.$goto.html(t).val( p.page + 1 ); + $el.html(t).val( p.page + 1 ); } if ($out.length) { $out[ ($out[0].nodeName === 'INPUT') ? 'val' : 'html' ](s); @@ -388,8 +393,9 @@ }, hideRowsSetup = function(table, p){ - p.size = parsePageSize( p, p.$size.val(), 'get' ); - p.$size.val( p.size ); + var $el = p.$container.find(p.cssPageSize); + p.size = parsePageSize( p, $el.val(), 'get' ); + $el.val( p.size ); $.data(table, 'pagerLastSize', p.size); pagerArrows( table, p ); if ( !p.removeRows ) { @@ -470,10 +476,12 @@ } } p.processAjaxOnInit = true; - // only add new header text if the length matches - if ( th && th.length === hl ) { + // update new header text + if ( th ) { hsh = $table.hasClass('hasStickyHeaders'); - $sh = hsh ? c.widgetOptions.$sticky.children('thead:first').children('tr').children() : ''; + $sh = hsh ? + c.widgetOptions.$sticky.children('thead:first').children('tr:not(.' + c.cssIgnoreRow + ')').children() : + ''; $f = $table.find('tfoot tr:first').children(); // don't change td headers (may contain pager) $headers = c.$headers.filter( 'th ' ); @@ -483,15 +491,17 @@ // add new test within the first span it finds, or just in the header if ( $h.find('.' + ts.css.icon).length ) { icon = $h.find('.' + ts.css.icon).clone(true); - $h.find('.tablesorter-header-inner').html( th[j] ).append(icon); + $h.find('.' + ts.css.headerIn).html( th[j] ).append(icon); if ( hsh && $sh.length ) { icon = $sh.eq(j).find('.' + ts.css.icon).clone(true); - $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ).append(icon); + $sh.eq(j).find('.' + ts.css.headerIn).html( th[j] ).append(icon); } } else { - $h.find('.tablesorter-header-inner').html( th[j] ); + $h.find('.' + ts.css.headerIn).html( th[j] ); if (hsh && $sh.length) { - $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ); + // add sticky header to container just in case it contains pager controls + p.$container = p.$container.add( c.widgetOptions.$sticky ); + $sh.eq(j).find('.' + ts.css.headerIn).html( th[j] ); } } $f.eq(j).html( th[j] ); @@ -693,9 +703,7 @@ } } // disable size selector - $controls = p.$size - .add( p.$goto ) - .add( p.$container.find( '.ts-startRow, .ts-page' ) ); + $controls = p.$container.find( p.cssGoto + ',' + p.cssPageSize + ', .ts-startRow, .ts-page' ); len = $controls.length; for ( index = 0; index < len; index++ ) { $controls.eq( index ).addClass( p.cssDisabled )[0].disabled = true; @@ -798,7 +806,7 @@ parsePageSize = function( p, size, mode ) { var s = parseInt( size, 10 ) || p.size || p.settings.size || 10; return p.initialized && (/all/i.test( size ) || s === p.totalRows) ? - // "get" to get `p.size` or "set" to set `p.$size.val()` + // "get" to get `p.size` or "set" to set `pageSize.val()` 'all' : ( mode === 'get' ? s : p.size ); }, @@ -812,7 +820,7 @@ setPageSize = function(table, size, p) { p.size = parsePageSize( p, size, 'get' ); - p.$size.val( parsePageSize( p, p.size, 'set' ) ); + p.$container.find(p.cssPageSize).val( parsePageSize( p, p.size, 'set' ) ); $.data(table, 'pagerLastPage', parsePageNumber( table, p ) ); $.data(table, 'pagerLastSize', p.size); p.totalPages = p.size === 'all' ? 1 : Math.ceil( p.totalRows / p.size ); @@ -887,7 +895,7 @@ enablePager = function(table, p, triggered) { var info, size, $el, c = table.config; - p.$size.add(p.$goto).add(p.$container.find('.ts-startRow, .ts-page')) + p.$container.find(p.cssGoto + ',' + p.cssPageSize + ',.ts-startRow, .ts-page') .removeClass(p.cssDisabled) .removeAttr('disabled') .each(function(){ @@ -895,9 +903,10 @@ }); p.isDisabled = false; p.page = $.data(table, 'pagerLastPage') || p.page || 0; - size = p.$size.find('option[selected]').val(); + $el = p.$container.find(p.cssPageSize); + size = $el.find('option[selected]').val(); p.size = $.data(table, 'pagerLastSize') || parsePageSize( p, size, 'get' ); - p.$size.val( p.size ); // set page size + $el.val( p.size ); // set page size p.totalPages = p.size === 'all' ? 1 : Math.ceil( getTotalPages( table, p ) / p.size ); // if table id exists, include page display with aria info if ( table.id && !c.$table.attr( 'aria-describedby' ) ) { @@ -923,7 +932,7 @@ }, init = function(table, settings) { - var t, ctrls, fxn, size, + var t, ctrls, fxn, size, $el, c = table.config, wo = c.widgetOptions, p = c.pager = $.extend( true, {}, $.tablesorterPager.defaults, settings ), @@ -1057,9 +1066,9 @@ }); // goto selector - p.$goto = pager.find(p.cssGoto); - if ( p.$goto.length ) { - p.$goto + $el = pager.find(p.cssGoto); + if ( $el.length ) { + $el .unbind('change' + namespace) .bind('change' + namespace, function(){ p.page = $(this).val() - 1; @@ -1070,14 +1079,15 @@ console.warn('Pager: >> Goto selector not found'); } // page size selector - p.$size = pager.find(p.cssPageSize); - if ( p.$size.length ) { + $el = pager.find(p.cssPageSize); + if ( $el.length ) { // setting an option as selected appears to cause issues with initial page size - p.$size.find('option').removeAttr('selected'); - p.$size.unbind('change' + namespace).bind('change' + namespace, function() { + $el.find('option').removeAttr('selected'); + $el.unbind('change' + namespace).bind('change' + namespace, function() { if ( !$(this).hasClass(p.cssDisabled) ) { var size = $(this).val(); - p.$size.val( size ); // in case there are more than one pagers + // in case there are more than one pager + p.$container.find(p.cssGoto).val( size ); setPageSize(table, size, p); changeHeight(table, p); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index c619536..8bcd34e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 12-16-2016 (v2.28.3)*/ +/*! tablesorter (FORK) - updated 01-06-2017 (v2.28.4)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.28.3 *//* +/*! TableSorter (FORK) v2.28.4 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.3', + version : '2.28.4', parsers : [], widgets : [], @@ -5071,7 +5071,7 @@ })( jQuery ); -/*! Widget: stickyHeaders - updated 7/31/2016 (v2.27.0) *//* +/*! Widget: stickyHeaders - updated 1/6/2017 (v2.28.4) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -5134,7 +5134,7 @@ // ************************** ts.addWidget({ id: 'stickyHeaders', - priority: 55, // sticky widget must be initialized after the filter widget! + priority: 54, // sticky widget must be initialized after the filter & before pager widget! options: { stickyHeaders : '', // extra class name added to the sticky header row stickyHeaders_appendTo : null, // jQuery selector or object to phycially attach the sticky headers diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index c93f759..c1dc95c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.28.3 *//* +/*! TableSorter (FORK) v2.28.4 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.3', + version : '2.28.4', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 51877bc..4430547 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 12-16-2016 (v2.28.3)*/ +/*! tablesorter (FORK) - updated 01-06-2017 (v2.28.4)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -2276,7 +2276,7 @@ })( jQuery ); -/*! Widget: stickyHeaders - updated 7/31/2016 (v2.27.0) *//* +/*! Widget: stickyHeaders - updated 1/6/2017 (v2.28.4) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -2339,7 +2339,7 @@ // ************************** ts.addWidget({ id: 'stickyHeaders', - priority: 55, // sticky widget must be initialized after the filter widget! + priority: 54, // sticky widget must be initialized after the filter & before pager widget! options: { stickyHeaders : '', // extra class name added to the sticky header row stickyHeaders_appendTo : null, // jQuery selector or object to phycially attach the sticky headers diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index 7b55e38..5615cd7 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -152,7 +152,7 @@ if ($container.length) { // set default col title name = $header.attr(wo.columnSelector_name) || $header.text().trim(); - if (typeof wo.columnSelector_layoutCustomizer === "function") { + if (typeof wo.columnSelector_layoutCustomizer === 'function') { $el = $header.find('.' + ts.css.headerIn); name = wo.columnSelector_layoutCustomizer( $el.length ? $el : $header, name, parseInt(colId, 10) ); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index 06f6901..2100080 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -1,8 +1,9 @@ -/*! Widget: output - updated 7/31/2016 (v2.27.0) *//* +/*! Widget: output - updated 1/6/2017 (v2.28.4) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * Modified from: * HTML Table to CSV: http://www.kunalbabre.com/projects/table2CSV.php (License unknown?) * Download-File-JS: https://github.com/PixelsCommander/Download-File-JS (http://www.apache.org/licenses/LICENSE-2.0) + * FileSaver.js: https://github.com/eligrey/FileSaver.js (MIT) */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery:false, alert:false */ @@ -25,7 +26,7 @@ replaceTab : '\x09', popupTitle : 'Output', - popupStyle : 'width:100%;height:100%;', // for textarea + popupStyle : 'width:100%;height:100%;margin:0;resize:none;', // for textarea message : 'Your device does not support downloading. Please try again in desktop browser.', init : function(c) { @@ -162,7 +163,6 @@ // process to array of arrays csvData = output.processRow(c, $rows); - if (wo.output_includeFooter) { // clone, to force the tfoot rows to the end of this selection of rows // otherwise they appear after the thead (the order in the HTML) @@ -183,9 +183,13 @@ // requires JSON stringify; if it doesn't exist, the output will show [object Object],... in the output window mydata = hasStringify ? JSON.stringify(tmpData) : tmpData; } else { - tmp = [ headers[ ( len > 1 && wo.output_headerRows ) ? indx % len : len - 1 ] ]; - tmpData = output.row2CSV(wo, wo.output_headerRows ? headers : tmp, outputArray) - .concat( output.row2CSV(wo, csvData, outputArray) ); + if (wo.output_includeHeader) { + tmp = [ headers[ ( len > 1 && wo.output_headerRows ) ? indx % len : len - 1 ] ]; + tmpData = output.row2CSV(wo, wo.output_headerRows ? headers : tmp, outputArray) + .concat( output.row2CSV(wo, csvData, outputArray) ); + } else { + tmpData = output.row2CSV(wo, csvData, outputArray); + } // stringify the array; if stringify doesn't exist the array will be flattened mydata = outputArray && hasStringify ? JSON.stringify(tmpData) : tmpData.join('\n'); @@ -204,7 +208,7 @@ if ( /p/i.test( wo.output_delivery || '' ) ) { output.popup(mydata, wo.output_popupStyle, outputJSON || outputArray); } else { - output.download(wo, mydata); + output.download(c, wo, mydata); } }, // end process @@ -274,13 +278,20 @@ popup : function(data, style, wrap) { var generator = window.open('', output.popupTitle, style); - generator.document.write( - '<html><head><title>' + output.popupTitle + '</title></head><body>' + - '<textarea wrap="' + (wrap ? 'on' : 'off') + '" style="' + output.popupStyle + '">' + data + '\n</textarea>' + - '</body></html>' - ); - generator.document.close(); - generator.focus(); + try { + generator.document.write( + '<html><head><title>' + output.popupTitle + '</title></head><body>' + + '<textarea wrap="' + (wrap ? 'on' : 'off') + '" style="' + + output.popupStyle + '">' + data + '\n</textarea>' + + '</body></html>' + ); + generator.document.close(); + generator.focus(); + } catch (e) { + // popup already open + generator.close(); + return output.popup(data, style, wrap); + } // select all text and focus within the textarea in the popup // $(generator.document).find('textarea').select().focus(); return true; @@ -288,13 +299,18 @@ // modified from https://github.com/PixelsCommander/Download-File-JS // & http://html5-demos.appspot.com/static/a.download.html - download : function (wo, data){ + download : function (c, wo, data) { + + if (typeof wo.output_savePlugin === 'function') { + return wo.output_savePlugin(c, wo, data); + } - var e, blob, gotBlob, + var e, blob, gotBlob, bom, nav = window.navigator, link = document.createElement('a'); - // iOS devices do not support downloading. We have to inform user about this. + // iOS devices do not support downloading. We have to inform user about + // this limitation. if (/(iP)/g.test(nav.userAgent)) { alert(output.message); return false; @@ -311,8 +327,12 @@ if ( gotBlob ) { window.URL = window.URL || window.webkitURL; - // prepend BOM for utf-8 encoding - see https://github.com/eligrey/FileSaver.js/blob/master/FileSaver.js#L140 - blob = new Blob( [ '\ufeff', data ], { type: wo.output_encoding } ); + // prepend BOM for UTF-8 XML and text/* types (including HTML) + // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF + // see https://github.com/eligrey/FileSaver.js/blob/master/FileSaver.js#L68 + bom = (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(wo.output_encoding)) ? + [ '\ufeff', data ] : [ data ]; + blob = new Blob( bom, { type: wo.output_encoding } ); if (nav.msSaveBlob) { // IE 10+ @@ -353,8 +373,9 @@ output_ignoreColumns : [], // columns to ignore [0, 1,... ] (zero-based index) output_hiddenColumns : false, // include hidden columns in the output output_includeFooter : false, // include footer rows in the output + output_includeHeader : true, // include header rows in the output + output_headerRows : false, // if true, include multiple header rows output_dataAttrib : 'data-name', // header attrib containing modified header name - output_headerRows : false, // if true, include multiple header rows (JSON only) output_delivery : 'popup', // popup, download output_saveRows : 'filtered', // (a)ll, (v)isible, (f)iltered or jQuery filter selector output_duplicateSpans : true, // duplicate output data in tbody colspan/rowspan @@ -373,7 +394,13 @@ // JSON callback executed when a colspan is encountered in the header output_callbackJSON : function($cell, txt, cellIndex) { return txt + '(' + (cellIndex) + ')'; }, // the need to modify this for Excel no longer exists - output_encoding : 'data:application/octet-stream;charset=utf8,' + output_encoding : 'data:application/octet-stream;charset=utf8,', + // override internal save file code and use an external plugin such as + // https://github.com/eligrey/FileSaver.js + output_savePlugin : null /* function(c, wo, data) { + var blob = new Blob([data], {type: wo.output_encoding}); + saveAs(blob, wo.output_saveFileName); + } */ }, init: function(table, thisWidget, c) { output.init(c); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index f5814a4..95a6850 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 11/26/2016 (v2.28.0) */ +/*! Widget: Pager - updated 1/6/2017 (v2.28.4) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -109,6 +109,7 @@ prev : '.prev', // previous page arrow next : '.next', // next page arrow last : '.last', // go to last page arrow + // goto is a reserved word #657 gotoPage : '.gotoPage', // go to page selector - select dropdown that sets the current page pageDisplay : '.pagedisplay', // location of where the 'output' is displayed pageSize : '.pagesize' // page size selector - select dropdown that sets the 'size' option @@ -168,10 +169,6 @@ p.size = $.data( table, 'pagerLastSize' ) || wo.pager_size; // added in case the pager is reinitialized after being destroyed. p.$container = $( s.container ).addClass( wo.pager_css.container ).show(); - // goto selector - p.$goto = p.$container.find( s.gotoPage ); // goto is a reserved word #657 - // page size selector - p.$size = p.$container.find( s.pageSize ); p.totalRows = c.$tbodies.eq( 0 ) .children( 'tr' ) .not( wo.pager_countChildRows ? '' : '.' + c.cssChildRow ) @@ -184,7 +181,7 @@ p.page = ( isNaN( t.page ) ? p.page : t.page ) || p.setPage || 0; p.size = t.size === 'all' ? t.size : ( isNaN( t.size ) ? p.size : t.size ) || p.setSize || 10; $.data( table, 'pagerLastSize', p.size ); - p.$size.val( p.size ); + p.$container.find( s.pageSize ).val( p.size ); } // skipped rows @@ -243,7 +240,7 @@ }, bindEvents: function( c ) { - var ctrls, fxn, + var ctrls, fxn, tmp, p = c.pager, wo = c.widgetOptions, namespace = c.namespace + 'pager', @@ -359,8 +356,9 @@ } }); - if ( p.$goto.length ) { - p.$goto + tmp = p.$container.find( wo.pager_selectors.gotoPage ); + if ( tmp.length ) { + tmp .off( 'change' + namespace ) .on( 'change' + namespace, function() { p.page = $( this ).val() - 1; @@ -371,15 +369,17 @@ console.warn( 'Pager: >> Goto selector not found' ); } - if ( p.$size.length ) { + tmp = p.$container.find( wo.pager_selectors.pageSize ); + if ( tmp.length ) { // setting an option as selected appears to cause issues with initial page size - p.$size.find( 'option' ).removeAttr( 'selected' ); - p.$size + tmp.find( 'option' ).removeAttr( 'selected' ); + tmp .off( 'change' + namespace ) .on( 'change' + namespace, function() { if ( !$( this ).hasClass( wo.pager_css.disabled ) ) { var size = $( this ).val(); - p.$size.val( size ); // in case there are more than one pagers + // in case there are more than one pager + p.$container.find( wo.pager_selectors.pageSize ).val( size ); tsp.setPageSize( c, size ); tsp.changeHeight( c ); } @@ -439,7 +439,7 @@ updatePageDisplay: function( c, completed ) { if ( c.pager && c.pager.initializing ) { return; } - var s, t, $out, options, indx, len, + var s, t, $out, options, indx, len, output, table = c.table, wo = c.widgetOptions, p = c.pager, @@ -447,8 +447,7 @@ sz = tsp.parsePageSize( c, p.size, 'get' ); // don't allow dividing by zero if ( sz === 'all' ) { sz = p.totalRows; } if ( wo.pager_countChildRows ) { t[ t.length ] = c.cssChildRow; } - p.$size - .add( p.$goto ) + p.$container.find( wo.pager_selectors.pageSize + ',' + wo.pager_selectors.gotoPage ) .removeClass( wo.pager_css.disabled ) .removeAttr( 'disabled' ) .prop( 'aria-disabled', 'false' ); @@ -469,8 +468,12 @@ if ( typeof wo.pager_output === 'function' ) { s = wo.pager_output( table, p ); } else { + output = $out + // get output template from data-pager-output or data-pager-output-filtered + .attr('data-pager-output' + (p.filteredRows < p.totalRows ? '-filtered' : '')) || + wo.pager_output; // form the output string (can now get a new output string from the server) - s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || wo.pager_output : wo.pager_output ) + s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || output : output ) // {page} = one-based index; {page+#} = zero based index +/- value .replace( /\{page([\-+]\d+)?\}/gi, function( m, n ) { return p.totalPages ? p.page + ( n ? parseInt( n, 10 ) : 1 ) : 0; @@ -494,7 +497,7 @@ p[ str ] || ( data ? data[ str ] : deflt ) || deflt; }); } - if ( p.$goto.length ) { + if ( p.$container.find( wo.pager_selectors.gotoPage ).length ) { t = ''; options = tsp.buildPageSelect( c, p ); len = options.length; @@ -502,7 +505,7 @@ t += '<option value="' + options[ indx ] + '">' + options[ indx ] + '</option>'; } // innerHTML doesn't work in IE9 - http://support2.microsoft.com/kb/276228 - p.$goto.html( t ).val( p.page + 1 ); + p.$container.find( wo.pager_selectors.gotoPage ).html( t ).val( p.page + 1 ); } if ( $out.length ) { $out[ ($out[ 0 ].nodeName === 'INPUT' ) ? 'val' : 'html' ]( s ); @@ -687,9 +690,10 @@ hideRowsSetup: function( c ) { var p = c.pager, namespace = c.namespace + 'pager', - size = p.$size.val(); + $el = p.$container.find( c.widgetOptions.pager_selectors.pageSize ), + size = $el.val(); p.size = tsp.parsePageSize( c, size, 'get' ); - p.$size.val( p.size ); + $el.val( p.size ); $.data( c.table, 'pagerLastSize', p.size ); tsp.pagerArrows( c ); if ( !c.widgetOptions.pager_removeRows ) { @@ -774,10 +778,12 @@ } } wo.pager_processAjaxOnInit = true; - // only add new header text if the length matches - if ( th && th.length === hl ) { + // update new header text + if ( th ) { hsh = $table.hasClass( 'hasStickyHeaders' ); - $sh = hsh ? wo.$sticky.children( 'thead:first' ).children( 'tr' ).children() : ''; + $sh = hsh ? + wo.$sticky.children( 'thead:first' ).children( 'tr:not(.' + c.cssIgnoreRow + ')' ).children() : + ''; $f = $table.find( 'tfoot tr:first' ).children(); // don't change td headers (may contain pager) $headers = c.$headers.filter( 'th' ); @@ -787,19 +793,24 @@ // add new test within the first span it finds, or just in the header if ( $h.find( '.' + ts.css.icon ).length ) { icon = $h.find( '.' + ts.css.icon ).clone( true ); - $h.find( '.tablesorter-header-inner' ).html( th[ j ] ).append( icon ); + $h.find( '.' + ts.css.headerIn ).html( th[ j ] ).append( icon ); if ( hsh && $sh.length ) { icon = $sh.eq( j ).find( '.' + ts.css.icon ).clone( true ); - $sh.eq( j ).find( '.tablesorter-header-inner' ).html( th[ j ] ).append( icon ); + $sh.eq( j ).find( '.' + ts.css.headerIn ).html( th[ j ] ).append( icon ); } } else { - $h.find( '.tablesorter-header-inner' ).html( th[ j ] ); + $h.find( '.' + ts.css.headerIn ).html( th[ j ] ); if ( hsh && $sh.length ) { - $sh.eq( j ).find( '.tablesorter-header-inner' ).html( th[ j ] ); + // add sticky header to container just in case it contains pager controls + p.$container = p.$container.add( wo.$sticky ); + $sh.eq( j ).find( '.' + ts.css.headerIn ).html( th[ j ] ); } } $f.eq( j ).html( th[ j ] ); } + if ( hsh ) { + tsp.bindEvents( c ); + } } } if ( c.showProcessing ) { @@ -1006,9 +1017,11 @@ } } // disable size selector - $controls = p.$size - .add( p.$goto ) - .add( p.$container.find( '.ts-startRow, .ts-page ' ) ); + $controls = p.$container.find( + wo.pager_selectors.pageSize + ',' + + wo.pager_selectors.gotoPage + ',' + + '.ts-startRow, .ts-page' + ); len = $controls.length; for ( index = 0; index < len; index++ ) { $controls.eq( index ) @@ -1122,7 +1135,7 @@ var p = c.pager, s = parseInt( size, 10 ) || p.size || c.widgetOptions.pager_size || 10; return p.initialized && (/all/i.test( size ) || s === p.totalRows) ? - // "get" to set `p.size` or "set" to set `p.$size.val()` + // "get" to set `p.size` or "set" to set `pageSize.val()` 'all' : ( mode === 'get' ? s : p.size ); }, @@ -1138,7 +1151,9 @@ var p = c.pager, table = c.table; p.size = tsp.parsePageSize( c, size, 'get' ); - p.$size.val( tsp.parsePageSize( c, p.size, 'set' ) ); + p.$container + .find( c.widgetOptions.pager_selectors.pageSize ) + .val( tsp.parsePageSize( c, p.size, 'set' ) ); $.data( table, 'pagerLastPage', tsp.parsePageNumber( c, p ) ); $.data( table, 'pagerLastSize', p.size ); p.totalPages = p.size === 'all' ? 1 : Math.ceil( p.totalRows / p.size ); @@ -1197,27 +1212,27 @@ ts.storage( table, c.widgetOptions.pager_storageKey, '' ); } p.$container = null; - p.$goto = null; - p.$size = null; c.pager = null; c.rowsCopy = null; } }, enablePager: function( c, triggered ) { - var info, size, $el, + var info, size, table = c.table, - p = c.pager; + p = c.pager, + wo = c.widgetOptions, + $el = p.$container.find( wo.pager_selectors.pageSize ); p.isDisabled = false; p.page = $.data( table, 'pagerLastPage' ) || p.page || 0; - size = p.$size.find( 'option[selected]' ).val(); + size = $el.find('option[selected]' ).val(); p.size = $.data( table, 'pagerLastSize' ) || tsp.parsePageSize( c, size, 'get' ); - p.$size.val( p.size ); // set page size + $el.val( p.size ); // set page size p.totalPages = p.size === 'all' ? 1 : Math.ceil( tsp.getTotalPages( c, p ) / p.size ); c.$table.removeClass( 'pagerDisabled' ); // if table id exists, include page display with aria info if ( table.id && !c.$table.attr( 'aria-describedby' ) ) { - $el = p.$container.find( c.widgetOptions.pager_selectors.pageDisplay ); + $el = p.$container.find( wo.pager_selectors.pageDisplay ); info = $el.attr( 'id' ); if ( !info ) { // only add pageDisplay id if it doesn't exist - see #1288 diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js index 8939513..b353b27 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js @@ -1,4 +1,4 @@ -/*! Widget: stickyHeaders - updated 7/31/2016 (v2.27.0) *//* +/*! Widget: stickyHeaders - updated 1/6/2017 (v2.28.4) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -61,7 +61,7 @@ // ************************** ts.addWidget({ id: 'stickyHeaders', - priority: 55, // sticky widget must be initialized after the filter widget! + priority: 54, // sticky widget must be initialized after the filter & before pager widget! options: { stickyHeaders : '', // extra class name added to the sticky header row stickyHeaders_appendTo : null, // jQuery selector or object to phycially attach the sticky headers diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index 3649b9a..78bfc7d 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -1,7 +1,9 @@ -/************* - Bootstrap theme - *************/ -/* jQuery Bootstrap Theme */ +/** + * Bootstrap theme v3.x + * + * WARNING!... once a stable Bootstrap v4.x is released, + * this file will be removed; use theme.bootstrap_3.css + */ .tablesorter-bootstrap { width: 100%; } @@ -83,6 +85,29 @@ background-repeat: no-repeat !important; } +/* Column Widget - column sort colors */ +.tablesorter-bootstrap > tbody > tr.odd td.primary { + background-color: #bfbfbf; +} +.tablesorter-bootstrap > tbody > tr td.primary, +.tablesorter-bootstrap > tbody > tr.even td.primary { + background-color: #d9d9d9; +} +.tablesorter-bootstrap > tbody > tr.odd td.secondary { + background-color: #d9d9d9; +} +.tablesorter-bootstrap > tbody > tr td.secondary, +.tablesorter-bootstrap > tbody > tr.even td.secondary { + background-color: #e6e6e6; +} +.tablesorter-bootstrap > tbody > tr.odd td.tertiary { + background-color: #e6e6e6; +} +.tablesorter-bootstrap > tbody > tr td.tertiary, +.tablesorter-bootstrap > tbody > tr.even td.tertiary { + background-color: #f2f2f2; +} + /* caption */ .caption { background-color: #fff; @@ -143,7 +168,7 @@ opacity: 0; filter: alpha(opacity=0); } -/* rows hidden by filtering (needed for child rows) */ +/* rows hidden by filtering */ .tablesorter .filtered { display: none; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css index ac9e8cb..aadf673 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css @@ -1,7 +1,6 @@ -/************* - Bootstrap 2 Theme - *************/ -/* jQuery Bootstrap 2 Theme */ +/** + * Bootstrap theme v2.x + */ .tablesorter-bootstrap { width: 100%; } @@ -89,6 +88,29 @@ z-index: 1000; } +/* Column Widget - column sort colors */ +.tablesorter-bootstrap > tbody > tr.odd td.primary { + background-color: #bfbfbf; +} +.tablesorter-bootstrap > tbody > tr td.primary, +.tablesorter-bootstrap > tbody > tr.even td.primary { + background-color: #d9d9d9; +} +.tablesorter-bootstrap > tbody > tr.odd td.secondary { + background-color: #d9d9d9; +} +.tablesorter-bootstrap > tbody > tr td.secondary, +.tablesorter-bootstrap > tbody > tr.even td.secondary { + background-color: #e6e6e6; +} +.tablesorter-bootstrap > tbody > tr.odd td.tertiary { + background-color: #e6e6e6; +} +.tablesorter-bootstrap > tbody > tr td.tertiary, +.tablesorter-bootstrap > tbody > tr.even td.tertiary { + background-color: #f2f2f2; +} + /* caption */ caption { background-color: #fff; @@ -145,7 +167,7 @@ caption { opacity: 0; filter: alpha(opacity=0); } -/* rows hidden by filtering (needed for child rows) */ +/* rows hidden by filtering */ .tablesorter .filtered { display: none; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_3.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_3.css new file mode 100644 index 0000000..67bc2f0 --- /dev/null +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_3.css @@ -0,0 +1,190 @@ +/** + * Bootstrap theme v3.x + */ +.tablesorter-bootstrap { + width: 100%; +} +.tablesorter-bootstrap thead th, +.tablesorter-bootstrap thead td, +.tablesorter-bootstrap tfoot th, +.tablesorter-bootstrap tfoot td { + font: 14px/20px Arial, Sans-serif; + font-weight: bold; + padding: 4px; + margin: 0 0 18px; + background-color: #eee; +} + +.tablesorter-bootstrap .tablesorter-header { + cursor: pointer; +} +.tablesorter-bootstrap .sorter-false { + cursor: default; +} + +.tablesorter-bootstrap .tablesorter-header.sorter-false i.tablesorter-icon { + display: none; +} + +.tablesorter-bootstrap .tablesorter-header-inner { + position: relative; + padding: 4px 18px 4px 4px; +} +.tablesorter-bootstrap .sorter-false .tablesorter-header-inner { + padding: 4px; +} + +/* bootstrap uses <i> for icons */ +.tablesorter-bootstrap .tablesorter-header i.tablesorter-icon { + font-size: 11px; + position: absolute; + right: 2px; + top: 50%; + margin-top: -7px; /* half the icon height; older IE doesn't like this */ + width: 14px; + height: 14px; + background-repeat: no-repeat; + line-height: 14px; + display: inline-block; +} + +/* black unsorted icon */ +.tablesorter-bootstrap .bootstrap-icon-unsorted { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAYAAAD5YeaVAAAA20lEQVR4AWJABpKSkoxALCstLb0aUAsZaCAMhVEY6B0amx8YZWDDEDSBa2AGe7XeIiAAClYwVGBvsAcIllsf/mvcC9DgOOd8h90fxWvngVEUbZIkuWRZZlE8eQjcisgZMM9zi+LJ6ZfwegmWZflZDugdHMfxTcGqql7TNBlUB/QObtv2VBSFrev6OY7jngzFk9OT/fn73fWYpqnlXNyXDMWT0zuYx/Bvel9ej+LJ6R08DMOu67q7DkTkrSA5vYPneV71fX/QASdTkJwezhs0TfMARn0wMDDGXEPgF4oijqwM5YjNAAAAAElFTkSuQmCC); +} + +/* white unsorted icon */ +.tablesorter-bootstrap .icon-white.bootstrap-icon-unsorted { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAYAAAD5YeaVAAAAe0lEQVR4AbXQoRWDMBiF0Sh2QLAAQ8SxJGugWSA6A2STW1PxTsnB9cnkfuYvv8OGC1t5G3Y0QMP+Bm857keAdQIzWBP3+Bw4MADQE18B6/etRnCV/w9nnGuLezfAmXhABGtAGIkruvk6auIFRwQJDywllsEAjCecB20GP59BQQ+gtlRLAAAAAElFTkSuQmCC); +} + +/* since bootstrap (table-striped) uses nth-child(), we just use this to add a zebra stripe color */ +.tablesorter-bootstrap > tbody > tr.odd > td, +.tablesorter-bootstrap > tbody > tr.tablesorter-hasChildRow.odd:hover ~ tr.tablesorter-hasChildRow.odd ~ .tablesorter-childRow.odd > td { + background-color: #f9f9f9; +} +.tablesorter-bootstrap > tbody > tr.hover > td, +.tablesorter-bootstrap > tbody > tr.odd:hover > td, +.tablesorter-bootstrap > tbody > tr.even:hover > td, +.tablesorter-bootstrap > tbody > tr.tablesorter-hasChildRow.odd:hover ~ .tablesorter-childRow.odd > td, +.tablesorter-bootstrap > tbody > tr.tablesorter-hasChildRow.even:hover ~ .tablesorter-childRow.even > td { + background-color: #f5f5f5; +} +.tablesorter-bootstrap > tbody > tr.even > td, +.tablesorter-bootstrap > tbody > tr.tablesorter-hasChildRow.even:hover ~ tr.tablesorter-hasChildRow.even ~ .tablesorter-childRow.even > td { + background-color: #fff; +} + +/* processing icon */ +.tablesorter-bootstrap .tablesorter-processing { + background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs='); + background-position: center center !important; + background-repeat: no-repeat !important; +} + +/* Column Widget - column sort colors */ +.tablesorter-bootstrap > tbody > tr.odd td.primary { + background-color: #bfbfbf; +} +.tablesorter-bootstrap > tbody > tr td.primary, +.tablesorter-bootstrap > tbody > tr.even td.primary { + background-color: #d9d9d9; +} +.tablesorter-bootstrap > tbody > tr.odd td.secondary { + background-color: #d9d9d9; +} +.tablesorter-bootstrap > tbody > tr td.secondary, +.tablesorter-bootstrap > tbody > tr.even td.secondary { + background-color: #e6e6e6; +} +.tablesorter-bootstrap > tbody > tr.odd td.tertiary { + background-color: #e6e6e6; +} +.tablesorter-bootstrap > tbody > tr td.tertiary, +.tablesorter-bootstrap > tbody > tr.even td.tertiary { + background-color: #f2f2f2; +} + +/* caption */ +.caption { + background-color: #fff; +} + +/* filter widget */ +.tablesorter-bootstrap .tablesorter-filter-row input.tablesorter-filter, +.tablesorter-bootstrap .tablesorter-filter-row select.tablesorter-filter { + width: 98%; + margin: 0; + padding: 4px 6px; + color: #333; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition: height 0.1s ease; + -moz-transition: height 0.1s ease; + -o-transition: height 0.1s ease; + transition: height 0.1s ease; +} +.tablesorter-bootstrap .tablesorter-filter-row .tablesorter-filter.disabled { + background-color: #eee; + color: #555; + cursor: not-allowed; + border: 1px solid #ccc; + border-radius: 4px; + box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.075) inset; + box-sizing: border-box; + transition: height 0.1s ease; +} +.tablesorter-bootstrap .tablesorter-filter-row { + background-color: #efefef; +} +.tablesorter-bootstrap .tablesorter-filter-row td { + background-color: #efefef; + line-height: normal; + text-align: center; + padding: 4px 6px; + vertical-align: middle; + -webkit-transition: line-height 0.1s ease; + -moz-transition: line-height 0.1s ease; + -o-transition: line-height 0.1s ease; + transition: line-height 0.1s ease; +} +/* hidden filter row */ +.tablesorter-bootstrap .tablesorter-filter-row.hideme td { + padding: 2px; /* change this to modify the thickness of the closed border row */ + margin: 0; + line-height: 0; +} +.tablesorter-bootstrap .tablesorter-filter-row.hideme * { + height: 1px; + min-height: 0; + border: 0; + padding: 0; + margin: 0; + /* don't use visibility: hidden because it disables tabbing */ + opacity: 0; + filter: alpha(opacity=0); +} +/* rows hidden by filtering */ +.tablesorter .filtered { + display: none; +} + +/* pager plugin */ +.tablesorter-bootstrap .tablesorter-pager select { + padding: 4px 6px; +} +.tablesorter-bootstrap .tablesorter-pager .pagedisplay { + border: 0; +} +/* tfoot i for pager controls */ +.tablesorter-bootstrap tfoot i { + font-size: 11px; +} + +/* ajax error row */ +.tablesorter .tablesorter-errorRow td { + text-align: center; + cursor: pointer; + background-color: #e6bf99; +} diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_4.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_4.css new file mode 100644 index 0000000..1d1140e --- /dev/null +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_4.css @@ -0,0 +1,189 @@ +/** + * Bootstrap theme v4.x + */ +.tablesorter-bootstrap { + width: 100%; +} +.tablesorter-bootstrap thead th, +.tablesorter-bootstrap thead td, +.tablesorter-bootstrap tfoot th, +.tablesorter-bootstrap tfoot td { + font: 14px/20px Arial, Sans-serif; + font-weight: bold; + padding: 4px; + margin: 0 0 18px; +} + +.tablesorter-bootstrap thead .tablesorter-header { + background-position: right 5px center; + background-repeat: no-repeat; + cursor: pointer; + white-space: normal; +} +.tablesorter-bootstrap:not(.table-inverse) thead:not(.thead-inverse) .tablesorter-header, +.tablesorter-bootstrap:not(.table-inverse) tfoot th, +.tablesorter-bootstrap:not(.table-inverse) tfoot td { + background-color: #eee; +} + +.tablesorter-bootstrap thead .sorter-false { + cursor: default; + background-image: none; +} + +.tablesorter-bootstrap .tablesorter-header-inner { + position: relative; + padding: 4px 18px 4px 4px; +} +.tablesorter-bootstrap .sorter-false .tablesorter-header-inner { + padding: 4px; +} + +/* black icons */ +.tablesorter-bootstrap thead .tablesorter-headerUnSorted:not(.sorter-false) { + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE0IDIwIj48cGF0aCBkPSJNMTQgMTNsLTIuNS0yLjVMNyAxNWwtNC41LTQuNUwwIDEzbDcgN3pNMTQgNy41TDExLjUgMTAgNyA1LjUgMi41IDEwIDAgNy41bDctN3oiLz48L3N2Zz4=); +} +.tablesorter-bootstrap thead .tablesorter-headerAsc { + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxMiIgdmlld0JveD0iMCAwIDE0IDE0Ij48cGF0aCBkPSJNMTQgOS41TDExLjUgMTIgNyA3LjUgMi41IDEyIDAgOS41bDctN3oiLz48L3N2Zz4=); +} +.tablesorter-bootstrap thead .tablesorter-headerDesc { + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxMiIgdmlld0JveD0iMCAwIDE0IDE0Ij48cGF0aCBkPSJNMTQgNWwtMi41LTIuNS00LjUgNC41LTQuNS00LjVMMCA1IDcgMTJ6Ii8+PC9zdmc+); +} + +/* white icons */ +.tablesorter-bootstrap thead.thead-inverse .tablesorter-headerUnSorted:not(.sorter-false), +.tablesorter-bootstrap.table-inverse thead .tablesorter-headerUnSorted:not(.sorter-false) { + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE0IDIwIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTQgMTNsLTIuNS0yLjVMNyAxNWwtNC41LTQuNUwwIDEzbDcgN3pNMTQgNy41TDExLjUgMTAgNyA1LjUgMi41IDEwIDAgNy41bDctN3oiLz48L3N2Zz4=); +} +.tablesorter-bootstrap thead.thead-inverse .tablesorter-headerAsc, +.tablesorter-bootstrap.table-inverse thead .tablesorter-headerAsc { + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxMiIgdmlld0JveD0iMCAwIDE0IDE0Ij48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTQgOS41TDExLjUgMTIgNyA3LjUgMi41IDEyIDAgOS41bDctN3oiLz48L3N2Zz4=); +} +.tablesorter-bootstrap thead.thead-inverse .tablesorter-headerDesc, +.tablesorter-bootstrap.table-inverse thead .tablesorter-headerDesc { + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxMiIgdmlld0JveD0iMCAwIDE0IDE0Ij48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTQgNWwtMi41LTIuNS00LjUgNC41LTQuNS00LjVMMCA1IDcgMTJ6Ii8+PC9zdmc+); +} + +/* since bootstrap (table-striped) uses nth-child(), we just use this to add a zebra stripe color */ +.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.odd > td, +.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.tablesorter-hasChildRow.odd:hover ~ tr.tablesorter-hasChildRow.odd ~ .tablesorter-childRow.odd > td { + background-color: #f9f9f9; +} +.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.hover > td, +.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.odd:hover > td, +.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.even:hover > td, +.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.tablesorter-hasChildRow.odd:hover ~ .tablesorter-childRow.odd > td, +.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.tablesorter-hasChildRow.even:hover ~ .tablesorter-childRow.even > td { + background-color: #f5f5f5; +} +.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.even > td, +.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.tablesorter-hasChildRow.even:hover ~ tr.tablesorter-hasChildRow.even ~ .tablesorter-childRow.even > td { + background-color: #fff; +} + +/* processing icon */ +.tablesorter-bootstrap .tablesorter-processing { + background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs='); + background-position: center center !important; + background-repeat: no-repeat !important; +} + +/* Column Widget - column sort colors */ +.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.odd td.primary { + background-color: #bfbfbf; +} +.tablesorter-bootstrap:not(.table-inverse) > tbody > tr td.primary, +.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.even td.primary { + background-color: #d9d9d9; +} +.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.odd td.secondary { + background-color: #d9d9d9; +} +.tablesorter-bootstrap:not(.table-inverse) > tbody > tr td.secondary, +.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.even td.secondary { + background-color: #e6e6e6; +} +.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.odd td.tertiary { + background-color: #e6e6e6; +} +.tablesorter-bootstrap:not(.table-inverse) > tbody > tr td.tertiary, +.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.even td.tertiary { + background-color: #f2f2f2; +} + +/* caption */ +.tablesorter-bootstrap:not(.table-inverse) .caption { + background-color: #fff; +} + +/* filter widget */ +.tablesorter-bootstrap .tablesorter-filter-row input.tablesorter-filter, +.tablesorter-bootstrap .tablesorter-filter-row select.tablesorter-filter { + width: 98%; + margin: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition: height 0.1s ease; + -moz-transition: height 0.1s ease; + -o-transition: height 0.1s ease; + transition: height 0.1s ease; +} +.tablesorter-bootstrap:not(.table-inverse) .tablesorter-filter-row { + background-color: #efefef; +} +.tablesorter-bootstrap:not(.table-inverse) .tablesorter-filter-row input.tablesorter-filter, +.tablesorter-bootstrap:not(.table-inverse) .tablesorter-filter-row select.tablesorter-filter { + color: #333; +} + +.tablesorter-bootstrap .tablesorter-filter-row .tablesorter-filter.disabled { + cursor: not-allowed; + box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.075) inset; + box-sizing: border-box; + transition: height 0.1s ease; +} + +.tablesorter-bootstrap:not(.table-inverse) .tablesorter-filter-row td { + line-height: normal; + text-align: center; + padding: 4px 6px; + vertical-align: middle; + -webkit-transition: line-height 0.1s ease; + -moz-transition: line-height 0.1s ease; + -o-transition: line-height 0.1s ease; + transition: line-height 0.1s ease; +} +/* hidden filter row */ +.tablesorter-bootstrap .tablesorter-filter-row.hideme td { + padding: 2px; /* change this to modify the thickness of the closed border row */ + margin: 0; + line-height: 0; +} +.tablesorter-bootstrap .tablesorter-filter-row.hideme * { + height: 1px; + min-height: 0; + border: 0; + padding: 0; + margin: 0; + /* don't use visibility: hidden because it disables tabbing */ + opacity: 0; + filter: alpha(opacity=0); +} +/* rows hidden by filtering */ +.tablesorter .filtered { + display: none; +} + +/* pager plugin */ + +.tablesorter-bootstrap .tablesorter-pager .pagedisplay { + border: 0; +} + +/* ajax error row */ +.tablesorter:not(.table-inverse) .tablesorter-errorRow td { + text-align: center; + cursor: pointer; + background-color: #e6bf99; +} diff --git a/vendor/assets/stylesheets/jquery-tablesorter/widget.grouping.css b/vendor/assets/stylesheets/jquery-tablesorter/widget.grouping.css new file mode 100644 index 0000000..28a04d9 --- /dev/null +++ b/vendor/assets/stylesheets/jquery-tablesorter/widget.grouping.css @@ -0,0 +1,38 @@ +/* Grouping widget css */ +tr.group-header td { + background: #eee; +} +.group-name { + text-transform: uppercase; + font-weight: bold; +} +.group-count { + color: #999; +} +.group-hidden { + display: none !important; +} +.group-header, .group-header td { + user-select: none; + -moz-user-select: none; +} +/* collapsed arrow */ +tr.group-header td i { + display: inline-block; + width: 0; + height: 0; + border-top: 4px solid transparent; + border-bottom: 4px solid #888; + border-right: 4px solid #888; + border-left: 4px solid transparent; + margin-right: 7px; + user-select: none; + -moz-user-select: none; +} +tr.group-header.collapsed td i { + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-left: 5px solid #888; + border-right: 0; + margin-right: 10px; +} From d7880118cac9f9f9336e9b6f6a8e49444c8323c6 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Fri, 10 Feb 2017 12:10:33 +0100 Subject: [PATCH 107/138] Update tablesorter to latest version (2.28.5) --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 13 ++++++---- .../jquery-tablesorter/jquery.tablesorter.js | 4 ++-- .../jquery.tablesorter.widgets.js | 9 ++++--- .../widgets/widget-output.js | 24 +++++++++++++++---- .../widgets/widget-resizable.js | 7 ++++-- .../widgets/widget-scroller.js | 3 ++- 10 files changed, 50 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 875ef34..46e1bd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.23.5 (2017-01-10) + +* Upgrade tablesorter to v2.28.5 + #### v1.23.4 (2017-01-08) * Upgrade tablesorter to v2.28.4 diff --git a/README.md b/README.md index 6bdb398..dc64adc 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.28.4 (1/6/2017), [documentation] +Current tablesorter version: 2.28.5 (1/28/2017), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index caa5789..fa904c3 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 23 - TINY = 4 + TINY = 5 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 245c951..88d3e00 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 245c951659789908fe3df20b7481acd2a361a42a +Subproject commit 88d3e00c5e6637ff4932bda537e1654a394e6b04 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 8bcd34e..46b9e85 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 01-06-2017 (v2.28.4)*/ +/*! tablesorter (FORK) - updated 01-28-2017 (v2.28.5)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.28.4 *//* +/*! TableSorter (FORK) v2.28.5 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.4', + version : '2.28.5', parsers : [], widgets : [], @@ -5374,7 +5374,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 12/15/2016 (v2.28.2) */ +/*! Widget: resizable - updated 1/28/2017 (v2.28.5) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -5630,9 +5630,12 @@ // right click to reset columns to default widths c.$table - .bind( 'columnUpdate' + namespace + ' pagerComplete' + namespace, function() { + .bind( 'columnUpdate pagerComplete resizableUpdate '.split( ' ' ).join( namespace + ' ' ), function() { ts.resizable.setHandlePosition( c, wo ); }) + .bind( 'resizableReset' + namespace, function() { + ts.resizableReset( c.table ); + }) .find( 'thead:first' ) .add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) .bind( 'contextmenu' + namespace, function() { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index c1dc95c..a885a40 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.28.4 *//* +/*! TableSorter (FORK) v2.28.5 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.4', + version : '2.28.5', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 4430547..78f65b2 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 01-06-2017 (v2.28.4)*/ +/*! tablesorter (FORK) - updated 01-28-2017 (v2.28.5)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -2579,7 +2579,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 12/15/2016 (v2.28.2) */ +/*! Widget: resizable - updated 1/28/2017 (v2.28.5) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -2835,9 +2835,12 @@ // right click to reset columns to default widths c.$table - .bind( 'columnUpdate' + namespace + ' pagerComplete' + namespace, function() { + .bind( 'columnUpdate pagerComplete resizableUpdate '.split( ' ' ).join( namespace + ' ' ), function() { ts.resizable.setHandlePosition( c, wo ); }) + .bind( 'resizableReset' + namespace, function() { + ts.resizableReset( c.table ); + }) .find( 'thead:first' ) .add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) .bind( 'contextmenu' + namespace, function() { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index 2100080..7d6fa62 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -1,4 +1,4 @@ -/*! Widget: output - updated 1/6/2017 (v2.28.4) *//* +/*! Widget: output - updated 1/28/2017 (v2.28.5) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * Modified from: * HTML Table to CSV: http://www.kunalbabre.com/projects/table2CSV.php (License unknown?) @@ -15,6 +15,13 @@ output = ts.output = { event : 'outputTable', + // Double click time is about 500ms; this value ignores double clicks + // and prevents multiple windows from opening - issue in Firefox + noDblClick : 600, // ms + lastEvent : 0, + // prevent overlapping multiple opens in case rendering of content in + // popup or download is longer than noDblClick time. + busy : false, // wrap line breaks & tabs in quotes regexQuote : /([\n\t\x09\x0d\x0a]|<[^<]+>)/, // test if cell needs wrapping quotes @@ -34,9 +41,17 @@ .off(output.event) .on(output.event, function( e ) { e.stopPropagation(); - // explicitly use table.config.widgetOptions because we want - // the most up-to-date values; not the 'wo' from initialization - output.process(c, c.widgetOptions); + // prevent multiple windows opening + if ( + !output.busy && + (e.timeStamp - output.lastEvent > output.noDblClick) + ) { + output.lastEvent = e.timeStamp; + output.busy = true; + // explicitly use table.config.widgetOptions because we want + // the most up-to-date values; not the 'wo' from initialization + output.process(c, c.widgetOptions); + } }); }, @@ -210,6 +225,7 @@ } else { output.download(c, wo, mydata); } + output.busy = false; }, // end process diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js index fe47ea7..5e3f943 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js @@ -1,4 +1,4 @@ -/*! Widget: resizable - updated 12/15/2016 (v2.28.2) */ +/*! Widget: resizable - updated 1/28/2017 (v2.28.5) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -254,9 +254,12 @@ // right click to reset columns to default widths c.$table - .bind( 'columnUpdate' + namespace + ' pagerComplete' + namespace, function() { + .bind( 'columnUpdate pagerComplete resizableUpdate '.split( ' ' ).join( namespace + ' ' ), function() { ts.resizable.setHandlePosition( c, wo ); }) + .bind( 'resizableReset' + namespace, function() { + ts.resizableReset( c.table ); + }) .find( 'thead:first' ) .add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) .bind( 'contextmenu' + namespace, function() { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index d796fd7..6be6b15 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -1,4 +1,4 @@ -/*! Widget: scroller - updated 7/31/2016 (v2.27.0) *//* +/*! Widget: scroller - updated 1/28/2017 (v2.28.5) *//* Copyright (C) 2011 T. Connell & Associates, Inc. Dual-licensed under the MIT and GPL licenses @@ -469,6 +469,7 @@ // update resizable widget handles setTimeout( function() { c.$table.triggerHandler( 'resizableUpdate' ); + c.$table.triggerHandler( 'scrollerComplete' ); }, 100 ); }, From b3d0bb9a9c830881dc18a189f0abce23c32dfd1f Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Sun, 2 Apr 2017 19:29:28 +0200 Subject: [PATCH 108/138] Update tablesorter to latest version (2.28.6) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 54 +++++---- .../jquery.tablesorter.combined.js | 89 +++++++++------ .../jquery-tablesorter/jquery.tablesorter.js | 52 ++++++--- .../jquery.tablesorter.widgets.js | 37 ++++--- .../parsers/parser-leading-zeros.js | 33 ++++++ .../widgets/widget-build-table.js | 4 +- .../widgets/widget-filter.js | 35 +++--- .../widgets/widget-output.js | 4 +- .../widgets/widget-pager.js | 51 +++++---- .../widgets/widget-sort2Hash.js | 15 ++- .../jquery-tablesorter/highlights.css | 104 ++++++++++++++++++ 15 files changed, 348 insertions(+), 140 deletions(-) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/parsers/parser-leading-zeros.js create mode 100644 vendor/assets/stylesheets/jquery-tablesorter/highlights.css diff --git a/CHANGELOG.md b/CHANGELOG.md index 46e1bd2..ff9f8c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.23.6 (2017-04-02) + +* Upgrade tablesorter to v2.28.6 + #### v1.23.5 (2017-01-10) * Upgrade tablesorter to v2.28.5 diff --git a/README.md b/README.md index dc64adc..9160d39 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.28.5 (1/28/2017), [documentation] +Current tablesorter version: 2.28.6 (4/2/2017), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index fa904c3..3f90a16 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 23 - TINY = 5 + TINY = 6 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 88d3e00..f006d8a 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 88d3e00c5e6637ff4932bda537e1654a394e6b04 +Subproject commit f006d8aa5f6141086de08e30317471d31f9e7434 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index ad8599e..a82facc 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin -* updated 1/6/2017 (v2.28.4) +* updated 4/2/2017 (v2.28.6) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -393,10 +393,8 @@ }, hideRowsSetup = function(table, p){ - var $el = p.$container.find(p.cssPageSize); - p.size = parsePageSize( p, $el.val(), 'get' ); - $el.val( p.size ); - $.data(table, 'pagerLastSize', p.size); + p.size = parsePageSize( p, p.$container.find(p.cssPageSize).val(), 'get' ); + setPageSize( table, p.size, p ); pagerArrows( table, p ); if ( !p.removeRows ) { hideRows(table, p); @@ -689,7 +687,7 @@ $.data(table, 'pagerLastPage', p.page); $.data(table, 'pagerLastSize', p.size); p.page = 0; - p.size = 'all'; + p.size = p.totalPages; p.totalPages = 1; $(table) .addClass('pagerDisabled') @@ -799,15 +797,9 @@ }, getTotalPages = function( table, p ) { - return ts.hasWidget( table, 'filter' ) ? Math.min( p.totalPages, p.filteredPages ) : p.totalPages; - }, - - // set to either set or get value - parsePageSize = function( p, size, mode ) { - var s = parseInt( size, 10 ) || p.size || p.settings.size || 10; - return p.initialized && (/all/i.test( size ) || s === p.totalRows) ? - // "get" to get `p.size` or "set" to set `pageSize.val()` - 'all' : ( mode === 'get' ? s : p.size ); + return ts.hasWidget( table, 'filter' ) ? + Math.min( p.totalPages, p.filteredPages ) : + p.totalPages; }, parsePageNumber = function( table, p ) { @@ -818,14 +810,26 @@ return p.page; }, + // set to either set or get value + parsePageSize = function( p, size, mode ) { + var s = parseInt( size, 10 ) || p.size || p.settings.size || 10; + if (p.initialized && (/all/i.test( s + ' ' + size ) || s === p.totalRows)) { + // Fixing #1364 & #1366 + return p.$container.find(p.cssPageSize + ' option[value="all"]').length ? + 'all' : p.totalRows; + } + // "get" to get `p.size` or "set" to set `pageSize.val()` + return mode === 'get' ? s : p.size; + }, + setPageSize = function(table, size, p) { + // "all" size is only returned if an "all" option exists - fixes #1366 p.size = parsePageSize( p, size, 'get' ); - p.$container.find(p.cssPageSize).val( parsePageSize( p, p.size, 'set' ) ); + p.$container.find( p.cssPageSize ).val( p.size ); $.data(table, 'pagerLastPage', parsePageNumber( table, p ) ); $.data(table, 'pagerLastSize', p.size); p.totalPages = p.size === 'all' ? 1 : Math.ceil( p.totalRows / p.size ); p.filteredPages = p.size === 'all' ? 1 : Math.ceil( p.filteredRows / p.size ); - moveToPage(table, p); }, moveToFirstPage = function(table, p) { @@ -906,8 +910,8 @@ $el = p.$container.find(p.cssPageSize); size = $el.find('option[selected]').val(); p.size = $.data(table, 'pagerLastSize') || parsePageSize( p, size, 'get' ); - $el.val( p.size ); // set page size p.totalPages = p.size === 'all' ? 1 : Math.ceil( getTotalPages( table, p ) / p.size ); + setPageSize(table, p.size, p); // set page size // if table id exists, include page display with aria info if ( table.id && !c.$table.attr( 'aria-describedby' ) ) { $el = p.$container.find( p.cssPageDisplay ); @@ -924,6 +928,7 @@ // tablesorter core update table ts.update( c ); setPageSize(table, p.size, p); + moveToPage(table, p); hideRowsSetup(table, p); if (c.debug) { console.log('Pager: Enabled'); @@ -952,8 +957,7 @@ t = ts.storage(table, p.storageKey) || {}; // fixes #387 p.page = isNaN(t.page) ? p.page : t.page; p.size = t.size === 'all' ? t.size : ( isNaN( t.size ) ? p.size : t.size ) || p.setSize || 10; - $.data(table, 'pagerLastSize', p.size); - pager.find(p.cssPageSize).val(p.size); + setPageSize(table, p.size, p); } // skipped rows p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.slice(1) + '|' + c.cssChildRow + ')'); @@ -965,7 +969,10 @@ .bind('filterInit filterStart '.split(' ').join(namespace + ' '), function(e, filters) { p.currentFilters = $.isArray(filters) ? filters : c.$table.data('lastSearch'); // don't change page if filters are the same (pager updating, etc) - if (e.type === 'filterStart' && p.pageReset !== false && (c.lastCombinedFilter || '') !== (p.currentFilters || []).join('')) { + if ( + e.type === 'filterStart' && + p.pageReset !== false && + (c.lastSearch || []).join(',') !== (p.currentFilters || []).join(',')) { p.page = p.pageReset; // fixes #456 & #565 } }) @@ -1020,6 +1027,7 @@ .bind('pageSize refreshComplete '.split(' ').join(namespace + ' '), function(e, size){ e.stopPropagation(); setPageSize(table, parsePageSize( p, size, 'get' ), p); + moveToPage(table, p); hideRows(table, p); updatePageDisplay(table, p, false); }) @@ -1087,8 +1095,8 @@ if ( !$(this).hasClass(p.cssDisabled) ) { var size = $(this).val(); // in case there are more than one pager - p.$container.find(p.cssGoto).val( size ); setPageSize(table, size, p); + moveToPage(table, p); changeHeight(table, p); } return false; @@ -1122,6 +1130,8 @@ if (!p.ajax && !p.initialized) { p.initializing = false; p.initialized = true; + // update page size on init + setPageSize(table, p.size, p); moveToPage(table, p); if (c.debug) { console.log('Pager: Triggering pagerInitialized'); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 46b9e85..e1b2fc3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 01-28-2017 (v2.28.5)*/ +/*! tablesorter (FORK) - updated 04-02-2017 (v2.28.6)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.28.5 *//* +/*! TableSorter (FORK) v2.28.6 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.5', + version : '2.28.6', parsers : [], widgets : [], @@ -380,7 +380,17 @@ .bind( 'sortReset' + namespace, function( e, callback ) { e.stopPropagation(); // using this.config to ensure functions are getting a non-cached version of the config - ts.sortReset( this.config, callback ); + ts.sortReset( this.config, function( table ) { + if (table.isApplyingWidgets) { + // multiple triggers in a row... filterReset, then sortReset - see #1361 + // wait to update widgets + setTimeout( function() { + ts.applyWidget( table, '', callback ); + }, 100 ); + } else { + ts.applyWidget( table, '', callback ); + } + }); }) .bind( 'updateAll' + namespace, function( e, resort, callback ) { e.stopPropagation(); @@ -449,7 +459,7 @@ var tmp = $.extend( true, {}, c.originalSettings ); // restore original settings; this clears out current settings, but does not clear // values saved to storage. - c = $.extend( true, ts.defaults, tmp ); + c = $.extend( true, {}, ts.defaults, tmp ); c.originalSettings = tmp; this.hasInitialized = false; // setup the entire table again @@ -684,8 +694,9 @@ for ( indx = 0; indx < max; indx++ ) { header = c.$headerIndexed[ colIndex ]; if ( header && header.length ) { - // get column indexed table cell - configHeaders = ts.getColumnData( table, c.headers, colIndex ); + // get column indexed table cell; adding true parameter fixes #1362 but + // it would break backwards compatibility... + configHeaders = ts.getColumnData( table, c.headers, colIndex ); // , true ); // get column parser/extractor extractor = ts.getParserById( ts.getData( header, configHeaders, 'extractor' ) ); parser = ts.getParserById( ts.getData( header, configHeaders, 'sorter' ) ); @@ -1786,6 +1797,10 @@ ts.setHeadersCss( c ); ts.multisort( c ); ts.appendCache( c ); + var indx; + for (indx = 0; indx < c.columns; indx++) { + c.sortVars[ indx ].count = -1; + } if ( $.isFunction( callback ) ) { callback( c.table ); } @@ -1928,14 +1943,15 @@ }, applyWidgetOptions : function( table ) { - var indx, widget, + var indx, widget, wo, c = table.config, len = c.widgets.length; if ( len ) { for ( indx = 0; indx < len; indx++ ) { widget = ts.getWidgetById( c.widgets[ indx ] ); if ( widget && widget.options ) { - c.widgetOptions = $.extend( true, {}, widget.options, c.widgetOptions ); + wo = $.extend( {}, widget.options ); + c.widgetOptions = $.extend( true, wo, c.widgetOptions ); // add widgetOptions to defaults for option validator $.extend( true, ts.defaults.widgetOptions, widget.options ); } @@ -2058,22 +2074,22 @@ } } if ( c.debug && console.groupEnd ) { console.groupEnd(); } - // callback executed on init only - if ( !init && typeof callback === 'function' ) { - callback( table ); - } } c.timerReady = setTimeout( function() { table.isApplyingWidgets = false; $.data( table, 'lastWidgetApplication', new Date() ); c.$table.triggerHandler( 'tablesorter-ready' ); + // callback executed on init only + if ( !init && typeof callback === 'function' ) { + callback( table ); + } + if ( c.debug ) { + widget = c.widgets.length; + console.log( 'Completed ' + + ( init === true ? 'initializing ' : 'applying ' ) + widget + + ' widget' + ( widget !== 1 ? 's' : '' ) + ts.benchmark( time ) ); + } }, 10 ); - if ( c.debug ) { - widget = c.widgets.length; - console.log( 'Completed ' + - ( init === true ? 'initializing ' : 'applying ' ) + widget + - ' widget' + ( widget !== 1 ? 's' : '' ) + ts.benchmark( time ) ); - } }, removeWidget : function( table, name, refreshing ) { @@ -3188,7 +3204,7 @@ })(jQuery); -/*! Widget: filter - updated 12/8/2016 (v2.28.1) *//* +/*! Widget: filter - updated 4/2/2017 (v2.28.6) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -4073,7 +4089,7 @@ wo = c.widgetOptions, filterArray = $.isArray( filter ), filters = ( filterArray ) ? filter : ts.getFilters( table, true ), - combinedFilters = ( filters || [] ).join( '' ); // combined filter values + currentFilters = filters || []; // current filter values // prevent errors if delay init is set if ( $.isEmptyObject( c.cache ) ) { // update cache if delayInit set & pager has initialized ( after user initiates a search ) @@ -4087,7 +4103,10 @@ // add filter array back into inputs if ( filterArray ) { ts.setFilters( table, filters, false, skipFirst !== true ); - if ( !wo.filter_initialized ) { c.lastCombinedFilter = ''; } + if ( !wo.filter_initialized ) { + c.lastSearch = []; + c.lastCombinedFilter = ''; + } } if ( wo.filter_hideFilters ) { // show/hide filter row as needed @@ -4097,11 +4116,11 @@ } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons - if ( c.lastCombinedFilter === combinedFilters && filter !== false ) { + if ( c.lastSearch.join(',') === currentFilters.join(',') && filter !== false ) { return; } else if ( filter === false ) { // force filter refresh - c.lastCombinedFilter = null; + c.lastCombinedFilter = ''; c.lastSearch = []; } // define filter inside it is false @@ -4118,11 +4137,11 @@ if ( c.showProcessing ) { // give it time for the processing icon to kick in setTimeout( function() { - tsf.findRows( table, filters, combinedFilters ); + tsf.findRows( table, filters, currentFilters ); return false; }, 30 ); } else { - tsf.findRows( table, filters, combinedFilters ); + tsf.findRows( table, filters, currentFilters ); return false; } }, @@ -4444,9 +4463,11 @@ } return showRow; }, - findRows: function( table, filters, combinedFilters ) { - if ( table.config.lastCombinedFilter === combinedFilters || - !table.config.widgetOptions.filter_initialized ) { + findRows: function( table, filters, currentFilters ) { + if ( + table.config.lastSearch.join(',') === ( currentFilters || [] ).join(',') || + !table.config.widgetOptions.filter_initialized + ) { return; } var len, norm_rows, rowData, $rows, $row, rowIndex, tbodyIndex, $tbody, columnIndex, @@ -4500,8 +4521,7 @@ // filtered rows count c.filteredRows = 0; c.totalRows = 0; - // combindedFilters are undefined on init - combinedFilters = ( storedFilters || [] ).join( '' ); + currentFilters = ( storedFilters || [] ); for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { $tbody = ts.processTbody( table, c.$tbodies.eq( tbodyIndex ), true ); @@ -4514,7 +4534,7 @@ return el[ columnIndex ].$row.get(); }) ); - if ( combinedFilters === '' || wo.filter_serversideFiltering ) { + if ( currentFilters.join('') === '' || wo.filter_serversideFiltering ) { $rows .removeClass( wo.filter_filteredRow ) .not( '.' + c.cssChildRow ) @@ -4689,7 +4709,8 @@ c.totalRows += $rows.length; ts.processTbody( table, $tbody, false ); } - c.lastCombinedFilter = combinedFilters; // save last search + // lastCombinedFilter is no longer used internally + c.lastCombinedFilter = storedFilters.join(''); // save last search // don't save 'filters' directly since it may have altered ( AnyMatch column searches ) c.lastSearch = storedFilters; c.$table.data( 'lastSearch', storedFilters ); @@ -4989,7 +5010,7 @@ if ( ( getRaw !== true && wo && !wo.filter_columnFilters ) || // setFilters called, but last search is exactly the same as the current // fixes issue #733 & #903 where calling update causes the input values to reset - ( $.isArray(setFilters) && setFilters.join('') === c.lastCombinedFilter ) ) { + ( $.isArray(setFilters) && setFilters.join(',') === c.lastSearch.join(',') ) ) { return $( table ).data( 'lastSearch' ); } if ( c ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index a885a40..4b41e43 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.28.5 *//* +/*! TableSorter (FORK) v2.28.6 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.5', + version : '2.28.6', parsers : [], widgets : [], @@ -362,7 +362,17 @@ .bind( 'sortReset' + namespace, function( e, callback ) { e.stopPropagation(); // using this.config to ensure functions are getting a non-cached version of the config - ts.sortReset( this.config, callback ); + ts.sortReset( this.config, function( table ) { + if (table.isApplyingWidgets) { + // multiple triggers in a row... filterReset, then sortReset - see #1361 + // wait to update widgets + setTimeout( function() { + ts.applyWidget( table, '', callback ); + }, 100 ); + } else { + ts.applyWidget( table, '', callback ); + } + }); }) .bind( 'updateAll' + namespace, function( e, resort, callback ) { e.stopPropagation(); @@ -431,7 +441,7 @@ var tmp = $.extend( true, {}, c.originalSettings ); // restore original settings; this clears out current settings, but does not clear // values saved to storage. - c = $.extend( true, ts.defaults, tmp ); + c = $.extend( true, {}, ts.defaults, tmp ); c.originalSettings = tmp; this.hasInitialized = false; // setup the entire table again @@ -666,8 +676,9 @@ for ( indx = 0; indx < max; indx++ ) { header = c.$headerIndexed[ colIndex ]; if ( header && header.length ) { - // get column indexed table cell - configHeaders = ts.getColumnData( table, c.headers, colIndex ); + // get column indexed table cell; adding true parameter fixes #1362 but + // it would break backwards compatibility... + configHeaders = ts.getColumnData( table, c.headers, colIndex ); // , true ); // get column parser/extractor extractor = ts.getParserById( ts.getData( header, configHeaders, 'extractor' ) ); parser = ts.getParserById( ts.getData( header, configHeaders, 'sorter' ) ); @@ -1768,6 +1779,10 @@ ts.setHeadersCss( c ); ts.multisort( c ); ts.appendCache( c ); + var indx; + for (indx = 0; indx < c.columns; indx++) { + c.sortVars[ indx ].count = -1; + } if ( $.isFunction( callback ) ) { callback( c.table ); } @@ -1910,14 +1925,15 @@ }, applyWidgetOptions : function( table ) { - var indx, widget, + var indx, widget, wo, c = table.config, len = c.widgets.length; if ( len ) { for ( indx = 0; indx < len; indx++ ) { widget = ts.getWidgetById( c.widgets[ indx ] ); if ( widget && widget.options ) { - c.widgetOptions = $.extend( true, {}, widget.options, c.widgetOptions ); + wo = $.extend( {}, widget.options ); + c.widgetOptions = $.extend( true, wo, c.widgetOptions ); // add widgetOptions to defaults for option validator $.extend( true, ts.defaults.widgetOptions, widget.options ); } @@ -2040,22 +2056,22 @@ } } if ( c.debug && console.groupEnd ) { console.groupEnd(); } - // callback executed on init only - if ( !init && typeof callback === 'function' ) { - callback( table ); - } } c.timerReady = setTimeout( function() { table.isApplyingWidgets = false; $.data( table, 'lastWidgetApplication', new Date() ); c.$table.triggerHandler( 'tablesorter-ready' ); + // callback executed on init only + if ( !init && typeof callback === 'function' ) { + callback( table ); + } + if ( c.debug ) { + widget = c.widgets.length; + console.log( 'Completed ' + + ( init === true ? 'initializing ' : 'applying ' ) + widget + + ' widget' + ( widget !== 1 ? 's' : '' ) + ts.benchmark( time ) ); + } }, 10 ); - if ( c.debug ) { - widget = c.widgets.length; - console.log( 'Completed ' + - ( init === true ? 'initializing ' : 'applying ' ) + widget + - ' widget' + ( widget !== 1 ? 's' : '' ) + ts.benchmark( time ) ); - } }, removeWidget : function( table, name, refreshing ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 78f65b2..c0df5d6 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 01-28-2017 (v2.28.5)*/ +/*! tablesorter (FORK) - updated 04-02-2017 (v2.28.6)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -393,7 +393,7 @@ })(jQuery); -/*! Widget: filter - updated 12/8/2016 (v2.28.1) *//* +/*! Widget: filter - updated 4/2/2017 (v2.28.6) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -1278,7 +1278,7 @@ wo = c.widgetOptions, filterArray = $.isArray( filter ), filters = ( filterArray ) ? filter : ts.getFilters( table, true ), - combinedFilters = ( filters || [] ).join( '' ); // combined filter values + currentFilters = filters || []; // current filter values // prevent errors if delay init is set if ( $.isEmptyObject( c.cache ) ) { // update cache if delayInit set & pager has initialized ( after user initiates a search ) @@ -1292,7 +1292,10 @@ // add filter array back into inputs if ( filterArray ) { ts.setFilters( table, filters, false, skipFirst !== true ); - if ( !wo.filter_initialized ) { c.lastCombinedFilter = ''; } + if ( !wo.filter_initialized ) { + c.lastSearch = []; + c.lastCombinedFilter = ''; + } } if ( wo.filter_hideFilters ) { // show/hide filter row as needed @@ -1302,11 +1305,11 @@ } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons - if ( c.lastCombinedFilter === combinedFilters && filter !== false ) { + if ( c.lastSearch.join(',') === currentFilters.join(',') && filter !== false ) { return; } else if ( filter === false ) { // force filter refresh - c.lastCombinedFilter = null; + c.lastCombinedFilter = ''; c.lastSearch = []; } // define filter inside it is false @@ -1323,11 +1326,11 @@ if ( c.showProcessing ) { // give it time for the processing icon to kick in setTimeout( function() { - tsf.findRows( table, filters, combinedFilters ); + tsf.findRows( table, filters, currentFilters ); return false; }, 30 ); } else { - tsf.findRows( table, filters, combinedFilters ); + tsf.findRows( table, filters, currentFilters ); return false; } }, @@ -1649,9 +1652,11 @@ } return showRow; }, - findRows: function( table, filters, combinedFilters ) { - if ( table.config.lastCombinedFilter === combinedFilters || - !table.config.widgetOptions.filter_initialized ) { + findRows: function( table, filters, currentFilters ) { + if ( + table.config.lastSearch.join(',') === ( currentFilters || [] ).join(',') || + !table.config.widgetOptions.filter_initialized + ) { return; } var len, norm_rows, rowData, $rows, $row, rowIndex, tbodyIndex, $tbody, columnIndex, @@ -1705,8 +1710,7 @@ // filtered rows count c.filteredRows = 0; c.totalRows = 0; - // combindedFilters are undefined on init - combinedFilters = ( storedFilters || [] ).join( '' ); + currentFilters = ( storedFilters || [] ); for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { $tbody = ts.processTbody( table, c.$tbodies.eq( tbodyIndex ), true ); @@ -1719,7 +1723,7 @@ return el[ columnIndex ].$row.get(); }) ); - if ( combinedFilters === '' || wo.filter_serversideFiltering ) { + if ( currentFilters.join('') === '' || wo.filter_serversideFiltering ) { $rows .removeClass( wo.filter_filteredRow ) .not( '.' + c.cssChildRow ) @@ -1894,7 +1898,8 @@ c.totalRows += $rows.length; ts.processTbody( table, $tbody, false ); } - c.lastCombinedFilter = combinedFilters; // save last search + // lastCombinedFilter is no longer used internally + c.lastCombinedFilter = storedFilters.join(''); // save last search // don't save 'filters' directly since it may have altered ( AnyMatch column searches ) c.lastSearch = storedFilters; c.$table.data( 'lastSearch', storedFilters ); @@ -2194,7 +2199,7 @@ if ( ( getRaw !== true && wo && !wo.filter_columnFilters ) || // setFilters called, but last search is exactly the same as the current // fixes issue #733 & #903 where calling update causes the input values to reset - ( $.isArray(setFilters) && setFilters.join('') === c.lastCombinedFilter ) ) { + ( $.isArray(setFilters) && setFilters.join(',') === c.lastSearch.join(',') ) ) { return $( table ).data( 'lastSearch' ); } if ( c ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-leading-zeros.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-leading-zeros.js new file mode 100644 index 0000000..d3ad897 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-leading-zeros.js @@ -0,0 +1,33 @@ +/*! Parser: leading zeros - updated 4/2/2017 (v2.28.6) */ +/* jshint jquery:true, unused:false */ +;( function( $ ) { + 'use strict'; + + var ts = $.tablesorter, + // modify this value to increase precision as needed + precision = 1e-10; + + ts.addParser({ + id: 'leadingZeros', + is: function() { + return false; + }, + format: function( s, table ) { + var val = ( s || '' ).replace( ts.regex.nondigit, '' ), + number = ts.formatFloat( val, table ), + str = number.toString(); + if ( + !isNaN( number ) && + number == val && // jshint ignore:line + val.length !== str.length + ) { + // subtract a decimal equivalent of the string length + // so "0001" sorts before "01" + number -= precision * ( s.length - str.length ); + } + return number; + }, + type: 'number' + }); + +})( jQuery ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js index 9acd0b1..057fef1 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js @@ -1,4 +1,4 @@ -/*! Widget: Build Table - updated 11/26/2016 (v2.28.0) *//* +/*! Widget: Build Table - updated 4/2/2017 (v2.28.6) *//* * for tableSorter v2.16.0+ * by Rob Garrison */ @@ -12,6 +12,8 @@ // data.header contains an array of header titles // data.rows contains an array of rows which contains an array of cells bt = ts.buildTable = function(tar, c){ + // add build options to defaults to prevent warnings + $.extend(true, ts.defaults.widgetOptions, bt.defaults); // add table if one doesn't exist var $tbl = tar.nodeName === 'TABLE' ? $(tar) : $('<table>').appendTo(tar), table = $tbl[0], diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 4e571e8..cdb8685 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 12/8/2016 (v2.28.1) *//* +/*! Widget: filter - updated 4/2/2017 (v2.28.6) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -883,7 +883,7 @@ wo = c.widgetOptions, filterArray = $.isArray( filter ), filters = ( filterArray ) ? filter : ts.getFilters( table, true ), - combinedFilters = ( filters || [] ).join( '' ); // combined filter values + currentFilters = filters || []; // current filter values // prevent errors if delay init is set if ( $.isEmptyObject( c.cache ) ) { // update cache if delayInit set & pager has initialized ( after user initiates a search ) @@ -897,7 +897,10 @@ // add filter array back into inputs if ( filterArray ) { ts.setFilters( table, filters, false, skipFirst !== true ); - if ( !wo.filter_initialized ) { c.lastCombinedFilter = ''; } + if ( !wo.filter_initialized ) { + c.lastSearch = []; + c.lastCombinedFilter = ''; + } } if ( wo.filter_hideFilters ) { // show/hide filter row as needed @@ -907,11 +910,11 @@ } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons - if ( c.lastCombinedFilter === combinedFilters && filter !== false ) { + if ( c.lastSearch.join(',') === currentFilters.join(',') && filter !== false ) { return; } else if ( filter === false ) { // force filter refresh - c.lastCombinedFilter = null; + c.lastCombinedFilter = ''; c.lastSearch = []; } // define filter inside it is false @@ -928,11 +931,11 @@ if ( c.showProcessing ) { // give it time for the processing icon to kick in setTimeout( function() { - tsf.findRows( table, filters, combinedFilters ); + tsf.findRows( table, filters, currentFilters ); return false; }, 30 ); } else { - tsf.findRows( table, filters, combinedFilters ); + tsf.findRows( table, filters, currentFilters ); return false; } }, @@ -1254,9 +1257,11 @@ } return showRow; }, - findRows: function( table, filters, combinedFilters ) { - if ( table.config.lastCombinedFilter === combinedFilters || - !table.config.widgetOptions.filter_initialized ) { + findRows: function( table, filters, currentFilters ) { + if ( + table.config.lastSearch.join(',') === ( currentFilters || [] ).join(',') || + !table.config.widgetOptions.filter_initialized + ) { return; } var len, norm_rows, rowData, $rows, $row, rowIndex, tbodyIndex, $tbody, columnIndex, @@ -1310,8 +1315,7 @@ // filtered rows count c.filteredRows = 0; c.totalRows = 0; - // combindedFilters are undefined on init - combinedFilters = ( storedFilters || [] ).join( '' ); + currentFilters = ( storedFilters || [] ); for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { $tbody = ts.processTbody( table, c.$tbodies.eq( tbodyIndex ), true ); @@ -1324,7 +1328,7 @@ return el[ columnIndex ].$row.get(); }) ); - if ( combinedFilters === '' || wo.filter_serversideFiltering ) { + if ( currentFilters.join('') === '' || wo.filter_serversideFiltering ) { $rows .removeClass( wo.filter_filteredRow ) .not( '.' + c.cssChildRow ) @@ -1499,7 +1503,8 @@ c.totalRows += $rows.length; ts.processTbody( table, $tbody, false ); } - c.lastCombinedFilter = combinedFilters; // save last search + // lastCombinedFilter is no longer used internally + c.lastCombinedFilter = storedFilters.join(''); // save last search // don't save 'filters' directly since it may have altered ( AnyMatch column searches ) c.lastSearch = storedFilters; c.$table.data( 'lastSearch', storedFilters ); @@ -1799,7 +1804,7 @@ if ( ( getRaw !== true && wo && !wo.filter_columnFilters ) || // setFilters called, but last search is exactly the same as the current // fixes issue #733 & #903 where calling update causes the input values to reset - ( $.isArray(setFilters) && setFilters.join('') === c.lastCombinedFilter ) ) { + ( $.isArray(setFilters) && setFilters.join(',') === c.lastSearch.join(',') ) ) { return $( table ).data( 'lastSearch' ); } if ( c ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index 7d6fa62..722bd4f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -1,4 +1,4 @@ -/*! Widget: output - updated 1/28/2017 (v2.28.5) *//* +/*! Widget: output - updated 4/2/2017 (v2.28.6) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * Modified from: * HTML Table to CSV: http://www.kunalbabre.com/projects/table2CSV.php (License unknown?) @@ -85,7 +85,7 @@ colspanLen = parseInt( $cell.attr('colspan'), 10) - 1; // allow data-attribute to be an empty string txt = output.formatData( c, wo, $cell, isHeader ); - for (col = 1; col <= colspanLen; col++) { + for (col = 0; col < colspanLen; col++) { // if we're processing the header & making JSON, the header names need to be unique if ($cell.filter('[rowspan]').length) { rowspanLen = parseInt( $cell.attr('rowspan'), 10); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 95a6850..b57978f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 1/6/2017 (v2.28.4) */ +/*! Widget: Pager - updated 4/2/2017 (v2.28.6) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -180,8 +180,7 @@ t = ts.storage( table, wo.pager_storageKey ) || {}; // fixes #387 p.page = ( isNaN( t.page ) ? p.page : t.page ) || p.setPage || 0; p.size = t.size === 'all' ? t.size : ( isNaN( t.size ) ? p.size : t.size ) || p.setSize || 10; - $.data( table, 'pagerLastSize', p.size ); - p.$container.find( s.pageSize ).val( p.size ); + tsp.setPageSize( c, p.size ); } // skipped rows @@ -219,7 +218,6 @@ initComplete: function( c ) { var p = c.pager; tsp.bindEvents( c ); - tsp.setPageSize( c, 0 ); // page size 0 is ignored if ( !p.ajax ) { tsp.hideRowsSetup( c ); } @@ -228,6 +226,7 @@ p.initialized = true; p.initializing = false; p.isInitializing = false; + tsp.setPageSize( c, p.size ); // page size 0 is ignored if ( c.debug ) { console.log( 'Pager: Triggering pagerInitialized' ); } @@ -252,7 +251,8 @@ p.currentFilters = $.isArray( filters ) ? filters : c.$table.data( 'lastSearch' ); // don't change page if filters are the same (pager updating, etc) if ( e.type === 'filterStart' && wo.pager_pageReset !== false && - ( c.lastCombinedFilter || '' ) !== ( p.currentFilters || [] ).join( '' ) ) { + ( c.lastSearch || [] ).join( ',' ) !== ( p.currentFilters || [] ).join( ',' ) + ) { p.page = wo.pager_pageReset; // fixes #456 & #565 } }) @@ -309,6 +309,7 @@ .on( 'pageSize refreshComplete '.split( ' ' ).join( namespace + ' ' ), function( e, size ) { e.stopPropagation(); tsp.setPageSize( c, tsp.parsePageSize( c, size, 'get' ) ); + tsp.moveToPage( c, p, true ); tsp.hideRows( c ); tsp.updatePageDisplay( c, false ); }) @@ -381,6 +382,7 @@ // in case there are more than one pager p.$container.find( wo.pager_selectors.pageSize ).val( size ); tsp.setPageSize( c, size ); + tsp.moveToPage( c, p, true ); tsp.changeHeight( c ); } return false; @@ -693,8 +695,7 @@ $el = p.$container.find( c.widgetOptions.pager_selectors.pageSize ), size = $el.val(); p.size = tsp.parsePageSize( c, size, 'get' ); - $el.val( p.size ); - $.data( c.table, 'pagerLastSize', p.size ); + tsp.setPageSize( c, p.size ); tsp.pagerArrows( c ); if ( !c.widgetOptions.pager_removeRows ) { tsp.hideRows( c ); @@ -1002,7 +1003,7 @@ $.data( table, 'pagerLastPage', p.page ); $.data( table, 'pagerLastSize', p.size ); p.page = 0; - p.size = 'all'; + p.size = p.totalPages; p.totalPages = 1; c.$table .addClass( 'pagerDisabled' ) @@ -1127,16 +1128,9 @@ }, getTotalPages: function( c, p ) { - return ts.hasWidget( c.table, 'filter' ) ? Math.min( p.totalPages, p.filteredPages ) : p.totalPages; - }, - - // set to either set or get value - parsePageSize: function( c, size, mode ) { - var p = c.pager, - s = parseInt( size, 10 ) || p.size || c.widgetOptions.pager_size || 10; - return p.initialized && (/all/i.test( size ) || s === p.totalRows) ? - // "get" to set `p.size` or "set" to set `pageSize.val()` - 'all' : ( mode === 'get' ? s : p.size ); + return ts.hasWidget( c.table, 'filter' ) ? + Math.min( p.totalPages, p.filteredPages ) : + p.totalPages; }, parsePageNumber: function( c, p ) { @@ -1147,18 +1141,32 @@ return p.page; }, + // set to either set or get value + parsePageSize: function( c, size, mode ) { + var p = c.pager, + wo = c.widgetOptions, + s = parseInt( size, 10 ) || p.size || wo.pager_size || 10; + if (p.initialized && (/all/i.test( s + ' ' + size ) || s === p.totalRows)) { + // Fixing #1364 & #1366 + return p.$container.find( wo.pager_selectors.pageSize + ' option[value="all"]').length ? + 'all' : p.totalRows; + } + // "get" to set `p.size` or "set" to set `pageSize.val()` + return mode === 'get' ? s : p.size; + }, + setPageSize: function( c, size ) { var p = c.pager, table = c.table; + // "all" size is only returned if an "all" option exists - fixes #1366 p.size = tsp.parsePageSize( c, size, 'get' ); p.$container .find( c.widgetOptions.pager_selectors.pageSize ) - .val( tsp.parsePageSize( c, p.size, 'set' ) ); + .val( p.size ); $.data( table, 'pagerLastPage', tsp.parsePageNumber( c, p ) ); $.data( table, 'pagerLastSize', p.size ); p.totalPages = p.size === 'all' ? 1 : Math.ceil( p.totalRows / p.size ); p.filteredPages = p.size === 'all' ? 1 : Math.ceil( p.filteredRows / p.size ); - tsp.moveToPage( c, p, true ); }, moveToFirstPage: function( c, p ) { @@ -1227,7 +1235,7 @@ p.page = $.data( table, 'pagerLastPage' ) || p.page || 0; size = $el.find('option[selected]' ).val(); p.size = $.data( table, 'pagerLastSize' ) || tsp.parsePageSize( c, size, 'get' ); - $el.val( p.size ); // set page size + tsp.setPageSize( c, p.size ); // set page size p.totalPages = p.size === 'all' ? 1 : Math.ceil( tsp.getTotalPages( c, p ) / p.size ); c.$table.removeClass( 'pagerDisabled' ); // if table id exists, include page display with aria info @@ -1246,6 +1254,7 @@ // tablesorter core update table ts.update( c ); tsp.setPageSize( c, p.size ); + tsp.moveToPage( c, p, true ); tsp.hideRowsSetup( c ); if ( c.debug ) { console.log( 'Pager: Enabled' ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js index 924e891..7429bf8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js @@ -1,4 +1,4 @@ -/*! Widget: sort2Hash (BETA) - updated 11/10/2015 (v2.24.4) */ +/*! Widget: sort2Hash (BETA) - updated 4/2/2017 (v2.28.6) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -227,15 +227,14 @@ sort2Hash_separator : '-', // don't '#' or '=' here sort2Hash_headerTextAttr : 'data-header', // data attribute containing alternate header text sort2Hash_directionText : [ 0, 1 ], // [ 'asc', 'desc' ], - sort2Hash_overrideSaveSort : false // if true, override saveSort widget if saved sort available + sort2Hash_overrideSaveSort : false, // if true, override saveSort widget if saved sort available - // Options below commented out for improved compression - // ****************** - // sort2Hash_tableId : null, // this option > table ID > table index on page, + // this option > table ID > table index on page + sort2Hash_tableId : null, // custom hash processing functions - // sort2Hash_encodeHash : null, - // sort2Hash_decodeHash : null, - // sort2Hash_cleanHash : null + sort2Hash_encodeHash : null, + sort2Hash_decodeHash : null, + sort2Hash_cleanHash : null }, init: function(table, thisWidget, c, wo) { s2h.init( c, wo ); diff --git a/vendor/assets/stylesheets/jquery-tablesorter/highlights.css b/vendor/assets/stylesheets/jquery-tablesorter/highlights.css new file mode 100644 index 0000000..e0b1bd4 --- /dev/null +++ b/vendor/assets/stylesheets/jquery-tablesorter/highlights.css @@ -0,0 +1,104 @@ +/* CSS Column & Row Hightlights - matches the blue theme + * See https://mottie.github.io/tablesorter/docs/example-css-highlighting.html + */ +table.hover-highlight td:before, +table.focus-highlight td:before { + background: #fff; +} + +/* ODD ZEBRA STRIPE color (needs zebra widget) */ +.hover-highlight .odd td:before, .hover-highlight .odd th:before, +.focus-highlight .odd td:before, .focus-highlight .odd th:before { + background: #ebf2fa; +} +/* EVEN ZEBRA STRIPE color (needs zebra widget) */ +.hover-highlight .even td:before, .hover-highlight .even th:before, +.focus-highlight .even td:before, .focus-highlight .even th:before { + background-color: #fff; +} + +/* FOCUS ROW highlight color (touch devices) */ +.focus-highlight td:focus::before, .focus-highlight th:focus::before { + background-color: lightblue; +} +/* FOCUS COLUMN highlight color (touch devices) */ +.focus-highlight td:focus::after, .focus-highlight th:focus::after { + background-color: lightblue; +} +/* FOCUS CELL highlight color */ +.focus-highlight th:focus, .focus-highlight td:focus, +.focus-highlight .even th:focus, .focus-highlight .even td:focus, +.focus-highlight .odd th:focus, .focus-highlight .odd td:focus { + background-color: #d9d9d9; + color: #333; +} + +/* HOVER ROW highlight colors */ +table.hover-highlight tbody > tr:hover > td, /* override tablesorter theme row hover */ +table.hover-highlight tbody > tr.odd:hover > td, +table.hover-highlight tbody > tr.even:hover > td { + background-color: #ffa; +} +/* HOVER COLUMN highlight colors */ +.hover-highlight tbody tr td:hover::after, +.hover-highlight tbody tr th:hover::after { + background-color: #ffa; +} + +/* ************************************************* */ +/* **** No need to modify the definitions below **** */ +/* ************************************************* */ +.focus-highlight td:focus::after, .focus-highlight th:focus::after, +.hover-highlight td:hover::after, .hover-highlight th:hover::after { + content: ''; + position: absolute; + width: 100%; + height: 999em; + left: 0; + top: -555em; + z-index: -1; +} +.focus-highlight td:focus::before, .focus-highlight th:focus::before { + content: ''; + position: absolute; + width: 999em; + height: 100%; + left: -555em; + top: 0; + z-index: -2; +} +/* required styles */ +.hover-highlight, +.focus-highlight { + overflow: hidden; +} +.hover-highlight td, .hover-highlight th, +.focus-highlight td, .focus-highlight th { + position: relative; + outline: 0; +} +/* override the tablesorter theme styling */ +table.hover-highlight, table.hover-highlight tbody > tr > td, +table.focus-highlight, table.focus-highlight tbody > tr > td, +/* override zebra styling */ +table.hover-highlight tbody tr.even > th, +table.hover-highlight tbody tr.even > td, +table.hover-highlight tbody tr.odd > th, +table.hover-highlight tbody tr.odd > td, +table.focus-highlight tbody tr.even > th, +table.focus-highlight tbody tr.even > td, +table.focus-highlight tbody tr.odd > th, +table.focus-highlight tbody tr.odd > td { + background: transparent; +} +/* table background positioned under the highlight */ +table.hover-highlight td:before, +table.focus-highlight td:before { + content: ''; + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + z-index: -3; +} \ No newline at end of file From 53f077d682ee02a7e4a4b6995ac3d415ba380a5f Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Sun, 9 Apr 2017 19:46:31 +0200 Subject: [PATCH 109/138] Update tablesorter to latest version (2.28.7) --- CHANGELOG.md | 4 +++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 25 ++++++++++++------- .../jquery-tablesorter/jquery.tablesorter.js | 4 +-- .../jquery.tablesorter.widgets.js | 21 ++++++++++------ .../widgets/widget-editable.js | 8 +++--- .../widgets/widget-filter.js | 19 +++++++++----- 9 files changed, 56 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff9f8c4..32f4f01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.23.7 (2017-04-09) + +* Upgrade tablesorter to v2.28.7 + #### v1.23.6 (2017-04-02) * Upgrade tablesorter to v2.28.6 diff --git a/README.md b/README.md index 9160d39..bd8a344 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.28.6 (4/2/2017), [documentation] +Current tablesorter version: 2.28.7 (4/4/2017), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 3f90a16..cfed8d4 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 23 - TINY = 6 + TINY = 7 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index f006d8a..b80d402 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit f006d8aa5f6141086de08e30317471d31f9e7434 +Subproject commit b80d402fc39b5ba884725ad0a6f44acb70555083 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index e1b2fc3..eea51e6 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 04-02-2017 (v2.28.6)*/ +/*! tablesorter (FORK) - updated 04-04-2017 (v2.28.7)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.28.6 *//* +/*! TableSorter (FORK) v2.28.7 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.6', + version : '2.28.7', parsers : [], widgets : [], @@ -3204,7 +3204,7 @@ })(jQuery); -/*! Widget: filter - updated 4/2/2017 (v2.28.6) *//* +/*! Widget: filter - updated 4/4/2017 (v2.28.7) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3275,7 +3275,7 @@ var tbodyIndex, $tbody, $table = c.$table, $tbodies = c.$tbodies, - events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' + events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterAndSortReset filterEnd search ' .split( ' ' ).join( c.namespace + 'filter ' ); $table .removeClass( 'hasFilters' ) @@ -3616,7 +3616,7 @@ } txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset ' + - 'filterResetSaved filterEnd search '.split( ' ' ).join( c.namespace + 'filter ' ); + 'filterAndSortReset filterResetSaved filterEnd search '.split( ' ' ).join( c.namespace + 'filter ' ); c.$table.bind( txt, function( event, filter ) { val = wo.filter_hideEmpty && $.isEmptyObject( c.cache ) && @@ -3627,9 +3627,16 @@ event.stopPropagation(); tsf.buildDefault( table, true ); } - if ( event.type === 'filterReset' ) { + // Add filterAndSortReset - see #1361 + if ( event.type === 'filterReset' || event.type === 'filterAndSortReset' ) { c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' ); - tsf.searching( table, [] ); + if ( event.type === 'filterAndSortReset' ) { + ts.sortReset( this.config, function() { + tsf.searching( table, [] ); + }); + } else { + tsf.searching( table, [] ); + } } else if ( event.type === 'filterResetSaved' ) { ts.storage( table, 'tablesorter-filters', '' ); } else if ( event.type === 'filterEnd' ) { @@ -5011,7 +5018,7 @@ // setFilters called, but last search is exactly the same as the current // fixes issue #733 & #903 where calling update causes the input values to reset ( $.isArray(setFilters) && setFilters.join(',') === c.lastSearch.join(',') ) ) { - return $( table ).data( 'lastSearch' ); + return $( table ).data( 'lastSearch' ) || []; } if ( c ) { if ( c.$filters ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 4b41e43..f153532 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.28.6 *//* +/*! TableSorter (FORK) v2.28.7 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.6', + version : '2.28.7', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index c0df5d6..171eac8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 04-02-2017 (v2.28.6)*/ +/*! tablesorter (FORK) - updated 04-04-2017 (v2.28.7)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -393,7 +393,7 @@ })(jQuery); -/*! Widget: filter - updated 4/2/2017 (v2.28.6) *//* +/*! Widget: filter - updated 4/4/2017 (v2.28.7) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -464,7 +464,7 @@ var tbodyIndex, $tbody, $table = c.$table, $tbodies = c.$tbodies, - events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' + events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterAndSortReset filterEnd search ' .split( ' ' ).join( c.namespace + 'filter ' ); $table .removeClass( 'hasFilters' ) @@ -805,7 +805,7 @@ } txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset ' + - 'filterResetSaved filterEnd search '.split( ' ' ).join( c.namespace + 'filter ' ); + 'filterAndSortReset filterResetSaved filterEnd search '.split( ' ' ).join( c.namespace + 'filter ' ); c.$table.bind( txt, function( event, filter ) { val = wo.filter_hideEmpty && $.isEmptyObject( c.cache ) && @@ -816,9 +816,16 @@ event.stopPropagation(); tsf.buildDefault( table, true ); } - if ( event.type === 'filterReset' ) { + // Add filterAndSortReset - see #1361 + if ( event.type === 'filterReset' || event.type === 'filterAndSortReset' ) { c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' ); - tsf.searching( table, [] ); + if ( event.type === 'filterAndSortReset' ) { + ts.sortReset( this.config, function() { + tsf.searching( table, [] ); + }); + } else { + tsf.searching( table, [] ); + } } else if ( event.type === 'filterResetSaved' ) { ts.storage( table, 'tablesorter-filters', '' ); } else if ( event.type === 'filterEnd' ) { @@ -2200,7 +2207,7 @@ // setFilters called, but last search is exactly the same as the current // fixes issue #733 & #903 where calling update causes the input values to reset ( $.isArray(setFilters) && setFilters.join(',') === c.lastSearch.join(',') ) ) { - return $( table ).data( 'lastSearch' ); + return $( table ).data( 'lastSearch' ) || []; } if ( c ) { if ( c.$filters ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js index 7ea710a..524f3cf 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js @@ -1,4 +1,4 @@ -/*! Widget: editable - updated 3/1/2016 (v2.25.5) *//* +/*! Widget: editable - updated 4/4/2017 (v2.28.7) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -150,7 +150,7 @@ }); c.$tbodies - .off( ( 'focus blur focusout keydown '.split( ' ' ).join( namespace + ' ' ) ).replace( /\s+/g, ' ' ) ) + .off( ( 'focus focusout keydown '.split( ' ' ).join( namespace + ' ' ) ).replace( /\s+/g, ' ' ) ) .on( 'focus' + namespace, '[contenteditable]', function( e ) { clearTimeout( $( this ).data( 'timer' ) ); c.$table.data( 'contentFocused', e.target ); @@ -186,7 +186,7 @@ } } }) - .on( 'blur focusout keydown '.split( ' ' ).join( namespace + ' ' ), '[contenteditable]', function( e ) { + .on( 'focusout keydown '.split( ' ' ).join( namespace + ' ' ), '[contenteditable]', function( e ) { if ( !c.$table.data( 'contentFocused' ) ) { return; } var t, validate, valid = false, @@ -277,7 +277,7 @@ tmp = ( 'updateComplete pagerComplete '.split( ' ' ).join( namespace + ' ' ) ).replace( /\s+/g, ' ' ); c.$table.off( tmp ); - tmp = ( 'focus blur focusout keydown paste '.split( ' ' ).join( namespace + ' ' ) ).replace( /\s+/g, ' ' ); + tmp = ( 'focus focusout keydown paste '.split( ' ' ).join( namespace + ' ' ) ).replace( /\s+/g, ' ' ); c.$tbodies .off( tmp ) .find( cols.join( ',' ) ) diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index cdb8685..a4d201a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 4/2/2017 (v2.28.6) *//* +/*! Widget: filter - updated 4/4/2017 (v2.28.7) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -69,7 +69,7 @@ var tbodyIndex, $tbody, $table = c.$table, $tbodies = c.$tbodies, - events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ' + events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterAndSortReset filterEnd search ' .split( ' ' ).join( c.namespace + 'filter ' ); $table .removeClass( 'hasFilters' ) @@ -410,7 +410,7 @@ } txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset ' + - 'filterResetSaved filterEnd search '.split( ' ' ).join( c.namespace + 'filter ' ); + 'filterAndSortReset filterResetSaved filterEnd search '.split( ' ' ).join( c.namespace + 'filter ' ); c.$table.bind( txt, function( event, filter ) { val = wo.filter_hideEmpty && $.isEmptyObject( c.cache ) && @@ -421,9 +421,16 @@ event.stopPropagation(); tsf.buildDefault( table, true ); } - if ( event.type === 'filterReset' ) { + // Add filterAndSortReset - see #1361 + if ( event.type === 'filterReset' || event.type === 'filterAndSortReset' ) { c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' ); - tsf.searching( table, [] ); + if ( event.type === 'filterAndSortReset' ) { + ts.sortReset( this.config, function() { + tsf.searching( table, [] ); + }); + } else { + tsf.searching( table, [] ); + } } else if ( event.type === 'filterResetSaved' ) { ts.storage( table, 'tablesorter-filters', '' ); } else if ( event.type === 'filterEnd' ) { @@ -1805,7 +1812,7 @@ // setFilters called, but last search is exactly the same as the current // fixes issue #733 & #903 where calling update causes the input values to reset ( $.isArray(setFilters) && setFilters.join(',') === c.lastSearch.join(',') ) ) { - return $( table ).data( 'lastSearch' ); + return $( table ).data( 'lastSearch' ) || []; } if ( c ) { if ( c.$filters ) { From 736e68b7babac9dbd97e51bf6cf5d5859984daf5 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Sun, 23 Apr 2017 19:01:32 +0200 Subject: [PATCH 110/138] Update tablesorter to latest version (2.28.8) --- CHANGELOG.md | 5 + README.md | 4 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 14 ++- .../jquery.tablesorter.combined.js | 102 ++++++++++++------ .../jquery-tablesorter/jquery.tablesorter.js | 7 +- .../jquery.tablesorter.widgets.js | 95 ++++++++++------ .../widgets/widget-filter.js | 22 +++- .../widgets/widget-pager.js | 13 ++- .../widgets/widget-resizable.js | 10 +- .../widgets/widget-scroller.js | 20 ++-- .../widgets/widget-storage.js | 61 +++++++---- 13 files changed, 235 insertions(+), 122 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32f4f01..dd9ea25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ Changelog === +#### v1.23.8 (2017-04-23) + +* Upgrade tablesorter to v2.28.8 +* Readme: Updated ruby version + #### v1.23.7 (2017-04-09) * Upgrade tablesorter to v2.28.7 diff --git a/README.md b/README.md index bd8a344..2300ab5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.28.7 (4/4/2017), [documentation] +Current tablesorter version: 2.28.8 (4/18/2017), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. @@ -24,7 +24,7 @@ Or install it yourself as: ## Requirements -It should work with Rails 3.2 and higher (tested up to 5) as well as with ruby 1.9.3 - 2.3.x. +It should work with Rails 3.2 and higher (tested up to 5) as well as with ruby 1.9.3 - 2.4.x. Each release is always tested with the latest version of both. ## Usage diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index cfed8d4..d28b77e 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 23 - TINY = 7 + TINY = 8 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index b80d402..72b4597 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit b80d402fc39b5ba884725ad0a6f44acb70555083 +Subproject commit 72b45976d81f21e063f6641549e4dfc351eab3e6 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index a82facc..71dbee8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin -* updated 4/2/2017 (v2.28.6) +* updated 4/18/2017 (v2.28.8) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -968,11 +968,15 @@ .unbind( pagerEvents.split(' ').join(namespace + ' ').replace(/\s+/g, ' ') ) .bind('filterInit filterStart '.split(' ').join(namespace + ' '), function(e, filters) { p.currentFilters = $.isArray(filters) ? filters : c.$table.data('lastSearch'); + var filtersEqual; + if (ts.filter.equalFilters) { + filtersEqual = ts.filter.equalFilters(c, c.lastSearch, p.currentFilters); + } else { + // will miss filter changes of the same value in a different column, see #1363 + filtersEqual = (c.lastSearch || []).join('') !== (p.currentFilters || []).join(''); + } // don't change page if filters are the same (pager updating, etc) - if ( - e.type === 'filterStart' && - p.pageReset !== false && - (c.lastSearch || []).join(',') !== (p.currentFilters || []).join(',')) { + if (e.type === 'filterStart' && p.pageReset !== false && !filtersEqual) { p.page = p.pageReset; // fixes #456 & #565 } }) diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index eea51e6..33db612 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 04-04-2017 (v2.28.7)*/ +/*! tablesorter (FORK) - updated 04-18-2017 (v2.28.8)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.28.7 *//* +/*! TableSorter (FORK) v2.28.8 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.7', + version : '2.28.8', parsers : [], widgets : [], @@ -1325,7 +1325,7 @@ cell = $cell[ 0 ]; // in case cell is a jQuery object // tbody may not exist if update is initialized while tbody is removed for processing if ( $tbodies.length && tbodyIndex >= 0 ) { - row = $tbodies.eq( tbodyIndex ).find( 'tr' ).index( $row ); + row = $tbodies.eq( tbodyIndex ).find( 'tr' ).not( '.' + c.cssChildRow ).index( $row ); cache = tbcache.normalized[ row ]; len = $row[ 0 ].cells.length; if ( len !== c.columns ) { @@ -1346,7 +1346,6 @@ cache[ c.columns ].raw[ icell ] = tmp; tmp = ts.getParsedText( c, cell, icell, tmp ); cache[ icell ] = tmp; // parsed - cache[ c.columns ].$row = $row; if ( ( c.parsers[ icell ].type || '' ).toLowerCase() === 'numeric' ) { // update column max value (ignore sign) tbcache.colMax[ icell ] = Math.max( Math.abs( tmp ) || 0, tbcache.colMax[ icell ] || 0 ); @@ -2827,12 +2826,26 @@ })( jQuery ); -/*! Widget: storage - updated 11/26/2016 (v2.28.0) */ +/*! Widget: storage - updated 4/18/2017 (v2.28.8) */ /*global JSON:false */ ;(function ($, window, document) { 'use strict'; var ts = $.tablesorter || {}; + + // update defaults for validator; these values must be falsy! + $.extend(true, ts.defaults, { + fixedUrl: '', + widgetOptions: { + storage_fixedUrl: '', + storage_group: '', + storage_page: '', + storage_storageType: '', + storage_tableId: '', + storage_useSessionStorage: '' + } + }); + // *** Store data in local storage, with a cookie fallback *** /* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json) if you need it, then include https://github.com/douglascrockford/JSON-js @@ -2859,8 +2872,12 @@ values = {}, c = table.config, wo = c && c.widgetOptions, - storageType = ( options && options.useSessionStorage ) || ( wo && wo.storage_useSessionStorage ) ? - 'sessionStorage' : 'localStorage', + storageType = ( + ( options && options.storageType ) || ( wo && wo.storage_storageType ) + ).toString().charAt(0).toLowerCase(), + // deprecating "useSessionStorage"; any storageType setting overrides it + session = storageType ? '' : + ( options && options.useSessionStorage ) || ( wo && wo.storage_useSessionStorage ), $table = $(table), // id from (1) options ID, (2) table 'data-table-group' attribute, (3) widgetOptions.storage_tableId, // (4) table ID, then (5) table index @@ -2872,29 +2889,26 @@ url = options && options.url || $table.attr(options && options.page || wo && wo.storage_page || 'data-table-page') || wo && wo.storage_fixedUrl || c && c.fixedUrl || window.location.pathname; - // update defaults for validator; these values must be falsy! - $.extend(true, ts.defaults, { - fixedUrl: '', - widgetOptions: { - storage_fixedUrl: '', - storage_group: '', - storage_page: '', - storage_tableId: '', - storage_useSessionStorage: '' - } - }); - // https://gist.github.com/paulirish/5558557 - if (storageType in window) { - try { - window[storageType].setItem('_tmptest', 'temp'); - hasStorage = true; - window[storageType].removeItem('_tmptest'); - } catch (error) { - if (c && c.debug) { - console.warn( storageType + ' is not supported in this browser' ); + + // skip if using cookies + if (storageType !== 'c') { + storageType = (storageType === 's' || session) ? 'sessionStorage' : 'localStorage'; + // https://gist.github.com/paulirish/5558557 + if (storageType in window) { + try { + window[storageType].setItem('_tmptest', 'temp'); + hasStorage = true; + window[storageType].removeItem('_tmptest'); + } catch (error) { + if (c && c.debug) { + console.warn( storageType + ' is not supported in this browser' ); + } } } } + if (c.debug) { + console.log('Storage widget using', hasStorage ? storageType : 'cookies'); + } // *** get value *** if ($.parseJSON) { if (hasStorage) { @@ -3204,7 +3218,7 @@ })(jQuery); -/*! Widget: filter - updated 4/4/2017 (v2.28.7) *//* +/*! Widget: filter - updated 4/18/2017 (v2.28.8) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -4091,6 +4105,19 @@ tsf.checkFilters( table, filter, skipFirst ); } }, + equalFilters: function (c, filter1, filter2) { + var indx, + f1 = [], + f2 = [], + len = c.columns + 1; // add one to include anyMatch filter + filter1 = $.isArray(filter1) ? filter1 : []; + filter2 = $.isArray(filter2) ? filter2 : []; + for (indx = 0; indx < len; indx++) { + f1[indx] = filter1[indx] || ''; + f2[indx] = filter2[indx] || ''; + } + return f1.join(',') === f2.join(','); + }, checkFilters: function( table, filter, skipFirst ) { var c = table.config, wo = c.widgetOptions, @@ -4123,7 +4150,7 @@ } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons - if ( c.lastSearch.join(',') === currentFilters.join(',') && filter !== false ) { + if ( tsf.equalFilters(c, c.lastSearch, currentFilters) && filter !== false ) { return; } else if ( filter === false ) { // force filter refresh @@ -4472,7 +4499,7 @@ }, findRows: function( table, filters, currentFilters ) { if ( - table.config.lastSearch.join(',') === ( currentFilters || [] ).join(',') || + tsf.equalFilters(table.config, table.config.lastSearch, currentFilters) || !table.config.widgetOptions.filter_initialized ) { return; @@ -5017,7 +5044,8 @@ if ( ( getRaw !== true && wo && !wo.filter_columnFilters ) || // setFilters called, but last search is exactly the same as the current // fixes issue #733 & #903 where calling update causes the input values to reset - ( $.isArray(setFilters) && setFilters.join(',') === c.lastSearch.join(',') ) ) { + ( $.isArray(setFilters) && tsf.equalFilters(c, setFilters, c.lastSearch) ) + ) { return $( table ).data( 'lastSearch' ) || []; } if ( c ) { @@ -5402,7 +5430,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 1/28/2017 (v2.28.5) */ +/*! Widget: resizable - updated 4/18/2017 (v2.28.8) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -5567,6 +5595,10 @@ tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); }); } + + if ( !wo.resizable_includeFooter && c.$table.children('tfoot').length ) { + tableHeight -= c.$table.children('tfoot').height(); + } // subtract out table left position from resizable handles. Fixes #864 startPosition = c.$table.position().left; $handles.each( function() { @@ -5737,10 +5769,10 @@ options: { resizable : true, // save column widths to storage resizable_addLastColumn : false, + resizable_includeFooter: true, resizable_widths : [], resizable_throttle : false, // set to true (5ms) or any number 0-10 range - resizable_targetLast : false, - resizable_fullWidth : null + resizable_targetLast : false }, init: function(table, thisWidget, c, wo) { ts.resizable.init( c, wo ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index f153532..08fd44c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.28.7 *//* +/*! TableSorter (FORK) v2.28.8 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.7', + version : '2.28.8', parsers : [], widgets : [], @@ -1307,7 +1307,7 @@ cell = $cell[ 0 ]; // in case cell is a jQuery object // tbody may not exist if update is initialized while tbody is removed for processing if ( $tbodies.length && tbodyIndex >= 0 ) { - row = $tbodies.eq( tbodyIndex ).find( 'tr' ).index( $row ); + row = $tbodies.eq( tbodyIndex ).find( 'tr' ).not( '.' + c.cssChildRow ).index( $row ); cache = tbcache.normalized[ row ]; len = $row[ 0 ].cells.length; if ( len !== c.columns ) { @@ -1328,7 +1328,6 @@ cache[ c.columns ].raw[ icell ] = tmp; tmp = ts.getParsedText( c, cell, icell, tmp ); cache[ icell ] = tmp; // parsed - cache[ c.columns ].$row = $row; if ( ( c.parsers[ icell ].type || '' ).toLowerCase() === 'numeric' ) { // update column max value (ignore sign) tbcache.colMax[ icell ] = Math.max( Math.abs( tmp ) || 0, tbcache.colMax[ icell ] || 0 ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 171eac8..1cbdc5f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 04-04-2017 (v2.28.7)*/ +/*! tablesorter (FORK) - updated 04-18-2017 (v2.28.8)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,12 +16,26 @@ } }(function(jQuery) { -/*! Widget: storage - updated 11/26/2016 (v2.28.0) */ +/*! Widget: storage - updated 4/18/2017 (v2.28.8) */ /*global JSON:false */ ;(function ($, window, document) { 'use strict'; var ts = $.tablesorter || {}; + + // update defaults for validator; these values must be falsy! + $.extend(true, ts.defaults, { + fixedUrl: '', + widgetOptions: { + storage_fixedUrl: '', + storage_group: '', + storage_page: '', + storage_storageType: '', + storage_tableId: '', + storage_useSessionStorage: '' + } + }); + // *** Store data in local storage, with a cookie fallback *** /* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json) if you need it, then include https://github.com/douglascrockford/JSON-js @@ -48,8 +62,12 @@ values = {}, c = table.config, wo = c && c.widgetOptions, - storageType = ( options && options.useSessionStorage ) || ( wo && wo.storage_useSessionStorage ) ? - 'sessionStorage' : 'localStorage', + storageType = ( + ( options && options.storageType ) || ( wo && wo.storage_storageType ) + ).toString().charAt(0).toLowerCase(), + // deprecating "useSessionStorage"; any storageType setting overrides it + session = storageType ? '' : + ( options && options.useSessionStorage ) || ( wo && wo.storage_useSessionStorage ), $table = $(table), // id from (1) options ID, (2) table 'data-table-group' attribute, (3) widgetOptions.storage_tableId, // (4) table ID, then (5) table index @@ -61,29 +79,26 @@ url = options && options.url || $table.attr(options && options.page || wo && wo.storage_page || 'data-table-page') || wo && wo.storage_fixedUrl || c && c.fixedUrl || window.location.pathname; - // update defaults for validator; these values must be falsy! - $.extend(true, ts.defaults, { - fixedUrl: '', - widgetOptions: { - storage_fixedUrl: '', - storage_group: '', - storage_page: '', - storage_tableId: '', - storage_useSessionStorage: '' - } - }); - // https://gist.github.com/paulirish/5558557 - if (storageType in window) { - try { - window[storageType].setItem('_tmptest', 'temp'); - hasStorage = true; - window[storageType].removeItem('_tmptest'); - } catch (error) { - if (c && c.debug) { - console.warn( storageType + ' is not supported in this browser' ); + + // skip if using cookies + if (storageType !== 'c') { + storageType = (storageType === 's' || session) ? 'sessionStorage' : 'localStorage'; + // https://gist.github.com/paulirish/5558557 + if (storageType in window) { + try { + window[storageType].setItem('_tmptest', 'temp'); + hasStorage = true; + window[storageType].removeItem('_tmptest'); + } catch (error) { + if (c && c.debug) { + console.warn( storageType + ' is not supported in this browser' ); + } } } } + if (c.debug) { + console.log('Storage widget using', hasStorage ? storageType : 'cookies'); + } // *** get value *** if ($.parseJSON) { if (hasStorage) { @@ -393,7 +408,7 @@ })(jQuery); -/*! Widget: filter - updated 4/4/2017 (v2.28.7) *//* +/*! Widget: filter - updated 4/18/2017 (v2.28.8) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -1280,6 +1295,19 @@ tsf.checkFilters( table, filter, skipFirst ); } }, + equalFilters: function (c, filter1, filter2) { + var indx, + f1 = [], + f2 = [], + len = c.columns + 1; // add one to include anyMatch filter + filter1 = $.isArray(filter1) ? filter1 : []; + filter2 = $.isArray(filter2) ? filter2 : []; + for (indx = 0; indx < len; indx++) { + f1[indx] = filter1[indx] || ''; + f2[indx] = filter2[indx] || ''; + } + return f1.join(',') === f2.join(','); + }, checkFilters: function( table, filter, skipFirst ) { var c = table.config, wo = c.widgetOptions, @@ -1312,7 +1340,7 @@ } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons - if ( c.lastSearch.join(',') === currentFilters.join(',') && filter !== false ) { + if ( tsf.equalFilters(c, c.lastSearch, currentFilters) && filter !== false ) { return; } else if ( filter === false ) { // force filter refresh @@ -1661,7 +1689,7 @@ }, findRows: function( table, filters, currentFilters ) { if ( - table.config.lastSearch.join(',') === ( currentFilters || [] ).join(',') || + tsf.equalFilters(table.config, table.config.lastSearch, currentFilters) || !table.config.widgetOptions.filter_initialized ) { return; @@ -2206,7 +2234,8 @@ if ( ( getRaw !== true && wo && !wo.filter_columnFilters ) || // setFilters called, but last search is exactly the same as the current // fixes issue #733 & #903 where calling update causes the input values to reset - ( $.isArray(setFilters) && setFilters.join(',') === c.lastSearch.join(',') ) ) { + ( $.isArray(setFilters) && tsf.equalFilters(c, setFilters, c.lastSearch) ) + ) { return $( table ).data( 'lastSearch' ) || []; } if ( c ) { @@ -2591,7 +2620,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 1/28/2017 (v2.28.5) */ +/*! Widget: resizable - updated 4/18/2017 (v2.28.8) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -2756,6 +2785,10 @@ tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); }); } + + if ( !wo.resizable_includeFooter && c.$table.children('tfoot').length ) { + tableHeight -= c.$table.children('tfoot').height(); + } // subtract out table left position from resizable handles. Fixes #864 startPosition = c.$table.position().left; $handles.each( function() { @@ -2926,10 +2959,10 @@ options: { resizable : true, // save column widths to storage resizable_addLastColumn : false, + resizable_includeFooter: true, resizable_widths : [], resizable_throttle : false, // set to true (5ms) or any number 0-10 range - resizable_targetLast : false, - resizable_fullWidth : null + resizable_targetLast : false }, init: function(table, thisWidget, c, wo) { ts.resizable.init( c, wo ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index a4d201a..fe1aec4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 4/4/2017 (v2.28.7) *//* +/*! Widget: filter - updated 4/18/2017 (v2.28.8) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -885,6 +885,19 @@ tsf.checkFilters( table, filter, skipFirst ); } }, + equalFilters: function (c, filter1, filter2) { + var indx, + f1 = [], + f2 = [], + len = c.columns + 1; // add one to include anyMatch filter + filter1 = $.isArray(filter1) ? filter1 : []; + filter2 = $.isArray(filter2) ? filter2 : []; + for (indx = 0; indx < len; indx++) { + f1[indx] = filter1[indx] || ''; + f2[indx] = filter2[indx] || ''; + } + return f1.join(',') === f2.join(','); + }, checkFilters: function( table, filter, skipFirst ) { var c = table.config, wo = c.widgetOptions, @@ -917,7 +930,7 @@ } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons - if ( c.lastSearch.join(',') === currentFilters.join(',') && filter !== false ) { + if ( tsf.equalFilters(c, c.lastSearch, currentFilters) && filter !== false ) { return; } else if ( filter === false ) { // force filter refresh @@ -1266,7 +1279,7 @@ }, findRows: function( table, filters, currentFilters ) { if ( - table.config.lastSearch.join(',') === ( currentFilters || [] ).join(',') || + tsf.equalFilters(table.config, table.config.lastSearch, currentFilters) || !table.config.widgetOptions.filter_initialized ) { return; @@ -1811,7 +1824,8 @@ if ( ( getRaw !== true && wo && !wo.filter_columnFilters ) || // setFilters called, but last search is exactly the same as the current // fixes issue #733 & #903 where calling update causes the input values to reset - ( $.isArray(setFilters) && setFilters.join(',') === c.lastSearch.join(',') ) ) { + ( $.isArray(setFilters) && tsf.equalFilters(c, setFilters, c.lastSearch) ) + ) { return $( table ).data( 'lastSearch' ) || []; } if ( c ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index b57978f..669090a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 4/2/2017 (v2.28.6) */ +/*! Widget: Pager - updated 4/18/2017 (v2.28.8) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -249,10 +249,15 @@ .off( namespace ) .on( 'filterInit filterStart '.split( ' ' ).join( namespace + ' ' ), function( e, filters ) { p.currentFilters = $.isArray( filters ) ? filters : c.$table.data( 'lastSearch' ); + var filtersEqual; + if (ts.filter.equalFilters) { + filtersEqual = ts.filter.equalFilters(c, c.lastSearch, p.currentFilters); + } else { + // will miss filter changes of the same value in a different column, see #1363 + filtersEqual = ( c.lastSearch || [] ).join( '' ) !== ( p.currentFilters || [] ).join( '' ); + } // don't change page if filters are the same (pager updating, etc) - if ( e.type === 'filterStart' && wo.pager_pageReset !== false && - ( c.lastSearch || [] ).join( ',' ) !== ( p.currentFilters || [] ).join( ',' ) - ) { + if ( e.type === 'filterStart' && wo.pager_pageReset !== false && !filtersEqual ) { p.page = wo.pager_pageReset; // fixes #456 & #565 } }) diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js index 5e3f943..543b143 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js @@ -1,4 +1,4 @@ -/*! Widget: resizable - updated 1/28/2017 (v2.28.5) */ +/*! Widget: resizable - updated 4/18/2017 (v2.28.8) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -163,6 +163,10 @@ tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); }); } + + if ( !wo.resizable_includeFooter && c.$table.children('tfoot').length ) { + tableHeight -= c.$table.children('tfoot').height(); + } // subtract out table left position from resizable handles. Fixes #864 startPosition = c.$table.position().left; $handles.each( function() { @@ -333,10 +337,10 @@ options: { resizable : true, // save column widths to storage resizable_addLastColumn : false, + resizable_includeFooter: true, resizable_widths : [], resizable_throttle : false, // set to true (5ms) or any number 0-10 range - resizable_targetLast : false, - resizable_fullWidth : null + resizable_targetLast : false }, init: function(table, thisWidget, c, wo) { ts.resizable.init( c, wo ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index 6be6b15..d005b3a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -1,4 +1,4 @@ -/*! Widget: scroller - updated 1/28/2017 (v2.28.5) *//* +/*! Widget: scroller - updated 4/18/2017 (v2.28.8) *//* Copyright (C) 2011 T. Connell & Associates, Inc. Dual-licensed under the MIT and GPL licenses @@ -113,7 +113,7 @@ when height < max height (filtering) */ '.' + tscss.scrollerTable + ' { position: relative; overflow: auto; }' + '.' + tscss.scrollerTable + ' table.' + tscss.table + - ' { border-top: 0; margin-top: 0; margin-bottom: 0; overflow: hidden; }' + + ' { border-top: 0; margin-top: 0; margin-bottom: 0; overflow: hidden; max-width: initial; }' + /* hide footer in original table */ '.' + tscss.scrollerTable + ' tfoot, .' + tscss.scrollerHideElement + ', .' + tscss.scrollerHideColumn + ' { display: none; }' + @@ -365,7 +365,7 @@ resize : function( c, wo ) { if ( wo.scroller_isBusy ) { return; } - var index, borderWidth, setWidth, $headers, $this, temp, + var index, borderWidth, setWidth, $headers, $this, tsScroller = ts.scroller, $container = wo.scroller_$container, $table = c.$table, @@ -378,7 +378,9 @@ // Hide other scrollers so we can resize $div = $( 'div.' + tscss.scrollerWrap + '[id!="' + id + '"]' ) .addClass( tscss.scrollerHideElement ), - row = '<tr class="' + tscss.scrollerSpacerRow + ' ' + c.selectorRemove.slice(1) + '">'; + temp = 'padding:0;margin:0;border:0;height:0;max-height:0;min-height:0;', + row = '<tr class="' + tscss.scrollerSpacerRow + ' ' + c.selectorRemove.slice(1) + + '" style="' + temp + '">'; wo.scroller_calcWidths = []; @@ -417,22 +419,22 @@ setWidth = $this.width(); } } - row += '<td data-column="' + index + '" style="padding:0;margin:0;border:0;height:0;max-height:0;' + - 'min-height:0;width:' + setWidth + 'px;min-width:' + setWidth + 'px;max-width:' + setWidth + 'px"></td>'; + row += '<td data-column="' + index + '" style="' + temp + 'width:' + setWidth + + 'px;min-width:' + setWidth + 'px;max-width:' + setWidth + 'px"></td>'; // save current widths wo.scroller_calcWidths[ index ] = setWidth; } row += '</tr>'; - c.$tbodies.eq(0).prepend( row ); // tbody + c.$tbodies.eq(0).append( row ); // tbody $hdr.children( 'thead' ).append( row ); $foot.children( 'tfoot' ).append( row ); // include colgroup or alignment is off ts.fixColumnWidth( c.table ); row = c.$table.children( 'colgroup' )[0].outerHTML; - $hdr.prepend( row ); - $foot.prepend( row ); + $hdr.append( row ); + $foot.append( row ); temp = $tableWrap.parent().innerWidth() - ( tsScroller.hasScrollBar( $tableWrap ) ? wo.scroller_barSetWidth : 0 ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js index e489d66..93485ac 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js @@ -1,9 +1,23 @@ -/*! Widget: storage - updated 11/26/2016 (v2.28.0) */ +/*! Widget: storage - updated 4/18/2017 (v2.28.8) */ /*global JSON:false */ ;(function ($, window, document) { 'use strict'; var ts = $.tablesorter || {}; + + // update defaults for validator; these values must be falsy! + $.extend(true, ts.defaults, { + fixedUrl: '', + widgetOptions: { + storage_fixedUrl: '', + storage_group: '', + storage_page: '', + storage_storageType: '', + storage_tableId: '', + storage_useSessionStorage: '' + } + }); + // *** Store data in local storage, with a cookie fallback *** /* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json) if you need it, then include https://github.com/douglascrockford/JSON-js @@ -30,8 +44,12 @@ values = {}, c = table.config, wo = c && c.widgetOptions, - storageType = ( options && options.useSessionStorage ) || ( wo && wo.storage_useSessionStorage ) ? - 'sessionStorage' : 'localStorage', + storageType = ( + ( options && options.storageType ) || ( wo && wo.storage_storageType ) + ).toString().charAt(0).toLowerCase(), + // deprecating "useSessionStorage"; any storageType setting overrides it + session = storageType ? '' : + ( options && options.useSessionStorage ) || ( wo && wo.storage_useSessionStorage ), $table = $(table), // id from (1) options ID, (2) table 'data-table-group' attribute, (3) widgetOptions.storage_tableId, // (4) table ID, then (5) table index @@ -43,29 +61,26 @@ url = options && options.url || $table.attr(options && options.page || wo && wo.storage_page || 'data-table-page') || wo && wo.storage_fixedUrl || c && c.fixedUrl || window.location.pathname; - // update defaults for validator; these values must be falsy! - $.extend(true, ts.defaults, { - fixedUrl: '', - widgetOptions: { - storage_fixedUrl: '', - storage_group: '', - storage_page: '', - storage_tableId: '', - storage_useSessionStorage: '' - } - }); - // https://gist.github.com/paulirish/5558557 - if (storageType in window) { - try { - window[storageType].setItem('_tmptest', 'temp'); - hasStorage = true; - window[storageType].removeItem('_tmptest'); - } catch (error) { - if (c && c.debug) { - console.warn( storageType + ' is not supported in this browser' ); + + // skip if using cookies + if (storageType !== 'c') { + storageType = (storageType === 's' || session) ? 'sessionStorage' : 'localStorage'; + // https://gist.github.com/paulirish/5558557 + if (storageType in window) { + try { + window[storageType].setItem('_tmptest', 'temp'); + hasStorage = true; + window[storageType].removeItem('_tmptest'); + } catch (error) { + if (c && c.debug) { + console.warn( storageType + ' is not supported in this browser' ); + } } } } + if (c.debug) { + console.log('Storage widget using', hasStorage ? storageType : 'cookies'); + } // *** get value *** if ($.parseJSON) { if (hasStorage) { From f0201425a1bf6b2f7e1542a18cf4b49377a32e34 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Fri, 5 May 2017 17:58:21 +0200 Subject: [PATCH 111/138] Update tablesorter to latest version (2.28.9) --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery-tablesorter/jquery.tablesorter.combined.js | 8 ++++---- .../javascripts/jquery-tablesorter/jquery.tablesorter.js | 6 +++--- .../jquery-tablesorter/jquery.tablesorter.widgets.js | 2 +- .../javascripts/jquery-tablesorter/widgets/widget-math.js | 4 ++-- 8 files changed, 17 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd9ea25..1954378 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.23.9 (2017-05-05) + +* Upgrade tablesorter to v2.28.9 + #### v1.23.8 (2017-04-23) * Upgrade tablesorter to v2.28.8 diff --git a/README.md b/README.md index 2300ab5..354f546 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.28.8 (4/18/2017), [documentation] +Current tablesorter version: 2.28.9 (5/3/2017), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index d28b77e..c6e84aa 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 23 - TINY = 8 + TINY = 9 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 72b4597..48a327d 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 72b45976d81f21e063f6641549e4dfc351eab3e6 +Subproject commit 48a327d6f671f934dca9902ecaa1594bc1528937 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 33db612..fcc927f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 04-18-2017 (v2.28.8)*/ +/*! tablesorter (FORK) - updated 05-03-2017 (v2.28.9)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.28.8 *//* +/*! TableSorter (FORK) v2.28.9 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.8', + version : '2.28.9', parsers : [], widgets : [], @@ -1949,7 +1949,7 @@ for ( indx = 0; indx < len; indx++ ) { widget = ts.getWidgetById( c.widgets[ indx ] ); if ( widget && widget.options ) { - wo = $.extend( {}, widget.options ); + wo = $.extend( true, {}, widget.options ); c.widgetOptions = $.extend( true, wo, c.widgetOptions ); // add widgetOptions to defaults for option validator $.extend( true, ts.defaults.widgetOptions, widget.options ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 08fd44c..55a49eb 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.28.8 *//* +/*! TableSorter (FORK) v2.28.9 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.8', + version : '2.28.9', parsers : [], widgets : [], @@ -1931,7 +1931,7 @@ for ( indx = 0; indx < len; indx++ ) { widget = ts.getWidgetById( c.widgets[ indx ] ); if ( widget && widget.options ) { - wo = $.extend( {}, widget.options ); + wo = $.extend( true, {}, widget.options ); c.widgetOptions = $.extend( true, wo, c.widgetOptions ); // add widgetOptions to defaults for option validator $.extend( true, ts.defaults.widgetOptions, widget.options ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 1cbdc5f..3b5f9c1 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 04-18-2017 (v2.28.8)*/ +/*! tablesorter (FORK) - updated 05-03-2017 (v2.28.9)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index a2f31f5..ccafa32 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -1,4 +1,4 @@ -/*! Widget: math - updated 11/26/2016 (v2.28.0) *//* +/*! Widget: math - updated 5/3/2017 (v2.28.9) *//* * Requires tablesorter v2.16+ and jQuery 1.7+ * by Rob Garrison */ @@ -606,7 +606,7 @@ // complete executed after each fucntion math_complete : null, // function($cell, wo, result, value, arry){ return result; }, // math_completed called after all math calculations have completed - // math_completed : function( config ) {}, + math_completed: function( config ) {}, // order of calculation; 'all' is last math_priority : [ 'row', 'above', 'below', 'col' ], // template for or just prepend the mask prefix & suffix with this HTML From d59b7f41120e53ef2ebf8bb16e634f957f6851e9 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Wed, 17 May 2017 21:29:27 +0200 Subject: [PATCH 112/138] Update tablesorter to latest version (2.28.10) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 26 ++-- .../jquery-tablesorter/jquery.tablesorter.js | 24 ++-- .../jquery.tablesorter.widgets.js | 2 +- .../parsers/parser-input-select.js | 111 +++++++++++++----- 8 files changed, 123 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1954378..b617eb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.23.10 (2017-05-17) + +* Upgrade tablesorter to v2.28.10 + #### v1.23.9 (2017-05-05) * Upgrade tablesorter to v2.28.9 diff --git a/README.md b/README.md index 354f546..41914f9 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.28.9 (5/3/2017), [documentation] +Current tablesorter version: 2.28.10 (5/16/2017), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index c6e84aa..c910cae 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 23 - TINY = 9 + TINY = 10 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 48a327d..7f082fa 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 48a327d6f671f934dca9902ecaa1594bc1528937 +Subproject commit 7f082fa0193f8cc9aec9e90071ac81c3f2f207a2 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index fcc927f..fc6dbe7 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 05-03-2017 (v2.28.9)*/ +/*! tablesorter (FORK) - updated 05-16-2017 (v2.28.10)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.28.9 *//* +/*! TableSorter (FORK) v2.28.10 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.9', + version : '2.28.10', parsers : [], widgets : [], @@ -272,11 +272,6 @@ if ( !/tablesorter\-/.test( $table.attr( 'class' ) ) ) { tmp = ( c.theme !== '' ? ' tablesorter-' + c.theme : '' ); } - c.table = table; - c.$table = $table - .addClass( ts.css.table + ' ' + c.tableClass + tmp ) - .attr( 'role', 'grid' ); - c.$headers = $table.find( c.selectorHeaders ); // give the table a unique id, which will be used in namespace binding if ( !c.namespace ) { @@ -286,6 +281,14 @@ c.namespace = '.' + c.namespace.replace( ts.regex.nonWord, '' ); } + c.table = table; + c.$table = $table + // add namespace to table to allow bindings on extra elements to target + // the parent table (e.g. parser-input-select) + .addClass( ts.css.table + ' ' + c.tableClass + tmp + ' ' + c.namespace.slice(1) ) + .attr( 'role', 'grid' ); + c.$headers = $table.find( c.selectorHeaders ); + c.$table.children().children( 'tr' ).attr( 'role', 'row' ); c.$tbodies = $table.children( 'tbody:not(.' + c.cssInfoBlock + ')' ).attr({ 'aria-live' : 'polite', @@ -1304,6 +1307,12 @@ }, updateCell : function( c, cell, resort, callback ) { + // updateCell for child rows is a mess - we'll ignore them for now + // eventually I'll break out the "update" row cache code to make everything consistent + if ( $( cell ).closest( 'tr' ).hasClass( c.cssChildRow ) ) { + console.warn('Tablesorter Warning! "updateCell" for child row content has been disabled, use "update" instead'); + return; + } if ( ts.isEmptyObject( c.cache ) ) { // empty table, do an update instead - fixes #1099 ts.updateHeader( c ); @@ -2522,6 +2531,7 @@ .unbind( ( 'mousedown mouseup keypress '.split( ' ' ).join( c.namespace + ' ' ) ).replace( ts.regex.spaces, ' ' ) ); ts.restoreHeaders( table ); $t.toggleClass( ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme, removeClasses === false ); + $t.removeClass(c.namespace.slice(1)); // clear flag in case the plugin is initialized again table.hasInitialized = false; delete table.config.cache; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 55a49eb..884d76f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.28.9 *//* +/*! TableSorter (FORK) v2.28.10 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.9', + version : '2.28.10', parsers : [], widgets : [], @@ -254,11 +254,6 @@ if ( !/tablesorter\-/.test( $table.attr( 'class' ) ) ) { tmp = ( c.theme !== '' ? ' tablesorter-' + c.theme : '' ); } - c.table = table; - c.$table = $table - .addClass( ts.css.table + ' ' + c.tableClass + tmp ) - .attr( 'role', 'grid' ); - c.$headers = $table.find( c.selectorHeaders ); // give the table a unique id, which will be used in namespace binding if ( !c.namespace ) { @@ -268,6 +263,14 @@ c.namespace = '.' + c.namespace.replace( ts.regex.nonWord, '' ); } + c.table = table; + c.$table = $table + // add namespace to table to allow bindings on extra elements to target + // the parent table (e.g. parser-input-select) + .addClass( ts.css.table + ' ' + c.tableClass + tmp + ' ' + c.namespace.slice(1) ) + .attr( 'role', 'grid' ); + c.$headers = $table.find( c.selectorHeaders ); + c.$table.children().children( 'tr' ).attr( 'role', 'row' ); c.$tbodies = $table.children( 'tbody:not(.' + c.cssInfoBlock + ')' ).attr({ 'aria-live' : 'polite', @@ -1286,6 +1289,12 @@ }, updateCell : function( c, cell, resort, callback ) { + // updateCell for child rows is a mess - we'll ignore them for now + // eventually I'll break out the "update" row cache code to make everything consistent + if ( $( cell ).closest( 'tr' ).hasClass( c.cssChildRow ) ) { + console.warn('Tablesorter Warning! "updateCell" for child row content has been disabled, use "update" instead'); + return; + } if ( ts.isEmptyObject( c.cache ) ) { // empty table, do an update instead - fixes #1099 ts.updateHeader( c ); @@ -2504,6 +2513,7 @@ .unbind( ( 'mousedown mouseup keypress '.split( ' ' ).join( c.namespace + ' ' ) ).replace( ts.regex.spaces, ' ' ) ); ts.restoreHeaders( table ); $t.toggleClass( ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme, removeClasses === false ); + $t.removeClass(c.namespace.slice(1)); // clear flag in case the plugin is initialized again table.hasInitialized = false; delete table.config.cache; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 3b5f9c1..6aebadf 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 05-03-2017 (v2.28.9)*/ +/*! tablesorter (FORK) - updated 05-16-2017 (v2.28.10)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index bdac971..cdb1b02 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,4 +1,4 @@ -/*! Parser: input & select - updated 11/26/2016 (v2.28.0) *//* +/*! Parser: input & select - updated 5/16/2017 (v2.28.10) *//* * for jQuery 1.7+ & tablesorter 2.7.11+ * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ @@ -131,31 +131,54 @@ $row.removeClass( checkboxClass ); } }, + updateCheckbox = function($el, state) { + if ($el[0].nodeName !== 'INPUT') { + $el = $el.find( 'input[type="checkbox"]' ); + } + if ($el.length) { + var ua = window.navigator.userAgent; + if (state === 'indeterminate') { + // needed for IE + $el.prop('checked', !(ua.indexOf('Trident/') > -1 || ua.indexOf('Edge/') > -1)); + $el.prop('indeterminate', true); + } else { + $el.prop('checked', state); + $el.prop('indeterminate', false); + } + } + }, updateHeaderCheckbox = function( $table, checkboxClass ) { - var ua = window.navigator.userAgent, - $rows = $table.children( 'tbody' ).children( ':visible' ), // (include child rows?) - len = $rows.length; + var $rows = $table.children( 'tbody' ).children( ':visible' ), // (include child rows?) + len = $rows.length, + hasSticky = $table[0].config.widgetOptions.$sticky; // set indeterminate state on header checkbox $table.children( 'thead' ).find( 'input[type="checkbox"]' ).each( function() { var column = $( this ).closest( 'td, th' ).attr( 'data-column' ), + $sticky = hasSticky.find( '[data-column="' + column + '"]' ), vis = $rows.filter( '.' + checkboxClass + '-' + column ).length, allChecked = vis === len && len > 0; if ( vis === 0 || allChecked ) { - this.checked = allChecked; - this.indeterminate = false; + updateCheckbox($(this), allChecked); + if ($sticky) { + updateCheckbox($sticky, allChecked); + } } else { - // needed for IE - this.checked = !(ua.indexOf('Trident/') > -1 || ua.indexOf('Edge/') > -1); - this.indeterminate = true; + updateCheckbox($(this), 'indeterminate'); + if ($sticky) { + updateCheckbox($sticky, 'indeterminate'); + } } }); + }; $( 'table' ).on( 'tablesorter-initialized updateComplete', function() { this.tablesorterBusy = false; var namespace = '.parser-forms'; - // bind to .tablesorter (default class name) - $( this ).children( 'tbody' ) + $( this ) + // add namespace to table in case of version mismatch (v2.28.10) + .addClass( this.config.namespace.slice(1) ) + .children( 'tbody' ) .off( namespace ) .on( 'mouseleave' + namespace, function( event ) { // make sure we restore original values (trigger blur) @@ -165,15 +188,30 @@ $( ':focus' ).blur(); } }) - .on( 'focus' + namespace, 'select, input:not([type=checkbox]), textarea', function() { + .on( 'focus' + namespace, 'select, input:not([type=checkbox]), textarea', function( event ) { + var $row = $( event.target ).closest( 'tr' ), + c = $row.closest( 'table' )[0].config; + if ( !c || c && c.ignoreChildRow && $row.hasClass( c.cssChildRow ) ) { + return; + } $( this ).data( 'ts-original-value', this.value ); }) - .on( 'blur' + namespace, 'input:not([type=checkbox]), textarea', function() { + .on( 'blur' + namespace, 'input:not([type=checkbox]), textarea', function( event ) { + var $row = $( event.target ).closest( 'tr' ), + c = $row.closest( 'table' )[0].config; + if ( !c || c && c.ignoreChildRow && $row.hasClass( c.cssChildRow ) ) { + return; + } // restore input value; // 'change' is triggered before 'blur' so this doesn't replace the new update with the original this.value = $( this ).data( 'ts-original-value' ); }) .on( 'change keyup '.split( ' ' ).join( namespace + ' ' ), 'select, input, textarea', function( event ) { + var $row = $( this ).closest( 'tr' ), + c = $row.closest( 'table' )[0].config; + if ( !c || c && c.ignoreChildRow && $row.hasClass( c.cssChildRow ) ) { + return; + } if ( event.which === 27 && !( this.nodeName === 'INPUT' && this.type === 'checkbox' ) ) { // escape: restore original value this.value = $( this ).data( 'ts-original-value' ); @@ -187,11 +225,9 @@ $target = $( event.target ), isCheckbox = event.target.type === 'checkbox', $cell = $target.closest( 'td' ), - $table = $cell.closest( 'table' ), indx = $cell[ 0 ].cellIndex, - c = $table[ 0 ].config || false, - busy = $table.length && $table[ 0 ].tablesorterBusy, - $hdr = c && c.$headerIndexed && c.$headerIndexed[ indx ] || [], + busy = c.table.tablesorterBusy, + $hdr = c.$headerIndexed && c.$headerIndexed[ indx ] || [], val = isCheckbox ? event.target.checked : $target.val(); // abort if not a tablesorter table, or busy if ( $.isEmptyObject( c ) || busy !== false ) { @@ -200,7 +236,7 @@ if ( isCheckbox ) { checkboxClass = c.checkboxClass || 'checked'; toggleRowClass( $cell.closest( 'tr' ), checkboxClass, indx, val ); - updateHeaderCheckbox( $table, checkboxClass ); + updateHeaderCheckbox( c.$table, checkboxClass ); } // don't use updateCell if column is set to 'sorter-false' and 'filter-false', // or column is set to 'parser-false' @@ -213,11 +249,11 @@ // ignore change event if nothing changed if ( c && val !== $target.data( 'ts-original-value' ) || isCheckbox ) { $target.data( 'ts-original-value', val ); - $table[ 0 ].tablesorterBusy = true; + c.table.tablesorterBusy = true; // pass undefined resort value so it falls back to config.resort setting $.tablesorter.updateCell( c, $cell, undef, function() { - updateServer( event, $table, $target ); - $table[ 0 ].tablesorterBusy = false; + updateServer( event, c.$table, $target ); + c.table.tablesorterBusy = false; }); } } @@ -240,32 +276,45 @@ } }) .children( 'thead' ) + .add( this.config.widgetOptions.$sticky ) .off( namespace ) // modified from http://jsfiddle.net/abkNM/6163/ // click needed for IE; a change isn't fired when going from an indeterminate checkbox to // either checked or unchecked .on( 'click' + namespace + ' change' + namespace, 'input[type="checkbox"]', function( event ) { - var undef, onlyVisible, column, $target, isParsed, checkboxClass, + var c, undef, onlyVisible, column, $target, isParsed, checkboxClass, $checkbox = $( this ), + isChecked = this.checked, $table = $checkbox.closest( 'table' ), - c = $table.length && $table[ 0 ].config, - isChecked = this.checked; + isSticky = $table.length && $table[0].className.match(/(tablesorter\w+)_extra_table/); + if (isSticky) { + isSticky = isSticky[1]; + $table = $('.' + isSticky + ':not(.' + isSticky + '_extra_table)'); + } + c = $table.length && $table[ 0 ].config; if ( $table.length && c && !$table[ 0 ].tablesorterBusy ) { column = parseInt( $checkbox.closest( 'td, th' ).attr( 'data-column' ), 10 ); isParsed = c.parsers[ column ].id === 'checkbox'; - onlyVisible = $table.length && c.checkboxVisible; + onlyVisible = c.checkboxVisible; $table[ 0 ].tablesorterBusy = true; // prevent "change" event from calling updateCell numerous times (see #971) - $target = $table - .children( 'tbody' ) - .children( 'tr' + ( typeof onlyVisible === 'undefined' || onlyVisible === true ? ':visible' : '' ) ) - .children( ':nth-child(' + ( column + 1 ) + ')' ) - .find( 'input[type="checkbox"]' ) - .prop( 'checked', isChecked ); + updateCheckbox( + $target = $table + .children( 'tbody' ) + .children( 'tr' + ( typeof onlyVisible === 'undefined' || onlyVisible === true ? ':visible' : '' ) ) + .children( ':nth-child(' + ( column + 1 ) + ')' ), + isChecked + ); // add checkbox class names to row checkboxClass = c.checkboxClass || 'checked'; $target.each( function() { toggleRowClass( $( this ).closest( 'tr' ), checkboxClass, column, isChecked ); }); + if (isSticky) { + // make main table checkbox match sticky header checkbox + updateCheckbox($table.children( 'thead' ).find( '[data-column="' + column + '"]' ), isChecked); + } else if (c.widgetOptions.$sticky) { + updateCheckbox(c.widgetOptions.$sticky.find( 'thead' ).find( '[data-column="' + column + '"]' ), isChecked); + } updateHeaderCheckbox( $table, checkboxClass ); if ( isParsed ) { // only update cache if checkboxes are being sorted From 1b8437ea1b9f42edbc2caffc95124a1af827690f Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Thu, 25 May 2017 20:18:28 +0200 Subject: [PATCH 113/138] Update tablesorter to latest version (2.28.11) --- CHANGELOG.md | 4 +++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 33 ++++++++++--------- .../jquery-tablesorter/jquery.tablesorter.js | 4 +-- .../jquery.tablesorter.widgets.js | 29 ++++++++-------- .../jquery-tablesorter/parsers/parser-date.js | 6 ++-- .../widgets/widget-columns.js | 4 +-- .../widgets/widget-cssStickyHeaders.js | 20 +++++++---- .../widgets/widget-filter.js | 23 +++++++------ .../widgets/widget-pager.js | 10 +++--- 12 files changed, 81 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b617eb3..5233e6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.23.11 (2017-05-25) + +* Upgrade tablesorter to v2.28.11 + #### v1.23.10 (2017-05-17) * Upgrade tablesorter to v2.28.10 diff --git a/README.md b/README.md index 41914f9..fd3ec64 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.28.10 (5/16/2017), [documentation] +Current tablesorter version: 2.28.11 (5/24/2017), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index c910cae..7541b6a 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 23 - TINY = 10 + TINY = 11 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 7f082fa..e3227a7 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 7f082fa0193f8cc9aec9e90071ac81c3f2f207a2 +Subproject commit e3227a7af0b2b2b689538190506e71b2b5a9ce4d diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index fc6dbe7..0130806 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 05-16-2017 (v2.28.10)*/ +/*! tablesorter (FORK) - updated 05-24-2017 (v2.28.11)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.28.10 *//* +/*! TableSorter (FORK) v2.28.11 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.10', + version : '2.28.11', parsers : [], widgets : [], @@ -3149,14 +3149,14 @@ })(jQuery); -/*! Widget: columns */ +/*! Widget: columns - updated 5/24/2017 (v2.28.11) */ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; ts.addWidget({ id: 'columns', - priority: 30, + priority: 65, options : { columns : [ 'primary', 'secondary', 'tertiary' ] }, @@ -3228,7 +3228,7 @@ })(jQuery); -/*! Widget: filter - updated 4/18/2017 (v2.28.8) *//* +/*! Widget: filter - updated 5/24/2017 (v2.28.11) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -4046,9 +4046,6 @@ if ( event.which === tskeyCodes.escape ) { // make sure to restore the last value on escape this.value = wo.filter_resetOnEsc ? '' : c.lastSearch[column]; - // live search - } else if ( liveSearch === false ) { - return; // don't return if the search value is empty ( all rows need to be revealed ) } else if ( this.value !== '' && ( // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace @@ -4057,26 +4054,32 @@ ( event.which !== tskeyCodes.enter && event.which !== tskeyCodes.backSpace && ( event.which < tskeyCodes.space || ( event.which >= tskeyCodes.left && event.which <= tskeyCodes.down ) ) ) ) ) { return; + // live search + } else if ( liveSearch === false ) { + if ( this.value !== '' && event.which !== tskeyCodes.enter ) { + return; + } } // change event = no delay; last true flag tells getFilters to skip newest timed input tsf.searching( table, true, true, column ); }) // include change for select - fixes #473 - .bind( 'search change keypress input '.split( ' ' ).join( namespace + ' ' ), function( event ) { + .bind( 'search change keypress input blur '.split( ' ' ).join( namespace + ' ' ), function( event ) { // don't get cached data, in case data-column changes dynamically var column = parseInt( $( this ).attr( 'data-column' ), 10 ), + eventType = event.type, liveSearch = typeof wo.filter_liveSearch === 'boolean' ? wo.filter_liveSearch : ts.getColumnData( table, wo.filter_liveSearch, column ); if ( table.config.widgetOptions.filter_initialized && // immediate search if user presses enter ( event.which === tskeyCodes.enter || - // immediate search if a "search" is triggered on the input - event.type === 'search' || + // immediate search if a "search" or "blur" is triggered on the input + ( eventType === 'search' || eventType === 'blur' ) || // change & input events must be ignored if liveSearch !== true - ( event.type === 'change' || event.type === 'input' ) && + ( eventType === 'change' || eventType === 'input' ) && // prevent search if liveSearch is a number - liveSearch === true && + ( liveSearch === true || liveSearch !== true && event.target.nodeName !== "INPUT" ) && // don't allow 'change' or 'input' event to process if the input value // is the same - fixes #685 this.value !== c.lastSearch[column] @@ -4085,7 +4088,7 @@ event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - tsf.searching( table, event.type !== 'keypress', true, column ); + tsf.searching( table, eventType !== 'keypress', true, column ); } }); }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 884d76f..c0393bb 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.28.10 *//* +/*! TableSorter (FORK) v2.28.11 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.10', + version : '2.28.11', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 6aebadf..a96e46a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 05-16-2017 (v2.28.10)*/ +/*! tablesorter (FORK) - updated 05-24-2017 (v2.28.11)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -329,14 +329,14 @@ })(jQuery); -/*! Widget: columns */ +/*! Widget: columns - updated 5/24/2017 (v2.28.11) */ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; ts.addWidget({ id: 'columns', - priority: 30, + priority: 65, options : { columns : [ 'primary', 'secondary', 'tertiary' ] }, @@ -408,7 +408,7 @@ })(jQuery); -/*! Widget: filter - updated 4/18/2017 (v2.28.8) *//* +/*! Widget: filter - updated 5/24/2017 (v2.28.11) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -1226,9 +1226,6 @@ if ( event.which === tskeyCodes.escape ) { // make sure to restore the last value on escape this.value = wo.filter_resetOnEsc ? '' : c.lastSearch[column]; - // live search - } else if ( liveSearch === false ) { - return; // don't return if the search value is empty ( all rows need to be revealed ) } else if ( this.value !== '' && ( // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace @@ -1237,26 +1234,32 @@ ( event.which !== tskeyCodes.enter && event.which !== tskeyCodes.backSpace && ( event.which < tskeyCodes.space || ( event.which >= tskeyCodes.left && event.which <= tskeyCodes.down ) ) ) ) ) { return; + // live search + } else if ( liveSearch === false ) { + if ( this.value !== '' && event.which !== tskeyCodes.enter ) { + return; + } } // change event = no delay; last true flag tells getFilters to skip newest timed input tsf.searching( table, true, true, column ); }) // include change for select - fixes #473 - .bind( 'search change keypress input '.split( ' ' ).join( namespace + ' ' ), function( event ) { + .bind( 'search change keypress input blur '.split( ' ' ).join( namespace + ' ' ), function( event ) { // don't get cached data, in case data-column changes dynamically var column = parseInt( $( this ).attr( 'data-column' ), 10 ), + eventType = event.type, liveSearch = typeof wo.filter_liveSearch === 'boolean' ? wo.filter_liveSearch : ts.getColumnData( table, wo.filter_liveSearch, column ); if ( table.config.widgetOptions.filter_initialized && // immediate search if user presses enter ( event.which === tskeyCodes.enter || - // immediate search if a "search" is triggered on the input - event.type === 'search' || + // immediate search if a "search" or "blur" is triggered on the input + ( eventType === 'search' || eventType === 'blur' ) || // change & input events must be ignored if liveSearch !== true - ( event.type === 'change' || event.type === 'input' ) && + ( eventType === 'change' || eventType === 'input' ) && // prevent search if liveSearch is a number - liveSearch === true && + ( liveSearch === true || liveSearch !== true && event.target.nodeName !== "INPUT" ) && // don't allow 'change' or 'input' event to process if the input value // is the same - fixes #685 this.value !== c.lastSearch[column] @@ -1265,7 +1268,7 @@ event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - tsf.searching( table, event.type !== 'keypress', true, column ); + tsf.searching( table, eventType !== 'keypress', true, column ); } }); }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js index d5a0ca7..30072c9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js @@ -1,4 +1,4 @@ -/*! Parser: dates - updated 8/22/2016 (v2.27.5) */ +/*! Parser: dates - updated 5/24/2017 (v2.28.11) */ /* Extract dates using popular natural language date parsers */ /*jshint jquery:true */ /*global Sugar*/ @@ -6,7 +6,7 @@ 'use strict'; /*! Sugar (https://sugarjs.com/docs/#/DateParsing) */ - /* demo: http://jsfiddle.net/Mottie/abkNM/4163/ */ + /* demo: http://jsfiddle.net/Mottie/7z0ss5xn/ */ $.tablesorter.addParser({ id: 'sugar', is: function() { @@ -22,7 +22,7 @@ }); /*! Datejs (http://www.datejs.com/) */ - /* demo: http://jsfiddle.net/Mottie/abkNM/4164/ */ + /* demo: http://jsfiddle.net/Mottie/zge0L2u6/ */ $.tablesorter.addParser({ id: 'datejs', is: function() { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columns.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columns.js index 774443d..20a2ac0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columns.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columns.js @@ -1,11 +1,11 @@ -/*! Widget: columns */ +/*! Widget: columns - updated 5/24/2017 (v2.28.11) */ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; ts.addWidget({ id: 'columns', - priority: 30, + priority: 65, options : { columns : [ 'primary', 'secondary', 'tertiary' ] }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js index 07ed0f6..c126713 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js @@ -1,4 +1,4 @@ -/*! Widget: cssStickyHeaders - updated 2/9/2015 (v2.19.1) *//* +/*! Widget: cssStickyHeaders - updated 5/24/2017 (v2.28.11) *//* * Requires a modern browser, tablesorter v2.8+ */ /*jshint jquery:true, unused:false */ @@ -18,10 +18,11 @@ cssStickyHeaders_filteredToTop : true }, init : function(table, thisWidget, c, wo) { - var ht, offst, adjustY, + var offst, adjustY, $table = c.$table, $attach = $(wo.cssStickyHeaders_attachTo), - isIE = 'ActiveXObject' in window, // target all versions of IE + // target all versions of IE + isIE = 'ActiveXObject' in window || window.navigator.userAgent.indexOf("Edge") > -1, namespace = c.namespace + 'cssstickyheader ', $thead = $table.children('thead'), $caption = $table.children('caption'), @@ -29,6 +30,8 @@ $parent = $table.parent().closest('table.' + ts.css.table), $parentThead = $parent.length && ts.hasWidget($parent[0], 'cssStickyHeaders') ? $parent.children('thead') : [], borderTopWidth = ( parseInt( $table.css('border-top-width'), 10 ) || 0 ), + // Fixes for Safari + tableH = $table.height(), lastCaptionSetting = wo.cssStickyHeaders_addCaption, // table offset top changes while scrolling in FF adjustOffsetTop = false, @@ -46,9 +49,8 @@ if ($caption.length) { // Firefox does not include the caption height when getting the table height // see https://bugzilla.mozilla.org/show_bug.cgi?id=820891, so lets detect it instead of browser sniff - ht = $table.height(); $caption.hide(); - addCaptionHeight = $table.height() === ht; + addCaptionHeight = $table.height() === tableH; $caption.show(); // Firefox changes the offset().top when translating the table caption @@ -71,6 +73,12 @@ adjustY = $table.offset().top; } + // Fix for safari, when caption present, table + // height changes while scrolling + if ($win.scrollTop() < $caption.outerHeight(true)) { + tableH = $table.height(); + } + var top = $attach.length ? $attach.offset().top : $win.scrollTop(), // add caption height; include table padding top & border-spacing or text may be above the fold (jQuery UI themes) // border-spacing needed in Firefox, but not webkit... not sure if I should account for that @@ -78,7 +86,7 @@ ( parseInt( $table.css('padding-top'), 10 ) || 0 ) + ( parseInt( $table.css('border-spacing'), 10 ) || 0 ), - bottom = $table.height() + ( addCaptionHeight && wo.cssStickyHeaders_addCaption ? captionHeight : 0 ) - + bottom = tableH + ( addCaptionHeight && wo.cssStickyHeaders_addCaption ? captionHeight : 0 ) - $thead.height() - ( $table.children('tfoot').height() || 0 ) - ( wo.cssStickyHeaders_addCaption ? captionHeight : ( addCaptionHeight ? 0 : captionHeight ) ), diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index fe1aec4..f37fd47 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 4/18/2017 (v2.28.8) *//* +/*! Widget: filter - updated 5/24/2017 (v2.28.11) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -816,9 +816,6 @@ if ( event.which === tskeyCodes.escape ) { // make sure to restore the last value on escape this.value = wo.filter_resetOnEsc ? '' : c.lastSearch[column]; - // live search - } else if ( liveSearch === false ) { - return; // don't return if the search value is empty ( all rows need to be revealed ) } else if ( this.value !== '' && ( // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace @@ -827,26 +824,32 @@ ( event.which !== tskeyCodes.enter && event.which !== tskeyCodes.backSpace && ( event.which < tskeyCodes.space || ( event.which >= tskeyCodes.left && event.which <= tskeyCodes.down ) ) ) ) ) { return; + // live search + } else if ( liveSearch === false ) { + if ( this.value !== '' && event.which !== tskeyCodes.enter ) { + return; + } } // change event = no delay; last true flag tells getFilters to skip newest timed input tsf.searching( table, true, true, column ); }) // include change for select - fixes #473 - .bind( 'search change keypress input '.split( ' ' ).join( namespace + ' ' ), function( event ) { + .bind( 'search change keypress input blur '.split( ' ' ).join( namespace + ' ' ), function( event ) { // don't get cached data, in case data-column changes dynamically var column = parseInt( $( this ).attr( 'data-column' ), 10 ), + eventType = event.type, liveSearch = typeof wo.filter_liveSearch === 'boolean' ? wo.filter_liveSearch : ts.getColumnData( table, wo.filter_liveSearch, column ); if ( table.config.widgetOptions.filter_initialized && // immediate search if user presses enter ( event.which === tskeyCodes.enter || - // immediate search if a "search" is triggered on the input - event.type === 'search' || + // immediate search if a "search" or "blur" is triggered on the input + ( eventType === 'search' || eventType === 'blur' ) || // change & input events must be ignored if liveSearch !== true - ( event.type === 'change' || event.type === 'input' ) && + ( eventType === 'change' || eventType === 'input' ) && // prevent search if liveSearch is a number - liveSearch === true && + ( liveSearch === true || liveSearch !== true && event.target.nodeName !== "INPUT" ) && // don't allow 'change' or 'input' event to process if the input value // is the same - fixes #685 this.value !== c.lastSearch[column] @@ -855,7 +858,7 @@ event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - tsf.searching( table, event.type !== 'keypress', true, column ); + tsf.searching( table, eventType !== 'keypress', true, column ); } }); }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 669090a..422c9d2 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 4/18/2017 (v2.28.8) */ +/*! Widget: Pager - updated 5/24/2017 (v2.28.11) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -244,12 +244,15 @@ wo = c.widgetOptions, namespace = c.namespace + 'pager', s = wo.pager_selectors; - c.$table .off( namespace ) .on( 'filterInit filterStart '.split( ' ' ).join( namespace + ' ' ), function( e, filters ) { p.currentFilters = $.isArray( filters ) ? filters : c.$table.data( 'lastSearch' ); var filtersEqual; + if (p.ajax && e.type === 'filterInit') { + // ensure pager ajax is called after filter widget has initialized + return tsp.moveToPage( c, p, false ); + } if (ts.filter.equalFilters) { filtersEqual = ts.filter.equalFilters(c, c.lastSearch, p.currentFilters); } else { @@ -270,7 +273,6 @@ tsp.updateCache( c ); } tsp.updatePageDisplay( c, false ); - // tsp.moveToPage( c, p, false ); <-- called when applyWidgets is triggered ts.applyWidget( c.table ); } }) @@ -1110,7 +1112,7 @@ p.filteredRows = typeof tmp.filtered !== 'undefined' ? tmp.filtered : ( c.debug ? console.error('Pager: no initial filtered page set!') || 0 : 0 ); tsp.updatePageDisplay( c, false ); - } else if (p.initialized) { + } else { tsp.getAjax( c ); } } else if ( !p.ajax ) { From 3125b8ddf28c7c92fea87f546a03317980851ddf Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 29 May 2017 20:18:45 +0200 Subject: [PATCH 114/138] Update tablesorter to latest version (2.28.12) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 38 ++++++++++++------- .../jquery-tablesorter/jquery.tablesorter.js | 4 +- .../jquery.tablesorter.widgets.js | 34 +++++++++++------ .../widgets/widget-columnSelector.js | 6 +-- .../widgets/widget-cssStickyHeaders.js | 2 +- .../widgets/widget-filter.js | 32 +++++++++++----- 10 files changed, 83 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5233e6d..4efe0ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.23.12 (2017-05-29) + +* Upgrade tablesorter to v2.28.12 + #### v1.23.11 (2017-05-25) * Upgrade tablesorter to v2.28.11 diff --git a/README.md b/README.md index fd3ec64..86a0258 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.28.11 (5/24/2017), [documentation] +Current tablesorter version: 2.28.12 (5/26/2017), [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 7541b6a..30f42c9 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 23 - TINY = 11 + TINY = 12 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index e3227a7..c77d7b2 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit e3227a7af0b2b2b689538190506e71b2b5a9ce4d +Subproject commit c77d7b2d6e4f49aa6efec1f9c96f14d405925cae diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 0130806..d434b42 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 05-24-2017 (v2.28.11)*/ +/*! tablesorter (FORK) - updated 05-26-2017 (v2.28.12)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.28.11 *//* +/*! TableSorter (FORK) v2.28.12 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.11', + version : '2.28.12', parsers : [], widgets : [], @@ -4079,7 +4079,7 @@ // change & input events must be ignored if liveSearch !== true ( eventType === 'change' || eventType === 'input' ) && // prevent search if liveSearch is a number - ( liveSearch === true || liveSearch !== true && event.target.nodeName !== "INPUT" ) && + ( liveSearch === true || liveSearch !== true && event.target.nodeName !== 'INPUT' ) && // don't allow 'change' or 'input' event to process if the input value // is the same - fixes #685 this.value !== c.lastSearch[column] @@ -4474,13 +4474,7 @@ fxn = vars.functions[ columnIndex ]; filterMatched = null; if ( fxn ) { - if ( fxn === true ) { - // default selector uses exact match unless 'filter-match' class is found - filterMatched = data.isMatch ? - // data.iExact may be a number - ( '' + data.iExact ).search( data.iFilter ) >= 0 : - data.filter === data.exact; - } else if ( typeof fxn === 'function' ) { + if ( typeof fxn === 'function' ) { // filter callback( exact cell content, parser normalized content, // filter input value, column index, jQuery row object ) filterMatched = fxn( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); @@ -4499,8 +4493,18 @@ result = filterMatched; // Look for match, and add child row data for matching } else { - txt = ( data.iExact + data.childRowText ).indexOf( tsf.parseFilter( c, data.iFilter, data ) ); - result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); + // check fxn (filter-select in header) after filter types are checked + // without this, the filter + jQuery UI selectmenu demo was breaking + if ( fxn === true ) { + // default selector uses exact match unless 'filter-match' class is found + result = data.isMatch ? + // data.iExact may be a number + ( '' + data.iExact ).search( data.iFilter ) >= 0 : + data.filter === data.exact; + } else { + txt = ( data.iExact + data.childRowText ).indexOf( tsf.parseFilter( c, data.iFilter, data ) ); + result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); + } } } else { result = filterMatched; @@ -4800,6 +4804,10 @@ } else if ( $.type( source ) === 'object' && fxn ) { // custom select source function for a SPECIFIC COLUMN arry = fxn( table, column, onlyAvail ); + // abort - updating the selects from an external method + if (arry === null) { + return null; + } } if ( arry === false ) { // fall back to original method @@ -4957,6 +4965,10 @@ // filter_selectSource or column data if ( typeof arry === 'undefined' || arry === '' ) { arry = tsf.getOptionSource( table, column, onlyAvail ); + // abort, selects are updated by an external method + if (arry === null) { + return; + } } if ( $.isArray( arry ) ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index c0393bb..262d82f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.28.11 *//* +/*! TableSorter (FORK) v2.28.12 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.11', + version : '2.28.12', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index a96e46a..cba9542 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 05-24-2017 (v2.28.11)*/ +/*! tablesorter (FORK) - updated 05-26-2017 (v2.28.12)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -1259,7 +1259,7 @@ // change & input events must be ignored if liveSearch !== true ( eventType === 'change' || eventType === 'input' ) && // prevent search if liveSearch is a number - ( liveSearch === true || liveSearch !== true && event.target.nodeName !== "INPUT" ) && + ( liveSearch === true || liveSearch !== true && event.target.nodeName !== 'INPUT' ) && // don't allow 'change' or 'input' event to process if the input value // is the same - fixes #685 this.value !== c.lastSearch[column] @@ -1654,13 +1654,7 @@ fxn = vars.functions[ columnIndex ]; filterMatched = null; if ( fxn ) { - if ( fxn === true ) { - // default selector uses exact match unless 'filter-match' class is found - filterMatched = data.isMatch ? - // data.iExact may be a number - ( '' + data.iExact ).search( data.iFilter ) >= 0 : - data.filter === data.exact; - } else if ( typeof fxn === 'function' ) { + if ( typeof fxn === 'function' ) { // filter callback( exact cell content, parser normalized content, // filter input value, column index, jQuery row object ) filterMatched = fxn( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); @@ -1679,8 +1673,18 @@ result = filterMatched; // Look for match, and add child row data for matching } else { - txt = ( data.iExact + data.childRowText ).indexOf( tsf.parseFilter( c, data.iFilter, data ) ); - result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); + // check fxn (filter-select in header) after filter types are checked + // without this, the filter + jQuery UI selectmenu demo was breaking + if ( fxn === true ) { + // default selector uses exact match unless 'filter-match' class is found + result = data.isMatch ? + // data.iExact may be a number + ( '' + data.iExact ).search( data.iFilter ) >= 0 : + data.filter === data.exact; + } else { + txt = ( data.iExact + data.childRowText ).indexOf( tsf.parseFilter( c, data.iFilter, data ) ); + result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); + } } } else { result = filterMatched; @@ -1980,6 +1984,10 @@ } else if ( $.type( source ) === 'object' && fxn ) { // custom select source function for a SPECIFIC COLUMN arry = fxn( table, column, onlyAvail ); + // abort - updating the selects from an external method + if (arry === null) { + return null; + } } if ( arry === false ) { // fall back to original method @@ -2137,6 +2145,10 @@ // filter_selectSource or column data if ( typeof arry === 'undefined' || arry === '' ) { arry = tsf.getOptionSource( table, column, onlyAvail ); + // abort, selects are updated by an external method + if (arry === null) { + return; + } } if ( $.isArray( arry ) ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index 5615cd7..6abdb41 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Widget: columnSelector (responsive table widget) - updated 12/15/2016 (v2.28.2) *//* +/* Widget: columnSelector (responsive table widget) - updated 5/25/2017 (v2.28.12) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -541,9 +541,9 @@ }, remove: function(table, c, wo, refreshing) { var csel = c.selector; - if ( csel) { csel.$container.empty(); } if ( refreshing || !csel ) { return; } - if (csel.$popup) { csel.$popup.empty(); } + if ( csel ) { csel.$container.empty(); } + if ( csel.$popup ) { csel.$popup.empty(); } csel.$style.remove(); csel.$breakpoints.remove(); $( c.namespace + 'columnselectorHasSpan' ).removeClass( wo.filter_filteredRow || 'filtered' ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js index c126713..9c5a038 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js @@ -22,7 +22,7 @@ $table = c.$table, $attach = $(wo.cssStickyHeaders_attachTo), // target all versions of IE - isIE = 'ActiveXObject' in window || window.navigator.userAgent.indexOf("Edge") > -1, + isIE = 'ActiveXObject' in window || window.navigator.userAgent.indexOf('Edge') > -1, namespace = c.namespace + 'cssstickyheader ', $thead = $table.children('thead'), $caption = $table.children('caption'), diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index f37fd47..816ce2b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -849,7 +849,7 @@ // change & input events must be ignored if liveSearch !== true ( eventType === 'change' || eventType === 'input' ) && // prevent search if liveSearch is a number - ( liveSearch === true || liveSearch !== true && event.target.nodeName !== "INPUT" ) && + ( liveSearch === true || liveSearch !== true && event.target.nodeName !== 'INPUT' ) && // don't allow 'change' or 'input' event to process if the input value // is the same - fixes #685 this.value !== c.lastSearch[column] @@ -1244,13 +1244,7 @@ fxn = vars.functions[ columnIndex ]; filterMatched = null; if ( fxn ) { - if ( fxn === true ) { - // default selector uses exact match unless 'filter-match' class is found - filterMatched = data.isMatch ? - // data.iExact may be a number - ( '' + data.iExact ).search( data.iFilter ) >= 0 : - data.filter === data.exact; - } else if ( typeof fxn === 'function' ) { + if ( typeof fxn === 'function' ) { // filter callback( exact cell content, parser normalized content, // filter input value, column index, jQuery row object ) filterMatched = fxn( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); @@ -1269,8 +1263,18 @@ result = filterMatched; // Look for match, and add child row data for matching } else { - txt = ( data.iExact + data.childRowText ).indexOf( tsf.parseFilter( c, data.iFilter, data ) ); - result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); + // check fxn (filter-select in header) after filter types are checked + // without this, the filter + jQuery UI selectmenu demo was breaking + if ( fxn === true ) { + // default selector uses exact match unless 'filter-match' class is found + result = data.isMatch ? + // data.iExact may be a number + ( '' + data.iExact ).search( data.iFilter ) >= 0 : + data.filter === data.exact; + } else { + txt = ( data.iExact + data.childRowText ).indexOf( tsf.parseFilter( c, data.iFilter, data ) ); + result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); + } } } else { result = filterMatched; @@ -1570,6 +1574,10 @@ } else if ( $.type( source ) === 'object' && fxn ) { // custom select source function for a SPECIFIC COLUMN arry = fxn( table, column, onlyAvail ); + // abort - updating the selects from an external method + if (arry === null) { + return null; + } } if ( arry === false ) { // fall back to original method @@ -1727,6 +1735,10 @@ // filter_selectSource or column data if ( typeof arry === 'undefined' || arry === '' ) { arry = tsf.getOptionSource( table, column, onlyAvail ); + // abort, selects are updated by an external method + if (arry === null) { + return; + } } if ( $.isArray( arry ) ) { From 541fe730d16cc1547df4ac650d1f508e6c4cc3f7 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Tue, 6 Jun 2017 23:09:25 +0200 Subject: [PATCH 115/138] Update tablesorter to latest version (2.28.13) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 73 ++++++++++++------- .../jquery-tablesorter/jquery.tablesorter.js | 45 ++++++++---- .../jquery.tablesorter.widgets.js | 28 ++++--- .../widgets/widget-stickyHeaders.js | 26 ++++--- 8 files changed, 112 insertions(+), 70 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4efe0ca..0b84bc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.23.13 (2017-06-06) + +* Upgrade tablesorter to v2.28.13 + #### v1.23.12 (2017-05-29) * Upgrade tablesorter to v2.28.12 diff --git a/README.md b/README.md index 86a0258..72ac2cd 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.28.12 (5/26/2017), [documentation] +Current tablesorter version: 2.28.13 (6/2/2017) [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 30f42c9..216303a 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 23 - TINY = 12 + TINY = 13 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index c77d7b2..8dbd34e 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit c77d7b2d6e4f49aa6efec1f9c96f14d405925cae +Subproject commit 8dbd34ece690fd488b9ec5472ee070a25def3a53 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index d434b42..479ecbf 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 05-26-2017 (v2.28.12)*/ +/*! tablesorter (FORK) - updated 06-02-2017 (v2.28.13)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.28.12 *//* +/*! TableSorter (FORK) v2.28.13 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.12', + version : '2.28.13', parsers : [], widgets : [], @@ -116,6 +116,7 @@ cssIconNone : '', // class name added to the icon when there is no column sort cssIconAsc : '', // class name added to the icon when the column has an ascending sort cssIconDesc : '', // class name added to the icon when the column has a descending sort + cssIconDisabled : '', // class name added to the icon when the column has a disabled sort // *** events pointerClick : 'click', @@ -1092,7 +1093,7 @@ ▀████▀ ██ █████▀ ██ ██ ██ ██████ */ setHeadersCss : function( c ) { - var $sorted, indx, column, + var indx, column, list = c.sortList, len = list.length, none = ts.css.sortNone + ' ' + c.cssNone, @@ -1100,20 +1101,32 @@ cssIcon = [ c.cssIconAsc, c.cssIconDesc, c.cssIconNone ], aria = [ 'ascending', 'descending' ], // find the footer - $headers = c.$table + $extras = c.$table .find( 'tfoot tr' ) .children( 'td, th' ) .add( $( c.namespace + '_extra_headers' ) ) - .removeClass( css.join( ' ' ) ); - // remove all header information - c.$headers - .add( $( 'thead ' + c.namespace + '_extra_headers' ) ) - .removeClass( css.join( ' ' ) ) - .addClass( none ) - .attr( 'aria-sort', 'none' ) + .removeClass( css.join( ' ' ) ), + // remove all header information + $sorted = c.$headers + .add( $( 'thead ' + c.namespace + '_extra_headers' ) ) + .removeClass( css.join( ' ' ) ) + .addClass( none ) + .attr( 'aria-sort', 'none' ) + .find( '.' + ts.css.icon ) + .removeClass( cssIcon.join( ' ' ) ) + .end(); + // add css none to all sortable headers + $sorted + .not( '.sorter-false' ) .find( '.' + ts.css.icon ) - .removeClass( cssIcon.join( ' ' ) ) .addClass( cssIcon[ 2 ] ); + // add disabled css icon class + if ( c.cssIconDisabled ) { + $sorted + .filter( '.sorter-false' ) + .find( '.' + ts.css.icon ) + .addClass( c.cssIconDisabled ); + } for ( indx = 0; indx < len; indx++ ) { // direction = 2 means reset! if ( list[ indx ][ 1 ] !== 2 ) { @@ -1150,8 +1163,8 @@ } } // add sorted class to footer & extra headers, if they exist - if ( $headers.length ) { - $headers + if ( $extras.length ) { + $extras .filter( '[data-column="' + list[ indx ][ 0 ] + '"]' ) .removeClass( none ) .addClass( css[ list[ indx ][ 1 ] ] ); @@ -1824,10 +1837,10 @@ }, // Natural sort - https://github.com/overset/javascript-natural-sort (date sorting removed) - // this function will only accept strings, or you'll see 'TypeError: undefined is not a function' - // I could add a = a.toString(); b = b.toString(); but it'll slow down the sort overall sortNatural : function( a, b ) { if ( a === b ) { return 0; } + a = a.toString(); + b = b.toString(); var aNum, bNum, aFloat, bFloat, indx, max, regex = ts.regex; // first try and sort Hex codes @@ -5152,7 +5165,7 @@ })( jQuery ); -/*! Widget: stickyHeaders - updated 1/6/2017 (v2.28.4) *//* +/*! Widget: stickyHeaders - updated 6/2/2017 (v2.28.13) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -5209,6 +5222,13 @@ }, options.timer); }; + function getStickyOffset(c, wo) { + var $el = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : []; + return $el.length ? + $el.height() || 0 : + parseInt(wo.stickyHeaders_offset, 10) || 0; + } + // Sticky headers based on this awesome article: // http://css-tricks.com/13465-persistent-headers/ // and https://github.com/jmosbech/StickyTableHeaders by Jonas Mosbech @@ -5245,8 +5265,7 @@ $thead = $table.children('thead:first'), $header = $thead.children('tr').not('.sticky-false').children(), $tfoot = $table.children('tfoot'), - $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '', - stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, + stickyOffset = getStickyOffset(c, wo), // is this table nested? If so, find parent sticky header wrapper (div, not table) $nestedSticky = $table.parent().closest('.' + ts.css.table).hasClass('hasStickyHeaders') ? $table.parent().closest('table.tablesorter')[0].config.widgetOptions.$sticky.parent() : [], @@ -5268,7 +5287,6 @@ $stickyThead = $stickyTable.children('thead:first'), $stickyCells, laststate = '', - spacing = 0, setWidth = function($orig, $clone){ var index, width, border, $cell, $this, $cells = $orig.filter(':visible'), @@ -5300,11 +5318,9 @@ } }, resizeHeader = function() { - stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0; - spacing = 0; $stickyWrap.css({ left : $attach.length ? parseInt($attach.css('padding-left'), 10) || 0 : - $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing, + $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft(), width: $table.outerWidth() }); setWidth( $table, $stickyTable ); @@ -5315,6 +5331,7 @@ // Detect nested tables - fixes #724 nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; var offset = $table.offset(), + stickyOffset = getStickyOffset(c, wo), yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 xWindow = $.isWindow( $xScroll[0] ), attachTop = $attach.length ? @@ -5330,11 +5347,9 @@ } if (xWindow) { // adjust when scrolling horizontally - fixes issue #143 - cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing; - } - if ($nestedSticky.length) { - cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; + cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft(); } + cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; $stickyWrap .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) @@ -5434,6 +5449,8 @@ }); } + // make sure sticky is visible if page is partially scrolled + scrollSticky( true ); $table.triggerHandler('stickyHeadersInit'); }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 262d82f..a5b632a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.28.12 *//* +/*! TableSorter (FORK) v2.28.13 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.12', + version : '2.28.13', parsers : [], widgets : [], @@ -98,6 +98,7 @@ cssIconNone : '', // class name added to the icon when there is no column sort cssIconAsc : '', // class name added to the icon when the column has an ascending sort cssIconDesc : '', // class name added to the icon when the column has a descending sort + cssIconDisabled : '', // class name added to the icon when the column has a disabled sort // *** events pointerClick : 'click', @@ -1074,7 +1075,7 @@ ▀████▀ ██ █████▀ ██ ██ ██ ██████ */ setHeadersCss : function( c ) { - var $sorted, indx, column, + var indx, column, list = c.sortList, len = list.length, none = ts.css.sortNone + ' ' + c.cssNone, @@ -1082,20 +1083,32 @@ cssIcon = [ c.cssIconAsc, c.cssIconDesc, c.cssIconNone ], aria = [ 'ascending', 'descending' ], // find the footer - $headers = c.$table + $extras = c.$table .find( 'tfoot tr' ) .children( 'td, th' ) .add( $( c.namespace + '_extra_headers' ) ) - .removeClass( css.join( ' ' ) ); - // remove all header information - c.$headers - .add( $( 'thead ' + c.namespace + '_extra_headers' ) ) - .removeClass( css.join( ' ' ) ) - .addClass( none ) - .attr( 'aria-sort', 'none' ) + .removeClass( css.join( ' ' ) ), + // remove all header information + $sorted = c.$headers + .add( $( 'thead ' + c.namespace + '_extra_headers' ) ) + .removeClass( css.join( ' ' ) ) + .addClass( none ) + .attr( 'aria-sort', 'none' ) + .find( '.' + ts.css.icon ) + .removeClass( cssIcon.join( ' ' ) ) + .end(); + // add css none to all sortable headers + $sorted + .not( '.sorter-false' ) .find( '.' + ts.css.icon ) - .removeClass( cssIcon.join( ' ' ) ) .addClass( cssIcon[ 2 ] ); + // add disabled css icon class + if ( c.cssIconDisabled ) { + $sorted + .filter( '.sorter-false' ) + .find( '.' + ts.css.icon ) + .addClass( c.cssIconDisabled ); + } for ( indx = 0; indx < len; indx++ ) { // direction = 2 means reset! if ( list[ indx ][ 1 ] !== 2 ) { @@ -1132,8 +1145,8 @@ } } // add sorted class to footer & extra headers, if they exist - if ( $headers.length ) { - $headers + if ( $extras.length ) { + $extras .filter( '[data-column="' + list[ indx ][ 0 ] + '"]' ) .removeClass( none ) .addClass( css[ list[ indx ][ 1 ] ] ); @@ -1806,10 +1819,10 @@ }, // Natural sort - https://github.com/overset/javascript-natural-sort (date sorting removed) - // this function will only accept strings, or you'll see 'TypeError: undefined is not a function' - // I could add a = a.toString(); b = b.toString(); but it'll slow down the sort overall sortNatural : function( a, b ) { if ( a === b ) { return 0; } + a = a.toString(); + b = b.toString(); var aNum, bNum, aFloat, bFloat, indx, max, regex = ts.regex; // first try and sort Hex codes diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index cba9542..ed3db84 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 05-26-2017 (v2.28.12)*/ +/*! tablesorter (FORK) - updated 06-02-2017 (v2.28.13)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -2332,7 +2332,7 @@ })( jQuery ); -/*! Widget: stickyHeaders - updated 1/6/2017 (v2.28.4) *//* +/*! Widget: stickyHeaders - updated 6/2/2017 (v2.28.13) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -2389,6 +2389,13 @@ }, options.timer); }; + function getStickyOffset(c, wo) { + var $el = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : []; + return $el.length ? + $el.height() || 0 : + parseInt(wo.stickyHeaders_offset, 10) || 0; + } + // Sticky headers based on this awesome article: // http://css-tricks.com/13465-persistent-headers/ // and https://github.com/jmosbech/StickyTableHeaders by Jonas Mosbech @@ -2425,8 +2432,7 @@ $thead = $table.children('thead:first'), $header = $thead.children('tr').not('.sticky-false').children(), $tfoot = $table.children('tfoot'), - $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '', - stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, + stickyOffset = getStickyOffset(c, wo), // is this table nested? If so, find parent sticky header wrapper (div, not table) $nestedSticky = $table.parent().closest('.' + ts.css.table).hasClass('hasStickyHeaders') ? $table.parent().closest('table.tablesorter')[0].config.widgetOptions.$sticky.parent() : [], @@ -2448,7 +2454,6 @@ $stickyThead = $stickyTable.children('thead:first'), $stickyCells, laststate = '', - spacing = 0, setWidth = function($orig, $clone){ var index, width, border, $cell, $this, $cells = $orig.filter(':visible'), @@ -2480,11 +2485,9 @@ } }, resizeHeader = function() { - stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0; - spacing = 0; $stickyWrap.css({ left : $attach.length ? parseInt($attach.css('padding-left'), 10) || 0 : - $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing, + $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft(), width: $table.outerWidth() }); setWidth( $table, $stickyTable ); @@ -2495,6 +2498,7 @@ // Detect nested tables - fixes #724 nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; var offset = $table.offset(), + stickyOffset = getStickyOffset(c, wo), yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 xWindow = $.isWindow( $xScroll[0] ), attachTop = $attach.length ? @@ -2510,11 +2514,9 @@ } if (xWindow) { // adjust when scrolling horizontally - fixes issue #143 - cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing; - } - if ($nestedSticky.length) { - cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; + cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft(); } + cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; $stickyWrap .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) @@ -2614,6 +2616,8 @@ }); } + // make sure sticky is visible if page is partially scrolled + scrollSticky( true ); $table.triggerHandler('stickyHeadersInit'); }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js index b353b27..12447b6 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js @@ -1,4 +1,4 @@ -/*! Widget: stickyHeaders - updated 1/6/2017 (v2.28.4) *//* +/*! Widget: stickyHeaders - updated 6/2/2017 (v2.28.13) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -55,6 +55,13 @@ }, options.timer); }; + function getStickyOffset(c, wo) { + var $el = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : []; + return $el.length ? + $el.height() || 0 : + parseInt(wo.stickyHeaders_offset, 10) || 0; + } + // Sticky headers based on this awesome article: // http://css-tricks.com/13465-persistent-headers/ // and https://github.com/jmosbech/StickyTableHeaders by Jonas Mosbech @@ -91,8 +98,7 @@ $thead = $table.children('thead:first'), $header = $thead.children('tr').not('.sticky-false').children(), $tfoot = $table.children('tfoot'), - $stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '', - stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0, + stickyOffset = getStickyOffset(c, wo), // is this table nested? If so, find parent sticky header wrapper (div, not table) $nestedSticky = $table.parent().closest('.' + ts.css.table).hasClass('hasStickyHeaders') ? $table.parent().closest('table.tablesorter')[0].config.widgetOptions.$sticky.parent() : [], @@ -114,7 +120,6 @@ $stickyThead = $stickyTable.children('thead:first'), $stickyCells, laststate = '', - spacing = 0, setWidth = function($orig, $clone){ var index, width, border, $cell, $this, $cells = $orig.filter(':visible'), @@ -146,11 +151,9 @@ } }, resizeHeader = function() { - stickyOffset = $stickyOffset.length ? $stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0; - spacing = 0; $stickyWrap.css({ left : $attach.length ? parseInt($attach.css('padding-left'), 10) || 0 : - $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing, + $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft(), width: $table.outerWidth() }); setWidth( $table, $stickyTable ); @@ -161,6 +164,7 @@ // Detect nested tables - fixes #724 nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; var offset = $table.offset(), + stickyOffset = getStickyOffset(c, wo), yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 xWindow = $.isWindow( $xScroll[0] ), attachTop = $attach.length ? @@ -176,11 +180,9 @@ } if (xWindow) { // adjust when scrolling horizontally - fixes issue #143 - cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft() - spacing; - } - if ($nestedSticky.length) { - cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; + cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft(); } + cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; $stickyWrap .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) @@ -280,6 +282,8 @@ }); } + // make sure sticky is visible if page is partially scrolled + scrollSticky( true ); $table.triggerHandler('stickyHeadersInit'); }, From 6f01d0fd4ee9f04afec929ec6ea96ba72e05e2ae Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Sun, 9 Jul 2017 19:53:06 +0200 Subject: [PATCH 116/138] Update tablesorter to latest version (2.28.15) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 20 +++++++--- .../jquery.tablesorter.combined.js | 39 +++++++++++++++++-- .../jquery-tablesorter/jquery.tablesorter.js | 37 +++++++++++++++++- .../jquery.tablesorter.widgets.js | 2 +- .../widgets/widget-pager.js | 16 +++++--- 9 files changed, 105 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b84bc0..2812dc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.23.14 (2017-07-09) + +* Upgrade tablesorter to v2.28.15 + #### v1.23.13 (2017-06-06) * Upgrade tablesorter to v2.28.13 diff --git a/README.md b/README.md index 72ac2cd..ea4b681 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.28.13 (6/2/2017) [documentation] +Current tablesorter version: 2.28.15 (7/4/2017) [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 216303a..df94c97 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 23 - TINY = 13 + TINY = 14 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 8dbd34e..d398c91 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 8dbd34ece690fd488b9ec5472ee070a25def3a53 +Subproject commit d398c91f0fd974efbcb289e86a2ebfa331b4913d diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 71dbee8..16a614d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -321,16 +321,22 @@ }, fixHeight = function(table, p) { - var d, h, + var d, h, bs, c = table.config, $b = c.$tbodies.eq(0); $b.find('tr.pagerSavedHeightSpacer').remove(); if (p.fixedHeight && !p.isDisabled) { h = $.data(table, 'pagerSavedHeight'); if (h) { - d = h - $b.height(); - if ( d > 5 && $.data(table, 'pagerLastSize') === p.size && - $b.children('tr:visible').length < (p.size === 'all' ? p.totalRows : p.size) ) { + bs = 0; + if ($(table).css('border-spacing').split(' ').length > 1) { + bs = $(table).css('border-spacing').split(' ')[1].replace(/[^-\d\.]/g, ''); + } + d = h - $b.height() + (bs * p.size) - bs; + if ( + d > 5 && $.data(table, 'pagerLastSize') === p.size && + $b.children('tr:visible').length < (p.size === 'all' ? p.totalRows : p.size) + ) { $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice(1) + '" style="height:' + d + 'px;"></tr>'); } } @@ -969,6 +975,10 @@ .bind('filterInit filterStart '.split(' ').join(namespace + ' '), function(e, filters) { p.currentFilters = $.isArray(filters) ? filters : c.$table.data('lastSearch'); var filtersEqual; + if (p.ajax && e.type === 'filterInit') { + // ensure pager ajax is called after filter widget has initialized + return moveToPage( table, p, false ); + } if (ts.filter.equalFilters) { filtersEqual = ts.filter.equalFilters(c, c.lastSearch, p.currentFilters); } else { @@ -1188,7 +1198,7 @@ valid = true, message = '', removeRow = function(){ - c.$table.find( 'thead' ).find( '.' + errorRow ).remove(); + c.$table.find( 'thead' ).find( c.selectorRemove ).remove(); }; if ( !$table.length ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 479ecbf..2df96a0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 06-02-2017 (v2.28.13)*/ +/*! tablesorter (FORK) - updated 06-08-2017 (v2.28.14)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.28.13 *//* +/*! TableSorter (FORK) v2.28.14 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.13', + version : '2.28.14', parsers : [], widgets : [], @@ -2293,9 +2293,42 @@ } } } + ts.checkColumnCount($rows, matrix, matrixrow.length); return matrixrow.length; }, + checkColumnCount : function($rows, matrix, columns) { + // this DOES NOT report any tbody column issues, except for the math and + // and column selector widgets + var i, len, + valid = true, + cells = []; + for ( i = 0; i < matrix.length; i++ ) { + // some matrix entries are undefined when testing the footer because + // it is using the rowIndex property + if ( matrix[i] ) { + len = matrix[i].length; + if ( matrix[i].length !== columns ) { + valid = false; + break; + } + } + } + if ( !valid ) { + $rows.each( function( indx, el ) { + var cell = el.parentElement.nodeName; + if ( cells.indexOf( cell ) ) { + cells.push( cell ); + } + }); + console.error( + 'Invalid or incorrect number of columns in the ' + + cells.join( ' or ' ) + '; expected ' + columns + + ', but found ' + len + ' columns' + ); + } + }, + // automatically add a colgroup with col elements set to a percentage width fixColumnWidth : function( table ) { table = $( table )[ 0 ]; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index a5b632a..ae09696 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.28.13 *//* +/*! TableSorter (FORK) v2.28.14 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.13', + version : '2.28.14', parsers : [], widgets : [], @@ -2275,9 +2275,42 @@ } } } + ts.checkColumnCount($rows, matrix, matrixrow.length); return matrixrow.length; }, + checkColumnCount : function($rows, matrix, columns) { + // this DOES NOT report any tbody column issues, except for the math and + // and column selector widgets + var i, len, + valid = true, + cells = []; + for ( i = 0; i < matrix.length; i++ ) { + // some matrix entries are undefined when testing the footer because + // it is using the rowIndex property + if ( matrix[i] ) { + len = matrix[i].length; + if ( matrix[i].length !== columns ) { + valid = false; + break; + } + } + } + if ( !valid ) { + $rows.each( function( indx, el ) { + var cell = el.parentElement.nodeName; + if ( cells.indexOf( cell ) ) { + cells.push( cell ); + } + }); + console.error( + 'Invalid or incorrect number of columns in the ' + + cells.join( ' or ' ) + '; expected ' + columns + + ', but found ' + len + ' columns' + ); + } + }, + // automatically add a colgroup with col elements set to a percentage width fixColumnWidth : function( table ) { table = $( table )[ 0 ]; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index ed3db84..85715a0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 06-02-2017 (v2.28.13)*/ +/*! tablesorter (FORK) - updated 06-08-2017 (v2.28.14)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 422c9d2..d2ccf0f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -611,7 +611,7 @@ }, fixHeight: function( c ) { - var d, h, + var d, h, bs, table = c.table, p = c.pager, wo = c.widgetOptions, @@ -620,9 +620,15 @@ if ( wo.pager_fixedHeight && !p.isDisabled ) { h = $.data( table, 'pagerSavedHeight' ); if ( h ) { - d = h - $b.height(); - if ( d > 5 && $.data( table, 'pagerLastSize' ) === p.size && - $b.children( 'tr:visible' ).length < ( p.size === 'all' ? p.totalRows : p.size ) ) { + bs = 0; + if ( $(table).css('border-spacing').split(' ').length > 1 ) { + bs = $(table).css('border-spacing').split(' ')[1].replace( /[^-\d\.]/g, '' ); + } + d = h - $b.height() + (bs * p.size) - bs; + if ( + d > 5 && $.data( table, 'pagerLastSize' ) === p.size && + $b.children( 'tr:visible' ).length < ( p.size === 'all' ? p.totalRows : p.size ) + ) { $b.append( '<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.slice( 1 ) + '" style="height:' + d + 'px;"></tr>' ); } @@ -1301,7 +1307,7 @@ valid = true, message = '', removeRow = function() { - c.$table.find( 'thead' ).find( '.' + errorRow ).remove(); + c.$table.find( 'thead' ).find( c.selectorRemove ).remove(); }; if ( !$table.length ) { From db53fbf48a42115d659bd8cc7bb7a06d07852fea Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Sun, 9 Jul 2017 20:01:28 +0200 Subject: [PATCH 117/138] Fix 2.28.14 instead of 2.28.15 asset data --- CHANGELOG.md | 4 + lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 22 ++-- .../jquery-tablesorter/jquery.tablesorter.js | 8 +- .../jquery.tablesorter.widgets.js | 14 +- .../widgets/widget-columnSelector.js | 7 +- .../widgets/widget-filter-formatter-jui.js | 122 +++++++++--------- .../widget-filter-formatter-select2.js | 26 ++-- .../widgets/widget-filter.js | 12 +- .../widgets/widget-sort2Hash.js | 17 ++- 11 files changed, 138 insertions(+), 98 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2812dc9..2227b16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.23.15 (2017-07-09) + +* Hotfix for adding v2.28.14 instead of v2.28.15 asset data in previous release + #### v1.23.14 (2017-07-09) * Upgrade tablesorter to v2.28.15 diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index df94c97..7b94a01 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 23 - TINY = 14 + TINY = 15 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index d398c91..c1c55c5 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit d398c91f0fd974efbcb289e86a2ebfa331b4913d +Subproject commit c1c55c56886b20d774f2007338d85fb2535d3148 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 2df96a0..5668d04 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 06-08-2017 (v2.28.14)*/ +/*! tablesorter (FORK) - updated 07-04-2017 (v2.28.15)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.28.14 *//* +/*! TableSorter (FORK) v2.28.15 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.14', + version : '2.28.15', parsers : [], widgets : [], @@ -2258,7 +2258,7 @@ cells = $rows[ i ].cells; for ( j = 0; j < cells.length; j++ ) { cell = cells[ j ]; - rowIndex = cell.parentNode.rowIndex; + rowIndex = i; rowSpan = cell.rowSpan || 1; colSpan = cell.colSpan || 1; if ( typeof matrix[ rowIndex ] === 'undefined' ) { @@ -2317,7 +2317,7 @@ if ( !valid ) { $rows.each( function( indx, el ) { var cell = el.parentElement.nodeName; - if ( cells.indexOf( cell ) ) { + if ( cells.indexOf( cell ) < 0 ) { cells.push( cell ); } }); @@ -3274,7 +3274,7 @@ })(jQuery); -/*! Widget: filter - updated 5/24/2017 (v2.28.11) *//* +/*! Widget: filter - updated 7/4/2017 (v2.28.15) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3345,8 +3345,10 @@ var tbodyIndex, $tbody, $table = c.$table, $tbodies = c.$tbodies, - events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterAndSortReset filterEnd search ' - .split( ' ' ).join( c.namespace + 'filter ' ); + events = ( + 'addRows updateCell update updateRows updateComplete appendCache filterReset ' + + 'filterAndSortReset filterFomatterUpdate filterEnd search stickyHeadersInit ' + ).split( ' ' ).join( c.namespace + 'filter ' ); $table .removeClass( 'hasFilters' ) // add filter namespace to all BUT search @@ -3875,7 +3877,9 @@ // so we have to work with it instead formatterUpdated: function( $cell, column ) { // prevent error if $cell is undefined - see #1056 - var wo = $cell && $cell.closest( 'table' )[0].config.widgetOptions; + var $table = $cell && $cell.closest( 'table' ); + var config = $table.length && $table[0].config, + wo = config && config.widgetOptions; if ( wo && !wo.filter_initialized ) { // add updates by column since this function // may be called numerous times before initialization diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index ae09696..f83a1d1 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.28.14 *//* +/*! TableSorter (FORK) v2.28.15 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.14', + version : '2.28.15', parsers : [], widgets : [], @@ -2240,7 +2240,7 @@ cells = $rows[ i ].cells; for ( j = 0; j < cells.length; j++ ) { cell = cells[ j ]; - rowIndex = cell.parentNode.rowIndex; + rowIndex = i; rowSpan = cell.rowSpan || 1; colSpan = cell.colSpan || 1; if ( typeof matrix[ rowIndex ] === 'undefined' ) { @@ -2299,7 +2299,7 @@ if ( !valid ) { $rows.each( function( indx, el ) { var cell = el.parentElement.nodeName; - if ( cells.indexOf( cell ) ) { + if ( cells.indexOf( cell ) < 0 ) { cells.push( cell ); } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 85715a0..c2c84d9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 06-08-2017 (v2.28.14)*/ +/*! tablesorter (FORK) - updated 07-04-2017 (v2.28.15)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -408,7 +408,7 @@ })(jQuery); -/*! Widget: filter - updated 5/24/2017 (v2.28.11) *//* +/*! Widget: filter - updated 7/4/2017 (v2.28.15) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -479,8 +479,10 @@ var tbodyIndex, $tbody, $table = c.$table, $tbodies = c.$tbodies, - events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterAndSortReset filterEnd search ' - .split( ' ' ).join( c.namespace + 'filter ' ); + events = ( + 'addRows updateCell update updateRows updateComplete appendCache filterReset ' + + 'filterAndSortReset filterFomatterUpdate filterEnd search stickyHeadersInit ' + ).split( ' ' ).join( c.namespace + 'filter ' ); $table .removeClass( 'hasFilters' ) // add filter namespace to all BUT search @@ -1009,7 +1011,9 @@ // so we have to work with it instead formatterUpdated: function( $cell, column ) { // prevent error if $cell is undefined - see #1056 - var wo = $cell && $cell.closest( 'table' )[0].config.widgetOptions; + var $table = $cell && $cell.closest( 'table' ); + var config = $table.length && $table[0].config, + wo = config && config.widgetOptions; if ( wo && !wo.filter_initialized ) { // add updates by column since this function // may be called numerous times before initialization diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index 6abdb41..c67ff36 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Widget: columnSelector (responsive table widget) - updated 5/25/2017 (v2.28.12) *//* +/* Widget: columnSelector (responsive table widget) - updated 7/4/2017 (v2.28.15) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -547,6 +547,11 @@ csel.$style.remove(); csel.$breakpoints.remove(); $( c.namespace + 'columnselectorHasSpan' ).removeClass( wo.filter_filteredRow || 'filtered' ); + c.$table.find('[data-col-span]').each(function(indx, el) { + var $el = $(el); + console.log($el, $el.attr('data-col-span')); + $el.attr('colspan', $el.attr('data-col-span')); + }); c.$table.off('updateAll' + namespace + ' update' + namespace); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js index 78fbbe0..9b933f0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js @@ -89,18 +89,20 @@ chkd = $cell.find('.toggle').is(':checked'); } state = o.disabled || !chkd ? 'disable' : 'enable'; - $cell.find('.filter') - // add equal to the beginning, so we filter exact numbers - .val( chkd ? (compare ? compare : o.exactMatch ? '=' : '') + v : '' ) - .trigger( notrigger ? '' : 'search', searchType ).end() - .find('.spinner').spinner(state).val(v); - // update sticky header cell - if ($shcell.length) { - $shcell - .find('.spinner').spinner(state).val(v).end() - .find(compareSelect).val( compare ); - if (o.addToggle) { - $shcell.find('.toggle')[0].checked = chkd; + if (!ts.isEmptyObject($cell.find('.spinner').data())) { + $cell.find('.filter') + // add equal to the beginning, so we filter exact numbers + .val( chkd ? (compare ? compare : o.exactMatch ? '=' : '') + v : '' ) + .trigger( notrigger ? '' : 'search', searchType ).end() + .find('.spinner').spinner(state).val(v); + // update sticky header cell + if ($shcell.length) { + $shcell + .find('.spinner').spinner(state).val(v).end() + .find(compareSelect).val( compare ); + if (o.addToggle) { + $shcell.find('.toggle')[0].checked = chkd; + } } } }; @@ -137,7 +139,7 @@ }); // update spinner from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ + c.$table.bind('filterFomatterUpdate' + c.namespace + 'filter', function(){ var val = tsff.updateCompare($cell, $input, o)[0]; $cell.find('.spinner').val( val ); updateSpinner({ value: val }, true); @@ -153,7 +155,7 @@ } // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ + c.$table.bind('stickyHeadersInit' + c.namespace + 'filter', function(){ $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); if (o.addToggle) { $('<div class="button"><input id="stickyuispinnerbutton' + indx + '" type="checkbox" class="toggle" />' + @@ -187,7 +189,7 @@ }); // on reset - c.$table.bind('filterReset', function(){ + c.$table.bind('filterReset' + c.namespace + 'filter', function(){ if ($.isArray(o.compare)) { $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); } @@ -250,24 +252,26 @@ // add values to the handle data-value attribute so the css tooltip will work properly $cell.find('.ui-slider-handle').addClass('value-popup').attr('data-value', result); } - // update the hidden input; - // ****** ADD AN EQUAL SIGN TO THE BEGINNING! <- this makes the slide exactly match the number ****** - // when the value is at the minimum, clear the hidden input so all rows will be seen - - $cell.find('.filter') - .val( ( compare ? compare + v : v === o.min ? '' : (o.exactMatch ? '=' : '') + v ) ) - .trigger( notrigger ? '' : 'search', searchType ).end() - .find('.slider').slider('value', v); - - // update sticky header cell - if ($shcell.length) { - $shcell - .find(compareSelect).val( compare ).end() + // prevent JS error if "resetToLoadState" or filter widget was removed for another reason + if (!ts.isEmptyObject($cell.find('.slider').data())) { + // update the hidden input; + $cell.find('.filter') + // ****** ADD AN EQUAL SIGN TO THE BEGINNING! <- this makes the slide exactly match the number ****** + // when the value is at the minimum, clear the hidden input so all rows will be seen + .val( ( compare ? compare + v : v === o.min ? '' : (o.exactMatch ? '=' : '') + v ) ) + .trigger( notrigger ? '' : 'search', searchType ).end() .find('.slider').slider('value', v); - if (o.valueToHeader) { - $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(' (' + result + ')'); - } else { - $shcell.find('.ui-slider-handle').addClass('value-popup').attr('data-value', result); + + // update sticky header cell + if ($shcell.length) { + $shcell + .find(compareSelect).val( compare ).end() + .find('.slider').slider('value', v); + if (o.valueToHeader) { + $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(' (' + result + ')'); + } else { + $shcell.find('.ui-slider-handle').addClass('value-popup').attr('data-value', result); + } } } @@ -296,7 +300,7 @@ .slider(o); // update slider from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ + c.$table.bind('filterFomatterUpdate' + c.namespace + 'filter', function(){ var val = tsff.updateCompare($cell, $input, o)[0]; $cell.find('.slider').slider('value', val ); updateSlider({ value: val }, false); @@ -312,7 +316,7 @@ } // on reset - c.$table.bind('filterReset', function(){ + c.$table.bind('filterReset' + c.namespace + 'filter', function(){ if ($.isArray(o.compare)) { $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); } @@ -322,7 +326,7 @@ }); // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ + c.$table.bind('stickyHeadersInit' + c.namespace + 'filter', function(){ $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); // add a jQuery UI slider! @@ -401,20 +405,22 @@ .eq(0).attr('data-value', val[0]).end() // adding value to data attribute .eq(1).attr('data-value', val[1]); // value popup shown via css } - // update the hidden input - $cell.find('.filter').val(range) - .trigger(notrigger ? '' : 'search', searchType).end() - .find('.range').slider('values', val); - // update sticky header cell - if ($shcell.length) { - $shcell.find('.range').slider('values', val); - if (o.valueToHeader) { - $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.currange').html(' (' + result + ')'); - } else { - $shcell.find('.ui-slider-handle') - .addClass('value-popup') - .eq(0).attr('data-value', val[0]).end() // adding value to data attribute - .eq(1).attr('data-value', val[1]); // value popup shown via css + if (!ts.isEmptyObject($cell.find('.range').data())) { + // update the hidden input + $cell.find('.filter').val(range) + .trigger(notrigger ? '' : 'search', searchType).end() + .find('.range').slider('values', val); + // update sticky header cell + if ($shcell.length) { + $shcell.find('.range').slider('values', val); + if (o.valueToHeader) { + $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.currange').html(' (' + result + ')'); + } else { + $shcell.find('.ui-slider-handle') + .addClass('value-popup') + .eq(0).attr('data-value', val[0]).end() // adding value to data attribute + .eq(1).attr('data-value', val[1]); // value popup shown via css + } } } @@ -443,13 +449,13 @@ .slider(o); // update slider from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ + c.$table.bind('filterFomatterUpdate' + c.namespace + 'filter', function(){ getRange(); ts.filter.formatterUpdated($cell, indx); }); // on reset - c.$table.bind('filterReset', function(){ + c.$table.bind('filterReset' + c.namespace + 'filter', function(){ $cell.find('.range').slider('values', o.values); setTimeout(function(){ updateUiRange(); @@ -457,7 +463,7 @@ }); // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ + c.$table.bind('stickyHeadersInit' + c.namespace + 'filter', function(){ $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); // add a jQuery UI slider! @@ -552,7 +558,7 @@ $date.datepicker(o); // on reset - c.$table.bind('filterReset', function(){ + c.$table.bind('filterReset' + c.namespace + 'filter', function(){ if ($.isArray(o.compare)) { $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); } @@ -563,7 +569,7 @@ }); // update date compare from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ + c.$table.bind('filterFomatterUpdate' + c.namespace + 'filter', function(){ var num, v = $input.val(); if (/\s+-\s+/.test(v)) { // date range found; assume an exact match on one day @@ -591,7 +597,7 @@ } // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ + c.$table.bind('stickyHeadersInit' + c.namespace + 'filter', function(){ $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); // add a jQuery datepicker! @@ -702,7 +708,7 @@ $cell.find('.dateTo').datepicker(o); // update date compare from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ + c.$table.bind('filterFomatterUpdate' + c.namespace + 'filter', function(){ var val = $input.val() || '', from = '', to = ''; @@ -733,7 +739,7 @@ }); // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ + c.$table.bind('stickyHeadersInit' + c.namespace + 'filter', function(){ $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); $shcell.append(t); @@ -747,7 +753,7 @@ }); // on reset - $cell.closest('table').bind('filterReset', function(){ + $cell.closest('table').bind('filterReset' + c.namespace + 'filter', function(){ $cell.add($shcell).find('.dateFrom').val('').datepicker('setDate', o.from || null ); $cell.add($shcell).find('.dateTo').val('').datepicker('setDate', o.to || null ); setTimeout(function(){ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js index cb51791..e1eeee0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js @@ -70,18 +70,20 @@ if (arry) { v = v.split('\u0000'); } - $input - // add regex, so we filter exact numbers - .val( - $.isArray(v) && v.length && v.join('') !== '' ? - '/(' + matchPrefix + (v || []).join(matchSuffix + '|' + matchPrefix) + matchSuffix + ')/' + flags : - '' - ) - .trigger('search').end() - .find('.select2').select2('val', v); - // update sticky header cell - if (c.widgetOptions.$sticky) { - c.widgetOptions.$sticky.find('.select2col' + indx + ' .select2').select2('val', v); + if (!ts.isEmptyObject($input.find('.select2').data())) { + $input + // add regex, so we filter exact numbers + .val( + $.isArray(v) && v.length && v.join('') !== '' ? + '/(' + matchPrefix + (v || []).join(matchSuffix + '|' + matchPrefix) + matchSuffix + ')/' + flags : + '' + ) + .trigger('search').end() + .find('.select2').select2('val', v); + // update sticky header cell + if (c.widgetOptions.$sticky) { + c.widgetOptions.$sticky.find('.select2col' + indx + ' .select2').select2('val', v); + } } }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 816ce2b..5a5b760 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 5/24/2017 (v2.28.11) *//* +/*! Widget: filter - updated 7/4/2017 (v2.28.15) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -69,8 +69,10 @@ var tbodyIndex, $tbody, $table = c.$table, $tbodies = c.$tbodies, - events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterAndSortReset filterEnd search ' - .split( ' ' ).join( c.namespace + 'filter ' ); + events = ( + 'addRows updateCell update updateRows updateComplete appendCache filterReset ' + + 'filterAndSortReset filterFomatterUpdate filterEnd search stickyHeadersInit ' + ).split( ' ' ).join( c.namespace + 'filter ' ); $table .removeClass( 'hasFilters' ) // add filter namespace to all BUT search @@ -599,7 +601,9 @@ // so we have to work with it instead formatterUpdated: function( $cell, column ) { // prevent error if $cell is undefined - see #1056 - var wo = $cell && $cell.closest( 'table' )[0].config.widgetOptions; + var $table = $cell && $cell.closest( 'table' ); + var config = $table.length && $table[0].config, + wo = config && config.widgetOptions; if ( wo && !wo.filter_initialized ) { // add updates by column since this function // may be called numerous times before initialization diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js index 7429bf8..cbb12c6 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js @@ -1,4 +1,4 @@ -/*! Widget: sort2Hash (BETA) - updated 4/2/2017 (v2.28.6) */ +/*! Widget: sort2Hash (BETA) - updated 7/4/2017 (v2.28.15) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -26,10 +26,21 @@ filter = filter.split( wo.sort2Hash_separator ); c.$table.one( 'tablesorter-ready', function() { setTimeout(function(){ - c.$table.one( 'filterEnd', function(){ + c.$table.one( 'filterEnd', function() { $(this).triggerHandler( 'pageAndSize', [ page, size ] ); }); - $.tablesorter.setFilters( table, filter, true ); + // use the newest filter comparison code + if ( ts.filter.equalFilters ) { + temp = ts.filter.equalFilters( c, c.lastSearch, filter ); + } else { + // quick n' dirty comparison... it will miss filter changes of + // the same value in a different column, see #1363 + temp = ( c.lastSearch || [] ).join( '' ) !== ( filter || [] ).join( '' ); + } + // don't set filters if they haven't changed + if ( !temp ) { + $.tablesorter.setFilters( table, filter, true ); + } }, 100 ); }); } From e4f5a2b08ff32cf850f01cdd9e388868d360ead7 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 9 Oct 2017 17:01:44 +0200 Subject: [PATCH 118/138] Update tablesorter to latest version (2.29.0) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 4 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 120 +++++++++++------- .../jquery-tablesorter/jquery.tablesorter.js | 71 +++++++---- .../jquery.tablesorter.widgets.js | 49 ++++--- .../widgets/widget-columnSelector.js | 32 +++-- .../widgets/widget-grouping.js | 11 +- .../widgets/widget-output.js | 16 ++- .../widgets/widget-resizable.js | 3 +- .../widgets/widget-scroller.js | 6 +- .../widgets/widget-sort2Hash.js | 26 +++- .../widgets/widget-stickyHeaders.js | 36 ++++-- .../widgets/widget-uitheme.js | 8 +- .../jquery-tablesorter/theme.bootstrap.css | 4 +- .../jquery-tablesorter/theme.bootstrap_3.css | 4 +- 17 files changed, 263 insertions(+), 135 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2227b16..0e440fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.24.0 (2017-10-09) + +* Upgrade tablesorter to v2.29.0 + #### v1.23.15 (2017-07-09) * Hotfix for adding v2.28.14 instead of v2.28.15 asset data in previous release diff --git a/README.md b/README.md index ea4b681..5337ac5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.28.15 (7/4/2017) [documentation] +Current tablesorter version: 2.29.0 (9/27/2017) [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 7b94a01..e4e9c39 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 - MINOR = 23 - TINY = 15 + MINOR = 24 + TINY = 0 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index c1c55c5..b6647fa 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit c1c55c56886b20d774f2007338d85fb2535d3148 +Subproject commit b6647faba58594c51ea9dc840ea6211e186c42ff diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 5668d04..116943f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 07-04-2017 (v2.28.15)*/ +/*! tablesorter (FORK) - updated 09-27-2017 (v2.29.0)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.28.15 *//* +/*! TableSorter (FORK) v2.29.0 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.15', + version : '2.29.0', parsers : [], widgets : [], @@ -439,10 +439,10 @@ e.stopPropagation(); ts.applyWidgetId( this, id ); }) - .bind( 'applyWidgets' + namespace, function( e, init ) { + .bind( 'applyWidgets' + namespace, function( e, callback ) { e.stopPropagation(); - // apply widgets - ts.applyWidget( this, init ); + // apply widgets (false = not initializing) + ts.applyWidget( this, false, callback ); }) .bind( 'refreshWidgets' + namespace, function( e, all, dontapply ) { e.stopPropagation(); @@ -479,9 +479,9 @@ downTarget = null; if ( core !== true ) { $headers.addClass( namespace.slice( 1 ) + '_extra_headers' ); - tmp = $.fn.closest ? $headers.closest( 'table' )[ 0 ] : $headers.parents( 'table' )[ 0 ]; - if ( tmp && tmp.nodeName === 'TABLE' && tmp !== table ) { - $( tmp ).addClass( namespace.slice( 1 ) + '_extra_table' ); + tmp = ts.getClosest( $headers, 'table' ); + if ( tmp.length && tmp[ 0 ].nodeName === 'TABLE' && tmp[ 0 ] !== table ) { + $( tmp[ 0 ] ).addClass( namespace.slice( 1 ) + '_extra_table' ); } } tmp = ( c.pointerDown + ' ' + c.pointerUp + ' ' + c.pointerClick + ' sort keyup ' ) @@ -533,8 +533,7 @@ ts.buildCache( c ); } // jQuery v1.2.6 doesn't have closest() - $cell = $.fn.closest ? $( this ).closest( 'th, td' ) : - /TH|TD/.test( this.nodeName ) ? $( this ) : $( this ).parents( 'th, td' ); + $cell = ts.getHeaderCell( $( this ) ); // reference original table headers and find the same cell // don't use $headers or IE8 throws an error - see #987 temp = $headers.index( $cell ); @@ -574,7 +573,7 @@ ''; // redefine c.$headers here in case of an updateAll that replaces or adds an entire header cell - see #683 c.$headers = $( $.map( c.$table.find( c.selectorHeaders ), function( elem, index ) { - var configHeaders, header, column, template, tmp, + var configHeaders, header, column, template, tmp, $th, $elem = $( elem ); // ignore cell (don't add it to c.$headers) if row has ignoreRow class if ( $elem.parent().hasClass( c.cssIgnoreRow ) ) { return; } @@ -600,7 +599,9 @@ if ( c.onRenderHeader ) { c.onRenderHeader.apply( $elem, [ index, c, c.$table ] ); } - column = parseInt( $elem.attr( 'data-column' ), 10 ); + // data-column stored on th or td only + $th = ts.getHeaderCell( $elem ); + column = parseInt( $th.attr( 'data-column' ), 10 ); elem.column = column; tmp = ts.getOrder( ts.getData( $elem, configHeaders, 'sortInitialOrder' ) || c.sortInitialOrder ); // this may get updated numerous times if there are multiple rows @@ -618,10 +619,9 @@ } // add cell to headerList c.headerList[ index ] = elem; + $elem.addClass( ts.css.header + ' ' + c.cssHeader ); // add to parent in case there are multiple rows - $elem - .addClass( ts.css.header + ' ' + c.cssHeader ) - .parent() + ts.getClosest( $elem, 'tr' ) .addClass( ts.css.headerRow + ' ' + c.cssHeaderRow ) .attr( 'role', 'row' ); // allow keyboard cursor to focus on element @@ -1179,10 +1179,29 @@ } }, + // This function does NOT return closest if the $el matches the selector + getClosest : function( $el, selector ) { + return $.fn.closest ? + $el.closest( selector ) : + $el.parents( selector ).filter( ':first' ); + }, + + getHeaderCell : function( $el ) { + // jQuery v1.2.6 doesn't have closest() + if ( $.fn.closest ) { + return $el.closest( 'th, td' ); + } + return /TH|TD/.test( $el[0].nodeName ) ? + $el : + $el.parents( 'th, td' ).filter( ':first' ); + }, + // nextSort (optional), lets you disable next sort text setColumnAriaLabel : function( c, $header, nextSort ) { if ( $header.length ) { - var column = parseInt( $header.attr( 'data-column' ), 10 ), + var $th = ts.getHeaderCell( $header ), + // data-column always stored on the th/td + column = parseInt( $th.attr( 'data-column' ), 10 ), vars = c.sortVars[ column ], tmp = $header.hasClass( ts.css.sortAsc ) ? 'sortAsc' : @@ -1340,10 +1359,9 @@ $cell = $( cell ), // update cache - format: function( s, table, cell, cellIndex ) // no closest in jQuery v1.2.6 - tbodyIndex = $tbodies - .index( $.fn.closest ? $cell.closest( 'tbody' ) : $cell.parents( 'tbody' ).filter( ':first' ) ), + tbodyIndex = $tbodies.index( ts.getClosest( $cell, 'tbody' ) ), tbcache = c.cache[ tbodyIndex ], - $row = $.fn.closest ? $cell.closest( 'tr' ) : $cell.parents( 'tr' ).filter( ':first' ); + $row = ts.getClosest( $cell, 'tr' ); cell = $cell[ 0 ]; // in case cell is a jQuery object // tbody may not exist if update is initialized while tbody is removed for processing if ( $tbodies.length && tbodyIndex >= 0 ) { @@ -1398,11 +1416,13 @@ if ( valid ) { $row = $( $row ); c.$tbodies.append( $row ); - } else if ( !$row || + } else if ( + !$row || // row is a jQuery object? !( $row instanceof jQuery ) || // row contained in the table? - ( $.fn.closest ? $row.closest( 'table' )[ 0 ] : $row.parents( 'table' )[ 0 ] ) !== c.table ) { + ( ts.getClosest( $row, 'table' )[ 0 ] !== c.table ) + ) { if ( c.debug ) { console.error( 'addRows method requires (1) a jQuery selector reference to rows that have already ' + 'been added to the table, or (2) row HTML string to be added to a table with only one tbody' ); @@ -1549,10 +1569,10 @@ notMultiSort = !event[ c.sortMultiSortKey ], table = c.table, len = c.$headers.length, - // get current column index - col = parseInt( $( cell ).attr( 'data-column' ), 10 ), + // get current column index; *always* stored on th/td + $th = ts.getHeaderCell( $( cell ) ), + col = parseInt( $th.attr( 'data-column' ), 10 ), order = c.sortVars[ col ].order; - // Only call sortStart if sorting is enabled c.$table.triggerHandler( 'sortStart', table ); // get current column sort order @@ -2076,7 +2096,7 @@ if ( !widget.priority ) { widget.priority = 10; } widgets[ indx ] = widget; } else if ( c.debug ) { - console.warn( '"' + names[ indx ] + '" widget code does not exist!' ); + console.warn( '"' + names[ indx ] + '" was enabled, but the widget code has not been loaded!' ); } } // sort widgets by priority @@ -2148,6 +2168,7 @@ c.widgetInit[ name[ index ] ] = false; } } + c.$table.triggerHandler( 'widgetRemoveEnd', table ); }, refreshWidgets : function( table, doAll, dontapply ) { @@ -2999,7 +3020,7 @@ })(jQuery, window, document); -/*! Widget: uitheme - updated 12/8/2016 (v2.28.1) */ +/*! Widget: uitheme - updated 9/27/2017 (v2.29.0) */ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; @@ -3016,10 +3037,10 @@ active : '', // applied when column is sorted hover : '', // custom css required - a defined bootstrap style may not override other classes // icon class names - icons : '', // add 'icon-white' to make them white; this icon class is added to the <i> in the header + icons : '', // add 'bootstrap-icon-white' to make them white; this icon class is added to the <i> in the header iconSortNone : 'bootstrap-icon-unsorted', // class name added to icon when column is not sorted - iconSortAsc : 'icon-chevron-up glyphicon glyphicon-chevron-up', // class name added to icon when column has ascending sort - iconSortDesc : 'icon-chevron-down glyphicon glyphicon-chevron-down', // class name added to icon when column has descending sort + iconSortAsc : 'glyphicon glyphicon-chevron-up', // class name added to icon when column has ascending sort + iconSortDesc : 'glyphicon glyphicon-chevron-down', // class name added to icon when column has descending sort filterRow : '', // filter row class footerRow : '', footerCells : '', @@ -5202,7 +5223,7 @@ })( jQuery ); -/*! Widget: stickyHeaders - updated 6/2/2017 (v2.28.13) *//* +/*! Widget: stickyHeaders - updated 9/27/2017 (v2.29.0) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -5354,10 +5375,14 @@ }); } }, + getLeftPosition = function() { + return $attach.length ? + parseInt($attach.css('padding-left'), 10) || 0 : + $table.offset().left - parseInt($table.css('margin-left'), 10) - $(window).scrollLeft(); + }, resizeHeader = function() { $stickyWrap.css({ - left : $attach.length ? parseInt($attach.css('padding-left'), 10) || 0 : - $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft(), + left : getLeftPosition(), width: $table.outerWidth() }); setWidth( $table, $stickyTable ); @@ -5367,10 +5392,10 @@ if (!$table.is(':visible')) { return; } // fixes #278 // Detect nested tables - fixes #724 nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; - var offset = $table.offset(), + var tmp, + offset = $table.offset(), stickyOffset = getStickyOffset(c, wo), yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 - xWindow = $.isWindow( $xScroll[0] ), attachTop = $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop(), @@ -5378,19 +5403,27 @@ scrollTop = attachTop + stickyOffset + nestedStickyTop - captionHeight, tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)) - captionHeight, isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', + state = isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide, + needsUpdating = !$stickyWrap.hasClass( state ), cssSettings = { visibility : isVisible }; if ($attach.length) { + // attached sticky headers always need updating + needsUpdating = true; cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); } - if (xWindow) { - // adjust when scrolling horizontally - fixes issue #143 - cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft(); + // adjust when scrolling horizontally - fixes issue #143 + tmp = getLeftPosition(); + if (tmp !== parseInt($stickyWrap.css('left'), 10)) { + needsUpdating = true; + cssSettings.left = tmp; } cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; - $stickyWrap - .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) - .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) - .css(cssSettings); + if (needsUpdating) { + $stickyWrap + .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) + .addClass( state ) + .css(cssSettings); + } if (isVisible !== laststate || resizing) { // make sure the column widths match resizeHeader(); @@ -5509,7 +5542,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 4/18/2017 (v2.28.8) */ +/*! Widget: resizable - updated 9/27/2017 (v2.29.0) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -5836,6 +5869,7 @@ vars.$target = vars.$next = null; // will update stickyHeaders, just in case, see #912 c.$table.triggerHandler('stickyHeadersUpdate'); + c.$table.triggerHandler('resizableComplete'); } }; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index f83a1d1..e530077 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.28.15 *//* +/*! TableSorter (FORK) v2.29.0 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.28.15', + version : '2.29.0', parsers : [], widgets : [], @@ -421,10 +421,10 @@ e.stopPropagation(); ts.applyWidgetId( this, id ); }) - .bind( 'applyWidgets' + namespace, function( e, init ) { + .bind( 'applyWidgets' + namespace, function( e, callback ) { e.stopPropagation(); - // apply widgets - ts.applyWidget( this, init ); + // apply widgets (false = not initializing) + ts.applyWidget( this, false, callback ); }) .bind( 'refreshWidgets' + namespace, function( e, all, dontapply ) { e.stopPropagation(); @@ -461,9 +461,9 @@ downTarget = null; if ( core !== true ) { $headers.addClass( namespace.slice( 1 ) + '_extra_headers' ); - tmp = $.fn.closest ? $headers.closest( 'table' )[ 0 ] : $headers.parents( 'table' )[ 0 ]; - if ( tmp && tmp.nodeName === 'TABLE' && tmp !== table ) { - $( tmp ).addClass( namespace.slice( 1 ) + '_extra_table' ); + tmp = ts.getClosest( $headers, 'table' ); + if ( tmp.length && tmp[ 0 ].nodeName === 'TABLE' && tmp[ 0 ] !== table ) { + $( tmp[ 0 ] ).addClass( namespace.slice( 1 ) + '_extra_table' ); } } tmp = ( c.pointerDown + ' ' + c.pointerUp + ' ' + c.pointerClick + ' sort keyup ' ) @@ -515,8 +515,7 @@ ts.buildCache( c ); } // jQuery v1.2.6 doesn't have closest() - $cell = $.fn.closest ? $( this ).closest( 'th, td' ) : - /TH|TD/.test( this.nodeName ) ? $( this ) : $( this ).parents( 'th, td' ); + $cell = ts.getHeaderCell( $( this ) ); // reference original table headers and find the same cell // don't use $headers or IE8 throws an error - see #987 temp = $headers.index( $cell ); @@ -556,7 +555,7 @@ ''; // redefine c.$headers here in case of an updateAll that replaces or adds an entire header cell - see #683 c.$headers = $( $.map( c.$table.find( c.selectorHeaders ), function( elem, index ) { - var configHeaders, header, column, template, tmp, + var configHeaders, header, column, template, tmp, $th, $elem = $( elem ); // ignore cell (don't add it to c.$headers) if row has ignoreRow class if ( $elem.parent().hasClass( c.cssIgnoreRow ) ) { return; } @@ -582,7 +581,9 @@ if ( c.onRenderHeader ) { c.onRenderHeader.apply( $elem, [ index, c, c.$table ] ); } - column = parseInt( $elem.attr( 'data-column' ), 10 ); + // data-column stored on th or td only + $th = ts.getHeaderCell( $elem ); + column = parseInt( $th.attr( 'data-column' ), 10 ); elem.column = column; tmp = ts.getOrder( ts.getData( $elem, configHeaders, 'sortInitialOrder' ) || c.sortInitialOrder ); // this may get updated numerous times if there are multiple rows @@ -600,10 +601,9 @@ } // add cell to headerList c.headerList[ index ] = elem; + $elem.addClass( ts.css.header + ' ' + c.cssHeader ); // add to parent in case there are multiple rows - $elem - .addClass( ts.css.header + ' ' + c.cssHeader ) - .parent() + ts.getClosest( $elem, 'tr' ) .addClass( ts.css.headerRow + ' ' + c.cssHeaderRow ) .attr( 'role', 'row' ); // allow keyboard cursor to focus on element @@ -1161,10 +1161,29 @@ } }, + // This function does NOT return closest if the $el matches the selector + getClosest : function( $el, selector ) { + return $.fn.closest ? + $el.closest( selector ) : + $el.parents( selector ).filter( ':first' ); + }, + + getHeaderCell : function( $el ) { + // jQuery v1.2.6 doesn't have closest() + if ( $.fn.closest ) { + return $el.closest( 'th, td' ); + } + return /TH|TD/.test( $el[0].nodeName ) ? + $el : + $el.parents( 'th, td' ).filter( ':first' ); + }, + // nextSort (optional), lets you disable next sort text setColumnAriaLabel : function( c, $header, nextSort ) { if ( $header.length ) { - var column = parseInt( $header.attr( 'data-column' ), 10 ), + var $th = ts.getHeaderCell( $header ), + // data-column always stored on the th/td + column = parseInt( $th.attr( 'data-column' ), 10 ), vars = c.sortVars[ column ], tmp = $header.hasClass( ts.css.sortAsc ) ? 'sortAsc' : @@ -1322,10 +1341,9 @@ $cell = $( cell ), // update cache - format: function( s, table, cell, cellIndex ) // no closest in jQuery v1.2.6 - tbodyIndex = $tbodies - .index( $.fn.closest ? $cell.closest( 'tbody' ) : $cell.parents( 'tbody' ).filter( ':first' ) ), + tbodyIndex = $tbodies.index( ts.getClosest( $cell, 'tbody' ) ), tbcache = c.cache[ tbodyIndex ], - $row = $.fn.closest ? $cell.closest( 'tr' ) : $cell.parents( 'tr' ).filter( ':first' ); + $row = ts.getClosest( $cell, 'tr' ); cell = $cell[ 0 ]; // in case cell is a jQuery object // tbody may not exist if update is initialized while tbody is removed for processing if ( $tbodies.length && tbodyIndex >= 0 ) { @@ -1380,11 +1398,13 @@ if ( valid ) { $row = $( $row ); c.$tbodies.append( $row ); - } else if ( !$row || + } else if ( + !$row || // row is a jQuery object? !( $row instanceof jQuery ) || // row contained in the table? - ( $.fn.closest ? $row.closest( 'table' )[ 0 ] : $row.parents( 'table' )[ 0 ] ) !== c.table ) { + ( ts.getClosest( $row, 'table' )[ 0 ] !== c.table ) + ) { if ( c.debug ) { console.error( 'addRows method requires (1) a jQuery selector reference to rows that have already ' + 'been added to the table, or (2) row HTML string to be added to a table with only one tbody' ); @@ -1531,10 +1551,10 @@ notMultiSort = !event[ c.sortMultiSortKey ], table = c.table, len = c.$headers.length, - // get current column index - col = parseInt( $( cell ).attr( 'data-column' ), 10 ), + // get current column index; *always* stored on th/td + $th = ts.getHeaderCell( $( cell ) ), + col = parseInt( $th.attr( 'data-column' ), 10 ), order = c.sortVars[ col ].order; - // Only call sortStart if sorting is enabled c.$table.triggerHandler( 'sortStart', table ); // get current column sort order @@ -2058,7 +2078,7 @@ if ( !widget.priority ) { widget.priority = 10; } widgets[ indx ] = widget; } else if ( c.debug ) { - console.warn( '"' + names[ indx ] + '" widget code does not exist!' ); + console.warn( '"' + names[ indx ] + '" was enabled, but the widget code has not been loaded!' ); } } // sort widgets by priority @@ -2130,6 +2150,7 @@ c.widgetInit[ name[ index ] ] = false; } } + c.$table.triggerHandler( 'widgetRemoveEnd', table ); }, refreshWidgets : function( table, doAll, dontapply ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index c2c84d9..85db662 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 07-04-2017 (v2.28.15)*/ +/*! tablesorter (FORK) - updated 09-27-2017 (v2.29.0)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -133,7 +133,7 @@ })(jQuery, window, document); -/*! Widget: uitheme - updated 12/8/2016 (v2.28.1) */ +/*! Widget: uitheme - updated 9/27/2017 (v2.29.0) */ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; @@ -150,10 +150,10 @@ active : '', // applied when column is sorted hover : '', // custom css required - a defined bootstrap style may not override other classes // icon class names - icons : '', // add 'icon-white' to make them white; this icon class is added to the <i> in the header + icons : '', // add 'bootstrap-icon-white' to make them white; this icon class is added to the <i> in the header iconSortNone : 'bootstrap-icon-unsorted', // class name added to icon when column is not sorted - iconSortAsc : 'icon-chevron-up glyphicon glyphicon-chevron-up', // class name added to icon when column has ascending sort - iconSortDesc : 'icon-chevron-down glyphicon glyphicon-chevron-down', // class name added to icon when column has descending sort + iconSortAsc : 'glyphicon glyphicon-chevron-up', // class name added to icon when column has ascending sort + iconSortDesc : 'glyphicon glyphicon-chevron-down', // class name added to icon when column has descending sort filterRow : '', // filter row class footerRow : '', footerCells : '', @@ -2336,7 +2336,7 @@ })( jQuery ); -/*! Widget: stickyHeaders - updated 6/2/2017 (v2.28.13) *//* +/*! Widget: stickyHeaders - updated 9/27/2017 (v2.29.0) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -2488,10 +2488,14 @@ }); } }, + getLeftPosition = function() { + return $attach.length ? + parseInt($attach.css('padding-left'), 10) || 0 : + $table.offset().left - parseInt($table.css('margin-left'), 10) - $(window).scrollLeft(); + }, resizeHeader = function() { $stickyWrap.css({ - left : $attach.length ? parseInt($attach.css('padding-left'), 10) || 0 : - $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft(), + left : getLeftPosition(), width: $table.outerWidth() }); setWidth( $table, $stickyTable ); @@ -2501,10 +2505,10 @@ if (!$table.is(':visible')) { return; } // fixes #278 // Detect nested tables - fixes #724 nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; - var offset = $table.offset(), + var tmp, + offset = $table.offset(), stickyOffset = getStickyOffset(c, wo), yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 - xWindow = $.isWindow( $xScroll[0] ), attachTop = $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop(), @@ -2512,19 +2516,27 @@ scrollTop = attachTop + stickyOffset + nestedStickyTop - captionHeight, tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)) - captionHeight, isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', + state = isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide, + needsUpdating = !$stickyWrap.hasClass( state ), cssSettings = { visibility : isVisible }; if ($attach.length) { + // attached sticky headers always need updating + needsUpdating = true; cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); } - if (xWindow) { - // adjust when scrolling horizontally - fixes issue #143 - cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft(); + // adjust when scrolling horizontally - fixes issue #143 + tmp = getLeftPosition(); + if (tmp !== parseInt($stickyWrap.css('left'), 10)) { + needsUpdating = true; + cssSettings.left = tmp; } cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; - $stickyWrap - .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) - .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) - .css(cssSettings); + if (needsUpdating) { + $stickyWrap + .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) + .addClass( state ) + .css(cssSettings); + } if (isVisible !== laststate || resizing) { // make sure the column widths match resizeHeader(); @@ -2643,7 +2655,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 4/18/2017 (v2.28.8) */ +/*! Widget: resizable - updated 9/27/2017 (v2.29.0) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -2970,6 +2982,7 @@ vars.$target = vars.$next = null; // will update stickyHeaders, just in case, see #912 c.$table.triggerHandler('stickyHeadersUpdate'); + c.$table.triggerHandler('resizableComplete'); } }; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index c67ff36..f401b33 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Widget: columnSelector (responsive table widget) - updated 7/4/2017 (v2.28.15) *//* +/* Widget: columnSelector (responsive table widget) - updated 9/27/2017 (v2.29.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -269,16 +269,16 @@ c.$table.triggerHandler(wo.columnSelector_updated); } }, - addSelectors: function( prefix, column ) { + addSelectors: function( wo, prefix, column ) { var array = [], temp = ' col:nth-child(' + column + ')'; array.push(prefix + temp + ',' + prefix + '_extra_table' + temp); - temp = ' tr:not(.hasSpan) th[data-column="' + ( column - 1 ) + '"]'; + temp = ' tr:not(.' + wo.columnSelector_classHasSpan + ') th[data-column="' + ( column - 1 ) + '"]'; array.push(prefix + temp + ',' + prefix + '_extra_table' + temp); - temp = ' tr:not(.hasSpan) td:nth-child(' + column + ')'; + temp = ' tr:not(.' + wo.columnSelector_classHasSpan + ') td:nth-child(' + column + ')'; array.push(prefix + temp + ',' + prefix + '_extra_table' + temp); // for other cells in colspan columns - temp = ' tr td:not(' + prefix + 'HasSpan)[data-column="' + (column - 1) + '"]'; + temp = ' tr td:not(' + prefix + wo.columnSelector_classHasSpan + ')[data-column="' + (column - 1) + '"]'; array.push(prefix + temp + ',' + prefix + '_extra_table' + temp); return array; }, @@ -301,7 +301,7 @@ isHidden[ column + 1 ] = ts.getData( c.$headerIndexed[ column ], col, 'columnSelector' ) === 'false'; if ( isHidden[ column + 1 ] ) { // hide columnSelector false column (in auto mode) - mediaAll = mediaAll.concat( tsColSel.addSelectors( prefix, column + 1 ) ); + mediaAll = mediaAll.concat( tsColSel.addSelectors( wo, prefix, column + 1 ) ); } } } @@ -313,7 +313,7 @@ column = parseInt($(this).attr('data-column'), 10) + 1; // don't reveal columnSelector false columns if ( !isHidden[ column ] ) { - breaks = breaks.concat( tsColSel.addSelectors( prefix, column ) ); + breaks = breaks.concat( tsColSel.addSelectors( wo, prefix, column ) ); } }); if (breaks.length) { @@ -343,7 +343,7 @@ colSel.$container.find('input[data-column]').filter('[data-column!="auto"]').each(function(){ if (!this.checked) { column = parseInt( $(this).attr('data-column'), 10 ) + 1; - styles = styles.concat( tsColSel.addSelectors( prefix, column ) ); + styles = styles.concat( tsColSel.addSelectors( wo, prefix, column ) ); } $(this).toggleClass( wo.columnSelector_cssChecked, this.checked ); }); @@ -373,10 +373,10 @@ if ( span > 1 ) { hasSpans = true; $cells.eq( index ) - .addClass( c.namespace.slice( 1 ) + 'columnselectorHasSpan' ) + .addClass( c.namespace.slice( 1 ) + 'columnselector' + wo.columnSelector_classHasSpan ) .attr( 'data-col-span', span ); // add data-column values - ts.computeColumnIndex( $cells.eq( index ).parent().addClass( 'hasSpan' ) ); + ts.computeColumnIndex( $cells.eq( index ).parent().addClass( wo.columnSelector_classHasSpan ) ); } } // only add resize end if using media queries @@ -402,7 +402,9 @@ autoModeOn = wo.columnSelector_mediaquery && colSel.auto, // find all header/footer cells in case a regular column follows a colspan; see #1238 $headers = c.$table.children( 'thead, tfoot' ).children().children() - .add( $(c.namespace + '_extra_table').children( 'thead, tfoot' ).children().children() ), + .add( $(c.namespace + '_extra_table').children( 'thead, tfoot' ).children().children() ) + // include grouping widget headers (they have colspans!) + .add( c.$table.find( '.group-header' ).children() ), len = $headers.length; for ( index = 0; index < len; index++ ) { $cell = $headers.eq(index); @@ -422,7 +424,7 @@ $cell.addClass( filtered ); } } else if ( typeof colSel.states[ col ] !== 'undefined' && colSel.states[ col ] !== null ) { - $cell.toggleClass( filtered, !colSel.states[ col ] ); + $cell.toggleClass( filtered, !autoModeOn && !colSel.states[ col ] ); } } }, @@ -533,6 +535,8 @@ // class name added to checked checkboxes - this fixes an issue with Chrome not updating FontAwesome // applied icons; use this class name (input.checked) instead of input:checked columnSelector_cssChecked : 'checked', + // class name added to rows that have a span (e.g. grouping widget & other rows inside the tbody) + columnSelector_classHasSpan : 'hasSpan', // event triggered when columnSelector completes columnSelector_updated : 'columnUpdate' }, @@ -546,10 +550,10 @@ if ( csel.$popup ) { csel.$popup.empty(); } csel.$style.remove(); csel.$breakpoints.remove(); - $( c.namespace + 'columnselectorHasSpan' ).removeClass( wo.filter_filteredRow || 'filtered' ); + $( c.namespace + 'columnselector' + wo.columnSelector_classHasSpan ) + .removeClass( wo.filter_filteredRow || 'filtered' ); c.$table.find('[data-col-span]').each(function(indx, el) { var $el = $(el); - console.log($el, $el.attr('data-col-span')); $el.attr('colspan', $el.attr('data-col-span')); }); c.$table.off('updateAll' + namespace + ' update' + namespace); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index b7dc09a..8e18d58 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -1,4 +1,4 @@ -/*! Widget: grouping - updated 11/26/2016 (v2.28.0) *//* +/*! Widget: grouping - updated 9/27/2017 (v2.29.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -172,7 +172,10 @@ groupHeaderHTML : function( c, wo, data ) { var name = ( data.currentGroup || '' ).toString().replace(/</g, '<').replace(/>/g, '>'); - return '<tr class="group-header ' + c.selectorRemove.slice(1) + + return '<tr class="group-header ' + c.selectorRemove.slice(1) + ' ' + + // prevent grouping row from being hidden by the columnSelector; + // classHasSpan option added 2.29.0 + ( wo.columnSelector_classHasSpan || 'hasSpan' ) + '" unselectable="on" ' + ( c.tabIndex ? 'tabindex="0" ' : '' ) + 'data-group-index="' + data.groupIndex + '">' + '<td colspan="' + c.columns + '">' + @@ -221,6 +224,10 @@ } } } + if ( ts.hasWidget( c.table, 'columnSelector' ) ) { + // make sure to handle the colspan adjustments of the grouping rows + ts.columnSelector.setUpColspan( c, wo ); + } }, insertGroupHeader: function( c, wo, data ) { var $header = c.$headerIndexed[ data.column ], diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index 722bd4f..53bc110 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -1,4 +1,4 @@ -/*! Widget: output - updated 4/2/2017 (v2.28.6) *//* +/*! Widget: output - updated 9/27/2017 (v2.29.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * Modified from: * HTML Table to CSV: http://www.kunalbabre.com/projects/table2CSV.php (License unknown?) @@ -131,8 +131,10 @@ return data; }, - process : function(c, wo) { - var mydata, $this, $rows, headers, csvData, len, rowsLen, tmp, + // optional vars $rows and dump added by TheSin to make + // process callable via callback for ajaxPager + process : function(c, wo, $rows, dump) { + var mydata, $this, headers, csvData, len, rowsLen, tmp, hasStringify = window.JSON && JSON.hasOwnProperty('stringify'), indx = 0, tmpData = (wo.output_separator || ',').toLowerCase(), @@ -162,7 +164,8 @@ headers = output.processRow(c, $this, true, outputJSON); // all tbody rows - do not include widget added rows (e.g. grouping widget headers) - $rows = $el.children('tbody').children('tr').not(c.selectorRemove); + if ( !$rows ) + $rows = $el.children('tbody').children('tr').not(c.selectorRemove); // check for a filter callback function first! because // /^f/.test(function(){ console.log('test'); }) is TRUE! (function is converted to a string) @@ -210,10 +213,15 @@ mydata = outputArray && hasStringify ? JSON.stringify(tmpData) : tmpData.join('\n'); } + if (dump) { + return mydata; + } + // callback; if true returned, continue processing if ($.isFunction(wo.output_callback)) { tmp = wo.output_callback(c, mydata, c.pager && c.pager.ajaxObject.url || null); if ( tmp === false ) { + output.busy = false; return; } else if ( typeof tmp === 'string' ) { mydata = tmp; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js index 543b143..8d9fc9f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js @@ -1,4 +1,4 @@ -/*! Widget: resizable - updated 4/18/2017 (v2.28.8) */ +/*! Widget: resizable - updated 9/27/2017 (v2.29.0) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -325,6 +325,7 @@ vars.$target = vars.$next = null; // will update stickyHeaders, just in case, see #912 c.$table.triggerHandler('stickyHeadersUpdate'); + c.$table.triggerHandler('resizableComplete'); } }; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index d005b3a..9e2ddd9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -1,4 +1,4 @@ -/*! Widget: scroller - updated 4/18/2017 (v2.28.8) *//* +/*! Widget: scroller - updated 9/27/2017 (v2.29.0) *//* Copyright (C) 2011 T. Connell & Associates, Inc. Dual-licensed under the MIT and GPL licenses @@ -273,6 +273,9 @@ $tableWrap .off( 'scroll' + namespace ) .on( 'scroll' + namespace, function() { + // Save position + wo.scroller_saved[0] = $tableWrap.scrollLeft(); + wo.scroller_saved[1] = $tableWrap.scrollTop(); if ( wo.scroller_jumpToHeader ) { var pos = $win.scrollTop() - $hdr.offset().top; if ( $( this ).scrollTop() !== 0 && pos < tbHt && pos > 0 ) { @@ -841,6 +844,7 @@ // adjust caption height, see #1202 $fixedColumn.find('caption').height( wo.scroller_$header.find( 'caption' ).height() ); + $tableWrap.scroll(); wo.scroller_isBusy = false; }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js index cbb12c6..b8501b4 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js @@ -1,4 +1,4 @@ -/*! Widget: sort2Hash (BETA) - updated 7/4/2017 (v2.28.15) */ +/*! Widget: sort2Hash (BETA) - updated 9/27/2017 (v2.29.0) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -225,8 +225,27 @@ hash = s2h.cleanHash( c, wo, component, hash ); str += value; }); - // add updated hash - window.location.hash = ( ( window.location.hash || '' ).replace( '#', '' ).length ? hash : wo.sort2Hash_hash ) + str; + + var hashChar = wo.sort2Hash_hash; + // Combine new hash with any existing hashes + var newHash = ( + ( window.location.hash || '' ).replace( hashChar, '' ).length ? + hash : hashChar + ) + str; + + if (wo.sort2Hash_replaceHistory) { + var baseUrl = window.location.href.split(hashChar)[0]; + // Ensure that there is a leading hash character + var firstChar = newHash[0]; + if (firstChar != hashChar) { + newHash = hashChar + newHash; + } + // Update URL in browser + window.location.replace(baseUrl + newHash); + } else { + // Add updated hash + window.location.hash = newHash; + } } }; @@ -239,6 +258,7 @@ sort2Hash_headerTextAttr : 'data-header', // data attribute containing alternate header text sort2Hash_directionText : [ 0, 1 ], // [ 'asc', 'desc' ], sort2Hash_overrideSaveSort : false, // if true, override saveSort widget if saved sort available + sort2Hash_replaceHistory : false, // if true, hash changes are not saved to browser history // this option > table ID > table index on page sort2Hash_tableId : null, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js index 12447b6..5cc596a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js @@ -1,4 +1,4 @@ -/*! Widget: stickyHeaders - updated 6/2/2017 (v2.28.13) *//* +/*! Widget: stickyHeaders - updated 9/27/2017 (v2.29.0) *//* * Requires tablesorter v2.8+ and jQuery 1.4.3+ * by Rob Garrison */ @@ -150,10 +150,14 @@ }); } }, + getLeftPosition = function() { + return $attach.length ? + parseInt($attach.css('padding-left'), 10) || 0 : + $table.offset().left - parseInt($table.css('margin-left'), 10) - $(window).scrollLeft(); + }, resizeHeader = function() { $stickyWrap.css({ - left : $attach.length ? parseInt($attach.css('padding-left'), 10) || 0 : - $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft(), + left : getLeftPosition(), width: $table.outerWidth() }); setWidth( $table, $stickyTable ); @@ -163,10 +167,10 @@ if (!$table.is(':visible')) { return; } // fixes #278 // Detect nested tables - fixes #724 nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; - var offset = $table.offset(), + var tmp, + offset = $table.offset(), stickyOffset = getStickyOffset(c, wo), yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 - xWindow = $.isWindow( $xScroll[0] ), attachTop = $attach.length ? ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : $yScroll.scrollTop(), @@ -174,19 +178,27 @@ scrollTop = attachTop + stickyOffset + nestedStickyTop - captionHeight, tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)) - captionHeight, isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', + state = isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide, + needsUpdating = !$stickyWrap.hasClass( state ), cssSettings = { visibility : isVisible }; if ($attach.length) { + // attached sticky headers always need updating + needsUpdating = true; cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); } - if (xWindow) { - // adjust when scrolling horizontally - fixes issue #143 - cssSettings.left = $table.offset().left - parseInt($table.css('margin-left'), 10) - $xScroll.scrollLeft(); + // adjust when scrolling horizontally - fixes issue #143 + tmp = getLeftPosition(); + if (tmp !== parseInt($stickyWrap.css('left'), 10)) { + needsUpdating = true; + cssSettings.left = tmp; } cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; - $stickyWrap - .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) - .addClass( isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide ) - .css(cssSettings); + if (needsUpdating) { + $stickyWrap + .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) + .addClass( state ) + .css(cssSettings); + } if (isVisible !== laststate || resizing) { // make sure the column widths match resizeHeader(); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js index 42a945e..9c16901 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js @@ -1,4 +1,4 @@ -/*! Widget: uitheme - updated 12/8/2016 (v2.28.1) */ +/*! Widget: uitheme - updated 9/27/2017 (v2.29.0) */ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; @@ -15,10 +15,10 @@ active : '', // applied when column is sorted hover : '', // custom css required - a defined bootstrap style may not override other classes // icon class names - icons : '', // add 'icon-white' to make them white; this icon class is added to the <i> in the header + icons : '', // add 'bootstrap-icon-white' to make them white; this icon class is added to the <i> in the header iconSortNone : 'bootstrap-icon-unsorted', // class name added to icon when column is not sorted - iconSortAsc : 'icon-chevron-up glyphicon glyphicon-chevron-up', // class name added to icon when column has ascending sort - iconSortDesc : 'icon-chevron-down glyphicon glyphicon-chevron-down', // class name added to icon when column has descending sort + iconSortAsc : 'glyphicon glyphicon-chevron-up', // class name added to icon when column has ascending sort + iconSortDesc : 'glyphicon glyphicon-chevron-down', // class name added to icon when column has descending sort filterRow : '', // filter row class footerRow : '', footerCells : '', diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index 78bfc7d..10e911d 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -56,8 +56,8 @@ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAYAAAD5YeaVAAAA20lEQVR4AWJABpKSkoxALCstLb0aUAsZaCAMhVEY6B0amx8YZWDDEDSBa2AGe7XeIiAAClYwVGBvsAcIllsf/mvcC9DgOOd8h90fxWvngVEUbZIkuWRZZlE8eQjcisgZMM9zi+LJ6ZfwegmWZflZDugdHMfxTcGqql7TNBlUB/QObtv2VBSFrev6OY7jngzFk9OT/fn73fWYpqnlXNyXDMWT0zuYx/Bvel9ej+LJ6R08DMOu67q7DkTkrSA5vYPneV71fX/QASdTkJwezhs0TfMARn0wMDDGXEPgF4oijqwM5YjNAAAAAElFTkSuQmCC); } -/* white unsorted icon */ -.tablesorter-bootstrap .icon-white.bootstrap-icon-unsorted { +/* white unsorted icon; updated to use bootstrap-icon-white - see #1432 */ +.tablesorter-bootstrap .bootstrap-icon-white.bootstrap-icon-unsorted { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAYAAAD5YeaVAAAAe0lEQVR4AbXQoRWDMBiF0Sh2QLAAQ8SxJGugWSA6A2STW1PxTsnB9cnkfuYvv8OGC1t5G3Y0QMP+Bm857keAdQIzWBP3+Bw4MADQE18B6/etRnCV/w9nnGuLezfAmXhABGtAGIkruvk6auIFRwQJDywllsEAjCecB20GP59BQQ+gtlRLAAAAAElFTkSuQmCC); } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_3.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_3.css index 67bc2f0..12ab36d 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_3.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_3.css @@ -53,8 +53,8 @@ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAYAAAD5YeaVAAAA20lEQVR4AWJABpKSkoxALCstLb0aUAsZaCAMhVEY6B0amx8YZWDDEDSBa2AGe7XeIiAAClYwVGBvsAcIllsf/mvcC9DgOOd8h90fxWvngVEUbZIkuWRZZlE8eQjcisgZMM9zi+LJ6ZfwegmWZflZDugdHMfxTcGqql7TNBlUB/QObtv2VBSFrev6OY7jngzFk9OT/fn73fWYpqnlXNyXDMWT0zuYx/Bvel9ej+LJ6R08DMOu67q7DkTkrSA5vYPneV71fX/QASdTkJwezhs0TfMARn0wMDDGXEPgF4oijqwM5YjNAAAAAElFTkSuQmCC); } -/* white unsorted icon */ -.tablesorter-bootstrap .icon-white.bootstrap-icon-unsorted { +/* white unsorted icon; updated to use bootstrap-icon-white - see #1432 */ +.tablesorter-bootstrap .bootstrap-icon-white.bootstrap-icon-unsorted { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAYAAAD5YeaVAAAAe0lEQVR4AbXQoRWDMBiF0Sh2QLAAQ8SxJGugWSA6A2STW1PxTsnB9cnkfuYvv8OGC1t5G3Y0QMP+Bm857keAdQIzWBP3+Bw4MADQE18B6/etRnCV/w9nnGuLezfAmXhABGtAGIkruvk6auIFRwQJDywllsEAjCecB20GP59BQQ+gtlRLAAAAAElFTkSuQmCC); } From 7166fec196c255861515be3e8040dd037794f758 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Fri, 15 Dec 2017 16:38:55 +0100 Subject: [PATCH 119/138] Update tablesorter to latest version (2.29.2) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 112 +++++++------- .../jquery-tablesorter/jquery.tablesorter.js | 85 +++++------ .../jquery.tablesorter.widgets.js | 27 +++- .../widgets/widget-filter.js | 8 +- .../widgets/widget-resizable.js | 17 ++- .../widgets/widget-scroller.js | 8 +- .../widgets/widget-vertical-group.js | 137 ++++++++++++++++++ 11 files changed, 292 insertions(+), 112 deletions(-) create mode 100644 vendor/assets/javascripts/jquery-tablesorter/widgets/widget-vertical-group.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e440fc..1b42ecd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.24.1 (2017-12-15) + +* Upgrade tablesorter to v2.29.2 + #### v1.24.0 (2017-10-09) * Upgrade tablesorter to v2.29.0 diff --git a/README.md b/README.md index 5337ac5..6827ef9 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.29.0 (9/27/2017) [documentation] +Current tablesorter version: 2.29.2 (12/13/2017) [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index e4e9c39..ab02aed 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 24 - TINY = 0 + TINY = 1 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index b6647fa..23a0750 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit b6647faba58594c51ea9dc840ea6211e186c42ff +Subproject commit 23a0750504fcecdae9c5d837959e1feb9144ec9b diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 116943f..8c2c427 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 09-27-2017 (v2.29.0)*/ +/*! tablesorter (FORK) - updated 12-13-2017 (v2.29.2)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.29.0 *//* +/*! TableSorter (FORK) v2.29.2 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.29.0', + version : '2.29.2', parsers : [], widgets : [], @@ -532,8 +532,7 @@ if ( c.delayInit && ts.isEmptyObject( c.cache ) ) { ts.buildCache( c ); } - // jQuery v1.2.6 doesn't have closest() - $cell = ts.getHeaderCell( $( this ) ); + $cell = ts.getClosest( $( this ), '.' + ts.css.header ); // reference original table headers and find the same cell // don't use $headers or IE8 throws an error - see #987 temp = $headers.index( $cell ); @@ -573,10 +572,15 @@ ''; // redefine c.$headers here in case of an updateAll that replaces or adds an entire header cell - see #683 c.$headers = $( $.map( c.$table.find( c.selectorHeaders ), function( elem, index ) { - var configHeaders, header, column, template, tmp, $th, + var configHeaders, header, column, template, tmp, $elem = $( elem ); // ignore cell (don't add it to c.$headers) if row has ignoreRow class - if ( $elem.parent().hasClass( c.cssIgnoreRow ) ) { return; } + if ( ts.getClosest( $elem, 'tr' ).hasClass( c.cssIgnoreRow ) ) { return; } + // transfer data-column to element if not th/td - #1459 + if ( !/(th|td)/i.test( elem.nodeName ) ) { + tmp = ts.getClosest( $elem, 'th, td' ); + $elem.attr( 'data-column', tmp.attr( 'data-column' ) ); + } // make sure to get header cell & not column indexed cell configHeaders = ts.getColumnData( c.table, c.headers, index, true ); // save original header content @@ -599,9 +603,7 @@ if ( c.onRenderHeader ) { c.onRenderHeader.apply( $elem, [ index, c, c.$table ] ); } - // data-column stored on th or td only - $th = ts.getHeaderCell( $elem ); - column = parseInt( $th.attr( 'data-column' ), 10 ); + column = parseInt( $elem.attr( 'data-column' ), 10 ); elem.column = column; tmp = ts.getOrder( ts.getData( $elem, configHeaders, 'sortInitialOrder' ) || c.sortInitialOrder ); // this may get updated numerous times if there are multiple rows @@ -637,6 +639,7 @@ if ( ts.isEmptyObject( c.sortVars[ indx ] ) ) { c.sortVars[ indx ] = {}; } + // Use c.$headers.parent() in case selectorHeaders doesn't point to the th/td $temp = c.$headers.filter( '[data-column="' + indx + '"]' ); // target sortable column cells, unless there are none, then use non-sortable cells // .last() added in jQuery 1.4; use .filter(':last') to maintain compatibility with jQuery v1.2.6 @@ -1100,6 +1103,15 @@ css = [ ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc ], cssIcon = [ c.cssIconAsc, c.cssIconDesc, c.cssIconNone ], aria = [ 'ascending', 'descending' ], + updateColumnSort = function($el, index) { + $el + .removeClass( none ) + .addClass( css[ index ] ) + .attr( 'aria-sort', aria[ index ] ) + .find( '.' + ts.css.icon ) + .removeClass( cssIcon[ 2 ] ) + .addClass( cssIcon[ index ] ); + }, // find the footer $extras = c.$table .find( 'tfoot tr' ) @@ -1138,7 +1150,7 @@ var include = true, $el = c.$headers.eq( i ), col = parseInt( $el.attr( 'data-column' ), 10 ), - end = col + c.$headers[ i ].colSpan; + end = col + ts.getClosest( $el, 'th, td' )[0].colSpan; for ( ; col < end; col++ ) { include = include ? include || ts.isValueInArray( col, c.sortList ) > -1 : false; } @@ -1152,23 +1164,13 @@ if ( $sorted.length ) { for ( column = 0; column < $sorted.length; column++ ) { if ( !$sorted[ column ].sortDisabled ) { - $sorted - .eq( column ) - .removeClass( none ) - .addClass( css[ list[ indx ][ 1 ] ] ) - .attr( 'aria-sort', aria[ list[ indx ][ 1 ] ] ) - .find( '.' + ts.css.icon ) - .removeClass( cssIcon[ 2 ] ) - .addClass( cssIcon[ list[ indx ][ 1 ] ] ); + updateColumnSort( $sorted.eq( column ) , list[ indx ][ 1 ] ); } } - // add sorted class to footer & extra headers, if they exist - if ( $extras.length ) { - $extras - .filter( '[data-column="' + list[ indx ][ 0 ] + '"]' ) - .removeClass( none ) - .addClass( css[ list[ indx ][ 1 ] ] ); - } + } + // add sorted class to footer & extra headers, if they exist + if ( $extras.length ) { + updateColumnSort( $extras.filter( '[data-column="' + list[ indx ][ 0 ] + '"]' ), list[ indx ][ 1 ] ); } } } @@ -1179,29 +1181,20 @@ } }, - // This function does NOT return closest if the $el matches the selector getClosest : function( $el, selector ) { - return $.fn.closest ? - $el.closest( selector ) : - $el.parents( selector ).filter( ':first' ); - }, - - getHeaderCell : function( $el ) { // jQuery v1.2.6 doesn't have closest() if ( $.fn.closest ) { - return $el.closest( 'th, td' ); + return $el.closest( selector ); } - return /TH|TD/.test( $el[0].nodeName ) ? + return $el.is( selector ) ? $el : - $el.parents( 'th, td' ).filter( ':first' ); + $el.parents( selector ).filter( ':first' ); }, // nextSort (optional), lets you disable next sort text setColumnAriaLabel : function( c, $header, nextSort ) { if ( $header.length ) { - var $th = ts.getHeaderCell( $header ), - // data-column always stored on the th/td - column = parseInt( $th.attr( 'data-column' ), 10 ), + var column = parseInt( $header.attr( 'data-column' ), 10 ), vars = c.sortVars[ column ], tmp = $header.hasClass( ts.css.sortAsc ) ? 'sortAsc' : @@ -1569,10 +1562,10 @@ notMultiSort = !event[ c.sortMultiSortKey ], table = c.table, len = c.$headers.length, - // get current column index; *always* stored on th/td - $th = ts.getHeaderCell( $( cell ) ), - col = parseInt( $th.attr( 'data-column' ), 10 ), + th = ts.getClosest( $( cell ), 'th, td' ), + col = parseInt( th.attr( 'data-column' ), 10 ), order = c.sortVars[ col ].order; + th = th[0]; // Only call sortStart if sorting is enabled c.$table.triggerHandler( 'sortStart', table ); // get current column sort order @@ -1607,8 +1600,8 @@ if ( dir < 2 ) { c.sortList[ c.sortList.length ] = [ col, dir ]; // add other columns if header spans across multiple - if ( cell.colSpan > 1 ) { - for ( indx = 1; indx < cell.colSpan; indx++ ) { + if ( th.colSpan > 1 ) { + for ( indx = 1; indx < th.colSpan; indx++ ) { c.sortList[ c.sortList.length ] = [ col + indx, dir ]; // update count on columns in colSpan c.sortVars[ col + indx ].count = $.inArray( dir, order ); @@ -1640,8 +1633,8 @@ if ( dir < 2 ) { c.sortList[ c.sortList.length ] = [ col, dir ]; // add other columns if header spans across multiple - if ( cell.colSpan > 1 ) { - for ( indx = 1; indx < cell.colSpan; indx++ ) { + if ( th.colSpan > 1 ) { + for ( indx = 1; indx < th.colSpan; indx++ ) { c.sortList[ c.sortList.length ] = [ col + indx, dir ]; // update count on columns in colSpan c.sortVars[ col + indx ].count = $.inArray( dir, order ); @@ -2410,7 +2403,7 @@ $cells = ( $headers || c.$headers ), // c.$headerIndexed is not defined initially $cell = c.$headerIndexed && c.$headerIndexed[ indx ] || - $cells.filter( '[data-column="' + indx + '"]:last' ); + $cells.find( '[data-column="' + indx + '"]:last' ); if ( typeof obj[ indx ] !== 'undefined' ) { return getCell ? obj[ indx ] : obj[ $cells.index( $cell ) ]; } @@ -3295,7 +3288,7 @@ })(jQuery); -/*! Widget: filter - updated 7/4/2017 (v2.28.15) *//* +/*! Widget: filter - updated 12/13/2017 (v2.29.1) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -4412,6 +4405,7 @@ if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) { matches = tsf.types[ffxn]( c, data, vars ); if ( matches !== null ) { + data.matchedOn = ffxn; filterMatched = matches; } } @@ -4459,6 +4453,7 @@ tsf.multipleColumns( c, wo.filter_$anyMatch ) : []; data.$cells = data.$row.children(); + data.matchedOn = null; if ( data.anyMatchFlag && columnIndex.length > 1 || ( data.anyMatchFilter && !hasAnyMatchInput ) ) { data.anyMatch = true; data.isMatch = true; @@ -4560,7 +4555,9 @@ // cycle through the different filters // filters return a boolean or null if nothing matches filterMatched = tsf.processTypes( c, data, vars ); - if ( filterMatched !== null ) { + // select with exact match; ignore "and" or "or" within the text; fixes #1486 + txt = fxn === true && (data.matchedOn === 'and' || data.matchedOn === 'or'); + if ( filterMatched !== null && !txt) { result = filterMatched; // Look for match, and add child row data for matching } else { @@ -5542,7 +5539,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 9/27/2017 (v2.29.0) */ +/*! Widget: resizable - updated 12/13/2017 (v2.29.1) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -5719,7 +5716,10 @@ columns = c.columns - 1, $header = $this.data( 'header' ); if ( !$header ) { return; } // see #859 - if ( !$header.is(':visible') ) { + if ( + !$header.is(':visible') || + ( !wo.resizable_addLastColumn && ts.resizable.checkVisibleColumns(c, column) ) + ) { $this.hide(); } else if ( column < columns || column === columns && wo.resizable_addLastColumn ) { $this.css({ @@ -5731,6 +5731,16 @@ }); }, + // Fixes #1485 + checkVisibleColumns: function( c, column ) { + var i, + len = 0; + for ( i = column + 1; i < c.columns; i++ ) { + len += c.$headerIndexed[i].is( ':visible' ) ? 1 : 0; + } + return len === 0; + }, + // prevent text selection while dragging resize bar toggleTextSelection : function( c, wo, toggle ) { var namespace = c.namespace + 'tsresize'; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index e530077..efc3eba 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.29.0 *//* +/*! TableSorter (FORK) v2.29.2 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.29.0', + version : '2.29.2', parsers : [], widgets : [], @@ -514,8 +514,7 @@ if ( c.delayInit && ts.isEmptyObject( c.cache ) ) { ts.buildCache( c ); } - // jQuery v1.2.6 doesn't have closest() - $cell = ts.getHeaderCell( $( this ) ); + $cell = ts.getClosest( $( this ), '.' + ts.css.header ); // reference original table headers and find the same cell // don't use $headers or IE8 throws an error - see #987 temp = $headers.index( $cell ); @@ -555,10 +554,15 @@ ''; // redefine c.$headers here in case of an updateAll that replaces or adds an entire header cell - see #683 c.$headers = $( $.map( c.$table.find( c.selectorHeaders ), function( elem, index ) { - var configHeaders, header, column, template, tmp, $th, + var configHeaders, header, column, template, tmp, $elem = $( elem ); // ignore cell (don't add it to c.$headers) if row has ignoreRow class - if ( $elem.parent().hasClass( c.cssIgnoreRow ) ) { return; } + if ( ts.getClosest( $elem, 'tr' ).hasClass( c.cssIgnoreRow ) ) { return; } + // transfer data-column to element if not th/td - #1459 + if ( !/(th|td)/i.test( elem.nodeName ) ) { + tmp = ts.getClosest( $elem, 'th, td' ); + $elem.attr( 'data-column', tmp.attr( 'data-column' ) ); + } // make sure to get header cell & not column indexed cell configHeaders = ts.getColumnData( c.table, c.headers, index, true ); // save original header content @@ -581,9 +585,7 @@ if ( c.onRenderHeader ) { c.onRenderHeader.apply( $elem, [ index, c, c.$table ] ); } - // data-column stored on th or td only - $th = ts.getHeaderCell( $elem ); - column = parseInt( $th.attr( 'data-column' ), 10 ); + column = parseInt( $elem.attr( 'data-column' ), 10 ); elem.column = column; tmp = ts.getOrder( ts.getData( $elem, configHeaders, 'sortInitialOrder' ) || c.sortInitialOrder ); // this may get updated numerous times if there are multiple rows @@ -619,6 +621,7 @@ if ( ts.isEmptyObject( c.sortVars[ indx ] ) ) { c.sortVars[ indx ] = {}; } + // Use c.$headers.parent() in case selectorHeaders doesn't point to the th/td $temp = c.$headers.filter( '[data-column="' + indx + '"]' ); // target sortable column cells, unless there are none, then use non-sortable cells // .last() added in jQuery 1.4; use .filter(':last') to maintain compatibility with jQuery v1.2.6 @@ -1082,6 +1085,15 @@ css = [ ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc ], cssIcon = [ c.cssIconAsc, c.cssIconDesc, c.cssIconNone ], aria = [ 'ascending', 'descending' ], + updateColumnSort = function($el, index) { + $el + .removeClass( none ) + .addClass( css[ index ] ) + .attr( 'aria-sort', aria[ index ] ) + .find( '.' + ts.css.icon ) + .removeClass( cssIcon[ 2 ] ) + .addClass( cssIcon[ index ] ); + }, // find the footer $extras = c.$table .find( 'tfoot tr' ) @@ -1120,7 +1132,7 @@ var include = true, $el = c.$headers.eq( i ), col = parseInt( $el.attr( 'data-column' ), 10 ), - end = col + c.$headers[ i ].colSpan; + end = col + ts.getClosest( $el, 'th, td' )[0].colSpan; for ( ; col < end; col++ ) { include = include ? include || ts.isValueInArray( col, c.sortList ) > -1 : false; } @@ -1134,23 +1146,13 @@ if ( $sorted.length ) { for ( column = 0; column < $sorted.length; column++ ) { if ( !$sorted[ column ].sortDisabled ) { - $sorted - .eq( column ) - .removeClass( none ) - .addClass( css[ list[ indx ][ 1 ] ] ) - .attr( 'aria-sort', aria[ list[ indx ][ 1 ] ] ) - .find( '.' + ts.css.icon ) - .removeClass( cssIcon[ 2 ] ) - .addClass( cssIcon[ list[ indx ][ 1 ] ] ); + updateColumnSort( $sorted.eq( column ) , list[ indx ][ 1 ] ); } } - // add sorted class to footer & extra headers, if they exist - if ( $extras.length ) { - $extras - .filter( '[data-column="' + list[ indx ][ 0 ] + '"]' ) - .removeClass( none ) - .addClass( css[ list[ indx ][ 1 ] ] ); - } + } + // add sorted class to footer & extra headers, if they exist + if ( $extras.length ) { + updateColumnSort( $extras.filter( '[data-column="' + list[ indx ][ 0 ] + '"]' ), list[ indx ][ 1 ] ); } } } @@ -1161,29 +1163,20 @@ } }, - // This function does NOT return closest if the $el matches the selector getClosest : function( $el, selector ) { - return $.fn.closest ? - $el.closest( selector ) : - $el.parents( selector ).filter( ':first' ); - }, - - getHeaderCell : function( $el ) { // jQuery v1.2.6 doesn't have closest() if ( $.fn.closest ) { - return $el.closest( 'th, td' ); + return $el.closest( selector ); } - return /TH|TD/.test( $el[0].nodeName ) ? + return $el.is( selector ) ? $el : - $el.parents( 'th, td' ).filter( ':first' ); + $el.parents( selector ).filter( ':first' ); }, // nextSort (optional), lets you disable next sort text setColumnAriaLabel : function( c, $header, nextSort ) { if ( $header.length ) { - var $th = ts.getHeaderCell( $header ), - // data-column always stored on the th/td - column = parseInt( $th.attr( 'data-column' ), 10 ), + var column = parseInt( $header.attr( 'data-column' ), 10 ), vars = c.sortVars[ column ], tmp = $header.hasClass( ts.css.sortAsc ) ? 'sortAsc' : @@ -1551,10 +1544,10 @@ notMultiSort = !event[ c.sortMultiSortKey ], table = c.table, len = c.$headers.length, - // get current column index; *always* stored on th/td - $th = ts.getHeaderCell( $( cell ) ), - col = parseInt( $th.attr( 'data-column' ), 10 ), + th = ts.getClosest( $( cell ), 'th, td' ), + col = parseInt( th.attr( 'data-column' ), 10 ), order = c.sortVars[ col ].order; + th = th[0]; // Only call sortStart if sorting is enabled c.$table.triggerHandler( 'sortStart', table ); // get current column sort order @@ -1589,8 +1582,8 @@ if ( dir < 2 ) { c.sortList[ c.sortList.length ] = [ col, dir ]; // add other columns if header spans across multiple - if ( cell.colSpan > 1 ) { - for ( indx = 1; indx < cell.colSpan; indx++ ) { + if ( th.colSpan > 1 ) { + for ( indx = 1; indx < th.colSpan; indx++ ) { c.sortList[ c.sortList.length ] = [ col + indx, dir ]; // update count on columns in colSpan c.sortVars[ col + indx ].count = $.inArray( dir, order ); @@ -1622,8 +1615,8 @@ if ( dir < 2 ) { c.sortList[ c.sortList.length ] = [ col, dir ]; // add other columns if header spans across multiple - if ( cell.colSpan > 1 ) { - for ( indx = 1; indx < cell.colSpan; indx++ ) { + if ( th.colSpan > 1 ) { + for ( indx = 1; indx < th.colSpan; indx++ ) { c.sortList[ c.sortList.length ] = [ col + indx, dir ]; // update count on columns in colSpan c.sortVars[ col + indx ].count = $.inArray( dir, order ); @@ -2392,7 +2385,7 @@ $cells = ( $headers || c.$headers ), // c.$headerIndexed is not defined initially $cell = c.$headerIndexed && c.$headerIndexed[ indx ] || - $cells.filter( '[data-column="' + indx + '"]:last' ); + $cells.find( '[data-column="' + indx + '"]:last' ); if ( typeof obj[ indx ] !== 'undefined' ) { return getCell ? obj[ indx ] : obj[ $cells.index( $cell ) ]; } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 85db662..c12e0fe 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 09-27-2017 (v2.29.0)*/ +/*! tablesorter (FORK) - updated 12-13-2017 (v2.29.2)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -408,7 +408,7 @@ })(jQuery); -/*! Widget: filter - updated 7/4/2017 (v2.28.15) *//* +/*! Widget: filter - updated 12/13/2017 (v2.29.1) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -1525,6 +1525,7 @@ if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) { matches = tsf.types[ffxn]( c, data, vars ); if ( matches !== null ) { + data.matchedOn = ffxn; filterMatched = matches; } } @@ -1572,6 +1573,7 @@ tsf.multipleColumns( c, wo.filter_$anyMatch ) : []; data.$cells = data.$row.children(); + data.matchedOn = null; if ( data.anyMatchFlag && columnIndex.length > 1 || ( data.anyMatchFilter && !hasAnyMatchInput ) ) { data.anyMatch = true; data.isMatch = true; @@ -1673,7 +1675,9 @@ // cycle through the different filters // filters return a boolean or null if nothing matches filterMatched = tsf.processTypes( c, data, vars ); - if ( filterMatched !== null ) { + // select with exact match; ignore "and" or "or" within the text; fixes #1486 + txt = fxn === true && (data.matchedOn === 'and' || data.matchedOn === 'or'); + if ( filterMatched !== null && !txt) { result = filterMatched; // Look for match, and add child row data for matching } else { @@ -2655,7 +2659,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 9/27/2017 (v2.29.0) */ +/*! Widget: resizable - updated 12/13/2017 (v2.29.1) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -2832,7 +2836,10 @@ columns = c.columns - 1, $header = $this.data( 'header' ); if ( !$header ) { return; } // see #859 - if ( !$header.is(':visible') ) { + if ( + !$header.is(':visible') || + ( !wo.resizable_addLastColumn && ts.resizable.checkVisibleColumns(c, column) ) + ) { $this.hide(); } else if ( column < columns || column === columns && wo.resizable_addLastColumn ) { $this.css({ @@ -2844,6 +2851,16 @@ }); }, + // Fixes #1485 + checkVisibleColumns: function( c, column ) { + var i, + len = 0; + for ( i = column + 1; i < c.columns; i++ ) { + len += c.$headerIndexed[i].is( ':visible' ) ? 1 : 0; + } + return len === 0; + }, + // prevent text selection while dragging resize bar toggleTextSelection : function( c, wo, toggle ) { var namespace = c.namespace + 'tsresize'; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 5a5b760..a1a4c03 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 7/4/2017 (v2.28.15) *//* +/*! Widget: filter - updated 12/13/2017 (v2.29.1) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -1115,6 +1115,7 @@ if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) { matches = tsf.types[ffxn]( c, data, vars ); if ( matches !== null ) { + data.matchedOn = ffxn; filterMatched = matches; } } @@ -1162,6 +1163,7 @@ tsf.multipleColumns( c, wo.filter_$anyMatch ) : []; data.$cells = data.$row.children(); + data.matchedOn = null; if ( data.anyMatchFlag && columnIndex.length > 1 || ( data.anyMatchFilter && !hasAnyMatchInput ) ) { data.anyMatch = true; data.isMatch = true; @@ -1263,7 +1265,9 @@ // cycle through the different filters // filters return a boolean or null if nothing matches filterMatched = tsf.processTypes( c, data, vars ); - if ( filterMatched !== null ) { + // select with exact match; ignore "and" or "or" within the text; fixes #1486 + txt = fxn === true && (data.matchedOn === 'and' || data.matchedOn === 'or'); + if ( filterMatched !== null && !txt) { result = filterMatched; // Look for match, and add child row data for matching } else { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js index 8d9fc9f..de084c6 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js @@ -1,4 +1,4 @@ -/*! Widget: resizable - updated 9/27/2017 (v2.29.0) */ +/*! Widget: resizable - updated 12/13/2017 (v2.29.1) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -175,7 +175,10 @@ columns = c.columns - 1, $header = $this.data( 'header' ); if ( !$header ) { return; } // see #859 - if ( !$header.is(':visible') ) { + if ( + !$header.is(':visible') || + ( !wo.resizable_addLastColumn && ts.resizable.checkVisibleColumns(c, column) ) + ) { $this.hide(); } else if ( column < columns || column === columns && wo.resizable_addLastColumn ) { $this.css({ @@ -187,6 +190,16 @@ }); }, + // Fixes #1485 + checkVisibleColumns: function( c, column ) { + var i, + len = 0; + for ( i = column + 1; i < c.columns; i++ ) { + len += c.$headerIndexed[i].is( ':visible' ) ? 1 : 0; + } + return len === 0; + }, + // prevent text selection while dragging resize bar toggleTextSelection : function( c, wo, toggle ) { var namespace = c.namespace + 'tsresize'; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index 9e2ddd9..dfe7b07 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -1,4 +1,4 @@ -/*! Widget: scroller - updated 9/27/2017 (v2.29.0) *//* +/*! Widget: scroller - updated 12/13/2017 (v2.29.1) *//* Copyright (C) 2011 T. Connell & Associates, Inc. Dual-licensed under the MIT and GPL licenses @@ -289,8 +289,10 @@ }); // resize/update events - filterEnd fires after "tablesorter-initialized" and "updateComplete" - events = ( ( ts.hasWidget( c.table, 'filter' ) ? 'filterEnd' : 'tablesorter-initialized updateComplete' ) + - ' sortEnd pagerComplete columnUpdate ' ).split( ' ' ).join( namespace + ' ' ); + tmp = ts.hasWidget( c.table, 'filter' ) ? + 'filterEnd filterInit' : + 'tablesorter-initialized updateComplete'; + events = ( tmp + ' sortEnd pagerComplete columnUpdate ' ).split( ' ' ).join( namespace + ' ' ); $table .off( namespace ) diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-vertical-group.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-vertical-group.js new file mode 100644 index 0000000..2cbf924 --- /dev/null +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-vertical-group.js @@ -0,0 +1,137 @@ +/*! Widget: vertical-group (BETA) - updated 12/13/2017 (v2.29.1) */ +/* Requires tablesorter and jQuery + * Originally by @aavmurphy (Andrew Murphy) + * Adapted for tablesorter by Rob Garrison - see #1469 & #1470 + * + * This widget is licensed under the same terms at mottie/tablesorter itself, i.e. free to use + */ +/* jshint browser:true, jquery:true, unused:false */ +/* global jQuery:false */ +;(function($){ + 'use strict'; + + var ts = $.tablesorter, + tscss = ts.css; + + $.extend( ts.css, { + verticalGroupHeader: 'tablesorter-vertical-group', + verticalGroupHide: 'tablesorter-vertical-group-hide', + verticalGroupShow: 'tablesorter-vertical-group-show' + }); + + ts.addWidget({ + id: 'vertical-group', + priority: 99, + init: verticalGroup, + format: verticalGroup + }); + + function cleanUp( el ) { + el.removeClass(tscss.verticalGroupHide + ' ' + tscss.verticalGroupShow); + } + + function setZebra( wo, $cell, indx ) { + var $row = $cell.parent(); + $row + .removeClass( wo.zebra[ (indx + 1) % 2 ] ) + .addClass( wo.zebra[ indx % 2] ); + } + + function verticalGroup( table, c, wo, init ) { + // ------------------------------------------------------------------------- + // loop thru the header row, + // - look for .vertical-group + // + // loop thru the rows + // + // set ALWAYS_SHOW = FALSE + // loop thru the 1st 4 columns + // if this cell does not exist, skip to next row + // if ALWAYS_SHOW, then this cell is SHOW + // else if this column does not have '.vertical-group', then this cell is SHOW + // else if this cell is NOT the same as the cell-above, then this cell is SHOW + // else this cell is HIDE + // if this cell is SHOW, set ALWAYS_SHOW + // if this cell is SHOW, + // then + // set the cell class to .vertical_group_show + // else + // set the cell class to vertical_group_show + // + // TO DO add/remove classes so as not to clobber other existing classes + // TO DO add classes + // + // .vertical-group-show { background-color: white !important; } + // .vertical-group-hide { visibility: hidden; border-top: white !important;background-color: white !important; } + // + // this is all because of stripped tables + // - background-colour show be the table's background colour (or the first row's) + // - the border-color needs to be the same + // + // ------------------------------------------------------------------------------------------------ + var tmp, + zebra_index = -1, // increments at start of loop + rows = table.tBodies[0].rows, + header = table.tHead.rows, + has_zebra = ts.hasWidget( table, 'zebra'), + is_vertical_group_col = [], + last_row = []; + + if ( wo.vertical_group_lock ) { + return; + } + wo.vertical_group_lock = true; + + is_vertical_group_col = $.map( c.$headerIndexed, function( el ) { + return el.hasClass( tscss.verticalGroupHeader ) ? 1 : ''; + }); + + if ( is_vertical_group_col.join('') === '' ) { + cleanUp( $(rows).find( '.' + tscss.verticalGroupHide + ',.' + tscss.verticalGroupShow ) ); + wo.vertical_group_lock = false; + return; + } + + for (var i = 0; i < rows.length; i++) { + var always_show_cell = false; + + for (var j = 0; j < c.columns; j++ ) { + if ( !is_vertical_group_col[ j ] || !rows[ i ].cells[ j ] ) { + zebra_index++; + continue; + } + + var $cell = $( rows[ i ].cells[ j ] ), + // only group if column is sorted + isSorted = ts.isValueInArray( j, c.sortList ), // returns equivalent of an indexOf value + cell_data = $cell.html(); + + if ( isSorted < 0 ) { + cleanUp( $cell ); + } else if ( !always_show_cell && cell_data === last_row[j] ) { + if ( !$cell.hasClass(tscss.verticalGroupHide) ) { + $cell.addClass( tscss.verticalGroupHide ); + } + if ( has_zebra ) { + setZebra( wo, $cell, zebra_index ); + } + $cell.removeClass( tscss.verticalGroupShow ); + } else if (isSorted === 0) { + // only show cells from the first sorted column + always_show_cell = true; // show + if ( !$cell.hasClass( tscss.verticalGroupShow ) ) { + $cell.addClass( tscss.verticalGroupShow ); + } + $cell.removeClass( tscss.verticalGroupHide ); + if ( has_zebra ) { + // only adjust striping based on the first sorted column + setZebra( wo, $cell, isSorted ? zebra_index : ++zebra_index ); + } + } + last_row[j] = cell_data; + } + } + wo.vertical_group_lock = false; + } + +})(jQuery); From 61da698030661252bdc3b26b9e6c33c6b5e4a058 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Thu, 11 Jan 2018 17:42:12 +0100 Subject: [PATCH 120/138] Update tablesorter to latest version (2.29.3) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 8 +-- .../jquery-tablesorter/jquery.tablesorter.js | 6 +- .../jquery.tablesorter.widgets.js | 2 +- .../parsers/parser-network.js | 33 +++++---- .../jquery-tablesorter/theme.bootstrap_4.css | 67 +++++++++---------- 9 files changed, 67 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b42ecd..29d8385 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.24.2 (2018-01-11) + +* Upgrade tablesorter to v2.29.3 + #### v1.24.1 (2017-12-15) * Upgrade tablesorter to v2.29.2 diff --git a/README.md b/README.md index 6827ef9..3d6e1d0 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.29.2 (12/13/2017) [documentation] +Current tablesorter version: 2.29.3 (2018-01-10) [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index ab02aed..9e6b666 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 24 - TINY = 1 + TINY = 2 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 23a0750..3c127e1 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 23a0750504fcecdae9c5d837959e1feb9144ec9b +Subproject commit 3c127e1125ec5b1a9c37db5dbfcbaae0ee112df1 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 8c2c427..e6bdb32 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 12-13-2017 (v2.29.2)*/ +/*! tablesorter (FORK) - updated 01-10-2018 (v2.29.3)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.29.2 *//* +/*! TableSorter (FORK) v2.29.3 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.29.2', + version : '2.29.3', parsers : [], widgets : [], @@ -1164,7 +1164,7 @@ if ( $sorted.length ) { for ( column = 0; column < $sorted.length; column++ ) { if ( !$sorted[ column ].sortDisabled ) { - updateColumnSort( $sorted.eq( column ) , list[ indx ][ 1 ] ); + updateColumnSort( $sorted.eq( column ), list[ indx ][ 1 ] ); } } } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index efc3eba..909acc3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.29.2 *//* +/*! TableSorter (FORK) v2.29.3 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.29.2', + version : '2.29.3', parsers : [], widgets : [], @@ -1146,7 +1146,7 @@ if ( $sorted.length ) { for ( column = 0; column < $sorted.length; column++ ) { if ( !$sorted[ column ].sortDisabled ) { - updateColumnSort( $sorted.eq( column ) , list[ indx ][ 1 ] ); + updateColumnSort( $sorted.eq( column ), list[ indx ][ 1 ] ); } } } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index c12e0fe..2b19cae 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 12-13-2017 (v2.29.2)*/ +/*! tablesorter (FORK) - updated 01-10-2018 (v2.29.3)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js index a39de29..2a8b9b6 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js @@ -1,4 +1,4 @@ -/*! Parser: network - updated 5/17/2015 (v2.22.0) */ +/*! Parser: network - updated 2018-01-10 (v2.29.3) */ /* IPv4, IPv6 and MAC Addresses */ /*global jQuery: false */ ;(function($){ @@ -23,6 +23,10 @@ ipv6Validate : /^\s*((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/i }); + // used for internal testing; it's not useful to set this to true because the natural sort algorithm + // is set up to only sort solitary hex values ("ffff") vs separated hex values ("ffff.ffff") + ts.defaults.ipv6HexFormat = false; + ts.addParser({ id: 'ipv6Address', is: function(s) { @@ -72,10 +76,10 @@ ('00000' + (parseInt(groups[i], 16) || 0)).slice(-5); expandedAddress += ( i != validGroupCount - 1) ? groups[i] + ':' : groups[i]; } - return hex ? expandedAddress : expandedAddress.replace(/:/g, ''); + return expandedAddress; }, // uses natural sort hex compare - type: 'numeric' + type: 'text' }); // ipv4 address @@ -83,14 +87,15 @@ ipv4Is = function(s) { return (/^\d{1,3}[\.]\d{1,3}[\.]\d{1,3}[\.]\d{1,3}$/).test(s); }; - ipv4Format = function(s, table) { - var i, a = s ? s.split('.') : '', - r = '', + ipv4Format = function(s) { + var i, + a = s ? s.split('.') : '', + r = [], l = a.length; for (i = 0; i < l; i++) { - r += ('000' + a[i]).slice(-3); + r.push(('000' + a[i]).slice(-3)); } - return s ? ts.formatFloat(r, table) : s; + return s ? r.join('.') : s; }; /*! Parser: ipv4Address (a.k.a. ipAddress) */ @@ -99,13 +104,13 @@ id: 'ipAddress', is: ipv4Is, format: ipv4Format, - type: 'numeric' + type: 'text' }); ts.addParser({ id: 'ipv4Address', is: ipv4Is, format: ipv4Format, - type: 'numeric' + type: 'text' }); /*! Parser: MAC address */ @@ -118,21 +123,21 @@ }, format : function( str ) { var indx, len, - mac = '', + mac = [], val = ( str || '' ).replace( /[:.-]/g, '' ).match( /\w{2}/g ); if ( val ) { // not assuming all mac addresses in the column will end up with six // groups of two to process, so it's not actually validating the address len = val.length; for ( indx = 0; indx < len; indx++ ) { - mac += ( '000' + parseInt( val[ indx ], 16 ) ).slice( -3 ); + mac.push(( '000' + parseInt( val[ indx ], 16 ) ).slice( -3 )); } - return mac; + return mac.join('.'); } return str; }, // uses natural sort hex compare - type : 'numeric' + type : 'text' }); })( jQuery ); diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_4.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_4.css index 1d1140e..32241bf 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_4.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_4.css @@ -20,9 +20,9 @@ cursor: pointer; white-space: normal; } -.tablesorter-bootstrap:not(.table-inverse) thead:not(.thead-inverse) .tablesorter-header, -.tablesorter-bootstrap:not(.table-inverse) tfoot th, -.tablesorter-bootstrap:not(.table-inverse) tfoot td { +.tablesorter-bootstrap:not(.table-dark) thead:not(.thead-dark) .tablesorter-header, +.tablesorter-bootstrap:not(.table-dark) tfoot th, +.tablesorter-bootstrap:not(.table-dark) tfoot td { background-color: #eee; } @@ -51,33 +51,33 @@ } /* white icons */ -.tablesorter-bootstrap thead.thead-inverse .tablesorter-headerUnSorted:not(.sorter-false), -.tablesorter-bootstrap.table-inverse thead .tablesorter-headerUnSorted:not(.sorter-false) { +.tablesorter-bootstrap thead.thead-dark .tablesorter-headerUnSorted:not(.sorter-false), +.tablesorter-bootstrap.table-dark thead .tablesorter-headerUnSorted:not(.sorter-false) { background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE0IDIwIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTQgMTNsLTIuNS0yLjVMNyAxNWwtNC41LTQuNUwwIDEzbDcgN3pNMTQgNy41TDExLjUgMTAgNyA1LjUgMi41IDEwIDAgNy41bDctN3oiLz48L3N2Zz4=); } -.tablesorter-bootstrap thead.thead-inverse .tablesorter-headerAsc, -.tablesorter-bootstrap.table-inverse thead .tablesorter-headerAsc { +.tablesorter-bootstrap thead.thead-dark .tablesorter-headerAsc, +.tablesorter-bootstrap.table-dark thead .tablesorter-headerAsc { background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxMiIgdmlld0JveD0iMCAwIDE0IDE0Ij48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTQgOS41TDExLjUgMTIgNyA3LjUgMi41IDEyIDAgOS41bDctN3oiLz48L3N2Zz4=); } -.tablesorter-bootstrap thead.thead-inverse .tablesorter-headerDesc, -.tablesorter-bootstrap.table-inverse thead .tablesorter-headerDesc { +.tablesorter-bootstrap thead.thead-dark .tablesorter-headerDesc, +.tablesorter-bootstrap.table-dark thead .tablesorter-headerDesc { background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMiIgaGVpZ2h0PSIxMiIgdmlld0JveD0iMCAwIDE0IDE0Ij48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTQgNWwtMi41LTIuNS00LjUgNC41LTQuNS00LjVMMCA1IDcgMTJ6Ii8+PC9zdmc+); } /* since bootstrap (table-striped) uses nth-child(), we just use this to add a zebra stripe color */ -.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.odd > td, -.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.tablesorter-hasChildRow.odd:hover ~ tr.tablesorter-hasChildRow.odd ~ .tablesorter-childRow.odd > td { +.tablesorter-bootstrap:not(.table-dark) > tbody > tr.odd > td, +.tablesorter-bootstrap:not(.table-dark) > tbody > tr.tablesorter-hasChildRow.odd:hover ~ tr.tablesorter-hasChildRow.odd ~ .tablesorter-childRow.odd > td { background-color: #f9f9f9; } -.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.hover > td, -.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.odd:hover > td, -.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.even:hover > td, -.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.tablesorter-hasChildRow.odd:hover ~ .tablesorter-childRow.odd > td, -.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.tablesorter-hasChildRow.even:hover ~ .tablesorter-childRow.even > td { +.tablesorter-bootstrap:not(.table-dark) > tbody > tr.hover > td, +.tablesorter-bootstrap:not(.table-dark) > tbody > tr.odd:hover > td, +.tablesorter-bootstrap:not(.table-dark) > tbody > tr.even:hover > td, +.tablesorter-bootstrap:not(.table-dark) > tbody > tr.tablesorter-hasChildRow.odd:hover ~ .tablesorter-childRow.odd > td, +.tablesorter-bootstrap:not(.table-dark) > tbody > tr.tablesorter-hasChildRow.even:hover ~ .tablesorter-childRow.even > td { background-color: #f5f5f5; } -.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.even > td, -.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.tablesorter-hasChildRow.even:hover ~ tr.tablesorter-hasChildRow.even ~ .tablesorter-childRow.even > td { +.tablesorter-bootstrap:not(.table-dark) > tbody > tr.even > td, +.tablesorter-bootstrap:not(.table-dark) > tbody > tr.tablesorter-hasChildRow.even:hover ~ tr.tablesorter-hasChildRow.even ~ .tablesorter-childRow.even > td { background-color: #fff; } @@ -89,30 +89,30 @@ } /* Column Widget - column sort colors */ -.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.odd td.primary { +.tablesorter-bootstrap:not(.table-dark) > tbody > tr.odd td.primary { background-color: #bfbfbf; } -.tablesorter-bootstrap:not(.table-inverse) > tbody > tr td.primary, -.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.even td.primary { +.tablesorter-bootstrap:not(.table-dark) > tbody > tr td.primary, +.tablesorter-bootstrap:not(.table-dark) > tbody > tr.even td.primary { background-color: #d9d9d9; } -.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.odd td.secondary { +.tablesorter-bootstrap:not(.table-dark) > tbody > tr.odd td.secondary { background-color: #d9d9d9; } -.tablesorter-bootstrap:not(.table-inverse) > tbody > tr td.secondary, -.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.even td.secondary { +.tablesorter-bootstrap:not(.table-dark) > tbody > tr td.secondary, +.tablesorter-bootstrap:not(.table-dark) > tbody > tr.even td.secondary { background-color: #e6e6e6; } -.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.odd td.tertiary { +.tablesorter-bootstrap:not(.table-dark) > tbody > tr.odd td.tertiary { background-color: #e6e6e6; } -.tablesorter-bootstrap:not(.table-inverse) > tbody > tr td.tertiary, -.tablesorter-bootstrap:not(.table-inverse) > tbody > tr.even td.tertiary { +.tablesorter-bootstrap:not(.table-dark) > tbody > tr td.tertiary, +.tablesorter-bootstrap:not(.table-dark) > tbody > tr.even td.tertiary { background-color: #f2f2f2; } /* caption */ -.tablesorter-bootstrap:not(.table-inverse) .caption { +.tablesorter-bootstrap:not(.table-dark) .caption { background-color: #fff; } @@ -129,11 +129,11 @@ -o-transition: height 0.1s ease; transition: height 0.1s ease; } -.tablesorter-bootstrap:not(.table-inverse) .tablesorter-filter-row { +.tablesorter-bootstrap:not(.table-dark) .tablesorter-filter-row { background-color: #efefef; } -.tablesorter-bootstrap:not(.table-inverse) .tablesorter-filter-row input.tablesorter-filter, -.tablesorter-bootstrap:not(.table-inverse) .tablesorter-filter-row select.tablesorter-filter { +.tablesorter-bootstrap:not(.table-dark) .tablesorter-filter-row input.tablesorter-filter, +.tablesorter-bootstrap:not(.table-dark) .tablesorter-filter-row select.tablesorter-filter { color: #333; } @@ -144,7 +144,7 @@ transition: height 0.1s ease; } -.tablesorter-bootstrap:not(.table-inverse) .tablesorter-filter-row td { +.tablesorter-bootstrap:not(.table-dark) .tablesorter-filter-row td { line-height: normal; text-align: center; padding: 4px 6px; @@ -176,13 +176,12 @@ } /* pager plugin */ - .tablesorter-bootstrap .tablesorter-pager .pagedisplay { border: 0; } /* ajax error row */ -.tablesorter:not(.table-inverse) .tablesorter-errorRow td { +.tablesorter:not(.table-dark) .tablesorter-errorRow td { text-align: center; cursor: pointer; background-color: #e6bf99; From e166247ed70dfec62fcbfcb247330d46d692ac7c Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Fri, 19 Jan 2018 17:09:07 +0100 Subject: [PATCH 121/138] Update tablesorter to latest version (2.29.4) --- CHANGELOG.md | 4 +++ README.md | 4 +-- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 26 +++++++++++++++---- .../jquery-tablesorter/jquery.tablesorter.js | 4 +-- .../jquery.tablesorter.widgets.js | 22 +++++++++++++--- .../widget-filter-formatter-select2.js | 6 ++--- .../widgets/widget-filter.js | 20 ++++++++++++-- 9 files changed, 71 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29d8385..4369448 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.24.3 (2018-01-11) + +* Upgrade tablesorter to v2.29.4 + #### v1.24.2 (2018-01-11) * Upgrade tablesorter to v2.29.3 diff --git a/README.md b/README.md index 3d6e1d0..53747cf 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.29.3 (2018-01-10) [documentation] +Current tablesorter version: 2.29.4 (2018-01-18) [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. @@ -24,7 +24,7 @@ Or install it yourself as: ## Requirements -It should work with Rails 3.2 and higher (tested up to 5) as well as with ruby 1.9.3 - 2.4.x. +It should work with Rails 3.2 and higher as well as with ruby 1.9.3 - 2.5.x. Each release is always tested with the latest version of both. ## Usage diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 9e6b666..bae1fc8 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 24 - TINY = 2 + TINY = 3 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 3c127e1..bf73390 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 3c127e1125ec5b1a9c37db5dbfcbaae0ee112df1 +Subproject commit bf7339036ce11c91f55281825b1b601d18b67ed8 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index e6bdb32..9dafc72 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 01-10-2018 (v2.29.3)*/ +/*! tablesorter (FORK) - updated 2018-01-18 (v2.29.4)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.29.3 *//* +/*! TableSorter (FORK) v2.29.4 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.29.3', + version : '2.29.4', parsers : [], widgets : [], @@ -3330,6 +3330,7 @@ filter_excludeFilter : {}, // filters to exclude, per column filter_external : '', // jQuery selector string ( or jQuery object ) of external filters filter_filteredRow : 'filtered', // class added to filtered rows; define in css with "display:none" to hide the filtered-out rows + filter_filterLabel : 'Filter "{{label}}" column by...', // Aria-label added to filter input/select; see #1495 filter_formatter : null, // add custom filter elements to the filter row filter_functions : null, // add custom filter functions using this option filter_hideEmpty : true, // hide filter row when table is empty @@ -3986,7 +3987,7 @@ cellFilter = wo.filter_cellFilter, columns = c.columns, arry = $.isArray( cellFilter ), - buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; + buildFilter = '<tr role="search" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; for ( column = 0; column < columns; column++ ) { if ( c.$headerIndexed[ column ].length ) { // account for entire column set with colspan. See #1047 @@ -4055,7 +4056,22 @@ ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : wo.filter_cssFilter ) || ''; // copy data-column from table cell (it will include colspan) - buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', $filter.attr( 'data-column' ) ); + buildFilter.addClass( tscss.filter + ' ' + name ); + name = wo.filter_filterLabel; + tmp = name.match(/{{([^}]+?)}}/g); + if (!tmp) { + tmp = ['{{label}}']; + } + $.each(tmp, function(indx, attr) { + var regex = new RegExp(attr, 'g'), + data = $header.attr('data-' + attr.replace(/{{|}}/g, '')), + text = typeof data === 'undefined' ? $header.text() : data; + name = name.replace( regex, $.trim( text ) ); + }); + buildFilter.attr({ + 'data-column': $filter.attr( 'data-column' ), + 'aria-label': name + }); if ( disabled ) { buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 909acc3..4785721 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.29.3 *//* +/*! TableSorter (FORK) v2.29.4 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.29.3', + version : '2.29.4', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 2b19cae..a5fd4d8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 01-10-2018 (v2.29.3)*/ +/*! tablesorter (FORK) - updated 2018-01-18 (v2.29.4)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -450,6 +450,7 @@ filter_excludeFilter : {}, // filters to exclude, per column filter_external : '', // jQuery selector string ( or jQuery object ) of external filters filter_filteredRow : 'filtered', // class added to filtered rows; define in css with "display:none" to hide the filtered-out rows + filter_filterLabel : 'Filter "{{label}}" column by...', // Aria-label added to filter input/select; see #1495 filter_formatter : null, // add custom filter elements to the filter row filter_functions : null, // add custom filter functions using this option filter_hideEmpty : true, // hide filter row when table is empty @@ -1106,7 +1107,7 @@ cellFilter = wo.filter_cellFilter, columns = c.columns, arry = $.isArray( cellFilter ), - buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; + buildFilter = '<tr role="search" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; for ( column = 0; column < columns; column++ ) { if ( c.$headerIndexed[ column ].length ) { // account for entire column set with colspan. See #1047 @@ -1175,7 +1176,22 @@ ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : wo.filter_cssFilter ) || ''; // copy data-column from table cell (it will include colspan) - buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', $filter.attr( 'data-column' ) ); + buildFilter.addClass( tscss.filter + ' ' + name ); + name = wo.filter_filterLabel; + tmp = name.match(/{{([^}]+?)}}/g); + if (!tmp) { + tmp = ['{{label}}']; + } + $.each(tmp, function(indx, attr) { + var regex = new RegExp(attr, 'g'), + data = $header.attr('data-' + attr.replace(/{{|}}/g, '')), + text = typeof data === 'undefined' ? $header.text() : data; + name = name.replace( regex, $.trim( text ) ); + }); + buildFilter.attr({ + 'data-column': $filter.attr( 'data-column' ), + 'aria-label': name + }); if ( disabled ) { buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js index e1eeee0..e04fb75 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js @@ -70,7 +70,7 @@ if (arry) { v = v.split('\u0000'); } - if (!ts.isEmptyObject($input.find('.select2').data())) { + if (!ts.isEmptyObject($cell.find('.select2').data())) { $input // add regex, so we filter exact numbers .val( @@ -78,8 +78,8 @@ '/(' + matchPrefix + (v || []).join(matchSuffix + '|' + matchPrefix) + matchSuffix + ')/' + flags : '' ) - .trigger('search').end() - .find('.select2').select2('val', v); + .trigger('search'); + $cell.find('.select2').select2('val', v); // update sticky header cell if (c.widgetOptions.$sticky) { c.widgetOptions.$sticky.find('.select2col' + indx + ' .select2').select2('val', v); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index a1a4c03..60aeac5 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -40,6 +40,7 @@ filter_excludeFilter : {}, // filters to exclude, per column filter_external : '', // jQuery selector string ( or jQuery object ) of external filters filter_filteredRow : 'filtered', // class added to filtered rows; define in css with "display:none" to hide the filtered-out rows + filter_filterLabel : 'Filter "{{label}}" column by...', // Aria-label added to filter input/select; see #1495 filter_formatter : null, // add custom filter elements to the filter row filter_functions : null, // add custom filter functions using this option filter_hideEmpty : true, // hide filter row when table is empty @@ -696,7 +697,7 @@ cellFilter = wo.filter_cellFilter, columns = c.columns, arry = $.isArray( cellFilter ), - buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; + buildFilter = '<tr role="search" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; for ( column = 0; column < columns; column++ ) { if ( c.$headerIndexed[ column ].length ) { // account for entire column set with colspan. See #1047 @@ -765,7 +766,22 @@ ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : wo.filter_cssFilter ) || ''; // copy data-column from table cell (it will include colspan) - buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', $filter.attr( 'data-column' ) ); + buildFilter.addClass( tscss.filter + ' ' + name ); + name = wo.filter_filterLabel; + tmp = name.match(/{{([^}]+?)}}/g); + if (!tmp) { + tmp = ['{{label}}']; + } + $.each(tmp, function(indx, attr) { + var regex = new RegExp(attr, 'g'), + data = $header.attr('data-' + attr.replace(/{{|}}/g, '')), + text = typeof data === 'undefined' ? $header.text() : data; + name = name.replace( regex, $.trim( text ) ); + }); + buildFilter.attr({ + 'data-column': $filter.attr( 'data-column' ), + 'aria-label': name + }); if ( disabled ) { buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; } From f3106149cd1faca8a43c7a65a58a4541d775c99d Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Wed, 31 Jan 2018 18:23:11 +0100 Subject: [PATCH 122/138] Update tablesorter to latest version (2.29.5) --- CHANGELOG.md | 6 +++- README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 31 +++++++++++-------- .../jquery-tablesorter/jquery.tablesorter.js | 4 +-- .../jquery.tablesorter.widgets.js | 27 +++++++++------- .../parsers/parser-input-select.js | 15 ++++++++- .../widget-filter-formatter-select2.js | 2 +- .../widgets/widget-filter.js | 25 +++++++++------ 10 files changed, 74 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4369448..98acd71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,11 @@ Changelog === -#### v1.24.3 (2018-01-11) +#### v1.24.4 (2018-01-31) + +* Upgrade tablesorter to v2.29.5 + +#### v1.24.3 (2018-01-19) * Upgrade tablesorter to v2.29.4 diff --git a/README.md b/README.md index 53747cf..455e080 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.29.4 (2018-01-18) [documentation] +Current tablesorter version: 2.29.5 (2018-01-30) [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index bae1fc8..786ac93 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 24 - TINY = 3 + TINY = 4 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index bf73390..361a0d8 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit bf7339036ce11c91f55281825b1b601d18b67ed8 +Subproject commit 361a0d89f5e06347cf551bf5568956ad3956e250 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 9dafc72..fafa9fc 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-01-18 (v2.29.4)*/ +/*! tablesorter (FORK) - updated 2018-01-30 (v2.29.5)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.29.4 *//* +/*! TableSorter (FORK) v2.29.5 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.29.4', + version : '2.29.5', parsers : [], widgets : [], @@ -3288,7 +3288,7 @@ })(jQuery); -/*! Widget: filter - updated 12/13/2017 (v2.29.1) *//* +/*! Widget: filter - updated 2018-01-30 (v2.29.5) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3657,7 +3657,12 @@ var options, string, txt, $header, column, val, fxn, noSelect, c = table.config, - wo = c.widgetOptions; + wo = c.widgetOptions, + processStr = function(prefix, str, suffix) { + str = str.trim(); + // don't include prefix/suffix if str is empty + return str === '' ? '' : (prefix || '') + str + (suffix || ''); + }; c.$table.addClass( 'hasFilters' ); c.lastSearch = []; @@ -3673,13 +3678,13 @@ $.extend( tsfRegex, { child : new RegExp( c.cssChildRow ), filtered : new RegExp( wo.filter_filteredRow ), - alreadyFiltered : new RegExp( '(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i' ), - toTest : new RegExp( '\\s+(-|' + ts.language.to + ')\\s+', 'i' ), - toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)', 'gi' ), - andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), - andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), - orTest : new RegExp( '(\\||\\s+' + ts.language.or + '\\s+)', 'i' ), - orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ), + alreadyFiltered : new RegExp( '(\\s+(-' + processStr('|', ts.language.or) + processStr('|', ts.language.to) + ')\\s+)', 'i' ), + toTest : new RegExp( '\\s+(-' + processStr('|', ts.language.to) + ')\\s+', 'i' ), + toSplit : new RegExp( '(?:\\s+(?:-' + processStr('|', ts.language.to) + ')\\s+)', 'gi' ), + andTest : new RegExp( '\\s+(' + processStr('', ts.language.and, '|') + '&&)\\s+', 'i' ), + andSplit : new RegExp( '(?:\\s+(?:' + processStr('', ts.language.and, '|') + '&&)\\s+)', 'gi' ), + orTest : new RegExp( '(\\|' + processStr('|\\s+', ts.language.or, '\\s+') + ')', 'i' ), + orSplit : new RegExp( '(?:\\|' + processStr('|\\s+(?:', ts.language.or, ')\\s+') + ')', 'gi' ), iQuery : new RegExp( val, 'i' ), igQuery : new RegExp( val, 'ig' ), operTest : /^[<>]=?/, @@ -4060,7 +4065,7 @@ name = wo.filter_filterLabel; tmp = name.match(/{{([^}]+?)}}/g); if (!tmp) { - tmp = ['{{label}}']; + tmp = [ '{{label}}' ]; } $.each(tmp, function(indx, attr) { var regex = new RegExp(attr, 'g'), diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 4785721..74e04a5 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.29.4 *//* +/*! TableSorter (FORK) v2.29.5 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.29.4', + version : '2.29.5', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index a5fd4d8..2575d38 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-01-18 (v2.29.4)*/ +/*! tablesorter (FORK) - updated 2018-01-30 (v2.29.5)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -408,7 +408,7 @@ })(jQuery); -/*! Widget: filter - updated 12/13/2017 (v2.29.1) *//* +/*! Widget: filter - updated 2018-01-30 (v2.29.5) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -777,7 +777,12 @@ var options, string, txt, $header, column, val, fxn, noSelect, c = table.config, - wo = c.widgetOptions; + wo = c.widgetOptions, + processStr = function(prefix, str, suffix) { + str = str.trim(); + // don't include prefix/suffix if str is empty + return str === '' ? '' : (prefix || '') + str + (suffix || ''); + }; c.$table.addClass( 'hasFilters' ); c.lastSearch = []; @@ -793,13 +798,13 @@ $.extend( tsfRegex, { child : new RegExp( c.cssChildRow ), filtered : new RegExp( wo.filter_filteredRow ), - alreadyFiltered : new RegExp( '(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i' ), - toTest : new RegExp( '\\s+(-|' + ts.language.to + ')\\s+', 'i' ), - toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)', 'gi' ), - andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), - andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), - orTest : new RegExp( '(\\||\\s+' + ts.language.or + '\\s+)', 'i' ), - orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ), + alreadyFiltered : new RegExp( '(\\s+(-' + processStr('|', ts.language.or) + processStr('|', ts.language.to) + ')\\s+)', 'i' ), + toTest : new RegExp( '\\s+(-' + processStr('|', ts.language.to) + ')\\s+', 'i' ), + toSplit : new RegExp( '(?:\\s+(?:-' + processStr('|', ts.language.to) + ')\\s+)', 'gi' ), + andTest : new RegExp( '\\s+(' + processStr('', ts.language.and, '|') + '&&)\\s+', 'i' ), + andSplit : new RegExp( '(?:\\s+(?:' + processStr('', ts.language.and, '|') + '&&)\\s+)', 'gi' ), + orTest : new RegExp( '(\\|' + processStr('|\\s+', ts.language.or, '\\s+') + ')', 'i' ), + orSplit : new RegExp( '(?:\\|' + processStr('|\\s+(?:', ts.language.or, ')\\s+') + ')', 'gi' ), iQuery : new RegExp( val, 'i' ), igQuery : new RegExp( val, 'ig' ), operTest : /^[<>]=?/, @@ -1180,7 +1185,7 @@ name = wo.filter_filterLabel; tmp = name.match(/{{([^}]+?)}}/g); if (!tmp) { - tmp = ['{{label}}']; + tmp = [ '{{label}}' ]; } $.each(tmp, function(indx, attr) { var regex = new RegExp(attr, 'g'), diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index cdb1b02..0742953 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,4 +1,4 @@ -/*! Parser: input & select - updated 5/16/2017 (v2.28.10) *//* +/*! Parser: input & select - updated 2018-01-30 (v2.29.5) *//* * for jQuery 1.7+ & tablesorter 2.7.11+ * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ @@ -66,6 +66,19 @@ type : 'text' }); + $.tablesorter.addParser({ + id: 'radio', + is: function() { + return false; + }, + format: function(txt, table, cell) { + var $input = $(cell).find('input:checked'); + return $input.length ? $input.val() : txt; + }, + parsed: true, + type: 'text' + }); + // Custom parser which returns the currently selected options // updated dynamically using the 'change' function below $.tablesorter.addParser({ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js index e04fb75..b43d00b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js @@ -1,4 +1,4 @@ -/*! Widget: filter, select2 formatter function - updated 7/11/2016 (v2.26.6) *//* +/*! Widget: filter, select2 formatter function - updated 1/18/2018 (v2.29.4) *//* * requires: jQuery 1.7.2+, tableSorter (FORK) 2.16+, filter widget 2.16+ and select2 v3.4.6+ plugin (this code is NOT compatible with select2 v4+) */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 60aeac5..b22806a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 12/13/2017 (v2.29.1) *//* +/*! Widget: filter - updated 2018-01-30 (v2.29.5) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -367,7 +367,12 @@ var options, string, txt, $header, column, val, fxn, noSelect, c = table.config, - wo = c.widgetOptions; + wo = c.widgetOptions, + processStr = function(prefix, str, suffix) { + str = str.trim(); + // don't include prefix/suffix if str is empty + return str === '' ? '' : (prefix || '') + str + (suffix || ''); + }; c.$table.addClass( 'hasFilters' ); c.lastSearch = []; @@ -383,13 +388,13 @@ $.extend( tsfRegex, { child : new RegExp( c.cssChildRow ), filtered : new RegExp( wo.filter_filteredRow ), - alreadyFiltered : new RegExp( '(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i' ), - toTest : new RegExp( '\\s+(-|' + ts.language.to + ')\\s+', 'i' ), - toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)', 'gi' ), - andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ), - andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ), - orTest : new RegExp( '(\\||\\s+' + ts.language.or + '\\s+)', 'i' ), - orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ), + alreadyFiltered : new RegExp( '(\\s+(-' + processStr('|', ts.language.or) + processStr('|', ts.language.to) + ')\\s+)', 'i' ), + toTest : new RegExp( '\\s+(-' + processStr('|', ts.language.to) + ')\\s+', 'i' ), + toSplit : new RegExp( '(?:\\s+(?:-' + processStr('|', ts.language.to) + ')\\s+)', 'gi' ), + andTest : new RegExp( '\\s+(' + processStr('', ts.language.and, '|') + '&&)\\s+', 'i' ), + andSplit : new RegExp( '(?:\\s+(?:' + processStr('', ts.language.and, '|') + '&&)\\s+)', 'gi' ), + orTest : new RegExp( '(\\|' + processStr('|\\s+', ts.language.or, '\\s+') + ')', 'i' ), + orSplit : new RegExp( '(?:\\|' + processStr('|\\s+(?:', ts.language.or, ')\\s+') + ')', 'gi' ), iQuery : new RegExp( val, 'i' ), igQuery : new RegExp( val, 'ig' ), operTest : /^[<>]=?/, @@ -770,7 +775,7 @@ name = wo.filter_filterLabel; tmp = name.match(/{{([^}]+?)}}/g); if (!tmp) { - tmp = ['{{label}}']; + tmp = [ '{{label}}' ]; } $.each(tmp, function(indx, attr) { var regex = new RegExp(attr, 'g'), From 8e9b66267b8fade5b7ade6278c5cb30b9cf7408e Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Sun, 25 Feb 2018 22:15:41 +0100 Subject: [PATCH 123/138] Update tablesorter to latest version (2.29.6) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 38 +++++++++++-------- .../jquery-tablesorter/jquery.tablesorter.js | 4 +- .../jquery.tablesorter.widgets.js | 34 ++++++++++------- .../widgets/widget-resizable.js | 6 +-- .../widgets/widget-scroller.js | 27 +++++++++---- .../widgets/widget-stickyHeaders.js | 26 ++++++++----- 10 files changed, 90 insertions(+), 55 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98acd71..a78ae94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.24.5 (2018-02-25) + +* Upgrade tablesorter to v2.29.6 + #### v1.24.4 (2018-01-31) * Upgrade tablesorter to v2.29.5 diff --git a/README.md b/README.md index 455e080..d696a3f 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.29.5 (2018-01-30) [documentation] +Current tablesorter version: 2.29.6 (2018-02-25) [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 786ac93..b7cf82a 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 24 - TINY = 4 + TINY = 5 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 361a0d8..e542aeb 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 361a0d89f5e06347cf551bf5568956ad3956e250 +Subproject commit e542aebf1c06ccf913b3167f61f6167857634f8d diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index fafa9fc..6d0cffc 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-01-30 (v2.29.5)*/ +/*! tablesorter (FORK) - updated 2018-02-25 (v2.29.6)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.29.5 *//* +/*! TableSorter (FORK) v2.29.6 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.29.5', + version : '2.29.6', parsers : [], widgets : [], @@ -5333,7 +5333,7 @@ var index, len, $t, $table = c.$table, // add position: relative to attach element, hopefully it won't cause trouble. - $attach = $(wo.stickyHeaders_attachTo), + $attach = $(wo.stickyHeaders_attachTo || wo.stickyHeaders_appendTo), namespace = c.namespace + 'stickyheaders ', // element to watch for the scroll event $yScroll = $(wo.stickyHeaders_yScroll || wo.stickyHeaders_attachTo || window), @@ -5393,7 +5393,10 @@ }); } }, - getLeftPosition = function() { + getLeftPosition = function(yWindow) { + if (yWindow === false && $nestedSticky.length) { + return $table.position().left; + } return $attach.length ? parseInt($attach.css('padding-left'), 10) || 0 : $table.offset().left - parseInt($table.css('margin-left'), 10) - $(window).scrollLeft(); @@ -5414,9 +5417,11 @@ offset = $table.offset(), stickyOffset = getStickyOffset(c, wo), yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 - attachTop = $attach.length ? - ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : - $yScroll.scrollTop(), + yScroll = yWindow ? + $yScroll.scrollTop() : + // use parent sticky position if nested AND inside of a scrollable element - see #1512 + $nestedSticky.length ? parseInt($nestedSticky[0].style.top, 10) : $yScroll.offset().top, + attachTop = $attach.length ? yScroll : $yScroll.scrollTop(), captionHeight = wo.stickyHeaders_includeCaption ? 0 : $table.children( 'caption' ).height() || 0, scrollTop = attachTop + stickyOffset + nestedStickyTop - captionHeight, tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)) - captionHeight, @@ -5430,12 +5435,14 @@ cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); } // adjust when scrolling horizontally - fixes issue #143 - tmp = getLeftPosition(); + tmp = getLeftPosition(yWindow); if (tmp !== parseInt($stickyWrap.css('left'), 10)) { needsUpdating = true; cssSettings.left = tmp; } - cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; + cssSettings.top = ( cssSettings.top || 0 ) + + // If nested AND inside of a scrollable element, only add parent sticky height + (!yWindow && $nestedSticky.length ? $nestedSticky.height() : stickyOffset + nestedStickyTop); if (needsUpdating) { $stickyWrap .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) @@ -5456,8 +5463,8 @@ if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; } // clear out cloned table, except for sticky header // include caption & filter row (fixes #126 & #249) - don't remove cells to get correct cell indexing - $stickyTable.find('thead:gt(0), tr.sticky-false').hide(); - $stickyTable.find('tbody, tfoot').remove(); + $stickyTable.find('> thead:gt(0), tr.sticky-false').hide(); + $stickyTable.find('> tbody, > tfoot').remove(); $stickyTable.find('caption').toggle(wo.stickyHeaders_includeCaption); // issue #172 - find td/th in sticky header $stickyCells = $stickyThead.children().children(); @@ -5489,7 +5496,6 @@ c.onRenderHeader.apply( $t.eq( index ), [ index, c, $stickyTable ] ); } } - // make it sticky! $xScroll.add($yScroll) .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') ) @@ -5560,7 +5566,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 12/13/2017 (v2.29.1) */ +/*! Widget: resizable - updated 2018-02-14 (v2.29.6) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -5951,7 +5957,7 @@ if ( vars.overflow && vars.tableWidth ) { ts.resizable.setWidth( c.$table, vars.tableWidth, true ); if ( vars.useStorage ) { - ts.storage( table, 'tablesorter-table-resized-width', 'auto' ); + ts.storage( table, 'tablesorter-table-resized-width', vars.tableWidth ); } } for ( index = 0; index < c.columns; index++ ) { @@ -5967,7 +5973,7 @@ // reset stickyHeader widths c.$table.triggerHandler( 'stickyHeadersUpdate' ); if ( ts.storage && !refreshing ) { - ts.storage( this, ts.css.resizableStorage, {} ); + ts.storage( this, ts.css.resizableStorage, [] ); } } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 74e04a5..a625c6e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.29.5 *//* +/*! TableSorter (FORK) v2.29.6 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.29.5', + version : '2.29.6', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 2575d38..46f3606 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-01-30 (v2.29.5)*/ +/*! tablesorter (FORK) - updated 2018-02-25 (v2.29.6)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -2453,7 +2453,7 @@ var index, len, $t, $table = c.$table, // add position: relative to attach element, hopefully it won't cause trouble. - $attach = $(wo.stickyHeaders_attachTo), + $attach = $(wo.stickyHeaders_attachTo || wo.stickyHeaders_appendTo), namespace = c.namespace + 'stickyheaders ', // element to watch for the scroll event $yScroll = $(wo.stickyHeaders_yScroll || wo.stickyHeaders_attachTo || window), @@ -2513,7 +2513,10 @@ }); } }, - getLeftPosition = function() { + getLeftPosition = function(yWindow) { + if (yWindow === false && $nestedSticky.length) { + return $table.position().left; + } return $attach.length ? parseInt($attach.css('padding-left'), 10) || 0 : $table.offset().left - parseInt($table.css('margin-left'), 10) - $(window).scrollLeft(); @@ -2534,9 +2537,11 @@ offset = $table.offset(), stickyOffset = getStickyOffset(c, wo), yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 - attachTop = $attach.length ? - ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : - $yScroll.scrollTop(), + yScroll = yWindow ? + $yScroll.scrollTop() : + // use parent sticky position if nested AND inside of a scrollable element - see #1512 + $nestedSticky.length ? parseInt($nestedSticky[0].style.top, 10) : $yScroll.offset().top, + attachTop = $attach.length ? yScroll : $yScroll.scrollTop(), captionHeight = wo.stickyHeaders_includeCaption ? 0 : $table.children( 'caption' ).height() || 0, scrollTop = attachTop + stickyOffset + nestedStickyTop - captionHeight, tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)) - captionHeight, @@ -2550,12 +2555,14 @@ cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); } // adjust when scrolling horizontally - fixes issue #143 - tmp = getLeftPosition(); + tmp = getLeftPosition(yWindow); if (tmp !== parseInt($stickyWrap.css('left'), 10)) { needsUpdating = true; cssSettings.left = tmp; } - cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; + cssSettings.top = ( cssSettings.top || 0 ) + + // If nested AND inside of a scrollable element, only add parent sticky height + (!yWindow && $nestedSticky.length ? $nestedSticky.height() : stickyOffset + nestedStickyTop); if (needsUpdating) { $stickyWrap .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) @@ -2576,8 +2583,8 @@ if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; } // clear out cloned table, except for sticky header // include caption & filter row (fixes #126 & #249) - don't remove cells to get correct cell indexing - $stickyTable.find('thead:gt(0), tr.sticky-false').hide(); - $stickyTable.find('tbody, tfoot').remove(); + $stickyTable.find('> thead:gt(0), tr.sticky-false').hide(); + $stickyTable.find('> tbody, > tfoot').remove(); $stickyTable.find('caption').toggle(wo.stickyHeaders_includeCaption); // issue #172 - find td/th in sticky header $stickyCells = $stickyThead.children().children(); @@ -2609,7 +2616,6 @@ c.onRenderHeader.apply( $t.eq( index ), [ index, c, $stickyTable ] ); } } - // make it sticky! $xScroll.add($yScroll) .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') ) @@ -2680,7 +2686,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 12/13/2017 (v2.29.1) */ +/*! Widget: resizable - updated 2018-02-14 (v2.29.6) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -3071,7 +3077,7 @@ if ( vars.overflow && vars.tableWidth ) { ts.resizable.setWidth( c.$table, vars.tableWidth, true ); if ( vars.useStorage ) { - ts.storage( table, 'tablesorter-table-resized-width', 'auto' ); + ts.storage( table, 'tablesorter-table-resized-width', vars.tableWidth ); } } for ( index = 0; index < c.columns; index++ ) { @@ -3087,7 +3093,7 @@ // reset stickyHeader widths c.$table.triggerHandler( 'stickyHeadersUpdate' ); if ( ts.storage && !refreshing ) { - ts.storage( this, ts.css.resizableStorage, {} ); + ts.storage( this, ts.css.resizableStorage, [] ); } } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js index de084c6..190d94c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js @@ -1,4 +1,4 @@ -/*! Widget: resizable - updated 12/13/2017 (v2.29.1) */ +/*! Widget: resizable - updated 2018-02-14 (v2.29.6) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -389,7 +389,7 @@ if ( vars.overflow && vars.tableWidth ) { ts.resizable.setWidth( c.$table, vars.tableWidth, true ); if ( vars.useStorage ) { - ts.storage( table, 'tablesorter-table-resized-width', 'auto' ); + ts.storage( table, 'tablesorter-table-resized-width', vars.tableWidth ); } } for ( index = 0; index < c.columns; index++ ) { @@ -405,7 +405,7 @@ // reset stickyHeader widths c.$table.triggerHandler( 'stickyHeadersUpdate' ); if ( ts.storage && !refreshing ) { - ts.storage( this, ts.css.resizableStorage, {} ); + ts.storage( this, ts.css.resizableStorage, [] ); } } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index dfe7b07..9e3d3aa 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -745,12 +745,18 @@ tsScroller = ts.scroller, fixedColumns = wo.scroller_fixedColumns, // get dimensions + getDim = function ($el, name, deflt) { + return parseInt( $el.css(name) || '', 10 ) || deflt || 0; + }, $temp = $table.find( 'tbody td' ), - borderRightWidth = parseInt( $temp.css( 'border-right-width' ), 10 ) || 1, - borderSpacing = parseInt( ( $temp.css( 'border-spacing' ) || '' ).split( /\s/ )[ 0 ], 10 ) / 2 || 0, - totalWidth = parseInt( $table.css( 'padding-left' ), 10 ) + - parseInt( $table.css( 'padding-right' ), 10 ) - - borderRightWidth, + borderRightWidth = getDim( $temp, 'border-right-width', 1 ), + borderSpacing = getDim( $temp, 'border-spacing', 0 ), + totalWidth = getDim( $table, 'padding-left' ) + + getDim( $table, 'padding-right' ) + + // include table left & row left border + getDim( $table, 'border-left-width', 1 ) * 2 + + getDim( $table, 'border-right-width', 1 ) - + borderRightWidth + borderSpacing / 2, widths = wo.scroller_calcWidths; ts.scroller.removeFixed( c, wo, false ); @@ -761,7 +767,7 @@ } // set fixed column width - totalWidth = totalWidth + borderRightWidth * 2 - borderSpacing; + totalWidth = totalWidth + borderRightWidth * 2; tsScroller.setWidth( $fixedColumn.add( $fixedColumn.children() ), totalWidth ); tsScroller.setWidth( $fixedColumn.children().children( 'table' ), totalWidth ); @@ -799,7 +805,6 @@ .css( 'width', totalWidth + adj ); } - $fixedColumn.removeClass( tscss.scrollerHideElement ); for ( index = 0; index < fixedColumns; index++ ) { temp = ':nth-child(' + ( index + 1 ) + ')'; $wrapper @@ -808,6 +813,14 @@ .find( 'th' + temp + ', td' + temp + ', col' + temp ) .addClass( tscss.scrollerHideColumn ); } + $fixedColumn + .removeClass( tscss.scrollerHideElement ) + .find('colgroup') + .each(function() { + $(this) + .find('col:gt(' + (fixedColumns - 1) + ')') + .addClass( tscss.scrollerHideElement ); + }); totalWidth = totalWidth - borderRightWidth; temp = $tableWrap.parent().innerWidth() - totalWidth; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js index 5cc596a..89b027a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js @@ -90,7 +90,7 @@ var index, len, $t, $table = c.$table, // add position: relative to attach element, hopefully it won't cause trouble. - $attach = $(wo.stickyHeaders_attachTo), + $attach = $(wo.stickyHeaders_attachTo || wo.stickyHeaders_appendTo), namespace = c.namespace + 'stickyheaders ', // element to watch for the scroll event $yScroll = $(wo.stickyHeaders_yScroll || wo.stickyHeaders_attachTo || window), @@ -150,7 +150,10 @@ }); } }, - getLeftPosition = function() { + getLeftPosition = function(yWindow) { + if (yWindow === false && $nestedSticky.length) { + return $table.position().left; + } return $attach.length ? parseInt($attach.css('padding-left'), 10) || 0 : $table.offset().left - parseInt($table.css('margin-left'), 10) - $(window).scrollLeft(); @@ -171,9 +174,11 @@ offset = $table.offset(), stickyOffset = getStickyOffset(c, wo), yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 - attachTop = $attach.length ? - ( yWindow ? $yScroll.scrollTop() : $yScroll.offset().top ) : - $yScroll.scrollTop(), + yScroll = yWindow ? + $yScroll.scrollTop() : + // use parent sticky position if nested AND inside of a scrollable element - see #1512 + $nestedSticky.length ? parseInt($nestedSticky[0].style.top, 10) : $yScroll.offset().top, + attachTop = $attach.length ? yScroll : $yScroll.scrollTop(), captionHeight = wo.stickyHeaders_includeCaption ? 0 : $table.children( 'caption' ).height() || 0, scrollTop = attachTop + stickyOffset + nestedStickyTop - captionHeight, tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)) - captionHeight, @@ -187,12 +192,14 @@ cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); } // adjust when scrolling horizontally - fixes issue #143 - tmp = getLeftPosition(); + tmp = getLeftPosition(yWindow); if (tmp !== parseInt($stickyWrap.css('left'), 10)) { needsUpdating = true; cssSettings.left = tmp; } - cssSettings.top = ( cssSettings.top || 0 ) + stickyOffset + nestedStickyTop; + cssSettings.top = ( cssSettings.top || 0 ) + + // If nested AND inside of a scrollable element, only add parent sticky height + (!yWindow && $nestedSticky.length ? $nestedSticky.height() : stickyOffset + nestedStickyTop); if (needsUpdating) { $stickyWrap .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) @@ -213,8 +220,8 @@ if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; } // clear out cloned table, except for sticky header // include caption & filter row (fixes #126 & #249) - don't remove cells to get correct cell indexing - $stickyTable.find('thead:gt(0), tr.sticky-false').hide(); - $stickyTable.find('tbody, tfoot').remove(); + $stickyTable.find('> thead:gt(0), tr.sticky-false').hide(); + $stickyTable.find('> tbody, > tfoot').remove(); $stickyTable.find('caption').toggle(wo.stickyHeaders_includeCaption); // issue #172 - find td/th in sticky header $stickyCells = $stickyThead.children().children(); @@ -246,7 +253,6 @@ c.onRenderHeader.apply( $t.eq( index ), [ index, c, $stickyTable ] ); } } - // make it sticky! $xScroll.add($yScroll) .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') ) From 7bc2c634ebe446b465f1e50502d8a76b3dd0bf18 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Thu, 22 Mar 2018 22:32:10 +0100 Subject: [PATCH 124/138] Update tablesorter to latest version (2.30.1) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 4 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 170 ++++++------- .../beta-testing/widget-reorder.js | 35 ++- .../jquery.tablesorter.combined.js | 229 ++++++++++-------- .../jquery-tablesorter/jquery.tablesorter.js | 128 +++++----- .../jquery.tablesorter.widgets.js | 101 ++++---- .../parsers/parser-date-extract.js | 2 +- .../parsers/parser-date-iso8601.js | 2 +- .../parsers/parser-date-month.js | 2 +- .../parsers/parser-date-range.js | 2 +- .../parsers/parser-date-two-digit-year.js | 4 +- .../parsers/parser-date-weekday.js | 2 +- .../jquery-tablesorter/parsers/parser-date.js | 2 +- .../parsers/parser-duration.js | 2 +- .../parsers/parser-feet-inch-fraction.js | 4 +- .../parsers/parser-file-type.js | 4 +- .../parsers/parser-ignore-articles.js | 2 +- .../parsers/parser-image.js | 4 +- .../parsers/parser-input-select.js | 2 +- .../parsers/parser-leading-zeros.js | 1 + .../parsers/parser-named-numbers.js | 2 +- .../parsers/parser-network.js | 8 +- .../parsers/parser-roman.js | 10 +- .../widgets/widget-alignChar.js | 30 +-- .../widgets/widget-build-table.js | 46 ++-- .../widgets/widget-chart.js | 2 +- .../widgets/widget-columnSelector.js | 40 +-- .../widgets/widget-cssStickyHeaders.js | 2 +- .../widgets/widget-filter-formatter-html5.js | 65 ++--- .../widgets/widget-filter-formatter-jui.js | 92 +++---- .../widget-filter-formatter-select2.js | 20 +- .../widgets/widget-filter-type-insideRange.js | 2 +- .../widgets/widget-filter.js | 30 ++- .../widgets/widget-formatter.js | 4 +- .../widgets/widget-grouping.js | 29 ++- .../widgets/widget-headerTitles.js | 17 +- .../widgets/widget-lazyload.js | 70 +++--- .../jquery-tablesorter/widgets/widget-math.js | 17 +- .../widgets/widget-output.js | 13 +- .../widgets/widget-pager.js | 116 ++++----- .../widgets/widget-print.js | 2 +- .../widgets/widget-reflow.js | 22 +- .../widgets/widget-repeatheaders.js | 4 +- .../widgets/widget-resizable.js | 6 +- .../widgets/widget-saveSort.js | 35 ++- .../widgets/widget-scroller.js | 7 +- .../widgets/widget-sort2Hash.js | 4 +- .../widgets/widget-sortTbodies.js | 2 +- .../widgets/widget-staticRow.js | 14 +- .../widgets/widget-stickyHeaders.js | 4 +- .../widgets/widget-storage.js | 11 +- .../widgets/widget-uitheme.js | 13 +- .../widgets/widget-vertical-group.js | 22 +- .../jquery-tablesorter/widgets/widget-view.js | 6 +- 57 files changed, 783 insertions(+), 694 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a78ae94..0604356 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.25.0 (2018-03-22) + +* Upgrade tablesorter to v2.30.1 + #### v1.24.5 (2018-02-25) * Upgrade tablesorter to v2.29.6 diff --git a/README.md b/README.md index d696a3f..38ee276 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.29.6 (2018-02-25) [documentation] +Current tablesorter version: 2.30.1 (2018-03-19) [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index b7cf82a..d32b33b 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 - MINOR = 24 - TINY = 5 + MINOR = 25 + TINY = 0 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index e542aeb..c74a34b 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit e542aebf1c06ccf913b3167f61f6167857634f8d +Subproject commit c74a34b070002776133757faa5b8613988b42470 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 16a614d..2cef476 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin -* updated 4/18/2017 (v2.28.8) +* updated 2018-03-19 (v2.30.1) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -27,7 +27,7 @@ customAjaxUrl: function(table, url) { return url; }, // ajax error callback from $.tablesorter.showError function - // ajaxError: function( config, xhr, settings, exception ){ return exception; }; + // ajaxError: function( config, xhr, settings, exception ) { return exception; }; // returning false will abort the error message ajaxError: null, @@ -52,7 +52,7 @@ // ], // [ "header1", "header2", ... "headerN" ] // optional // ] - ajaxProcessing: function(ajax){ return [ 0, [], null ]; }, + ajaxProcessing: function( /* ajax */ ) { return [ 0, [], null ]; }, // output default: '{page}/{totalPages}' // possible variables: {size}, {page}, {totalPages}, {filteredPages}, {startRow}, @@ -139,12 +139,12 @@ if ( p.updateArrows ) { tmp = p.$container.find(p.cssFirst + ',' + p.cssPrev); tmp[ first ? a : r ](d); // toggle disabled class - tmp.each(function(){ + tmp.each(function() { this.ariaDisabled = first; }); tmp = p.$container.find(p.cssNext + ',' + p.cssLast); tmp[ last ? a : r ](d); - tmp.each(function(){ + tmp.each(function() { this.ariaDisabled = last; }); } @@ -203,11 +203,11 @@ // form the output string (can now get a new output string from the server) s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || output : output ) // {page} = one-based index; {page+#} = zero based index +/- value - .replace(/\{page([\-+]\d+)?\}/gi, function(m, n){ + .replace(/\{page([\-+]\d+)?\}/gi, function(m, n) { return p.totalPages ? p.page + (n ? parseInt(n, 10) : 1) : 0; }) // {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object) - .replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m){ + .replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m) { var len, indx, str = m.replace(/[{}\s]/g, ''), extra = str.split(':'), @@ -236,7 +236,7 @@ if ($out.length) { $out[ ($out[0].nodeName === 'INPUT') ? 'val' : 'html' ](s); // rebind startRow/page inputs - $out.find('.ts-startRow, .ts-page').unbind('change' + namespace).bind('change' + namespace, function(){ + $out.find('.ts-startRow, .ts-page').unbind('change' + namespace).bind('change' + namespace, function() { var v = $(this).val(), pg = $(this).hasClass('ts-startRow') ? Math.floor( v / sz ) + 1 : v; c.$table.triggerHandler('pageSet' + namespace, [ pg ]); @@ -246,8 +246,8 @@ pagerArrows( table, p ); fixHeight(table, p); if (p.initialized && completed !== false) { - if (c.debug) { - console.log('Pager: Triggering pagerComplete'); + if (ts.debug(c, 'pager')) { + console.log('Pager >> Triggering pagerComplete'); } c.$table.triggerHandler('pagerComplete', p); // save pager info to storage @@ -357,7 +357,7 @@ $.data(table, 'pagerLastSize', p.size); }, - hideRows = function(table, p){ + hideRows = function(table, p) { if (!p.ajaxUrl) { var i, lastIndex = 0, @@ -370,7 +370,7 @@ last = 0, // for cache indexing j = 0; // size counter p.cacheIndex = []; - for ( i = 0; i < l; i++ ){ + for ( i = 0; i < l; i++ ) { if ( !p.regexFiltered.test(rows[i].className) ) { if (j === s && rows[i].className.match(c.cssChildRow)) { // hide child rows @ start of pager (if already visible) @@ -398,19 +398,19 @@ } }, - hideRowsSetup = function(table, p){ + hideRowsSetup = function(table, p) { p.size = parsePageSize( p, p.$container.find(p.cssPageSize).val(), 'get' ); setPageSize( table, p.size, p ); pagerArrows( table, p ); if ( !p.removeRows ) { hideRows(table, p); - $(table).bind('sortEnd filterEnd '.split(' ').join(table.config.namespace + 'pager '), function(){ + $(table).bind('sortEnd filterEnd '.split(' ').join(table.config.namespace + 'pager '), function() { hideRows(table, p); }); } }, - renderAjax = function(data, table, p, xhr, settings, exception){ + renderAjax = function(data, table, p, xhr, settings, exception) { // process data if ( typeof p.ajaxProcessing === 'function' ) { @@ -424,15 +424,13 @@ c = table.config, $table = c.$table, tds = '', - result = p.ajaxProcessing(data, table, xhr) || [ 0, [] ], - hl = $table.find('thead th').length; - + result = p.ajaxProcessing(data, table, xhr) || [ 0, [] ]; // Clean up any previous error. ts.showError( table ); if ( exception ) { - if (c.debug) { - console.error('Pager: >> Ajax Error', xhr, settings, exception); + if (ts.debug(c, 'pager')) { + console.error('Pager >> Ajax Error', xhr, settings, exception); } ts.showError( table, xhr, settings, exception ); c.$tbodies.eq(0).children('tr').detach(); @@ -524,13 +522,13 @@ p.last.sortList = (c.sortList || []).join(','); updatePageDisplay(table, p, false); // tablesorter core updateCache (not pager) - ts.updateCache( c, function(){ + ts.updateCache( c, function() { if (p.initialized) { // apply widgets after table has rendered & after a delay to prevent // multiple applyWidget blocking code from blocking this trigger - setTimeout(function(){ - if (c.debug) { - console.log('Pager: Triggering pagerChange'); + setTimeout(function() { + if (ts.debug(c, 'pager')) { + console.log('Pager >> Triggering pagerChange'); } $table.triggerHandler( 'pagerChange', p ); ts.applyWidget( table ); @@ -566,7 +564,7 @@ p.ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl p.ajaxObject.success = function(data, status, jqxhr) { // Refuse to process old ajax commands that were overwritten by new ones - see #443 - if (counter < p.ajaxCounter){ + if (counter < p.ajaxCounter) { return; } renderAjax(data, table, p, jqxhr); @@ -575,8 +573,8 @@ p.oldAjaxSuccess(data); } }; - if (c.debug) { - console.log('Pager: Ajax initialized', p.ajaxObject); + if (ts.debug(c, 'pager')) { + console.log('Pager >> Ajax initialized', p.ajaxObject); } $.ajax(p.ajaxObject); } @@ -587,7 +585,7 @@ c = table.config, url = (p.ajaxUrl) ? p.ajaxUrl // allow using "{page+1}" in the url string to switch to a non-zero based index - .replace(/\{page([\-+]\d+)?\}/, function(s, n){ return p.page + (n ? parseInt(n, 10) : 0); }) + .replace(/\{page([\-+]\d+)?\}/, function(s, n) { return p.page + (n ? parseInt(n, 10) : 0); }) // this will pass "all" to server when size is set to "all" .replace(/\{size\}/g, p.size) : '', sortList = c.sortList, @@ -620,8 +618,8 @@ if ( typeof p.customAjaxUrl === 'function' ) { url = p.customAjaxUrl(table, url); } - if (c.debug) { - console.log('Pager: Ajax url = ' + url); + if (ts.debug(c, 'pager')) { + console.log('Pager >> Ajax url = ' + url); } return url; }, @@ -630,13 +628,14 @@ var $tb, index, count, added, $t = $(table), c = table.config, + debug = ts.debug(c, 'pager'), f = c.$table.hasClass('hasFilters'), l = rows && rows.length || 0, // rows may be undefined e = p.size === 'all' ? p.totalRows : p.size, s = ( p.page * e ); if ( l < 1 ) { - if (c.debug) { - console.warn('Pager: >> No rows for pager to render'); + if (debug) { + console.warn('Pager >> No rows for pager to render'); } // empty table, abort! return; @@ -648,8 +647,8 @@ p.cacheIndex = []; p.isDisabled = false; // needed because sorting will change the page and re-enable the pager if (p.initialized) { - if (c.debug) { - console.log('Pager: Triggering pagerChange'); + if (debug) { + console.log('Pager >> Triggering pagerChange'); } $t.triggerHandler( 'pagerChange', p ); } @@ -664,7 +663,7 @@ count = f ? 0 : s; added = 0; while (added < e && index < rows.length) { - if (!f || !p.regexFiltered.test(rows[index][0].className)){ + if (!f || !p.regexFiltered.test(rows[index][0].className)) { count++; if (count > s && added <= e) { added++; @@ -678,8 +677,8 @@ } updatePageDisplay(table, p); if (table.isUpdating) { - if (c.debug) { - console.log('Pager: Triggering updateComplete'); + if (debug) { + console.log('Pager >> Triggering updateComplete'); } $t.triggerHandler('updateComplete', [ table, true ]); } @@ -693,17 +692,17 @@ $.data(table, 'pagerLastPage', p.page); $.data(table, 'pagerLastSize', p.size); p.page = 0; - p.size = p.totalPages; + p.size = p.totalRows; p.totalPages = 1; $(table) - .addClass('pagerDisabled') - .removeAttr('aria-describedby') - .find('tr.pagerSavedHeightSpacer').remove(); + .addClass('pagerDisabled') + .removeAttr('aria-describedby') + .find('tr.pagerSavedHeightSpacer').remove(); renderTable(table, table.config.rowsCopy, p); p.isDisabled = true; ts.applyWidget( table ); - if (table.config.debug) { - console.log('Pager: Disabled'); + if (ts.debug(table.config, 'pager')) { + console.log('Pager >> Disabled'); } } // disable size selector @@ -720,7 +719,7 @@ var c = table.config, p = c.pager; // tablesorter core updateCache (not pager) - ts.updateCache( c, function(){ + ts.updateCache( c, function() { var i, rows = [], n = table.config.cache[0].normalized; @@ -736,9 +735,10 @@ moveToPage = function(table, p, pageMoved) { if ( p.isDisabled ) { return; } var tmp, - c = table.config, - $t = $(table), - l = p.last; + c = table.config, + debug = ts.debug(c, 'pager'), + $t = $(table), + l = p.last; if ( pageMoved !== false && p.initialized && ts.isEmptyObject(c.cache)) { return updateCache(table); } @@ -758,8 +758,8 @@ // & ajax url option changes (dynamically add/remove/rename sort & filter parameters) (l.optAjaxUrl || '') === (p.ajaxUrl || '') && l.sortList === (c.sortList || []).join(',') ) { return; } - if (c.debug) { - console.log('Pager: Changing to page ' + p.page); + if (debug) { + console.log('Pager >> Changing to page ' + p.page); } p.last = { page : p.page, @@ -776,9 +776,9 @@ p.processAjaxOnInit = true; tmp = p.initialRows; p.totalRows = typeof tmp.total !== 'undefined' ? tmp.total : - ( c.debug ? console.error('Pager: no initial total page set!') || 0 : 0 ); + ( debug ? console.error('Pager >> No initial total page set!') || 0 : 0 ); p.filteredRows = typeof tmp.filtered !== 'undefined' ? tmp.filtered : - ( c.debug ? console.error('Pager: no initial filtered page set!') || 0 : 0 ); + ( debug ? console.error('Pager >> No initial filtered page set!') || 0 : 0 ); pagerInitialized( table, p ); } else { getAjax(table, p); @@ -788,14 +788,14 @@ } $.data(table, 'pagerLastPage', p.page); if (p.initialized && pageMoved !== false) { - if (c.debug) { - console.log('Pager: Triggering pageMoved'); + if (debug) { + console.log('Pager >> Triggering pageMoved'); } $t.triggerHandler('pageMoved', p); ts.applyWidget( table ); if (table.isUpdating) { - if (c.debug) { - console.log('Pager: Triggering updateComplete'); + if (debug) { + console.log('Pager >> Triggering updateComplete'); } $t.triggerHandler('updateComplete', [ table, true ]); } @@ -868,8 +868,8 @@ pagerInitialized = function(table, p) { p.initialized = true; p.initializing = false; - if (table.config.debug) { - console.log('Pager: Triggering pagerInitialized'); + if (ts.debug(table.config, 'pager')) { + console.log('Pager >> Triggering pagerInitialized'); } $(table).triggerHandler( 'pagerInitialized', p ); ts.applyWidget( table ); @@ -908,7 +908,7 @@ p.$container.find(p.cssGoto + ',' + p.cssPageSize + ',.ts-startRow, .ts-page') .removeClass(p.cssDisabled) .removeAttr('disabled') - .each(function(){ + .each(function() { this.ariaDisabled = false; }); p.isDisabled = false; @@ -936,16 +936,17 @@ setPageSize(table, p.size, p); moveToPage(table, p); hideRowsSetup(table, p); - if (c.debug) { - console.log('Pager: Enabled'); + if (ts.debug(c, 'pager')) { + console.log('Pager >> Enabled'); } } }, init = function(table, settings) { - var t, ctrls, fxn, size, $el, + var t, ctrls, fxn, $el, c = table.config, wo = c.widgetOptions, + debug = ts.debug(c, 'pager'), p = c.pager = $.extend( true, {}, $.tablesorterPager.defaults, settings ), $t = c.$table, namespace = c.namespace + 'pager', @@ -953,8 +954,8 @@ pager = p.$container = $(p.container).addClass('tablesorter-pager').show(); // save a copy of the original settings p.settings = $.extend( true, {}, $.tablesorterPager.defaults, settings ); - if (c.debug) { - console.log('Pager: Initializing'); + if (debug) { + console.log('Pager >> Initializing'); } p.oldAjaxSuccess = p.oldAjaxSuccess || p.ajaxObject.success; c.appender = $this.appender; @@ -1003,23 +1004,23 @@ ts.applyWidget( table ); } }) - .bind('disablePager' + namespace, function(e){ + .bind('disablePager' + namespace, function(e) { e.stopPropagation(); showAllRows(table, p); }) - .bind('enablePager' + namespace, function(e){ + .bind('enablePager' + namespace, function(e) { e.stopPropagation(); enablePager(table, p, true); }) - .bind('destroyPager' + namespace, function(e){ + .bind('destroyPager' + namespace, function(e) { e.stopPropagation(); destroyPager(table, p); }) - .bind('resetToLoadState' + namespace, function(e){ + .bind('resetToLoadState' + namespace, function(e) { e.stopPropagation(); resetState(table, p); }) - .bind('updateComplete' + namespace, function(e, table, triggered){ + .bind('updateComplete' + namespace, function(e, table, triggered) { e.stopPropagation(); // table can be unintentionally undefined in tablesorter v2.17.7 and earlier // don't recalculate total rows/pages if using ajax @@ -1038,14 +1039,14 @@ changeHeight(table, p); updatePageDisplay(table, p, true); }) - .bind('pageSize refreshComplete '.split(' ').join(namespace + ' '), function(e, size){ + .bind('pageSize refreshComplete '.split(' ').join(namespace + ' '), function(e, size) { e.stopPropagation(); setPageSize(table, parsePageSize( p, size, 'get' ), p); moveToPage(table, p); hideRows(table, p); updatePageDisplay(table, p, false); }) - .bind('pageSet pagerUpdate '.split(' ').join(namespace + ' '), function(e, num){ + .bind('pageSet pagerUpdate '.split(' ').join(namespace + ' '), function(e, num) { e.stopPropagation(); // force pager refresh if (e.type === 'pagerUpdate') { @@ -1056,7 +1057,7 @@ moveToPage(table, p, true); updatePageDisplay(table, p, false); }) - .bind('pageAndSize' + namespace, function(e, page, size){ + .bind('pageAndSize' + namespace, function(e, page, size) { e.stopPropagation(); p.page = (parseInt(page, 10) || 1) - 1; setPageSize(table, parsePageSize( p, size, 'get' ), p); @@ -1068,13 +1069,13 @@ // clicked controls ctrls = [ p.cssFirst, p.cssPrev, p.cssNext, p.cssLast ]; fxn = [ moveToFirstPage, moveToPrevPage, moveToNextPage, moveToLastPage ]; - if (c.debug && !pager.length) { - console.warn('Pager: >> Container not found'); + if (debug && !pager.length) { + console.warn('Pager >> "container" not found'); } pager.find(ctrls.join(',')) .attr('tabindex', 0) .unbind('click' + namespace) - .bind('click' + namespace, function(e){ + .bind('click' + namespace, function(e) { e.stopPropagation(); var i, $t = $(this), l = ctrls.length; if ( !$t.hasClass(p.cssDisabled) ) { @@ -1092,13 +1093,13 @@ if ( $el.length ) { $el .unbind('change' + namespace) - .bind('change' + namespace, function(){ + .bind('change' + namespace, function() { p.page = $(this).val() - 1; moveToPage(table, p, true); updatePageDisplay(table, p, false); }); - } else if (c.debug) { - console.warn('Pager: >> Goto selector not found'); + } else if (debug) { + console.warn('Pager >> "goto" selector not found'); } // page size selector $el = pager.find(p.cssPageSize); @@ -1115,8 +1116,8 @@ } return false; }); - } else if (c.debug) { - console.warn('Pager: >> Size selector not found'); + } else if (debug) { + console.warn('Pager >> "size" selector not found'); } // clear initialized flag @@ -1147,8 +1148,8 @@ // update page size on init setPageSize(table, p.size, p); moveToPage(table, p); - if (c.debug) { - console.log('Pager: Triggering pagerInitialized'); + if (debug) { + console.log('Pager >> Triggering pagerInitialized'); } c.$table.triggerHandler( 'pagerInitialized', p ); if ( !( c.widgetOptions.filter_initialized && ts.hasWidget(table, 'filter') ) ) { @@ -1187,8 +1188,7 @@ // see #486 ts.showError = function( table, xhr, settings, exception ) { - var $row, - $table = $( table ), + var $table = $( table ), c = $table[0].config, wo = c && c.widgetOptions, errorRow = c.pager && c.pager.cssErrorRow || @@ -1197,7 +1197,7 @@ typ = typeof xhr, valid = true, message = '', - removeRow = function(){ + removeRow = function() { c.$table.find( 'thead' ).find( c.selectorRemove ).remove(); }; @@ -1243,7 +1243,7 @@ } // allow message to include entire row HTML! - $row = ( /tr\>/.test(message) ? $(message) : $('<tr><td colspan="' + c.columns + '">' + message + '</td></tr>') ) + $( /tr\>/.test(message) ? message : '<tr><td colspan="' + c.columns + '">' + message + '</td></tr>' ) .click( function() { $( this ).remove(); }) diff --git a/vendor/assets/javascripts/jquery-tablesorter/beta-testing/widget-reorder.js b/vendor/assets/javascripts/jquery-tablesorter/beta-testing/widget-reorder.js index 5d09f83..09b6bab 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/beta-testing/widget-reorder.js +++ b/vendor/assets/javascripts/jquery-tablesorter/beta-testing/widget-reorder.js @@ -4,8 +4,8 @@ */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ -;(function($){ - "use strict"; +;(function($) { + 'use strict'; $.tablesorter.addWidget({ id: 'reorder', @@ -22,14 +22,13 @@ $.tablesorter.addWidget({ init: function(table, thisWidget, c, wo) { var i, timer, $helper, $bar, clickOffset, lastIndx = -1, - ts = $.tablesorter, endIndex = -1, startIndex = -1, t = wo.reorder_blocked.split(' '), noReorderLeft = t[0] || 'reorder-block-left', noReorderLast = t[1] || 'reorder-block-end', lastOffset = c.$headers.not('.' + noReorderLeft).first(), - offsets = c.$headers.map(function(i){ + offsets = c.$headers.map(function() { var s, $t = $(this); if ($t.hasClass(noReorderLeft)) { s = lastOffset; @@ -40,7 +39,7 @@ $.tablesorter.addWidget({ return $t.offset().left; }).get(), len = offsets.length, - startReorder = function(e, $th){ + startReorder = function(e, $th) { var p = $th.position(), r = $th.parent().position(), i = startIndex = $th.index(); @@ -68,7 +67,7 @@ $.tablesorter.addWidget({ positionBar(e); lastIndx = endIndex; }, - positionBar = function(e){ + positionBar = function(e) { for (i = 0; i <= len; i++) { if ( i > 0 && e.pageX < offsets[i-1] + (offsets[i] - offsets[i-1])/2 && !c.$headers.eq(i).hasClass(noReorderLeft) ) { endIndex = i - 1; @@ -87,7 +86,7 @@ $.tablesorter.addWidget({ $bar.css('left', offsets[len]); } }, - finishReorder = function(){ + finishReorder = function() { $helper.remove(); $bar.remove(); // finish reorder @@ -95,7 +94,7 @@ $.tablesorter.addWidget({ rows = c.$table.find('tr'), cols; startIndex = -1; // stop mousemove updates - if ( s > -1 && endIndex > -1 && s != endIndex && s + 1 !== endIndex ) { + if ( s > -1 && endIndex > -1 && s !== endIndex && s + 1 !== endIndex ) { adj = endIndex !== 0; if (c.debug) { console.log( 'Inserting column ' + s + (adj ? ' after' : ' before') + ' column ' + (endIndex - adj ? 1 : 0) ); @@ -123,10 +122,10 @@ $.tablesorter.addWidget({ } endIndex = -1; }, - mdown = function(e, el){ + mdown = function(e, el) { var $t = $(el), evt = e; if ($t.hasClass(wo.reorder_noReorder)) { return; } - timer = setTimeout(function(){ + timer = setTimeout(function() { $t.addClass('tablesorter-reorder'); startReorder(evt, $t); }, wo.reorder_delay); @@ -140,13 +139,13 @@ $.tablesorter.addWidget({ offsets.push( c.$table.offset().left + c.$table.outerWidth() ); } - c.$headers.not('.' + wo.reorder_noReorder).bind('mousedown.reorder', function(e){ + c.$headers.not('.' + wo.reorder_noReorder).bind('mousedown.reorder', function(e) { mdown(e, this); }); $(document) - .bind('mousemove.reorder', function(e){ - if (startIndex !== -1){ + .bind('mousemove.reorder', function(e) { + if (startIndex !== -1) { var c = { left : e.pageX - clickOffset[0] }; endIndex = -1; if (/y/.test(wo.reorder_axis)) { @@ -157,9 +156,9 @@ $.tablesorter.addWidget({ } }) .add( c.$headers ) - .bind('mouseup.reorder', function(){ + .bind('mouseup.reorder', function() { clearTimeout(timer); - if (startIndex !== -1 && endIndex !== -1){ + if (startIndex !== -1 && endIndex !== -1) { finishReorder(); } else { startIndex = -1; @@ -167,8 +166,8 @@ $.tablesorter.addWidget({ }); // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ - wo.$sticky.find('thead').children().not('.' + wo.reorder_noReorder).bind('mousedown.reorder', function(e){ + c.$table.bind('stickyHeadersInit', function() { + wo.$sticky.find('thead').children().not('.' + wo.reorder_noReorder).bind('mousedown.reorder', function(e) { mdown(e, this); }); }); @@ -177,6 +176,6 @@ $.tablesorter.addWidget({ }); // add mouse coordinates -$x = $('#main h1:last'); $(document).mousemove(function(e){ $x.html( e.pageX ); }); +$x = $('#main h1:last'); $(document).mousemove(function(e) { $x.html( e.pageX ); }); })(jQuery); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 6d0cffc..1026745 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-02-25 (v2.29.6)*/ +/*! tablesorter (FORK) - updated 2018-03-19 (v2.30.1)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.29.6 *//* +/*! TableSorter (FORK) v2.30.1 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.29.6', + version : '2.30.1', parsers : [], widgets : [], @@ -52,8 +52,8 @@ showProcessing : false, // show an indeterminate timer icon in the header when the table is sorted or filtered. headerTemplate : '{content}',// header layout template (HTML ok); {content} = innerHTML, {icon} = <i/> // class from cssIcon - onRenderTemplate : null, // function( index, template ){ return template; }, // template is a string - onRenderHeader : null, // function( index ){}, // nothing to return + onRenderTemplate : null, // function( index, template ) { return template; }, // template is a string + onRenderHeader : null, // function( index ) {}, // nothing to return // *** functionality cancelSelection : true, // prevent text selection in the header @@ -82,7 +82,7 @@ emptyTo : 'bottom', // sort empty cell to bottom, top, none, zero, emptyMax, emptyMin stringTo : 'max', // sort strings in numerical column as max, min, top, bottom, zero duplicateSpan : true, // colspan cells in the tbody will have duplicated content in the cache for each spanned column - textExtraction : 'basic', // text extraction method/function - function( node, table, cellIndex ){} + textExtraction : 'basic', // text extraction method/function - function( node, table, cellIndex ) {} textAttribute : 'data-text',// data-attribute that contains alternate cell text (used in default textExtraction function) textSorter : null, // choose overall or specific column sorter function( a, b, direction, table, columnIndex ) [alt: ts.sortText] numberSorter : null, // choose overall numeric sorter function( a, b, direction, maxColumnValue ) @@ -96,7 +96,7 @@ }, // *** callbacks - initialized : null, // function( table ){}, + initialized : null, // function( table ) {}, // *** extra css class names tableClass : '', @@ -234,7 +234,7 @@ setup : function( table, c ) { // if no thead or tbody, or tablesorter is already present, quit if ( !table || !table.tHead || table.tBodies.length === 0 || table.hasInitialized === true ) { - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { if ( table.hasInitialized ) { console.warn( 'Stopping initialization. Tablesorter has already been initialized' ); } else { @@ -255,7 +255,7 @@ table.config = c; // save the settings where they read $.data( table, 'tablesorter', c ); - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { console[ console.group ? 'group' : 'log' ]( 'Initializing tablesorter v' + ts.version ); $.data( table, 'startoveralltimer', new Date() ); } @@ -316,7 +316,10 @@ ts.setupParsers( c ); // start total row count at zero c.totalRows = 0; - ts.validateOptions( c ); + // only validate options while debugging. See #1528 + if (c.debug) { + ts.validateOptions( c ); + } // build the cache for the tbody cells // delayInit will delay building the cache until the user starts a sort if ( !c.delayInit ) { ts.buildCache( c ); } @@ -361,9 +364,9 @@ // initialized table.hasInitialized = true; table.isProcessing = false; - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { console.log( 'Overall initialization time:' + ts.benchmark( $.data( table, 'startoveralltimer' ) ) ); - if ( c.debug && console.groupEnd ) { console.groupEnd(); } + if ( ts.debug(c, 'core') && console.groupEnd ) { console.groupEnd(); } } $table.triggerHandler( 'tablesorter-initialized', table ); if ( typeof c.initialized === 'function' ) { @@ -560,7 +563,7 @@ c.headerList = []; c.headerContent = []; c.sortVars = []; - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { timer = new Date(); } // children tr in tfoot - see issue #196 & #547 @@ -655,7 +658,7 @@ }); // enable/disable sorting ts.updateHeader( c ); - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { console.log( 'Built headers:' + ts.benchmark( timer ) ); console.log( c.$headers ); } @@ -678,14 +681,15 @@ noParser, parser, extractor, time, tbody, len, table = c.table, tbodyIndex = 0, - debug = {}; + debug = ts.debug(c, 'core'), + debugOutput = {}; // update table bodies in case we start with an empty table c.$tbodies = c.$table.children( 'tbody:not(.' + c.cssInfoBlock + ')' ); tbody = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies; len = tbody.length; if ( len === 0 ) { - return c.debug ? console.warn( 'Warning: *Empty table!* Not building a parser cache' ) : ''; - } else if ( c.debug ) { + return debug ? console.warn( 'Warning: *Empty table!* Not building a parser cache' ) : ''; + } else if ( debug ) { time = new Date(); console[ console.group ? 'group' : 'log' ]( 'Detecting parsers for each column' ); } @@ -727,8 +731,8 @@ if ( !parser ) { parser = ts.detectParserForColumn( c, rows, -1, colIndex ); } - if ( c.debug ) { - debug[ '(' + colIndex + ') ' + header.text() ] = { + if ( debug ) { + debugOutput[ '(' + colIndex + ') ' + header.text() ] = { parser : parser.id, extractor : extractor ? extractor.id : 'none', string : c.strings[ colIndex ], @@ -754,9 +758,9 @@ } tbodyIndex += ( list.parsers.length ) ? len : 1; } - if ( c.debug ) { - if ( !ts.isEmptyObject( debug ) ) { - console[ console.table ? 'table' : 'log' ]( debug ); + if ( debug ) { + if ( !ts.isEmptyObject( debugOutput ) ) { + console[ console.table ? 'table' : 'log' ]( debugOutput ); } else { console.warn( ' No parsers detected!' ); } @@ -782,7 +786,7 @@ }, getParserById : function( name ) { - /*jshint eqeqeq:false */ + /*jshint eqeqeq:false */ // eslint-disable-next-line eqeqeq if ( name == 'false' ) { return false; } var indx, len = ts.parsers.length; @@ -799,6 +803,7 @@ indx = ts.parsers.length, node = false, nodeValue = '', + debug = ts.debug(c, 'core'), keepLooking = true; while ( nodeValue === '' && keepLooking ) { rowIndex++; @@ -809,7 +814,7 @@ node = rows[ rowIndex ].cells[ cellIndex ]; nodeValue = ts.getElementText( c, node, cellIndex ); $node = $( node ); - if ( c.debug ) { + if ( debug ) { console.log( 'Checking if value was empty on row ' + rowIndex + ', column: ' + cellIndex + ': "' + nodeValue + '"' ); } @@ -891,7 +896,8 @@ cols, $cells, cell, cacheTime, totalRows, rowData, prevRowData, colMax, span, cacheIndex, hasParser, max, len, index, table = c.table, - parsers = c.parsers; + parsers = c.parsers, + debug = ts.debug(c, 'core'); // update tbody variable c.$tbodies = c.$table.children( 'tbody:not(.' + c.cssInfoBlock + ')' ); $tbody = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies, @@ -899,9 +905,9 @@ c.totalRows = 0; // if no parsers found, return - it's an empty table. if ( !parsers ) { - return c.debug ? console.warn( 'Warning: *Empty table!* Not building a cache' ) : ''; + return debug ? console.warn( 'Warning: *Empty table!* Not building a cache' ) : ''; } - if ( c.debug ) { + if ( debug ) { cacheTime = new Date(); } // processing icon @@ -970,7 +976,7 @@ cell = $row[ 0 ].cells[ colIndex ]; if ( cell && cacheIndex < c.columns ) { hasParser = typeof parsers[ cacheIndex ] !== 'undefined'; - if ( !hasParser && c.debug ) { + if ( !hasParser && debug ) { console.warn( 'No parser found for row: ' + rowIndex + ', column: ' + colIndex + '; cell containing: "' + $(cell).text() + '"; does it have a header?' ); } @@ -1018,7 +1024,7 @@ if ( c.showProcessing ) { ts.isProcessing( table ); // remove processing icon } - if ( c.debug ) { + if ( debug ) { len = Math.min( 5, c.cache[ 0 ].normalized.length ); console[ console.group ? 'group' : 'log' ]( 'Building cache for ' + c.totalRows + ' rows (showing ' + len + ' rows in log) and ' + c.columns + ' columns' + @@ -1049,7 +1055,7 @@ data = { raw : [], parsed: [], $cell: [] }, c = table.config; if ( ts.isEmptyObject( c ) ) { - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { console.warn( 'No cache found - aborting getColumnText function!' ); } } else { @@ -1143,8 +1149,8 @@ // direction = 2 means reset! if ( list[ indx ][ 1 ] !== 2 ) { // multicolumn sorting updating - see #1005 - // .not(function(){}) needs jQuery 1.4 - // filter(function(i, el){}) <- el is undefined in jQuery v1.2.6 + // .not(function() {}) needs jQuery 1.4 + // filter(function(i, el) {}) <- el is undefined in jQuery v1.2.6 $sorted = c.$headers.filter( function( i ) { // only include headers that are in the sortList (this includes colspans) var include = true, @@ -1393,7 +1399,7 @@ ts.resortComplete( c, callback ); } } else { - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { console.error( 'updateCell aborted, tbody missing or not within the indicated table' ); } c.table.isUpdating = false; @@ -1416,7 +1422,7 @@ // row contained in the table? ( ts.getClosest( $row, 'table' )[ 0 ] !== c.table ) ) { - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { console.error( 'addRows method requires (1) a jQuery selector reference to rows that have already ' + 'been added to the table, or (2) row HTML string to be added to a table with only one tbody' ); } @@ -1488,7 +1494,6 @@ appendCache : function( c, init ) { var parsed, totalRows, $tbody, $curTbody, rowIndex, tbodyIndex, appendTime, table = c.table, - wo = c.widgetOptions, $tbodies = c.$tbodies, rows = [], cache = c.cache; @@ -1498,7 +1503,7 @@ return c.appender ? c.appender( table, rows ) : table.isUpdating ? c.$table.triggerHandler( 'updateComplete', table ) : ''; // Fixes #532 } - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { appendTime = new Date(); } for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { @@ -1511,7 +1516,7 @@ for ( rowIndex = 0; rowIndex < totalRows; rowIndex++ ) { rows[rows.length] = parsed[ rowIndex ][ c.columns ].$row; // removeRows used by the pager plugin; don't render if using ajax - fixes #411 - if ( !c.appender || ( c.pager && ( !c.pager.removeRows || !wo.pager_removeRows ) && !c.pager.ajax ) ) { + if ( !c.appender || ( c.pager && !c.pager.removeRows && !c.pager.ajax ) ) { $curTbody.append( parsed[ rowIndex ][ c.columns ].$row ); } } @@ -1522,7 +1527,7 @@ if ( c.appender ) { c.appender( table, rows ); } - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { console.log( 'Rebuilt table' + ts.benchmark( appendTime ) ); } // apply table widgets; but not before ajax completes @@ -1553,7 +1558,7 @@ initSort : function( c, cell, event ) { if ( c.table.isUpdating ) { // let any updates complete before initializing a sort - return setTimeout( function(){ + return setTimeout( function() { ts.initSort( c, cell, event ); }, 50 ); } @@ -1704,7 +1709,7 @@ // empty table - fixes #206/#346 return; } - if ( c.debug ) { sortTime = new Date(); } + if ( ts.debug(c, 'core') ) { sortTime = new Date(); } // cache textSorter to optimize speed if ( typeof textSorter === 'object' ) { colMax = c.columns; @@ -1766,7 +1771,7 @@ return a[ c.columns ].order - b[ c.columns ].order; }); } - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { console.log( 'Applying sort ' + sortList.toString() + ts.benchmark( sortTime ) ); } }, @@ -2019,6 +2024,7 @@ var applied, time, name, c = table.config, wo = c.widgetOptions, + debug = ts.debug(c, 'core'), widget = ts.getWidgetById( id ); if ( widget ) { name = widget.id; @@ -2027,7 +2033,7 @@ if ( $.inArray( name, c.widgets ) < 0 ) { c.widgets[ c.widgets.length ] = name; } - if ( c.debug ) { time = new Date(); } + if ( debug ) { time = new Date(); } if ( init || !( c.widgetInit[ name ] ) ) { // set init flag first to prevent calling init more than once (e.g. pager) @@ -2038,7 +2044,7 @@ } if ( typeof widget.init === 'function' ) { applied = true; - if ( c.debug ) { + if ( debug ) { console[ console.group ? 'group' : 'log' ]( 'Initializing ' + name + ' widget' ); } widget.init( table, widget, c, wo ); @@ -2046,12 +2052,12 @@ } if ( !init && typeof widget.format === 'function' ) { applied = true; - if ( c.debug ) { + if ( debug ) { console[ console.group ? 'group' : 'log' ]( 'Updating ' + name + ' widget' ); } widget.format( table, c, wo, false ); } - if ( c.debug ) { + if ( debug ) { if ( applied ) { console.log( 'Completed ' + ( init ? 'initializing ' : 'applying ' ) + name + ' widget' + ts.benchmark( time ) ); if ( console.groupEnd ) { console.groupEnd(); } @@ -2064,12 +2070,13 @@ table = $( table )[ 0 ]; // in case this is called externally var indx, len, names, widget, time, c = table.config, + debug = ts.debug(c, 'core'), widgets = []; // prevent numerous consecutive widget applications if ( init !== false && table.hasInitialized && ( table.isApplyingWidgets || table.isUpdating ) ) { return; } - if ( c.debug ) { time = new Date(); } + if ( debug ) { time = new Date(); } ts.addWidgetFromClass( table ); // prevent "tablesorter-ready" from firing multiple times in a row clearTimeout( c.timerReady ); @@ -2088,7 +2095,7 @@ // set priority to 10 if not defined if ( !widget.priority ) { widget.priority = 10; } widgets[ indx ] = widget; - } else if ( c.debug ) { + } else if ( debug ) { console.warn( '"' + names[ indx ] + '" was enabled, but the widget code has not been loaded!' ); } } @@ -2098,7 +2105,7 @@ }); // add/update selected widgets len = widgets.length; - if ( c.debug ) { + if ( debug ) { console[ console.group ? 'group' : 'log' ]( 'Start ' + ( init ? 'initializing' : 'applying' ) + ' widgets' ); } for ( indx = 0; indx < len; indx++ ) { @@ -2107,7 +2114,7 @@ ts.applyWidgetId( table, widget.id, init ); } } - if ( c.debug && console.groupEnd ) { console.groupEnd(); } + if ( debug && console.groupEnd ) { console.groupEnd(); } } c.timerReady = setTimeout( function() { table.isApplyingWidgets = false; @@ -2117,7 +2124,7 @@ if ( !init && typeof callback === 'function' ) { callback( table ); } - if ( c.debug ) { + if ( debug ) { widget = c.widgets.length; console.log( 'Completed ' + ( init === true ? 'initializing ' : 'applying ' ) + widget + @@ -2154,7 +2161,7 @@ c.widgets.splice( indx, 1 ); } if ( widget && widget.remove ) { - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { console.log( ( refreshing ? 'Refreshing' : 'Removing' ) + ' "' + name[ index ] + '" widget' ); } widget.remove( table, c, c.widgetOptions, refreshing ); @@ -2208,6 +2215,12 @@ log : function() { console.log( arguments ); }, + debug : function(c, name) { + return c && ( + c.debug === true || + typeof c.debug === 'string' && c.debug.indexOf(name) > -1 + ); + }, // $.isEmptyObject from jQuery v1.4 isEmptyObject : function( obj ) { @@ -2513,7 +2526,7 @@ ignore = 'headers sortForce sortList sortAppend widgets'.split( ' ' ), orig = c.originalSettings; if ( orig ) { - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { timer = new Date(); } for ( setting in orig ) { @@ -2529,7 +2542,7 @@ } } } - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { console.log( 'validate options time:' + ts.benchmark( timer ) ); } } @@ -2560,7 +2573,6 @@ var events, $t = $( table ), c = table.config, - debug = c.debug, $h = $t.find( 'thead:first' ), $r = $h.find( 'tr.' + ts.css.headerRow ).removeClass( ts.css.headerRow + ' ' + c.cssHeaderRow ), $f = $t.find( 'tfoot:first > tr' ).children( 'th, td' ); @@ -2598,7 +2610,7 @@ if ( typeof callback === 'function' ) { callback( table ); } - if ( debug ) { + if ( ts.debug(c, 'core') ) { console.log( 'tablesorter has been removed' ); } } @@ -2715,7 +2727,7 @@ is : function( str ) { return ts.regex.isoDate.test( str ); }, - format : function( str, table ) { + format : function( str ) { var date = str ? new Date( str.replace( ts.regex.dash, '/' ) ) : str; return date instanceof Date && isFinite( date ) ? date.getTime() : str; }, @@ -2758,7 +2770,7 @@ // Jan 01, 2013 12:34:56 PM or 01 Jan 2013 return ts.regex.usLongDateTest1.test( str ) || ts.regex.usLongDateTest2.test( str ); }, - format : function( str, table ) { + format : function( str ) { var date = str ? new Date( str.replace( ts.regex.dateReplace, '$1 $2' ) ) : str; return date instanceof Date && isFinite( date ) ? date.getTime() : str; }, @@ -2819,7 +2831,7 @@ is : function( str ) { return ts.regex.timeTest.test( str ); }, - format : function( str, table ) { + format : function( str ) { // isolate time... ignore month, day and year var temp, timePart = ( str || '' ).match( ts.regex.timeMatch ), @@ -2886,7 +2898,7 @@ var tbodyIndex, $tbody, $tbodies = c.$tbodies, toRemove = ( wo.zebra || [ 'even', 'odd' ] ).join( ' ' ); - for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ){ + for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { $tbody = ts.processTbody( table, $tbodies.eq( tbodyIndex ), true ); // remove tbody $tbody.children().removeClass( toRemove ); ts.processTbody( table, $tbody, false ); // restore tbody @@ -2896,7 +2908,7 @@ })( jQuery ); -/*! Widget: storage - updated 4/18/2017 (v2.28.8) */ +/*! Widget: storage - updated 2018-03-18 (v2.30.0) */ /*global JSON:false */ ;(function ($, window, document) { 'use strict'; @@ -2942,6 +2954,7 @@ values = {}, c = table.config, wo = c && c.widgetOptions, + debug = ts.debug(c, 'storage'), storageType = ( ( options && options.storageType ) || ( wo && wo.storage_storageType ) ).toString().charAt(0).toLowerCase(), @@ -2970,14 +2983,12 @@ hasStorage = true; window[storageType].removeItem('_tmptest'); } catch (error) { - if (c && c.debug) { - console.warn( storageType + ' is not supported in this browser' ); - } + console.warn( storageType + ' is not supported in this browser' ); } } } - if (c.debug) { - console.log('Storage widget using', hasStorage ? storageType : 'cookies'); + if (debug) { + console.log('Storage >> Using', hasStorage ? storageType : 'cookies'); } // *** get value *** if ($.parseJSON) { @@ -3013,7 +3024,7 @@ })(jQuery, window, document); -/*! Widget: uitheme - updated 9/27/2017 (v2.29.0) */ +/*! Widget: uitheme - updated 2018-03-18 (v2.30.0) */ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; @@ -3078,8 +3089,9 @@ theme = c.theme || 'jui', themes = themesAll[theme] || {}, remove = $.trim( [ themes.sortNone, themes.sortDesc, themes.sortAsc, themes.active ].join( ' ' ) ), - iconRmv = $.trim( [ themes.iconSortNone, themes.iconSortDesc, themes.iconSortAsc ].join( ' ' ) ); - if (c.debug) { time = new Date(); } + iconRmv = $.trim( [ themes.iconSortNone, themes.iconSortDesc, themes.iconSortAsc ].join( ' ' ) ), + debug = ts.debug(c, 'uitheme'); + if (debug) { time = new Date(); } // initialization code - run once if (!$table.hasClass('tablesorter-' + theme) || c.theme !== c.appliedTheme || !wo.uitheme_applied) { wo.uitheme_applied = true; @@ -3124,7 +3136,7 @@ $(this)[ event.type === 'mouseenter' ? 'addClass' : 'removeClass' ](themes.hover || ''); }); - $headers.each(function(){ + $headers.each(function() { var $this = $(this); if (!$this.find('.' + ts.css.wrapper).length) { // Firefox needs this inner div to position the icon & resizer correctly @@ -3182,8 +3194,8 @@ } } } - if (c.debug) { - console.log('Applying ' + theme + ' theme' + ts.benchmark(time)); + if (debug) { + console.log('uitheme >> Applied ' + theme + ' theme' + ts.benchmark(time)); } }, remove: function(table, c, wo, refreshing) { @@ -3288,7 +3300,7 @@ })(jQuery); -/*! Widget: filter - updated 2018-01-30 (v2.29.5) *//* +/*! Widget: filter - updated 2018-03-18 (v2.30.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -3570,6 +3582,7 @@ if ( tsfRegex.exact.test( data.iFilter ) ) { var txt = data.iFilter.replace( tsfRegex.exact, '' ), filter = tsf.parseFilter( c, txt, data ) || ''; + // eslint-disable-next-line eqeqeq return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact; } return null; @@ -3743,7 +3756,7 @@ c.lastCombinedFilter = null; c.lastSearch = []; // update filterFormatters after update (& small delay) - Fixes #1237 - setTimeout(function(){ + setTimeout(function() { c.$table.triggerHandler( 'filterFomatterUpdate' ); }, 100); } @@ -3837,7 +3850,7 @@ // show processing icon if ( c.showProcessing ) { - txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter ' ); + txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter-sp ' ); c.$table .unbind( txt.replace( ts.regex.spaces, ' ' ) ) .bind( txt, function( event, columns ) { @@ -3916,6 +3929,9 @@ c.lastSearch = c.$table.data( 'lastSearch' ); c.$table.triggerHandler( 'filterInit', c ); tsf.findRows( c.table, c.lastSearch || [] ); + if (ts.debug(c, 'filter')) { + console.log('Filter >> Widget initialized'); + } }; if ( $.isEmptyObject( wo.filter_formatter ) ) { completed(); @@ -4616,6 +4632,7 @@ storedFilters = $.extend( [], filters ), c = table.config, wo = c.widgetOptions, + debug = ts.debug(c, 'filter'), // data object passed to filters; anyMatch is a flag for the filters data = { anyMatch: false, @@ -4632,7 +4649,6 @@ defaultColFilter : [], defaultAnyFilter : ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '' }; - // parse columns after formatter, in case the class is added at that point data.parsed = []; for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { @@ -4654,8 +4670,8 @@ ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split( /\s+/ ); } - if ( c.debug ) { - console.log( 'Filter: Starting filter widget search', filters ); + if ( debug ) { + console.log( 'Filter >> Starting filter widget search', filters ); time = new Date(); } // filtered rows count @@ -4753,8 +4769,8 @@ notFiltered = $rows.not( '.' + wo.filter_filteredRow ).length; // can't search when all rows are hidden - this happens when looking for exact matches if ( searchFiltered && notFiltered === 0 ) { searchFiltered = false; } - if ( c.debug ) { - console.log( 'Filter: Searching through ' + + if ( debug ) { + console.log( 'Filter >> Searching through ' + ( searchFiltered && notFiltered < len ? notFiltered : 'all' ) + ' rows' ); } if ( data.anyMatchFlag ) { @@ -4857,8 +4873,8 @@ if ( wo.filter_saveFilters && ts.storage ) { ts.storage( table, 'tablesorter-filters', tsf.processFilters( storedFilters, true ) ); } - if ( c.debug ) { - console.log( 'Completed filter widget search' + ts.benchmark(time) ); + if ( debug ) { + console.log( 'Filter >> Completed search' + ts.benchmark(time) ); } if ( wo.filter_initialized ) { c.$table.triggerHandler( 'filterBeforeEnd', c ); @@ -5073,13 +5089,13 @@ options += '<option'; for ( val in option ) { if ( option.hasOwnProperty( val ) && val !== 'text' ) { - options += ' ' + val + '="' + option[ val ] + '"'; + options += ' ' + val + '="' + option[ val ].replace( tsfRegex.quote, '"' ) + '"'; } } if ( !option.value ) { - options += ' value="' + option.text + '"'; + options += ' value="' + option.text.replace( tsfRegex.quote, '"' ) + '"'; } - options += '>' + option.text + '</option>'; + options += '>' + option.text.replace( tsfRegex.quote, '"' ) + '</option>'; // above code is needed in jQuery < v1.8 // make sure we don't turn an object into a string (objects without a "text" property) @@ -5363,7 +5379,7 @@ $stickyThead = $stickyTable.children('thead:first'), $stickyCells, laststate = '', - setWidth = function($orig, $clone){ + setWidth = function($orig, $clone) { var index, width, border, $cell, $this, $cells = $orig.filter(':visible'), len = $cells.length; @@ -5504,7 +5520,7 @@ }); c.$table .unbind('stickyHeadersUpdate' + namespace) - .bind('stickyHeadersUpdate' + namespace, function(){ + .bind('stickyHeadersUpdate' + namespace, function() { scrollSticky( true ); }); @@ -5580,7 +5596,7 @@ }); // Add extra scroller css - $(function(){ + $(function() { var s = '<style>' + 'body.' + ts.css.resizableNoSelect + ' { -ms-user-select: none; -moz-user-select: -moz-none;' + '-khtml-user-select: none; -webkit-user-select: none; user-select: none; }' + @@ -5725,7 +5741,7 @@ if ( ts.hasWidget( c.table, 'scroller' ) ) { tableHeight = 0; - c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function(){ + c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function() { var $this = $(this); // center table has a max-height set tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); @@ -5947,7 +5963,7 @@ }); ts.resizableReset = function( table, refreshing ) { - $( table ).each(function(){ + $( table ).each(function() { var index, $t, c = this.config, wo = c && c.widgetOptions, @@ -5981,7 +5997,7 @@ })( jQuery, window ); -/*! Widget: saveSort - updated 10/31/2015 (v2.24.0) *//* +/*! Widget: saveSort - updated 2018-03-19 (v2.30.1) *//* * Requires tablesorter v2.16+ * by Rob Garrison */ @@ -5989,6 +6005,15 @@ 'use strict'; var ts = $.tablesorter || {}; + function getStoredSortList(c) { + var stored = ts.storage( c.table, 'tablesorter-savesort' ); + return (stored && stored.hasOwnProperty('sortList') && $.isArray(stored.sortList)) ? stored.sortList : []; + } + + function sortListChanged(c, sortList) { + return (sortList || getStoredSortList(c)).join(',') !== c.sortList.join(','); + } + // this widget saves the last sort only if the // saveSort widget option is true AND the // $.tablesorter.storage function is included @@ -6004,18 +6029,19 @@ thisWidget.format(table, c, wo, true); }, format: function(table, c, wo, init) { - var stored, time, + var time, $table = c.$table, saveSort = wo.saveSort !== false, // make saveSort active/inactive; default to true - sortList = { 'sortList' : c.sortList }; - if (c.debug) { + sortList = { 'sortList' : c.sortList }, + debug = ts.debug(c, 'saveSort'); + if (debug) { time = new Date(); } if ($table.hasClass('hasSaveSort')) { - if (saveSort && table.hasInitialized && ts.storage) { + if (saveSort && table.hasInitialized && ts.storage && sortListChanged(c)) { ts.storage( table, 'tablesorter-savesort', sortList ); - if (c.debug) { - console.log('saveSort widget: Saving last sort: ' + c.sortList + ts.benchmark(time)); + if (debug) { + console.log('saveSort >> Saving last sort: ' + c.sortList + ts.benchmark(time)); } } } else { @@ -6024,10 +6050,9 @@ sortList = ''; // get data if (ts.storage) { - stored = ts.storage( table, 'tablesorter-savesort' ); - sortList = (stored && stored.hasOwnProperty('sortList') && $.isArray(stored.sortList)) ? stored.sortList : ''; - if (c.debug) { - console.log('saveSort: Last sort loaded: "' + sortList + '"' + ts.benchmark(time)); + sortList = getStoredSortList(c); + if (debug) { + console.log('saveSort >> Last sort loaded: "' + sortList + '"' + ts.benchmark(time)); } $table.bind('saveSortReset', function(event) { event.stopPropagation(); @@ -6040,7 +6065,9 @@ c.sortList = sortList; } else if (table.hasInitialized && sortList && sortList.length > 0) { // update sort change - ts.sortOn( c, sortList ); + if (sortListChanged(c, sortList)) { + ts.sortOn(c, sortList); + } } } }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index a625c6e..c324368 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.29.6 *//* +/*! TableSorter (FORK) v2.30.1 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.29.6', + version : '2.30.1', parsers : [], widgets : [], @@ -34,8 +34,8 @@ showProcessing : false, // show an indeterminate timer icon in the header when the table is sorted or filtered. headerTemplate : '{content}',// header layout template (HTML ok); {content} = innerHTML, {icon} = <i/> // class from cssIcon - onRenderTemplate : null, // function( index, template ){ return template; }, // template is a string - onRenderHeader : null, // function( index ){}, // nothing to return + onRenderTemplate : null, // function( index, template ) { return template; }, // template is a string + onRenderHeader : null, // function( index ) {}, // nothing to return // *** functionality cancelSelection : true, // prevent text selection in the header @@ -64,7 +64,7 @@ emptyTo : 'bottom', // sort empty cell to bottom, top, none, zero, emptyMax, emptyMin stringTo : 'max', // sort strings in numerical column as max, min, top, bottom, zero duplicateSpan : true, // colspan cells in the tbody will have duplicated content in the cache for each spanned column - textExtraction : 'basic', // text extraction method/function - function( node, table, cellIndex ){} + textExtraction : 'basic', // text extraction method/function - function( node, table, cellIndex ) {} textAttribute : 'data-text',// data-attribute that contains alternate cell text (used in default textExtraction function) textSorter : null, // choose overall or specific column sorter function( a, b, direction, table, columnIndex ) [alt: ts.sortText] numberSorter : null, // choose overall numeric sorter function( a, b, direction, maxColumnValue ) @@ -78,7 +78,7 @@ }, // *** callbacks - initialized : null, // function( table ){}, + initialized : null, // function( table ) {}, // *** extra css class names tableClass : '', @@ -216,7 +216,7 @@ setup : function( table, c ) { // if no thead or tbody, or tablesorter is already present, quit if ( !table || !table.tHead || table.tBodies.length === 0 || table.hasInitialized === true ) { - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { if ( table.hasInitialized ) { console.warn( 'Stopping initialization. Tablesorter has already been initialized' ); } else { @@ -237,7 +237,7 @@ table.config = c; // save the settings where they read $.data( table, 'tablesorter', c ); - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { console[ console.group ? 'group' : 'log' ]( 'Initializing tablesorter v' + ts.version ); $.data( table, 'startoveralltimer', new Date() ); } @@ -298,7 +298,10 @@ ts.setupParsers( c ); // start total row count at zero c.totalRows = 0; - ts.validateOptions( c ); + // only validate options while debugging. See #1528 + if (c.debug) { + ts.validateOptions( c ); + } // build the cache for the tbody cells // delayInit will delay building the cache until the user starts a sort if ( !c.delayInit ) { ts.buildCache( c ); } @@ -343,9 +346,9 @@ // initialized table.hasInitialized = true; table.isProcessing = false; - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { console.log( 'Overall initialization time:' + ts.benchmark( $.data( table, 'startoveralltimer' ) ) ); - if ( c.debug && console.groupEnd ) { console.groupEnd(); } + if ( ts.debug(c, 'core') && console.groupEnd ) { console.groupEnd(); } } $table.triggerHandler( 'tablesorter-initialized', table ); if ( typeof c.initialized === 'function' ) { @@ -542,7 +545,7 @@ c.headerList = []; c.headerContent = []; c.sortVars = []; - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { timer = new Date(); } // children tr in tfoot - see issue #196 & #547 @@ -637,7 +640,7 @@ }); // enable/disable sorting ts.updateHeader( c ); - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { console.log( 'Built headers:' + ts.benchmark( timer ) ); console.log( c.$headers ); } @@ -660,14 +663,15 @@ noParser, parser, extractor, time, tbody, len, table = c.table, tbodyIndex = 0, - debug = {}; + debug = ts.debug(c, 'core'), + debugOutput = {}; // update table bodies in case we start with an empty table c.$tbodies = c.$table.children( 'tbody:not(.' + c.cssInfoBlock + ')' ); tbody = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies; len = tbody.length; if ( len === 0 ) { - return c.debug ? console.warn( 'Warning: *Empty table!* Not building a parser cache' ) : ''; - } else if ( c.debug ) { + return debug ? console.warn( 'Warning: *Empty table!* Not building a parser cache' ) : ''; + } else if ( debug ) { time = new Date(); console[ console.group ? 'group' : 'log' ]( 'Detecting parsers for each column' ); } @@ -709,8 +713,8 @@ if ( !parser ) { parser = ts.detectParserForColumn( c, rows, -1, colIndex ); } - if ( c.debug ) { - debug[ '(' + colIndex + ') ' + header.text() ] = { + if ( debug ) { + debugOutput[ '(' + colIndex + ') ' + header.text() ] = { parser : parser.id, extractor : extractor ? extractor.id : 'none', string : c.strings[ colIndex ], @@ -736,9 +740,9 @@ } tbodyIndex += ( list.parsers.length ) ? len : 1; } - if ( c.debug ) { - if ( !ts.isEmptyObject( debug ) ) { - console[ console.table ? 'table' : 'log' ]( debug ); + if ( debug ) { + if ( !ts.isEmptyObject( debugOutput ) ) { + console[ console.table ? 'table' : 'log' ]( debugOutput ); } else { console.warn( ' No parsers detected!' ); } @@ -764,7 +768,7 @@ }, getParserById : function( name ) { - /*jshint eqeqeq:false */ + /*jshint eqeqeq:false */ // eslint-disable-next-line eqeqeq if ( name == 'false' ) { return false; } var indx, len = ts.parsers.length; @@ -781,6 +785,7 @@ indx = ts.parsers.length, node = false, nodeValue = '', + debug = ts.debug(c, 'core'), keepLooking = true; while ( nodeValue === '' && keepLooking ) { rowIndex++; @@ -791,7 +796,7 @@ node = rows[ rowIndex ].cells[ cellIndex ]; nodeValue = ts.getElementText( c, node, cellIndex ); $node = $( node ); - if ( c.debug ) { + if ( debug ) { console.log( 'Checking if value was empty on row ' + rowIndex + ', column: ' + cellIndex + ': "' + nodeValue + '"' ); } @@ -873,7 +878,8 @@ cols, $cells, cell, cacheTime, totalRows, rowData, prevRowData, colMax, span, cacheIndex, hasParser, max, len, index, table = c.table, - parsers = c.parsers; + parsers = c.parsers, + debug = ts.debug(c, 'core'); // update tbody variable c.$tbodies = c.$table.children( 'tbody:not(.' + c.cssInfoBlock + ')' ); $tbody = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies, @@ -881,9 +887,9 @@ c.totalRows = 0; // if no parsers found, return - it's an empty table. if ( !parsers ) { - return c.debug ? console.warn( 'Warning: *Empty table!* Not building a cache' ) : ''; + return debug ? console.warn( 'Warning: *Empty table!* Not building a cache' ) : ''; } - if ( c.debug ) { + if ( debug ) { cacheTime = new Date(); } // processing icon @@ -952,7 +958,7 @@ cell = $row[ 0 ].cells[ colIndex ]; if ( cell && cacheIndex < c.columns ) { hasParser = typeof parsers[ cacheIndex ] !== 'undefined'; - if ( !hasParser && c.debug ) { + if ( !hasParser && debug ) { console.warn( 'No parser found for row: ' + rowIndex + ', column: ' + colIndex + '; cell containing: "' + $(cell).text() + '"; does it have a header?' ); } @@ -1000,7 +1006,7 @@ if ( c.showProcessing ) { ts.isProcessing( table ); // remove processing icon } - if ( c.debug ) { + if ( debug ) { len = Math.min( 5, c.cache[ 0 ].normalized.length ); console[ console.group ? 'group' : 'log' ]( 'Building cache for ' + c.totalRows + ' rows (showing ' + len + ' rows in log) and ' + c.columns + ' columns' + @@ -1031,7 +1037,7 @@ data = { raw : [], parsed: [], $cell: [] }, c = table.config; if ( ts.isEmptyObject( c ) ) { - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { console.warn( 'No cache found - aborting getColumnText function!' ); } } else { @@ -1125,8 +1131,8 @@ // direction = 2 means reset! if ( list[ indx ][ 1 ] !== 2 ) { // multicolumn sorting updating - see #1005 - // .not(function(){}) needs jQuery 1.4 - // filter(function(i, el){}) <- el is undefined in jQuery v1.2.6 + // .not(function() {}) needs jQuery 1.4 + // filter(function(i, el) {}) <- el is undefined in jQuery v1.2.6 $sorted = c.$headers.filter( function( i ) { // only include headers that are in the sortList (this includes colspans) var include = true, @@ -1375,7 +1381,7 @@ ts.resortComplete( c, callback ); } } else { - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { console.error( 'updateCell aborted, tbody missing or not within the indicated table' ); } c.table.isUpdating = false; @@ -1398,7 +1404,7 @@ // row contained in the table? ( ts.getClosest( $row, 'table' )[ 0 ] !== c.table ) ) { - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { console.error( 'addRows method requires (1) a jQuery selector reference to rows that have already ' + 'been added to the table, or (2) row HTML string to be added to a table with only one tbody' ); } @@ -1470,7 +1476,6 @@ appendCache : function( c, init ) { var parsed, totalRows, $tbody, $curTbody, rowIndex, tbodyIndex, appendTime, table = c.table, - wo = c.widgetOptions, $tbodies = c.$tbodies, rows = [], cache = c.cache; @@ -1480,7 +1485,7 @@ return c.appender ? c.appender( table, rows ) : table.isUpdating ? c.$table.triggerHandler( 'updateComplete', table ) : ''; // Fixes #532 } - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { appendTime = new Date(); } for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { @@ -1493,7 +1498,7 @@ for ( rowIndex = 0; rowIndex < totalRows; rowIndex++ ) { rows[rows.length] = parsed[ rowIndex ][ c.columns ].$row; // removeRows used by the pager plugin; don't render if using ajax - fixes #411 - if ( !c.appender || ( c.pager && ( !c.pager.removeRows || !wo.pager_removeRows ) && !c.pager.ajax ) ) { + if ( !c.appender || ( c.pager && !c.pager.removeRows && !c.pager.ajax ) ) { $curTbody.append( parsed[ rowIndex ][ c.columns ].$row ); } } @@ -1504,7 +1509,7 @@ if ( c.appender ) { c.appender( table, rows ); } - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { console.log( 'Rebuilt table' + ts.benchmark( appendTime ) ); } // apply table widgets; but not before ajax completes @@ -1535,7 +1540,7 @@ initSort : function( c, cell, event ) { if ( c.table.isUpdating ) { // let any updates complete before initializing a sort - return setTimeout( function(){ + return setTimeout( function() { ts.initSort( c, cell, event ); }, 50 ); } @@ -1686,7 +1691,7 @@ // empty table - fixes #206/#346 return; } - if ( c.debug ) { sortTime = new Date(); } + if ( ts.debug(c, 'core') ) { sortTime = new Date(); } // cache textSorter to optimize speed if ( typeof textSorter === 'object' ) { colMax = c.columns; @@ -1748,7 +1753,7 @@ return a[ c.columns ].order - b[ c.columns ].order; }); } - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { console.log( 'Applying sort ' + sortList.toString() + ts.benchmark( sortTime ) ); } }, @@ -2001,6 +2006,7 @@ var applied, time, name, c = table.config, wo = c.widgetOptions, + debug = ts.debug(c, 'core'), widget = ts.getWidgetById( id ); if ( widget ) { name = widget.id; @@ -2009,7 +2015,7 @@ if ( $.inArray( name, c.widgets ) < 0 ) { c.widgets[ c.widgets.length ] = name; } - if ( c.debug ) { time = new Date(); } + if ( debug ) { time = new Date(); } if ( init || !( c.widgetInit[ name ] ) ) { // set init flag first to prevent calling init more than once (e.g. pager) @@ -2020,7 +2026,7 @@ } if ( typeof widget.init === 'function' ) { applied = true; - if ( c.debug ) { + if ( debug ) { console[ console.group ? 'group' : 'log' ]( 'Initializing ' + name + ' widget' ); } widget.init( table, widget, c, wo ); @@ -2028,12 +2034,12 @@ } if ( !init && typeof widget.format === 'function' ) { applied = true; - if ( c.debug ) { + if ( debug ) { console[ console.group ? 'group' : 'log' ]( 'Updating ' + name + ' widget' ); } widget.format( table, c, wo, false ); } - if ( c.debug ) { + if ( debug ) { if ( applied ) { console.log( 'Completed ' + ( init ? 'initializing ' : 'applying ' ) + name + ' widget' + ts.benchmark( time ) ); if ( console.groupEnd ) { console.groupEnd(); } @@ -2046,12 +2052,13 @@ table = $( table )[ 0 ]; // in case this is called externally var indx, len, names, widget, time, c = table.config, + debug = ts.debug(c, 'core'), widgets = []; // prevent numerous consecutive widget applications if ( init !== false && table.hasInitialized && ( table.isApplyingWidgets || table.isUpdating ) ) { return; } - if ( c.debug ) { time = new Date(); } + if ( debug ) { time = new Date(); } ts.addWidgetFromClass( table ); // prevent "tablesorter-ready" from firing multiple times in a row clearTimeout( c.timerReady ); @@ -2070,7 +2077,7 @@ // set priority to 10 if not defined if ( !widget.priority ) { widget.priority = 10; } widgets[ indx ] = widget; - } else if ( c.debug ) { + } else if ( debug ) { console.warn( '"' + names[ indx ] + '" was enabled, but the widget code has not been loaded!' ); } } @@ -2080,7 +2087,7 @@ }); // add/update selected widgets len = widgets.length; - if ( c.debug ) { + if ( debug ) { console[ console.group ? 'group' : 'log' ]( 'Start ' + ( init ? 'initializing' : 'applying' ) + ' widgets' ); } for ( indx = 0; indx < len; indx++ ) { @@ -2089,7 +2096,7 @@ ts.applyWidgetId( table, widget.id, init ); } } - if ( c.debug && console.groupEnd ) { console.groupEnd(); } + if ( debug && console.groupEnd ) { console.groupEnd(); } } c.timerReady = setTimeout( function() { table.isApplyingWidgets = false; @@ -2099,7 +2106,7 @@ if ( !init && typeof callback === 'function' ) { callback( table ); } - if ( c.debug ) { + if ( debug ) { widget = c.widgets.length; console.log( 'Completed ' + ( init === true ? 'initializing ' : 'applying ' ) + widget + @@ -2136,7 +2143,7 @@ c.widgets.splice( indx, 1 ); } if ( widget && widget.remove ) { - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { console.log( ( refreshing ? 'Refreshing' : 'Removing' ) + ' "' + name[ index ] + '" widget' ); } widget.remove( table, c, c.widgetOptions, refreshing ); @@ -2190,6 +2197,12 @@ log : function() { console.log( arguments ); }, + debug : function(c, name) { + return c && ( + c.debug === true || + typeof c.debug === 'string' && c.debug.indexOf(name) > -1 + ); + }, // $.isEmptyObject from jQuery v1.4 isEmptyObject : function( obj ) { @@ -2495,7 +2508,7 @@ ignore = 'headers sortForce sortList sortAppend widgets'.split( ' ' ), orig = c.originalSettings; if ( orig ) { - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { timer = new Date(); } for ( setting in orig ) { @@ -2511,7 +2524,7 @@ } } } - if ( c.debug ) { + if ( ts.debug(c, 'core') ) { console.log( 'validate options time:' + ts.benchmark( timer ) ); } } @@ -2542,7 +2555,6 @@ var events, $t = $( table ), c = table.config, - debug = c.debug, $h = $t.find( 'thead:first' ), $r = $h.find( 'tr.' + ts.css.headerRow ).removeClass( ts.css.headerRow + ' ' + c.cssHeaderRow ), $f = $t.find( 'tfoot:first > tr' ).children( 'th, td' ); @@ -2580,7 +2592,7 @@ if ( typeof callback === 'function' ) { callback( table ); } - if ( debug ) { + if ( ts.debug(c, 'core') ) { console.log( 'tablesorter has been removed' ); } } @@ -2697,7 +2709,7 @@ is : function( str ) { return ts.regex.isoDate.test( str ); }, - format : function( str, table ) { + format : function( str ) { var date = str ? new Date( str.replace( ts.regex.dash, '/' ) ) : str; return date instanceof Date && isFinite( date ) ? date.getTime() : str; }, @@ -2740,7 +2752,7 @@ // Jan 01, 2013 12:34:56 PM or 01 Jan 2013 return ts.regex.usLongDateTest1.test( str ) || ts.regex.usLongDateTest2.test( str ); }, - format : function( str, table ) { + format : function( str ) { var date = str ? new Date( str.replace( ts.regex.dateReplace, '$1 $2' ) ) : str; return date instanceof Date && isFinite( date ) ? date.getTime() : str; }, @@ -2801,7 +2813,7 @@ is : function( str ) { return ts.regex.timeTest.test( str ); }, - format : function( str, table ) { + format : function( str ) { // isolate time... ignore month, day and year var temp, timePart = ( str || '' ).match( ts.regex.timeMatch ), @@ -2868,7 +2880,7 @@ var tbodyIndex, $tbody, $tbodies = c.$tbodies, toRemove = ( wo.zebra || [ 'even', 'odd' ] ).join( ' ' ); - for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ){ + for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { $tbody = ts.processTbody( table, $tbodies.eq( tbodyIndex ), true ); // remove tbody $tbody.children().removeClass( toRemove ); ts.processTbody( table, $tbody, false ); // restore tbody diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 46f3606..bf4c24a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-02-25 (v2.29.6)*/ +/*! tablesorter (FORK) - updated 2018-03-19 (v2.30.1)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! Widget: storage - updated 4/18/2017 (v2.28.8) */ +/*! Widget: storage - updated 2018-03-18 (v2.30.0) */ /*global JSON:false */ ;(function ($, window, document) { 'use strict'; @@ -62,6 +62,7 @@ values = {}, c = table.config, wo = c && c.widgetOptions, + debug = ts.debug(c, 'storage'), storageType = ( ( options && options.storageType ) || ( wo && wo.storage_storageType ) ).toString().charAt(0).toLowerCase(), @@ -90,14 +91,12 @@ hasStorage = true; window[storageType].removeItem('_tmptest'); } catch (error) { - if (c && c.debug) { - console.warn( storageType + ' is not supported in this browser' ); - } + console.warn( storageType + ' is not supported in this browser' ); } } } - if (c.debug) { - console.log('Storage widget using', hasStorage ? storageType : 'cookies'); + if (debug) { + console.log('Storage >> Using', hasStorage ? storageType : 'cookies'); } // *** get value *** if ($.parseJSON) { @@ -133,7 +132,7 @@ })(jQuery, window, document); -/*! Widget: uitheme - updated 9/27/2017 (v2.29.0) */ +/*! Widget: uitheme - updated 2018-03-18 (v2.30.0) */ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; @@ -198,8 +197,9 @@ theme = c.theme || 'jui', themes = themesAll[theme] || {}, remove = $.trim( [ themes.sortNone, themes.sortDesc, themes.sortAsc, themes.active ].join( ' ' ) ), - iconRmv = $.trim( [ themes.iconSortNone, themes.iconSortDesc, themes.iconSortAsc ].join( ' ' ) ); - if (c.debug) { time = new Date(); } + iconRmv = $.trim( [ themes.iconSortNone, themes.iconSortDesc, themes.iconSortAsc ].join( ' ' ) ), + debug = ts.debug(c, 'uitheme'); + if (debug) { time = new Date(); } // initialization code - run once if (!$table.hasClass('tablesorter-' + theme) || c.theme !== c.appliedTheme || !wo.uitheme_applied) { wo.uitheme_applied = true; @@ -244,7 +244,7 @@ $(this)[ event.type === 'mouseenter' ? 'addClass' : 'removeClass' ](themes.hover || ''); }); - $headers.each(function(){ + $headers.each(function() { var $this = $(this); if (!$this.find('.' + ts.css.wrapper).length) { // Firefox needs this inner div to position the icon & resizer correctly @@ -302,8 +302,8 @@ } } } - if (c.debug) { - console.log('Applying ' + theme + ' theme' + ts.benchmark(time)); + if (debug) { + console.log('uitheme >> Applied ' + theme + ' theme' + ts.benchmark(time)); } }, remove: function(table, c, wo, refreshing) { @@ -408,7 +408,7 @@ })(jQuery); -/*! Widget: filter - updated 2018-01-30 (v2.29.5) *//* +/*! Widget: filter - updated 2018-03-18 (v2.30.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -690,6 +690,7 @@ if ( tsfRegex.exact.test( data.iFilter ) ) { var txt = data.iFilter.replace( tsfRegex.exact, '' ), filter = tsf.parseFilter( c, txt, data ) || ''; + // eslint-disable-next-line eqeqeq return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact; } return null; @@ -863,7 +864,7 @@ c.lastCombinedFilter = null; c.lastSearch = []; // update filterFormatters after update (& small delay) - Fixes #1237 - setTimeout(function(){ + setTimeout(function() { c.$table.triggerHandler( 'filterFomatterUpdate' ); }, 100); } @@ -957,7 +958,7 @@ // show processing icon if ( c.showProcessing ) { - txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter ' ); + txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter-sp ' ); c.$table .unbind( txt.replace( ts.regex.spaces, ' ' ) ) .bind( txt, function( event, columns ) { @@ -1036,6 +1037,9 @@ c.lastSearch = c.$table.data( 'lastSearch' ); c.$table.triggerHandler( 'filterInit', c ); tsf.findRows( c.table, c.lastSearch || [] ); + if (ts.debug(c, 'filter')) { + console.log('Filter >> Widget initialized'); + } }; if ( $.isEmptyObject( wo.filter_formatter ) ) { completed(); @@ -1736,6 +1740,7 @@ storedFilters = $.extend( [], filters ), c = table.config, wo = c.widgetOptions, + debug = ts.debug(c, 'filter'), // data object passed to filters; anyMatch is a flag for the filters data = { anyMatch: false, @@ -1752,7 +1757,6 @@ defaultColFilter : [], defaultAnyFilter : ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '' }; - // parse columns after formatter, in case the class is added at that point data.parsed = []; for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { @@ -1774,8 +1778,8 @@ ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split( /\s+/ ); } - if ( c.debug ) { - console.log( 'Filter: Starting filter widget search', filters ); + if ( debug ) { + console.log( 'Filter >> Starting filter widget search', filters ); time = new Date(); } // filtered rows count @@ -1873,8 +1877,8 @@ notFiltered = $rows.not( '.' + wo.filter_filteredRow ).length; // can't search when all rows are hidden - this happens when looking for exact matches if ( searchFiltered && notFiltered === 0 ) { searchFiltered = false; } - if ( c.debug ) { - console.log( 'Filter: Searching through ' + + if ( debug ) { + console.log( 'Filter >> Searching through ' + ( searchFiltered && notFiltered < len ? notFiltered : 'all' ) + ' rows' ); } if ( data.anyMatchFlag ) { @@ -1977,8 +1981,8 @@ if ( wo.filter_saveFilters && ts.storage ) { ts.storage( table, 'tablesorter-filters', tsf.processFilters( storedFilters, true ) ); } - if ( c.debug ) { - console.log( 'Completed filter widget search' + ts.benchmark(time) ); + if ( debug ) { + console.log( 'Filter >> Completed search' + ts.benchmark(time) ); } if ( wo.filter_initialized ) { c.$table.triggerHandler( 'filterBeforeEnd', c ); @@ -2193,13 +2197,13 @@ options += '<option'; for ( val in option ) { if ( option.hasOwnProperty( val ) && val !== 'text' ) { - options += ' ' + val + '="' + option[ val ] + '"'; + options += ' ' + val + '="' + option[ val ].replace( tsfRegex.quote, '"' ) + '"'; } } if ( !option.value ) { - options += ' value="' + option.text + '"'; + options += ' value="' + option.text.replace( tsfRegex.quote, '"' ) + '"'; } - options += '>' + option.text + '</option>'; + options += '>' + option.text.replace( tsfRegex.quote, '"' ) + '</option>'; // above code is needed in jQuery < v1.8 // make sure we don't turn an object into a string (objects without a "text" property) @@ -2483,7 +2487,7 @@ $stickyThead = $stickyTable.children('thead:first'), $stickyCells, laststate = '', - setWidth = function($orig, $clone){ + setWidth = function($orig, $clone) { var index, width, border, $cell, $this, $cells = $orig.filter(':visible'), len = $cells.length; @@ -2624,7 +2628,7 @@ }); c.$table .unbind('stickyHeadersUpdate' + namespace) - .bind('stickyHeadersUpdate' + namespace, function(){ + .bind('stickyHeadersUpdate' + namespace, function() { scrollSticky( true ); }); @@ -2700,7 +2704,7 @@ }); // Add extra scroller css - $(function(){ + $(function() { var s = '<style>' + 'body.' + ts.css.resizableNoSelect + ' { -ms-user-select: none; -moz-user-select: -moz-none;' + '-khtml-user-select: none; -webkit-user-select: none; user-select: none; }' + @@ -2845,7 +2849,7 @@ if ( ts.hasWidget( c.table, 'scroller' ) ) { tableHeight = 0; - c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function(){ + c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function() { var $this = $(this); // center table has a max-height set tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); @@ -3067,7 +3071,7 @@ }); ts.resizableReset = function( table, refreshing ) { - $( table ).each(function(){ + $( table ).each(function() { var index, $t, c = this.config, wo = c && c.widgetOptions, @@ -3101,7 +3105,7 @@ })( jQuery, window ); -/*! Widget: saveSort - updated 10/31/2015 (v2.24.0) *//* +/*! Widget: saveSort - updated 2018-03-19 (v2.30.1) *//* * Requires tablesorter v2.16+ * by Rob Garrison */ @@ -3109,6 +3113,15 @@ 'use strict'; var ts = $.tablesorter || {}; + function getStoredSortList(c) { + var stored = ts.storage( c.table, 'tablesorter-savesort' ); + return (stored && stored.hasOwnProperty('sortList') && $.isArray(stored.sortList)) ? stored.sortList : []; + } + + function sortListChanged(c, sortList) { + return (sortList || getStoredSortList(c)).join(',') !== c.sortList.join(','); + } + // this widget saves the last sort only if the // saveSort widget option is true AND the // $.tablesorter.storage function is included @@ -3124,18 +3137,19 @@ thisWidget.format(table, c, wo, true); }, format: function(table, c, wo, init) { - var stored, time, + var time, $table = c.$table, saveSort = wo.saveSort !== false, // make saveSort active/inactive; default to true - sortList = { 'sortList' : c.sortList }; - if (c.debug) { + sortList = { 'sortList' : c.sortList }, + debug = ts.debug(c, 'saveSort'); + if (debug) { time = new Date(); } if ($table.hasClass('hasSaveSort')) { - if (saveSort && table.hasInitialized && ts.storage) { + if (saveSort && table.hasInitialized && ts.storage && sortListChanged(c)) { ts.storage( table, 'tablesorter-savesort', sortList ); - if (c.debug) { - console.log('saveSort widget: Saving last sort: ' + c.sortList + ts.benchmark(time)); + if (debug) { + console.log('saveSort >> Saving last sort: ' + c.sortList + ts.benchmark(time)); } } } else { @@ -3144,10 +3158,9 @@ sortList = ''; // get data if (ts.storage) { - stored = ts.storage( table, 'tablesorter-savesort' ); - sortList = (stored && stored.hasOwnProperty('sortList') && $.isArray(stored.sortList)) ? stored.sortList : ''; - if (c.debug) { - console.log('saveSort: Last sort loaded: "' + sortList + '"' + ts.benchmark(time)); + sortList = getStoredSortList(c); + if (debug) { + console.log('saveSort >> Last sort loaded: "' + sortList + '"' + ts.benchmark(time)); } $table.bind('saveSortReset', function(event) { event.stopPropagation(); @@ -3160,7 +3173,9 @@ c.sortList = sortList; } else if (table.hasInitialized && sortList && sortList.length > 0) { // update sort change - ts.sortOn( c, sortList ); + if (sortListChanged(c, sortList)) { + ts.sortOn(c, sortList); + } } } }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js index 7af9585..d99300e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js @@ -1,6 +1,6 @@ /*! Parser: Extract out date - updated 10/26/2014 (v2.18.0) */ /*jshint jquery:true */ -;(function($){ +;(function($) { 'use strict'; var regex = { diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js index 5a1e3e6..e7c230c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js @@ -5,7 +5,7 @@ * See https://github.com/Mottie/tablesorter/issues/247 */ /*global jQuery: false */ -;(function($){ +;(function($) { 'use strict'; var iso8601date = /^([0-9]{4})(-([0-9]{2})(-([0-9]{2})(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?$/; diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js index 9a9f6e8..19eda84 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js @@ -1,7 +1,7 @@ /*! Parser: Month - updated 11/22/2015 (v2.24.6) */ /* Demo: http://jsfiddle.net/Mottie/abkNM/4169/ */ /*jshint jquery:true */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter; diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-range.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-range.js index 9c23097..f33ab0e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-range.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-range.js @@ -1,7 +1,7 @@ /*! Parser: date ranges -updated 11/22/2015 (v2.24.6) */ /* Include the 'widget-filter-type-insideRange.js' to filter ranges */ /*jshint jquery:true */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter, diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js index ceb822c..e301170 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js @@ -1,7 +1,7 @@ /*! Parser: two digit year - updated 11/26/2016 (v2.28.0) */ /* Demo: http://mottie.github.io/tablesorter/docs/example-parsers-dates.html */ /*jshint jquery:true */ -;(function($){ +;(function($) { 'use strict'; // Make the date be within +/- range of the 2 digit year @@ -20,7 +20,7 @@ ts.dates.regxxxxyy = /(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{2})/; ts.dates.regyyxxxx = /(\d{2})[\/\s](\d{1,2})[\/\s](\d{1,2})/; - ts.formatDate = function(s, regex, format, table){ + ts.formatDate = function(s, regex, format, table) { if (s) { var y, rng, n = s diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js index 734508d..af02891 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js @@ -1,7 +1,7 @@ /*! Parser: weekday - updated 11/22/2015 (v2.24.6) */ /* Demo: http://jsfiddle.net/Mottie/abkNM/4169/ */ /*jshint jquery:true */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter; diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js index 30072c9..313288a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js @@ -2,7 +2,7 @@ /* Extract dates using popular natural language date parsers */ /*jshint jquery:true */ /*global Sugar*/ -;(function($){ +;(function($) { 'use strict'; /*! Sugar (https://sugarjs.com/docs/#/DateParsing) */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-duration.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-duration.js index 2ad1fd0..0848cf7 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-duration.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-duration.js @@ -1,6 +1,6 @@ /*! Parser: duration & countdown - updated 2/7/2015 (v2.19.0) */ /*jshint jquery:true, unused:false */ -;(function($){ +;(function($) { 'use strict'; // If any number > 9999, then set table.config.durationLength = 5 diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-feet-inch-fraction.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-feet-inch-fraction.js index 3db96cb..6abdb3c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-feet-inch-fraction.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-feet-inch-fraction.js @@ -4,7 +4,7 @@ * Demo: http://jsfiddle.net/Mottie/abkNM/154/ */ /*global jQuery: false */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter; @@ -26,7 +26,7 @@ n = p + parseInt(t[0], 10) / parseInt(t[1] || 1, 10); // look for fraction symbols } else if (ts.symbolRegex.test(n)) { - n = p + n.replace(ts.symbolRegex, function(m){ + n = p + n.replace(ts.symbolRegex, function(m) { return { '\u215b' : '.125', // 1/8 '\u215c' : '.375', // 3/8 diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js index a6d8195..9e211b5 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js @@ -3,7 +3,7 @@ * prefixed into the parsed data, so sorting occurs in groups */ /*global jQuery: false */ -;(function($){ +;(function($) { 'use strict'; // basic list from http://en.wikipedia.org/wiki/List_of_file_formats @@ -51,7 +51,7 @@ if (!m) { // make a string to 'quick' match the existing equivalents t = []; - $.each(types, function(i, v){ + $.each(types, function(i, v) { t.push(v); }); m = $.tablesorter.fileTypes.matching = sep + t.join(sep) + sep; diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js index 4b6795a..1de34ed 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js @@ -4,7 +4,7 @@ * Demo: http://jsfiddle.net/Mottie/abkNM/5/ */ /*jshint browser: true, jquery:true, unused:false */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter; diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-image.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-image.js index adba3c4..59eb023 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-image.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-image.js @@ -2,12 +2,12 @@ /* alt attribute parser for jQuery 1.7+ & tablesorter 2.7.11+ */ /* NOTE! Moved to jquery.tablesorter.js (core) in v2.18.0 */ /*jshint jquery:true, unused:false */ -;(function($){ +;(function($) { 'use strict'; $.tablesorter.addParser({ id: 'image', - is: function(){ + is: function() { return false; }, format: function(s, table, cell) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index 0742953..9d45c04 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -6,7 +6,7 @@ ;( function( $ ) { 'use strict'; - var updateServer = function( event, $table, $input ) { + var updateServer = function( /* event, $table, $input */ ) { // do something here to update your server, if needed // event = change event object // $table = jQuery object of the table that was just updated diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-leading-zeros.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-leading-zeros.js index d3ad897..99ba16b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-leading-zeros.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-leading-zeros.js @@ -18,6 +18,7 @@ str = number.toString(); if ( !isNaN( number ) && + // eslint-disable-next-line eqeqeq number == val && // jshint ignore:line val.length !== str.length ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js index 28d2dc8..a39930f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js @@ -2,7 +2,7 @@ * code modified from http://stackoverflow.com/a/12014376/145346 */ /*jshint jquery:true */ -;(function($){ +;(function($) { 'use strict'; // Change language of the named numbers as needed diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js index 2a8b9b6..148f53a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-network.js @@ -1,7 +1,7 @@ /*! Parser: network - updated 2018-01-10 (v2.29.3) */ /* IPv4, IPv6 and MAC Addresses */ /*global jQuery: false */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter, @@ -47,13 +47,13 @@ if (ts.regex.ipv4Validate.test(address)) { groups = address.match(ts.regex.ipv4Extract); t = ''; - for (i = 1; i < groups.length; i++){ + for (i = 1; i < groups.length; i++) { t += ('00' + (parseInt(groups[i], 10).toString(16)) ).slice(-2) + ( i === 2 ? ':' : '' ); } address = address.replace( ts.regex.ipv4Extract, t ); } - if (address.indexOf('::') == -1) { + if (address.indexOf('::') === -1) { // All eight groups are present fullAddress = address; } else { @@ -74,7 +74,7 @@ // it's fastest & easiest for tablesorter to sort decimal values (vs hex) groups[i] = hex ? ('0000' + groups[i]).slice(-4) : ('00000' + (parseInt(groups[i], 16) || 0)).slice(-5); - expandedAddress += ( i != validGroupCount - 1) ? groups[i] + ':' : groups[i]; + expandedAddress += ( i !== validGroupCount - 1) ? groups[i] + ':' : groups[i]; } return expandedAddress; }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-roman.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-roman.js index 0386bde..62f7c8e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-roman.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-roman.js @@ -4,7 +4,7 @@ * Jonathan Snook comment @ http://blog.stevenlevithan.com/archives/javascript-roman-numeral-converter#comment-16140 */ /*jshint jquery:true, unused:false */ -;(function($){ +;(function($) { 'use strict'; // allow lower case roman numerals, since lists use i, ii, iii, etc. @@ -14,7 +14,7 @@ $.tablesorter.addParser({ id: 'roman', - is: function(){ + is: function() { return false; }, format: function(s) { @@ -39,7 +39,7 @@ $.tablesorter.addParser({ id: 'roman-ignore', - is: function(){ + is: function() { return false; }, format: function(s, table, cell, column) { @@ -80,13 +80,13 @@ $.tablesorter.addParser({ id: 'roman-extract', - is: function(){ + is: function() { return false; }, format: function(s) { var val, // find roman numerals - roman = $.grep(s.split(/\b/), function(v, i){ + roman = $.grep(s.split(/\b/), function(v) { return validator.test(v) ? v : ''; }).join('').match(matcher), diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js index 628f1d0..bae8ba7 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js @@ -4,14 +4,14 @@ */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter; ts.alignChar = { init : function(table, c, wo) { - c.$headers.filter('[' + wo.alignChar_charAttrib + ']').each(function(){ + c.$headers.filter('[' + wo.alignChar_charAttrib + ']').each(function() { var $this = $(this), vars = { column : this.column, @@ -27,14 +27,14 @@ }); }, - setup: function(table, c, wo, v){ + setup: function(table, c, wo, v) { // do nothing for empty tables if ($.isEmptyObject(c.cache)) { return; } var tbodyIndex, rowIndex, start, end, last, index, rows, val, count, len, wLeft, wRight, alignChar, $row, left = [], right = []; - for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++){ + for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++) { rows = c.cache[tbodyIndex]; len = rows.normalized.length; for (rowIndex = 0; rowIndex < len; rowIndex++) { @@ -70,14 +70,14 @@ } // find widest segments - wLeft = ($.extend([], left)).sort(function(a, b){ return b.length - a.length; })[0]; - wRight = ($.extend([], right)).sort(function(a, b){ return b.length - a.length; })[0]; + wLeft = ($.extend([], left)).sort(function(a, b) { return b.length - a.length; })[0]; + wRight = ($.extend([], right)).sort(function(a, b) { return b.length - a.length; })[0]; // calculate percentage widths v.width = v.width || ( Math.floor(wLeft.length / (wLeft.length + wRight.length) * 100) + v.adjust ); wLeft = 'min-width:' + v.width + '%'; wRight = 'min-width:' + (100 - v.width) + '%'; - for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++){ + for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++) { rows = c.cache[tbodyIndex]; len = rows.normalized.length; for (rowIndex = 0; rowIndex < len; rowIndex++) { @@ -94,10 +94,10 @@ }, - remove: function(table, c, column){ + remove: function(table, c, column) { if ($.isEmptyObject(c.cache)) { return; } var tbodyIndex, rowIndex, len, rows, $row, $cell; - for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++){ + for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++) { rows = c.cache[tbodyIndex]; len = rows.normalized.length; for (rowIndex = 0; rowIndex < len; rowIndex++) { @@ -118,26 +118,26 @@ alignChar_indexAttrib : 'data-align-index', alignChar_adjustAttrib : 'data-align-adjust' // percentage width adjustments }, - init: function(table, thisWidget, c, wo){ + init: function(table, thisWidget, c, wo) { wo.alignChar_initialized = false; wo.alignChar_savedVars = []; ts.alignChar.init(table, c, wo); - c.$table.on('pagerEnd refreshAlign', function(){ - c.$headers.filter('[' + wo.alignChar_charAttrib + ']').each(function(){ + c.$table.on('pagerEnd refreshAlign', function() { + c.$headers.filter('[' + wo.alignChar_charAttrib + ']').each(function() { ts.alignChar.remove(table, c, this.column); }); ts.alignChar.init(table, c, wo); }); }, - format : function(table, c, wo){ + format : function(table, c, wo) { // reinitialize in case table is empty when first initialized if (!wo.alignChar_initialized) { c.$table.triggerHandler('refreshAlign'); } }, - remove : function(table, c, wo, refreshing){ + remove : function(table, c, wo, refreshing) { if (refreshing) { return; } - c.$headers.filter('[' + wo.alignChar_charAttrib + ']').each(function(){ + c.$headers.filter('[' + wo.alignChar_charAttrib + ']').each(function() { ts.alignChar.remove(table, c, this.column); }); wo.alignChar_initialized = false; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js index 057fef1..0d60dfd 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js @@ -1,17 +1,17 @@ -/*! Widget: Build Table - updated 4/2/2017 (v2.28.6) *//* +/*! Widget: Build Table - updated 2018-03-18 (v2.30.0) *//* * for tableSorter v2.16.0+ * by Rob Garrison */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter = $.tablesorter || {}, // build a table from data (requires existing <table> tag) // data.header contains an array of header titles // data.rows contains an array of rows which contains an array of cells - bt = ts.buildTable = function(tar, c){ + bt = ts.buildTable = function(tar, c) { // add build options to defaults to prevent warnings $.extend(true, ts.defaults.widgetOptions, bt.defaults); // add table if one doesn't exist @@ -21,9 +21,10 @@ p = wo.build_processing, typ = wo.build_type, d = wo.build_source || c.data, + debug = ts.debug(c, 'build'), // determine type: html, json, array, csv, object - runType = function(d){ + runType = function(d) { var t = $.type(d), jq = d instanceof jQuery; // run any processing if set @@ -59,7 +60,9 @@ // even if wo.build_type is undefined, we can try to figure out the type if ( !ts.buildTable.hasOwnProperty(typ) && typ !== '' ) { - if (c.debug) { console.error('aborting build table widget, incorrect build type'); } + if (debug) { + console.error('Build >> ERROR: Aborting build table widget, incorrect build type'); + } return false; } @@ -72,8 +75,10 @@ .done(function(data) { runType(data); }) - .fail(function( jqXHR, textStatus, errorThrown) { - if (c.debug) { console.error('aborting build table widget, failed ajax load'); } + .fail(function( jqXHR, textStatus) { + if (debug) { + console.error('Build >> ERROR: Aborting build table widget, failed ajax load'); + } $tbl.html('<tr><td class="error">' + jqXHR.status + ' ' + textStatus + '</td></tr>'); }); } else { @@ -125,7 +130,7 @@ // add colgroup if widths set if (widths && widths.length) { t += '<colgroup>'; - $.each(widths, function(i, w){ + $.each(widths, function(i, w) { t += '<col' + ( w ? ' style="width:' + w + '"' : '' ) + '>'; }); t += '</colgroup>'; @@ -133,7 +138,7 @@ return t; }, // d = cell data; typ = 'th' or 'td'; first = save widths from first header row only - cell : function(d, wo, typ, col, first){ + cell : function(d, wo, typ, col, first) { var j, $td, $col = first ? $('<col>') : '', cls = wo.build_headers.classes, @@ -150,7 +155,7 @@ // assume we have an object $td = $('<' + typ + '>'); for (j in d) { - if (d.hasOwnProperty(j)){ + if (d.hasOwnProperty(j)) { if (j === 'text' || j === 'html') { $td[j]( d[j] ); } else if (first && j === 'width') { @@ -165,7 +170,7 @@ return [ $td, $col ]; }, // h1 = header text from data - header : function(h1, wo){ + header : function(h1, wo) { var h2 = wo.build_headers.text, cls = wo.build_headers.classes, t = '<tr>' + (wo.build_numbers.addColumn ? '<th' + (wo.build_numbers.sortable ? '' : @@ -180,7 +185,7 @@ }); return t + '</tr>'; }, - rows : function(items, txt, c, wo, num, ftr){ + rows : function(items, txt, c, wo, num, ftr) { var h = (ftr ? 'th' : 'td'), t = '<tr>' + (wo.build_numbers.addColumn ? '<' + h + '>' + (ftr ? '' : num) + '</' + h + '>' : ''); $.each(items, function(i, item) { @@ -196,8 +201,11 @@ } }; - bt.buildComplete = function(table, wo){ + bt.buildComplete = function(table, wo) { $(table).triggerHandler(wo.build_complete); + if (table.config && ts.debug(table.config, 'build')) { + console.log('Build >> Table build complete'); + } ts.setup(table, table.config); }; @@ -361,7 +369,9 @@ r = data.hasOwnProperty(kr) && !$.isEmptyObject(data.kr) ? data.kr : data.hasOwnProperty('rows') ? data.rows : false; if (!h || !r || h.length === 0 || r.length === 0) { - if (c.debug) { console.error('aborting build table widget, missing data for object build'); } + if (ts.debug(c, 'build')) { + console.error('Build >> ERROR: Aborting build table widget, missing data for object build'); + } return false; } @@ -371,7 +381,7 @@ // Build thead // h = [ ['headerRow1Cell1', 'headerRow1Cell2', ... 'headerRow1CellN' ], ['headerRow2Cell1', ... ] ] // or h = [ [ { text: 'firstCell', class: 'fc', width: '20%' }, ..., { text: 'last Cell' } ], [ /* second row */ ] ] - $.each(h, function(i, d){ + $.each(h, function(i, d) { $tr = $('<tr>').appendTo( $t.find('thead') ); l = d.length; // header row for ( j = 0; j < l; j++ ) { @@ -388,14 +398,14 @@ $tb = $('<tbody>'); // Build tbody - $.each(r, function(i, d){ + $.each(r, function(i, d) { var j; t = $.type(d) === 'object'; // add new tbody if (t && d.newTbody) { $tb = $('<tbody>').appendTo( $t ); for (j in d) { - if (d.hasOwnProperty(j) && j !== 'newTbody'){ + if (d.hasOwnProperty(j) && j !== 'newTbody') { $tb.attr(j, d[j]); } } @@ -409,7 +419,7 @@ if (t) { // row defined by object for (j in d) { - if (d.hasOwnProperty(j) && j !== wo.build_objectCellKey){ + if (d.hasOwnProperty(j) && j !== wo.build_objectCellKey) { $tr.attr(j, d[j]); } } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js index a28f2cb..08838aa 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js @@ -3,7 +3,7 @@ */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index f401b33..eeaf81d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,10 +1,10 @@ -/* Widget: columnSelector (responsive table widget) - updated 9/27/2017 (v2.29.0) *//* +/* Widget: columnSelector (responsive table widget) - updated 2018-03-18 (v2.30.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter, @@ -15,13 +15,14 @@ queryBreak : '@media all and (min-width: [size]) { [columns] { display: table-cell; } } ', init: function(table, c, wo) { - var $t, colSel; + var $t, colSel, + debug = ts.debug(c, 'columnSelector'); // abort if no input is contained within the layout $t = $(wo.columnSelector_layout); if (!$t.find('input').add( $t.filter('input') ).length) { - if (c.debug) { - console.error('ColumnSelector: >> ERROR: Column Selector aborting, no input found in the layout! ***'); + if (debug) { + console.error('ColumnSelector >> ERROR: Column Selector aborting, no input found in the layout! ***'); } return; } @@ -45,8 +46,8 @@ colSel.isInitializing = false; if (colSel.$container.length) { tsColSel.updateCols(c, wo); - } else if (c.debug) { - console.warn('ColumnSelector: >> container not found'); + } else if (debug) { + console.warn('ColumnSelector >> container not found'); } c.$table @@ -59,11 +60,14 @@ [ 'auto', [2,3,4] ] = set visible columns; turn on "auto" mode. true = turn on "auto" mode. */ - .on('refreshColumnSelector' + namespace, function( e, optName, optState ){ + .on('refreshColumnSelector' + namespace, function( e, optName, optState ) { // make sure we're using current config settings tsColSel.refreshColumns( this.config, optName, optState ); }); + if (debug) { + console.log('ColumnSelector >> Widget initialized'); + } }, refreshColumns: function( c, optName, optState ) { @@ -87,7 +91,7 @@ if (isArry) { arry = optState || optName; // make sure array contains numbers - $.each(arry, function(i, v){ + $.each(arry, function(i, v) { arry[i] = parseInt(v, 10); }); for (i = 0; i < c.columns; i++) { @@ -229,7 +233,7 @@ .attr('data-column', 'auto') .prop('checked', colSel.auto) .toggleClass( wo.columnSelector_cssChecked, colSel.auto ) - .on('change', function(){ + .on('change', function() { tsColSel.updateAuto(c, wo, $(this)); }).change(); } @@ -243,7 +247,7 @@ updateAuto: function(c, wo, $el) { var colSel = c.selector; colSel.auto = $el.prop('checked') || false; - $.each( colSel.$checkbox, function(i, $cb){ + $.each( colSel.$checkbox, function(i, $cb) { if ($cb) { $cb[0].disabled = colSel.auto; colSel.$wrapper[i].toggleClass('disabled', colSel.auto); @@ -257,7 +261,7 @@ if (c.selector.$popup) { c.selector.$popup.find('.tablesorter-column-selector') .html( colSel.$container.html() ) - .find('input').each(function(){ + .find('input').each(function() { var indx = $(this).attr('data-column'); $(this).prop( 'checked', indx === 'auto' ? colSel.auto : colSel.states[indx] ); }); @@ -306,10 +310,10 @@ } } // only 6 breakpoints (same as jQuery Mobile) - for (priority = 0; priority < wo.columnSelector_maxPriorities; priority++){ + for (priority = 0; priority < wo.columnSelector_maxPriorities; priority++) { /*jshint loopfunc:true */ breaks = []; - c.$headers.filter('[' + wo.columnSelector_priority + '=' + (priority + 1) + ']').each(function(){ + c.$headers.filter('[' + wo.columnSelector_priority + '=' + (priority + 1) + ']').each(function() { column = parseInt($(this).attr('data-column'), 10) + 1; // don't reveal columnSelector false columns if ( !isHidden[ column ] ) { @@ -340,14 +344,14 @@ colSel = c.selector, styles = [], prefix = c.namespace + 'columnselector'; - colSel.$container.find('input[data-column]').filter('[data-column!="auto"]').each(function(){ + colSel.$container.find('input[data-column]').filter('[data-column!="auto"]').each(function() { if (!this.checked) { column = parseInt( $(this).attr('data-column'), 10 ) + 1; styles = styles.concat( tsColSel.addSelectors( wo, prefix, column ) ); } $(this).toggleClass( wo.columnSelector_cssChecked, this.checked ); }); - if (wo.columnSelector_mediaquery){ + if (wo.columnSelector_mediaquery) { colSel.$breakpoints.prop('disabled', true); } if (colSel.$style) { @@ -451,7 +455,7 @@ wo = c.widgetOptions; $popup.find('.tablesorter-column-selector') .html( colSel.$container.html() ) - .find('input').each(function(){ + .find('input').each(function() { var indx = $(this).attr('data-column'), isChecked = indx === 'auto' ? colSel.auto : colSel.states[indx]; $(this) @@ -503,7 +507,7 @@ // container layout columnSelector_layout : '<label><input type="checkbox">{name}</label>', // layout customizer callback called for each column - // function($cell, name, column){ return name || $cell.html(); } + // function($cell, name, column) { return name || $cell.html(); } columnSelector_layoutCustomizer : null, // data attribute containing column name to use in the selector container columnSelector_name : 'data-selector-name', diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js index 9c5a038..02e248c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js @@ -2,7 +2,7 @@ * Requires a modern browser, tablesorter v2.8+ */ /*jshint jquery:true, unused:false */ -;(function($, window){ +;(function($, window) { 'use strict'; var ts = $.tablesorter; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js index 67e6735..771fc9c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js @@ -7,7 +7,7 @@ */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter || {}, @@ -17,12 +17,12 @@ tsff = ts.filterFormatter = $.extend( {}, ts.filterFormatter, { - addCompare: function($cell, indx, options){ + addCompare: function($cell, indx, options) { if (options.compare && $.isArray(options.compare) && options.compare.length > 1) { var opt = '', compareSelectClass = [ compareSelect.slice(1), ' ' + compareSelect.slice(1), '' ], txt = options.cellText ? '<label class="' + compareSelectClass.join('-label') + indx + '">' + options.cellText + '</label>' : ''; - $.each(options.compare, function(i, c){ + $.each(options.compare, function(i, c) { opt += '<option ' + (options.selected === i ? 'selected' : '') + '>' + c + '</option>'; }); $cell @@ -38,7 +38,7 @@ num = val.replace(/\s*?[><=]\s*?/g, ''), compare = val.match(/[><=]/g) || ''; if (o.compare) { - if ($.isArray(o.compare)){ + if ($.isArray(o.compare)) { compare = (compare || []).join('') || o.compare[o.selected || 0]; } $cell.find(compareSelect).val( compare ); @@ -72,7 +72,7 @@ $shcell = [], c = $cell.closest('table')[0].config, - updateNumber = function(delayed, notrigger){ + updateNumber = function(delayed, notrigger) { var chkd = o.addToggle ? $cell.find('.toggle').is(':checked') : true, v = $cell.find('.number').val(), compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', @@ -104,13 +104,13 @@ // add HTML5 number (spinner) $cell .append(t + '<input type="hidden" />') - .find('.toggle, .number').bind('change', function(){ + .find('.toggle, .number').bind('change', function() { updateNumber(); }) .closest('thead').find('th[data-column=' + indx + ']') .addClass('filter-parsed') // get exact numbers from column // on reset - .closest('table').bind('filterReset', function(){ + .closest('table').bind('filterReset', function() { if ($.isArray(o.compare)) { $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); } @@ -122,17 +122,17 @@ } } $cell.find('.number').val( o.value ); - setTimeout(function(){ + setTimeout(function() { updateNumber(); }, 0); }); - $input = $cell.find('input[type=hidden]').bind('change', function(){ + $input = $cell.find('input[type=hidden]').bind('change', function() { $cell.find('.number').val( this.value ); updateNumber(); }); // update slider from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ + c.$table.bind('filterFomatterUpdate', function() { var val = tsff.updateCompare($cell, $input, o)[0] || o.value; $cell.find('.number').val( ((val || '') + '').replace(/[><=]/g, '') ); updateNumber(false, true); @@ -142,17 +142,17 @@ if (o.compare) { // add compare select tsff.addCompare($cell, indx, o); - $cell.find(compareSelect).bind('change', function(){ + $cell.find(compareSelect).bind('change', function() { updateNumber(); }); } // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ + c.$table.bind('stickyHeadersInit', function() { $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); $shcell .append(t) - .find('.toggle, .number').bind('change', function(){ + .find('.toggle, .number').bind('change', function() { $cell.find('.number').val( $(this).val() ); updateNumber(); }); @@ -160,7 +160,7 @@ if (o.compare) { // add compare select tsff.addCompare($shcell, indx, o); - $shcell.find(compareSelect).bind('change', function(){ + $shcell.find(compareSelect).bind('change', function() { $cell.find(compareSelect).val( $(this).val() ); updateNumber(); }); @@ -203,15 +203,17 @@ $shcell = [], c = $cell.closest('table')[0].config, - updateRange = function(v, delayed, notrigger){ + updateRange = function(v, delayed, notrigger) { /*jshint eqeqeq:false */ // hidden input changes may include compare symbols v = ( typeof v === 'undefined' ? $input.val() : v ).toString().replace(/[<>=]/g, '') || o.value; var compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '', + // eslint-disable-next-line eqeqeq t = ' (' + (compare ? compare + v : v == o.min ? o.allText : v) + ')', searchType = c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) || '' : true; $cell.find('input[type=hidden]') // add equal to the beginning, so we filter exact numbers + // eslint-disable-next-line eqeqeq .val( ( compare ? compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ) ) // ( val == o.min ? '' : val + (o.exactMatch ? '=' : '')) .trigger( notrigger ? '' : 'search', searchType ).end() @@ -237,23 +239,24 @@ // add span to header for the current slider value .find('.tablesorter-header-inner').append('<span class="curvalue" />'); // hidden filter update namespace trigger by filter widget - $input = $cell.find('input[type=hidden]').bind('change' + c.namespace + 'filter', function(){ + $input = $cell.find('input[type=hidden]').bind('change' + c.namespace + 'filter', function() { /*jshint eqeqeq:false */ var v = this.value, compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || ''; if (v !== this.lastValue) { + // eslint-disable-next-line eqeqeq this.lastValue = ( compare ? compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ); this.value = this.lastValue; updateRange( v ); } }); - $cell.find('.range').bind('change', function(){ + $cell.find('.range').bind('change', function() { updateRange( this.value ); }); // update spinner from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ + c.$table.bind('filterFomatterUpdate', function() { var val = tsff.updateCompare($cell, $input, o)[0]; $cell.find('.range').val( val ); updateRange(val, false, true); @@ -263,17 +266,17 @@ if (o.compare) { // add compare select tsff.addCompare($cell, indx, o); - $cell.find(compareSelect).bind('change', function(){ + $cell.find(compareSelect).bind('change', function() { updateRange(); }); } // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ + c.$table.bind('stickyHeadersInit', function() { $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); $shcell .html('<input class="range" type="range" min="' + o.min + '" max="' + o.max + '" value="' + o.value + '" />') - .find('.range').bind('change', function(){ + .find('.range').bind('change', function() { updateRange( $shcell.find('.range').val() ); }); updateRange(); @@ -281,7 +284,7 @@ if (o.compare) { // add compare select tsff.addCompare($shcell, indx, o); - $shcell.find(compareSelect).bind('change', function(){ + $shcell.find(compareSelect).bind('change', function() { $cell.find(compareSelect).val( $(this).val() ); updateRange(); }); @@ -290,11 +293,11 @@ }); // on reset - $cell.closest('table').bind('filterReset', function(){ + $cell.closest('table').bind('filterReset', function() { if ($.isArray(o.compare)) { $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); } - setTimeout(function(){ + setTimeout(function() { updateRange(o.value, false, true); }, 0); }); @@ -378,23 +381,23 @@ $cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="curcolor" />'); } - $cell.find('.toggle, .colorpicker').bind('change', function(){ + $cell.find('.toggle, .colorpicker').bind('change', function() { updateColor( $cell.find('.colorpicker').val() ); }); // hidden filter update namespace trigger by filter widget - $input = $cell.find('input[type=hidden]').bind('change' + c.namespace + 'filter', function(){ + $input = $cell.find('input[type=hidden]').bind('change' + c.namespace + 'filter', function() { updateColor( this.value ); }); // update slider from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate', function(){ + c.$table.bind('filterFomatterUpdate', function() { updateColor( $input.val(), true ); ts.filter.formatterUpdated($cell, indx); }); // on reset - $cell.closest('table').bind('filterReset', function(){ + $cell.closest('table').bind('filterReset', function() { // just turn off the colorpicker if (o.addToggle) { $cell.find('.toggle')[0].checked = false; @@ -402,17 +405,17 @@ // delay needed because default color needs to be set in the filter // there is no compare option here, so if addToggle = false, // default color is #000000 (even with no value set) - setTimeout(function(){ + setTimeout(function() { updateColor(); }, 0); }); // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ + c.$table.bind('stickyHeadersInit', function() { $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx); $shcell .html(t) - .find('.toggle, .colorpicker').bind('change', function(){ + .find('.toggle, .colorpicker').bind('change', function() { updateColor( $shcell.find('.colorpicker').val() ); }); updateColor( $shcell.find('.colorpicker').val() ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js index 9b933f0..6e39d63 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-jui.js @@ -9,7 +9,7 @@ */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter || {}, @@ -19,12 +19,12 @@ tsff = ts.filterFormatter = $.extend( {}, ts.filterFormatter, { - addCompare: function($cell, indx, options){ + addCompare: function($cell, indx, options) { if (options.compare && $.isArray(options.compare) && options.compare.length > 1) { var opt = '', compareSelectClass = [ compareSelect.slice(1), ' ' + compareSelect.slice(1), '' ], txt = options.cellText ? '<label class="' + compareSelectClass.join('-label') + indx + '">' + options.cellText + '</label>' : ''; - $.each(options.compare, function(i, c){ + $.each(options.compare, function(i, c) { opt += '<option ' + (options.selected === i ? 'selected' : '') + '>' + c + '</option>'; }); $cell @@ -40,7 +40,7 @@ num = val.replace(/\s*?[><=]\s*?/g, ''), compare = val.match(/[><=]/g) || ''; if (o.compare) { - if ($.isArray(o.compare)){ + if ($.isArray(o.compare)) { compare = (compare || []).join('') || o.compare[o.selected || 0]; } $cell.find(compareSelect).val( compare ); @@ -72,7 +72,7 @@ $input = $('<input class="filter" type="hidden">') .appendTo($cell) // hidden filter update namespace trigger by filter widget - .bind('change' + c.namespace + 'filter', function(){ + .bind('change' + c.namespace + 'filter', function() { updateSpinner({ value: this.value, delayed: false }); }), $shcell = [], @@ -123,7 +123,7 @@ '<label for="uispinnerbutton' + indx + '"></label></div>') .appendTo($cell) .find('.toggle') - .bind('change', function(){ + .bind('change', function() { updateSpinner(); }); } @@ -134,12 +134,12 @@ .val(o.value) .appendTo($cell) .spinner(o) - .bind('change keyup', function(){ + .bind('change keyup', function() { updateSpinner(); }); // update spinner from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate' + c.namespace + 'filter', function(){ + c.$table.bind('filterFomatterUpdate' + c.namespace + 'filter', function() { var val = tsff.updateCompare($cell, $input, o)[0]; $cell.find('.spinner').val( val ); updateSpinner({ value: val }, true); @@ -149,20 +149,20 @@ if (o.compare) { // add compare select tsff.addCompare($cell, indx, o); - $cell.find(compareSelect).bind('change', function(){ + $cell.find(compareSelect).bind('change', function() { updateSpinner(); }); } // has sticky headers? - c.$table.bind('stickyHeadersInit' + c.namespace + 'filter', function(){ + c.$table.bind('stickyHeadersInit' + c.namespace + 'filter', function() { $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); if (o.addToggle) { $('<div class="button"><input id="stickyuispinnerbutton' + indx + '" type="checkbox" class="toggle" />' + '<label for="stickyuispinnerbutton' + indx + '"></label></div>') .appendTo($shcell) .find('.toggle') - .bind('change', function(){ + .bind('change', function() { $cell.find('.toggle')[0].checked = this.checked; updateSpinner(); }); @@ -172,7 +172,7 @@ .val(o.value) .appendTo($shcell) .spinner(o) - .bind('change keyup', function(){ + .bind('change keyup', function() { $cell.find('.spinner').val( this.value ); updateSpinner(); }); @@ -180,7 +180,7 @@ if (o.compare) { // add compare select tsff.addCompare($shcell, indx, o); - $shcell.find(compareSelect).bind('change', function(){ + $shcell.find(compareSelect).bind('change', function() { $cell.find(compareSelect).val( $(this).val() ); updateSpinner(); }); @@ -189,7 +189,7 @@ }); // on reset - c.$table.bind('filterReset' + c.namespace + 'filter', function(){ + c.$table.bind('filterReset' + c.namespace + 'filter', function() { if ($.isArray(o.compare)) { $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); } @@ -198,7 +198,7 @@ $cell.find('.toggle')[0].checked = false; } $cell.find('.spinner').spinner('value', o.value); - setTimeout(function(){ + setTimeout(function() { updateSpinner(); }, 0); }); @@ -232,7 +232,7 @@ $input = $('<input class="filter" type="hidden">') .appendTo($cell) // hidden filter update namespace trigger by filter widget - .bind('change' + c.namespace + 'filter', function(){ + .bind('change' + c.namespace + 'filter', function() { updateSlider({ value: this.value }); }), $shcell = [], @@ -300,7 +300,7 @@ .slider(o); // update slider from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate' + c.namespace + 'filter', function(){ + c.$table.bind('filterFomatterUpdate' + c.namespace + 'filter', function() { var val = tsff.updateCompare($cell, $input, o)[0]; $cell.find('.slider').slider('value', val ); updateSlider({ value: val }, false); @@ -310,23 +310,23 @@ if (o.compare) { // add compare select tsff.addCompare($cell, indx, o); - $cell.find(compareSelect).bind('change', function(){ + $cell.find(compareSelect).bind('change', function() { updateSlider({ value: $cell.find('.slider').slider('value') }); }); } // on reset - c.$table.bind('filterReset' + c.namespace + 'filter', function(){ + c.$table.bind('filterReset' + c.namespace + 'filter', function() { if ($.isArray(o.compare)) { $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); } - setTimeout(function(){ + setTimeout(function() { updateSlider({ value: o.value }); }, 0); }); // has sticky headers? - c.$table.bind('stickyHeadersInit' + c.namespace + 'filter', function(){ + c.$table.bind('stickyHeadersInit' + c.namespace + 'filter', function() { $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); // add a jQuery UI slider! @@ -334,7 +334,7 @@ .val(o.value) .appendTo($shcell) .slider(o) - .bind('change keyup', function(){ + .bind('change keyup', function() { $cell.find('.slider').slider('value', this.value ); updateSlider(); }); @@ -342,7 +342,7 @@ if (o.compare) { // add compare select tsff.addCompare($shcell, indx, o); - $shcell.find(compareSelect).bind('change', function(){ + $shcell.find(compareSelect).bind('change', function() { $cell.find(compareSelect).val( $(this).val() ); updateSlider(); }); @@ -373,12 +373,12 @@ $input = $('<input class="filter" type="hidden">') .appendTo($cell) // hidden filter update namespace trigger by filter widget - .bind('change' + c.namespace + 'filter', function(){ + .bind('change' + c.namespace + 'filter', function() { getRange(); }), $shcell = [], - getRange = function(){ + getRange = function() { var val = $input.val(), v = val.split(' - '); if (val === '') { v = [ o.min, o.max ]; } @@ -449,21 +449,21 @@ .slider(o); // update slider from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate' + c.namespace + 'filter', function(){ + c.$table.bind('filterFomatterUpdate' + c.namespace + 'filter', function() { getRange(); ts.filter.formatterUpdated($cell, indx); }); // on reset - c.$table.bind('filterReset' + c.namespace + 'filter', function(){ + c.$table.bind('filterReset' + c.namespace + 'filter', function() { $cell.find('.range').slider('values', o.values); - setTimeout(function(){ + setTimeout(function() { updateUiRange(); }, 0); }); // has sticky headers? - c.$table.bind('stickyHeadersInit' + c.namespace + 'filter', function(){ + c.$table.bind('stickyHeadersInit' + c.namespace + 'filter', function() { $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); // add a jQuery UI slider! @@ -471,7 +471,7 @@ .val(o.value) .appendTo($shcell) .slider(o) - .bind('change keyup', function(){ + .bind('change keyup', function() { $cell.find('.range').val( this.value ); updateUiRange(); }); @@ -508,7 +508,7 @@ $input = $('<input class="dateCompare" type="hidden">') .appendTo($cell) // hidden filter update namespace trigger by filter widget - .bind('change' + c.namespace + 'filter', function(){ + .bind('change' + c.namespace + 'filter', function() { var v = this.value; if (v) { o.onClose(v); @@ -558,18 +558,18 @@ $date.datepicker(o); // on reset - c.$table.bind('filterReset' + c.namespace + 'filter', function(){ + c.$table.bind('filterReset' + c.namespace + 'filter', function() { if ($.isArray(o.compare)) { $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] ); } $cell.add($shcell).find('.date').val(o.defaultDate).datepicker('setDate', o.defaultDate || null); - setTimeout(function(){ + setTimeout(function() { date1Compare(); }, 0); }); // update date compare from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate' + c.namespace + 'filter', function(){ + c.$table.bind('filterFomatterUpdate' + c.namespace + 'filter', function() { var num, v = $input.val(); if (/\s+-\s+/.test(v)) { // date range found; assume an exact match on one day @@ -582,7 +582,7 @@ num = num !== '' ? /\d{5}/g.test(num) ? new Date(Number(num)) : num || '' : ''; } $cell.add($shcell).find('.date').datepicker( 'setDate', num || null ); - setTimeout(function(){ + setTimeout(function() { date1Compare(true); ts.filter.formatterUpdated($cell, indx); }, 0); @@ -591,13 +591,13 @@ if (o.compare) { // add compare select tsff.addCompare($cell, indx, o); - $cell.find(compareSelect).bind('change', function(){ + $cell.find(compareSelect).bind('change', function() { date1Compare(); }); } // has sticky headers? - c.$table.bind('stickyHeadersInit' + c.namespace + 'filter', function(){ + c.$table.bind('stickyHeadersInit' + c.namespace + 'filter', function() { $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); // add a jQuery datepicker! @@ -609,7 +609,7 @@ if (o.compare) { // add compare select tsff.addCompare($shcell, indx, o); - $shcell.find(compareSelect).bind('change', function(){ + $shcell.find(compareSelect).bind('change', function() { $cell.find(compareSelect).val( $(this).val() ); date1Compare(); }); @@ -639,14 +639,14 @@ }, defDate), t, closeDate, $shcell = [], c = $cell.closest('table')[0].config, - validDate = function(d){ + validDate = function(d) { return d instanceof Date && isFinite(d); }, // Add a hidden input to hold the range values $input = $('<input class="dateRange" type="hidden">') .appendTo($cell) // hidden filter update namespace trigger by filter widget - .bind('change' + c.namespace + 'filter', function(){ + .bind('change' + c.namespace + 'filter', function() { var v = this.value; if (v.match(' - ')) { v = v.split(' - '); @@ -708,12 +708,12 @@ $cell.find('.dateTo').datepicker(o); // update date compare from hidden input, in case of saved filters - c.$table.bind('filterFomatterUpdate' + c.namespace + 'filter', function(){ + c.$table.bind('filterFomatterUpdate' + c.namespace + 'filter', function() { var val = $input.val() || '', from = '', to = ''; // date range - if (/\s+-\s+/.test(val)){ + if (/\s+-\s+/.test(val)) { val = val.split(/\s+-\s+/) || []; from = val[0] || ''; to = val[1] || ''; @@ -732,14 +732,14 @@ $cell.add($shcell).find('.dateFrom').datepicker('setDate', from || null); $cell.add($shcell).find('.dateTo').datepicker('setDate', to || null); // give datepicker time to process - setTimeout(function(){ + setTimeout(function() { closeDate(); ts.filter.formatterUpdated($cell, indx); }, 0); }); // has sticky headers? - c.$table.bind('stickyHeadersInit' + c.namespace + 'filter', function(){ + c.$table.bind('stickyHeadersInit' + c.namespace + 'filter', function() { $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty(); $shcell.append(t); @@ -753,10 +753,10 @@ }); // on reset - $cell.closest('table').bind('filterReset' + c.namespace + 'filter', function(){ + $cell.closest('table').bind('filterReset' + c.namespace + 'filter', function() { $cell.add($shcell).find('.dateFrom').val('').datepicker('setDate', o.from || null ); $cell.add($shcell).find('.dateTo').val('').datepicker('setDate', o.to || null ); - setTimeout(function(){ + setTimeout(function() { closeDate(); }, 0); }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js index b43d00b..a910406 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js @@ -4,7 +4,7 @@ */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter || {}; @@ -33,7 +33,7 @@ $input = $('<input class="filter" type="hidden">') .appendTo($cell) // hidden filter update namespace trigger by filter widget - .bind('change' + c.namespace + 'filter', function(){ + .bind('change' + c.namespace + 'filter', function() { var val = convertRegex(this.value); c.$table.find('.select2col' + indx + ' .select2').select2('val', val); updateSelect2(); @@ -88,11 +88,11 @@ }, // get options from table cell content or filter_selectSource (v2.16) - updateOptions = function(){ + updateOptions = function() { data = []; arry = ts.filter.getOptionSource(c.$table[0], indx, onlyAvail) || []; // build select2 data option - $.each(arry, function(i, v){ + $.each(arry, function(i, v) { // getOptionSource returns { parsed: "value", text: "value" } in v2.24.4 data.push({ id: '' + v.parsed, text: v.text }); }); @@ -109,7 +109,7 @@ // data options are already defined if (!(o.ajax && !$.isEmptyObject(o.ajax)) && !o.data) { updateOptions(); - c.$table.bind('filterEnd', function(){ + c.$table.bind('filterEnd', function() { updateOptions(); c.$table .find('.select2col' + indx) @@ -123,7 +123,7 @@ .val(o.value) .appendTo($cell) .select2(o) - .bind('change', function(){ + .bind('change', function() { updateSelect2(); }); @@ -138,14 +138,14 @@ }); // has sticky headers? - c.$table.bind('stickyHeadersInit', function(){ + c.$table.bind('stickyHeadersInit', function() { var $shcell = c.widgetOptions.$sticky.find('.select2col' + indx).empty(); // add a select2! $('<input class="select2 select2-' + indx + '" type="hidden">') .val(o.value) .appendTo($shcell) .select2(o) - .bind('change', function(){ + .bind('change', function() { c.$table.find('.select2col' + indx) .find('.select2') .select2('val', c.widgetOptions.$sticky.find('.select2col' + indx + ' .select2').select2('val') ); @@ -157,9 +157,9 @@ }); // on reset - c.$table.bind('filterReset', function(){ + c.$table.bind('filterReset', function() { c.$table.find('.select2col' + indx).find('.select2').select2('val', o.value || ''); - setTimeout(function(){ + setTimeout(function() { updateSelect2(); }, 0); }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js index e412a4b..f6152a5 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-type-insideRange.js @@ -1,5 +1,5 @@ /*! Widget: filter, insideRange filter type - updated 12/10/2015 (v2.25.0) */ -;(function($){ +;(function($) { 'use strict'; // Add insideRange filter type diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index b22806a..99a8515 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -1,4 +1,4 @@ -/*! Widget: filter - updated 2018-01-30 (v2.29.5) *//* +/*! Widget: filter - updated 2018-03-18 (v2.30.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -280,6 +280,7 @@ if ( tsfRegex.exact.test( data.iFilter ) ) { var txt = data.iFilter.replace( tsfRegex.exact, '' ), filter = tsf.parseFilter( c, txt, data ) || ''; + // eslint-disable-next-line eqeqeq return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact; } return null; @@ -453,7 +454,7 @@ c.lastCombinedFilter = null; c.lastSearch = []; // update filterFormatters after update (& small delay) - Fixes #1237 - setTimeout(function(){ + setTimeout(function() { c.$table.triggerHandler( 'filterFomatterUpdate' ); }, 100); } @@ -547,7 +548,7 @@ // show processing icon if ( c.showProcessing ) { - txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter ' ); + txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter-sp ' ); c.$table .unbind( txt.replace( ts.regex.spaces, ' ' ) ) .bind( txt, function( event, columns ) { @@ -626,6 +627,9 @@ c.lastSearch = c.$table.data( 'lastSearch' ); c.$table.triggerHandler( 'filterInit', c ); tsf.findRows( c.table, c.lastSearch || [] ); + if (ts.debug(c, 'filter')) { + console.log('Filter >> Widget initialized'); + } }; if ( $.isEmptyObject( wo.filter_formatter ) ) { completed(); @@ -1326,6 +1330,7 @@ storedFilters = $.extend( [], filters ), c = table.config, wo = c.widgetOptions, + debug = ts.debug(c, 'filter'), // data object passed to filters; anyMatch is a flag for the filters data = { anyMatch: false, @@ -1342,7 +1347,6 @@ defaultColFilter : [], defaultAnyFilter : ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '' }; - // parse columns after formatter, in case the class is added at that point data.parsed = []; for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { @@ -1364,8 +1368,8 @@ ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split( /\s+/ ); } - if ( c.debug ) { - console.log( 'Filter: Starting filter widget search', filters ); + if ( debug ) { + console.log( 'Filter >> Starting filter widget search', filters ); time = new Date(); } // filtered rows count @@ -1463,8 +1467,8 @@ notFiltered = $rows.not( '.' + wo.filter_filteredRow ).length; // can't search when all rows are hidden - this happens when looking for exact matches if ( searchFiltered && notFiltered === 0 ) { searchFiltered = false; } - if ( c.debug ) { - console.log( 'Filter: Searching through ' + + if ( debug ) { + console.log( 'Filter >> Searching through ' + ( searchFiltered && notFiltered < len ? notFiltered : 'all' ) + ' rows' ); } if ( data.anyMatchFlag ) { @@ -1567,8 +1571,8 @@ if ( wo.filter_saveFilters && ts.storage ) { ts.storage( table, 'tablesorter-filters', tsf.processFilters( storedFilters, true ) ); } - if ( c.debug ) { - console.log( 'Completed filter widget search' + ts.benchmark(time) ); + if ( debug ) { + console.log( 'Filter >> Completed search' + ts.benchmark(time) ); } if ( wo.filter_initialized ) { c.$table.triggerHandler( 'filterBeforeEnd', c ); @@ -1783,13 +1787,13 @@ options += '<option'; for ( val in option ) { if ( option.hasOwnProperty( val ) && val !== 'text' ) { - options += ' ' + val + '="' + option[ val ] + '"'; + options += ' ' + val + '="' + option[ val ].replace( tsfRegex.quote, '"' ) + '"'; } } if ( !option.value ) { - options += ' value="' + option.text + '"'; + options += ' value="' + option.text.replace( tsfRegex.quote, '"' ) + '"'; } - options += '>' + option.text + '</option>'; + options += '>' + option.text.replace( tsfRegex.quote, '"' ) + '</option>'; // above code is needed in jQuery < v1.8 // make sure we don't turn an object into a string (objects without a "text" property) diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js index c3608a5..d7b224a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js @@ -4,7 +4,7 @@ */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter; @@ -33,7 +33,7 @@ formatter[ column ] = ts.getColumnData( c.table, wo.formatter_column, column ) || false; } // main loop - for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ){ + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { $tbody = ts.processTbody( c.table, c.$tbodies.eq( tbodyIndex ), true ); // detach tbody rows = c.cache[ tbodyIndex ]; len = rows.normalized.length; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js index 8e18d58..1791419 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js @@ -4,7 +4,7 @@ */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter, @@ -12,7 +12,7 @@ types : { number : function(c, $column, txt, num) { - var word, result, + var result, ascSort = $column.hasClass( ts.css.sortAsc ); if ( num > 1 && txt !== '' ) { if ( ascSort ) { @@ -43,7 +43,7 @@ letter : function(c, $column, txt, num) { return txt ? (txt + ' ').substring(0, num) : ''; }, - date : function(c, $column, txt, part, group) { + date : function(c, $column, txt, part) { var year, month, wo = c.widgetOptions, time = new Date(txt || ''); @@ -203,7 +203,7 @@ return savedGroup; }, findColumnGroups : function( c, wo, data ) { - var tbodyIndex, norm_rows, $row, rowIndex, end, undef, + var tbodyIndex, norm_rows, rowIndex, end, undef, hasPager = ts.hasWidget( c.table, 'pager' ), p = c.pager || {}; data.groupIndex = 0; @@ -251,15 +251,15 @@ } }, - bindEvents : function(table, c, wo){ + bindEvents : function(table, c, wo) { if (wo.group_collapsible) { wo.group_collapsedGroups = []; // .on() requires jQuery 1.7+ - c.$table.on('click toggleGroup keyup', 'tr.group-header', function(event){ + c.$table.on('click toggleGroup keyup', 'tr.group-header', function(event) { event.stopPropagation(); // pressing enter will toggle the group if (event.type === 'keyup' && event.which !== 13) { return; } - var isCollapsed, $groups, indx, + var isCollapsed, indx, $this = $(this), name = $this.find('.group-name').text().toLowerCase() + $this.attr('data-group-index'); // use shift-click to toggle ALL groups @@ -276,7 +276,6 @@ } // save collapsed groups if (wo.group_saveGroups && ts.storage) { - $groups = c.$table.find('.group-header'); if (!wo.group_collapsedGroups[wo.group_collapsedGroup]) { wo.group_collapsedGroups[wo.group_collapsedGroup] = []; } @@ -292,15 +291,15 @@ } }); } - $(wo.group_saveReset).on('click', function(){ + $(wo.group_saveReset).on('click', function() { tsg.clearSavedGroups(table); }); - c.$table.on('pagerChange.tsgrouping', function(){ + c.$table.on('pagerChange.tsgrouping', function() { tsg.update(table); }); }, - clearSavedGroups: function(table){ + clearSavedGroups: function(table) { if (table && ts.storage) { ts.storage(table, 'tablesorter-groups', ''); tsg.update(table); @@ -320,7 +319,7 @@ group_count : ' ({num})', // if not false, the '{num}' string is replaced with the number of rows in the group group_separator : '-', // group name separator; used when group-separator-# class is used. group_formatter : null, // function(txt, column, table, c, wo) { return txt; } - group_callback : null, // function($cell, $rows, column, table){}, callback allowing modification of the group header labels + group_callback : null, // function($cell, $rows, column, table) {}, callback allowing modification of the group header labels group_complete : 'groupingComplete', // event triggered on the table when the grouping widget has finished work // apply the grouping widget only to selected column @@ -344,13 +343,13 @@ // reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#Conversion_getter group_dateString : function(date) { return date.toLocaleString(); } }, - init: function(table, thisWidget, c, wo){ + init: function(table, thisWidget, c, wo) { tsg.bindEvents(table, c, wo); }, - format: function(table, c, wo) { + format: function(table) { tsg.update(table); }, - remove : function(table, c, wo){ + remove : function(table, c) { c.$table .off('click', 'tr.group-header') .off('pagerChange.tsgrouping') diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js index 599086f..fabe370 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-headerTitles.js @@ -4,7 +4,7 @@ */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter; @@ -35,14 +35,14 @@ // manipulate the title as desired headerTitle_callback : null // function($cell, txt) { return txt; } }, - init: function(table, thisWidget, c, wo){ + init: function(table, thisWidget, c, wo) { // force refresh - c.$table.on('refreshHeaderTitle', function(){ + c.$table.on('refreshHeaderTitle', function() { thisWidget.format(table, c, wo); }); // add tooltip class if ($.isArray(wo.headerTitle_tooltip)) { - c.$headers.each(function(){ + c.$headers.each(function() { $(this).addClass( wo.headerTitle_tooltip[this.column] || '' ); }); } else if (wo.headerTitle_tooltip !== '') { @@ -51,9 +51,8 @@ }, format: function (table, c, wo) { var txt; - c.$headers.each(function(){ - var t = this, - $this = $(this), + c.$headers.each(function() { + var $this = $(this), col = parseInt( $this.attr( 'data-column' ), 10 ), sortType = wo.headerTitle_type[ col ] || c.parsers[ col ].type || 'text', sortDirection = $this.hasClass(ts.css.sortAsc) ? 0 : $this.hasClass(ts.css.sortDesc) ? 1 : 2, @@ -64,7 +63,7 @@ txt = (wo.headerTitle_prefix || '') + // now deprecated ($this.hasClass('sorter-false') ? wo.headerTitle_output_nosort : ts.isValueInArray( col, c.sortList ) >= 0 ? wo.headerTitle_output_sorted : wo.headerTitle_output_unsorted); - txt = txt.replace(/\{(current|next|name)\}/gi, function(m){ + txt = txt.replace(/\{(current|next|name)\}/gi, function(m) { return { '{name}' : $this.text(), '{current}' : wo[ 'headerTitle_cur_' + sortType ][ sortDirection ] || '', @@ -80,7 +79,7 @@ c.$table.off('refreshHeaderTitle'); // remove tooltip class if ($.isArray(wo.headerTitle_tooltip)) { - c.$headers.each(function(){ + c.$headers.each(function() { $(this).removeClass( wo.headerTitle_tooltip[this.column] || '' ); }); } else if (wo.headerTitle_tooltip !== '') { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-lazyload.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-lazyload.js index e9b4b6c..ba83d24 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-lazyload.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-lazyload.js @@ -51,7 +51,7 @@ $(window).scroll(); }, 1); }, - remove : function( c, wo ) { + remove : function( c ) { c.$table.off( c.namespace + 'lazyload' ); } }; @@ -200,26 +200,26 @@ var settings = { threshold : 0, failure_limit : 0, - event : "scroll", - effect : "show", + event : 'scroll', + effect : 'show', container : window, - data_attribute : "original", + data_attribute : 'original', skip_invisible : false, appear : null, load : null, - placeholder : "data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" + placeholder : 'data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' }; function update() { var counter = 0; elements.each(function() { var $this = $(this); - if (settings.skip_invisible && !$this.is(":visible")) { + if (settings.skip_invisible && !$this.is(':visible')) { return; } if ($.abovethetop(this, settings) || $.leftofbegin(this, settings)) { /* Nothing. */ } else if (!$.belowthefold(this, settings) && !$.rightoffold(this, settings)) { - $this.trigger("appear"); + $this.trigger('appear'); /* if we found an image we'll load, reset the counter */ counter = 0; } else { @@ -245,7 +245,7 @@ $container = (settings.container === undefined || settings.container === window) ? $window : $(settings.container); /* Fire one scroll event per scroll. Not one scroll event per image. */ - if (0 === settings.event.indexOf("scroll")) { + if (0 === settings.event.indexOf('scroll')) { $container.bind(settings.event, function() { return update(); }); @@ -255,26 +255,26 @@ var $self = $(self); self.loaded = false; /* If no src attribute given use data:uri. */ - if ($self.attr("src") === undefined || $self.attr("src") === false) { - if ($self.is("img")) { - $self.attr("src", settings.placeholder); + if ($self.attr('src') === undefined || $self.attr('src') === false) { + if ($self.is('img')) { + $self.attr('src', settings.placeholder); } } /* When appear is triggered load original image. */ - $self.one("appear", function() { + $self.one('appear', function() { if (!this.loaded) { if (settings.appear) { var elements_left = elements.length; settings.appear.call(self, elements_left, settings); } - $("<img />") - .bind("load", function() { - var original = $self.attr("data-" + settings.data_attribute); + $('<img />') + .bind('load', function() { + var original = $self.attr('data-' + settings.data_attribute); $self.hide(); - if ($self.is("img")) { - $self.attr("src", original); + if ($self.is('img')) { + $self.attr('src', original); } else { - $self.css("background-image", "url('" + original + "')"); + $self.css('background-image', 'url("' + original + '")'); } $self[settings.effect](settings.effect_speed); self.loaded = true; @@ -288,30 +288,30 @@ settings.load.call(self, elements_left, settings); } }) - .attr("src", $self.attr("data-" + settings.data_attribute)); + .attr('src', $self.attr('data-' + settings.data_attribute)); } }); /* When wanted event is triggered load original image */ /* by triggering appear. */ - if (0 !== settings.event.indexOf("scroll")) { + if (0 !== settings.event.indexOf('scroll')) { $self.bind(settings.event, function() { if (!self.loaded) { - $self.trigger("appear"); + $self.trigger('appear'); } }); } }); /* Check if something appears when window is resized. */ - $window.bind("resize", function() { + $window.bind('resize', function() { update(); }); /* With IOS5 force loading images when navigating with back button. */ /* Non optimal workaround. */ if ((/(?:iphone|ipod|ipad).*os 5/gi).test(navigator.appVersion)) { - $window.bind("pageshow", function(event) { + $window.bind('pageshow', function(event) { if (event.originalEvent && event.originalEvent.persisted) { elements.each(function() { - $(this).trigger("appear"); + $(this).trigger('appear'); }); } }); @@ -365,17 +365,17 @@ !$.belowthefold(element, settings) && !$.abovethetop(element, settings); }; /* Custom selectors for your convenience. */ - /* Use as $("img:below-the-fold").something() or */ - /* $("img").filter(":below-the-fold").something() which is faster */ - $.extend($.expr[":"], { - "below-the-fold" : function(a) { return $.belowthefold(a, {threshold : 0}); }, - "above-the-top" : function(a) { return !$.belowthefold(a, {threshold : 0}); }, - "right-of-screen": function(a) { return $.rightoffold(a, {threshold : 0}); }, - "left-of-screen" : function(a) { return !$.rightoffold(a, {threshold : 0}); }, - "in-viewport" : function(a) { return $.inviewport(a, {threshold : 0}); }, + /* Use as $('img:below-the-fold').something() or */ + /* $('img').filter(':below-the-fold').something() which is faster */ + $.extend($.expr[':'], { + 'below-the-fold' : function(a) { return $.belowthefold(a, {threshold : 0}); }, + 'above-the-top' : function(a) { return !$.belowthefold(a, {threshold : 0}); }, + 'right-of-screen': function(a) { return $.rightoffold(a, {threshold : 0}); }, + 'left-of-screen' : function(a) { return !$.rightoffold(a, {threshold : 0}); }, + 'in-viewport' : function(a) { return $.inviewport(a, {threshold : 0}); }, /* Maintain BC for couple of versions. */ - "above-the-fold" : function(a) { return !$.belowthefold(a, {threshold : 0}); }, - "right-of-fold" : function(a) { return $.rightoffold(a, {threshold : 0}); }, - "left-of-fold" : function(a) { return !$.rightoffold(a, {threshold : 0}); } + 'above-the-fold' : function(a) { return !$.belowthefold(a, {threshold : 0}); }, + 'right-of-fold' : function(a) { return $.rightoffold(a, {threshold : 0}); }, + 'left-of-fold' : function(a) { return !$.rightoffold(a, {threshold : 0}); } }); })(jQuery, window, document); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index ccafa32..38f6fa5 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -171,7 +171,6 @@ if ( hasFilter || !$row.hasClass( filtered ) ) { $cells = $row.children().not( mathIgnore ); cellLen = $cells.length; - // $row.children().each(function(){ for ( cellIndex = 0; cellIndex < cellLen; cellIndex++ ) { $t = $cells.eq( cellIndex ); col = math.getCellIndex( $t ); @@ -221,7 +220,7 @@ recalculate : function(c, wo, init) { if ( c && ( !wo.math_isUpdating || init ) ) { - var undef, time, mathAttr, $mathCells, indx, len, + var time, mathAttr, $mathCells, indx, len, changed = false, filters = {}; if ( c.debug || wo.math_debug ) { @@ -435,7 +434,7 @@ mask = mask.substring( start, index ); // convert any string to number according to formation sign. - val = mask.charAt( 0 ) == '-' ? -val : +val; + val = mask.charAt( 0 ) === '-' ? -val : +val; isNegative = val < 0 ? val = -val : 0; // process only abs(), and turn on flag. // search for separator for grp & decimal, anything not digit, not +/- sign, not #. @@ -517,7 +516,7 @@ len = arry.length; if ( len > 1 ) { // https://gist.github.com/caseyjustus/1166258 - arry.sort( function( a, b ){ return a - b; } ); + arry.sort( function( a, b ) { return a - b; } ); half = Math.floor( len / 2 ); return ( len % 2 ) ? arry[ half ] : ( arry[ half - 1 ] + arry[ half ] ) / 2; } @@ -542,7 +541,7 @@ } } // returns arry of modes if there is a tie - return modes.sort( function( a, b ){ return a - b; } ); + return modes.sort( function( a, b ) { return a - b; } ); }, max : function(arry) { return Math.max.apply( Math, arry ); @@ -551,7 +550,7 @@ return Math.min.apply( Math, arry ); }, range: function(arry) { - var v = arry.sort( function( a, b ){ return a - b; } ); + var v = arry.sort( function( a, b ) { return a - b; } ); return v[ arry.length - 1 ] - v[ 0 ]; }, // common variance equation @@ -604,9 +603,9 @@ // mask info: https://code.google.com/p/javascript-number-formatter/ math_mask : '#,##0.00', // complete executed after each fucntion - math_complete : null, // function($cell, wo, result, value, arry){ return result; }, + math_complete : null, // function($cell, wo, result, value, arry) { return result; }, // math_completed called after all math calculations have completed - math_completed: function( config ) {}, + math_completed: function( /* config */ ) {}, // order of calculation; 'all' is last math_priority : [ 'row', 'above', 'below', 'col' ], // template for or just prepend the mask prefix & suffix with this HTML @@ -639,7 +638,7 @@ } }) .on( update, function() { - setTimeout( function(){ + setTimeout( function() { math.updateComplete( c ); }, 40 ); }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index 53bc110..f9d1215 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -7,7 +7,7 @@ */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery:false, alert:false */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter, @@ -164,11 +164,12 @@ headers = output.processRow(c, $this, true, outputJSON); // all tbody rows - do not include widget added rows (e.g. grouping widget headers) - if ( !$rows ) + if ( !$rows ) { $rows = $el.children('tbody').children('tr').not(c.selectorRemove); + } // check for a filter callback function first! because - // /^f/.test(function(){ console.log('test'); }) is TRUE! (function is converted to a string) + // /^f/.test(function() { console.log('test'); }) is TRUE! (function is converted to a string) $rows = typeof saveRows === 'function' ? $rows.filter(saveRows) : // get (f)iltered, (v)isible, all rows (look for the first letter only), or jQuery filter selector /^f/.test(saveRows) ? $rows.not('.' + (wo.filter_filteredRow || 'filtered') ) : @@ -410,11 +411,11 @@ output_popupStyle : 'width=500,height=300', output_saveFileName : 'mytable.csv', // format $cell content callback - output_formatContent : null, // function(config, widgetOptions, data){ return data.content; } + output_formatContent : null, // function(config, widgetOptions, data) { return data.content; } // callback executed when processing completes // return true to continue download/output // return false to stop delivery & do something else with the data - output_callback : function(config, data){ return true; }, + output_callback : function(/* config, data */) { return true; }, // JSON callback executed when a colspan is encountered in the header output_callbackJSON : function($cell, txt, cellIndex) { return txt + '(' + (cellIndex) + ')'; }, // the need to modify this for Excel no longer exists @@ -429,7 +430,7 @@ init: function(table, thisWidget, c) { output.init(c); }, - remove: function(table, c){ + remove: function(table, c) { output.remove(c); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index d2ccf0f..302d451 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,9 +1,9 @@ -/*! Widget: Pager - updated 5/24/2017 (v2.28.11) */ +/*! Widget: Pager - updated 2018-03-19 (v2.30.1) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ /*jshint browser:true, jquery:true, unused:false */ -;(function($){ +;(function($) { 'use strict'; var tsp, ts = $.tablesorter; @@ -66,7 +66,7 @@ pager_customAjaxUrl: function( table, url ) { return url; }, // ajax error callback from $.tablesorter.showError function - // pager_ajaxError: function( config, xhr, settings, exception ){ return exception; }; + // pager_ajaxError: function( config, xhr, settings, exception ) { return exception; }; // returning false will abort the error message pager_ajaxError: null, @@ -91,7 +91,7 @@ // ], // [ "header1", "header2", ... "headerN" ] // optional // ] - pager_ajaxProcessing: function( ajax ){ return [ 0, [], null ]; }, + pager_ajaxProcessing: function( /* ajax */ ) { return [ 0, [], null ]; }, // css class names of pager arrows pager_css: { @@ -158,12 +158,15 @@ setPage: wo.pager_startPage }, c.pager ); + // Used by core appendCache; !undefined is always true + p.removeRows = wo.pager_removeRows; + // pager initializes multiple times before table has completed initialization if ( p.isInitializing ) { return; } p.isInitializing = true; - if ( c.debug ) { - console.log( 'Pager: Initializing' ); + if ( ts.debug(c, 'pager') ) { + console.log( 'Pager >> Initializing' ); } p.size = $.data( table, 'pagerLastSize' ) || wo.pager_size; @@ -227,8 +230,8 @@ p.initializing = false; p.isInitializing = false; tsp.setPageSize( c, p.size ); // page size 0 is ignored - if ( c.debug ) { - console.log( 'Pager: Triggering pagerInitialized' ); + if ( ts.debug(c, 'pager') ) { + console.log( 'Pager >> Triggering pagerInitialized' ); } c.$table.triggerHandler( 'pagerInitialized', c ); // filter widget not initialized; it will update the output display & fire off the pagerComplete event @@ -243,7 +246,8 @@ p = c.pager, wo = c.widgetOptions, namespace = c.namespace + 'pager', - s = wo.pager_selectors; + s = wo.pager_selectors, + debug = ts.debug(c, 'pager'); c.$table .off( namespace ) .on( 'filterInit filterStart '.split( ' ' ).join( namespace + ' ' ), function( e, filters ) { @@ -284,7 +288,7 @@ e.stopPropagation(); tsp.enablePager( c, true ); }) - .on( 'destroyPager' + namespace, function( e, refreshing ) { + .on( 'destroyPager' + namespace, function( e ) { e.stopPropagation(); // call removeWidget to make sure internal flags are modified. ts.removeWidget( c.table, 'pager', false ); @@ -343,8 +347,8 @@ // clicked controls ctrls = [ s.first, s.prev, s.next, s.last ]; fxn = [ 'moveToFirstPage', 'moveToPrevPage', 'moveToNextPage', 'moveToLastPage' ]; - if ( c.debug && !p.$container.length ) { - console.warn( 'Pager: >> Container not found' ); + if ( debug && !p.$container.length ) { + console.warn( 'Pager >> "container" not found' ); } p.$container.find( ctrls.join( ',' ) ) .attr( 'tabindex', 0 ) @@ -373,8 +377,8 @@ tsp.moveToPage( c, p, true ); tsp.updatePageDisplay( c, false ); }); - } else if ( c.debug ) { - console.warn( 'Pager: >> Goto selector not found' ); + } else if ( debug ) { + console.warn( 'Pager >> "goto" selector not found' ); } tmp = p.$container.find( wo.pager_selectors.pageSize ); @@ -394,8 +398,8 @@ } return false; }); - } else if ( c.debug ) { - console.warn('Pager: >> Size selector not found'); + } else if ( debug ) { + console.warn('Pager >> "size" selector not found'); } }, @@ -532,8 +536,8 @@ tsp.pagerArrows( c ); tsp.fixHeight( c ); if ( p.initialized && completed !== false ) { - if ( c.debug ) { - console.log( 'Pager: Triggering pagerComplete' ); + if ( ts.debug(c, 'pager') ) { + console.log( 'Pager >> Triggering pagerComplete' ); } c.$table.triggerHandler( 'pagerComplete', c ); // save pager info to storage @@ -655,7 +659,6 @@ hideRows: function( c ) { if ( !c.widgetOptions.pager_ajaxUrl ) { var tbodyIndex, rowIndex, $rows, len, lastIndex, - table = c.table, p = c.pager, wo = c.widgetOptions, tbodyLen = c.$tbodies.length, @@ -721,7 +724,8 @@ renderAjax: function( data, c, xhr, settings, exception ) { var table = c.table, p = c.pager, - wo = c.widgetOptions; + wo = c.widgetOptions, + debug = ts.debug(c, 'pager'); // process data if ( $.isFunction( wo.pager_ajaxProcessing ) ) { @@ -734,15 +738,14 @@ var i, j, t, hsh, $f, $sh, $headers, $h, icon, th, d, l, rr_count, len, sz, $table = c.$table, tds = '', - result = wo.pager_ajaxProcessing( data, table, xhr ) || [ 0, [] ], - hl = $table.find( 'thead th' ).length; + result = wo.pager_ajaxProcessing( data, table, xhr ) || [ 0, [] ]; // Clean up any previous error. ts.showError( table ); if ( exception ) { - if ( c.debug ) { - console.error( 'Pager: >> Ajax Error', xhr, settings, exception ); + if ( debug ) { + console.error( 'Pager >> Ajax Error', xhr, settings, exception ); } ts.showError( table, xhr, settings, exception ); c.$tbodies.eq( 0 ).children( 'tr' ).detach(); @@ -846,8 +849,8 @@ // apply widgets after table has rendered & after a delay to prevent // multiple applyWidget blocking code from blocking this trigger setTimeout( function() { - if ( c.debug ) { - console.log( 'Pager: Triggering pagerChange' ); + if ( debug ) { + console.log( 'Pager >> Triggering pagerChange' ); } $table.triggerHandler( 'pagerChange', p ); ts.applyWidget( table ); @@ -889,8 +892,8 @@ p.oldAjaxSuccess( data ); } }; - if ( c.debug ) { - console.log( 'Pager: Ajax initialized', p.ajaxObject ); + if ( ts.debug(c, 'pager') ) { + console.log( 'Pager >> Ajax initialized', p.ajaxObject ); } $.ajax( p.ajaxObject ); } @@ -935,8 +938,8 @@ if ( $.isFunction( wo.pager_customAjaxUrl ) ) { url = wo.pager_customAjaxUrl( c.table, url ); } - if ( c.debug ) { - console.log( 'Pager: Ajax url = ' + url ); + if ( ts.debug(c, 'pager') ) { + console.log( 'Pager >> Ajax url = ' + url ); } return url; }, @@ -946,13 +949,14 @@ table = c.table, p = c.pager, wo = c.widgetOptions, + debug = ts.debug(c, 'pager'), f = c.$table.hasClass('hasFilters'), l = rows && rows.length || 0, // rows may be undefined e = p.size === 'all' ? p.totalRows : p.size, s = ( p.page * e ); if ( l < 1 ) { - if ( c.debug ) { - console.warn( 'Pager: >> No rows for pager to render' ); + if ( debug ) { + console.warn( 'Pager >> No rows for pager to render' ); } // empty table, abort! return; @@ -964,8 +968,8 @@ p.cacheIndex = []; p.isDisabled = false; // needed because sorting will change the page and re-enable the pager if ( p.initialized ) { - if ( c.debug ) { - console.log( 'Pager: Triggering pagerChange' ); + if ( debug ) { + console.log( 'Pager >> Triggering pagerChange' ); } c.$table.triggerHandler( 'pagerChange', c ); } @@ -997,8 +1001,8 @@ wo.pager_startPage = p.page; wo.pager_size = p.size; if ( table.isUpdating ) { - if ( c.debug ) { - console.log( 'Pager: Triggering updateComplete' ); + if ( debug ) { + console.log( 'Pager >> Triggering updateComplete' ); } c.$table.triggerHandler( 'updateComplete', [ table, true ] ); } @@ -1016,7 +1020,7 @@ $.data( table, 'pagerLastPage', p.page ); $.data( table, 'pagerLastSize', p.size ); p.page = 0; - p.size = p.totalPages; + p.size = p.totalRows; p.totalPages = 1; c.$table .addClass( 'pagerDisabled' ) @@ -1026,8 +1030,8 @@ tsp.renderTable( c, c.rowsCopy ); p.isDisabled = true; ts.applyWidget( table ); - if ( c.debug ) { - console.log( 'Pager: Disabled' ); + if ( ts.debug(c, 'pager') ) { + console.log( 'Pager >> Disabled' ); } } // disable size selector @@ -1074,14 +1078,14 @@ var tmp, table = c.table, wo = c.widgetOptions, - l = p.last; + l = p.last, + debug = ts.debug(c, 'pager'); // abort page move if the table has filters and has not been initialized if ( p.ajax && !wo.filter_initialized && ts.hasWidget( table, 'filter' ) ) { return; } tsp.parsePageNumber( c, p ); tsp.calcFilters( c ); - // fixes issue where one current filter is [] and the other is [ '', '', '' ], // making the next if comparison think the filters as different. Fixes #202. l.currentFilters = ( l.currentFilters || [] ).join( '' ) === '' ? [] : l.currentFilters; @@ -1096,8 +1100,8 @@ l.sortList === ( c.sortList || [] ).join( ',' ) ) { return; } - if ( c.debug ) { - console.log( 'Pager: Changing to page ' + p.page ); + if ( debug ) { + console.log( 'Pager >> Changing to page ' + p.page ); } p.last = { page: p.page, @@ -1114,9 +1118,9 @@ wo.pager_processAjaxOnInit = true; tmp = wo.pager_initialRows; p.totalRows = typeof tmp.total !== 'undefined' ? tmp.total : - ( c.debug ? console.error('Pager: no initial total page set!') || 0 : 0 ); + ( debug ? console.error('Pager >> No initial total page set!') || 0 : 0 ); p.filteredRows = typeof tmp.filtered !== 'undefined' ? tmp.filtered : - ( c.debug ? console.error('Pager: no initial filtered page set!') || 0 : 0 ); + ( debug ? console.error('Pager >> No initial filtered page set!') || 0 : 0 ); tsp.updatePageDisplay( c, false ); } else { tsp.getAjax( c ); @@ -1126,14 +1130,14 @@ } $.data( table, 'pagerLastPage', p.page ); if ( p.initialized && pageMoved !== false ) { - if ( c.debug ) { - console.log( 'Pager: Triggering pageMoved' ); + if ( debug ) { + console.log( 'Pager >> Triggering pageMoved' ); } c.$table.triggerHandler( 'pageMoved', c ); ts.applyWidget( table ); if ( !p.ajax && table.isUpdating ) { - if ( c.debug ) { - console.log( 'Pager: Triggering updateComplete' ); + if ( debug ) { + console.log( 'Pager >> Triggering updateComplete' ); } c.$table.triggerHandler( 'updateComplete', [ table, true ] ); } @@ -1269,8 +1273,8 @@ tsp.setPageSize( c, p.size ); tsp.moveToPage( c, p, true ); tsp.hideRowsSetup( c ); - if ( c.debug ) { - console.log( 'Pager: Enabled' ); + if ( ts.debug(c, 'pager') ) { + console.log( 'Pager >> Enabled' ); } } }, @@ -1296,8 +1300,7 @@ // see #486 ts.showError = function( table, xhr, settings, exception ) { - var $row, - $table = $( table ), + var $table = $( table ), c = $table[ 0 ].config, wo = c && c.widgetOptions, errorRow = c.pager && c.pager.cssErrorRow || @@ -1352,9 +1355,10 @@ } // allow message to include entire row HTML! - $row = ( /tr\>/.test( message ) ? - $( message ) : - $( '<tr><td colspan="' + c.columns + '">' + message + '</td></tr>' ) ) + $( /tr\>/.test( message ) ? + message : + '<tr><td colspan="' + c.columns + '">' + message + '</td></tr>' + ) .click( function() { $( this ).remove(); }) diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js index 64c9f17..1bdf3ad 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-print.js @@ -46,7 +46,7 @@ // Make sure all lazy loaded images are visible - see #1169 data = 'data-' + (wo.lazyload_data_attribute || 'original'); - $table.find('img[' + data + ']').each(function(){ + $table.find('img[' + data + ']').each(function() { $this = $(this); $this.attr('src', $this.attr(data)); }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-reflow.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-reflow.js index 781e20a..4a32f34 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-reflow.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-reflow.js @@ -49,7 +49,7 @@ */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter, @@ -68,15 +68,15 @@ .off('refresh.tsreflow updateComplete.tsreflow2') // emulate jQuery Mobile refresh // https://api.jquerymobile.com/table-reflow/#method-refresh - .on('refresh.tsreflow updateComplete.tsreflow2', function(){ + .on('refresh.tsreflow updateComplete.tsreflow2', function() { tablereflow.init(table, c, wo); }); - c.$headers.each(function(){ + c.$headers.each(function() { $this = $(this); headers.push( $.trim( $this.attr(header) || $this.text() ) ); }); - c.$tbodies.children().each(function(){ - $(this).children().each(function(i){ + c.$tbodies.children().each(function() { + $(this).children().each(function(i) { $(this).attr(title, headers[i]); }); }); @@ -91,7 +91,7 @@ .off('refresh.tsreflow2 updateComplete.tsreflow2') // emulate jQuery Mobile refresh // https://api.jquerymobile.com/table-reflow/#method-refresh - .on('refresh.tsreflow2 updateComplete.tsreflow2', function(){ + .on('refresh.tsreflow2 updateComplete.tsreflow2', function() { tablereflow.init2(table, c, wo); }); @@ -101,7 +101,7 @@ if ($hdr.length > 1) { txt = []; /*jshint loopfunc:true */ - $hdr.each(function(){ + $hdr.each(function() { $this = $(this); if (!$this.hasClass(wo.reflow2_classIgnore)) { txt.push( $this.attr(header) || $this.text() ); @@ -114,9 +114,9 @@ } // include 'remove-me' class so these additional elements are removed before updating txt = '<b class="' + c.selectorRemove.slice(1) + ' ' + wo.reflow2_labelClass; - c.$tbodies.children().each(function(){ + c.$tbodies.children().each(function() { $tbody = ts.processTbody(table, $(this), true); - $tbody.children().each(function(j){ + $tbody.children().each(function(j) { $this = $(this); len = headers[j].length; i = len - 1; @@ -149,7 +149,7 @@ init: function(table, thisWidget, c, wo) { tablereflow.init(table, c, wo); }, - remove: function(table, c, wo){ + remove: function(table, c, wo) { tablereflow.remove(table, c, wo); } }); @@ -171,7 +171,7 @@ init: function(table, thisWidget, c, wo) { tablereflow.init2(table, c, wo); }, - remove: function(table, c, wo){ + remove: function(table, c, wo) { tablereflow.remove2(table, c, wo); } }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js index 68a0d7e..81e7844 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js @@ -3,7 +3,7 @@ * Original by Christian Bach from the example-widgets.html demo */ /*global jQuery: false */ -;(function($){ +;(function($) { 'use strict'; $.tablesorter.addWidget({ @@ -42,7 +42,7 @@ }, // this remove function is called when using the refreshWidgets method or when destroying the tablesorter plugin // this function only applies to tablesorter v2.4+ - remove: function(table, c, wo){ + remove: function(table, c, wo) { wo.repeatHeaders = ''; c.$table.find('tr.repeated-header').remove(); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js index 190d94c..5bcb55e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js @@ -12,7 +12,7 @@ }); // Add extra scroller css - $(function(){ + $(function() { var s = '<style>' + 'body.' + ts.css.resizableNoSelect + ' { -ms-user-select: none; -moz-user-select: -moz-none;' + '-khtml-user-select: none; -webkit-user-select: none; user-select: none; }' + @@ -157,7 +157,7 @@ if ( ts.hasWidget( c.table, 'scroller' ) ) { tableHeight = 0; - c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function(){ + c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function() { var $this = $(this); // center table has a max-height set tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); @@ -379,7 +379,7 @@ }); ts.resizableReset = function( table, refreshing ) { - $( table ).each(function(){ + $( table ).each(function() { var index, $t, c = this.config, wo = c && c.widgetOptions, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js index 1278345..5a71627 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js @@ -1,4 +1,4 @@ -/*! Widget: saveSort - updated 10/31/2015 (v2.24.0) *//* +/*! Widget: saveSort - updated 2018-03-19 (v2.30.1) *//* * Requires tablesorter v2.16+ * by Rob Garrison */ @@ -6,6 +6,15 @@ 'use strict'; var ts = $.tablesorter || {}; + function getStoredSortList(c) { + var stored = ts.storage( c.table, 'tablesorter-savesort' ); + return (stored && stored.hasOwnProperty('sortList') && $.isArray(stored.sortList)) ? stored.sortList : []; + } + + function sortListChanged(c, sortList) { + return (sortList || getStoredSortList(c)).join(',') !== c.sortList.join(','); + } + // this widget saves the last sort only if the // saveSort widget option is true AND the // $.tablesorter.storage function is included @@ -21,18 +30,19 @@ thisWidget.format(table, c, wo, true); }, format: function(table, c, wo, init) { - var stored, time, + var time, $table = c.$table, saveSort = wo.saveSort !== false, // make saveSort active/inactive; default to true - sortList = { 'sortList' : c.sortList }; - if (c.debug) { + sortList = { 'sortList' : c.sortList }, + debug = ts.debug(c, 'saveSort'); + if (debug) { time = new Date(); } if ($table.hasClass('hasSaveSort')) { - if (saveSort && table.hasInitialized && ts.storage) { + if (saveSort && table.hasInitialized && ts.storage && sortListChanged(c)) { ts.storage( table, 'tablesorter-savesort', sortList ); - if (c.debug) { - console.log('saveSort widget: Saving last sort: ' + c.sortList + ts.benchmark(time)); + if (debug) { + console.log('saveSort >> Saving last sort: ' + c.sortList + ts.benchmark(time)); } } } else { @@ -41,10 +51,9 @@ sortList = ''; // get data if (ts.storage) { - stored = ts.storage( table, 'tablesorter-savesort' ); - sortList = (stored && stored.hasOwnProperty('sortList') && $.isArray(stored.sortList)) ? stored.sortList : ''; - if (c.debug) { - console.log('saveSort: Last sort loaded: "' + sortList + '"' + ts.benchmark(time)); + sortList = getStoredSortList(c); + if (debug) { + console.log('saveSort >> Last sort loaded: "' + sortList + '"' + ts.benchmark(time)); } $table.bind('saveSortReset', function(event) { event.stopPropagation(); @@ -57,7 +66,9 @@ c.sortList = sortList; } else if (table.hasInitialized && sortList && sortList.length > 0) { // update sort change - ts.sortOn( c, sortList ); + if (sortListChanged(c, sortList)) { + ts.sortOn(c, sortList); + } } } }, diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index 9e3d3aa..09d2527 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -1,4 +1,4 @@ -/*! Widget: scroller - updated 12/13/2017 (v2.29.1) *//* +/*! Widget: scroller - updated 2018-02-25 (v2.29.6) *//* Copyright (C) 2011 T. Connell & Associates, Inc. Dual-licensed under the MIT and GPL licenses @@ -188,7 +188,7 @@ }, setup : function( c, wo ) { - var tbHt, $hdr, $t, $hCells, $fCells, $tableWrap, events, tmp, detectedWidth, + var tbHt, $hdr, $t, $hCells, $tableWrap, events, tmp, detectedWidth, $win = $( window ), tsScroller = ts.scroller, namespace = c.namespace + 'tsscroller', @@ -228,7 +228,6 @@ // maintain any bindings on the tfoot cells .append( $t.clone( true ) ) .wrap( '<div class="' + tscss.scrollerFooter + '"/>' ); - $fCells = $foot.children( 'tfoot' ).eq( 0 ).children( 'tr' ).children(); } wo.scroller_$footer = $foot; @@ -361,7 +360,7 @@ // updateAll called - need to give the browser time to adjust the layout // before calculating fix column widths if ( c.table.hasInitialized && c.isScrolling ) { - setTimeout(function(){ + setTimeout(function() { ts.scroller.resize( c, wo ); }, 50); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js index b8501b4..303b8cb 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sort2Hash.js @@ -25,7 +25,7 @@ if ( filter ) { filter = filter.split( wo.sort2Hash_separator ); c.$table.one( 'tablesorter-ready', function() { - setTimeout(function(){ + setTimeout(function() { c.$table.one( 'filterEnd', function() { $(this).triggerHandler( 'pageAndSize', [ page, size ] ); }); @@ -237,7 +237,7 @@ var baseUrl = window.location.href.split(hashChar)[0]; // Ensure that there is a leading hash character var firstChar = newHash[0]; - if (firstChar != hashChar) { + if (firstChar !== hashChar) { newHash = hashChar + newHash; } // Update URL in browser diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js index 8bd3cfc..eba047e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js @@ -230,7 +230,7 @@ init : function( table, thisWidget, c, wo ) { ts.sortTbodies.init( c, wo ); }, - remove : function( table, c, wo, refreshing ) { + remove : function( table, c, wo ) { c.$table.unbind( 'sortBegin updateComplete '.split( ' ' ).join( c.namespace + 'sortTbody ' ) ); c.serverSideSorting = wo.sortTbody_original_serverSideSorting; c.cssInfoBlock = wo.sortTbody_original_cssInfoBlock; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js index a68abd3..ff8411e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js @@ -11,19 +11,19 @@ */ /*jshint browser:true, jquery:true, unused:false */ /*global jQuery: false */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter, // add/refresh row indexes - addIndexes = function(table){ + addIndexes = function(table) { var $tr, wo, v, indx, rows, c = table.config; // 'Index' the static rows, saving their current (starting) position in the // table inside a data() param on the <tr> element itself for later use. if (c) { wo = c.widgetOptions; - c.$tbodies.each(function(){ + c.$tbodies.each(function() { $tr = $(this).children(); rows = $tr.length; $tr.filter(wo.staticRow_class).each(function() { @@ -55,12 +55,12 @@ staticRow_event : 'staticRowsRefresh' }, - init: function(table, thisWidget, c, wo){ + init: function(table, thisWidget, c, wo) { addIndexes(table); // refresh static rows after updates c.$table .unbind( ('updateComplete.tsstaticrows ' + wo.staticRow_event).replace(/\s+/g, ' ') ) - .bind('updateComplete.tsstaticrows ' + wo.staticRow_event, function(){ + .bind('updateComplete.tsstaticrows ' + wo.staticRow_event, function() { addIndexes(table); ts.applyWidget( table ); }); @@ -71,7 +71,7 @@ // & repeat until no more re-shuffling is needed var targetIndex, $thisRow, indx, numRows, $tbody, hasShuffled, $rows, max; - c.$tbodies.each(function(){ + c.$tbodies.each(function() { $tbody = $.tablesorter.processTbody(table, $(this), true); // remove tbody hasShuffled = true; indx = 0; @@ -112,7 +112,7 @@ c.$table.triggerHandler('staticRowsComplete', table); }, - remove : function(table, c, wo){ + remove : function(table, c, wo) { c.$table.unbind( ('updateComplete.tsstaticrows ' + wo.staticRow_event).replace(/\s+/g, ' ') ); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js index 89b027a..32018ef 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js @@ -120,7 +120,7 @@ $stickyThead = $stickyTable.children('thead:first'), $stickyCells, laststate = '', - setWidth = function($orig, $clone){ + setWidth = function($orig, $clone) { var index, width, border, $cell, $this, $cells = $orig.filter(':visible'), len = $cells.length; @@ -261,7 +261,7 @@ }); c.$table .unbind('stickyHeadersUpdate' + namespace) - .bind('stickyHeadersUpdate' + namespace, function(){ + .bind('stickyHeadersUpdate' + namespace, function() { scrollSticky( true ); }); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js index 93485ac..9819ff8 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js @@ -1,4 +1,4 @@ -/*! Widget: storage - updated 4/18/2017 (v2.28.8) */ +/*! Widget: storage - updated 2018-03-18 (v2.30.0) */ /*global JSON:false */ ;(function ($, window, document) { 'use strict'; @@ -44,6 +44,7 @@ values = {}, c = table.config, wo = c && c.widgetOptions, + debug = ts.debug(c, 'storage'), storageType = ( ( options && options.storageType ) || ( wo && wo.storage_storageType ) ).toString().charAt(0).toLowerCase(), @@ -72,14 +73,12 @@ hasStorage = true; window[storageType].removeItem('_tmptest'); } catch (error) { - if (c && c.debug) { - console.warn( storageType + ' is not supported in this browser' ); - } + console.warn( storageType + ' is not supported in this browser' ); } } } - if (c.debug) { - console.log('Storage widget using', hasStorage ? storageType : 'cookies'); + if (debug) { + console.log('Storage >> Using', hasStorage ? storageType : 'cookies'); } // *** get value *** if ($.parseJSON) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js index 9c16901..817ed17 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js @@ -1,4 +1,4 @@ -/*! Widget: uitheme - updated 9/27/2017 (v2.29.0) */ +/*! Widget: uitheme - updated 2018-03-18 (v2.30.0) */ ;(function ($) { 'use strict'; var ts = $.tablesorter || {}; @@ -63,8 +63,9 @@ theme = c.theme || 'jui', themes = themesAll[theme] || {}, remove = $.trim( [ themes.sortNone, themes.sortDesc, themes.sortAsc, themes.active ].join( ' ' ) ), - iconRmv = $.trim( [ themes.iconSortNone, themes.iconSortDesc, themes.iconSortAsc ].join( ' ' ) ); - if (c.debug) { time = new Date(); } + iconRmv = $.trim( [ themes.iconSortNone, themes.iconSortDesc, themes.iconSortAsc ].join( ' ' ) ), + debug = ts.debug(c, 'uitheme'); + if (debug) { time = new Date(); } // initialization code - run once if (!$table.hasClass('tablesorter-' + theme) || c.theme !== c.appliedTheme || !wo.uitheme_applied) { wo.uitheme_applied = true; @@ -109,7 +110,7 @@ $(this)[ event.type === 'mouseenter' ? 'addClass' : 'removeClass' ](themes.hover || ''); }); - $headers.each(function(){ + $headers.each(function() { var $this = $(this); if (!$this.find('.' + ts.css.wrapper).length) { // Firefox needs this inner div to position the icon & resizer correctly @@ -167,8 +168,8 @@ } } } - if (c.debug) { - console.log('Applying ' + theme + ' theme' + ts.benchmark(time)); + if (debug) { + console.log('uitheme >> Applied ' + theme + ' theme' + ts.benchmark(time)); } }, remove: function(table, c, wo, refreshing) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-vertical-group.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-vertical-group.js index 2cbf924..9cb39e1 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-vertical-group.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-vertical-group.js @@ -1,5 +1,5 @@ /*! Widget: vertical-group (BETA) - updated 12/13/2017 (v2.29.1) */ -/* Requires tablesorter and jQuery +/* Requires tablesorter and jQuery * Originally by @aavmurphy (Andrew Murphy) * Adapted for tablesorter by Rob Garrison - see #1469 & #1470 * @@ -7,7 +7,7 @@ */ /* jshint browser:true, jquery:true, unused:false */ /* global jQuery:false */ -;(function($){ +;(function($) { 'use strict'; var ts = $.tablesorter, @@ -19,11 +19,11 @@ verticalGroupShow: 'tablesorter-vertical-group-show' }); - ts.addWidget({ - id: 'vertical-group', + ts.addWidget({ + id: 'vertical-group', priority: 99, init: verticalGroup, - format: verticalGroup + format: verticalGroup }); function cleanUp( el ) { @@ -37,7 +37,7 @@ .addClass( wo.zebra[ indx % 2] ); } - function verticalGroup( table, c, wo, init ) { + function verticalGroup( table, c, wo ) { // ------------------------------------------------------------------------- // loop thru the header row, // - look for .vertical-group @@ -51,9 +51,9 @@ // else if this column does not have '.vertical-group', then this cell is SHOW // else if this cell is NOT the same as the cell-above, then this cell is SHOW // else this cell is HIDE - // if this cell is SHOW, set ALWAYS_SHOW - // if this cell is SHOW, - // then + // if this cell is SHOW, set ALWAYS_SHOW + // if this cell is SHOW, + // then // set the cell class to .vertical_group_show // else // set the cell class to vertical_group_show @@ -69,10 +69,8 @@ // - the border-color needs to be the same // // ------------------------------------------------------------------------------------------------ - var tmp, - zebra_index = -1, // increments at start of loop + var zebra_index = -1, // increments at start of loop rows = table.tBodies[0].rows, - header = table.tHead.rows, has_zebra = ts.hasWidget( table, 'zebra'), is_vertical_group_col = [], last_row = []; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-view.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-view.js index e84e82c..8c76db9 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-view.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-view.js @@ -31,7 +31,7 @@ $.each(wo.view_layouts, function(k, v) { var classes = wo.view_switcher_class; - if (k == wo.view_layout) { + if (k === wo.view_layout) { classes += ' active'; } @@ -108,7 +108,7 @@ var $tmpl = $(tmpl); $.each(data.$row[0].attributes, function(idx, attr) { - if (attr.nodeName == 'class') { + if (attr.nodeName === 'class') { $tmpl.attr(attr.nodeName, $tmpl.attr(attr.nodeName) + ' ' + attr.nodeValue); } else { $tmpl.attr(attr.nodeName, attr.nodeValue); @@ -125,7 +125,7 @@ $(wo.view_container).empty(); }, - hideTable: function(c, wo) { + hideTable: function(c) { tpos = c.$table.css('position'); ttop = c.$table.css('bottom'); tleft = c.$table.css('left'); From c28ecf702d27d94b559da62422646e3b307cc95f Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Tue, 1 May 2018 18:36:15 +0200 Subject: [PATCH 125/138] Update tablesorter to latest version (2.30.3) --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 4 ++-- .../jquery.tablesorter.combined.js | 22 +++++++++---------- .../jquery-tablesorter/jquery.tablesorter.js | 15 +++++-------- .../jquery.tablesorter.widgets.js | 7 +++--- .../parsers/parser-input-select.js | 9 +++++--- .../widgets/widget-build-table.js | 8 +++---- .../widgets/widget-pager.js | 4 ++-- .../widgets/widget-resizable.js | 5 +++-- 12 files changed, 44 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0604356..2120c47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.25.1 (2018-04-01) + +* Upgrade tablesorter to v2.30.3 + #### v1.25.0 (2018-03-22) * Upgrade tablesorter to v2.30.1 diff --git a/README.md b/README.md index 38ee276..595aa11 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.30.1 (2018-03-19) [documentation] +Current tablesorter version: 2.30.3 (2018-03-30) [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index d32b33b..d849e15 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 25 - TINY = 0 + TINY = 1 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index c74a34b..e5592a7 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit c74a34b070002776133757faa5b8613988b42470 +Subproject commit e5592a7101def13a74bbac9a460e5fc60706457f diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 2cef476..4c4202c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin -* updated 2018-03-19 (v2.30.1) +* updated 2018-03-26 (v2.30.2) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -456,7 +456,7 @@ th = result[2]; // headers } l = d && d.length; - if (d instanceof jQuery) { + if (d instanceof $) { if (p.processAjaxOnInit) { // append jQuery object c.$tbodies.eq(0).empty(); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 1026745..b29f109 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-03-19 (v2.30.1)*/ +/*! tablesorter (FORK) - updated 2018-04-30 (v2.30.3)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.30.1 *//* +/*! TableSorter (FORK) v2.30.3 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.30.1', + version : '2.30.3', parsers : [], widgets : [], @@ -536,12 +536,9 @@ ts.buildCache( c ); } $cell = ts.getClosest( $( this ), '.' + ts.css.header ); - // reference original table headers and find the same cell - // don't use $headers or IE8 throws an error - see #987 - temp = $headers.index( $cell ); - c.last.clickedIndex = ( temp < 0 ) ? $cell.attr( 'data-column' ) : temp; - // use column index if $headers is undefined - cell = c.$headers[ c.last.clickedIndex ]; + // use column index from data-attribute or index of current row; fixes #1116 + c.last.clickedIndex = $cell.attr( 'data-column' ) || $cell.index(); + cell = c.$headerIndexed[ c.last.clickedIndex ]; if ( cell && !cell.sortDisabled ) { ts.initSort( c, cell, e ); } @@ -1418,7 +1415,7 @@ } else if ( !$row || // row is a jQuery object? - !( $row instanceof jQuery ) || + !( $row instanceof $ ) || // row contained in the table? ( ts.getClosest( $row, 'table' )[ 0 ] !== c.table ) ) { @@ -5582,7 +5579,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 2018-02-14 (v2.29.6) */ +/*! Widget: resizable - updated 2018-03-26 (v2.30.2) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -5752,7 +5749,8 @@ tableHeight -= c.$table.children('tfoot').height(); } // subtract out table left position from resizable handles. Fixes #864 - startPosition = c.$table.position().left; + // jQuery v3.3.0+ appears to include the start position with the $header.position().left; see #1544 + startPosition = parseFloat($.fn.jquery) >= 3.3 ? 0 : c.$table.position().left; $handles.each( function() { var $this = $(this), column = parseInt( $this.attr( 'data-column' ), 10 ), diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index c324368..14a84c1 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.30.1 *//* +/*! TableSorter (FORK) v2.30.3 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.30.1', + version : '2.30.3', parsers : [], widgets : [], @@ -518,12 +518,9 @@ ts.buildCache( c ); } $cell = ts.getClosest( $( this ), '.' + ts.css.header ); - // reference original table headers and find the same cell - // don't use $headers or IE8 throws an error - see #987 - temp = $headers.index( $cell ); - c.last.clickedIndex = ( temp < 0 ) ? $cell.attr( 'data-column' ) : temp; - // use column index if $headers is undefined - cell = c.$headers[ c.last.clickedIndex ]; + // use column index from data-attribute or index of current row; fixes #1116 + c.last.clickedIndex = $cell.attr( 'data-column' ) || $cell.index(); + cell = c.$headerIndexed[ c.last.clickedIndex ]; if ( cell && !cell.sortDisabled ) { ts.initSort( c, cell, e ); } @@ -1400,7 +1397,7 @@ } else if ( !$row || // row is a jQuery object? - !( $row instanceof jQuery ) || + !( $row instanceof $ ) || // row contained in the table? ( ts.getClosest( $row, 'table' )[ 0 ] !== c.table ) ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index bf4c24a..3d046c3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-03-19 (v2.30.1)*/ +/*! tablesorter (FORK) - updated 2018-04-30 (v2.30.3)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -2690,7 +2690,7 @@ })(jQuery, window); -/*! Widget: resizable - updated 2018-02-14 (v2.29.6) */ +/*! Widget: resizable - updated 2018-03-26 (v2.30.2) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -2860,7 +2860,8 @@ tableHeight -= c.$table.children('tfoot').height(); } // subtract out table left position from resizable handles. Fixes #864 - startPosition = c.$table.position().left; + // jQuery v3.3.0+ appears to include the start position with the $header.position().left; see #1544 + startPosition = parseFloat($.fn.jquery) >= 3.3 ? 0 : c.$table.position().left; $handles.each( function() { var $this = $(this), column = parseInt( $this.attr( 'data-column' ), 10 ), diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index 9d45c04..74c1823 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,4 +1,4 @@ -/*! Parser: input & select - updated 2018-01-30 (v2.29.5) *//* +/*! Parser: input & select - updated 2018-03-03 (v2.30.2) *//* * for jQuery 1.7+ & tablesorter 2.7.11+ * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ @@ -161,13 +161,16 @@ } }, updateHeaderCheckbox = function( $table, checkboxClass ) { - var $rows = $table.children( 'tbody' ).children( ':visible' ), // (include child rows?) + var $sticky, + $rows = $table.children( 'tbody' ).children( ':visible' ), // (include child rows?) len = $rows.length, hasSticky = $table[0].config.widgetOptions.$sticky; // set indeterminate state on header checkbox $table.children( 'thead' ).find( 'input[type="checkbox"]' ).each( function() { + if (hasSticky) { + $sticky = hasSticky.find( '[data-column="' + column + '"]' ); + } var column = $( this ).closest( 'td, th' ).attr( 'data-column' ), - $sticky = hasSticky.find( '[data-column="' + column + '"]' ), vis = $rows.filter( '.' + checkboxClass + '-' + column ).length, allChecked = vis === len && len > 0; if ( vis === 0 || allChecked ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js index 0d60dfd..10aab49 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js @@ -1,4 +1,4 @@ -/*! Widget: Build Table - updated 2018-03-18 (v2.30.0) *//* +/*! Widget: Build Table - updated 2018-03-26 (v2.30.2) *//* * for tableSorter v2.16.0+ * by Rob Garrison */ @@ -26,7 +26,7 @@ // determine type: html, json, array, csv, object runType = function(d) { var t = $.type(d), - jq = d instanceof jQuery; + jq = d instanceof $; // run any processing if set if ( typeof p === 'function' ) { d = p(d, wo); } // store processed data in table.config.data @@ -66,7 +66,7 @@ return false; } - if ( d instanceof jQuery ) { + if ( d instanceof $ ) { // get data from within a jQuery object (csv) runType( $.trim( d.html() ) ); } else if ( d && ( d.hasOwnProperty('url') || typ === 'json' ) ) { @@ -309,7 +309,7 @@ // data may be a jQuery object after processing bt.html = function(table, data, wo) { var $t = $(table); - if ( data instanceof jQuery ) { + if ( data instanceof $ ) { $t.empty().append(data); } else { $t.html(data); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 302d451..cd0c93f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 2018-03-19 (v2.30.1) */ +/*! Widget: Pager - updated 2018-03-26 (v2.30.2) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -773,7 +773,7 @@ th = result[ 2 ]; // headers } l = d && d.length; - if ( d instanceof jQuery ) { + if ( d instanceof $ ) { if ( wo.pager_processAjaxOnInit ) { // append jQuery object c.$tbodies.eq( 0 ).empty(); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js index 5bcb55e..14ca65e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js @@ -1,4 +1,4 @@ -/*! Widget: resizable - updated 2018-02-14 (v2.29.6) */ +/*! Widget: resizable - updated 2018-03-26 (v2.30.2) */ /*jshint browser:true, jquery:true, unused:false */ ;(function ($, window) { 'use strict'; @@ -168,7 +168,8 @@ tableHeight -= c.$table.children('tfoot').height(); } // subtract out table left position from resizable handles. Fixes #864 - startPosition = c.$table.position().left; + // jQuery v3.3.0+ appears to include the start position with the $header.position().left; see #1544 + startPosition = parseFloat($.fn.jquery) >= 3.3 ? 0 : c.$table.position().left; $handles.each( function() { var $this = $(this), column = parseInt( $this.attr( 'data-column' ), 10 ), From 7a687d83b08f3fb1be8ffcaa41a78b40ab126be9 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 21 May 2018 19:53:45 +0200 Subject: [PATCH 126/138] Update tablesorter to latest version (2.30.4) --- CHANGELOG.md | 4 +++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 8 +++--- .../jquery-tablesorter/jquery.tablesorter.js | 6 ++--- .../jquery.tablesorter.widgets.js | 2 +- .../widgets/widget-scroller.js | 26 ++++++++++++++----- 8 files changed, 34 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2120c47..092ab73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.25.2 (2018-05-21) + +* Upgrade tablesorter to v2.30.4 + #### v1.25.1 (2018-04-01) * Upgrade tablesorter to v2.30.3 diff --git a/README.md b/README.md index 595aa11..eb39d65 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.30.3 (2018-03-30) [documentation] +Current tablesorter version: 2.30.4 (2018-05-16) [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index d849e15..4fea353 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 25 - TINY = 1 + TINY = 2 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index e5592a7..ec0ed84 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit e5592a7101def13a74bbac9a460e5fc60706457f +Subproject commit ec0ed84277d10b13b8170748182b68851fe8def0 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index b29f109..a8350e5 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-04-30 (v2.30.3)*/ +/*! tablesorter (FORK) - updated 2018-05-16 (v2.30.4)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.30.3 *//* +/*! TableSorter (FORK) v2.30.4 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.30.3', + version : '2.30.4', parsers : [], widgets : [], @@ -538,7 +538,7 @@ $cell = ts.getClosest( $( this ), '.' + ts.css.header ); // use column index from data-attribute or index of current row; fixes #1116 c.last.clickedIndex = $cell.attr( 'data-column' ) || $cell.index(); - cell = c.$headerIndexed[ c.last.clickedIndex ]; + cell = c.$headerIndexed[ c.last.clickedIndex ][0]; if ( cell && !cell.sortDisabled ) { ts.initSort( c, cell, e ); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 14a84c1..293d8b5 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.30.3 *//* +/*! TableSorter (FORK) v2.30.4 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.30.3', + version : '2.30.4', parsers : [], widgets : [], @@ -520,7 +520,7 @@ $cell = ts.getClosest( $( this ), '.' + ts.css.header ); // use column index from data-attribute or index of current row; fixes #1116 c.last.clickedIndex = $cell.attr( 'data-column' ) || $cell.index(); - cell = c.$headerIndexed[ c.last.clickedIndex ]; + cell = c.$headerIndexed[ c.last.clickedIndex ][0]; if ( cell && !cell.sortDisabled ) { ts.initSort( c, cell, e ); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 3d046c3..4d73125 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-04-30 (v2.30.3)*/ +/*! tablesorter (FORK) - updated 2018-05-16 (v2.30.4)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index 09d2527..46c5b2e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -1,4 +1,4 @@ -/*! Widget: scroller - updated 2018-02-25 (v2.29.6) *//* +/*! Widget: scroller - updated 2018-05-07 (v2.30.4) *//* Copyright (C) 2011 T. Connell & Associates, Inc. Dual-licensed under the MIT and GPL licenses @@ -203,6 +203,7 @@ wo.scroller_calcWidths = []; wo.scroller_saved = [ 0, 0 ]; wo.scroller_isBusy = true; + wo.scroller_scrollTimer = null; // set scrollbar width to one of the following (1) explicitly set scroller_barWidth option, // (2) detected scrollbar width or (3) fallback of 15px @@ -273,8 +274,11 @@ .off( 'scroll' + namespace ) .on( 'scroll' + namespace, function() { // Save position - wo.scroller_saved[0] = $tableWrap.scrollLeft(); - wo.scroller_saved[1] = $tableWrap.scrollTop(); + clearTimeout(wo.scroller_scrollTimer); + wo.scroller_scrollTimer = setTimeout(function() { + wo.scroller_saved[0] = $tableWrap.scrollLeft(); + wo.scroller_saved[1] = $tableWrap.scrollTop(); + }, 300); if ( wo.scroller_jumpToHeader ) { var pos = $win.scrollTop() - $hdr.offset().top; if ( $( this ).scrollTop() !== 0 && pos < tbHt && pos > 0 ) { @@ -295,12 +299,18 @@ $table .off( namespace ) + .on( 'sortStart' + namespace, function() { + clearTimeout(wo.scroller_scrollTimer); + wo.scroller_isBusy = true; + }) .on( 'sortEnd filterEnd'.split( ' ' ).join( namespace + ' ' ), function( event ) { // Sorting, so scroll to top if ( event.type === 'sortEnd' && wo.scroller_upAfterSort ) { - $tableWrap.animate({ - scrollTop : 0 - }, 'fast' ); + $tableWrap + .scrollLeft( wo.scroller_saved[0] ) + .animate({ scrollTop : 0 }, 'fast', function() { + wo.scroller_isBusy = false; + }); } else if ( wo.scroller_fixedColumns ) { setTimeout( function() { // restore previous scroll position @@ -859,7 +869,9 @@ $fixedColumn.find('caption').height( wo.scroller_$header.find( 'caption' ).height() ); $tableWrap.scroll(); - wo.scroller_isBusy = false; + setTimeout(function() { + wo.scroller_isBusy = false; + }, 0) }, From 26ed1386aecf0f7fedb629c7903bae69375b5776 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Sun, 3 Jun 2018 19:38:06 +0200 Subject: [PATCH 127/138] Update tablesorter to latest version (2.30.5) --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 10 ++++++---- .../jquery-tablesorter/jquery.tablesorter.js | 8 +++++--- .../jquery.tablesorter.widgets.js | 2 +- .../jquery-tablesorter/widgets/widget-output.js | 15 ++++++++------- 8 files changed, 27 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 092ab73..423a059 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.25.3 (2018-06-01) + +* Upgrade tablesorter to v2.30.5 + #### v1.25.2 (2018-05-21) * Upgrade tablesorter to v2.30.4 diff --git a/README.md b/README.md index eb39d65..869a602 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.30.4 (2018-05-16) [documentation] +Current tablesorter version: 2.30.5 (2018-05-28) [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 4fea353..b848fe6 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 25 - TINY = 2 + TINY = 3 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index ec0ed84..a3e6b9d 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit ec0ed84277d10b13b8170748182b68851fe8def0 +Subproject commit a3e6b9d55e8b0dc9392f6dcffbbf81ca314b61bf diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index a8350e5..aac923e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-05-16 (v2.30.4)*/ +/*! tablesorter (FORK) - updated 2018-05-28 (v2.30.5)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.30.4 *//* +/*! TableSorter (FORK) v2.30.5 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.30.4', + version : '2.30.5', parsers : [], widgets : [], @@ -524,10 +524,13 @@ return; } downTarget = null; + $cell = ts.getClosest( $( this ), '.' + ts.css.header ); // prevent sort being triggered on form elements if ( ts.regex.formElements.test( e.target.nodeName ) || // nosort class name, or elements within a nosort container $target.hasClass( c.cssNoSort ) || $target.parents( '.' + c.cssNoSort ).length > 0 || + // disabled cell directly clicked + $cell.hasClass( 'sorter-false' ) || // elements within a button $target.parents( 'button' ).length > 0 ) { return !c.cancelSelection; @@ -535,7 +538,6 @@ if ( c.delayInit && ts.isEmptyObject( c.cache ) ) { ts.buildCache( c ); } - $cell = ts.getClosest( $( this ), '.' + ts.css.header ); // use column index from data-attribute or index of current row; fixes #1116 c.last.clickedIndex = $cell.attr( 'data-column' ) || $cell.index(); cell = c.$headerIndexed[ c.last.clickedIndex ][0]; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 293d8b5..826c169 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.30.4 *//* +/*! TableSorter (FORK) v2.30.5 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.30.4', + version : '2.30.5', parsers : [], widgets : [], @@ -506,10 +506,13 @@ return; } downTarget = null; + $cell = ts.getClosest( $( this ), '.' + ts.css.header ); // prevent sort being triggered on form elements if ( ts.regex.formElements.test( e.target.nodeName ) || // nosort class name, or elements within a nosort container $target.hasClass( c.cssNoSort ) || $target.parents( '.' + c.cssNoSort ).length > 0 || + // disabled cell directly clicked + $cell.hasClass( 'sorter-false' ) || // elements within a button $target.parents( 'button' ).length > 0 ) { return !c.cancelSelection; @@ -517,7 +520,6 @@ if ( c.delayInit && ts.isEmptyObject( c.cache ) ) { ts.buildCache( c ); } - $cell = ts.getClosest( $( this ), '.' + ts.css.header ); // use column index from data-attribute or index of current row; fixes #1116 c.last.clickedIndex = $cell.attr( 'data-column' ) || $cell.index(); cell = c.$headerIndexed[ c.last.clickedIndex ][0]; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 4d73125..f145983 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-05-16 (v2.30.4)*/ +/*! tablesorter (FORK) - updated 2018-05-28 (v2.30.5)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js index f9d1215..9d3187d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js @@ -74,7 +74,7 @@ // process rowspans if ($cell.filter('[rowspan]').length) { rowspanLen = parseInt( $cell.attr('rowspan'), 10) - 1; - txt = output.formatData( c, wo, $cell, isHeader ); + txt = output.formatData( c, wo, $cell, isHeader, indx ); for (row = 1; row <= rowspanLen; row++) { if (!tmpRow[rowIndex + row]) { tmpRow[rowIndex + row] = []; } tmpRow[rowIndex + row][cellIndex] = isHeader ? txt : dupe ? txt : ''; @@ -84,7 +84,7 @@ if ($cell.filter('[colspan]').length) { colspanLen = parseInt( $cell.attr('colspan'), 10) - 1; // allow data-attribute to be an empty string - txt = output.formatData( c, wo, $cell, isHeader ); + txt = output.formatData( c, wo, $cell, isHeader, indx ); for (col = 0; col < colspanLen; col++) { // if we're processing the header & making JSON, the header names need to be unique if ($cell.filter('[rowspan]').length) { @@ -107,7 +107,7 @@ while (typeof tmpRow[rowIndex][cellIndex] !== 'undefined') { cellIndex++; } tmpRow[rowIndex][cellIndex] = tmpRow[rowIndex][cellIndex] || - output.formatData( c, wo, $cell, isHeader ); + output.formatData( c, wo, $cell, isHeader, cellIndex ); cellIndex++; } } @@ -264,7 +264,7 @@ return json; }, - formatData : function(c, wo, $el, isHeader) { + formatData : function(c, wo, $el, isHeader, colIndex) { var attr = $el.attr(wo.output_dataAttrib), txt = typeof attr !== 'undefined' ? attr : $el.html(), quotes = (wo.output_separator || ',').toLowerCase(), @@ -288,13 +288,14 @@ // JSON & array outputs don't need quotes quotes = separator ? false : wo.output_wrapQuotes || wo.output_regex.test(result) || output.regexQuote.test(result); result = quotes ? '"' + result + '"' : result; - // formatting callback - added v2.22.4 if ( typeof wo.output_formatContent === 'function' ) { return wo.output_formatContent( c, wo, { - isHeader : isHeader, + isHeader : isHeader || false, $cell : $el, - content : result + content : result, + columnIndex: colIndex, + parsed: c.parsers[colIndex].format(result, c.table, $el[0], colIndex) }); } From 4fcc71340923d62ac6365376fb266fe9ff862932 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Sun, 17 Jun 2018 18:16:01 +0200 Subject: [PATCH 128/138] Update tablesorter to latest version (2.30.6) --- CHANGELOG.md | 4 + README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 6 +- .../jquery-tablesorter/jquery.tablesorter.js | 4 +- .../jquery.tablesorter.widgets.js | 2 +- .../parsers/parser-input-select.js | 4 +- .../widgets/widget-cssStickyHeaders.js | 244 +++++++++--------- 9 files changed, 140 insertions(+), 130 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 423a059..e27fcfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.25.4 (2018-06-17) + +* Upgrade tablesorter to v2.30.6 + #### v1.25.3 (2018-06-01) * Upgrade tablesorter to v2.30.5 diff --git a/README.md b/README.md index 869a602..7ccd094 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.30.5 (2018-05-28) [documentation] +Current tablesorter version: 2.30.6 (2018-06-16) [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index b848fe6..5e28d10 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 25 - TINY = 3 + TINY = 4 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index a3e6b9d..6dbe7ed 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit a3e6b9d55e8b0dc9392f6dcffbbf81ca314b61bf +Subproject commit 6dbe7ed3fef295c85b3c39f91114de39aa6dc6d7 diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index aac923e..b228551 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-05-28 (v2.30.5)*/ +/*! tablesorter (FORK) - updated 2018-06-16 (v2.30.6)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.30.5 *//* +/*! TableSorter (FORK) v2.30.6 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.30.5', + version : '2.30.6', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 826c169..d943b6a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.30.5 *//* +/*! TableSorter (FORK) v2.30.6 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.30.5', + version : '2.30.6', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index f145983..e910e56 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-05-28 (v2.30.5)*/ +/*! tablesorter (FORK) - updated 2018-06-16 (v2.30.6)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index 74c1823..43e6b7e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,4 +1,4 @@ -/*! Parser: input & select - updated 2018-03-03 (v2.30.2) *//* +/*! Parser: input & select - updated 2018-06-16 (v2.30.6) *//* * for jQuery 1.7+ & tablesorter 2.7.11+ * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ @@ -145,7 +145,7 @@ } }, updateCheckbox = function($el, state) { - if ($el[0].nodeName !== 'INPUT') { + if ($el.length && $el[0].nodeName !== 'INPUT') { $el = $el.find( 'input[type="checkbox"]' ); } if ($el.length) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js index 02e248c..4a12268 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js @@ -1,4 +1,4 @@ -/*! Widget: cssStickyHeaders - updated 5/24/2017 (v2.28.11) *//* +/*! Widget: cssStickyHeaders - updated 6/16/2018 (v2.30.6) *//* * Requires a modern browser, tablesorter v2.8+ */ /*jshint jquery:true, unused:false */ @@ -7,149 +7,155 @@ var ts = $.tablesorter; - ts.addWidget({ - id: 'cssStickyHeaders', - priority: 10, - options: { - cssStickyHeaders_offset : 0, - cssStickyHeaders_addCaption : false, - // jQuery selector or object to attach sticky header to - cssStickyHeaders_attachTo : null, - cssStickyHeaders_filteredToTop : true - }, - init : function(table, thisWidget, c, wo) { - var offst, adjustY, - $table = c.$table, - $attach = $(wo.cssStickyHeaders_attachTo), - // target all versions of IE - isIE = 'ActiveXObject' in window || window.navigator.userAgent.indexOf('Edge') > -1, - namespace = c.namespace + 'cssstickyheader ', - $thead = $table.children('thead'), - $caption = $table.children('caption'), - $win = $attach.length ? $attach : $(window), - $parent = $table.parent().closest('table.' + ts.css.table), - $parentThead = $parent.length && ts.hasWidget($parent[0], 'cssStickyHeaders') ? $parent.children('thead') : [], - borderTopWidth = ( parseInt( $table.css('border-top-width'), 10 ) || 0 ), - // Fixes for Safari - tableH = $table.height(), - lastCaptionSetting = wo.cssStickyHeaders_addCaption, - // table offset top changes while scrolling in FF - adjustOffsetTop = false, - addCaptionHeight = false, - setTransform = function( $elms, y ) { - var translate = y === 0 ? '' : 'translate(0px,' + y + 'px)'; - $elms.css({ - 'transform' : translate, - '-ms-transform' : translate, - '-webkit-transform' : translate - }); - }; - - // Firefox fixes - if ($caption.length) { - // Firefox does not include the caption height when getting the table height - // see https://bugzilla.mozilla.org/show_bug.cgi?id=820891, so lets detect it instead of browser sniff - $caption.hide(); - addCaptionHeight = $table.height() === tableH; - $caption.show(); - - // Firefox changes the offset().top when translating the table caption - offst = $table.offset().top; - setTransform( $caption, 20 ); - adjustOffsetTop = $table.offset().top !== offst; - setTransform( $caption, 0 ); - } + function cssStickyHeadersInit(c, wo) { + var offst, adjustY, + $table = c.$table, + $attach = $(wo.cssStickyHeaders_attachTo), + // target all versions of IE + isIE = 'ActiveXObject' in window || window.navigator.userAgent.indexOf('Edge') > -1, + namespace = c.namespace + 'cssstickyheader ', + $thead = $table.children('thead'), + $caption = $table.children('caption'), + $win = $attach.length ? $attach : $(window), + $parent = $table.parent().closest('table.' + ts.css.table), + $parentThead = $parent.length && ts.hasWidget($parent[0], 'cssStickyHeaders') ? $parent.children('thead') : [], + borderTopWidth = ( parseInt( $table.css('border-top-width'), 10 ) || 0 ), + // Fixes for Safari + tableH = $table.height(), + lastCaptionSetting = wo.cssStickyHeaders_addCaption, + // table offset top changes while scrolling in FF + adjustOffsetTop = false, + addCaptionHeight = false, + setTransform = function( $elms, y ) { + var translate = y === 0 ? '' : 'translate(0px,' + y + 'px)'; + $elms.css({ + 'transform' : translate, + '-ms-transform' : translate, + '-webkit-transform' : translate + }); + }; + + // Firefox fixes + if ($caption.length) { + // Firefox does not include the caption height when getting the table height + // see https://bugzilla.mozilla.org/show_bug.cgi?id=820891, so lets detect it instead of browser sniff + $caption.hide(); + addCaptionHeight = $table.height() === tableH; + $caption.show(); + + // Firefox changes the offset().top when translating the table caption + offst = $table.offset().top; + setTransform( $caption, 20 ); + adjustOffsetTop = $table.offset().top !== offst; + setTransform( $caption, 0 ); + } - $win - .unbind( ('scroll resize '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) - .bind('scroll resize '.split(' ').join(namespace), function() { - // make sure "wo" is current otherwise changes to widgetOptions - // are not dynamic (like the add caption button in the demo) - wo = c.widgetOptions; + $win + .unbind( ('scroll resize '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) + .bind('scroll resize '.split(' ').join(namespace), function() { + // make sure "wo" is current otherwise changes to widgetOptions + // are not dynamic (like the add caption button in the demo) + wo = c.widgetOptions; - if ( adjustOffsetTop ) { - // remove transform from caption to get the correct offset().top value - setTransform( $caption, 0 ); - adjustY = $table.offset().top; - } + if ( adjustOffsetTop ) { + // remove transform from caption to get the correct offset().top value + setTransform( $caption, 0 ); + adjustY = $table.offset().top; + } - // Fix for safari, when caption present, table - // height changes while scrolling - if ($win.scrollTop() < $caption.outerHeight(true)) { - tableH = $table.height(); - } + // Fix for safari, when caption present, table + // height changes while scrolling + if ($win.scrollTop() < $caption.outerHeight(true)) { + tableH = $table.height(); + } - var top = $attach.length ? $attach.offset().top : $win.scrollTop(), - // add caption height; include table padding top & border-spacing or text may be above the fold (jQuery UI themes) - // border-spacing needed in Firefox, but not webkit... not sure if I should account for that - captionHeight = ( $caption.outerHeight(true) || 0 ) + - ( parseInt( $table.css('padding-top'), 10 ) || 0 ) + - ( parseInt( $table.css('border-spacing'), 10 ) || 0 ), + var top = $attach.length ? $attach.offset().top : $win.scrollTop(), + // add caption height; include table padding top & border-spacing or text may be above the fold (jQuery UI themes) + // border-spacing needed in Firefox, but not webkit... not sure if I should account for that + captionHeight = ( $caption.outerHeight(true) || 0 ) + + ( parseInt( $table.css('padding-top'), 10 ) || 0 ) + + ( parseInt( $table.css('border-spacing'), 10 ) || 0 ), - bottom = tableH + ( addCaptionHeight && wo.cssStickyHeaders_addCaption ? captionHeight : 0 ) - - $thead.height() - ( $table.children('tfoot').height() || 0 ) - - ( wo.cssStickyHeaders_addCaption ? captionHeight : ( addCaptionHeight ? 0 : captionHeight ) ), + bottom = tableH + ( addCaptionHeight && wo.cssStickyHeaders_addCaption ? captionHeight : 0 ) - + $thead.height() - ( $table.children('tfoot').height() || 0 ) - + ( wo.cssStickyHeaders_addCaption ? captionHeight : ( addCaptionHeight ? 0 : captionHeight ) ), - parentTheadHeight = $parentThead.length ? $parentThead.height() : 0, + parentTheadHeight = $parentThead.length ? $parentThead.height() : 0, - // get bottom of nested sticky headers - nestedStickyBottom = $parentThead.length ? ( - isIE ? $parent.data('cssStickyHeaderBottom') + parentTheadHeight : - $parentThead.offset().top + parentTheadHeight - $win.scrollTop() - ) : 0, + // get bottom of nested sticky headers + nestedStickyBottom = $parentThead.length ? ( + isIE ? $parent.data('cssStickyHeaderBottom') + parentTheadHeight : + $parentThead.offset().top + parentTheadHeight - $win.scrollTop() + ) : 0, - // in this case FF's offsetTop changes while scrolling, so we get a saved offsetTop before scrolling occurs - // but when the table is inside a wrapper ($attach) we need to continually update the offset top - tableOffsetTop = adjustOffsetTop ? adjustY : $table.offset().top, + // in this case FF's offsetTop changes while scrolling, so we get a saved offsetTop before scrolling occurs + // but when the table is inside a wrapper ($attach) we need to continually update the offset top + tableOffsetTop = adjustOffsetTop ? adjustY : $table.offset().top, - offsetTop = addCaptionHeight ? tableOffsetTop - ( wo.cssStickyHeaders_addCaption ? captionHeight : 0 ) : tableOffsetTop, + offsetTop = addCaptionHeight ? tableOffsetTop - ( wo.cssStickyHeaders_addCaption ? captionHeight : 0 ) : tableOffsetTop, - // Detect nested tables - fixes #724 - deltaY = top - offsetTop + nestedStickyBottom + borderTopWidth + ( wo.cssStickyHeaders_offset || 0 ) - - ( wo.cssStickyHeaders_addCaption ? ( addCaptionHeight ? captionHeight : 0 ) : captionHeight ), + // Detect nested tables - fixes #724 + deltaY = top - offsetTop + nestedStickyBottom + borderTopWidth + ( wo.cssStickyHeaders_offset || 0 ) - + ( wo.cssStickyHeaders_addCaption ? ( addCaptionHeight ? captionHeight : 0 ) : captionHeight ), - finalY = deltaY > 0 && deltaY <= bottom ? deltaY : 0, + finalY = deltaY > 0 && deltaY <= bottom ? deltaY : 0, - // All IE (even IE11) can only transform header cells - fixes #447 thanks to @gakreol! - $cells = isIE ? $thead.children().children() : $thead; + // All IE (even IE11) can only transform header cells - fixes #447 thanks to @gakreol! + $cells = isIE ? $thead.children().children() : $thead; - // more crazy IE stuff... - if (isIE) { - // I didn't bother testing 3 nested tables deep in IE, because I hate it - c.$table.data( 'cssStickyHeaderBottom', ( $parentThead.length ? parentTheadHeight : 0 ) - - ( wo.cssStickyHeaders_addCaption ? captionHeight : 0 ) ); - } + // more crazy IE stuff... + if (isIE) { + // I didn't bother testing 3 nested tables deep in IE, because I hate it + c.$table.data( 'cssStickyHeaderBottom', ( $parentThead.length ? parentTheadHeight : 0 ) - + ( wo.cssStickyHeaders_addCaption ? captionHeight : 0 ) ); + } - if (wo.cssStickyHeaders_addCaption) { - $cells = $cells.add($caption); - } - if (lastCaptionSetting !== wo.cssStickyHeaders_addCaption) { - lastCaptionSetting = wo.cssStickyHeaders_addCaption; - // reset caption position if addCaption option is dynamically changed to false - if (!lastCaptionSetting) { - setTransform( $caption, 0 ); - } + if (wo.cssStickyHeaders_addCaption) { + $cells = $cells.add($caption); + } + if (lastCaptionSetting !== wo.cssStickyHeaders_addCaption) { + lastCaptionSetting = wo.cssStickyHeaders_addCaption; + // reset caption position if addCaption option is dynamically changed to false + if (!lastCaptionSetting) { + setTransform( $caption, 0 ); } + } - setTransform( $cells, finalY ); + setTransform( $cells, finalY ); + }); + $table + .unbind( ('filterEnd updateComplete '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) + .bind('filterEnd' + namespace, function() { + if (wo.cssStickyHeaders_filteredToTop) { + // scroll top of table into view + window.scrollTo(0, $table.position().top); + } + }) + .bind('updateComplete' + namespace, function() { + cssStickyHeadersInit(c, c.widgetOptions); }); - $table - .unbind( ('filterEnd' + namespace).replace(/\s+/g, ' ') ) - .bind('filterEnd' + namespace, function() { - if (wo.cssStickyHeaders_filteredToTop) { - // scroll top of table into view - window.scrollTo(0, $table.position().top); - } - }); + } + ts.addWidget({ + id: 'cssStickyHeaders', + priority: 10, + options: { + cssStickyHeaders_offset : 0, + cssStickyHeaders_addCaption : false, + // jQuery selector or object to attach sticky header to + cssStickyHeaders_attachTo : null, + cssStickyHeaders_filteredToTop : true + }, + init : function(table, thisWidget, c, wo) { + cssStickyHeadersInit(c, wo); }, remove: function(table, c, wo, refreshing) { if (refreshing) { return; } var namespace = c.namespace + 'cssstickyheader '; $(window).unbind( ('scroll resize '.split(' ').join(namespace)).replace(/\s+/g, ' ') ); c.$table - .unbind( ('filterEnd scroll resize '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) + .unbind( ('filterEnd scroll resize updateComplete '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) .add( c.$table.children('thead').children().children() ) .children('thead, caption').css({ 'transform' : '', From 68aca4087a35c21403b77d9c729c10986a5397b6 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 20 Aug 2018 21:25:04 +0200 Subject: [PATCH 129/138] Update tablesorter to latest version (2.30.7) --- CHANGELOG.md | 4 ++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery.tablesorter.combined.js | 54 +++++++++++++------ .../jquery-tablesorter/jquery.tablesorter.js | 52 ++++++++++++------ .../jquery.tablesorter.widgets.js | 2 +- .../parsers/parser-input-select.js | 9 ++-- .../widgets/widget-scroller.js | 2 +- .../jquery-tablesorter/theme.black-ice.css | 2 +- .../jquery-tablesorter/theme.blue.css | 2 +- .../jquery-tablesorter/theme.bootstrap.css | 2 +- .../jquery-tablesorter/theme.bootstrap_2.css | 4 +- .../jquery-tablesorter/theme.bootstrap_3.css | 2 +- .../jquery-tablesorter/theme.bootstrap_4.css | 2 +- .../jquery-tablesorter/theme.dark.css | 2 +- .../jquery-tablesorter/theme.default.css | 2 +- .../jquery-tablesorter/theme.dropbox.css | 2 +- .../jquery-tablesorter/theme.green.css | 2 +- .../jquery-tablesorter/theme.grey.css | 2 +- .../jquery-tablesorter/theme.ice.css | 2 +- .../jquery-tablesorter/theme.jui.css | 2 +- .../jquery-tablesorter/theme.materialize.css | 2 +- 23 files changed, 105 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e27fcfd..09fdcfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.25.5 (2018-08-20) + +* Upgrade tablesorter to v2.30.7 + #### v1.25.4 (2018-06-17) * Upgrade tablesorter to v2.30.6 diff --git a/README.md b/README.md index 7ccd094..891676c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.30.6 (2018-06-16) [documentation] +Current tablesorter version: 2.30.7 (2018-07-10) [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 5e28d10..4500dde 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 25 - TINY = 4 + TINY = 5 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 6dbe7ed..90f7326 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 6dbe7ed3fef295c85b3c39f91114de39aa6dc6d7 +Subproject commit 90f7326819c573ee5ab333f4a0672623d7c52e1e diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index b228551..206de5a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-06-16 (v2.30.6)*/ +/*! tablesorter (FORK) - updated 2018-07-10 (v2.30.7)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { @@ -16,7 +16,7 @@ } }(function(jQuery) { -/*! TableSorter (FORK) v2.30.6 *//* +/*! TableSorter (FORK) v2.30.7 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +40,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.30.6', + version : '2.30.7', parsers : [], widgets : [], @@ -611,10 +611,11 @@ // this may get updated numerous times if there are multiple rows c.sortVars[ column ] = { count : -1, // set to -1 because clicking on the header automatically adds one - order: tmp ? + order : tmp ? ( c.sortReset ? [ 1, 0, 2 ] : [ 1, 0 ] ) : // desc, asc, unsorted ( c.sortReset ? [ 0, 1, 2 ] : [ 0, 1 ] ), // asc, desc, unsorted - lockedOrder : false + lockedOrder : false, + sortedBy : '' }; tmp = ts.getData( $elem, configHeaders, 'lockedOrder' ) || false; if ( typeof tmp !== 'undefined' && tmp !== false ) { @@ -1214,6 +1215,11 @@ txt += ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ]; } $header.attr( 'aria-label', txt ); + if (vars.sortedBy) { + $header.attr( 'data-sortedBy', vars.sortedBy ); + } else { + $header.removeAttr('data-sortedBy'); + } } }, @@ -1568,6 +1574,7 @@ len = c.$headers.length, th = ts.getClosest( $( cell ), 'th, td' ), col = parseInt( th.attr( 'data-column' ), 10 ), + sortedBy = event.type === 'mouseup' ? 'user' : event.type, order = c.sortVars[ col ].order; th = th[0]; // Only call sortStart if sorting is enabled @@ -1588,6 +1595,9 @@ } // user only wants to sort on one column if ( notMultiSort ) { + $.each( c.sortVars, function( i ) { + c.sortVars[ i ].sortedBy = ''; + }); // flush the sort list c.sortList = []; c.last.sortList = []; @@ -1596,6 +1606,7 @@ for ( indx = 0; indx < arry.length; indx++ ) { if ( arry[ indx ][ 0 ] !== col ) { c.sortList[ c.sortList.length ] = arry[ indx ]; + c.sortVars[ arry[ indx ][ 0 ] ].sortedBy = 'sortForce'; } } } @@ -1603,12 +1614,14 @@ dir = order[ c.sortVars[ col ].count ]; if ( dir < 2 ) { c.sortList[ c.sortList.length ] = [ col, dir ]; + c.sortVars[ col ].sortedBy = sortedBy; // add other columns if header spans across multiple if ( th.colSpan > 1 ) { for ( indx = 1; indx < th.colSpan; indx++ ) { c.sortList[ c.sortList.length ] = [ col + indx, dir ]; // update count on columns in colSpan c.sortVars[ col + indx ].count = $.inArray( dir, order ); + c.sortVars[ col + indx ].sortedBy = sortedBy; } } } @@ -1620,6 +1633,7 @@ // the user has clicked on an already sorted column if ( ts.isValueInArray( col, c.sortList ) >= 0 ) { // reverse the sorting direction + c.sortVars[ col ].sortedBy = sortedBy; for ( indx = 0; indx < c.sortList.length; indx++ ) { tmp = c.sortList[ indx ]; if ( tmp[ 0 ] === col ) { @@ -1634,6 +1648,7 @@ } else { // add column to sort list array dir = order[ c.sortVars[ col ].count ]; + c.sortVars[ col ].sortedBy = sortedBy; if ( dir < 2 ) { c.sortList[ c.sortList.length ] = [ col, dir ]; // add other columns if header spans across multiple @@ -1642,6 +1657,7 @@ c.sortList[ c.sortList.length ] = [ col + indx, dir ]; // update count on columns in colSpan c.sortVars[ col + indx ].count = $.inArray( dir, order ); + c.sortVars[ col + indx ].sortedBy = sortedBy; } } } @@ -1677,6 +1693,7 @@ } } c.sortList[ c.sortList.length ] = [ arry[ indx ][ 0 ], dir ]; + c.sortVars[ arry[ indx ][ 0 ] ].sortedBy = 'sortAppend'; } } } @@ -1762,7 +1779,7 @@ sort = sorter[ col ]( x[ col ], y[ col ], dir, col, table ); } else { // fall back to natural sort - sort = ts[ 'sortNatural' + ( dir ? 'Asc' : 'Desc' ) ]( a[ col ], b[ col ], col, c ); + sort = ts[ 'sortNatural' + ( dir ? 'Asc' : 'Desc' ) ]( a[ col ] || '', b[ col ] || '', col, c ); } } if ( sort ) { return sort; } @@ -1808,8 +1825,12 @@ }, sortOn : function( c, list, callback, init ) { - var table = c.table; + var indx, + table = c.table; c.$table.triggerHandler( 'sortStart', table ); + for (indx = 0; indx < c.columns; indx++) { + c.sortVars[ indx ].sortedBy = ts.isValueInArray( indx, list ) > -1 ? 'sorton' : ''; + } // update header count index ts.updateHeaderSortCount( c, list ); // set css for headers @@ -1832,13 +1853,14 @@ sortReset : function( c, callback ) { c.sortList = []; - ts.setHeadersCss( c ); - ts.multisort( c ); - ts.appendCache( c ); var indx; for (indx = 0; indx < c.columns; indx++) { c.sortVars[ indx ].count = -1; + c.sortVars[ indx ].sortedBy = ''; } + ts.setHeadersCss( c ); + ts.multisort( c ); + ts.appendCache( c ); if ( $.isFunction( callback ) ) { callback( c.table ); } @@ -1856,20 +1878,20 @@ // Natural sort - https://github.com/overset/javascript-natural-sort (date sorting removed) sortNatural : function( a, b ) { if ( a === b ) { return 0; } - a = a.toString(); - b = b.toString(); + a = ( a || '' ).toString(); + b = ( b || '' ).toString(); var aNum, bNum, aFloat, bFloat, indx, max, regex = ts.regex; // first try and sort Hex codes if ( regex.hex.test( b ) ) { - aNum = parseInt( ( a || '' ).match( regex.hex ), 16 ); - bNum = parseInt( ( b || '' ).match( regex.hex ), 16 ); + aNum = parseInt( a.match( regex.hex ), 16 ); + bNum = parseInt( b.match( regex.hex ), 16 ); if ( aNum < bNum ) { return -1; } if ( aNum > bNum ) { return 1; } } // chunk/tokenize - aNum = ( a || '' ).replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); - bNum = ( b || '' ).replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); + aNum = a.replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); + bNum = b.replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); max = Math.max( aNum.length, bNum.length ); // natural sorting through split numeric strings and default strings for ( indx = 0; indx < max; indx++ ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index d943b6a..2b5e94a 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.30.6 *//* +/*! TableSorter (FORK) v2.30.7 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.30.6', + version : '2.30.7', parsers : [], widgets : [], @@ -593,10 +593,11 @@ // this may get updated numerous times if there are multiple rows c.sortVars[ column ] = { count : -1, // set to -1 because clicking on the header automatically adds one - order: tmp ? + order : tmp ? ( c.sortReset ? [ 1, 0, 2 ] : [ 1, 0 ] ) : // desc, asc, unsorted ( c.sortReset ? [ 0, 1, 2 ] : [ 0, 1 ] ), // asc, desc, unsorted - lockedOrder : false + lockedOrder : false, + sortedBy : '' }; tmp = ts.getData( $elem, configHeaders, 'lockedOrder' ) || false; if ( typeof tmp !== 'undefined' && tmp !== false ) { @@ -1196,6 +1197,11 @@ txt += ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ]; } $header.attr( 'aria-label', txt ); + if (vars.sortedBy) { + $header.attr( 'data-sortedBy', vars.sortedBy ); + } else { + $header.removeAttr('data-sortedBy'); + } } }, @@ -1550,6 +1556,7 @@ len = c.$headers.length, th = ts.getClosest( $( cell ), 'th, td' ), col = parseInt( th.attr( 'data-column' ), 10 ), + sortedBy = event.type === 'mouseup' ? 'user' : event.type, order = c.sortVars[ col ].order; th = th[0]; // Only call sortStart if sorting is enabled @@ -1570,6 +1577,9 @@ } // user only wants to sort on one column if ( notMultiSort ) { + $.each( c.sortVars, function( i ) { + c.sortVars[ i ].sortedBy = ''; + }); // flush the sort list c.sortList = []; c.last.sortList = []; @@ -1578,6 +1588,7 @@ for ( indx = 0; indx < arry.length; indx++ ) { if ( arry[ indx ][ 0 ] !== col ) { c.sortList[ c.sortList.length ] = arry[ indx ]; + c.sortVars[ arry[ indx ][ 0 ] ].sortedBy = 'sortForce'; } } } @@ -1585,12 +1596,14 @@ dir = order[ c.sortVars[ col ].count ]; if ( dir < 2 ) { c.sortList[ c.sortList.length ] = [ col, dir ]; + c.sortVars[ col ].sortedBy = sortedBy; // add other columns if header spans across multiple if ( th.colSpan > 1 ) { for ( indx = 1; indx < th.colSpan; indx++ ) { c.sortList[ c.sortList.length ] = [ col + indx, dir ]; // update count on columns in colSpan c.sortVars[ col + indx ].count = $.inArray( dir, order ); + c.sortVars[ col + indx ].sortedBy = sortedBy; } } } @@ -1602,6 +1615,7 @@ // the user has clicked on an already sorted column if ( ts.isValueInArray( col, c.sortList ) >= 0 ) { // reverse the sorting direction + c.sortVars[ col ].sortedBy = sortedBy; for ( indx = 0; indx < c.sortList.length; indx++ ) { tmp = c.sortList[ indx ]; if ( tmp[ 0 ] === col ) { @@ -1616,6 +1630,7 @@ } else { // add column to sort list array dir = order[ c.sortVars[ col ].count ]; + c.sortVars[ col ].sortedBy = sortedBy; if ( dir < 2 ) { c.sortList[ c.sortList.length ] = [ col, dir ]; // add other columns if header spans across multiple @@ -1624,6 +1639,7 @@ c.sortList[ c.sortList.length ] = [ col + indx, dir ]; // update count on columns in colSpan c.sortVars[ col + indx ].count = $.inArray( dir, order ); + c.sortVars[ col + indx ].sortedBy = sortedBy; } } } @@ -1659,6 +1675,7 @@ } } c.sortList[ c.sortList.length ] = [ arry[ indx ][ 0 ], dir ]; + c.sortVars[ arry[ indx ][ 0 ] ].sortedBy = 'sortAppend'; } } } @@ -1744,7 +1761,7 @@ sort = sorter[ col ]( x[ col ], y[ col ], dir, col, table ); } else { // fall back to natural sort - sort = ts[ 'sortNatural' + ( dir ? 'Asc' : 'Desc' ) ]( a[ col ], b[ col ], col, c ); + sort = ts[ 'sortNatural' + ( dir ? 'Asc' : 'Desc' ) ]( a[ col ] || '', b[ col ] || '', col, c ); } } if ( sort ) { return sort; } @@ -1790,8 +1807,12 @@ }, sortOn : function( c, list, callback, init ) { - var table = c.table; + var indx, + table = c.table; c.$table.triggerHandler( 'sortStart', table ); + for (indx = 0; indx < c.columns; indx++) { + c.sortVars[ indx ].sortedBy = ts.isValueInArray( indx, list ) > -1 ? 'sorton' : ''; + } // update header count index ts.updateHeaderSortCount( c, list ); // set css for headers @@ -1814,13 +1835,14 @@ sortReset : function( c, callback ) { c.sortList = []; - ts.setHeadersCss( c ); - ts.multisort( c ); - ts.appendCache( c ); var indx; for (indx = 0; indx < c.columns; indx++) { c.sortVars[ indx ].count = -1; + c.sortVars[ indx ].sortedBy = ''; } + ts.setHeadersCss( c ); + ts.multisort( c ); + ts.appendCache( c ); if ( $.isFunction( callback ) ) { callback( c.table ); } @@ -1838,20 +1860,20 @@ // Natural sort - https://github.com/overset/javascript-natural-sort (date sorting removed) sortNatural : function( a, b ) { if ( a === b ) { return 0; } - a = a.toString(); - b = b.toString(); + a = ( a || '' ).toString(); + b = ( b || '' ).toString(); var aNum, bNum, aFloat, bFloat, indx, max, regex = ts.regex; // first try and sort Hex codes if ( regex.hex.test( b ) ) { - aNum = parseInt( ( a || '' ).match( regex.hex ), 16 ); - bNum = parseInt( ( b || '' ).match( regex.hex ), 16 ); + aNum = parseInt( a.match( regex.hex ), 16 ); + bNum = parseInt( b.match( regex.hex ), 16 ); if ( aNum < bNum ) { return -1; } if ( aNum > bNum ) { return 1; } } // chunk/tokenize - aNum = ( a || '' ).replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); - bNum = ( b || '' ).replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); + aNum = a.replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); + bNum = b.replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); max = Math.max( aNum.length, bNum.length ); // natural sorting through split numeric strings and default strings for ( indx = 0; indx < max; indx++ ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index e910e56..e58c42f 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-06-16 (v2.30.6)*/ +/*! tablesorter (FORK) - updated 2018-07-10 (v2.30.7)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory) { if (typeof define === 'function' && define.amd) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js index 43e6b7e..129952e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +++ b/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js @@ -1,4 +1,4 @@ -/*! Parser: input & select - updated 2018-06-16 (v2.30.6) *//* +/*! Parser: input & select - updated 2018-07-10 (v2.30.7) *//* * for jQuery 1.7+ & tablesorter 2.7.11+ * Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html */ @@ -164,9 +164,12 @@ var $sticky, $rows = $table.children( 'tbody' ).children( ':visible' ), // (include child rows?) len = $rows.length, - hasSticky = $table[0].config.widgetOptions.$sticky; + c = $table[0].config, + wo = c && c.widgetOptions, + $headers = c && c.$headers.add( $( c.namespace + '_extra_headers' ) ) || $table.children( 'thead' ), + hasSticky = wo && wo.$sticky; // set indeterminate state on header checkbox - $table.children( 'thead' ).find( 'input[type="checkbox"]' ).each( function() { + $headers.find( 'input[type="checkbox"]' ).each( function() { if (hasSticky) { $sticky = hasSticky.find( '[data-column="' + column + '"]' ); } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js index 46c5b2e..082b7a2 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js @@ -871,7 +871,7 @@ $tableWrap.scroll(); setTimeout(function() { wo.scroller_isBusy = false; - }, 0) + }, 0); }, diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css index e139f7f..6e7443b 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css @@ -117,7 +117,7 @@ } /* caption */ -caption { +.tablesorter-blackice > caption { background-color: #fff; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css index 487f117..f27fe9f 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css @@ -154,7 +154,7 @@ } /* caption */ -caption { +.tablesorter-blue > caption { background-color: #fff; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css index 10e911d..60cc60c 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css @@ -109,7 +109,7 @@ } /* caption */ -.caption { +.tablesorter-bootstrap > .caption { background-color: #fff; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css index aadf673..2d49ba4 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css @@ -112,7 +112,7 @@ } /* caption */ -caption { +.tablesorter-bootstrap > caption { background-color: #fff; } @@ -185,4 +185,4 @@ caption { text-align: center; cursor: pointer; background-color: #e6bf99; -} \ No newline at end of file +} diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_3.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_3.css index 12ab36d..ec5ef05 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_3.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_3.css @@ -106,7 +106,7 @@ } /* caption */ -.caption { +.tablesorter-bootstrap > .caption { background-color: #fff; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_4.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_4.css index 32241bf..3f1fa73 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_4.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_4.css @@ -112,7 +112,7 @@ } /* caption */ -.tablesorter-bootstrap:not(.table-dark) .caption { +.tablesorter-bootstrap:not(.table-dark) > .caption { background-color: #fff; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css index 26504e2..73ca661 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css @@ -116,7 +116,7 @@ } /* caption */ -caption { +.tablesorter-dark > caption { background-color: #202020; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css index 67002de..63e6f23 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css @@ -118,7 +118,7 @@ Default Theme } /* caption */ -caption { +.tablesorter-default > caption { background-color: #fff; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css index 897ab11..eb5e7b4 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css @@ -138,7 +138,7 @@ } /* caption */ -caption { +.tablesorter-dropbox > caption { background-color: #fff; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css index ef30e37..a8f55e5 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css @@ -137,7 +137,7 @@ } /* caption */ -caption { +.tablesorter-green > caption { background-color: #fff; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css index c92a37c..f58bf06 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css @@ -176,7 +176,7 @@ } /* caption */ -caption { +.tablesorter-grey > caption { background-color: #fff; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css index 71e0fcb..75dccbc 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css @@ -137,7 +137,7 @@ } /* caption */ -caption { +.tablesorter-ice > caption { background-color: #fff; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css index 5d9ee29..dde56ec 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css @@ -89,7 +89,7 @@ } /* caption */ -.tablesorter-jui caption { +.tablesorter-jui > caption { border: 0; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.materialize.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.materialize.css index a9fb113..08a7fc4 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.materialize.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.materialize.css @@ -94,7 +94,7 @@ } /* caption */ -.caption { +.tablesorter-materialize > .caption { background-color: #fff; } From fbe2cc8c2d9eed455728c8ffe96b85188d588e30 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 20 Aug 2018 21:29:09 +0200 Subject: [PATCH 130/138] Update License file --- MIT-LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIT-LICENSE b/MIT-LICENSE index 241c48d..8fd9e87 100644 --- a/MIT-LICENSE +++ b/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright 2016 Jun Lin, Erik-B. Ernst +Copyright 2018 Jun Lin, Erik-B. Ernst Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the From 5dcb4ee07d09037779ae390aa14523fea5ea7d0e Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 3 Sep 2018 23:16:10 +0200 Subject: [PATCH 131/138] Update tablesorter to latest version (2.31.0) --- CHANGELOG.md | 4 +++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 4 +-- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 4 +-- .../jquery.tablesorter.combined.js | 21 +++---------- .../jquery-tablesorter/jquery.tablesorter.js | 4 +-- .../jquery.tablesorter.widgets.js | 17 ++-------- .../widgets/widget-editable.js | 31 +++++++++++-------- .../widgets/widget-pager.js | 4 +-- 10 files changed, 40 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09fdcfb..0f5cbf2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.26.0 (2018-09-03) + +* Upgrade tablesorter to v2.31.0 + #### v1.25.5 (2018-08-20) * Upgrade tablesorter to v2.30.7 diff --git a/README.md b/README.md index 891676c..be40551 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.30.7 (2018-07-10) [documentation] +Current tablesorter version: 2.31.0 (2018-08-27) [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 4500dde..01cc537 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 - MINOR = 25 - TINY = 5 + MINOR = 26 + TINY = 0 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 90f7326..9d53e3b 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 90f7326819c573ee5ab333f4a0672623d7c52e1e +Subproject commit 9d53e3b35b494dc1c2e7d7266cec437fc9f26e39 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index 4c4202c..e7a6559 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin -* updated 2018-03-26 (v2.30.2) +* updated 2018-08-27 (v2.31.0) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -52,7 +52,7 @@ // ], // [ "header1", "header2", ... "headerN" ] // optional // ] - ajaxProcessing: function( /* ajax */ ) { return [ 0, [], null ]; }, + ajaxProcessing: function(data) { return data; }, // output default: '{page}/{totalPages}' // possible variables: {size}, {page}, {totalPages}, {filteredPages}, {startRow}, diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index 206de5a..e1226a0 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,19 +4,10 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-07-10 (v2.30.7)*/ +/*! tablesorter (FORK) - updated 2018-08-27 (v2.31.0)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ -(function(factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - module.exports = factory(require('jquery')); - } else { - factory(jQuery); - } -}(function(jQuery) { - -/*! TableSorter (FORK) v2.30.7 *//* +(function(factory){if (typeof define === 'function' && define.amd){define(['jquery'], factory);} else if (typeof module === 'object' && typeof module.exports === 'object'){module.exports = factory(require('jquery'));} else {factory(jQuery);}}(function(jQuery) { +/*! TableSorter (FORK) v2.31.0 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -40,7 +31,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.30.7', + version : '2.31.0', parsers : [], widgets : [], @@ -6101,6 +6092,4 @@ }); })(jQuery); - -return jQuery.tablesorter; -})); +return jQuery.tablesorter;})); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 2b5e94a..07d7fd3 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.30.7 *//* +/*! TableSorter (FORK) v2.31.0 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.30.7', + version : '2.31.0', parsers : [], widgets : [], diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index e58c42f..8f3edf2 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,18 +4,9 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-07-10 (v2.30.7)*/ +/*! tablesorter (FORK) - updated 2018-08-27 (v2.31.0)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ -(function(factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - module.exports = factory(require('jquery')); - } else { - factory(jQuery); - } -}(function(jQuery) { - +(function(factory){if (typeof define === 'function' && define.amd){define(['jquery'], factory);} else if (typeof module === 'object' && typeof module.exports === 'object'){module.exports = factory(require('jquery'));} else {factory(jQuery);}}(function(jQuery) { /*! Widget: storage - updated 2018-03-18 (v2.30.0) */ /*global JSON:false */ ;(function ($, window, document) { @@ -3188,6 +3179,4 @@ }); })(jQuery); - -return jQuery.tablesorter; -})); +return jQuery.tablesorter;})); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js index 524f3cf..8e3f16d 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js @@ -1,4 +1,4 @@ -/*! Widget: editable - updated 4/4/2017 (v2.28.7) *//* +/*! Widget: editable - updated 2018-08-27 (v2.31.0) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -85,6 +85,16 @@ return cols; }, + trimContent: function( wo, $cell ) { + if ( wo.editable_trimContent ) { + var html = $cell.html(); + if (html.trim() !== html) { + // Add to bypass Firefox issue - see #1570 + $cell.html( html === '' ? ' ' : html ); + } + } + }, + update: function( c, wo ) { var $t, $cells, cellIndex, cellLen, $editable, editableIndex, editableLen, tmp = $( '<div>' ).wrapInner( wo.editable_wrapContent ).children().length || $.isFunction( wo.editable_wrapContent ), @@ -104,6 +114,9 @@ $t = $cells.eq( cellIndex ); if ( tmp && $t.children( 'div, span' ).length === 0 ) { $t.wrapInner( wo.editable_wrapContent ); + if ($t.children().text().trim() === '') { + $t.children().html(' '); + } } $editable = $t.children( 'div, span' ).not( '.' + wo.editable_noEdit ); editableLen = $editable.length; @@ -111,19 +124,11 @@ // make div/span children content editable for ( editableIndex = 0; editableIndex < editableLen; editableIndex++ ) { var $this = $editable.eq( editableIndex ); - if ( wo.editable_trimContent ) { - $this.html( function( i, txt ) { - return $.trim( txt ); - }); - } + tse.trimContent( wo, $this ); $this.prop( 'contenteditable', true ); } } else { - if ( wo.editable_trimContent ) { - $t.html( function( i, txt ) { - return $.trim( txt ); - }); - } + tse.trimContent( wo, $t ); $t.prop( 'contenteditable', true ); } } @@ -160,7 +165,7 @@ column = $this.closest( 'td' ).index(), txt = $this.html(); if ( wo.editable_trimContent ) { - txt = $.trim( txt ); + txt = $.trim( txt === '' ? ' ' : txt ); } // prevent enter from adding into the content $this @@ -194,7 +199,7 @@ txt = $this.html(), column = $this.closest( 'td' ).index(); if ( wo.editable_trimContent ) { - txt = $.trim( txt ); + txt = $.trim( txt === '' ? ' ' : txt ); } if ( e.which === 27 ) { // user cancelled diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index cd0c93f..362e77b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 2018-03-26 (v2.30.2) */ +/*! Widget: Pager - updated 2018-08-27 (v2.31.0) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -91,7 +91,7 @@ // ], // [ "header1", "header2", ... "headerN" ] // optional // ] - pager_ajaxProcessing: function( /* ajax */ ) { return [ 0, [], null ]; }, + pager_ajaxProcessing: function( data ) { return data; }, // css class names of pager arrows pager_css: { From 67715c564cde1ad4267863059a187167216760b2 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Thu, 22 Nov 2018 18:48:58 +0100 Subject: [PATCH 132/138] Update tablesorter to latest version (2.31.1) --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../jquery-tablesorter/jquery.tablesorter.combined.js | 8 +++++--- .../jquery-tablesorter/jquery.tablesorter.js | 6 ++++-- .../jquery-tablesorter/jquery.tablesorter.widgets.js | 2 +- .../jquery-tablesorter/widgets/widget-math.js | 10 ++++++++-- 8 files changed, 25 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f5cbf2..aa13182 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.26.1 (2018-11-22) + +* Upgrade tablesorter to v2.31.1 + #### v1.26.0 (2018-09-03) * Upgrade tablesorter to v2.31.0 diff --git a/README.md b/README.md index be40551..44f75cf 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.31.0 (2018-08-27) [documentation] +Current tablesorter version: 2.31.1 (2018-11-20) [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 01cc537..be0fd5a 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 26 - TINY = 0 + TINY = 1 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index 9d53e3b..fa56764 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit 9d53e3b35b494dc1c2e7d7266cec437fc9f26e39 +Subproject commit fa56764ad5630ed3ab9da4a30fdb8dd34bfbaf1f diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index e1226a0..ceeaa2e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,10 +4,10 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-08-27 (v2.31.0)*/ +/*! tablesorter (FORK) - updated 2018-11-20 (v2.31.1)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory){if (typeof define === 'function' && define.amd){define(['jquery'], factory);} else if (typeof module === 'object' && typeof module.exports === 'object'){module.exports = factory(require('jquery'));} else {factory(jQuery);}}(function(jQuery) { -/*! TableSorter (FORK) v2.31.0 *//* +/*! TableSorter (FORK) v2.31.1 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -31,7 +31,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.31.0', + version : '2.31.1', parsers : [], widgets : [], @@ -328,6 +328,8 @@ ts.applyWidget( table, true ); // if user has supplied a sort list to constructor if ( c.sortList.length > 0 ) { + // save sortList before any sortAppend is added + c.last.sortList = c.sortList; ts.sortOn( c, c.sortList, {}, !c.initWidgets ); } else { ts.setHeadersCss( c ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index 07d7fd3..e072473 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.31.0 *//* +/*! TableSorter (FORK) v2.31.1 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.31.0', + version : '2.31.1', parsers : [], widgets : [], @@ -319,6 +319,8 @@ ts.applyWidget( table, true ); // if user has supplied a sort list to constructor if ( c.sortList.length > 0 ) { + // save sortList before any sortAppend is added + c.last.sortList = c.sortList; ts.sortOn( c, c.sortList, {}, !c.initWidgets ); } else { ts.setHeadersCss( c ); diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 8f3edf2..5955f8c 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-08-27 (v2.31.0)*/ +/*! tablesorter (FORK) - updated 2018-11-20 (v2.31.1)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory){if (typeof define === 'function' && define.amd){define(['jquery'], factory);} else if (typeof module === 'object' && typeof module.exports === 'object'){module.exports = factory(require('jquery'));} else {factory(jQuery);}}(function(jQuery) { /*! Widget: storage - updated 2018-03-18 (v2.30.0) */ diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index 38f6fa5..3c68718 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -1,4 +1,4 @@ -/*! Widget: math - updated 5/3/2017 (v2.28.9) *//* +/*! Widget: math - updated 11/20/2018 (v2.31.1) *//* * Requires tablesorter v2.16+ and jQuery 1.7+ * by Rob Garrison */ @@ -28,10 +28,14 @@ events : ( 'tablesorter-initialized update updateAll updateRows addRows updateCell filterReset ' ) .split(' ').join('.tsmath '), - processText : function( c, $cell ) { + processText : function( c, $cell ) { var tmp, + wo = c.widgetOptions, txt = ts.getElementText( c, $cell, math.getCellIndex( $cell ) ), prefix = c.widgetOptions.math_prefix; + if (wo.math_textAttr) { + txt = $cell.attr(wo.math_textAttr) || txt; + } if ( /</.test( prefix ) ) { // prefix contains HTML; remove it & any text before using formatFloat tmp = $( '<div>' + prefix + '</div>' ).text() @@ -612,6 +616,8 @@ // e.g. '<span class="red">{content}</span>' math_prefix : '', math_suffix : '', + // cell attribute containing the math value to use + math_textAttr : '', // no matching math elements found (text added to cell) math_none : 'N/A', math_event : 'recalculate', From 463d4d1bf62c53b58987c30eb26905b78f72899a Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Sun, 18 Aug 2019 19:12:08 +0200 Subject: [PATCH 133/138] Add metadata to Gemspec --- jquery-tablesorter.gemspec | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/jquery-tablesorter.gemspec b/jquery-tablesorter.gemspec index d8adef5..a3a1310 100644 --- a/jquery-tablesorter.gemspec +++ b/jquery-tablesorter.gemspec @@ -13,6 +13,11 @@ Gem::Specification.new do |s| s.summary = "Simple integration of jquery-tablesorter (Mottie's fork) into the Rails asset pipeline." s.description = "Simple integration of jquery-tablesorter (Mottie's fork) into the Rails asset pipeline." s.license = 'MIT' + s.metadata = { + 'bug_tracker_uri' => 'https://github.com/themilkman/jquery-tablesorter-rails/issues', + 'changelog_uri' => 'https://github.com/themilkman/jquery-tablesorter-rails/blob/master/CHANGELOG.md', + 'source_code_uri' => 'https://github.com/themilkman/jquery-tablesorter-rails' + } s.files = Dir['{vendor,lib}/**/*'] + %w( MIT-LICENSE Rakefile README.md ) From 2f5137f0a41367abbfc104d9b7f5537fed707f14 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Sun, 18 Aug 2019 19:12:37 +0200 Subject: [PATCH 134/138] Update Ruby support in Readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 44f75cf..7dcb31a 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Or install it yourself as: ## Requirements -It should work with Rails 3.2 and higher as well as with ruby 1.9.3 - 2.5.x. +It should work with Rails 3.2 and higher as well as with ruby 1.9.3 - 2.6.x. Each release is always tested with the latest version of both. ## Usage From a72d4fbfa685cad316cef1dd0388753700050077 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Sun, 18 Aug 2019 19:13:01 +0200 Subject: [PATCH 135/138] Update copyright date --- MIT-LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIT-LICENSE b/MIT-LICENSE index 8fd9e87..1a74992 100644 --- a/MIT-LICENSE +++ b/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright 2018 Jun Lin, Erik-B. Ernst +Copyright 2019 Jun Lin, Erik-B. Ernst Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the From 6ec4091cc7afda4227418dc93e99d1ee2fb98789 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Sun, 18 Aug 2019 19:15:13 +0200 Subject: [PATCH 136/138] Update gem deps for Rails 6 and release the kraken --- CHANGELOG.md | 7 +++++++ jquery-tablesorter.gemspec | 5 ++--- lib/jquery-tablesorter/version.rb | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa13182..7f13e7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Changelog === +#### v1.27.0 (2019-08-18) + +* Bump rails dependency to support Rails 6.0.0 +* Add some metadata to gemspec +* Update Readme +* Update copyright + #### v1.26.1 (2018-11-22) * Upgrade tablesorter to v2.31.1 diff --git a/jquery-tablesorter.gemspec b/jquery-tablesorter.gemspec index a3a1310..d06af6b 100644 --- a/jquery-tablesorter.gemspec +++ b/jquery-tablesorter.gemspec @@ -19,9 +19,8 @@ Gem::Specification.new do |s| 'source_code_uri' => 'https://github.com/themilkman/jquery-tablesorter-rails' } - s.files = Dir['{vendor,lib}/**/*'] + %w( MIT-LICENSE Rakefile README.md ) + s.files = Dir['{vendor,lib}/**/*'] + %w[MIT-LICENSE Rakefile README.md] s.required_ruby_version = '>= 1.9.3' - - s.add_dependency 'railties', '>= 3.2', '< 6' + s.add_dependency 'railties', '>= 3.2', '~> 6.0.0' end diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index be0fd5a..1cca385 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 - MINOR = 26 - TINY = 1 + MINOR = 27 + TINY = 0 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end From 61f2097292041669d569015fca282db6de018c72 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <git@black-milk.de> Date: Mon, 7 Oct 2019 21:57:05 +0200 Subject: [PATCH 137/138] Relax railties dependencies; bump version to 1.27.1 --- CHANGELOG.md | 4 ++++ jquery-tablesorter.gemspec | 2 +- lib/jquery-tablesorter/version.rb | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f13e7e..947ab1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog === +#### v1.27.1 (2019-10-07) + +* Fix installing in Rails < 6, remove railties limit constraint for now (fix github#10) + #### v1.27.0 (2019-08-18) * Bump rails dependency to support Rails 6.0.0 diff --git a/jquery-tablesorter.gemspec b/jquery-tablesorter.gemspec index d06af6b..0a1761d 100644 --- a/jquery-tablesorter.gemspec +++ b/jquery-tablesorter.gemspec @@ -22,5 +22,5 @@ Gem::Specification.new do |s| s.files = Dir['{vendor,lib}/**/*'] + %w[MIT-LICENSE Rakefile README.md] s.required_ruby_version = '>= 1.9.3' - s.add_dependency 'railties', '>= 3.2', '~> 6.0.0' + s.add_dependency 'railties', '>= 3.2' end diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 1cca385..3100179 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 27 - TINY = 0 + TINY = 1 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end From c01a45df57973a65e3be3e090dc487f65a7bd134 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" <github@black-milk.de> Date: Thu, 16 Apr 2020 09:56:06 +0200 Subject: [PATCH 138/138] Update tablesorter to latest version (2.31.3) ...plus some general housekeeping --- CHANGELOG.md | 6 ++++ MIT-LICENSE | 2 +- README.md | 4 +-- lib/jquery-tablesorter/version.rb | 2 +- tablesorter | 2 +- .../addons/pager/jquery.tablesorter.pager.js | 4 +-- .../jquery.tablesorter.combined.js | 24 ++++++++------- .../jquery-tablesorter/jquery.tablesorter.js | 6 ++-- .../jquery.tablesorter.widgets.js | 18 ++++++----- .../widgets/widget-columnSelector.js | 30 +++++++++++++++++-- .../widget-filter-formatter-select2.js | 9 +++--- .../widgets/widget-filter.js | 16 +++++----- .../jquery-tablesorter/widgets/widget-math.js | 11 +++---- .../widgets/widget-pager.js | 6 ++-- .../jquery-tablesorter/dragtable.mod.css | 2 +- .../jquery-tablesorter/theme.black-ice.css | 2 +- .../jquery-tablesorter/theme.blue.css | 8 ++--- .../jquery-tablesorter/theme.dark.css | 2 +- .../jquery-tablesorter/theme.default.css | 2 +- .../jquery-tablesorter/theme.dropbox.css | 10 +++---- .../jquery-tablesorter/theme.green.css | 10 +++---- .../jquery-tablesorter/theme.grey.css | 2 +- .../jquery-tablesorter/theme.ice.css | 8 ++--- .../jquery-tablesorter/theme.jui.css | 2 +- .../jquery-tablesorter/theme.metro-dark.css | 2 +- 25 files changed, 114 insertions(+), 76 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 947ab1c..d7031e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ Changelog === +#### v1.27.2 (2020-04-16) + +* Upgrade tablesorter to v2.31.3 +* Update Readme +* Update copyright + #### v1.27.1 (2019-10-07) * Fix installing in Rails < 6, remove railties limit constraint for now (fix github#10) diff --git a/MIT-LICENSE b/MIT-LICENSE index 1a74992..f7e7530 100644 --- a/MIT-LICENSE +++ b/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright 2019 Jun Lin, Erik-B. Ernst +Copyright 2020 Jun Lin, Erik-B. Ernst Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.md b/README.md index 7dcb31a..20370e0 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Simple integration of jQuery tablesorter ([Mottie's fork]) into the asset pipeline. -Current tablesorter version: 2.31.1 (2018-11-20) [documentation] +Current tablesorter version: 2.31.3 (2020-03-03) [documentation] Any issue associated with the js/css files, please report to [Mottie's fork]. @@ -24,7 +24,7 @@ Or install it yourself as: ## Requirements -It should work with Rails 3.2 and higher as well as with ruby 1.9.3 - 2.6.x. +It should work with Rails 3.2 and higher as well as with ruby 1.9.3 - 2.7.x. Each release is always tested with the latest version of both. ## Usage diff --git a/lib/jquery-tablesorter/version.rb b/lib/jquery-tablesorter/version.rb index 3100179..2e88c77 100644 --- a/lib/jquery-tablesorter/version.rb +++ b/lib/jquery-tablesorter/version.rb @@ -1,7 +1,7 @@ module JqueryTablesorter MAJOR = 1 MINOR = 27 - TINY = 1 + TINY = 2 VERSION = [MAJOR, MINOR, TINY].compact.join('.') end diff --git a/tablesorter b/tablesorter index fa56764..7202d5f 160000 --- a/tablesorter +++ b/tablesorter @@ -1 +1 @@ -Subproject commit fa56764ad5630ed3ab9da4a30fdb8dd34bfbaf1f +Subproject commit 7202d5faf8105a5ecd1a2b7a653777618713ffe5 diff --git a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js index e7a6559..1d67c1b 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js @@ -1,6 +1,6 @@ /*! * tablesorter (FORK) pager plugin -* updated 2018-08-27 (v2.31.0) +* updated 2020-03-03 (v2.31.3) */ /*jshint browser:true, jquery:true, unused:false */ ;(function($) { @@ -367,7 +367,7 @@ sz = p.size === 'all' ? p.totalRows : p.size, s = ( p.page * sz ), e = s + sz, - last = 0, // for cache indexing + last = -1, // for cache indexing j = 0; // size counter p.cacheIndex = []; for ( i = 0; i < l; i++ ) { diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js index ceeaa2e..6f910e1 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js @@ -4,10 +4,10 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-11-20 (v2.31.1)*/ +/*! tablesorter (FORK) - updated 2020-03-03 (v2.31.3)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory){if (typeof define === 'function' && define.amd){define(['jquery'], factory);} else if (typeof module === 'object' && typeof module.exports === 'object'){module.exports = factory(require('jquery'));} else {factory(jQuery);}}(function(jQuery) { -/*! TableSorter (FORK) v2.31.1 *//* +/*! TableSorter (FORK) v2.31.3 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -31,7 +31,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.31.1', + version : '2.31.3', parsers : [], widgets : [], @@ -991,7 +991,7 @@ // instead of setting duplicate span to empty string, use textExtraction to try to get a value // see http://stackoverflow.com/q/36449711/145346 txt = c.duplicateSpan || index === 0 ? - val : + txt : typeof c.textExtraction !== 'string' ? ts.getElementText( c, cell, cacheIndex + index ) || '' : ''; @@ -4203,7 +4203,7 @@ event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - tsf.searching( table, eventType !== 'keypress', true, column ); + tsf.searching( table, eventType !== 'keypress' || event.which === tskeyCodes.enter, true, column ); } }); }, @@ -4278,12 +4278,14 @@ } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons - if ( tsf.equalFilters(c, c.lastSearch, currentFilters) && filter !== false ) { - return; - } else if ( filter === false ) { - // force filter refresh - c.lastCombinedFilter = ''; - c.lastSearch = []; + if ( tsf.equalFilters(c, c.lastSearch, currentFilters) ) { + if ( filter !== false ) { + return; + } else { + // force filter refresh + c.lastCombinedFilter = ''; + c.lastSearch = []; + } } // define filter inside it is false filters = filters || []; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js index e072473..fe1baaa 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js @@ -1,4 +1,4 @@ -/*! TableSorter (FORK) v2.31.1 *//* +/*! TableSorter (FORK) v2.31.3 *//* * Client-side table sorting with ease! * @requires jQuery v1.2.6+ * @@ -22,7 +22,7 @@ 'use strict'; var ts = $.tablesorter = { - version : '2.31.1', + version : '2.31.3', parsers : [], widgets : [], @@ -982,7 +982,7 @@ // instead of setting duplicate span to empty string, use textExtraction to try to get a value // see http://stackoverflow.com/q/36449711/145346 txt = c.duplicateSpan || index === 0 ? - val : + txt : typeof c.textExtraction !== 'string' ? ts.getElementText( c, cell, cacheIndex + index ) || '' : ''; diff --git a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js index 5955f8c..567374e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +++ b/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js @@ -4,7 +4,7 @@ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 2018-11-20 (v2.31.1)*/ +/*! tablesorter (FORK) - updated 2020-03-03 (v2.31.3)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ (function(factory){if (typeof define === 'function' && define.amd){define(['jquery'], factory);} else if (typeof module === 'object' && typeof module.exports === 'object'){module.exports = factory(require('jquery'));} else {factory(jQuery);}}(function(jQuery) { /*! Widget: storage - updated 2018-03-18 (v2.30.0) */ @@ -1288,7 +1288,7 @@ event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - tsf.searching( table, eventType !== 'keypress', true, column ); + tsf.searching( table, eventType !== 'keypress' || event.which === tskeyCodes.enter, true, column ); } }); }, @@ -1363,12 +1363,14 @@ } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons - if ( tsf.equalFilters(c, c.lastSearch, currentFilters) && filter !== false ) { - return; - } else if ( filter === false ) { - // force filter refresh - c.lastCombinedFilter = ''; - c.lastSearch = []; + if ( tsf.equalFilters(c, c.lastSearch, currentFilters) ) { + if ( filter !== false ) { + return; + } else { + // force filter refresh + c.lastCombinedFilter = ''; + c.lastSearch = []; + } } // define filter inside it is false filters = filters || []; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js index eeaf81d..90f7af2 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js @@ -1,4 +1,4 @@ -/* Widget: columnSelector (responsive table widget) - updated 2018-03-18 (v2.30.0) *//* +/* Widget: columnSelector (responsive table widget) - updated 2018-08-03 (v2.31.2) *//* * Requires tablesorter v2.8+ and jQuery 1.7+ * by Justin Hallett & Rob Garrison */ @@ -399,6 +399,27 @@ }); } }, + + // Extracted from buildHeaders in core; needed for scroller widget compatibility + findHeaders : function(c) { + var indx, $temp, + sel = '.' + ts.css.scrollerHeader + ' thead > tr > ', + $headers = $(sel + 'th,' + sel + 'td'), + result = []; + for ( indx = 0; indx < c.columns; indx++ ) { + // Use $headers.parent() in case selectorHeaders doesn't point to the th/td + $temp = $headers.filter( '[data-column="' + indx + '"]' ); + // target sortable column cells, unless there are none, then use non-sortable cells + // .last() added in jQuery 1.4; use .filter(':last') to maintain compatibility with jQuery v1.2.6 + result[ indx ] = $temp.length ? + $temp.not( '.sorter-false' ).length ? + $temp.not( '.sorter-false' ).filter( ':last' ) : + $temp.filter( ':last' ) : + $(); + } + return result; + }, + adjustColspans: function(c, wo) { var index, cols, col, span, end, $cell, colSel = c.selector, @@ -409,7 +430,10 @@ .add( $(c.namespace + '_extra_table').children( 'thead, tfoot' ).children().children() ) // include grouping widget headers (they have colspans!) .add( c.$table.find( '.group-header' ).children() ), - len = $headers.length; + len = $headers.length, + $headerIndexed = ts.hasWidget(c.table, 'scroller') + ? tsColSel.findHeaders(c) + : c.$headerIndexed; for ( index = 0; index < len; index++ ) { $cell = $headers.eq(index); col = parseInt( $cell.attr('data-column'), 10 ) || $cell[0].cellIndex; @@ -418,7 +442,7 @@ if ( span > 1 ) { for ( cols = col; cols < end; cols++ ) { if ( !autoModeOn && colSel.states[ cols ] === false || - autoModeOn && c.$headerIndexed[ cols ] && !c.$headerIndexed[ cols ].is(':visible') ) { + autoModeOn && $headerIndexed[ cols ] && !$headerIndexed[ cols ].is(':visible') ) { span--; } } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js index a910406..46bee72 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-select2.js @@ -1,4 +1,4 @@ -/*! Widget: filter, select2 formatter function - updated 1/18/2018 (v2.29.4) *//* +/*! Widget: filter, select2 formatter function - updated 12/1/2019 (v2.31.2) *//* * requires: jQuery 1.7.2+, tableSorter (FORK) 2.16+, filter widget 2.16+ and select2 v3.4.6+ plugin (this code is NOT compatible with select2 v4+) */ @@ -65,17 +65,18 @@ v = v.join('\u0000'); } // escape special regex characters (http://stackoverflow.com/a/9310752/145346) - v = v.replace(/[-[\]{}()*+?.,/\\^$|#\s]/g, '\\$&'); + var v_escape = v.replace(/[-[\]{}()*+?.,/\\^$|#]/g, '\\$&'); // convert string back into an array if (arry) { v = v.split('\u0000'); + v_escape = v_escape.split('\u0000'); } if (!ts.isEmptyObject($cell.find('.select2').data())) { $input // add regex, so we filter exact numbers .val( - $.isArray(v) && v.length && v.join('') !== '' ? - '/(' + matchPrefix + (v || []).join(matchSuffix + '|' + matchPrefix) + matchSuffix + ')/' + flags : + $.isArray(v_escape) && v_escape.length && v_escape.join('') !== '' ? + '/(' + matchPrefix + (v_escape || []).join(matchSuffix + '|' + matchPrefix) + matchSuffix + ')/' + flags : '' ) .trigger('search'); diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js index 99a8515..c6953ac 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js @@ -887,7 +887,7 @@ event.preventDefault(); // init search with no delay $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); - tsf.searching( table, eventType !== 'keypress', true, column ); + tsf.searching( table, eventType !== 'keypress' || event.which === tskeyCodes.enter, true, column ); } }); }, @@ -962,12 +962,14 @@ } // return if the last search is the same; but filter === false when updating the search // see example-widget-filter.html filter toggle buttons - if ( tsf.equalFilters(c, c.lastSearch, currentFilters) && filter !== false ) { - return; - } else if ( filter === false ) { - // force filter refresh - c.lastCombinedFilter = ''; - c.lastSearch = []; + if ( tsf.equalFilters(c, c.lastSearch, currentFilters) ) { + if ( filter !== false ) { + return; + } else { + // force filter refresh + c.lastCombinedFilter = ''; + c.lastSearch = []; + } } // define filter inside it is false filters = filters || []; diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js index 3c68718..3199a2e 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js @@ -1,4 +1,4 @@ -/*! Widget: math - updated 11/20/2018 (v2.31.1) *//* +/*! Widget: math - updated 12/1/2019 (v2.31.2) *//* * Requires tablesorter v2.16+ and jQuery 1.7+ * by Rob Garrison */ @@ -28,7 +28,7 @@ events : ( 'tablesorter-initialized update updateAll updateRows addRows updateCell filterReset ' ) .split(' ').join('.tsmath '), - processText : function( c, $cell ) { + processText : function( c, $cell ) { var tmp, wo = c.widgetOptions, txt = ts.getElementText( c, $cell, math.getCellIndex( $cell ) ), @@ -110,7 +110,7 @@ // stop calculating 'above', when encountering another 'above' if ( mathAbove ) { index = 0; - } else if ( $t.length ) { + } else if ( $t.length && $t.not( mathIgnore ).length ) { arry[ arry.length ] = math.processText( c, $t ); } } @@ -132,7 +132,7 @@ }); if ( ( hasFilter || !$tr.hasClass( filtered ) ) && $tr.not( mathIgnore ).length && - $t.length ) { + $t.length && $t.not( mathIgnore ) ) { arry[ arry.length ] = math.processText( c, $t ); } } @@ -149,7 +149,8 @@ }); if ( ( hasFilter || !$tr.hasClass( filtered ) ) && $t.not( mathAttrs.join( ',' ) ).length && - !$t.is( $el ) ) { + !$t.is( $el ) && $t.not( mathIgnore ).length + ) { arry[ arry.length ] = math.processText( c, $t ); } } diff --git a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js index 362e77b..0ce0388 100644 --- a/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +++ b/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js @@ -1,4 +1,4 @@ -/*! Widget: Pager - updated 2018-08-27 (v2.31.0) */ +/*! Widget: Pager - updated 2020-03-03 (v2.31.3) */ /* Requires tablesorter v2.8+ and jQuery 1.7+ * by Rob Garrison */ @@ -665,14 +665,14 @@ sz = p.size === 'all' ? p.totalRows : p.size, start = ( p.page * sz ), end = start + sz, - last = 0, // for cache indexing + last = -1, // for cache indexing size = 0; // size counter p.cacheIndex = []; for ( tbodyIndex = 0; tbodyIndex < tbodyLen; tbodyIndex++ ) { $rows = c.$tbodies.eq( tbodyIndex ).children( 'tr' ); len = $rows.length; lastIndex = 0; - last = 0; // for cache indexing + last = -1; // for cache indexing size = 0; // size counter for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { if ( !p.regexFiltered.test( $rows[ rowIndex ].className ) ) { diff --git a/vendor/assets/stylesheets/jquery-tablesorter/dragtable.mod.css b/vendor/assets/stylesheets/jquery-tablesorter/dragtable.mod.css index 6cb7d02..e8b59d3 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/dragtable.mod.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/dragtable.mod.css @@ -47,7 +47,7 @@ visibility: hidden; } .table-handle, .table-handle-disabled { - /* background-image: url(/assets/jquery-tablesorter/dragtable-handle.png); */ + /* background-image: url(images/dragtable-handle.png); */ /* background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAANAQMAAAC5Li2yAAAABlBMVEUAAAAzMzPI8eYgAAAAAnRSTlMAzORBQ6MAAAAOSURBVAjXYwABByyYAQAQWgFBLN2RnwAAAABJRU5ErkJggg=='); */ background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyIiBoZWlnaHQ9IjEzIj48cmVjdCBzdHlsZT0iZmlsbDojMzMzO2ZpbGwtb3BhY2l0eTouODsiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHg9IjEiIHk9IjIiLz4JPHJlY3Qgc3R5bGU9ImZpbGw6IzMzMztmaWxsLW9wYWNpdHk6Ljg7IiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4PSIxIiB5PSI0Ii8+CTxyZWN0IHN0eWxlPSJmaWxsOiMzMzM7ZmlsbC1vcGFjaXR5Oi44OyIgd2lkdGg9IjEiIGhlaWdodD0iMSIgeD0iMSIgeT0iNiIvPjxyZWN0IHN0eWxlPSJmaWxsOiMzMzM7ZmlsbC1vcGFjaXR5Oi44OyIgd2lkdGg9IjEiIGhlaWdodD0iMSIgeD0iMSIgeT0iOCIvPjxyZWN0IHN0eWxlPSJmaWxsOiMzMzM7ZmlsbC1vcGFjaXR5Oi44OyIgd2lkdGg9IjEiIGhlaWdodD0iMSIgeD0iMSIgeT0iMTAiLz48L3N2Zz4=); background-repeat: repeat-x; diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css index 6e7443b..51cde9f 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css @@ -81,7 +81,7 @@ .tablesorter-blackice .tablesorter-processing { background-position: center center !important; background-repeat: no-repeat !important; - /* background-image: url(/assets/jquery-tablesorter/loading.gif) !important; */ + /* background-image: url(images/loading.gif) !important; */ background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css index f27fe9f..da90a81 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css @@ -41,7 +41,7 @@ /* white (unsorted) double arrow */ /* background-image: url(data:image/gif;base64,R0lGODlhFQAJAIAAAP///////yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==); */ /* image */ - /* background-image: url(/assets/jquery-tablesorter/black-unsorted.gif); */ + /* background-image: url(images/black-unsorted.gif); */ background-repeat: no-repeat; background-position: center right; padding: 4px 18px 4px 4px; @@ -57,7 +57,7 @@ /* white asc arrow */ /* background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7); */ /* image */ - /* background-image: url(/assets/jquery-tablesorter/black-asc.gif); */ + /* background-image: url(images/black-asc.gif); */ } .tablesorter-blue .headerSortDown, .tablesorter-blue .tablesorter-headerSortDown, @@ -68,7 +68,7 @@ /* white desc arrow */ /* background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7); */ /* image */ - /* background-image: url(/assets/jquery-tablesorter/black-desc.gif); */ + /* background-image: url(images/black-desc.gif); */ } .tablesorter-blue thead .sorter-false { background-image: none; @@ -118,7 +118,7 @@ .tablesorter-blue .tablesorter-processing { background-position: center center !important; background-repeat: no-repeat !important; - /* background-image: url(/assets/jquery-tablesorter/loading.gif) !important; */ + /* background-image: url(images/loading.gif) !important; */ background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css index 73ca661..91c0660 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css @@ -80,7 +80,7 @@ .tablesorter-dark .tablesorter-processing { background-position: center center !important; background-repeat: no-repeat !important; - /* background-image: url(/assets/jquery-tablesorter/loading.gif) !important; */ + /* background-image: url(images/loading.gif) !important; */ background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css index 63e6f23..f3d271d 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css @@ -82,7 +82,7 @@ Default Theme .tablesorter-default .tablesorter-processing { background-position: center center !important; background-repeat: no-repeat !important; - /* background-image: url(/assets/jquery-tablesorter/loading.gif) !important; */ + /* background-image: url(images/loading.gif) !important; */ background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css index eb5e7b4..300db96 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css @@ -58,22 +58,22 @@ .tablesorter-dropbox .tablesorter-headerSortUp i.tablesorter-icon, .tablesorter-dropbox .tablesorter-headerAsc i.tablesorter-icon { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAALhJREFUeNpi/P//PwMhwILMiexYx8bIxNTy/9+/muUVQb9g4kzIitg4edI4+YRLQTSyOCPMupjerUI8whK3OXgEhH58+fDuy9sXqkuKvd+hmMTOxdvCxS8sxMUvxACiQXwU6+Im7DDg5BNKY+fiY2BmYWMA0SA+SByuiJ2bbzIHrwAzMxsb0AGMDCAaxAeJg+SZ7wtaqfAISfQAdTIwMUM8ywhUyMTEzPD/71+5FXvPLWUkJpwAAgwAZqYvvHStbD4AAAAASUVORK5CYII='); - /* background-image: url(/assets/jquery-tablesorter/dropbox-asc.png); */ + /* background-image: url(images/dropbox-asc.png); */ } .tablesorter-dropbox .tablesorter-headerSortUp:hover i.tablesorter-icon, .tablesorter-dropbox .tablesorter-headerAsc:hover i.tablesorter-icon { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAALVJREFUeNpi/P//PwMhwILMCc+qZGNkYmr5/+9fzcpp7b9g4kzIitjYOdM4uXlLQTSyOCPMuqi8OiEefsHbHFzcQj++fX335eN71WWTmt6hmMTOwdXCycMnBDSJAUSD+CjWxRQ0GHBw86Sxc3AyMDOzMIBoEB8kDlfEzsk1mYOLByjPCnQAIwOIBvFB4iB55rsfmVS4+QV7QNYwMTNDHApUyMTExPDv/z+5Feu3L2UkJpwAAgwA244u+I9CleAAAAAASUVORK5CYII='); - /* background-image: url(/assets/jquery-tablesorter/dropbox-asc-hovered.png); */ + /* background-image: url(images/dropbox-asc-hovered.png); */ } .tablesorter-dropbox .tablesorter-headerSortDown i.tablesorter-icon, .tablesorter-dropbox .tablesorter-headerDesc i.tablesorter-icon { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAALdJREFUeNpi/P//PwMhwBLdtVGFhZ3zNhMzC4bkv79/GP78/K7KCDIpZ9mVw+xcfDaMTExwBf///WP4+e3TkSlROrZg7UxMLLns3HxnmFnZmGGK/v7+9ff3j2+5YHkQMSlC48Kv719m/f//D2IKkAbxQeJwRSDw4/OHmr+/fr0DqmAA0SA+TA6uaEq0zjugG+r//vkFcks9iA/3HbJvvn18O+vf379yP758mMXAoAAXZyQmnAACDADX316BiTFbMQAAAABJRU5ErkJggg=='); - /* background-image: url(/assets/jquery-tablesorter/dropbox-desc.png); */ + /* background-image: url(images/dropbox-desc.png); */ } .tablesorter-dropbox .tablesorter-headerSortDown:hover i.tablesorter-icon, .tablesorter-dropbox .tablesorter-headerDesc:hover i.tablesorter-icon { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAALNJREFUeNpi/P//PwMhwBJf3uP879e3PUzMzBiS//7+ZWBi43JhBJmU2z7nIzMzEx8jIyNcAUj8799/nyZXpvCzgARYuXjTWBkZVjCzIEz7++cvw+//DGkgNiPMTWVT1l5hZvynDTINbMp/pqtdOcE6IDkmmM5fv3/5//v37z9QBQOIBvFhcnBFEwoj7/5jZFnz9+8fBhAN4sN9h+ybH9++JrGxscr/+vE1CVmckZhwAggwANvlUyq5Dd1wAAAAAElFTkSuQmCC'); - /* background-image: url(/assets/jquery-tablesorter/dropbox-desc-hovered.png); */ + /* background-image: url(images/dropbox-desc-hovered.png); */ } .tablesorter-dropbox thead .sorter-false { cursor: default; @@ -110,7 +110,7 @@ .tablesorter-dropbox .tablesorter-processing { background-position: center center !important; background-repeat: no-repeat !important; - /* background-image: url(/assets/jquery-tablesorter/loading.gif) !important; */ + /* background-image: url(images/loading.gif) !important; */ background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css index a8f55e5..f6f1f4c 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css @@ -22,7 +22,7 @@ background-position: center center; background-repeat: repeat-x; background-image: url(data:image/gif;base64,R0lGODlhAQBkAOYAAN/e39XU1fX19tTU1eXm5uTl5ePk5OLj4+Hi4vX29fT19PP08/Lz8vHy8fDx8O/w7+7v7uzt7Orr6ufo5/T08/Pz8ufn5uLi4eDg39/f3t3d3Nzc29HR0NDQz8/Pzuvq6urp6eno6Ojn5+fm5tfW1tbV1dTT09PS0tLR0dHQ0NDPz/f39/b29vX19fT09PPz8/Ly8vHx8e/v7+7u7u3t7ezs7Ovr6+rq6unp6ejo6Ofn5+bm5uXl5eTk5OPj4+Li4uHh4eDg4N/f397e3t3d3dzc3Nvb29ra2tnZ2djY2NfX19XV1dPT09LS0tHR0dDQ0M/Pz8rKysXFxf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAFMALAAAAAABAGQAAAdegCsrLC0tLi+ILi6FCSwsCS0KkhQVDA0OMjM0NTYfICEiIzw9P0AYGUQaG0ZHSEoDTU9Qs08pTk1MSyRJR0VDQT8+PTw7Ojg3NTMyMTAvi4WOhC0vMTI1OT9GTlFSgQA7); - /* background-image: url(/assets/jquery-tablesorter/green-header.gif); */ + /* background-image: url(images/green-header.gif); */ } .tablesorter-green th, .tablesorter-green thead td { @@ -36,7 +36,7 @@ background-position: 5px center; background-repeat: no-repeat; background-image: url(data:image/gif;base64,R0lGODlhEAAQAOYAAA5NDBBYDlWWUzRUM5DVjp7inJ/fnQ1ECiCsGhyYFxqKFRFdDhBXDQxCCiO8HSK2HCCqGh2aGByUFxuPFhqNFhmHFRZ2EhVvERRpEBBVDSS8HiGyHB+mGh6fGRuTFxiAFBd5Eww/Cgs5CRp7Fiu+JRx8GCy/JjHAKyynKCuhJzXCMDbCMDnDMyNuHz3EODy9N0LFPSl7JkvIRjycOFDKS1LKTVPLT1XLUFTCT17OWTBkLmbQYnDTbHXVcXnWdoXago/djGmUZ112XCJEIEdjRf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAEUALAAAAAAQABAAAAdlgEWCg4SFhoIvh4cVLECKhCMeJjwFj0UlEwgaMD4Gii0WFAkRHQ47BIY6IQAZDAwBCyAPOJa1toRBGBAwNTY3OT0/AoZCDQoOKi4yNDOKRCIfGycrKZYDBxIkKLZDFxy3RTHgloEAOw==); - /* background-image: url(/assets/jquery-tablesorter/green-unsorted.gif); */ + /* background-image: url(images/green-unsorted.gif); */ border-collapse: collapse; white-space: normal; cursor: pointer; @@ -45,13 +45,13 @@ .tablesorter-green thead .tablesorter-headerSortUp .tablesorter-header-inner, .tablesorter-green thead .tablesorter-headerAsc .tablesorter-header-inner { background-image: url(data:image/gif;base64,R0lGODlhEAAQANUAAA5NDBBYDpDVjp7inJ/fnSCsGhyYFxFdDhBXDSO8HSK2HB2aGBuPFhqNFhmHFRZ2EhBVDSS8Hh6fGRuTFxd5Eww/Chp7Fhx8GCy/JjnDMyNuHzy9N0LFPVTCTzBkLmbQYnDTbHnWdo/djP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACMALAAAAAAQABAAAAY4wJFwSCwaj8ikcslMbpojR0bEtEwwoIHywihEOCECUvNoGBaSxEdg9FQAEAQicKAoOtC8fs8fBgEAOw==) - /* background-image: url(/assets/jquery-tablesorter/green-asc.gif); */ + /* background-image: url(images/green-asc.gif); */ } .tablesorter-green thead .headerSortDown .tablesorter-header-inner, .tablesorter-green thead .tablesorter-headerSortDown .tablesorter-header-inner, .tablesorter-green thead .tablesorter-headerDesc .tablesorter-header-inner { background-image: url(data:image/gif;base64,R0lGODlhEAAQANUAAFWWUzRUMw1EChqKFQxCCiO8HSCqGhyUFxVvERRpECGyHB+mGhiAFAs5CSu+JTHAKyynKCuhJzXCMDbCMD3EOELFPSl7JkvIRjycOFDKS1LKTVPLT1XLUF7OWXXVcYXagmmUZ112XCJEIEdjRf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACQALAAAAAAQABAAAAY4QJJwSCwaj8ikcskkghKGimbD6Xg+AGOIMChIKJcMBjlqMBSPSUQZEBwcEKYIsWiSLPa8fs9HBgEAOw==) - /* background-image: url(/assets/jquery-tablesorter/green-desc.gif); */ + /* background-image: url(images/green-desc.gif); */ } .tablesorter-green th.tablesorter-header .tablesorter-header-inner, .tablesorter-green td.tablesorter-header .tablesorter-header-inner { @@ -101,7 +101,7 @@ .tablesorter-green .tablesorter-processing { background-position: center center !important; background-repeat: no-repeat !important; - /* background-image: url(/assets/jquery-tablesorter/loading.gif) !important; */ + /* background-image: url(images/loading.gif) !important; */ background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css index f58bf06..a0baaee 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css @@ -134,7 +134,7 @@ .tablesorter-grey .tablesorter-processing { background-position: center center !important; background-repeat: no-repeat !important; - /* background-image: url(/assets/jquery-tablesorter/loading.gif) !important; */ + /* background-image: url(images/loading.gif) !important; */ background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css index 75dccbc..bffa0ac 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css @@ -39,7 +39,7 @@ background-position: center right; background-repeat: no-repeat; background-image: url(data:image/gif;base64,R0lGODlhDAAMAMQAAAJEjAJCiwJBigJAiANFjgNGjgNEjQRIkQRHkANIkAVMlAVQmAZWnQZUnAdYoAhdpAhZoAlhqQlepQliqQppsApmrQxutgtutQtutAxwtwxwtg1yug1zugxtsw1yuP8A/yH5BAEAAB8ALAAAAAAMAAwAAAUx4Cd+3GiOW4ado2d9VMVm1xg9ptadTsP+QNZEcjoQTBDGCAFgLRSfQgCYMAiCn8EvBAA7); - /* background-image: url(/assets/jquery-tablesorter/ice-unsorted.gif) */ + /* background-image: url(images/ice-unsorted.gif) */ padding: 4px 20px 4px 4px; white-space: normal; cursor: pointer; @@ -52,7 +52,7 @@ background-position: center right; background-repeat: no-repeat; background-image: url(data:image/gif;base64,R0lGODlhDAAMANUAAAJCiwNHkANFjgNEjQRIkQNJkQRMlARKkwRKkgVPlwZSmgdaogdYnwhfpghcowlhqgliqglgqAlgpwljqwporwpmrQplrAtsswtqsgtrsgtqsQxttAtvtQtttAxyuQxwtwxxtwxvtg10uw1zuQ1xuP8A/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACUALAAAAAAMAAwAAAY6wJKwJBoahyNQ6Dj0fDoZCpPEuWgqk4jxs8FQLI+Gg8Esm5kQydFQMC7IwkOAqUiUCAIzIjA4lwBlQQA7); - /* background-image: url(/assets/jquery-tablesorter/ice-desc.gif) */ + /* background-image: url(images/ice-desc.gif) */ } .tablesorter-ice .headerSortDown, .tablesorter-ice .tablesorter-headerSortDown, @@ -62,7 +62,7 @@ background-position: center right; background-repeat: no-repeat; background-image: url(data:image/gif;base64,R0lGODlhDAAMANUAAAE/iAJBigNFjgNEjQNFjQNDiwRHkQRHjwNHjwROlgRMlQRMlARJkgRKkgZQmAVPlgZWnQZSmgZRmAdXoAdXnwdUnAdbogdZoQhbowlhqAlepglkrAliqQtstAtqsQxyugxyuQxwuAxxuAxxtwxwtgxvtQ10vA12vA10u/8A/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACkALAAAAAAMAAwAAAY6wJQwdRoah6bP6DhEiVIdDxNEGm4yxlDpiJkwv2AmR2OhVCSJBsJ4gUQeCwOB6VAwBAXwYRAIpwBfQQA7); - /* background-image: url(/assets/jquery-tablesorter/ice-asc.gif); */ + /* background-image: url(images/ice-asc.gif); */ } .tablesorter-ice thead .sorter-false { background-image: none; @@ -95,7 +95,7 @@ .tablesorter-ice .tablesorter-processing { background-position: center center !important; background-repeat: no-repeat !important; - /* background-image: url(/assets/jquery-tablesorter/loading.gif) !important; */ + /* background-image: url(images/loading.gif) !important; */ background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css index dde56ec..6dd9bac 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css @@ -72,7 +72,7 @@ .tablesorter-jui .tablesorter-processing .tablesorter-header-inner { background-position: center center !important; background-repeat: no-repeat !important; - /* background-image: url(/assets/jquery-tablesorter/loading.gif) !important; */ + /* background-image: url(images/loading.gif) !important; */ background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=') !important; } diff --git a/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css b/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css index fd3511b..461c2db 100644 --- a/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css +++ b/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css @@ -80,7 +80,7 @@ Metro Dark Theme .tablesorter-metro-dark .tablesorter-processing { background-position: center center !important; background-repeat: no-repeat !important; - /* background-image: url(/assets/jquery-tablesorter/loading.gif) !important; */ + /* background-image: url(images/loading.gif) !important; */ background-image: url(data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=) !important; }