diff --git a/examples/table-with-pager.html b/examples/table-with-pager.html
new file mode 100644
index 000000000000..54655f83fba7
--- /dev/null
+++ b/examples/table-with-pager.html
@@ -0,0 +1,360 @@
+
+
+
+
+ App / Fbootstrapp by Clemens Krack, based on Bootstrap, from Twitter
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Example: Default table styles
+
+
+
+ | # |
+ First Name |
+ Last Name |
+ Language |
+
+
+
+
+ | 1 |
+ Some |
+ One |
+ English |
+
+
+ | 2 |
+ Joe |
+ Sixpack |
+ English |
+
+
+ | 3 |
+ Stu |
+ Dent |
+ Code |
+
+
+ | 1 |
+ Some |
+ One |
+ English |
+
+
+ | 2 |
+ Joe |
+ Sixpack |
+ English |
+
+
+ | 3 |
+ Stu |
+ Dent |
+ Code |
+
+
+ | 1 |
+ Some |
+ One |
+ English |
+
+
+ | 2 |
+ Joe |
+ Sixpack |
+ English |
+
+
+ | 3 |
+ Stu |
+ Dent |
+ Code |
+
+
+ | 1 |
+ Some |
+ One |
+ English |
+
+
+ | 2 |
+ Joe |
+ Sixpack |
+ English |
+
+
+ | 3 |
+ Stu |
+ Dent |
+ Code |
+
+
+ | 1 |
+ Some |
+ One |
+ English |
+
+
+ | 2 |
+ Joe |
+ Sixpack |
+ English |
+
+
+ | 3 |
+ Stu |
+ Dent |
+ Code |
+
+
+ | 1 |
+ Some |
+ One |
+ English |
+
+
+ | 2 |
+ Joe |
+ Sixpack |
+ English |
+
+
+ | 3 |
+ Stu |
+ Dent |
+ Code |
+
+
+ | 1 |
+ Some |
+ One |
+ English |
+
+
+ | 2 |
+ Joe |
+ Sixpack |
+ English |
+
+
+ | 3 |
+ Stu |
+ Dent |
+ Code |
+
+
+ | 1 |
+ Some |
+ One |
+ English |
+
+
+ | 2 |
+ Joe |
+ Sixpack |
+ English |
+
+
+ | 3 |
+ Stu |
+ Dent |
+ Code |
+
+
+ | 1 |
+ Some |
+ One |
+ English |
+
+
+ | 2 |
+ Joe |
+ Sixpack |
+ English |
+
+
+ | 3 |
+ Stu |
+ Dent |
+ Code |
+
+
+ | 1 |
+ Some |
+ One |
+ English |
+
+
+ | 2 |
+ Joe |
+ Sixpack |
+ English |
+
+
+ | 3 |
+ Stu |
+ Dent |
+ Code |
+
+
+ | 1 |
+ Some |
+ One |
+ English |
+
+
+ | 2 |
+ Joe |
+ Sixpack |
+ English |
+
+
+ | 3 |
+ Stu |
+ Dent |
+ Code |
+
+
+ | 1 |
+ Some |
+ One |
+ English |
+
+
+ | 2 |
+ Joe |
+ Sixpack |
+ English |
+
+
+ | 3 |
+ Stu |
+ Dent |
+ Code |
+
+
+ | 1 |
+ Some |
+ One |
+ English |
+
+
+ | 2 |
+ Joe |
+ Sixpack |
+ English |
+
+
+ | 3 |
+ Stu |
+ Dent |
+ Code |
+
+
+
+
+
+ Example: Zebra-striped
+
+
+
+ | # |
+ First Name |
+ Last Name |
+ Language |
+
+
+
+
+ | 1 |
+ Some |
+ One |
+ English |
+
+
+ | 2 |
+ Joe |
+ Sixpack |
+ English |
+
+
+ | 3 |
+ Stu |
+ Dent |
+ Code |
+
+
+ |
+ span 4 columns
+ |
+
+
+ |
+ span 2 columns
+ |
+
+ span 2 columns
+ |
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/js/jquery.tablesorter.fbpager.js b/js/jquery.tablesorter.fbpager.js
new file mode 100644
index 000000000000..2e256e039f75
--- /dev/null
+++ b/js/jquery.tablesorter.fbpager.js
@@ -0,0 +1,374 @@
+/*
+ * tablesorter pager plugin
+ * updated 2/22/2012 by christianz
+ */
+
+(function($) {
+ $.extend({tablesorterPager: new function() {
+
+ // hide arrows at extremes
+ var pagerArrows = function(c) {
+ var a = 'addClass', r = 'removeClass', d = c.cssDisabled;
+ if (c.updateArrows) {
+ c.container[(c.totalRows < c.size) ? a : r](d);
+ $(c.cssFirst + ',' + c.cssPrev, c.container)[(c.page === 0) ? a : r](d);
+ $(c.cssNext + ',' + c.cssLast, c.container)[(c.page === c.totalPages - 1) ? a : r](d);
+ }
+ },
+
+ updatePageDisplay = function(table, c) {
+ c.startRow = c.size * (c.page) + 1;
+ c.endRow = Math.min(c.totalRows, c.size * (c.page+1));
+ var out = $(c.cssPageDisplay, c.container);
+
+ pagerArrows(c);
+ c.container.show(); // added in case the pager is reinitialized after being destroyed.
+ $(table).trigger('pagerComplete', c);
+ },
+
+ fixPosition = function(table, c) {
+ var o = $(table);
+ if (!c.pagerPositionSet && c.positionFixed) {
+ if (o.offset) {
+ c.container.css({
+ top: o.offset().top + o.height() + c.offset + 'px',
+ position: 'absolute'
+ });
+ }
+ c.pagerPositionSet = true;
+ }
+ },
+
+ hideRows = function(table, c){
+ var i, rows = $('tr:not(.' + c.cssChildRow + ')', table.tBodies[0]),
+ l = rows.length,
+ s = (c.page * c.size),
+ e = (s + c.size);
+ if (e > l) { e = l; }
+ for (i = 0; i < l; i++){
+ rows[i].style.display = (i >= s && i < e) ? '' : 'none';
+ }
+ },
+
+ hideRowsSetup = function(table, c){
+ pagerArrows(c);
+ if (!c.removeRows) {
+ hideRows(table, c);
+ $(table).bind('sortEnd.pager', function(){
+ hideRows(table, c);
+ $(table).trigger("applyWidgets");
+ });
+ }
+ },
+
+ renderTable = function(table, rows, c) {
+ var i, j, o,
+ tableBody = $(table.tBodies[0]),
+ l = rows.length,
+ s = (c.page * c.size),
+ e = (s + c.size);
+ if (l < 1) { return; } // empty table, abort!
+ $(table).trigger('pagerChange', c);
+ if (!c.removeRows) {
+ hideRows(table, c);
+ } else {
+ if (e > rows.length ) {
+ e = rows.length;
+ }
+
+ // clear the table body
+ $.tablesorter.clearTableBody(table);
+ for (i = s; i < e; i++) {
+ o = rows[i];
+ l = o.length;
+ for (j = 0; j < l; j++) {
+ tableBody[0].appendChild(o[j]);
+ }
+ }
+ }
+ fixPosition(table, tableBody, c);
+ $(table).trigger("applyWidgets");
+ if ( c.page >= c.totalPages ) {
+ moveToLastPage(table, c);
+ }
+ updatePageDisplay(table, c);
+ },
+
+ showAllRows = function(table, c){
+ c.lastPage = c.page;
+ c.lastSize = c.size;
+ c.size = c.totalRows;
+ c.totalPages = 1;
+ renderTable(table, c.rowsCopy, c);
+ },
+
+ moveToPage = function(table, c) {
+ if (c.isDisabled) { return; }
+ if (c.page < 0 || c.page > (c.totalPages-1)) {
+ c.page = 0;
+ }
+
+ $(".pagerBtn").removeClass("active");
+ $(".pagerBtn[rel=" + c.page + "]").addClass("active");
+
+ renderTable(table, c.rowsCopy, c);
+ },
+
+ setPageSize = function(table, size, c) {
+ c.size = c.lastSize = size;
+ c.totalPages = Math.ceil(c.totalRows / c.size);
+ c.pagerPositionSet = false;
+ moveToPage(table, c);
+ fixPosition(table, c);
+ },
+
+ moveToFirstPage = function(table, c) {
+ c.page = 0;
+ moveToPage(table, c);
+ },
+
+ moveToLastPage = function(table, c) {
+ c.page = (c.totalPages-1);
+ moveToPage(table, c);
+ },
+
+ moveToNextPage = function(table, c) {
+ c.page++;
+
+ if (c.page >= (c.totalPages-1)) {
+ c.page = (c.totalPages-1);
+ }
+
+ if (c.page % c.pagesToDisplay == 0) {
+ jumpToNextPageSet(table, c);
+ }
+
+ moveToPage(table, c);
+ },
+
+ jumpToNextPageSet = function(table, c) {
+ c.currentPageSet++;
+
+ buildNumberButtons(table, c);
+ },
+
+ jumpToPrevPageSet = function(table, c) {
+ c.currentPageSet--;
+
+ buildNumberButtons(table, c);
+ },
+
+ buildNumberButtons = function(table, c) {
+ if (c.currentPageSet == 0) {
+ var numRows = table.rows.length;
+
+ // If the page size is larger than the number of rows, don't display the pager.
+ if (c.size >= numRows) {
+ return;
+ }
+
+ var numPages = numRows / c.size;
+
+ if (c.pagesToDisplay > numPages) {
+ c.pagesToDisplay = numPages;
+ }
+ }
+
+ var list = $("");
+ list.addClass("pagerContainer");
+
+ c.container.html("");
+
+ var prevBtn = $("").addClass("prev disabled");
+ prevBtn.html("" + c.prevText + "");
+
+ prevBtn.bind("click.pager", function() {
+ moveToPrevPage(table, c);
+ });
+
+ list.append(prevBtn);
+
+ var startAt = (c.currentPageSet * c.pagesToDisplay);
+ var endAt = (startAt + c.pagesToDisplay);
+
+ if (c.currentPageSet > 0 && (endAt > c.totalPages)) {
+ endAt = c.totalPages;
+ }
+
+ for (var i = startAt; i < endAt; i++) {
+ var numBtn = $("");
+ numBtn.addClass("pagerBtn numBtn");
+
+ numBtn.attr("rel", i);
+
+ if (i == 0)
+ numBtn.addClass("active");
+
+ numBtn.html("" + (i + 1) + "");
+
+ numBtn.bind("click.pager", function() {
+ c.page = parseInt($(this).attr("rel"));
+ moveToPage(table, c);
+ });
+
+ list.append(numBtn);
+ }
+
+ var nextBtn = $("").addClass("next");
+ nextBtn.html("" + c.nextText + "");
+
+ nextBtn.bind("click.pager", function() {
+ moveToNextPage(table, c);
+ });
+
+ list.append(nextBtn);
+
+ c.container.append(list);
+ }
+
+ moveToPrevPage = function(table, c) {
+ c.page--;
+ if (c.page <= 0) {
+ c.page = 0;
+ }
+
+ if ((c.page + 1) % c.pagesToDisplay == 0) {
+ jumpToPrevPageSet(table, c);
+ }
+
+ moveToPage(table, c);
+ },
+
+ destroyPager = function(table, c){
+ showAllRows(table, c);
+ c.container.hide(); // hide pager
+ c.appender = null; // remove pager appender function
+ $(table).unbind('destroy.pager sortEnd.pager enable.pager disable.pager');
+ },
+
+ enablePager = function(table, c){
+ c.isDisabled = false;
+ $('table').trigger('update');
+ c.page = c.lastPage || 0;
+ c.totalPages = Math.ceil(c.totalRows / c.size);
+ hideRowsSetup(table, c);
+ };
+
+ this.appender = function(table, rows) {
+ var c = table.config;
+ c.rowsCopy = rows;
+ c.totalRows = rows.length;
+ c.size = c.lastSize || c.size;
+ c.totalPages = Math.ceil(c.totalRows / c.size);
+ renderTable(table, rows, c);
+ };
+
+ this.defaults = {
+ // target the pager markup
+ container: null,
+
+ // output default: '{page}/{totalPages}'
+ output: '{startRow} to {endRow} of {totalRows} rows', // '{page}/{totalPages}'
+
+ // apply disabled classname to the pager arrows when the rows at either extreme is visible
+ updateArrows: true,
+
+ // starting page of the pager (zero based index)
+ page: 0,
+
+ // Number of visible rows
+ size: 10,
+
+ // if true, moves the pager below the table at a fixed position; so if only 2 rows showing, the pager remains in the same place
+ positionFixed: true,
+
+ // offset added to the pager top, but only when "positionFixed" is true
+ offset: 0,
+
+ // Number of pages to display at a time
+ pagesToDisplay: 5,
+
+ // remove rows from the table to speed up the sort of large tables.
+ // setting this to false, only hides the non-visible rows; needed if you plan to add/remove rows with the pager enabled.
+ removeRows: true, // removing rows in larger tables speeds up the sort
+
+ // css class names of pager arrows
+ cssNext: '.next', // next page arrow
+ cssPrev: '.prev', // previous page arrow
+
+ // text on the previous and next buttons
+ prevText: '← Previous',
+ nextText: 'Next →',
+
+ // class added to arrows when at the extremes (i.e. prev/first arrows are "disabled" when on the first page)
+ cssDisabled: 'disabled', // Note there is no period "." in front of this class name
+
+ // stuff not set by the user
+ totalRows: 0,
+ totalPages: 0,
+ currentPageSet: 0,
+ appender: this.appender
+ };
+
+ this.construct = function(settings) {
+
+ return this.each(function() {
+ var c = $.extend(this.config, $.tablesorterPager.defaults, settings),
+ table = this,
+ pager = c.container;
+
+ hideRowsSetup(table, c);
+
+ buildNumberButtons(table, c);
+
+ $(this).trigger("appendCache");
+
+/*
+ $(c.cssFirst,pager).unbind('click.pager').bind('click.pager', function() {
+ moveToFirstPage(table, c);
+ return false;
+ });
+ $(c.cssNext,pager).unbind('click.pager').bind('click.pager', function() {
+ moveToNextPage(table, c);
+ return false;
+ });
+ $(c.cssPrev,pager).unbind('click.pager').bind('click.pager', function() {
+ moveToPrevPage(table, c);
+ return false;
+ });
+ $(c.cssLast,pager).unbind('click.pager').bind('click.pager', function() {
+ moveToLastPage(table, c);
+ return false;
+ });
+ $(c.cssPageSize,pager).unbind('change.pager').bind('change.pager', function() {
+ setPageSize(table, parseInt($(this).val(), 10), c);
+ return false;
+ });
+*/
+
+ $(this)
+ .unbind('disable.pager enable.pager destroy.pager')
+ .bind('disable.pager', function(){
+ c.isDisabled = true;
+ showAllRows(table, c);
+ })
+ .bind('enable.pager', function(){
+ enablePager(table, c);
+ })
+ .bind('destroy.pager', function(){
+ destroyPager(table, c);
+ });
+
+
+ moveToPage(table, c);
+ });
+ };
+
+ }
+});
+// extend plugin scope
+$.fn.extend({
+ tablesorterPager: $.tablesorterPager.construct
+});
+
+})(jQuery);
\ No newline at end of file
diff --git a/js/jquery.tablesorter.fbpager.min.js b/js/jquery.tablesorter.fbpager.min.js
new file mode 100644
index 000000000000..37105844bba1
--- /dev/null
+++ b/js/jquery.tablesorter.fbpager.min.js
@@ -0,0 +1,2 @@
+// tablesorter pager plugin updated 2/22/2012 by christianz
+(function(a){a.extend({tablesorterPager:new function(){var b=function(b){var c="addClass",d="removeClass",e=b.cssDisabled;if(b.updateArrows){b.container[b.totalRowsf){h=f}for(d=0;d=g&&df.length){o=f.length}a.tablesorter.clearTableBody(b);for(h=n;h=g.totalPages){l(b,g)}c(b,g)},h=function(a,b){b.lastPage=b.page;b.lastSize=b.size;b.size=b.totalRows;b.totalPages=1;g(a,b.rowsCopy,b)},i=function(b,c){if(c.isDisabled){return}if(c.page<0||c.page>c.totalPages-1){c.page=0}a(".pagerBtn").removeClass("active");a(".pagerBtn[rel="+c.page+"]").addClass("active");g(b,c.rowsCopy,c)},j=function(a,b,c){c.size=c.lastSize=b;c.totalPages=Math.ceil(c.totalRows/c.size);c.pagerPositionSet=false;i(a,c);d(a,c)},k=function(a,b){b.page=0;i(a,b)},l=function(a,b){b.page=b.totalPages-1;i(a,b)},m=function(a,b){b.page++;if(b.page>=b.totalPages-1){b.page=b.totalPages-1}if(b.page%b.pagesToDisplay==0){n(a,b)}i(a,b)},n=function(a,b){b.currentPageSet++;p(a,b)},o=function(a,b){b.currentPageSet--;p(a,b)},p=function(b,c){if(c.currentPageSet==0){var d=b.rows.length;if(c.size>=d){return}var e=d/c.size;if(c.pagesToDisplay>e){c.pagesToDisplay=e}}var f=a("");f.addClass("pagerContainer");c.container.html("");var g=a("").addClass("prev disabled");g.html(""+c.prevText+"");g.bind("click.pager",function(){moveToPrevPage(b,c)});f.append(g);var h=c.currentPageSet*c.pagesToDisplay;var j=h+c.pagesToDisplay;if(c.currentPageSet>0&&j>c.totalPages){j=c.totalPages}for(var k=h;k");l.addClass("pagerBtn numBtn");l.attr("rel",k);if(k==0)l.addClass("active");l.html(""+(k+1)+"");l.bind("click.pager",function(){c.page=parseInt(a(this).attr("rel"));i(b,c)});f.append(l)}var n=a("").addClass("next");n.html(""+c.nextText+"");n.bind("click.pager",function(){m(b,c)});f.append(n);c.container.append(f)};moveToPrevPage=function(a,b){b.page--;if(b.page<=0){b.page=0}if((b.page+1)%b.pagesToDisplay==0){o(a,b)}i(a,b)},destroyPager=function(b,c){h(b,c);c.container.hide();c.appender=null;a(b).unbind("destroy.pager sortEnd.pager enable.pager disable.pager")},enablePager=function(b,c){c.isDisabled=false;a("table").trigger("update");c.page=c.lastPage||0;c.totalPages=Math.ceil(c.totalRows/c.size);f(b,c)};this.appender=function(a,b){var c=a.config;c.rowsCopy=b;c.totalRows=b.length;c.size=c.lastSize||c.size;c.totalPages=Math.ceil(c.totalRows/c.size);g(a,b,c)};this.defaults={container:null,output:"{startRow} to {endRow} of {totalRows} rows",updateArrows:true,page:0,size:10,positionFixed:true,offset:0,pagesToDisplay:5,removeRows:true,cssNext:".next",cssPrev:".prev",prevText:"? Previous",nextText:"Next ?",cssDisabled:"disabled",totalRows:0,totalPages:0,currentPageSet:0,appender:this.appender};this.construct=function(b){return this.each(function(){var c=a.extend(this.config,a.tablesorterPager.defaults,b),d=this,e=c.container;f(d,c);p(d,c);a(this).trigger("appendCache");a(this).unbind("disable.pager enable.pager destroy.pager").bind("disable.pager",function(){c.isDisabled=true;h(d,c)}).bind("enable.pager",function(){enablePager(d,c)}).bind("destroy.pager",function(){destroyPager(d,c)});i(d,c)})}}});a.fn.extend({tablesorterPager:a.tablesorterPager.construct})})(jQuery)
\ No newline at end of file
diff --git a/js/jquery.tablesorter.min.js b/js/jquery.tablesorter.min.js
new file mode 100644
index 000000000000..aa7d5192ee5a
--- /dev/null
+++ b/js/jquery.tablesorter.min.js
@@ -0,0 +1,7 @@
+/*
+* TableSorter 2.0 - Client-side table sorting with ease!
+* Version 2.0.30 Minified using http://dean.edwards.name/packer/
+* Copyright (c) 2007 Christian Bach
+*/
+
+!(function($){$.extend({tablesorter:new function(){var g=[],widgets=[],tbl;this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",cssChildRow:"expand-child",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,sortLocaleCompare:false,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"mmddyyyy",onRenderHeader:null,selectorHeaders:'thead th',tableClass:'tablesorter',debug:false};function log(s){if(typeof console!=="undefined"&&typeof console.log!=="undefined"){console.log(s)}else{alert(s)}}function benchmark(s,d){log(s+" ("+(new Date().getTime()-d.getTime())+"ms)")}this.benchmark=benchmark;this.hasInitialized=false;function getElementText(a,b,c){var d="",te=a.textExtraction;if(!b){return""}if(!a.supportsTextContent){a.supportsTextContent=b.textContent||false}if(te==="simple"){if(a.supportsTextContent){d=b.textContent}else{if(b.childNodes[0]&&b.childNodes[0].hasChildNodes()){d=b.childNodes[0].innerHTML}else{d=b.innerHTML}}}else{if(typeof(te)==="function"){d=te(b)}else if(typeof(te)==="object"&&te.hasOwnProperty(c)){d=te[c](b)}else{d=$(b).text()}}return d}function getParserById(a){var i,l=g.length;for(i=0;i").each(function(a){this.column=header_index[this.parentNode.rowIndex+"-"+this.cellIndex];this.order=formatSortingOrder(checkHeaderOrder(b,a));this.count=this.order;if(checkHeaderMetadata(this)||checkHeaderOptions(b,a)||$(this).is('.sorter-false')){this.sortDisabled=true}this.lockedOrder=false;lock=checkHeaderLocked(b,a);if(typeof(lock)!=='undefined'&&lock!==false){this.order=this.lockedOrder=formatSortingOrder(lock)}if(!this.sortDisabled){$th=$(this).addClass(c.cssHeader);if(c.onRenderHeader){c.onRenderHeader.apply($th,[a])}}c.headerList[a]=this});if(c.debug){benchmark("Built headers",time);log($tableHeaders)}return $tableHeaders}function checkCellColSpan(a,b,d){var i,cell,arr=[],r=a.tHead.rows,c=r[d].cells;for(i=0;i1){arr=arr.concat(checkCellColSpan(a,b,d++))}else{if(a.tHead.length===1||(cell.rowSpan>1||!r[d+1])){arr.push(cell)}}}return arr}function isValueInArray(v,a){var i,l=a.length;for(i=0;i');$("tr:first td",a.tBodies[0]).each(function(){c.append($('').css('width',$(this).width()))});$(a).prepend(c)}}function updateHeaderSortCount(a,b){var i,s,o,c=a.config,l=b.length;for(i=0;ib)?1:-1}catch(er){return 0}}function sortTextDesc(a,b){if(a===''){return 1}if(b===''){return-1}if(a===b){return 0}if($.data(tbl[0],"tablesorter").sortLocaleCompare){return b.localeCompare(a)}return-sortText(a,b)}function getTextValue(a,b,d){if(b){var i,l=a.length,n=b+d;for(i=0;i=2){config.sortList.splice(j,1);o.count=0}}}}else{if(this.order<2){config.sortList.push([i,this.order])}}}if(config.sortAppend!==null){a=config.sortAppend;for(j=0;j0){c.trigger("sorton",[config.sortList])}else{applyWidget(this)}this.hasInitialized=true})};this.addParser=function(b){var i,l=g.length,a=true;for(i=0;i