jquery-tablesorter 1.0.5 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. data/README.markdown +48 -31
  2. data/Rakefile +56 -1
  3. data/lib/jquery-tablesorter/version.rb +1 -1
  4. data/vendor/assets/images/jquery-tablesorter/addons/{icons → pager/icons}/first.png +0 -0
  5. data/vendor/assets/images/jquery-tablesorter/addons/{icons → pager/icons}/last.png +0 -0
  6. data/vendor/assets/images/jquery-tablesorter/addons/pager/icons/loading.gif +0 -0
  7. data/vendor/assets/images/jquery-tablesorter/addons/{icons → pager/icons}/next.png +0 -0
  8. data/vendor/assets/images/jquery-tablesorter/addons/{icons → pager/icons}/prev.png +0 -0
  9. data/vendor/assets/images/jquery-tablesorter/black-asc.gif +0 -0
  10. data/vendor/assets/images/jquery-tablesorter/black-desc.gif +0 -0
  11. data/vendor/assets/images/jquery-tablesorter/black-unsorted.gif +0 -0
  12. data/vendor/assets/images/jquery-tablesorter/dropbox-asc1.png +0 -0
  13. data/vendor/assets/images/jquery-tablesorter/dropbox-asc2.png +0 -0
  14. data/vendor/assets/images/jquery-tablesorter/dropbox-desc1.png +0 -0
  15. data/vendor/assets/images/jquery-tablesorter/dropbox-desc2.png +0 -0
  16. data/vendor/assets/images/jquery-tablesorter/green-asc.png +0 -0
  17. data/vendor/assets/images/jquery-tablesorter/green-desc.png +0 -0
  18. data/vendor/assets/images/jquery-tablesorter/green-header.png +0 -0
  19. data/vendor/assets/images/jquery-tablesorter/green-unsorted.png +0 -0
  20. data/vendor/assets/images/jquery-tablesorter/ice-asc.gif +0 -0
  21. data/vendor/assets/images/jquery-tablesorter/ice-desc.gif +0 -0
  22. data/vendor/assets/images/jquery-tablesorter/ice-unsorted.gif +0 -0
  23. data/vendor/assets/images/jquery-tablesorter/white-asc.gif +0 -0
  24. data/vendor/assets/images/jquery-tablesorter/white-desc.gif +0 -0
  25. data/vendor/assets/images/jquery-tablesorter/white-unsorted.gif +0 -0
  26. data/vendor/assets/javascripts/jquery-tablesorter.js +3 -1
  27. data/vendor/assets/javascripts/jquery-tablesorter/addons/{jquery.tablesorter.pager.js → pager/jquery.tablesorter.pager.js} +507 -419
  28. data/vendor/assets/javascripts/jquery-tablesorter/jquery.metadata.js +53 -85
  29. data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +465 -314
  30. data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.min.js +6 -0
  31. data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +618 -267
  32. data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.min.js +12 -0
  33. data/vendor/assets/stylesheets/jquery-tablesorter/addons/{jquery.tablesorter.pager.css → pager/jquery.tablesorter.pager.css} +15 -14
  34. data/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css +161 -0
  35. data/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css +196 -0
  36. data/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +118 -0
  37. data/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css +162 -0
  38. data/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css +164 -0
  39. data/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css +175 -0
  40. data/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css +174 -0
  41. data/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css +217 -0
  42. data/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css +164 -0
  43. data/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css +128 -0
  44. metadata +44 -14
  45. data/lib/tasks/jquery-tablesorter_tasks.rake +0 -39
  46. data/vendor/assets/images/jquery-tablesorter/addons/icons/loading.gif +0 -0
  47. data/vendor/assets/stylesheets/jquery-tablesorter/blue.css +0 -3
  48. data/vendor/assets/stylesheets/jquery-tablesorter/blue/style.css +0 -128
  49. data/vendor/assets/stylesheets/jquery-tablesorter/ui.css +0 -4
  50. data/vendor/assets/stylesheets/jquery-tablesorter/ui/style.css +0 -67
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * TableSorter 2.4.6 - Client-side table sorting with ease!
3
+ * Minified using UglifyJS (http://jscompress.com/)
4
+ * Copyright (c) 2007 Christian Bach
5
+ */
6
+ !function($){"use strict";$.extend({tablesorter:new function(){function log(a){if(typeof console!=="undefined"&&typeof console.log!=="undefined"){console.log(a)}else{alert(a)}}function benchmark(a,b){log(a+" ("+((new Date).getTime()-b.getTime())+"ms)")}function getElementText(a,b,c){if(!b){return""}var d=a.config,e=d.textExtraction,f="";if(e==="simple"){if(d.supportsTextContent){f=b.textContent}else{f=$(b).text()}}else{if(typeof e==="function"){f=e(b,a,c)}else if(typeof e==="object"&&e.hasOwnProperty(c)){f=e[c](b,a,c)}else{f=d.supportsTextContent?b.textContent:$(b).text()}}return $.trim(f)}function detectParserForColumn(a,b,c,d){var e,f=ts.parsers.length,g=false,h="",i=true;while(h===""&&i){c++;if(b[c]){g=b[c].cells[d];h=getElementText(a,g,d);if(a.config.debug){log("Checking if value was empty on row "+c+", column: "+d+": "+h)}}else{i=false}}for(e=1;e<f;e++){if(ts.parsers[e].is(h,a,g)){return ts.parsers[e]}}return ts.parsers[0]}function buildParserCache(a,b){var c=a.config,d=$(a.tBodies).filter(":not(."+c.cssInfoBlock+")"),e,f,g,h,i,j,k,l="";if(d.length===0){return}e=d[0].rows;if(e[0]){f=[];g=e[0].cells.length;for(h=0;h<g;h++){i=b.filter(':not([colspan])[data-column="'+h+'"]:last,[colspan="1"][data-column="'+h+'"]:last');j=c.headers[h];k=ts.getParserById(ts.getData(i,j,"sorter"));c.empties[h]=ts.getData(i,j,"empty")||c.emptyTo||(c.emptyToBottom?"bottom":"top");c.strings[h]=ts.getData(i,j,"string")||c.stringTo||"max";if(!k){k=detectParserForColumn(a,e,-1,h)}if(c.debug){l+="column:"+h+"; parser:"+k.id+"; string:"+c.strings[h]+"; empty: "+c.empties[h]+"\n"}f.push(k)}}if(c.debug){log(l)}return f}function buildCache(a){var b=a.tBodies,c=a.config,d,e,f=c.parsers,g,h,i,j,k,l,m;c.cache={};if(c.debug){m=new Date}if(c.showProcessing){ts.isProcessing(a,true)}for(j=0;j<b.length;j++){c.cache[j]={row:[],normalized:[]};if(!$(b[j]).hasClass(c.cssInfoBlock)){d=b[j]&&b[j].rows.length||0;e=b[j].rows[0]&&b[j].rows[0].cells.length||0;for(h=0;h<d;++h){k=$(b[j].rows[h]);l=[];if(k.hasClass(c.cssChildRow)){c.cache[j].row[c.cache[j].row.length-1]=c.cache[j].row[c.cache[j].row.length-1].add(k);continue}c.cache[j].row.push(k);for(i=0;i<e;++i){g=getElementText(a,k[0].cells[i],i);l.push(f[i].format(g,a,k[0].cells[i],i))}l.push(c.cache[j].normalized.length);c.cache[j].normalized.push(l)}}}if(c.showProcessing){ts.isProcessing(a)}if(c.debug){benchmark("Building cache for "+d+" rows",m)}}function appendToTable(a,b){var c=a.config,d=a.tBodies,e=[],f=c.cache,g,h,i,j,k,l,m,n,o,p,q,r;if(c.debug){r=new Date}for(o=0;o<d.length;o++){k=$(d[o]);if(!k.hasClass(c.cssInfoBlock)){l=ts.processTbody(a,k,true);g=f[o].row;h=f[o].normalized;i=h.length;j=i?h[0].length-1:0;for(m=0;m<i;m++){q=h[m][j];e.push(g[q]);if(!c.appender||!c.removeRows){p=g[q].length;for(n=0;n<p;n++){l.append(g[q][n])}}}ts.processTbody(a,l,false)}}if(c.appender){c.appender(a,e)}if(c.debug){benchmark("Rebuilt table",r)}if(!b){ts.applyWidget(a)}$(a).trigger("sortEnd",a)}function computeThIndexes(a){var b=[],c={},d=$(a).find("thead:eq(0) tr, tfoot tr"),e,f,g,h,i,j,k,l,m,n,o,p;for(e=0;e<d.length;e++){j=d[e].cells;for(f=0;f<j.length;f++){i=j[f];k=i.parentNode.rowIndex;l=k+"-"+i.cellIndex;m=i.rowSpan||1;n=i.colSpan||1;if(typeof b[k]==="undefined"){b[k]=[]}for(g=0;g<b[k].length+1;g++){if(typeof b[k][g]==="undefined"){o=g;break}}c[l]=o;$(i).attr({"data-column":o});for(g=k;g<k+m;g++){if(typeof b[g]==="undefined"){b[g]=[]}p=b[g];for(h=o;h<o+n;h++){p[h]="x"}}}}return c}function formatSortingOrder(a){return/^d/i.test(a)||a===1}function buildHeaders(a){var b=computeThIndexes(a),c,d,e,f,g,h,i=a.config;i.headerList=[];if(i.debug){g=new Date}h=$(a).find(i.selectorHeaders).each(function(a){d=$(this);c=i.headers[a];e=i.cssIcon?'<i class="'+i.cssIcon+'"></i>':"";this.innerHTML='<div class="tablesorter-header-inner">'+this.innerHTML+e+"</div>";if(i.onRenderHeader){i.onRenderHeader.apply(d,[a])}this.column=b[this.parentNode.rowIndex+"-"+this.cellIndex];this.order=formatSortingOrder(ts.getData(d,c,"sortInitialOrder")||i.sortInitialOrder)?[1,0,2]:[0,1,2];this.count=-1;if(ts.getData(d,c,"sorter")==="false"){this.sortDisabled=true;d.addClass("sorter-false")}else{d.removeClass("sorter-false")}this.lockedOrder=false;f=ts.getData(d,c,"lockedOrder")||false;if(typeof f!=="undefined"&&f!==false){this.order=this.lockedOrder=formatSortingOrder(f)?[1,1,1]:[0,0,0]}d.addClass(this.sortDisabled?"sorter-false":i.cssHeader);i.headerList[a]=this;d.parent().addClass(i.cssHeaderRow)});if(a.config.debug){benchmark("Built headers:",g);log(h)}return h}function setHeadersCss(a,b){var c,d,e,f,g=a.config,h=g.sortList,i=[g.cssDesc,g.cssAsc],j=$(a).find("tfoot tr").children().removeClass(i.join(" "));b.removeClass(i.join(" "));f=h.length;for(d=0;d<f;d++){if(h[d][1]!==2){c=b.not(".sorter-false").filter('[data-column="'+h[d][0]+'"]'+(f===1?":last":""));if(c.length){for(e=0;e<c.length;e++){if(!c[e].sortDisabled){c.eq(e).addClass(i[h[d][1]]);if(j.length){j.filter('[data-column="'+h[d][0]+'"]').eq(e).addClass(i[h[d][1]])}}}}}}}function fixColumnWidth(a){if(a.config.widthFixed&&$(a).find("colgroup").length===0){var b=$("<colgroup>"),c=$(a).width();$("tr:first td",a.tBodies[0]).each(function(){b.append($("<col>").css("width",parseInt($(this).width()/c*1e3,10)/10+"%"))});$(a).prepend(b)}}function updateHeaderSortCount(a,b){var c,d,e=a.config,f=e.headerList.length,g=b||e.sortList;e.sortList=[];$.each(g,function(a,b){c=[parseInt(b[0],10),parseInt(b[1],10)];d=e.headerList[c[0]];if(d){e.sortList.push(c);d.count=c[1]%(e.sortReset?3:2)}})}function getCachedSortType(a,b){return a&&a[b]?a[b].type||"":""}function multisort(table){var dynamicExp,sortWrapper,col,mx=0,dir=0,tc=table.config,sortList=tc.sortList,l=sortList.length,bl=table.tBodies.length,sortTime,i,j,k,c,cache,lc,s,e,order,orgOrderCol;if(tc.debug){sortTime=new Date}for(k=0;k<bl;k++){dynamicExp="sortWrapper = function(a,b) {";cache=tc.cache[k];lc=cache.normalized.length;for(i=0;i<l;i++){c=sortList[i][0];order=sortList[i][1];s=/n/i.test(getCachedSortType(tc.parsers,c))?"Numeric":"Text";s+=order===0?"":"Desc";e="e"+i;if(/Numeric/.test(s)&&tc.strings[c]){for(j=0;j<lc;j++){col=Math.abs(parseFloat(cache.normalized[j][c]));mx=Math.max(mx,isNaN(col)?0:col)}if(typeof tc.string[tc.strings[c]]==="boolean"){dir=(order===0?1:-1)*(tc.string[tc.strings[c]]?-1:1)}else{dir=tc.strings[c]?tc.string[tc.strings[c]]||0:0}}dynamicExp+="var "+e+" = $.tablesorter.sort"+s+"(table,a["+c+"],b["+c+"],"+c+","+mx+","+dir+"); ";dynamicExp+="if ("+e+") { return "+e+"; } ";dynamicExp+="else { "}orgOrderCol=cache.normalized&&cache.normalized[0]?cache.normalized[0].length-1:0;dynamicExp+="return a["+orgOrderCol+"]-b["+orgOrderCol+"];";for(i=0;i<l;i++){dynamicExp+="}; "}dynamicExp+="return 0; ";dynamicExp+="}; ";cache.normalized.sort(eval(dynamicExp))}if(tc.debug){benchmark("Sorting on "+sortList.toString()+" and dir "+order+" time",sortTime)}}function resortComplete(a,b){a.trigger("updateComplete");if(typeof b==="function"){b(a[0])}}function checkResort(a,b,c){if(b!==false){a.trigger("sorton",[a[0].config.sortList,function(){resortComplete(a,c)}])}else{resortComplete(a,c)}}var ts=this;ts.version="2.4.5";ts.parsers=[];ts.widgets=[];ts.defaults={theme:"default",widthFixed:false,showProcessing:false,cancelSelection:true,dateFormat:"mmddyyyy",sortMultiSortKey:"shiftKey",usNumberFormat:true,delayInit:false,headers:{},ignoreCase:true,sortForce:null,sortList:[],sortAppend:null,sortInitialOrder:"asc",sortLocaleCompare:false,sortReset:false,sortRestart:false,emptyTo:"bottom",stringTo:"max",textExtraction:"simple",textSorter:null,widgets:[],widgetOptions:{zebra:["even","odd"]},initWidgets:true,initialized:null,onRenderHeader:null,tableClass:"tablesorter",cssAsc:"tablesorter-headerSortUp",cssChildRow:"tablesorter-childRow",cssDesc:"tablesorter-headerSortDown",cssHeader:"tablesorter-header",cssHeaderRow:"tablesorter-headerRow",cssIcon:"tablesorter-icon",cssInfoBlock:"tablesorter-infoOnly",cssProcessing:"tablesorter-processing",selectorHeaders:"> thead th, > thead td",selectorSort:"th, td",selectorRemove:".remove-me",debug:false,headerList:[],empties:{},strings:{},parsers:[]};ts.benchmark=benchmark;ts.construct=function(a){return this.each(function(){if(!this.tHead||this.tBodies.length===0||this.hasInitialized===true){return}var b,c,d=$(this),e,f,g,h="",i,j,k,l,m=$.metadata;this.hasInitialized=false;this.config={};e=$.extend(true,this.config,ts.defaults,a);$.data(this,"tablesorter",e);if(e.debug){$.data(this,"startoveralltimer",new Date)}e.supportsTextContent=$("<span>x</span>")[0].textContent==="x";e.supportsDataObject=parseFloat($.fn.jquery)>=1.4;e.string={max:1,min:-1,"max+":1,"max-":-1,zero:0,none:0,"null":0,top:true,bottom:false};if(!/tablesorter\-/.test(d.attr("class"))){h=e.theme!==""?" tablesorter-"+e.theme:""}d.addClass(e.tableClass+h);b=buildHeaders(this);e.parsers=buildParserCache(this,b);if(!e.delayInit){buildCache(this)}b.find("*").andSelf().filter(e.selectorSort).unbind("mousedown.tablesorter mouseup.tablesorter").bind("mousedown.tablesorter mouseup.tablesorter",function(a,c){var m=this.tagName.match("TH|TD")?$(this):$(this).parents("th, td").filter(":last"),n=m[0];if((a.which||a.button)!==1){return false}if(a.type==="mousedown"){l=(new Date).getTime();return a.target.tagName==="INPUT"?"":!e.cancelSelection}if(c!==true&&(new Date).getTime()-l>250){return false}if(e.delayInit&&!e.cache){buildCache(d[0])}if(!n.sortDisabled){d.trigger("sortStart",d[0]);h=!a[e.sortMultiSortKey];n.count=(n.count+1)%(e.sortReset?3:2);if(e.sortRestart){f=n;b.each(function(){if(this!==f&&(h||!$(this).is("."+e.cssDesc+",."+e.cssAsc))){this.count=-1}})}f=n.column;if(h){e.sortList=[];if(e.sortForce!==null){i=e.sortForce;for(g=0;g<i.length;g++){if(i[g][0]!==f){e.sortList.push(i[g])}}}k=n.order[n.count];if(k<2){e.sortList.push([f,k]);if(n.colSpan>1){for(g=1;g<n.colSpan;g++){e.sortList.push([f+g,k])}}}}else{if(e.sortAppend&&e.sortList.length>1){if(ts.isValueInArray(e.sortAppend[0][0],e.sortList)){e.sortList.pop()}}if(ts.isValueInArray(f,e.sortList)){for(g=0;g<e.sortList.length;g++){j=e.sortList[g];k=e.headerList[j[0]];if(j[0]===f){j[1]=k.order[k.count];if(j[1]===2){e.sortList.splice(g,1);k.count=-1}}}}else{k=n.order[n.count];if(k<2){e.sortList.push([f,k]);if(n.colSpan>1){for(g=1;g<n.colSpan;g++){e.sortList.push([f+g,k])}}}}}if(e.sortAppend!==null){i=e.sortAppend;for(g=0;g<i.length;g++){if(i[g][0]!==f){e.sortList.push(i[g])}}}d.trigger("sortBegin",d[0]);setTimeout(function(){setHeadersCss(d[0],b);multisort(d[0]);appendToTable(d[0])},1)}});if(e.cancelSelection){b.each(function(){this.onselectstart=function(){return false}})}d.unbind("update updateCell addRows sorton appendCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave").bind("update",function(a,c,f){$(e.selectorRemove,this).remove();e.parsers=buildParserCache(this,b);buildCache(this);checkResort(d,c,f)}).bind("updateCell",function(a,b,c,f){var g,h,i,j=this,k=$(this).find("tbody"),l=k.index($(b).parents("tbody").filter(":last")),m=$(b).parents("tr").filter(":last");if(k.length&&l>=0){h=k.eq(l).find("tr").index(m);i=b.cellIndex;g=j.config.cache[l].normalized[h].length-1;j.config.cache[l].row[j.config.cache[l].normalized[h][g]]=m;j.config.cache[l].normalized[h][i]=e.parsers[i].format(getElementText(j,b,i),j,b,i);checkResort(d,c,f)}}).bind("addRows",function(a,b,c,f){var h,i=b.filter("tr").length,j=[],k=b[0].cells.length,l=this,m=$(this).find("tbody").index(b.closest("tbody"));for(h=0;h<i;h++){for(g=0;g<k;g++){j[g]=e.parsers[g].format(getElementText(l,b[h].cells[g],g),l,b[h].cells[g],g)}j.push(e.cache[m].row.length);e.cache[m].row.push([b[h]]);e.cache[m].normalized.push(j);j=[]}checkResort(d,c,f)}).bind("sorton",function(a,c,d,e){$(this).trigger("sortStart",this);updateHeaderSortCount(this,c);setHeadersCss(this,b);multisort(this);appendToTable(this,e);if(typeof d==="function"){d(this)}}).bind("appendCache",function(a,b,c){appendToTable(this,c);if(typeof b==="function"){b(this)}}).bind("applyWidgetId",function(a,b){ts.getWidgetById(b).format(this,e,e.widgetOptions)}).bind("applyWidgets",function(a,b){ts.applyWidget(this,b)}).bind("refreshWidgets",function(a,b,c){ts.refreshWidgets(this,b,c)}).bind("destroy",function(a,b,c){ts.destroy(this,b,c)});if(e.supportsDataObject&&typeof d.data().sortlist!=="undefined"){e.sortList=d.data().sortlist}else if(m&&d.metadata()&&d.metadata().sortlist){e.sortList=d.metadata().sortlist}ts.applyWidget(this,true);if(e.sortList.length>0){d.trigger("sorton",[e.sortList,{},!e.initWidgets])}else if(e.initWidgets){ts.applyWidget(this)}fixColumnWidth(this);if(e.showProcessing){d.unbind("sortBegin sortEnd").bind("sortBegin sortEnd",function(a){ts.isProcessing(d[0],a.type==="sortBegin")})}this.hasInitialized=true;if(e.debug){ts.benchmark("Overall initialization time",$.data(this,"startoveralltimer"))}d.trigger("tablesorter-initialized",this);if(typeof e.initialized==="function"){e.initialized(this)}})};ts.isProcessing=function(a,b,c){var d=a.config,e=c||$(a).find("."+d.cssHeader);if(b){if(d.sortList.length>0){e=e.filter(function(){return this.sortDisabled?false:ts.isValueInArray(parseFloat($(this).attr("data-column")),d.sortList)})}e.addClass(d.cssProcessing)}else{e.removeClass(d.cssProcessing)}};ts.processTbody=function(a,b,c){var d,e;if(c){b.before('<span class="tablesorter-savemyplace"/>');e=$.fn.detach?b.detach():b.remove();return e}e=$(a).find("span.tablesorter-savemyplace");b.insertAfter(e);e.remove()};ts.clearTableBody=function(a){$(a.tBodies).filter(":not(."+a.config.cssInfoBlock+")").empty()};ts.destroy=function(a,b,c){var d=$(a),e=a.config,f=d.find("thead:first");a.hasInitialized=false;f.find("tr:not(."+e.cssHeaderRow+")").remove();f.find(".tablesorter-resizer").remove();ts.refreshWidgets(a,true,true);d.removeData("tablesorter").unbind("update updateCell addRows sorton appendCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave").find("."+e.cssHeader).unbind("click mousedown mousemove mouseup").removeClass(e.cssHeader+" "+e.cssAsc+" "+e.cssDesc).find(".tablesorter-header-inner").each(function(){if(e.cssIcon!==""){$(this).find("."+e.cssIcon).remove()}$(this).replaceWith($(this).contents())});if(b!==false){d.removeClass(e.tableClass)}if(typeof c==="function"){c(a)}};ts.regex=[/(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi,/(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,/^0x[0-9a-f]+$/i];ts.sortText=function(a,b,c,d){if(b===c){return 0}var e=a.config,f=e.string[e.empties[d]||e.emptyTo],g=ts.regex,h,i,j,k,l,m,n,o;if(b===""&&f!==0){return typeof f==="boolean"?f?-1:1:-f||-1}if(c===""&&f!==0){return typeof f==="boolean"?f?1:-1:f||1}if(typeof e.textSorter==="function"){return e.textSorter(b,c,a,d)}h=b.replace(g[0],"\\0$1\\0").replace(/\\0$/,"").replace(/^\\0/,"").split("\\0");j=c.replace(g[0],"\\0$1\\0").replace(/\\0$/,"").replace(/^\\0/,"").split("\\0");i=parseInt(b.match(g[2]),16)||h.length!==1&&b.match(g[1])&&Date.parse(b);k=parseInt(c.match(g[2]),16)||i&&c.match(g[1])&&Date.parse(c)||null;if(k){if(i<k){return-1}if(i>k){return 1}}o=Math.max(h.length,j.length);for(n=0;n<o;n++){l=isNaN(h[n])?h[n]||0:parseFloat(h[n])||0;m=isNaN(j[n])?j[n]||0:parseFloat(j[n])||0;if(isNaN(l)!==isNaN(m)){return isNaN(l)?1:-1}if(typeof l!==typeof m){l+="";m+=""}if(l<m){return-1}if(l>m){return 1}}return 0};ts.sortTextDesc=function(a,b,c,d){if(b===c){return 0}var e=a.config,f=e.string[e.empties[d]||e.emptyTo];if(b===""&&f!==0){return typeof f==="boolean"?f?-1:1:f||1}if(c===""&&f!==0){return typeof f==="boolean"?f?1:-1:-f||-1}if(typeof e.textSorter==="function"){return e.textSorter(c,b,a,d)}return ts.sortText(a,c,b)};ts.getTextValue=function(a,b,c){if(b){var d,e=a.length,f=b+c;for(d=0;d<e;d++){f+=a.charCodeAt(d)}return c*f}return 0};ts.sortNumeric=function(a,b,c,d,e,f){if(b===c){return 0}var g=a.config,h=g.string[g.empties[d]||g.emptyTo];if(b===""&&h!==0){return typeof h==="boolean"?h?-1:1:-h||-1}if(c===""&&h!==0){return typeof h==="boolean"?h?1:-1:h||1}if(isNaN(b)){b=ts.getTextValue(b,e,f)}if(isNaN(c)){c=ts.getTextValue(c,e,f)}return b-c};ts.sortNumericDesc=function(a,b,c,d,e,f){if(b===c){return 0}var g=a.config,h=g.string[g.empties[d]||g.emptyTo];if(b===""&&h!==0){return typeof h==="boolean"?h?-1:1:h||1}if(c===""&&h!==0){return typeof h==="boolean"?h?1:-1:-h||-1}if(isNaN(b)){b=ts.getTextValue(b,e,f)}if(isNaN(c)){c=ts.getTextValue(c,e,f)}return c-b};ts.characterEquivalents={a:"\u00e1\u00e0\u00e2\u00e3\u00e4",A:"\u00c1\u00c0\u00c2\u00c3\u00c4",c:"\u00e7",C:"\u00c7",e:"\u00e9\u00e8\u00ea\u00eb",E:"\u00c9\u00c8\u00ca\u00cb",i:"\u00ed\u00ec\u0130\u00ee\u00ef",I:"\u00cd\u00cc\u0130\u00ce\u00cf",o:"\u00f3\u00f2\u00f4\u00f5\u00f6",O:"\u00d3\u00d2\u00d4\u00d5\u00d6",S:"\u00df",u:"\u00fa\u00f9\u00fb\u00fc",U:"\u00da\u00d9\u00db\u00dc"};;ts.replaceAccents=function(a){var b,c="[",d=ts.characterEquivalents;if(!ts.characterRegex){ts.characterRegexArray={};for(b in d){if(typeof b==="string"){c+=d[b];ts.characterRegexArray[b]=new RegExp("["+d[b]+"]","g")}}ts.characterRegex=new RegExp(c+"]")}if(ts.characterRegex.test(a)){for(b in d){if(typeof b==="string"){a=a.replace(ts.characterRegexArray[b],b)}}}return a};ts.isValueInArray=function(a,b){var c,d=b.length;for(c=0;c<d;c++){if(b[c][0]===a){return true}}return false};ts.addParser=function(a){var b,c=ts.parsers.length,d=true;for(b=0;b<c;b++){if(ts.parsers[b].id.toLowerCase()===a.id.toLowerCase()){d=false}}if(d){ts.parsers.push(a)}};ts.getParserById=function(a){var b,c=ts.parsers.length;for(b=0;b<c;b++){if(ts.parsers[b].id.toLowerCase()===a.toString().toLowerCase()){return ts.parsers[b]}}return false};ts.addWidget=function(a){ts.widgets.push(a)};ts.getWidgetById=function(a){var b,c,d=ts.widgets.length;for(b=0;b<d;b++){c=ts.widgets[b];if(c&&c.hasOwnProperty("id")&&c.id.toLowerCase()===a.toLowerCase()){return c}}};ts.applyWidget=function(a,b){var c=a.config,d=c.widgetOptions,e=c.widgets.sort().reverse(),f,g,h,i=e.length;g=$.inArray("zebra",c.widgets);if(g>=0){c.widgets.splice(g,1);c.widgets.push("zebra")}if(c.debug){f=new Date}for(g=0;g<i;g++){h=ts.getWidgetById(e[g]);if(h){if(b===true&&h.hasOwnProperty("init")){h.init(a,h,c,d)}else if(!b&&h.hasOwnProperty("format")){h.format(a,c,d)}}}if(c.debug){benchmark("Completed "+(b===true?"initializing":"applying")+" widgets",f)}};ts.refreshWidgets=function(a,b,c){var d,e=a.config,f=e.widgets,g=ts.widgets,h=g.length;for(d=0;d<h;d++){if(g[d]&&g[d].id&&(b||$.inArray(g[d].id,f)<0)){if(e.debug){log("removing "+g[d].id)}if(g[d].hasOwnProperty("remove")){g[d].remove(a,e,e.widgetOptions)}}}if(c!==true){ts.applyWidget(a,b)}};ts.getData=function(a,b,c){var d="",e=$(a),f,g;if(!e.length){return""}f=$.metadata?e.metadata():false;g=" "+(e.attr("class")||"");if(typeof e.data(c)!=="undefined"||typeof e.data(c.toLowerCase())!=="undefined"){d+=e.data(c)||e.data(c.toLowerCase())}else if(f&&typeof f[c]!=="undefined"){d+=f[c]}else if(b&&typeof b[c]!=="undefined"){d+=b[c]}else if(g!==" "&&g.match(" "+c+"-")){d=g.match(new RegExp(" "+c+"-(\\w+)"))[1]||""}return $.trim(d)};ts.formatFloat=function(a,b){if(typeof a!=="string"||a===""){return a}if(b.config.usNumberFormat!==false){a=a.replace(/,/g,"")}else{a=a.replace(/[\s|\.]/g,"").replace(/,/g,".")}if(/^\s*\([.\d]+\)/.test(a)){a=a.replace(/^\s*\(/,"-").replace(/\)/,"")}var c=parseFloat(a);return isNaN(c)?$.trim(a):c};ts.isDigit=function(a){return isNaN(a)?/^[\-+(]?\d+[)]?$/.test(a.toString().replace(/[,.'\s]/g,"")):true}}});var ts=$.tablesorter;$.fn.extend({tablesorter:ts.construct});ts.addParser({id:"text",is:function(a,b,c){return true},format:function(a,b,c,d){var e=b.config;a=$.trim(e.ignoreCase?a.toLocaleLowerCase():a);return e.sortLocaleCompare?ts.replaceAccents(a):a},type:"text"});ts.addParser({id:"currency",is:function(a){return/^\(?[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+/.test(a)},format:function(a,b){return ts.formatFloat(a.replace(/[^\w,. \-()]/g,""),b)},type:"numeric"});ts.addParser({id:"ipAddress",is:function(a){return/^\d{1,3}[\.]\d{1,3}[\.]\d{1,3}[\.]\d{1,3}$/.test(a)},format:function(a,b){var c,d=a.split("."),e="",f=d.length;for(c=0;c<f;c++){e+=("00"+d[c]).slice(-3)}return ts.formatFloat(e,b)},type:"numeric"});ts.addParser({id:"url",is:function(a){return/^(https?|ftp|file):\/\//.test(a)},format:function(a){return $.trim(a.replace(/(https?|ftp|file):\/\//,""))},type:"text"});ts.addParser({id:"isoDate",is:function(a){return/^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/.test(a)},format:function(a,b){return ts.formatFloat(a!==""?(new Date(a.replace(/-/g,"/"))).getTime()||"":"",b)},type:"numeric"});ts.addParser({id:"percent",is:function(a){return/\d%\)?$/.test(a)},format:function(a,b){return ts.formatFloat(a.replace(/%/g,""),b)},type:"numeric"});ts.addParser({id:"usLongDate",is:function(a){return/^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4}|'?\d{2})\s+(([0-2]?\d:[0-5]\d)|([0-1]?\d:[0-5]\d\s?([AP]M)))$/i.test(a)},format:function(a,b){return ts.formatFloat((new Date(a.replace(/(\S)([AP]M)$/i,"$1 $2"))).getTime()||"",b)},type:"numeric"});ts.addParser({id:"shortDate",is:function(a){return/^(\d{2}|\d{4})[\/\-\,\.\s+]\d{2}[\/\-\.\,\s+](\d{2}|\d{4})$/.test(a)},format:function(a,b,c,d){var e=b.config,f=e.headerList[d],g=f.shortDateFormat;if(typeof g==="undefined"){g=f.shortDateFormat=ts.getData(f,e.headers[d],"dateFormat")||e.dateFormat}a=a.replace(/\s+/g," ").replace(/[\-|\.|\,]/g,"/");if(g==="mmddyyyy"){a=a.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/,"$3/$1/$2")}else if(g==="ddmmyyyy"){a=a.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/,"$3/$2/$1")}else if(g==="yyyymmdd"){a=a.replace(/(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/,"$1/$2/$3")}return ts.formatFloat((new Date(a)).getTime()||"",b)},type:"numeric"});ts.addParser({id:"time",is:function(a){return/^(([0-2]?\d:[0-5]\d)|([0-1]?\d:[0-5]\d\s?([AP]M)))$/i.test(a)},format:function(a,b){return ts.formatFloat((new Date("2000/01/01 "+a.replace(/(\S)([AP]M)$/i,"$1 $2"))).getTime()||"",b)},type:"numeric"});ts.addParser({id:"digit",is:function(a){return ts.isDigit(a)},format:function(a,b){return ts.formatFloat(a.replace(/[^\w,. \-()]/g,""),b)},type:"numeric"});ts.addParser({id:"metadata",is:function(a){return false},format:function(a,b,c){var d=b.config,e=!d.parserMetadataName?"sortValue":d.parserMetadataName;return $(c).metadata()[e]},type:"numeric"});ts.addWidget({id:"zebra",format:function(a,b,c){var d,e,f,g,h,i,j,k,l=new RegExp(b.cssChildRow,"i"),m=$(a).children("tbody:not(."+b.cssInfoBlock+")");if(b.debug){i=new Date}for(j=0;j<m.length;j++){d=$(m[j]);k=d.children("tr").length;if(k>1){g=0;e=d.children("tr:visible");e.each(function(){f=$(this);if(!l.test(this.className)){g++}h=g%2===0;f.removeClass(c.zebra[h?1:0]).addClass(c.zebra[h?0:1])})}}if(b.debug){ts.benchmark("Applying Zebra widget",i)}},remove:function(a,b,c){var d,e,f=$(a).children("tbody:not(."+b.cssInfoBlock+")"),g=(b.widgetOptions.zebra||["even","odd"]).join(" ");for(d=0;d<f.length;d++){e=$.tablesorter.processTbody(a,$(f[d]),true);e.children().removeClass(g);$.tablesorter.processTbody(a,e,false)}}})}(jQuery)
@@ -1,14 +1,47 @@
1
- /*! tableSorter 2.3 widgets - updated 6/21/2012
1
+ /*! tableSorter 2.4+ widgets - updated 10/17/2012
2
2
  *
3
- * jQuery UI Theme
4
3
  * Column Styles
5
4
  * Column Filters
6
- * Sticky Header
7
5
  * Column Resizing
6
+ * Sticky Header
7
+ * UI Theme (generalized)
8
8
  * Save Sort
9
- *
9
+ * ["zebra", "uitheme", "stickyHeaders", "filter", "columns"]
10
10
  */
11
+ /*jshint browser:true, jquery:true, unused:false, loopfunc:true */
12
+ /*global jQuery: false, localStorage: false, navigator: false */
11
13
  ;(function($){
14
+ "use strict";
15
+ $.tablesorter = $.tablesorter || {};
16
+
17
+ $.tablesorter.themes = {
18
+ "bootstrap" : {
19
+ table : 'table table-bordered table-striped',
20
+ header : 'bootstrap-header', // give the header a gradient background
21
+ icons : '', // add "icon-white" to make them white; this icon class is added to the <i> in the header
22
+ sortNone : 'bootstrap-icon-unsorted',
23
+ sortAsc : 'icon-chevron-up',
24
+ sortDesc : 'icon-chevron-down',
25
+ active : '', // applied when column is sorted
26
+ hover : '', // use custom css here - bootstrap class may not override it
27
+ filterRow: '', // filter row class
28
+ even : '', // even row zebra striping
29
+ odd : '' // odd row zebra striping
30
+ },
31
+ "jui" : {
32
+ table : 'ui-widget ui-widget-content ui-corner-all', // table classes
33
+ header : 'ui-widget-header ui-corner-all ui-state-default', // header classes
34
+ icons : 'ui-icon', // icon class added to the <i> in the header
35
+ sortNone : 'ui-icon-carat-2-n-s',
36
+ sortAsc : 'ui-icon-carat-1-n',
37
+ sortDesc : 'ui-icon-carat-1-s',
38
+ active : 'ui-state-active', // applied when column is sorted
39
+ hover : 'ui-state-hover', // hover class
40
+ filterRow: '',
41
+ even : 'ui-widget-content', // even row zebra striping
42
+ odd : 'ui-state-default' // odd row zebra striping
43
+ }
44
+ };
12
45
 
13
46
  // *** Store data in local storage, with a cookie fallback ***
14
47
  /* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json)
@@ -35,8 +68,8 @@ $.tablesorter.storage = function(table, key, val){
35
68
  url = window.location.pathname;
36
69
  try { ls = !!(localStorage.getItem); } catch(e) {}
37
70
  // *** get val ***
38
- if ($.parseJSON) {
39
- if (ls) {
71
+ if ($.parseJSON){
72
+ if (ls){
40
73
  v = $.parseJSON(localStorage[key]) || {};
41
74
  } else {
42
75
  k = document.cookie.split(/[;\s|=]/); // cookie
@@ -44,12 +77,12 @@ $.tablesorter.storage = function(table, key, val){
44
77
  v = (d !== 0) ? $.parseJSON(k[d]) || {} : {};
45
78
  }
46
79
  }
47
- if (val && JSON && JSON.hasOwnProperty('stringify')) {
80
+ if (val && JSON && JSON.hasOwnProperty('stringify')){
48
81
  // add unique identifiers = url pathname > table ID/index on page > data
49
- if (v[url] && v[url][id]) {
82
+ if (v[url] && v[url][id]){
50
83
  v[url][id] = val;
51
84
  } else {
52
- if (v[url]) {
85
+ if (v[url]){
53
86
  v[url][id] = val;
54
87
  } else {
55
88
  v[url] = {};
@@ -57,11 +90,11 @@ $.tablesorter.storage = function(table, key, val){
57
90
  }
58
91
  }
59
92
  // *** set val ***
60
- if (ls) {
93
+ if (ls){
61
94
  localStorage[key] = JSON.stringify(v);
62
95
  } else {
63
96
  d = new Date();
64
- d.setTime(d.getTime()+(31536e+6)); // 365 days
97
+ d.setTime(d.getTime() + (31536e+6)); // 365 days
65
98
  document.cookie = key + '=' + (JSON.stringify(v)).replace(/\"/g,'\"') + '; expires=' + d.toGMTString() + '; path=/';
66
99
  }
67
100
  } else {
@@ -69,92 +102,128 @@ $.tablesorter.storage = function(table, key, val){
69
102
  }
70
103
  };
71
104
 
72
- // Widget: jQuery UI theme
105
+ // Widget: General UI theme
73
106
  // "uitheme" option in "widgetOptions"
74
107
  // **************************
75
108
  $.tablesorter.addWidget({
76
109
  id: "uitheme",
77
- format: function(table) {
78
- var time, klass, rmv, $t, t, $table = $(table),
79
- c = table.config, wo = c.widgetOptions,
80
- // ["up/down arrow (cssHeaders, unsorted)", "down arrow (cssDesc, descending)", "up arrow (cssAsc, ascending)" ]
81
- icons = ["ui-icon-arrowthick-2-n-s", "ui-icon-arrowthick-1-s", "ui-icon-arrowthick-1-n"];
82
- // keep backwards compatibility, for now
83
- icons = (c.widgetUitheme && c.widgetUitheme.hasOwnProperty('css')) ? c.widgetUitheme.css || icons :
84
- (wo && wo.hasOwnProperty('uitheme')) ? wo.uitheme : icons;
85
- rmv = icons.join(' ');
86
- if (c.debug) {
87
- time = new Date();
88
- }
89
- if (!$table.hasClass('ui-theme')) {
90
- $table.addClass('ui-widget ui-widget-content ui-corner-all ui-theme');
91
- $.each(c.headerList, function(){
92
- $(this)
93
- // using "ui-theme" class in case the user adds their own ui-icon using onRenderHeader
94
- .addClass('ui-widget-header ui-corner-all ui-state-default')
95
- .append('<span class="ui-icon"/>')
96
- .wrapInner('<div class="tablesorter-inner"/>')
110
+ format: function(table){
111
+ var time, klass, $el, $tar, t,
112
+ $t = $(table),
113
+ c = table.config,
114
+ wo = c.widgetOptions,
115
+ theme = typeof wo.uitheme === 'object' ? 'jui' : wo.uitheme || 'jui', // default uitheme is 'jui'
116
+ // use Object.prototype.toString.call().test('Array') instead of $.isArray to make this widget compatible with jQuery v1.2.6
117
+ o = (typeof wo.uitheme === 'object' && !Object.prototype.toString.call(wo.uitheme).test('Array')) ? wo.uitheme : $.tablesorter.themes[ $.tablesorter.themes.hasOwnProperty(theme) ? theme : 'jui'],
118
+ $h = $(c.headerList),
119
+ sh = 'tr.' + (wo.stickyHeaders || 'tablesorter-stickyHeader'),
120
+ rmv = o.sortNone + ' ' + o.sortDesc + ' ' + o.sortAsc;
121
+ if (c.debug) { time = new Date(); }
122
+ if (!$t.hasClass('tablesorter-' + theme) || c.theme === theme || !table.hasInitialized){
123
+ // update zebra stripes
124
+ if (o.even !== '') { wo.zebra[0] += ' ' + o.even; }
125
+ if (o.odd !== '') { wo.zebra[1] += ' ' + o.odd; }
126
+ // add table/footer class names
127
+ $t
128
+ // remove other selected themes; use widgetOptions.theme_remove
129
+ .removeClass( c.theme === '' ? '' : 'tablesorter-' + c.theme )
130
+ .addClass('tablesorter-' + theme + ' ' + o.table); // add theme widget class name
131
+ c.theme = ''; // clear out theme option so it doesn't interfere
132
+ // update header classes
133
+ $h
134
+ .addClass(o.header)
135
+ .filter(':not(.sorter-false)')
97
136
  .hover(function(){
98
- $(this).addClass('ui-state-hover');
137
+ $(this).addClass(o.hover);
99
138
  }, function(){
100
- $(this).removeClass('ui-state-hover');
139
+ $(this).removeClass(o.hover);
101
140
  });
102
- });
141
+ if (!$h.find('.tablesorter-wrapper').length) {
142
+ // Firefox needs this inner div to position the resizer correctly
143
+ $h.wrapInner('<div class="tablesorter-wrapper" style="position:relative;height:100%;width:100%"></div>');
144
+ }
145
+ if (c.cssIcon){
146
+ // if c.cssIcon is '', then no <i> is added to the header
147
+ $h.find('.' + c.cssIcon).addClass(o.icons);
148
+ }
149
+ if ($t.hasClass('hasFilters')){
150
+ $h.find('.tablesorter-filter-row').addClass(o.filterRow);
151
+ }
103
152
  }
104
- $.each(c.headerList, function(i){
105
- $t = $(this);
106
- if (this.sortDisabled) {
153
+ $.each($h, function(i){
154
+ $el = $(this);
155
+ $tar = (c.cssIcon) ? $el.find('.' + c.cssIcon) : $el;
156
+ if (this.sortDisabled){
107
157
  // no sort arrows for disabled columns!
108
- $t.find('span.ui-icon').removeClass(rmv + ' ui-icon');
158
+ $el.removeClass(rmv);
159
+ $tar.removeClass(rmv + ' tablesorter-icon ' + o.icons);
109
160
  } else {
110
- klass = ($t.hasClass(c.cssAsc)) ? icons[1] : ($t.hasClass(c.cssDesc)) ? icons[2] : $t.hasClass(c.cssHeader) ? icons[0] : '';
111
- t = ($table.hasClass('hasStickyHeaders')) ? $table.find('tr.' + (wo.stickyHeaders || 'tablesorter-stickyHeader')).find('th').eq(i).add($t) : $t;
112
- t[klass === icons[0] ? 'removeClass' : 'addClass']('ui-state-active')
113
- .find('span.ui-icon').removeClass(rmv).addClass(klass);
161
+ t = ($t.hasClass('hasStickyHeaders')) ? $t.find(sh).find('th').eq(i).add($el) : $el;
162
+ klass = ($el.hasClass(c.cssAsc)) ? o.sortDesc : ($el.hasClass(c.cssDesc)) ? o.sortAsc : $el.hasClass(c.cssHeader) ? o.sortNone : '';
163
+ $el[klass === o.sortNone ? 'removeClass' : 'addClass'](o.active);
164
+ $tar.removeClass(rmv).addClass(klass);
114
165
  }
115
166
  });
116
- if (c.debug) {
117
- $.tablesorter.benchmark("Applying uitheme widget", time);
167
+ if (c.debug){
168
+ $.tablesorter.benchmark("Applying " + theme + " theme", time);
118
169
  }
170
+ },
171
+ remove: function(table, c, wo){
172
+ var $t = $(table),
173
+ theme = typeof wo.uitheme === 'object' ? 'jui' : wo.uitheme || 'jui',
174
+ o = typeof wo.uitheme === 'object' ? wo.uitheme : $.tablesorter.themes[ $.tablesorter.themes.hasOwnProperty(theme) ? theme : 'jui'],
175
+ $h = $t.children('thead').children(),
176
+ rmv = o.sortNone + ' ' + o.sortDesc + ' ' + o.sortAsc;
177
+ $t
178
+ .removeClass('tablesorter-' + theme + ' ' + o.table)
179
+ .find(c.cssHeader).removeClass(o.header);
180
+ $h
181
+ .unbind('mouseenter mouseleave') // remove hover
182
+ .removeClass(o.hover + ' ' + rmv + ' ' + o.active)
183
+ .find('.tablesorter-filter-row').removeClass(o.filterRow);
184
+ $h.find('.tablesorter-icon').removeClass(o.icons);
119
185
  }
120
186
  });
121
187
 
122
188
  // Widget: Column styles
123
- // "columns" option in "widgetOptions"
189
+ // "columns", "columns_thead" (true) and
190
+ // "columns_tfoot" (true) options in "widgetOptions"
124
191
  // **************************
125
192
  $.tablesorter.addWidget({
126
193
  id: "columns",
127
- format: function(table) {
194
+ format: function(table){
128
195
  var $tb, $tr, $td, $t, time, last, rmv, i, k, l,
196
+ $tbl = $(table),
129
197
  c = table.config,
130
- b = $(table).children('tbody:not(.' + c.cssInfoBlock + ')'),
198
+ wo = c.widgetOptions,
199
+ b = $tbl.children('tbody:not(.' + c.cssInfoBlock + ')'),
131
200
  list = c.sortList,
132
201
  len = list.length,
133
202
  css = [ "primary", "secondary", "tertiary" ]; // default options
134
203
  // keep backwards compatibility, for now
135
204
  css = (c.widgetColumns && c.widgetColumns.hasOwnProperty('css')) ? c.widgetColumns.css || css :
136
- (c.widgetOptions && c.widgetOptions.hasOwnProperty('columns')) ? c.widgetOptions.columns || css : css;
205
+ (wo && wo.hasOwnProperty('columns')) ? wo.columns || css : css;
137
206
  last = css.length-1;
138
207
  rmv = css.join(' ');
139
- if (c.debug) {
208
+ if (c.debug){
140
209
  time = new Date();
141
210
  }
142
211
  // check if there is a sort (on initialization there may not be one)
143
- for (k = 0; k < b.length; k++ ) {
144
- $tb = $(b[k]);
145
- $tr = $tb.addClass('tablesorter-hidden').children('tr');
212
+ for (k = 0; k < b.length; k++ ){
213
+ $tb = $.tablesorter.processTbody(table, $(b[k]), true); // detach tbody
214
+ $tr = $tb.children('tr');
146
215
  l = $tr.length;
147
216
  // loop through the visible rows
148
217
  $tr.each(function(){
149
218
  $t = $(this);
150
- if (this.style.display !== 'none') {
219
+ if (this.style.display !== 'none'){
151
220
  // remove all columns class names
152
221
  $td = $t.children().removeClass(rmv);
153
222
  // add appropriate column class names
154
- if (list && list[0]) {
223
+ if (list && list[0]){
155
224
  // primary sort column class
156
225
  $td.eq(list[0][0]).addClass(css[0]);
157
- if (len > 1) {
226
+ if (len > 1){
158
227
  for (i = 1; i < len; i++){
159
228
  // secondary, tertiary, etc sort column classes
160
229
  $td.eq(list[i][0]).addClass( css[i] || css[last] );
@@ -163,24 +232,65 @@ $.tablesorter.addWidget({
163
232
  }
164
233
  }
165
234
  });
166
- $tb.removeClass('tablesorter-hidden');
235
+ $.tablesorter.processTbody(table, $tb, false);
236
+ }
237
+ // add classes to thead and tfoot
238
+ $tr = wo.columns_thead !== false ? 'thead tr' : '';
239
+ if (wo.columns_tfoot !== false) {
240
+ $tr += ($tr === '' ? '' : ',') + 'tfoot tr';
241
+ }
242
+ if ($tr.length) {
243
+ $t = $tbl.find($tr).children().removeClass(rmv);
244
+ if (list && list[0]){
245
+ // primary sort column class
246
+ $t.filter('[data-column="' + list[0][0] + '"]').addClass(css[0]);
247
+ if (len > 1){
248
+ for (i = 1; i < len; i++){
249
+ // secondary, tertiary, etc sort column classes
250
+ $t.filter('[data-column="' + list[i][0] + '"]').addClass(css[i] || css[last]);
251
+ }
252
+ }
253
+ }
167
254
  }
168
- if (c.debug) {
255
+ if (c.debug){
169
256
  $.tablesorter.benchmark("Applying Columns widget", time);
170
257
  }
258
+ },
259
+ remove: function(table, c, wo){
260
+ var k, $tb,
261
+ b = $(table).children('tbody:not(.' + c.cssInfoBlock + ')'),
262
+ rmv = (c.widgetOptions.columns || [ "primary", "secondary", "tertiary" ]).join(' ');
263
+ for (k = 0; k < b.length; k++ ){
264
+ $tb = $.tablesorter.processTbody(table, $(b[k]), true); // remove tbody
265
+ $tb.children('tr').each(function(){
266
+ $(this).children().removeClass(rmv);
267
+ });
268
+ $.tablesorter.processTbody(table, $tb, false); // restore tbody
269
+ }
171
270
  }
172
271
  });
173
272
 
174
- // Widget: Filter
175
- // "filter_startsWith", "filter_childRows", "filter_ignoreCase",
176
- // "filter_searchDelay" & "filter_functions" options in "widgetOptions"
177
- // **************************
273
+ /* Widget: filter
274
+ widgetOptions:
275
+ filter_childRows : false // if true, filter includes child row content in the search
276
+ filter_columnFilters : true // if true, a filter will be added to the top of each table column
277
+ filter_cssFilter : 'tablesorter-filter' // css class name added to the filter row & each input in the row
278
+ filter_functions : null // add custom filter functions using this option
279
+ filter_hideFilters : false // collapse filter row when mouse leaves the area
280
+ filter_ignoreCase : true // if true, make all searches case-insensitive
281
+ filter_reset : null // jQuery selector string of an element used to reset the filters
282
+ filter_searchDelay : 300 // typing delay in milliseconds before starting a search
283
+ filter_startsWith : false // if true, filter start from the beginning of the cell contents
284
+ filter_useParsedData : false // filter all data using parsed content
285
+ **************************/
178
286
  $.tablesorter.addWidget({
179
287
  id: "filter",
180
- format: function(table) {
181
- if (table.config.parsers && !$(table).hasClass('hasFilters')) {
182
- var i, j, k, l, cv, v, val, r, ff, t, x, xi, cr,
183
- sel, $tb, $th, $tr, $td, reg2,
288
+ format: function(table){
289
+ if (table.config.parsers && !$(table).hasClass('hasFilters')){
290
+ var i, j, k, l, val, ff, x, xi, st, sel, str,
291
+ ft, ft2, $th, rg, s, t, dis, col,
292
+ last = '', // save last filter search
293
+ ts = $.tablesorter,
184
294
  c = table.config,
185
295
  $ths = $(c.headerList),
186
296
  wo = c.widgetOptions,
@@ -188,27 +298,68 @@ $.tablesorter.addWidget({
188
298
  $t = $(table).addClass('hasFilters'),
189
299
  b = $t.children('tbody:not(.' + c.cssInfoBlock + ')'),
190
300
  cols = c.parsers.length,
191
- fr = '<tr class="' + css + '">',
192
- regexp = /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/,
193
- reg1 = new RegExp(c.cssChildRow),
301
+ reg = [ // regex used in filter "check" functions
302
+ /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // 0 = regex to test for regex
303
+ new RegExp(c.cssChildRow), // 1 = child row
304
+ /undefined|number/, // 2 = check type
305
+ /(^[\"|\'|=])|([\"|\'|=]$)/, // 3 = exact match
306
+ /[\"\'=]/g, // 4 = replace exact match flags
307
+ /[^\w,. \-()]/g, // 5 = replace non-digits (from digit & currency parser)
308
+ /[<>=]/g // 6 = replace operators
309
+ ],
310
+ parsed = $ths.map(function(i){
311
+ return (ts.getData) ? ts.getData($ths.filter('[data-column="' + i + '"]:last'), c.headers[i], 'filter') === 'parsed' : $(this).hasClass('filter-parsed');
312
+ }).get(),
194
313
  time, timer,
195
- findRows = function(){
314
+
315
+ // dig fer gold
316
+ checkFilters = function(filter){
317
+ var arry = $.isArray(filter),
318
+ $inpts = $t.find('thead').eq(0).children('tr').find('select.' + css + ', input.' + css),
319
+ v = (arry) ? filter : $inpts.map(function(){
320
+ return $(this).val() || '';
321
+ }).get(),
322
+ cv = (v || []).join(''); // combined filter values
323
+ // add filter array back into inputs
324
+ if (arry) {
325
+ $inpts.each(function(i,el){
326
+ $(el).val(filter[i] || '');
327
+ });
328
+ }
329
+ if (wo.filter_hideFilters === true){
330
+ // show/hide filter row as needed
331
+ $t.find('.tablesorter-filter-row').trigger( cv === '' ? 'mouseleave' : 'mouseenter' );
332
+ }
333
+ // return if the last search is the same; but filter === false when updating the search
334
+ // see example-widget-filter.html filter toggle buttons
335
+ if (last === cv && filter !== false) { return; }
336
+ $t.trigger('filterStart', [v]);
337
+ if (c.showProcessing) {
338
+ // give it time for the processing icon to kick in
339
+ setTimeout(function(){
340
+ findRows(filter, v, cv);
341
+ return false;
342
+ }, 30);
343
+ } else {
344
+ findRows(filter, v, cv);
345
+ return false;
346
+ }
347
+ },
348
+ findRows = function(filter, v, cv){
349
+ var $tb, $tr, $td, cr, r, l, ff, time, arry;
196
350
  if (c.debug) { time = new Date(); }
197
- v = $t.find('thead').eq(0).children('tr').find('select.' + css + ', input.' + css).map(function(){
198
- return $(this).val() || '';
199
- }).get();
200
- cv = v.join('');
201
- for (k = 0; k < b.length; k++ ) {
202
- $tb = $(b[k]);
203
- $tr = $tb.addClass('tablesorter-hidden').children('tr');
351
+
352
+ for (k = 0; k < b.length; k++ ){
353
+ $tb = $.tablesorter.processTbody(table, $(b[k]), true);
354
+ $tr = $tb.children('tr');
204
355
  l = $tr.length;
205
- // loop through the rows
206
- for (j = 0; j < l; j++) {
207
- if (cv === '') {
208
- $tr[j].style.display = '';
209
- } else {
356
+ if (cv === ''){
357
+ $tr.show().removeClass('filtered');
358
+ } else {
359
+ // loop through the rows
360
+ for (j = 0; j < l; j++){
210
361
  // skip child rows
211
- if (reg1.test($tr[j].className)) { continue; }
362
+ if (reg[1].test($tr[j].className)) { continue; }
212
363
  r = true;
213
364
  cr = $tr.eq(j).nextUntil('tr:not(.' + c.cssChildRow + ')');
214
365
  // so, if "table.config.widgetOptions.filter_childRows" is true and there is
@@ -218,19 +369,25 @@ $.tablesorter.addWidget({
218
369
  typeof wo.filter_childRows !== 'undefined' ? wo.filter_childRows : true)) ? cr.text() : '';
219
370
  t = wo.filter_ignoreCase ? t.toLocaleLowerCase() : t;
220
371
  $td = $tr.eq(j).children('td');
221
- for (i = 0; i < cols; i++) {
222
- x = $.trim($td.eq(i).text());
223
- xi = wo.filter_ignoreCase ? x.toLocaleLowerCase() : x;
224
- // ignore if filter is empty
225
- if (v[i] !== '') {
372
+ for (i = 0; i < cols; i++){
373
+ // ignore if filter is empty or disabled
374
+ if (v[i]){
375
+ // check if column data should be from the cell or from parsed data
376
+ if (wo.filter_useParsedData || parsed[i]){
377
+ x = c.cache[k].normalized[j][i];
378
+ } else {
379
+ // using older or original tablesorter
380
+ x = $.trim($td.eq(i).text());
381
+ }
382
+ xi = !reg[2].test(typeof x) && wo.filter_ignoreCase ? x.toLocaleLowerCase() : x;
226
383
  ff = r; // if r is true, show that row
227
384
  // val = case insensitive, v[i] = case sensitive
228
385
  val = wo.filter_ignoreCase ? v[i].toLocaleLowerCase() : v[i];
229
- if (wo.filter_functions && wo.filter_functions[i]) {
230
- if (wo.filter_functions[i] === true) {
386
+ if (wo.filter_functions && wo.filter_functions[i]){
387
+ if (wo.filter_functions[i] === true){
231
388
  // default selector; no "filter-select" class
232
- ff = wo.filter_ignoreCase ? val === xi : v[i] === x;
233
- } else if (typeof wo.filter_functions[i] === 'function') {
389
+ ff = ($ths.filter('[data-column="' + i + '"]:last').hasClass('filter-match')) ? xi.search(val) >= 0 : v[i] === x;
390
+ } else if (typeof wo.filter_functions[i] === 'function'){
234
391
  // filter callback( exact cell content, parser normalized content, filter input value, column index )
235
392
  ff = wo.filter_functions[i](x, c.cache[k].normalized[j][i], v[i], i);
236
393
  } else if (typeof wo.filter_functions[i][v[i]] === 'function'){
@@ -238,18 +395,30 @@ $.tablesorter.addWidget({
238
395
  ff = wo.filter_functions[i][v[i]](x, c.cache[k].normalized[j][i], v[i], i);
239
396
  }
240
397
  // Look for regex
241
- } else if (regexp.test(val)) {
242
- reg2 = regexp.exec(val);
398
+ } else if (reg[0].test(val)){
399
+ rg = reg[0].exec(val);
243
400
  try {
244
- ff = new RegExp(reg2[1], reg2[2]).test(xi);
245
- } catch (err) {
401
+ ff = new RegExp(rg[1], rg[2]).test(xi);
402
+ } catch (err){
246
403
  ff = false;
247
404
  }
248
- // Look for quotes to get an exact match
249
- } else if (/[\"|\']$/.test(val) && xi === val.replace(/(\"|\')/g,'')) {
405
+ // Look for quotes or equals to get an exact match
406
+ } else if (reg[3].test(val) && xi === val.replace(reg[4], '')){
250
407
  ff = true;
408
+ // Look for a not match
409
+ } else if (/^\!/.test(val)){
410
+ val = val.replace('!','');
411
+ s = xi.search($.trim(val));
412
+ ff = val === '' ? true : !(wo.filter_startsWith ? s === 0 : s >= 0);
413
+ // Look for operators >, >=, < or <=
414
+ } else if (/^[<>]=?/.test(val)){
415
+ // xi may be numeric - see issue #149
416
+ rg = isNaN(xi) ? $.tablesorter.formatFloat(xi.replace(reg[5], ''), table) : $.tablesorter.formatFloat(xi, table);
417
+ s = $.tablesorter.formatFloat(val.replace(reg[5], '').replace(reg[6],''), table);
418
+ if (/>/.test(val)) { ff = />=/.test(val) ? rg >= s : rg > s; }
419
+ if (/</.test(val)) { ff = /<=/.test(val) ? rg <= s : rg < s; }
251
420
  // Look for wild card: ? = single, or * = multiple
252
- } else if (/[\?|\*]/.test(val)) {
421
+ } else if (/[\?|\*]/.test(val)){
253
422
  ff = new RegExp( val.replace(/\?/g, '\\S{1}').replace(/\*/g, '\\S*') ).test(xi);
254
423
  // Look for match, and add child row data for matching
255
424
  } else {
@@ -260,167 +429,316 @@ $.tablesorter.addWidget({
260
429
  }
261
430
  }
262
431
  $tr[j].style.display = (r ? '' : 'none');
432
+ $tr.eq(j)[r ? 'removeClass' : 'addClass']('filtered');
263
433
  if (cr.length) { cr[r ? 'show' : 'hide'](); }
264
434
  }
265
435
  }
266
- $tb.removeClass('tablesorter-hidden');
436
+ $.tablesorter.processTbody(table, $tb, false);
267
437
  }
268
- if (c.debug) {
269
- $.tablesorter.benchmark("Completed filter widget search", time);
438
+
439
+ last = cv; // save last search
440
+ if (c.debug){
441
+ ts.benchmark("Completed filter widget search", time);
270
442
  }
271
443
  $t.trigger('applyWidgets'); // make sure zebra widget is applied
444
+ $t.trigger('filterEnd');
272
445
  },
273
446
  buildSelect = function(i, updating){
274
447
  var o, arry = [];
275
448
  i = parseInt(i, 10);
276
449
  o = '<option value="">' + ($ths.filter('[data-column="' + i + '"]:last').attr('data-placeholder') || '') + '</option>';
277
- for (k = 0; k < b.length; k++ ) {
450
+ for (k = 0; k < b.length; k++ ){
278
451
  l = c.cache[k].row.length;
279
452
  // loop through the rows
280
- for (j = 0; j < l; j++) {
453
+ for (j = 0; j < l; j++){
281
454
  // get non-normalized cell content
282
- t = c.cache[k].row[j][0].cells[i];
283
- if (t) {
284
- arry.push( c.supportsTextContent ? t.textContent : $(t).text() );
455
+ if (wo.filter_useParsedData){
456
+ arry.push( '' + c.cache[k].normalized[j][i] );
457
+ } else {
458
+ t = c.cache[k].row[j][0].cells[i];
459
+ if (t){
460
+ arry.push( $.trim(c.supportsTextContent ? t.textContent : $(t).text()) );
461
+ }
285
462
  }
286
463
  }
287
464
  }
465
+
288
466
  // get unique elements and sort the list
289
- arry = arry.getUnique(true);
467
+ // if $.tablesorter.sortText exists (not in the original tablesorter),
468
+ // then natural sort the list otherwise use a basic sort
469
+ arry = $.grep(arry, function(v, k){
470
+ return $.inArray(v ,arry) === k;
471
+ });
472
+ arry = (ts.sortText) ? arry.sort(function(a,b){ return ts.sortText(table, a, b, i); }) : arry.sort(true);
473
+
290
474
  // build option list
291
- for (k = 0; k < arry.length; k++) {
475
+ for (k = 0; k < arry.length; k++){
292
476
  o += '<option value="' + arry[k] + '">' + arry[k] + '</option>';
293
477
  }
294
478
  $t.find('thead').find('select.' + css + '[data-column="' + i + '"]')[ updating ? 'html' : 'append' ](o);
295
479
  },
296
480
  buildDefault = function(updating){
297
481
  // build default select dropdown
298
- for (i = 0; i < cols; i++) {
482
+ for (i = 0; i < cols; i++){
299
483
  t = $ths.filter('[data-column="' + i + '"]:last');
300
484
  // look for the filter-select class, but don't build it twice.
301
485
  if (t.hasClass('filter-select') && !t.hasClass('filter-false') && !(wo.filter_functions && wo.filter_functions[i] === true)){
486
+ if (!wo.filter_functions) { wo.filter_functions = {}; }
487
+ wo.filter_functions[i] = true; // make sure this select gets processed by filter_functions
302
488
  buildSelect(i, updating);
303
489
  }
304
490
  }
305
491
  };
306
- if (c.debug) {
492
+
493
+ if (c.debug){
307
494
  time = new Date();
308
495
  }
309
496
  wo.filter_ignoreCase = wo.filter_ignoreCase !== false; // set default filter_ignoreCase to true
310
- for (i=0; i < cols; i++){
311
- $th = $ths.filter('[data-column="' + i + '"]:last'); // assuming last cell of a column is the main column
312
- sel = (wo.filter_functions && wo.filter_functions[i] && typeof wo.filter_functions[i] !== 'function') || $th.hasClass('filter-select');
313
- fr += '<td>';
314
- if (sel){
315
- fr += '<select data-column="' + i + '" class="' + css;
316
- } else {
317
- fr += '<input type="search" placeholder="' + ($th.attr('data-placeholder') || "") + '" data-column="' + i + '" class="' + css;
318
- }
319
- // use header option - headers: { 1: { filter: false } } OR add class="filter-false"
320
- if ($.tablesorter.getData) {
321
- // get data from jQuery data, metadata, headers option or header class name
322
- fr += $.tablesorter.getData($th[0], c.headers[i], 'filter') === 'false' ? ' disabled" disabled' : '"';
323
- } else {
324
- // only class names and header options - keep this for compatibility with tablesorter v2.0.5
325
- fr += ((c.headers[i] && c.headers[i].hasOwnProperty('filter') && c.headers[i].filter === false) || $th.hasClass('filter-false') ) ? ' disabled" disabled' : '"';
497
+ wo.filter_useParsedData = wo.filter_useParsedData === true; // default is false
498
+ // don't build filter row if columnFilters is false or all columns are set to "filter-false" - issue #156
499
+ if (wo.filter_columnFilters !== false && $ths.filter('.filter-false').length !== $ths.length){
500
+ t = '<tr class="tablesorter-filter-row">'; // build filter row
501
+ for (i = 0; i < cols; i++){
502
+ dis = false;
503
+ $th = $ths.filter('[data-column="' + i + '"]:last'); // assuming last cell of a column is the main column
504
+ sel = (wo.filter_functions && wo.filter_functions[i] && typeof wo.filter_functions[i] !== 'function') || $th.hasClass('filter-select');
505
+ t += '<td>';
506
+ if (sel){
507
+ t += '<select data-column="' + i + '" class="' + css;
508
+ } else {
509
+ t += '<input type="search" placeholder="' + ($th.attr('data-placeholder') || "") + '" data-column="' + i + '" class="' + css;
510
+ }
511
+ // use header option - headers: { 1: { filter: false } } OR add class="filter-false"
512
+ if (ts.getData){
513
+ dis = ts.getData($th[0], c.headers[i], 'filter') === 'false';
514
+ // get data from jQuery data, metadata, headers option or header class name
515
+ t += dis ? ' disabled" disabled' : '"';
516
+ } else {
517
+ dis = (c.headers[i] && c.headers[i].hasOwnProperty('filter') && c.headers[i].filter === false) || $th.hasClass('filter-false');
518
+ // only class names and header options - keep this for compatibility with tablesorter v2.0.5
519
+ t += (dis) ? ' disabled" disabled' : '"';
520
+ }
521
+ t += (sel ? '></select>' : '>') + '</td>';
326
522
  }
327
- fr += (sel ? '></select>' : '>') + '</td>';
523
+ $t.find('thead').eq(0).append(t += '</tr>');
328
524
  }
329
525
  $t
330
- .bind('addRows updateCell update appendCache', function(){
331
- buildDefault(true);
332
- findRows();
526
+ // add .tsfilter namespace to all BUT search
527
+ .bind('addRows updateCell update appendCache search'.split(' ').join('.tsfilter '), function(e, filter){
528
+ if (e.type !== 'search'){
529
+ buildDefault(true);
530
+ }
531
+ checkFilters(e.type === 'search' ? filter : '');
532
+ return false;
333
533
  })
334
- .find('thead').eq(0).append(fr += '</tr>')
335
- .find('input.' + css).bind('keyup search', function(e, delay){
534
+ .find('input.' + css).bind('keyup search', function(e, filter){
336
535
  // ignore arrow and meta keys; allow backspace
337
536
  if ((e.which < 32 && e.which !== 8) || (e.which >= 37 && e.which <=40)) { return; }
338
537
  // skip delay
339
- if (delay === false) {
340
- findRows();
341
- return;
538
+ if (typeof filter !== 'undefined'){
539
+ checkFilters(filter);
540
+ return false;
342
541
  }
343
542
  // delay filtering
344
543
  clearTimeout(timer);
345
544
  timer = setTimeout(function(){
346
- findRows();
545
+ checkFilters();
347
546
  }, wo.filter_searchDelay || 300);
348
547
  });
349
- if (wo.filter_functions) {
548
+
549
+ // reset button/link
550
+ if (wo.filter_reset && $(wo.filter_reset).length){
551
+ $(wo.filter_reset).bind('click', function(){
552
+ $t.find('.' + css).val('');
553
+ checkFilters();
554
+ return false;
555
+ });
556
+ }
557
+ if (wo.filter_functions){
350
558
  // i = column # (string)
351
- for (i in wo.filter_functions) {
352
- t = $ths.filter('[data-column="' + i + '"]:last');
353
- fr = '';
354
- if (typeof i === 'string' && wo.filter_functions[i] === true && !t.hasClass('filter-false')) {
355
- buildSelect(i);
356
- } else if (typeof i === 'string' && !t.hasClass('filter-false')) {
357
- // add custom drop down list
358
- for (j in wo.filter_functions[i]) {
359
- if (typeof j === 'string') {
360
- fr += fr === '' ? '<option>' + (t.attr('data-placeholder') || '') + '</option>' : '';
361
- fr += '<option>' + j + '</option>';
559
+ for (col in wo.filter_functions){
560
+ if (wo.filter_functions.hasOwnProperty(col) && typeof col === 'string'){
561
+ t = $ths.filter('[data-column="' + col + '"]:last');
562
+ ff = '';
563
+ if (wo.filter_functions[col] === true && !t.hasClass('filter-false')){
564
+ buildSelect(col);
565
+ } else if (typeof col === 'string' && !t.hasClass('filter-false')){
566
+ // add custom drop down list
567
+ for (str in wo.filter_functions[col]){
568
+ if (typeof str === 'string'){
569
+ ff += ff === '' ? '<option value="">' + (t.attr('data-placeholder') || '') + '</option>' : '';
570
+ ff += '<option value="' + str + '">' + str + '</option>';
571
+ }
362
572
  }
573
+ $t.find('thead').find('select.' + css + '[data-column="' + col + '"]').append(ff);
363
574
  }
364
- $t.find('thead').find('select.' + css + '[data-column="' + i + '"]').append(fr);
365
575
  }
366
576
  }
367
577
  }
368
578
  buildDefault();
369
579
 
370
- $t.find('select.' + css).bind('change', function(){
371
- findRows();
580
+ $t.find('select.' + css).bind('change search', function(){
581
+ checkFilters();
372
582
  });
373
583
 
374
- if (c.debug) {
375
- $.tablesorter.benchmark("Applying Filter widget", time);
584
+ if (wo.filter_hideFilters === true){
585
+ $t
586
+ .find('.tablesorter-filter-row')
587
+ .addClass('hideme')
588
+ .bind('mouseenter mouseleave', function(e){
589
+ // save event object - http://bugs.jquery.com/ticket/12140
590
+ var all, evt = e;
591
+ ft = $(this);
592
+ clearTimeout(st);
593
+ st = setTimeout(function(){
594
+ if (/enter|over/.test(evt.type)){
595
+ ft.removeClass('hideme');
596
+ } else {
597
+ // don't hide if input has focus
598
+ // $(':focus') needs jQuery 1.6+
599
+ if ($(document.activeElement).closest('tr')[0] !== ft[0]){
600
+ // get all filter values
601
+ all = $t.find('.' + (wo.filter_cssFilter || 'tablesorter-filter')).map(function(){
602
+ return $(this).val() || '';
603
+ }).get().join('');
604
+ // don't hide row if any filter has a value
605
+ if (all === ''){
606
+ ft.addClass('hideme');
607
+ }
608
+ }
609
+ }
610
+ }, 200);
611
+ })
612
+ .find('input, select').bind('focus blur', function(e){
613
+ ft2 = $(this).closest('tr');
614
+ clearTimeout(st);
615
+ st = setTimeout(function(){
616
+ // don't hide row if any filter has a value
617
+ if ($t.find('.' + (wo.filter_cssFilter || 'tablesorter-filter')).map(function(){ return $(this).val() || ''; }).get().join('') === ''){
618
+ ft2[ e.type === 'focus' ? 'removeClass' : 'addClass']('hideme');
619
+ }
620
+ }, 200);
621
+ });
622
+ }
623
+
624
+ // show processing icon
625
+ if (c.showProcessing) {
626
+ $t.bind('filterStart filterEnd', function(e, v) {
627
+ var fc = (v) ? $t.find('.' + c.cssHeader).filter('[data-column]').filter(function(){
628
+ return v[$(this).data('column')] !== '';
629
+ }) : '';
630
+ ts.isProcessing($t[0], e.type === 'filterStart', v ? fc : '');
631
+ });
632
+ }
633
+
634
+ if (c.debug){
635
+ ts.benchmark("Applying Filter widget", time);
376
636
  }
637
+ // filter widget initialized
638
+ $t.trigger('filterInit');
377
639
  }
640
+ },
641
+ remove: function(table, c, wo){
642
+ var k, $tb,
643
+ $t = $(table),
644
+ b = $t.children('tbody:not(.' + c.cssInfoBlock + ')');
645
+ $t
646
+ .removeClass('hasFilters')
647
+ // add .tsfilter namespace to all BUT search
648
+ .unbind('addRows updateCell update appendCache search'.split(' ').join('.tsfilter'))
649
+ .find('.tablesorter-filter-row').remove();
650
+ for (k = 0; k < b.length; k++ ){
651
+ $tb = $.tablesorter.processTbody(table, $(b[k]), true); // remove tbody
652
+ $tb.children().removeClass('filtered').show();
653
+ $.tablesorter.processTbody(table, $tb, false); // restore tbody
654
+ }
655
+ if (wo.filterreset) { $(wo.filter_reset).unbind('click'); }
378
656
  }
379
657
  });
380
658
 
381
659
  // Widget: Sticky headers
382
660
  // based on this awesome article:
383
- // http://css-tricks.com/13465-persistent-headers/
661
+ // http://css-tricks.com/13465-persistent-headers/
662
+ // and https://github.com/jmosbech/StickyTableHeaders by Jonas Mosbech
384
663
  // **************************
385
664
  $.tablesorter.addWidget({
386
665
  id: "stickyHeaders",
387
- format: function(table) {
666
+ format: function(table){
388
667
  if ($(table).hasClass('hasStickyHeaders')) { return; }
389
668
  var $table = $(table).addClass('hasStickyHeaders'),
390
- wo = table.config.widgetOptions,
669
+ c = table.config,
670
+ wo = c.widgetOptions,
391
671
  win = $(window),
392
- header = $(table).children('thead'),
672
+ header = $(table).children('thead:first'), //.add( $(table).find('caption') ),
393
673
  hdrCells = header.children('tr:not(.sticky-false)').children(),
394
674
  css = wo.stickyHeaders || 'tablesorter-stickyHeader',
395
675
  innr = '.tablesorter-header-inner',
396
676
  firstCell = hdrCells.eq(0),
397
677
  tfoot = $table.find('tfoot'),
398
- sticky = header.find('tr.tablesorter-header:not(.sticky-false)').clone()
399
- .removeClass('tablesorter-header')
678
+ // clone the entire thead - seems to work in IE8+
679
+ stkyHdr = header.clone()
400
680
  .addClass(css)
401
681
  .css({
402
682
  width : header.outerWidth(true),
403
683
  position : 'fixed',
404
- left : firstCell.offset().left,
405
684
  margin : 0,
406
685
  top : 0,
407
686
  visibility : 'hidden',
408
- zIndex : 10
687
+ zIndex : 1
409
688
  }),
410
- stkyCells = sticky.children(),
411
- laststate = '';
689
+ stkyCells = stkyHdr.find('tr').children(),
690
+ laststate = '',
691
+ spacing = 0,
692
+ resizeHdr = function(){
693
+ var bwsr = navigator.userAgent;
694
+ spacing = 0;
695
+ // yes, I dislike browser sniffing, but it really is needed here :(
696
+ // webkit automatically compensates for border spacing
697
+ if ($table.css('border-collapse') !== 'collapse' && !/webkit/i.test(bwsr)) {
698
+ // IE needs to be adjusted by the padding; Firefox & Opera use the border-spacing
699
+ // update border-spacing here because of demos that switch themes
700
+ spacing = /msie/i.test(bwsr) ? parseInt(header.css('padding-left'), 10) : parseInt($table.css('border-spacing'), 10);
701
+ }
702
+ stkyHdr.css({
703
+ left : header.offset().left - win.scrollLeft() - spacing,
704
+ width: header.outerWidth()
705
+ });
706
+ stkyCells
707
+ .each(function(i){
708
+ var $h = hdrCells.eq(i);
709
+ $(this).css({
710
+ width: $h.width(),
711
+ height: $h.height()
712
+ });
713
+ })
714
+ .find(innr).each(function(i){
715
+ var hi = hdrCells.eq(i).find(innr),
716
+ w = hi.width(); // - ( parseInt(hi.css('padding-left'), 10) + parseInt(hi.css('padding-right'), 10) );
717
+ $(this).width(w);
718
+ });
719
+ };
720
+ // remove rows you don't want to be sticky
721
+ stkyHdr.find('tr.sticky-false').remove();
722
+ // remove resizable block
723
+ stkyCells.find('.tablesorter-resizer').remove();
412
724
  // update sticky header class names to match real header after sorting
413
- $table.bind('sortEnd', function(e,t){
414
- var th = $(t).find('thead tr'),
415
- sh = th.filter('.' + css).children();
416
- th.filter(':not(.' + css + ')').children().each(function(i){
417
- sh.eq(i).attr('class', $(this).attr('class'));
725
+ $table
726
+ .bind('sortEnd.tsSticky', function(){
727
+ hdrCells.each(function(i){
728
+ var t = stkyCells.eq(i);
729
+ t.attr('class', $(this).attr('class'));
730
+ if (c.cssIcon){
731
+ t
732
+ .find('.' + c.cssIcon)
733
+ .attr('class', $(this).find('.' + c.cssIcon).attr('class'));
734
+ }
418
735
  });
419
- }).bind('pagerComplete', function(){
420
- win.resize(); // trigger window resize to make sure column widths & position are correct
736
+ })
737
+ .bind('pagerComplete.tsSticky', function(){
738
+ resizeHdr();
421
739
  });
422
740
  // set sticky header cell width and link clicks to real header
423
- hdrCells.each(function(i){
741
+ hdrCells.find('*').andSelf().filter(c.selectorSort).each(function(i){
424
742
  var t = $(this);
425
743
  stkyCells.eq(i)
426
744
  // clicking on sticky will trigger sort
@@ -431,41 +749,40 @@ $.tablesorter.addWidget({
431
749
  .bind('mousedown', function(){
432
750
  this.onselectstart = function(){ return false; };
433
751
  return false;
434
- })
435
- // set cell widths
436
- .find(innr).width( t.find(innr).width() );
752
+ });
437
753
  });
438
- header.prepend( sticky );
754
+ header.after( stkyHdr );
439
755
  // make it sticky!
440
756
  win
441
- .scroll(function(){
442
- var offset = firstCell.offset(),
443
- sTop = win.scrollTop(),
444
- tableHt = $table.height() - (firstCell.height() + (tfoot.height() || 0)),
445
- vis = (sTop > offset.top) && (sTop < offset.top + tableHt) ? 'visible' : 'hidden';
446
- sticky.css({
447
- left : offset.left - win.scrollLeft(),
448
- visibility : vis
449
- });
450
- if (vis !== laststate) {
451
- // trigger resize to make sure the column widths match
452
- win.resize();
453
- laststate = vis;
454
- }
455
- })
456
- .resize(function(){
457
- var ht = 0;
458
- sticky.css({
459
- left : firstCell.offset().left - win.scrollLeft(),
460
- width: header.outerWidth()
461
- }).each(function(i){
462
- $(this).css('top', ht);
463
- ht += header.find('tr').eq(i).outerHeight();
464
- });
465
- stkyCells.find(innr).each(function(i){
466
- $(this).width( hdrCells.eq(i).find(innr).width() );
467
- });
757
+ .bind('scroll.tsSticky', function(){
758
+ var offset = firstCell.offset(),
759
+ sTop = win.scrollTop(),
760
+ tableHt = $table.height() - (firstCell.height() + (tfoot.height() || 0)),
761
+ vis = (sTop > offset.top) && (sTop < offset.top + tableHt) ? 'visible' : 'hidden';
762
+ stkyHdr
763
+ .css({
764
+ // adjust when scrolling horizontally - fixes issue #143
765
+ left : header.offset().left - win.scrollLeft() - spacing,
766
+ visibility : vis
468
767
  });
768
+ if (vis !== laststate){
769
+ // make sure the column widths match
770
+ resizeHdr();
771
+ laststate = vis;
772
+ }
773
+ })
774
+ .bind('resize.tsSticky', function(){
775
+ resizeHdr();
776
+ });
777
+ },
778
+ remove: function(table, c, wo){
779
+ var $t = $(table),
780
+ css = wo.stickyHeaders || 'tablesorter-stickyHeader';
781
+ $t
782
+ .removeClass('hasStickyHeaders')
783
+ .unbind('sortEnd.tsSticky pagerComplete.tsSticky')
784
+ .find('.' + css).remove();
785
+ $(window).unbind('scroll.tsSticky resize.tsSticky');
469
786
  }
470
787
  });
471
788
 
@@ -475,85 +792,128 @@ $.tablesorter.addWidget({
475
792
  // **************************
476
793
  $.tablesorter.addWidget({
477
794
  id: "resizable",
478
- format: function(table) {
795
+ format: function(table){
479
796
  if ($(table).hasClass('hasResizable')) { return; }
480
797
  $(table).addClass('hasResizable');
481
- var j, s, c = table.config,
482
- $cols = $(c.headerList).filter(':gt(0)'),
798
+ var t, j, s, $c, $cols,
799
+ $tbl = $(table),
800
+ c = table.config,
801
+ wo = c.widgetOptions,
483
802
  position = 0,
484
803
  $target = null,
485
- $prev = null,
804
+ $next = null,
486
805
  stopResize = function(){
487
806
  position = 0;
488
- $target = $prev = null;
807
+ $target = $next = null;
489
808
  $(window).trigger('resize'); // will update stickyHeaders, just in case
490
809
  };
491
- s = ($.tablesorter.storage) ? $.tablesorter.storage(table, 'tablesorter-resizable') : '';
810
+ s = ($.tablesorter.storage && wo.resizable !== false) ? $.tablesorter.storage(table, 'tablesorter-resizable') : {};
492
811
  // process only if table ID or url match
493
- if (s) {
494
- for (j in s) {
495
- if (!isNaN(j) && j < c.headerList.length) {
812
+ if (s){
813
+ for (j in s){
814
+ if (!isNaN(j) && j < c.headerList.length){
496
815
  $(c.headerList[j]).width(s[j]); // set saved resizable widths
497
816
  }
498
817
  }
499
818
  }
819
+ $tbl.children('thead:first').find('tr').each(function(){
820
+ $c = $(this).children();
821
+ if (!$(this).find('.tablesorter-wrapper').length) {
822
+ // Firefox needs this inner div to position the resizer correctly
823
+ $c.wrapInner('<div class="tablesorter-wrapper" style="position:relative;height:100%;width:100%"></div>');
824
+ }
825
+ $c = $c.slice(0,-1); // don't include the last column of the row
826
+ $cols = $cols ? $cols.add($c) : $c;
827
+ });
500
828
  $cols
501
- .each(function(){
502
- $(this)
503
- .append('<div class="tablesorter-resizer" style="cursor:w-resize;position:absolute;height:100%;width:20px;left:-20px;top:0;z-index:1;"></div>')
504
- .wrapInner('<div style="position:relative;height:100%;width:100%"></div>');
505
- })
506
- .bind('mousemove', function(e){
507
- // ignore mousemove if no mousedown
508
- if (position === 0 || !$target) { return; }
509
- var w = e.pageX - position;
510
- // make sure
511
- if ( $target.width() < -w || ( $prev && $prev.width() <= w )) { return; }
512
- // resize current column
513
- $prev.width( $prev.width() + w );
514
- position = e.pageX;
515
- })
516
- .bind('mouseup', function(){
517
- if (s && $.tablesorter.storage && $target) {
518
- s[$prev.index()] = $prev.width();
829
+ .each(function(){
830
+ t = $(this);
831
+ j = parseInt(t.css('padding-right'), 10) + 8; // 8 is 1/2 of the 16px wide resizer
832
+ t
833
+ .find('.tablesorter-wrapper')
834
+ .append('<div class="tablesorter-resizer" style="cursor:w-resize;position:absolute;height:100%;width:16px;right:-' + j + 'px;top:0;z-index:1;"></div>');
835
+ })
836
+ .bind('mousemove.tsresize', function(e){
837
+ // ignore mousemove if no mousedown
838
+ if (position === 0 || !$target) { return; }
839
+ // resize columns
840
+ var w = e.pageX - position;
841
+ $target.width( $target.width() + w );
842
+ $next.width( $next.width() - w );
843
+ position = e.pageX;
844
+ })
845
+ .bind('mouseup.tsresize', function(){
846
+ if ($.tablesorter.storage && $target){
847
+ s[$target.index()] = $target.width();
848
+ s[$next.index()] = $next.width();
849
+ if (wo.resizable !== false){
519
850
  $.tablesorter.storage(table, 'tablesorter-resizable', s);
520
851
  }
521
- stopResize();
522
- return false;
523
- })
524
- .find('.tablesorter-resizer')
525
- .bind('mousedown', function(e){
526
- // save header cell and mouse position
527
- $target = $(e.target).closest('th');
528
- $prev = $target.prev();
529
- position = e.pageX;
530
- return false;
531
- });
532
- $(table).find('thead').bind('mouseup mouseleave', function(){
852
+ }
853
+ stopResize();
854
+ })
855
+ .find('.tablesorter-resizer')
856
+ .bind('mousedown', function(e){
857
+ // save header cell and mouse position; closest() not supported by jQuery v1.2.6
858
+ $target = $(e.target).parents('th:last');
859
+ $next = $target.next();
860
+ position = e.pageX;
861
+ });
862
+ $tbl.find('thead:first')
863
+ .bind('mouseup.tsresize mouseleave.tsresize', function(e){
533
864
  stopResize();
865
+ })
866
+ // right click to reset columns to default widths
867
+ .bind('contextmenu.tsresize', function(){
868
+ $.tablesorter.resizableReset(table);
869
+ // $.isEmptyObject() needs jQuery 1.4+
870
+ var rtn = $.isEmptyObject ? $.isEmptyObject(s) : s === {}; // allow right click if already reset
871
+ s = {};
872
+ return rtn;
534
873
  });
874
+ },
875
+ remove: function(table, c, wo){
876
+ $(table)
877
+ .removeClass('hasResizable')
878
+ .find('thead')
879
+ .unbind('mouseup.tsresize mouseleave.tsresize contextmenu.tsresize')
880
+ .find('tr').children()
881
+ .unbind('mousemove.tsresize mouseup.tsresize')
882
+ .find('.tablesorter-wrapper').each(function(){
883
+ $(this).find('.tablesorter-resizer').remove();
884
+ $(this).replaceWith( $(this).contents() );
885
+ });
886
+ $.tablesorter.resizableReset(table);
535
887
  }
536
888
  });
889
+ $.tablesorter.resizableReset = function(table){
890
+ $(table.config.headerList).width('auto');
891
+ $.tablesorter.storage(table, 'tablesorter-resizable', {});
892
+ };
537
893
 
538
894
  // Save table sort widget
539
895
  // this widget saves the last sort only if the
896
+ // saveSort widget option is true AND the
540
897
  // $.tablesorter.storage function is included
541
898
  // **************************
542
899
  $.tablesorter.addWidget({
543
900
  id: 'saveSort',
544
- init: function(table, allWidgets, thisWidget){
901
+ init: function(table, thisWidget){
545
902
  // run widget format before all other widgets are applied to the table
546
903
  thisWidget.format(table, true);
547
904
  },
548
- format: function(table, init) {
549
- var sl, time, c = table.config, sortList = { "sortList" : c.sortList };
550
- if (c.debug) {
905
+ format: function(table, init){
906
+ var sl, time, c = table.config,
907
+ wo = c.widgetOptions,
908
+ ss = wo.saveSort !== false, // make saveSort active/inactive; default to true
909
+ sortList = { "sortList" : c.sortList };
910
+ if (c.debug){
551
911
  time = new Date();
552
912
  }
553
- if ($(table).hasClass('hasSaveSort')) {
554
- if (table.hasInitialized && $.tablesorter.storage) {
913
+ if ($(table).hasClass('hasSaveSort')){
914
+ if (ss && table.hasInitialized && $.tablesorter.storage){
555
915
  $.tablesorter.storage( table, 'tablesorter-savesort', sortList );
556
- if (c.debug) {
916
+ if (c.debug){
557
917
  $.tablesorter.benchmark('saveSort widget: Saving last sort: ' + c.sortList, time);
558
918
  }
559
919
  }
@@ -562,36 +922,27 @@ $.tablesorter.addWidget({
562
922
  $(table).addClass('hasSaveSort');
563
923
  sortList = '';
564
924
  // get data
565
- if ($.tablesorter.storage) {
925
+ if ($.tablesorter.storage){
566
926
  sl = $.tablesorter.storage( table, 'tablesorter-savesort' );
567
927
  sortList = (sl && sl.hasOwnProperty('sortList') && $.isArray(sl.sortList)) ? sl.sortList : '';
568
- if (c.debug) {
569
- $.tablesorter.benchmark('saveSort: Last sort loaded: ' + sortList, time);
928
+ if (c.debug){
929
+ $.tablesorter.benchmark('saveSort: Last sort loaded: "' + sortList + '"', time);
570
930
  }
571
931
  }
572
932
  // init is true when widget init is run, this will run this widget before all other widgets have initialized
573
933
  // this method allows using this widget in the original tablesorter plugin; but then it will run all widgets twice.
574
- if (init && sortList && sortList.length > 0) {
934
+ if (init && sortList && sortList.length > 0){
575
935
  c.sortList = sortList;
576
- } else if (table.hasInitialized && sortList && sortList.length > 0) {
936
+ } else if (table.hasInitialized && sortList && sortList.length > 0){
577
937
  // update sort change
578
938
  $(table).trigger('sorton', [sortList]);
579
939
  }
580
940
  }
941
+ },
942
+ remove: function(table, c, wo){
943
+ // clear storage
944
+ $.tablesorter.storage( table, 'tablesorter-savesort', '' );
581
945
  }
582
946
  });
583
947
 
584
948
  })(jQuery);
585
-
586
- // return an array with unique values https://gist.github.com/461516
587
- Array.prototype.getUnique = function(s){
588
- var c, a = [], o = {}, i, j = 0, l = this.length;
589
- for(i=0; i < l; ++i) {
590
- c = this[i];
591
- if (!o[c]) {
592
- o[c] = {};
593
- a[j++] = c;
594
- }
595
- }
596
- return (s) ? a.sort() : a;
597
- };