jquery-tablesorter 1.0.5 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
- };