From 2cf8c3e27ed711d7e6d06927b41c1c1fe8ac11f3 Mon Sep 17 00:00:00 2001 From: Erik Ernst 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 += ''; } - $(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 = '' + - (exception ? exception.message + ' (' + exception.name + ')' : 'No rows found') + '', - result = c.ajaxProcessing(data) || [ 0, [] ], - d = result[1] || [], - l = d.length, - th = result[2]; - if ( l > 0 ) { - for ( i = 0; i < l; i++ ) { - tds += ''; - for ( j = 0; j < d[i].length; j++ ) { - // build tbody cells - tds += '' + d[i][j] + ''; - } - tds += ''; - } - } - // 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 = $('' + ( + 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 + ']' ) + '') + .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 += ''; + for ( j = 0; j < d[i].length; j++ ) { + // build tbody cells + tds += '' + d[i][j] + ''; + } + tds += ''; + } + } + // 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 ? '' : ''; // 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 = $(''), - $cgo = c.$table.find('colgroup'), - n = c.columns.length, - overallWidth = c.$table.width(); - $("tr:first td", table.tBodies[0]).each(function(i) { - $c = $(''); - 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 = $(''), + overallWidth = $(table).width(); + $(table.tBodies[0]).find("tr:first").children("td").each(function() { + colgroup.append($('').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 = $('x')[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(''); 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 = $('') + .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) { + $('
') + .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! + $('') + .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) { + $('
') + .appendTo($shcell) + .find('.toggle') + .bind('change', function(){ + $cell.find('.toggle')[0].checked = this.checked; + updateSpinner(); + }); + } + // add a jQuery UI spinner! + $('') + .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 = $('') + .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(''); + } + + // 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! + $('
') + .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! + $('
') + .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 = $('') + .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(''); + } + + // 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); } + }; + $('
') + .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! + $('
') + .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 = $('') + .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 = ''; + $cell.append(l) + .find('.compare') + .bind('change', function(){ + updateCompare($(this).val()); + }); + } else if (o.cellText) { + l = ''; + $cell.append(l); + } + + t = ''; + $(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 = $('') + .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 = ''; + $(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 = $('').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 ? '
' : ''; + } + + if (o.compareOptions) { + l = ''; + $cell.append(l) + .find('.compare') + .bind('change', function(){ + updateCompare($(this).val()); + }); + } else { + if (l) + $cell.append(l); + } + + if (numberSupported) { + t = ''; + // add HTML5 number (spinner) + $cell + .append(t + '') + .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"]') : $(''); + }, + + /**********************\ + 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 = $('').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('') + .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(''); + + $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('') + .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"]') : $(''); + }, + + /**********************\ + 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 = $('').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 = '
'; + t += o.addToggle ? '
' : ''; + t += ''; + t += (o.valueToHeader ? '' : '(#000000)') + '
'; + $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(''); + } + + $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"]') : $(''); + } + +}; + +})(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 (/= 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 = ''; + t = $ths.filter('[data-column="' + i + '"]:last'); + // t.data('placeholder') won't work in jQuery older than 1.4.3 + o = ''; 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 += ''; + t = arry[k].replace(/\"/g, """); + // replace quotes - fixes #242 & ignore empty strings - see http://stackoverflow.com/q/14990971/145346 + o += arry[k] !== '' ? '' : ''; } $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 = ''; // build filter row + // build filter row + t = ''; + for (i = 0; i < cols; i++){ + t += ''; + } + c.$filters = $(t += '').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 += ''; - if (sel){ - t += '').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 ? '>' : '>') + ''; } - $t.find('thead').eq(0).append(t += ''); } $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 === '' ? '' : ''; + ff += ff === '' ? '' : ''; ff += ''; } } @@ -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('
'); } - $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 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 -[![Still Maintained](http://stillmaintained.com/linjunpop/jquery-tablesorter-rails.png)](http://stillmaintained.com/linjunpop/jquery-tablesorter-rails) [![Gem Version](https://badge.fury.io/rb/jquery-tablesorter.png)](http://badge.fury.io/rb/jquery-tablesorter) -[![endorse](http://api.coderwall.com/linjunpop/endorsecount.png)](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 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 += ''; } - 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(''); + if ( d > 5 && $.data(table, 'pagerLastSize') === p.size && $b.children('tr:visible').length < p.size ) { + $b.append(''); } } } }, - 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 = $('' + ( + $err = $('' + ( 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 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 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 ? '' : ''; // add icon if cssIcon option exists + i = c.cssIcon ? '' : ''; // 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 = $(''), 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($('').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 = $('x')[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 = $('x')[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 = $('') .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 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 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] !== '' ? '' : ''; } - $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 += ''; } } - $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 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 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 = '
' + t + '
'; // faster than wrapInner + $(this).html('
' + t + '
'); // 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 = ''; From be84de2cef921da7194467b04d44d1b1c949033c Mon Sep 17 00:00:00 2001 From: Erik Ernst 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 = $('').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 (/ 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 = $('' + ( 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 += ''; - for ( j = 0; j < d[i].length; j++ ) { - // build tbody cells - tds += '' + d[i][j] + ''; - } - tds += ''; + for ( i = 0; i < l; i++ ) { + tds += ''; + for ( j = 0; j < d[i].length; j++ ) { + // build tbody cells + tds += '' + d[i][j] + ''; } + tds += ''; } // 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('
'); } 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 ( /= 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 (/= 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 = ''; - 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 === '' ? + '' : ''; + options += ''; } } + 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] !== '' ? '' : ''; - } - $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 = ''; + for (column = 0; column < columns; column++) { + buildFilter += ''; + } + c.$filters = $(buildFilter += '').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 = $('').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 = ''; - for (i = 0; i < cols; i++){ - t += ''; + 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 += '').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 = $('').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 === '' ? '' : ''; - ff += ''; - } + // 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 = ''; + 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] !== '' ? '' : ''; + } + 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 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 ? '' : ''; // add icon if cssIcon option exists + // add icon if cssIcon option exists + i = c.cssIcon ? '' : ''; 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('
'); + if (!$headers.find('.tablesorter-wrapper').length) { + // Firefox needs this inner div to position the resizer correctly + $headers.wrapInner('
'); } - if (c.cssIcon){ + if (c.cssIcon) { // if c.cssIcon is '', then no 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('
'); + $column.wrapInner('
'); } // 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 = '
'; - $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('
'); }) - .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 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 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 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 += ''; for ( j = 0; j < d[i].length; j++ ) { - // build tbody cells - tds += '' + d[i][j] + ''; + // build tbody cells; watch for data containing HTML markup - see #434 + tds += /^\s*' + d[i][j] + ''; } tds += ''; } @@ -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" 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 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" 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 += ''; } // 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" 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" 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 = $('' + ( + 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 + ']' ) + '') - .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 += ''; } // 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) : $('' + message + '') ) + .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('
' + t + '
'); // faster than wrapInner + $(this).html('
' + t + '
'); // 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 ? '' : ''; + $.each(options.compare, function(i, c){ + opt += ''; + }); + $cell + .wrapInner('
') + .prepend( txt + '') @@ -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) { - $('
') + $('
' + + '
') .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) { - $('
') + $('
' + + '
') .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 = $('') @@ -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! - $('
') - .val(o.value) - .appendTo($shcell) - .slider(o) - .bind('change keyup', function(){ - $cell.find('.slider').val( this.value ); - updateSlider(); - }); + // add a jQuery UI slider! + $('
') + .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 = $('') .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 = $('') .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 = ''; - $cell.append(l) - .find('.compare') - .bind('change', function(){ - updateCompare($(this).val()); - }); - } else if (o.cellText) { - l = ''; - $cell.append(l); - } - - t = ''; - $(t).appendTo($cell); + t = ''; + $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 = $('').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 ? '
' : ''; - } - - if (o.compareOptions) { - l = ''; - $cell.append(l) - .find('.compare') - .bind('change', function(){ - updateCompare($(this).val()); - }); - } else { - if (l) - $cell.append(l); - } - - if (numberSupported) { - t = '' + + '
' : ''; + t += ''; // add HTML5 number (spinner) $cell .append(t + '') .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 = $('').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(''); - - $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 = $('').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 = '
'; - t += o.addToggle ? '
' : ''; - t += ''; - t += (o.valueToHeader ? '' : '(#000000)') + '
'; + t = '
' + + (o.addToggle ? '
' : '') + + '' + + (o.valueToHeader ? '' : '(#000000)') + '
'; $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(''); @@ -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('
'); + $headers.wrapInner('
'); } if (c.cssIcon) { // if c.cssIcon is '', then no 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 += ''; } } - 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 = ''; + buildFilter = ''; for (column = 0; column < columns; column++) { buildFilter += ''; } @@ -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] !== '' ? '' : ''; } - 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('
'); + $column.wrapInner('
'); } // 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('
'); }) .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" 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" 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] !== '' ? '' : ''; } - 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" 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.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 ''; +}; + +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 ''; +}; + +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.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 ''; +}; + +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 ''; +}; + +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. + * HDD Size + * Distance + */ +/*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 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) : $('
').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 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(''); + }); + } 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 += ''; + $.each(widths, function(i, w){ + t += ''; + }); + t += ''; + } + 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 ? $('') : '', + 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 + ''); + // 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 = '' + (wo.build_numbers.addColumn ? '' + wo.build_numbers.addColumn + '' : ''); + $.each(h1, function(i, h) { + t += '' + + (h2 && h2[i] ? h2[i] : h) + ''; + }); + return t + ''; + }, + rows : function(items, txt, c, wo, num, ftr){ + var h = (ftr ? 'th' : 'td'), + t = '' + (wo.build_numbers.addColumn ? '<' + h + '>' + (ftr ? '' : num) + '' : ''); + $.each(items, function(i, item) { + t += '<' + (ftr ? h + (c && c[i] ? ' class="' + c[i] + '"' : '') : h) + '>' + + (ftr && txt && txt.length && txt[i] ? txt[i] : item) + ''; + }); + return t + ''; + } + }; + + 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 ) + ''; + + $.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 += ''; + } + items = csv ? bt.splitCSV( line, wo.build_csvSeparator ) : line; + if (infooter && f > 0) { + tableHTML += (n === len - f ? '' : '') + + (n === len ? '' : ''); + } + 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 ? '' : ''); + 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 = $(''); + $t = $('
' + jqXHR.status + ' ' + textStatus + '
'); + + // 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 = $('').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 = $(''); + // Build tbody + $.each(r, function(i, d){ + t = $.type(d) === 'object'; + // add new tbody + if (t && d.newTbody) { + $tb = $('').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 = $('').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('' + $c + ''); + } else { + $c = $('').appendTo( $t ); + $.each(t, function(i, d) { + $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 || '
') }; + tsColSel.setupSelector(table, c, wo); + + if (wo.columnSelector_mediaquery) { + tsColSel.setupBreakpoints(c, wo); + } + + if (colSel.$container.length) { + colSel.$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 = $('').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(''); + } + 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 : '', + // 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('' + (wo.group_collapsible ? '' : '') + '' + + 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'); + 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 += ''; + } + 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(''); + } + } + } + }, + + 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 += ''; + for ( j = 0; j < d[i].length; j++ ) { + // build tbody cells; watch for data containing HTML markup - see #434 + tds += /^\s*' + d[i][j] + ''; + } + tds += ''; + } + // 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) : $('' + message + '') ) + .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 = ''; + $.each(c.headerContent, function(i,t) { + h += '' + t + ''; + }); + // "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 + ''; + } + + // 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 = ''; + $(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 = $('' + $tbl.find('thead:first').html() + '
'); + $tbl + .wrap('
') + .before($hdr) + .find('.tablesorter-filter-row').addClass('hideme'); + + $cells = $hdr + .wrap('
') + .find('.' + ts.css.header) + .bind('mousedown', function(){ + this.onselectstart = function(){ return false; }; + return false; + }); + + $tbl + .wrap('
') + .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" 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('' + (wo.group_collapsible ? '' : '') + '' + currentGroup + ''); + 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" 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 = $('') .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 = $('') .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 = $('') .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 = $('') .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 = $('') .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(''); - // 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 || '
') }; + colSel.$style = $('').prop('disabled', true).appendTo('head'); + colSel.$breakpoints = $('').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 = $('').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 = $('').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" 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 = $('') .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 = $('') .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 = $('') .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 = $('') .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( + '' + left[rowIndex] + '' + + '' + alignChar + + right[rowIndex].slice(v.align.length) + '' + ); + } + } + 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" 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 += ''; } - c.$filters = $(buildFilter += '').appendTo( c.$table.find('thead').eq(0) ).find('td'); + c.$filters = $(buildFilter += '').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" 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)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)o985veE zvmi|S`{ysi@Ba*GAT}e+O=VNoUSVeZrwuXc1cZKk`S9+`+ZU=! zioM^Vn8eP-+rq^!EX2hx!~mi}d=%^AChRcaJ^%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)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)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?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(?@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_-kiKB_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+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0005VNkl5S^TyoTR$Ah~N^mV2vM0W22%;ZHsz!s3tKQp*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#w2Mzxor2TKajxh!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|pxIjZhYOSqxtNGu5KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?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(?@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_-kiKB_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+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0005ENklqz1L=8w7oWAPQPX zvJ?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{TVK(61DF+&D$r(ku_UV6 y0%U+zYi#~`rd6r~Wf57ieTe11f2K_XehvW8YnqtZvd#|x0000KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?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(?@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_-kiKB_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+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0005nNklL1JNc4WcA<^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 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)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?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(?@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_-kiKB_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+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0008yNklG8{5)rHxr4}RD zMRg~LinuXqaA6_RHna=Vg4o1XB1Fw93n6H!nD|R8H44g1GC%$$lbK9zW-|AxY-v6_{NUPn^w%wgdIQl;~{@o5t0=Iz7tcdCy-bw?3E&lw2hcs(0)l|H=2YG3X?uF7S!p1);NlXt z!}=G^8%qDadb8kUoMO+DFQ5&~AXwC_56xxaUd9@Nl6nztM%1)8m4)tD?_wVbN4>L-8f>^YfB z=dJ-UB_a#5`Rpe>0~ebzQS z2B3<3MX%OXuJ7v}=&l_eeq8A|P8d*gBQ6vQV#WhYvJasee6)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" 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" 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 ? '' : ''; c.$headers = $(table).find(c.selectorHeaders).each(function(index) { @@ -455,8 +461,7 @@ $(this).html('
' + t + '
'); // 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" 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" 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('' + (wo.group_collapsible ? '' : '') + '' + currentGroup + ''); @@ -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" 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 ? '' : ''; 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 = $('x')[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 = $('') + .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(''); + } + + // 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! + $('') + .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! + $('') + .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(''); + } + + }); + + // 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 = ''; + ($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 = $('') .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 = ''; + t = '' + + ''; $(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 === '' ? - '' : ''; + '' : ''; options += ''; } } @@ -715,7 +725,7 @@ ts.filter = { buildFilter = $('').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 = ''; - 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 = '', + 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 = '' + (wo.build_numbers.addColumn ? '' + wo.build_numbers.addColumn + '' : ''); $.each(h1, function(i, h) { - t += '' + - (h2 && h2[i] ? h2[i] : h) + ''; + if (/<\s*\/t(d|h)\s*>/.test(h)) { + t += h; + } else { + t += '' + + (h2 && h2[i] ? h2[i] : h) + ''; + } }); return t + ''; }, @@ -172,8 +178,13 @@ var ts = $.tablesorter = $.tablesorter || {}, var h = (ftr ? 'th' : 'td'), t = '' + (wo.build_numbers.addColumn ? '<' + h + '>' + (ftr ? '' : num) + '' : ''); $.each(items, function(i, item) { - t += '<' + (ftr ? h + (c && c[i] ? ' class="' + c[i] + '"' : '') : h) + '>' + - (ftr && txt && txt.length && txt[i] ? txt[i] : item) + ''; + // test if HTML is already included; look for closing + 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) + ''; + } }); return t + ''; } @@ -372,6 +383,7 @@ var ts = $.tablesorter = $.tablesorter || {}, $tb = $(''); // 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 : /(|\n)/g, // replace + regexIMG : /]+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( + '' + output.popupTitle + '' + + '' + + '' + ); + 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 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 = '' + headers[j][i] + ''); + 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('
') - .find('.' + ts.css.header) - .bind('mousedown', function(){ - this.onselectstart = function(){ return false; }; - return false; - }); + .find('.' + ts.css.header); $tbl .wrap('
') @@ -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 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" 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('
') .find('.' + ts.css.header); - $tbl - .wrap('
') - .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('
') // make scroller header sortable ts.bindEvents(table, $cells); From e162a4f427094e150bb99bd2b631bf6a7fb14517 Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" 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. ) + 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" 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|7*RL%sEWEtDfVy;b zbxll6jE#-e)zyOpeq8{XD^U{U7tC<&w4}=pSw^+cIEHXsFFo$b)S$q_a*%=RSZ%q`zyJQp@4l^^wPsR(OvfDM=HriKqBL$joSm{K zXVT90He-#d&nmaQUaJZJH4i^GW#_qP3J;DgTe~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^?&UREc$ gEEzP#QWe-4B7cis^t{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{7oXg6#sMkxrPKgI|jHK z=@~FHGB7YG{*!VpN=+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?`{gpL$+T8S zx1$Ck6B1@`W8`Hy^>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{#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}iShLSD9Mo*f5ZF4kG4X`v&>jX)S3j3^P6' : ''; - 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 ( /= 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 === '' ? '' : ''; @@ -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 = ''; @@ -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 = $(''; + } 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 ? '' : ''; - 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 = $(''), 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($('').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 === '' ? '' : ''; - options += ''; + val = string; + txt = string; + if (string.indexOf(wo.filter_selectSourceSeparator) >= 0) { + val = string.split(wo.filter_selectSourceSeparator); + txt = val[1]; + val = val[0]; + } + options += ''; } } 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 = ''; + buildFilter = ''; for (column = 0; column < columns; column++) { buildFilter += ''; } @@ -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 = $(''; + } 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" 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" 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 += ''; + // 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 += ''; } - 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('
' + t + '
'); // faster than wrapInner } - $(this).html('
' + t + '
'); // 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 = $(''), - overallWidth = $(table).width(); + var colgroup, overallWidth, + c = table.config; + if (c.widthFixed && c.$table.find('colgroup').length === 0) { + 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($('').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(data.iFilter) ) { result = />=/.test(data.iFilter) ? cachedValue >= query : cachedValue > query; } + if ( /= 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. ) - 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. ) + 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 : '
', // 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 = $('
').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 += ''; } - 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" 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 += ''; - } - p.$goto[0].innerHTML = t; - p.$goto[0].value = current_page; + $.each(buildPageSelect(p), function(i, opt){ + t += ''; + }); + // 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(''); + $b.append(''); } } } }, 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(' '); + } + 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('
' + t + '
'); // 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 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 = ''; for (column = 0; column < columns; column++) { - buildFilter += ''; + if (arry) { + buildFilter += ''; + } else { + buildFilter += ''; + } } c.$filters = $(buildFilter += '').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('
'), + $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 : '
', // 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 = $( '
' ).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 = $('
').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 : '
', // 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 += ''; - } - p.$goto[0].innerHTML = t; - p.$goto[0].value = p.page + 1; + $.each(tsp.buildPageSelect(p, c), function(i, opt){ + t += ''; + }); + // 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(''); + $b.append(''); } } } }, 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(' '); + } + 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 = $('' + $tbl.find('thead:first').html() + '
'); $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" 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 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" 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" 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" 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 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 = $(''); 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 += ''; - } else { + } else { buildFilter += ''; } } @@ -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" 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" 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 ? '' : ''; // 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('
' + t + '
'); // faster than wrapInner + $t.html('
' + t + '
'); // 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('
'), $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 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 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" 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 @@ + + + + + + + \ 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 += ''; + }); + // 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 += ''; - }); - // 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('
') + // add handle class + "-disabled" to drag-disabled columns + .prepend('
'); + } + }); + }, + 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 = '
    '; + 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] = '
  • ' + + '' + + ( captionHeight ? '' : '' ) + + ''; + // thead + headerRows.each(function(j){ + sortableColumn[i] += '' + + row[j].outerHTML + ''; + }); + sortableColumn[i] += ''; + // 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] += '' + + this.outerHTML + ''; + }); + sortableColumn[i] += ''; + + // add footer to end of max Rows + if (!_this.options.excludeFooter) { + sortableColumn[i] += '' + + thtb.filter('tfoot').children('tr:visible').children()[i].outerHTML + ''; + } + sortableColumn[i] += '
  • '; + } + } + sortableHtml += sortableColumn.join('') + '
'; + 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('
'); + }, + 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 '); + $(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 '; $(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 = $('' + - '' + $tbl.find('thead:first').html() + '' + + $tbl.children('thead')[0].outerHTML + '
'); + + $t = $tbl.children('tfoot'); + if ($t.length) { + $ft = $('
') + .append( $t.clone(true) ) // maintain any bindings on the tfoot cells + .append( $tbl.children('thead')[0].outerHTML ) + .wrap('