jquery-tablesorter 1.10.2 → 1.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/README.markdown +6 -3
  3. data/Rakefile +30 -16
  4. data/lib/jquery-tablesorter/version.rb +1 -1
  5. data/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.quicksearch.js +191 -0
  6. data/vendor/assets/javascripts/jquery-tablesorter/extras/semver-mod.js +1026 -0
  7. data/vendor/assets/javascripts/jquery-tablesorter/extras/semver.js +1011 -0
  8. data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +2 -2
  9. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js +34 -0
  10. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js +33 -0
  11. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js +74 -0
  12. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js +33 -0
  13. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js +36 -0
  14. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-feet-inch-fraction.js +63 -0
  15. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js +73 -0
  16. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ignore-articles.js +47 -0
  17. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +86 -0
  18. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-ipv6.js +76 -0
  19. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js +77 -0
  20. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-build-table.js +441 -0
  21. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +291 -0
  22. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js +67 -0
  23. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +89 -0
  24. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +183 -0
  25. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +834 -0
  26. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js +50 -0
  27. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +241 -0
  28. metadata +24 -2
@@ -0,0 +1,834 @@
1
+ /* Pager widget (beta) for TableSorter 2/23/2014 (v2.15.5) */
2
+ /*jshint browser:true, jquery:true, unused:false */
3
+ ;(function($){
4
+ "use strict";
5
+ var tsp,
6
+ ts = $.tablesorter;
7
+
8
+ ts.addWidget({
9
+ id: "pager",
10
+ priority: 55, // load pager after filter widget
11
+ options : {
12
+ // output default: '{page}/{totalPages}'
13
+ // possible variables: {page}, {totalPages}, {filteredPages}, {startRow}, {endRow}, {filteredRows} and {totalRows}
14
+ pager_output: '{startRow} to {endRow} of {totalRows} rows', // '{page}/{totalPages}'
15
+
16
+ // apply disabled classname to the pager arrows when the rows at either extreme is visible
17
+ pager_updateArrows: true,
18
+
19
+ // starting page of the pager (zero based index)
20
+ pager_startPage: 0,
21
+
22
+ // Number of visible rows
23
+ pager_size: 10,
24
+
25
+ // Save pager page & size if the storage script is loaded (requires $.tablesorter.storage in jquery.tablesorter.widgets.js)
26
+ pager_savePages: true,
27
+
28
+ //defines custom storage key
29
+ pager_storageKey: 'tablesorter-pager',
30
+
31
+ // if true, the table will remain the same height no matter how many records are displayed. The space is made up by an empty
32
+ // table row set to a height to compensate; default is false
33
+ pager_fixedHeight: false,
34
+
35
+ // count child rows towards the set page size? (set true if it is a visible table row within the pager)
36
+ // if true, child row(s) may not appear to be attached to its parent row, may be split across pages or
37
+ // may distort the table if rowspan or cellspans are included.
38
+ pager_countChildRows: false,
39
+
40
+ // remove rows from the table to speed up the sort of large tables.
41
+ // setting this to false, only hides the non-visible rows; needed if you plan to add/remove rows with the pager enabled.
42
+ pager_removeRows: false, // removing rows in larger tables speeds up the sort
43
+
44
+ // use this format: "http://mydatabase.com?page={page}&size={size}&{sortList:col}&{filterList:fcol}"
45
+ // where {page} is replaced by the page number, {size} is replaced by the number of records to show,
46
+ // {sortList:col} adds the sortList to the url into a "col" array, and {filterList:fcol} adds
47
+ // the filterList to the url into an "fcol" array.
48
+ // So a sortList = [[2,0],[3,0]] becomes "&col[2]=0&col[3]=0" in the url
49
+ // and a filterList = [[2,Blue],[3,13]] becomes "&fcol[2]=Blue&fcol[3]=13" in the url
50
+ pager_ajaxUrl: null,
51
+
52
+ // modify the url after all processing has been applied
53
+ pager_customAjaxUrl: function(table, url) { return url; },
54
+
55
+ // modify the $.ajax object to allow complete control over your ajax requests
56
+ pager_ajaxObject: {
57
+ dataType: 'json'
58
+ },
59
+
60
+ // set this to false if you want to block ajax loading on init
61
+ pager_processAjaxOnInit: true,
62
+
63
+ // process ajax so that the following information is returned:
64
+ // [ total_rows (number), rows (array of arrays), headers (array; optional) ]
65
+ // example:
66
+ // [
67
+ // 100, // total rows
68
+ // [
69
+ // [ "row1cell1", "row1cell2", ... "row1cellN" ],
70
+ // [ "row2cell1", "row2cell2", ... "row2cellN" ],
71
+ // ...
72
+ // [ "rowNcell1", "rowNcell2", ... "rowNcellN" ]
73
+ // ],
74
+ // [ "header1", "header2", ... "headerN" ] // optional
75
+ // ]
76
+ pager_ajaxProcessing: function(ajax){ return [ 0, [], null ]; },
77
+
78
+ // css class names of pager arrows
79
+ pager_css: {
80
+ container : 'tablesorter-pager',
81
+ errorRow : 'tablesorter-errorRow', // error information row (don't include period at beginning)
82
+ disabled : 'disabled' // class added to arrows @ extremes (i.e. prev/first arrows "disabled" on first page)
83
+ },
84
+
85
+ // jQuery selectors
86
+ pager_selectors: {
87
+ container : '.pager', // target the pager markup
88
+ first : '.first', // go to first page arrow
89
+ prev : '.prev', // previous page arrow
90
+ next : '.next', // next page arrow
91
+ last : '.last', // go to last page arrow
92
+ goto : '.gotoPage', // go to page selector - select dropdown that sets the current page
93
+ pageDisplay : '.pagedisplay', // location of where the "output" is displayed
94
+ pageSize : '.pagesize' // page size selector - select dropdown that sets the "size" option
95
+ }
96
+ },
97
+ init: function(table){
98
+ tsp.init(table);
99
+ },
100
+ // only update to complete sorter initialization
101
+ format: function(table, c){
102
+ if (!(c.pager && c.pager.initialized)){
103
+ return tsp.initComplete(table, c);
104
+ }
105
+ tsp.moveToPage(table, c.pager, false);
106
+ },
107
+ remove: function(table, c){
108
+ tsp.destroyPager(table, c);
109
+ }
110
+ });
111
+
112
+ /* pager widget functions */
113
+ tsp = ts.pager = {
114
+
115
+ init: function(table) {
116
+ // check if tablesorter has initialized
117
+ if (table.hasInitialized && table.config.pager.initialized) { return; }
118
+ var t,
119
+ c = table.config,
120
+ wo = c.widgetOptions,
121
+ s = wo.pager_selectors,
122
+
123
+ // save pager variables
124
+ p = c.pager = $.extend({
125
+ totalPages: 0,
126
+ filteredRows: 0,
127
+ filteredPages: 0,
128
+ currentFilters: [],
129
+ page: wo.pager_startPage,
130
+ size: wo.pager_size,
131
+ startRow: 0,
132
+ endRow: 0,
133
+ ajaxCounter: 0,
134
+ $size: null,
135
+ last: {}
136
+ }, c.pager);
137
+
138
+ // pager initializes multiple times before table has completed initialization
139
+ if (p.isInitializing) { return; }
140
+
141
+ p.isInitializing = true;
142
+ if (c.debug) {
143
+ ts.log('Pager initializing');
144
+ }
145
+
146
+ // added in case the pager is reinitialized after being destroyed.
147
+ p.$container = $(s.container).addClass(wo.pager_css.container).show();
148
+ // goto selector
149
+ p.$goto = p.$container.find(s.goto);
150
+ // page size selector
151
+ p.$size = p.$container.find(s.pageSize);
152
+ p.totalRows = c.$tbodies.eq(0).children().length;
153
+
154
+ p.oldAjaxSuccess = p.oldAjaxSuccess || wo.pager_ajaxObject.success;
155
+ c.appender = tsp.appender;
156
+ if (ts.filter && $.inArray('filter', c.widgets) >= 0) {
157
+ // get any default filter settings (data-value attribute) fixes #388
158
+ p.currentFilters = c.$table.data('lastSearch') || ts.filter.setDefaults(table, c, wo) || [];
159
+ // set, but don't apply current filters
160
+ ts.setFilters(table, p.currentFilters, false);
161
+ }
162
+ if (wo.pager_savePages && ts.storage) {
163
+ t = ts.storage(table, wo.pager_storageKey) || {}; // fixes #387
164
+ p.page = isNaN(t.page) ? p.page : t.page;
165
+ p.size = ( isNaN(t.size) ? p.size : t.size ) || 10;
166
+ $.data(table, 'pagerLastSize', p.size);
167
+ }
168
+ // clear initialized flag
169
+ p.initialized = false;
170
+ // before initialization event
171
+ c.$table.trigger('pagerBeforeInitialized', c);
172
+
173
+ tsp.enablePager(table, c, false);
174
+
175
+ if ( typeof(wo.pager_ajaxUrl) === 'string' ) {
176
+ // ajax pager; interact with database
177
+ p.ajax = true;
178
+ // When filtering with ajax, allow only custom filtering function, disable default filtering since it will be done server side.
179
+ wo.filter_serversideFiltering = true;
180
+ c.serverSideSorting = true;
181
+ tsp.moveToPage(table, p);
182
+ } else {
183
+ p.ajax = false;
184
+ // Regular pager; all rows stored in memory
185
+ c.$table.trigger("appendCache", true);
186
+ tsp.hideRowsSetup(table, c);
187
+ }
188
+
189
+ },
190
+
191
+ initComplete: function(table, c){
192
+ var p = c.pager;
193
+ tsp.changeHeight(table, c);
194
+ tsp.bindEvents(table, c);
195
+
196
+ // pager initialized
197
+ p.initialized = true;
198
+ p.isInitializing = false;
199
+ tsp.setPageSize(table, 0, c); // page size 0 is ignored
200
+ c.$table.trigger('pagerInitialized', c);
201
+
202
+ },
203
+
204
+ bindEvents: function(table, c){
205
+ var ctrls, fxn,
206
+ p = c.pager,
207
+ wo = c.widgetOptions,
208
+ s = wo.pager_selectors;
209
+
210
+ c.$table
211
+ .unbind('filterStart filterEnd sortEnd disable enable destroy update updateRows updateAll addRows pageSize '.split(' ').join('.pager '))
212
+ .bind('filterStart.pager', function(e, filters) {
213
+ p.currentFilters = filters;
214
+ p.page = 0; // fixes #456
215
+ })
216
+ // update pager after filter widget completes
217
+ .bind('filterEnd.pager sortEnd.pager', function() {
218
+ if (p.initialized) {
219
+ tsp.moveToPage(table, p, false);
220
+ tsp.updatePageDisplay(table, c, false);
221
+ tsp.fixHeight(table, c);
222
+ }
223
+ })
224
+ .bind('disable.pager', function(e){
225
+ e.stopPropagation();
226
+ tsp.showAllRows(table, c);
227
+ })
228
+ .on('enable.pager', function(e){
229
+ e.stopPropagation();
230
+ tsp.enablePager(table, c, true);
231
+ })
232
+ .on('destroy.pager', function(e){
233
+ e.stopPropagation();
234
+ tsp.destroyPager(table, c);
235
+ })
236
+ .on('update updateRows updateAll addRows '.split(' ').join('.pager '), function(e){
237
+ e.stopPropagation();
238
+ tsp.hideRows(table, c);
239
+ // make sure widgets are applied - fixes #450
240
+ c.$table.trigger('applyWidgets');
241
+ })
242
+ .on('pageSize.pager', function(e,v){
243
+ e.stopPropagation();
244
+ tsp.setPageSize(table, parseInt(v, 10) || 10, c);
245
+ tsp.hideRows(table, c);
246
+ tsp.updatePageDisplay(table, c, false);
247
+ if (p.$size.length) { p.$size.val(p.size); } // twice?
248
+ })
249
+ .on('pageSet.pager', function(e,v){
250
+ e.stopPropagation();
251
+ p.page = (parseInt(v, 10) || 1) - 1;
252
+ if (p.$goto.length) { p.$goto.val(c.size); } // twice?
253
+ tsp.moveToPage(table, p);
254
+ tsp.updatePageDisplay(table, c, false);
255
+ });
256
+
257
+ // clicked controls
258
+ ctrls = [ s.first, s.prev, s.next, s.last ];
259
+ fxn = [ 'moveToFirstPage', 'moveToPrevPage', 'moveToNextPage', 'moveToLastPage' ];
260
+ p.$container.find(ctrls.join(','))
261
+ .attr("tabindex", 0)
262
+ .unbind('click.pager')
263
+ .bind('click.pager', function(e){
264
+ e.stopPropagation();
265
+ var i,
266
+ $c = $(this),
267
+ l = ctrls.length;
268
+ if ( !$c.hasClass(wo.pager_css.disabled) ) {
269
+ for (i = 0; i < l; i++) {
270
+ if ($c.is(ctrls[i])) {
271
+ tsp[fxn[i]](table, p);
272
+ break;
273
+ }
274
+ }
275
+ }
276
+ });
277
+
278
+ if ( p.$goto.length ) {
279
+ p.$goto
280
+ .unbind('change')
281
+ .bind('change', function(){
282
+ p.page = $(this).val() - 1;
283
+ tsp.moveToPage(table, p);
284
+ tsp.updatePageDisplay(table, c, false);
285
+ });
286
+ }
287
+
288
+ if ( p.$size.length ) {
289
+ p.$size
290
+ .unbind('change.pager')
291
+ .bind('change.pager', function() {
292
+ p.$size.val( $(this).val() ); // in case there are more than one pagers
293
+ if ( !$(this).hasClass(wo.pager_css.disabled) ) {
294
+ tsp.setPageSize(table, parseInt( $(this).val(), 10 ), c);
295
+ tsp.changeHeight(table, c);
296
+ }
297
+ return false;
298
+ });
299
+ }
300
+
301
+ },
302
+
303
+ // hide arrows at extremes
304
+ pagerArrows: function(c, disable) {
305
+ var p = c.pager,
306
+ dis = !!disable,
307
+ first = dis || p.page === 0,
308
+ tp = Math.min( p.totalPages, p.filteredPages ),
309
+ last = dis || p.page === tp - 1 || p.totalPages === 0,
310
+ wo = c.widgetOptions,
311
+ s = wo.pager_selectors;
312
+ if ( wo.pager_updateArrows ) {
313
+ p.$container.find(s.first + ',' + s.prev).toggleClass(wo.pager_css.disabled, first).attr('aria-disabled', first);
314
+ p.$container.find(s.next + ',' + s.last).toggleClass(wo.pager_css.disabled, last).attr('aria-disabled', last);
315
+ }
316
+ },
317
+
318
+ updatePageDisplay: function(table, c, flag) {
319
+ var i, pg, s, out,
320
+ wo = c.widgetOptions,
321
+ p = c.pager,
322
+ f = c.$table.hasClass('hasFilters') && !wo.pager_ajaxUrl,
323
+ t = (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered') + ',' + c.selectorRemove +
324
+ (wo.pager_countChildRows ? '' : ',.' + c.cssChildRow),
325
+ sz = p.size || 10; // don't allow dividing by zero
326
+ p.$size.add(p.$goto).removeClass(wo.pager_css.disabled).removeAttr('disabled').attr('aria-disabled', 'false');
327
+ p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method
328
+ p.filteredRows = (f) ? c.$tbodies.eq(0).children('tr').not('.' + t).length : p.totalRows;
329
+ p.filteredPages = (f) ? Math.ceil( p.filteredRows / sz ) || 1 : p.totalPages;
330
+ if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) {
331
+ t = (p.size * p.page > p.filteredRows);
332
+ p.startRow = (t) ? 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1);
333
+ p.page = (t) ? 0 : p.page;
334
+ p.endRow = Math.min( p.filteredRows, p.totalRows, p.size * ( p.page + 1 ) );
335
+ out = p.$container.find(wo.pager_selectors.pageDisplay);
336
+ // form the output string (can now get a new output string from the server)
337
+ s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || wo.pager_output : wo.pager_output )
338
+ // {page} = one-based index; {page+#} = zero based index +/- value
339
+ .replace(/\{page([\-+]\d+)?\}/gi, function(m,n){
340
+ return p.totalPages ? p.page + (n ? parseInt(n, 10) : 1) : 0;
341
+ })
342
+ // {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object)
343
+ .replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m){
344
+ var str = m.replace(/[{}\s]/g,''),
345
+ extra = str.split(':'),
346
+ data = p.ajaxData,
347
+ // return zero for default page/row numbers
348
+ deflt = /(rows?|pages?)$/i.test(str) ? 0 : '';
349
+ return extra.length > 1 && data && data[extra[0]] ? data[extra[0]][extra[1]] : p[str] || (data ? data[str] : deflt) || deflt;
350
+ });
351
+ if (out.length) {
352
+ out[ (out[0].tagName === 'INPUT') ? 'val' : 'html' ](s);
353
+ if ( p.$goto.length ) {
354
+ t = '';
355
+ pg = Math.min( p.totalPages, p.filteredPages );
356
+ for ( i = 1; i <= pg; i++ ) {
357
+ t += '<option>' + i + '</option>';
358
+ }
359
+ p.$goto.html(t).val( p.page + 1 );
360
+ }
361
+ }
362
+ }
363
+ tsp.pagerArrows(c);
364
+ if (p.initialized && flag !== false) {
365
+ c.$table.trigger('pagerComplete', c);
366
+ // save pager info to storage
367
+ if (wo.pager_savePages && ts.storage) {
368
+ ts.storage(table, wo.pager_storageKey, {
369
+ page : p.page,
370
+ size : p.size
371
+ });
372
+ }
373
+ }
374
+ },
375
+
376
+ fixHeight: function(table, c) {
377
+ var d, h,
378
+ p = c.pager,
379
+ wo = c.widgetOptions,
380
+ $b = c.$tbodies.eq(0);
381
+ if (wo.pager_fixedHeight) {
382
+ $b.find('tr.pagerSavedHeightSpacer').remove();
383
+ h = $.data(table, 'pagerSavedHeight');
384
+ if (h) {
385
+ d = h - $b.height();
386
+ if ( d > 5 && $.data(table, 'pagerLastSize') === p.size && $b.children('tr:visible').length < p.size ) {
387
+ $b.append('<tr class="pagerSavedHeightSpacer ' + wo.pager_selectors.remove.replace(/(tr)?\./g,'') + '" style="height:' + d + 'px;"></tr>');
388
+ }
389
+ }
390
+ }
391
+ },
392
+
393
+ changeHeight: function(table, c) {
394
+ var $b = c.$tbodies.eq(0);
395
+ $b.find('tr.pagerSavedHeightSpacer').remove();
396
+ $.data(table, 'pagerSavedHeight', $b.height());
397
+ tsp.fixHeight(table, c);
398
+ $.data(table, 'pagerLastSize', c.pager.size);
399
+ },
400
+
401
+ hideRows: function(table, c){
402
+ if (!c.widgetOptions.pager_ajaxUrl) {
403
+ var i,
404
+ p = c.pager,
405
+ wo = c.widgetOptions,
406
+ rows = c.$tbodies.eq(0).children(),
407
+ l = rows.length,
408
+ s = ( p.page * p.size ),
409
+ e = s + p.size,
410
+ f = wo && wo.filter_filteredRow || 'filtered',
411
+ j = 0; // size counter
412
+ for ( i = 0; i < l; i++ ){
413
+ if ( !rows[i].className.match(f) ) {
414
+ rows[i].style.display = ( j >= s && j < e ) ? '' : 'none';
415
+ // don't count child rows
416
+ j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) && !wo.pager_countChildRows ? 0 : 1;
417
+ }
418
+ }
419
+ }
420
+ },
421
+
422
+ hideRowsSetup: function(table, c){
423
+ var p = c.pager;
424
+ p.size = parseInt( p.$size.val(), 10 ) || p.size;
425
+ $.data(table, 'pagerLastSize', p.size);
426
+ tsp.pagerArrows(c);
427
+ if ( !c.widgetOptions.pager_removeRows ) {
428
+ tsp.hideRows(table, c);
429
+ c.$table.on('sortEnd.pager filterEnd.pager', function(){
430
+ tsp.hideRows(table, c);
431
+ });
432
+ }
433
+ },
434
+
435
+ renderAjax: function(data, table, c, xhr, exception){
436
+ var p = c.pager,
437
+ wo = c.widgetOptions;
438
+ // process data
439
+ if ( $.isFunction(wo.pager_ajaxProcessing) ) {
440
+ // ajaxProcessing result: [ total, rows, headers ]
441
+ var i, j, t, hsh, $f, $sh, th, d, l, rr_count,
442
+ $t = c.$table,
443
+ tds = '',
444
+ result = wo.pager_ajaxProcessing(data, table) || [ 0, [] ],
445
+ hl = $t.find('thead th').length;
446
+
447
+ // Clean up any previous error.
448
+ ts.showError(table);
449
+
450
+ if ( exception ) {
451
+ if (c.debug) {
452
+ ts.log('Ajax Error', xhr, exception);
453
+ }
454
+ ts.showError(table, exception.message + ' (' + xhr.status + ')');
455
+ c.$tbodies.eq(0).empty();
456
+ } else {
457
+ // process ajax object
458
+ if (!$.isArray(result)) {
459
+ p.ajaxData = result;
460
+ p.totalRows = result.total;
461
+ th = result.headers;
462
+ d = result.rows;
463
+ } else {
464
+ // allow [ total, rows, headers ] or [ rows, total, headers ]
465
+ t = isNaN(result[0]) && !isNaN(result[1]);
466
+ //ensure a zero returned row count doesn't fail the logical ||
467
+ rr_count = result[t ? 1 : 0];
468
+ p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count;
469
+ d = p.totalRows === 0 ? [""] : result[t ? 0 : 1] || []; // row data
470
+ th = result[2]; // headers
471
+ }
472
+ l = d.length;
473
+ if (d instanceof jQuery) {
474
+ // append jQuery object
475
+ c.$tbodies.eq(0).empty().append(d);
476
+ } else if (l) {
477
+ // build table from array
478
+ for ( i = 0; i < l; i++ ) {
479
+ tds += '<tr>';
480
+ for ( j = 0; j < d[i].length; j++ ) {
481
+ // build tbody cells; watch for data containing HTML markup - see #434
482
+ tds += /^\s*<td/.test(d[i][j]) ? $.trim(d[i][j]) : '<td>' + d[i][j] + '</td>';
483
+ }
484
+ tds += '</tr>';
485
+ }
486
+ // add rows to first tbody
487
+ if (wo.pager_processAjaxOnInit) {
488
+ c.$tbodies.eq(0).html( tds );
489
+ } else {
490
+ wo.pager_processAjaxOnInit = true;
491
+ }
492
+ }
493
+ // only add new header text if the length matches
494
+ if ( th && th.length === hl ) {
495
+ hsh = $t.hasClass('hasStickyHeaders');
496
+ $sh = hsh ? wo.$sticky.children('thead:first').children().children() : '';
497
+ $f = $t.find('tfoot tr:first').children();
498
+ // don't change td headers (may contain pager)
499
+ c.$headers.filter('th').each(function(j){
500
+ var $t = $(this), icn;
501
+ // add new test within the first span it finds, or just in the header
502
+ if ( $t.find('.' + ts.css.icon).length ) {
503
+ icn = $t.find('.' + ts.css.icon).clone(true);
504
+ $t.find('.tablesorter-header-inner').html( th[j] ).append(icn);
505
+ if ( hsh && $sh.length ) {
506
+ icn = $sh.eq(j).find('.' + ts.css.icon).clone(true);
507
+ $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ).append(icn);
508
+ }
509
+ } else {
510
+ $t.find('.tablesorter-header-inner').html( th[j] );
511
+ if (hsh && $sh.length) {
512
+ $sh.eq(j).find('.tablesorter-header-inner').html( th[j] );
513
+ }
514
+ }
515
+ $f.eq(j).html( th[j] );
516
+ });
517
+ }
518
+ }
519
+ if (c.showProcessing) {
520
+ ts.isProcessing(table); // remove loading icon
521
+ }
522
+ // make sure last pager settings are saved, prevents multiple server side calls with
523
+ // the same parameters
524
+ p.last.totalPages = p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) );
525
+ p.last.currentFilters = p.currentFilters;
526
+ p.last.sortList = (c.sortList || []).join(',');
527
+ tsp.updatePageDisplay(table, c);
528
+ tsp.fixHeight(table, c);
529
+ $t.trigger('updateCache', [function(){
530
+ if (p.initialized) {
531
+ // apply widgets after table has rendered
532
+ $t.trigger('applyWidgets');
533
+ $t.trigger('pagerChange', p);
534
+ }
535
+ }]);
536
+ }
537
+ if (!p.initialized) {
538
+ c.$table.trigger('applyWidgets');
539
+ }
540
+ },
541
+
542
+ getAjax: function(table, c){
543
+ var counter,
544
+ url = tsp.getAjaxUrl(table, c),
545
+ $doc = $(document),
546
+ wo = c.widgetOptions,
547
+ p = c.pager;
548
+ if ( url !== '' ) {
549
+ if (c.showProcessing) {
550
+ ts.isProcessing(table, true); // show loading icon
551
+ }
552
+ $doc.on('ajaxError.pager', function(e, xhr, settings, exception) {
553
+ tsp.renderAjax(null, table, c, xhr, exception);
554
+ $doc.unbind('ajaxError.pager');
555
+ });
556
+ counter = ++p.ajaxCounter;
557
+ wo.pager_ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl
558
+ wo.pager_ajaxObject.success = function(data) {
559
+ // Refuse to process old ajax commands that were overwritten by new ones - see #443
560
+ if (counter < p.ajaxCounter){
561
+ return;
562
+ }
563
+ tsp.renderAjax(data, table, c);
564
+ $doc.unbind('ajaxError.pager');
565
+ if (typeof p.oldAjaxSuccess === 'function') {
566
+ p.oldAjaxSuccess(data);
567
+ }
568
+ };
569
+ if (c.debug) {
570
+ ts.log('ajax initialized', wo.pager_ajaxObject);
571
+ }
572
+ $.ajax(wo.pager_ajaxObject);
573
+ }
574
+ },
575
+
576
+ getAjaxUrl: function(table, c) {
577
+ var p = c.pager,
578
+ wo = c.widgetOptions,
579
+ url = (wo.pager_ajaxUrl) ? wo.pager_ajaxUrl
580
+ // allow using "{page+1}" in the url string to switch to a non-zero based index
581
+ .replace(/\{page([\-+]\d+)?\}/, function(s,n){ return p.page + (n ? parseInt(n, 10) : 0); })
582
+ .replace(/\{size\}/g, p.size) : '',
583
+ sl = c.sortList,
584
+ fl = p.currentFilters || $(table).data('lastSearch') || [],
585
+ sortCol = url.match(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/),
586
+ filterCol = url.match(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/),
587
+ arry = [];
588
+ if (sortCol) {
589
+ sortCol = sortCol[1];
590
+ $.each(sl, function(i,v){
591
+ arry.push(sortCol + '[' + v[0] + ']=' + v[1]);
592
+ });
593
+ // if the arry is empty, just add the col parameter... "&{sortList:col}" becomes "&col"
594
+ url = url.replace(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : sortCol );
595
+ arry = [];
596
+ }
597
+ if (filterCol) {
598
+ filterCol = filterCol[1];
599
+ $.each(fl, function(i,v){
600
+ if (v) {
601
+ arry.push(filterCol + '[' + i + ']=' + encodeURIComponent(v));
602
+ }
603
+ });
604
+ // if the arry is empty, just add the fcol parameter... "&{filterList:fcol}" becomes "&fcol"
605
+ url = url.replace(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : filterCol );
606
+ p.currentFilters = fl;
607
+ }
608
+ if ( $.isFunction(wo.pager_customAjaxUrl) ) {
609
+ url = wo.pager_customAjaxUrl(table, url);
610
+ }
611
+ if (c.debug) {
612
+ ts.log('Pager ajax url: ' + url);
613
+ }
614
+ return url;
615
+ },
616
+
617
+ renderTable: function(table, rows) {
618
+ var i, $tb,
619
+ c = table.config,
620
+ p = c.pager,
621
+ wo = c.widgetOptions,
622
+ l = rows && rows.length || 0, // rows may be undefined
623
+ s = ( p.page * p.size ),
624
+ e = ( s + p.size );
625
+ if ( l < 1 ) { return; } // empty table, abort!
626
+ if ( p.page >= p.totalPages ) {
627
+ // lets not render the table more than once
628
+ return tsp.moveToLastPage(table, p);
629
+ }
630
+ p.isDisabled = false; // needed because sorting will change the page and re-enable the pager
631
+ if (p.initialized) { c.$table.trigger('pagerChange', c); }
632
+
633
+ if ( !wo.pager_removeRows ) {
634
+ tsp.hideRows(table, c);
635
+ } else {
636
+ if ( e > rows.length ) {
637
+ e = rows.length;
638
+ }
639
+ ts.clearTableBody(table);
640
+ $tb = ts.processTbody(table, c.$tbodies.eq(0), true);
641
+ for ( i = s; i < e; i++ ) {
642
+ $tb.append(rows[i]);
643
+ }
644
+ ts.processTbody(table, $tb, false);
645
+ }
646
+
647
+ tsp.updatePageDisplay(table, c);
648
+ if ( !p.isDisabled ) { tsp.fixHeight(table, c); }
649
+
650
+ wo.pager_startPage = p.page;
651
+ wo.pager_size = p.size;
652
+ c.$table.trigger('applyWidgets');
653
+
654
+ },
655
+
656
+ showAllRows: function(table, c){
657
+ var p = c.pager,
658
+ wo = c.widgetOptions;
659
+ if ( p.ajax ) {
660
+ tsp.pagerArrows(c, true);
661
+ } else {
662
+ p.isDisabled = true;
663
+ $.data(table, 'pagerLastPage', p.page);
664
+ $.data(table, 'pagerLastSize', p.size);
665
+ p.page = 0;
666
+ p.size = p.totalRows;
667
+ p.totalPages = 1;
668
+ c.$table
669
+ .addClass('pagerDisabled')
670
+ .removeAttr('aria-describedby')
671
+ .find('tr.pagerSavedHeightSpacer').remove();
672
+ tsp.renderTable(table, c.rowsCopy);
673
+ if (c.debug) {
674
+ ts.log('pager disabled');
675
+ }
676
+ }
677
+ // disable size selector
678
+ p.$size.add(p.$goto).each(function(){
679
+ $(this).attr('aria-disabled', 'true').addClass(wo.pager_css.disabled)[0].disabled = true;
680
+ });
681
+ },
682
+
683
+ moveToPage: function(table, p, flag) {
684
+ if ( p.isDisabled ) { return; }
685
+ var c = table.config,
686
+ l = p.last,
687
+ pg = Math.min( p.totalPages, p.filteredPages );
688
+ if ( p.page < 0 ) { p.page = 0; }
689
+ if ( p.page > ( pg - 1 ) && pg !== 0 ) { p.page = pg - 1; }
690
+ // don't allow rendering multiple times on the same page/size/totalpages/filters/sorts
691
+ if ( l.page === p.page && l.size === p.size && l.totalPages === p.totalPages &&
692
+ (l.currentFilters || []).join(',') === (p.currentFilters || []).join(',') &&
693
+ l.sortList === (c.sortList || []).join(',') ) {
694
+ return;
695
+ }
696
+ if (c.debug) {
697
+ ts.log('Pager changing to page ' + p.page);
698
+ }
699
+ p.last = {
700
+ page : p.page,
701
+ size : p.size,
702
+ // fixes #408; modify sortList otherwise it auto-updates
703
+ sortList : (c.sortList || []).join(','),
704
+ totalPages : p.totalPages,
705
+ currentFilters : p.currentFilters || []
706
+ };
707
+ if (p.ajax) {
708
+ tsp.getAjax(table, c);
709
+ } else if (!p.ajax) {
710
+ tsp.renderTable(table, c.rowsCopy);
711
+ }
712
+ $.data(table, 'pagerLastPage', p.page);
713
+ if (p.initialized && flag !== false) {
714
+ c.$table.trigger('pageMoved', c);
715
+ }
716
+ },
717
+
718
+ setPageSize: function(table, size, c) {
719
+ var p = c.pager;
720
+ p.size = size || p.size || 10;
721
+ p.$size.val(p.size);
722
+ $.data(table, 'pagerLastPage', p.page);
723
+ $.data(table, 'pagerLastSize', p.size);
724
+ p.totalPages = Math.ceil( p.totalRows / p.size );
725
+ tsp.moveToPage(table, p);
726
+ },
727
+
728
+ moveToFirstPage: function(table, p) {
729
+ p.page = 0;
730
+ tsp.moveToPage(table, p);
731
+ },
732
+
733
+ moveToLastPage: function(table, p) {
734
+ p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 );
735
+ tsp.moveToPage(table, p);
736
+ },
737
+
738
+ moveToNextPage: function(table, p) {
739
+ p.page++;
740
+ if ( p.page >= ( Math.min( p.totalPages, p.filteredPages ) - 1 ) ) {
741
+ p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 );
742
+ }
743
+ tsp.moveToPage(table, p);
744
+ },
745
+
746
+ moveToPrevPage: function(table, p) {
747
+ p.page--;
748
+ if ( p.page <= 0 ) {
749
+ p.page = 0;
750
+ }
751
+ tsp.moveToPage(table, p);
752
+ },
753
+
754
+ destroyPager: function(table, c){
755
+ var p = c.pager;
756
+ tsp.showAllRows(table, c);
757
+ p.$container.hide(); // hide pager
758
+ c.appender = null; // remove pager appender function
759
+ p.initialized = false;
760
+ c.$table.unbind('destroy.pager sortEnd.pager filterEnd.pager enable.pager disable.pager');
761
+ if (ts.storage) {
762
+ ts.storage(table, c.widgetOptions.pager_storageKey, '');
763
+ }
764
+ },
765
+
766
+ enablePager: function(table, c, triggered){
767
+ var info, p = c.pager;
768
+ p.isDisabled = false;
769
+ p.page = $.data(table, 'pagerLastPage') || p.page || 0;
770
+ p.size = $.data(table, 'pagerLastSize') || parseInt(p.$size.find('option[selected]').val(), 10) || p.size || 10;
771
+ p.$size.val(p.size); // set page size
772
+ p.totalPages = Math.ceil( Math.min( p.totalRows, p.filteredRows ) / p.size );
773
+ c.$table.removeClass('pagerDisabled');
774
+ // if table id exists, include page display with aria info
775
+ if ( table.id ) {
776
+ info = table.id + '_pager_info';
777
+ p.$container.find(c.widgetOptions.pager_selectors.pageDisplay).attr('id', info);
778
+ c.$table.attr('aria-describedby', info);
779
+ }
780
+ if ( triggered ) {
781
+ c.$table.trigger('updateRows');
782
+ tsp.setPageSize(table, p.size, c);
783
+ tsp.hideRowsSetup(table, c);
784
+ tsp.fixHeight(table, c);
785
+ if (c.debug) {
786
+ ts.log('pager enabled');
787
+ }
788
+ }
789
+ },
790
+
791
+ appender: function(table, rows) {
792
+ var c = table.config,
793
+ wo = c.widgetOptions,
794
+ p = c.pager;
795
+ if ( !p.ajax ) {
796
+ c.rowsCopy = rows;
797
+ p.totalRows = c.widgetOptions.pager_countChildRows ? c.$tbodies.eq(0).children().length : rows.length;
798
+ p.size = $.data(table, 'pagerLastSize') || p.size || wo.pager_size || 10;
799
+ p.totalPages = Math.ceil( p.totalRows / p.size );
800
+ tsp.moveToPage(table, p, true);
801
+ // update display here in case all rows are removed
802
+ tsp.updatePageDisplay(table, c, false);
803
+ }
804
+ }
805
+
806
+ };
807
+
808
+ // see #486
809
+ ts.showError = function(table, message){
810
+ $(table).each(function(){
811
+ var $row,
812
+ c = this.config,
813
+ errorRow = c.pager && c.pager.cssErrorRow || c.widgetOptions.pager_css && c.widgetOptions.pager_css.errorRow || 'tablesorter-errorRow';
814
+ if (c) {
815
+ if (typeof message === 'undefined') {
816
+ c.$table.find('thead').find(c.selectorRemove).remove();
817
+ } else {
818
+ $row = ( /tr\>/.test(message) ? $(message) : $('<tr><td colspan="' + c.columns + '">' + message + '</td></tr>') )
819
+ .click(function(){
820
+ $(this).remove();
821
+ })
822
+ // add error row to thead instead of tbody, or clicking on the header will result in a parser error
823
+ .appendTo( c.$table.find('thead:first') )
824
+ .addClass( errorRow + ' ' + c.selectorRemove.replace(/^[.#]/, '') )
825
+ .attr({
826
+ role : 'alert',
827
+ 'aria-live' : 'assertive'
828
+ });
829
+ }
830
+ }
831
+ });
832
+ };
833
+
834
+ })(jQuery);