jquery-tablesorter 1.12.8 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/jquery-tablesorter/version.rb +1 -1
  4. data/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +115 -67
  5. data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +62 -40
  6. data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +251 -118
  7. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-extract.js +48 -24
  8. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-iso8601.js +5 -4
  9. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-month.js +16 -10
  10. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-two-digit-year.js +26 -19
  11. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date-weekday.js +16 -10
  12. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-date.js +7 -4
  13. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-named-numbers.js +121 -0
  14. data/vendor/assets/javascripts/jquery-tablesorter/parsers/{parser-ipv6.js → parser-network.js} +66 -17
  15. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +10 -7
  16. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js +62 -35
  17. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +197 -174
  18. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +3 -6
  19. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +145 -62
  20. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js +1 -1
  21. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +16 -14
  22. data/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css +2 -2
  23. data/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css +2 -2
  24. data/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css +2 -2
  25. data/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css +2 -2
  26. data/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css +2 -2
  27. data/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css +2 -2
  28. data/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css +2 -2
  29. data/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css +2 -2
  30. data/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css +2 -2
  31. data/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css +4 -1
  32. data/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css +4 -4
  33. metadata +4 -3
@@ -1,15 +1,18 @@
1
- /*! IPv6 Address parser (WIP)
2
- * IPv6 Address (ffff:0000:0000:0000:0000:0000:0000:0000)
3
- * needs to support short versions like "::8" or "1:2::7:8"
4
- * and "::00:192.168.10.184" (embedded IPv4 address)
5
- * see http://www.intermapper.com/support/tools/IPV6-Validator.aspx
6
- */
1
+ /*! Network parsers - IPv4, IPv6 and MAC Addresses - 10/26/2014 (v2.18.0) */
7
2
  /*global jQuery: false */
8
3
  ;(function($){
9
4
  "use strict";
10
5
 
11
- var ts = $.tablesorter;
6
+ var ts = $.tablesorter,
7
+ ipv4Format,
8
+ ipv4Is;
12
9
 
10
+ /*! IPv6 Address parser (WIP)
11
+ * IPv6 Address (ffff:0000:0000:0000:0000:0000:0000:0000)
12
+ * needs to support short versions like "::8" or "1:2::7:8"
13
+ * and "::00:192.168.10.184" (embedded IPv4 address)
14
+ * see http://www.intermapper.com/support/tools/IPV6-Validator.aspx
15
+ */
13
16
  $.extend( ts.regex, {}, {
14
17
  ipv4Validate : /((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})/,
15
18
  ipv4Extract : /([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/,
@@ -20,14 +23,14 @@
20
23
  });
21
24
 
22
25
  ts.addParser({
23
- id: "ipv6Address",
26
+ id: 'ipv6Address',
24
27
  is: function(s) {
25
28
  return ts.regex.ipv6Validate.test(s);
26
29
  },
27
30
  format: function(address, table) {
28
31
  // code modified from http://forrst.com/posts/JS_Expand_Abbreviated_IPv6_Addresses-1OR
29
32
  var i, t, sides, groups, groupsPresent,
30
- hex = table ? (typeof table === "boolean" ? table : table && table.config.ipv6HexFormat || false) : false,
33
+ hex = table ? (typeof table === 'boolean' ? table : table && table.config.ipv6HexFormat || false) : false,
31
34
  fullAddress = '',
32
35
  expandedAddress = '',
33
36
  validGroupCount = 8,
@@ -44,23 +47,23 @@
44
47
  address = address.replace( ts.regex.ipv4Extract, t );
45
48
  }
46
49
 
47
- if (address.indexOf("::") == -1) {
50
+ if (address.indexOf('::') == -1) {
48
51
  // All eight groups are present
49
52
  fullAddress = address;
50
53
  } else {
51
- // Consecutive groups of zeroes have been collapsed with "::".
52
- sides = address.split("::");
54
+ // Consecutive groups of zeroes have been collapsed with '::'.
55
+ sides = address.split('::');
53
56
  groupsPresent = 0;
54
57
  for (i = 0; i < sides.length; i++) {
55
- groupsPresent += sides[i].split(":").length;
58
+ groupsPresent += sides[i].split(':').length;
56
59
  }
57
- fullAddress += sides[0] + ":";
60
+ fullAddress += sides[0] + ':';
58
61
  for (i = 0; i < validGroupCount - groupsPresent; i++) {
59
- fullAddress += "0000:";
62
+ fullAddress += '0000:';
60
63
  }
61
64
  fullAddress += sides[1];
62
65
  }
63
- groups = fullAddress.split(":");
66
+ groups = fullAddress.split(':');
64
67
  for (i = 0; i < validGroupCount; i++) {
65
68
  // it's fastest & easiest for tablesorter to sort decimal values (vs hex)
66
69
  groups[i] = hex ? ('0000' + groups[i]).slice(-4) :
@@ -70,7 +73,53 @@
70
73
  return hex ? expandedAddress : expandedAddress.replace(/:/g, '');
71
74
  },
72
75
  // uses natural sort hex compare
73
- type: "numeric"
76
+ type: 'numeric'
77
+ });
78
+
79
+ // ipv4 address
80
+ // moved here from jquery.tablesorter.js (core file)
81
+ ipv4Is = function(s) {
82
+ return (/^\d{1,3}[\.]\d{1,3}[\.]\d{1,3}[\.]\d{1,3}$/).test(s);
83
+ };
84
+ ipv4Format = function(s, table) {
85
+ var i, a = s ? s.split('.') : '',
86
+ r = '',
87
+ l = a.length;
88
+ for (i = 0; i < l; i++) {
89
+ r += ('000' + a[i]).slice(-3);
90
+ }
91
+ return s ? ts.formatFloat(r, table) : s;
92
+ };
93
+
94
+ // duplicate "ipAddress" as "ipv4Address" (to maintain backwards compatility)
95
+ ts.addParser({
96
+ id: 'ipAddress',
97
+ is: ipv4Is,
98
+ format: ipv4Format,
99
+ type: 'numeric'
100
+ });
101
+ ts.addParser({
102
+ id: 'ipv4Address',
103
+ is: ipv4Is,
104
+ format: ipv4Format,
105
+ type: 'numeric'
106
+ });
107
+
108
+ ts.addParser({
109
+ id: 'MAC',
110
+ is: function(s) {
111
+ return ts.regex.ipv6Validate.test(s);
112
+ },
113
+ format: function(s) {
114
+ var t = '',
115
+ val = s.replace(/[:.-]/g, '').match(/\w{2}/g);
116
+ $.each(val, function(i, v){
117
+ t += ( '000' + parseInt(v, 16) ).slice(-3);
118
+ });
119
+ return t;
120
+ },
121
+ // uses natural sort hex compare
122
+ type: 'numeric'
74
123
  });
75
124
 
76
125
  })(jQuery);
@@ -1,4 +1,4 @@
1
- /* Column Selector/Responsive table widget (beta) for TableSorter 5/22/2014 (v2.17.0)
1
+ /* Column Selector/Responsive table widget (beta) for TableSorter - 10/26/2014 (v2.18.0)
2
2
  * Requires tablesorter v2.8+ and jQuery 1.7+
3
3
  * by Justin Hallett & Rob Garrison
4
4
  */
@@ -11,8 +11,8 @@ var ts = $.tablesorter,
11
11
  namespace = '.tscolsel',
12
12
  tsColSel = ts.columnSelector = {
13
13
 
14
- queryAll : '@media only all { [columns] { display: none; } }',
15
- queryBreak : '@media all and (min-width: [size]) { [columns] { display: table-cell; } }',
14
+ queryAll : '@media only all { [columns] { display: none; } } ',
15
+ queryBreak : '@media all and (min-width: [size]) { [columns] { display: table-cell; } } ',
16
16
 
17
17
  init: function(table, c, wo) {
18
18
  var $t, colSel;
@@ -81,7 +81,6 @@ tsColSel = ts.columnSelector = {
81
81
  colId = $this.attr('data-column'),
82
82
  state = ts.getData(this, c.headers[colId], 'columnSelector');
83
83
 
84
-
85
84
  // if this column not hidable at all
86
85
  // include getData check (includes "columnSelector-false" class, data attribute, etc)
87
86
  if ( isNaN(priority) && priority.length > 0 || state === 'disable' ||
@@ -191,6 +190,7 @@ tsColSel = ts.columnSelector = {
191
190
  breaks = [];
192
191
  c.$headers.filter('[' + wo.columnSelector_priority + '=' + (priority + 1) + ']').each(function(){
193
192
  column = parseInt($(this).attr('data-column'), 10) + 1;
193
+ breaks.push(prefix + ' col:nth-child(' + column + ')');
194
194
  breaks.push(prefix + ' tr th:nth-child(' + column + ')');
195
195
  breaks.push(prefix + ' tr td:nth-child(' + column + ')');
196
196
  });
@@ -204,9 +204,11 @@ tsColSel = ts.columnSelector = {
204
204
  if (colSel.$style) {
205
205
  colSel.$style.prop('disabled', true);
206
206
  }
207
- colSel.$breakpoints
208
- .prop('disabled', false)
209
- .html( tsColSel.queryAll.replace(/\[columns\]/g, mediaAll.join(',')) + breakpts );
207
+ if (mediaAll.length) {
208
+ colSel.$breakpoints
209
+ .prop('disabled', false)
210
+ .html( tsColSel.queryAll.replace(/\[columns\]/g, mediaAll.join(',')) + breakpts );
211
+ }
210
212
  },
211
213
 
212
214
  updateCols: function(c, wo) {
@@ -220,6 +222,7 @@ tsColSel = ts.columnSelector = {
220
222
  colSel.$container.find('input[data-column]').filter('[data-column!="auto"]').each(function(){
221
223
  if (!this.checked) {
222
224
  column = parseInt( $(this).attr('data-column'), 10 ) + 1;
225
+ styles.push(prefix + ' col:nth-child(' + column + ')');
223
226
  styles.push(prefix + ' tr th:nth-child(' + column + ')');
224
227
  styles.push(prefix + ' tr td:nth-child(' + column + ')');
225
228
  }
@@ -1,68 +1,95 @@
1
- /*! tablesorter CSS Sticky Headers widget - updated 5/5/2014 (v2.16.4)
1
+ /*! tablesorter CSS Sticky Headers widget - updated 10/26/2014 (v2.18.0)
2
2
  * Requires a modern browser, tablesorter v2.8+
3
3
  */
4
4
  /*jshint jquery:true, unused:false */
5
5
  ;(function($){
6
- "use strict";
6
+ 'use strict';
7
7
 
8
- $.tablesorter.addWidget({
9
- id: "cssStickyHeaders",
8
+ var ts = $.tablesorter;
9
+
10
+ ts.addWidget({
11
+ id: 'cssStickyHeaders',
10
12
  priority: 10,
11
13
  options: {
12
14
  cssStickyHeaders_offset : 0,
13
15
  cssStickyHeaders_addCaption : false,
16
+ // jQuery selector or object to attach sticky header to
14
17
  cssStickyHeaders_attachTo : null,
15
- cssStickyHeaders_filteredToTop : true,
16
- cssStickyHeaders_zIndex : 10
18
+ cssStickyHeaders_filteredToTop : true
17
19
  },
18
20
  init : function(table, thisWidget, c, wo) {
19
- var $attach = $(wo.cssStickyHeaders_attachTo),
20
- namespace = '.cssstickyheader',
21
- $thead = c.$table.children('thead'),
22
- $caption = c.$table.find('caption'),
23
- $win = $attach.length ? $attach : $(window);
24
- $win.bind('scroll resize '.split(' ').join(namespace + ' '), function() {
21
+ var isIE = 'ActiveXObject' in window, // target all versions of IE
22
+ $table = c.$table,
23
+ $attach = $(wo.cssStickyHeaders_attachTo),
24
+ namespace = c.namespace + 'cssstickyheader ',
25
+ $thead = $table.children('thead'),
26
+ $caption = $table.children('caption'),
27
+ $win = $attach.length ? $attach : $(window),
28
+ $parent = $table.parent().closest('table.' + ts.css.table),
29
+ $parentThead = $parent.length && ts.hasWidget($parent[0], 'cssStickyHeaders') ? $parent.children('thead') : [];
30
+
31
+ $win
32
+ .unbind('scroll resize '.split(' ').join(namespace))
33
+ .bind('scroll resize '.split(' ').join(namespace), function() {
25
34
  var top = $attach.length ? $attach.offset().top : $win.scrollTop(),
26
35
  // add caption height; include table padding top & border-spacing or text may be above the fold (jQuery UI themes)
27
36
  // border-spacing needed in Firefox, but not webkit... not sure if I should account for that
28
- captionTop = wo.cssStickyHeaders_addCaption ? $caption.outerHeight(true) +
29
- (parseInt(c.$table.css('padding-top'), 10) || 0) + (parseInt(c.$table.css('border-spacing'), 10) || 0) : 0,
30
- bottom = c.$table.height() - $thead.height() - (c.$table.find('tfoot').height() || 0) - captionTop,
31
- deltaY = top - $thead.offset().top + (parseInt(c.$table.css('border-top-width'), 10) || 0) +
32
- (wo.cssStickyHeaders_offset || 0) + captionTop,
33
- finalY = (deltaY > 0 && deltaY <= bottom ? deltaY : 0),
34
- // IE can only transform header cells - fixes #447 thanks to @gakreol!
35
- $cells = $thead.children().children();
37
+ captionHeight = wo.cssStickyHeaders_addCaption ? ( $caption.outerHeight(true) || 0 ) +
38
+ ( parseInt( $table.css('padding-top'), 10 ) || 0 ) + ( parseInt( $table.css('border-spacing'), 10 ) || 0 ) : 0,
39
+
40
+ bottom = $table.height() - $thead.height() - ( $table.children('tfoot').height() || 0 ) - captionHeight,
41
+ // get bottom of nested sticky headers
42
+ nestedStickyTop = $parentThead.length ? ( isIE ? $parent.data('cssStickyHeaderTop') : $parentThead.offset().top ) +
43
+ $parentThead.height() - $win.scrollTop() : 0,
44
+
45
+ // Detect nested tables - fixes #724
46
+ deltaY = top - $table.offset().top + nestedStickyTop + ( parseInt( $table.css('border-top-width'), 10 ) || 0 ) +
47
+ // Again, I dislike browser sniffing... but I have no idea why I need to include a captionHeight
48
+ // for Firefox here and not for Chrome. Even IE behaves, sorta!
49
+ ( wo.cssStickyHeaders_offset || 0 ) + ( navigator.userAgent.toLowerCase().indexOf('firefox') > -1 ? captionHeight : 0 ),
50
+
51
+ finalY = deltaY > 0 && deltaY <= bottom ? deltaY : 0,
52
+
53
+ // All IE (even IE11) can only transform header cells - fixes #447 thanks to @gakreol!
54
+ $cells = isIE ? $thead.children().children() : $thead;
55
+
56
+ // more crazy IE stuff.. somehow the second nested table is completely ignored
57
+ if (isIE) {
58
+ c.$table.data('cssStickyHeaderTop', finalY - ( $parentThead.length ? $parentThead.height() : 0 ));
59
+ if ($parentThead.length) {
60
+ top = $parent.data('cssStickyHeaderTop') - $parentThead.height();
61
+ finalY = top > 0 && top <= bottom ? top : 0;
62
+ }
63
+ }
64
+
36
65
  if (wo.cssStickyHeaders_addCaption) {
37
66
  $cells = $cells.add($caption);
38
67
  }
68
+
39
69
  $cells.css({
40
- "position" : "relative",
41
- "z-index" : wo.cssStickyHeaders_zIndex,
42
- "transform" : finalY === 0 ? "" : "translate(0px," + finalY + "px)",
43
- "-ms-transform" : finalY === 0 ? "" : "translate(0px," + finalY + "px)",
44
- "-webkit-transform" : finalY === 0 ? "" : "translate(0px," + finalY + "px)"
70
+ 'transform' : finalY === 0 ? '' : 'translate(0px,' + finalY + 'px)',
71
+ '-ms-transform' : finalY === 0 ? '' : 'translate(0px,' + finalY + 'px)',
72
+ '-webkit-transform' : finalY === 0 ? '' : 'translate(0px,' + finalY + 'px)'
45
73
  });
46
74
  });
47
- c.$table.bind('filterEnd', function() {
75
+ $table.unbind('filterEnd' + namespace).bind('filterEnd' + namespace, function() {
48
76
  if (wo.cssStickyHeaders_filteredToTop) {
49
77
  // scroll top of table into view
50
- window.scrollTo(0, c.$table.position().top);
78
+ window.scrollTo(0, $table.position().top);
51
79
  }
52
80
  });
53
81
 
54
82
  },
55
83
  remove: function(table, c, wo){
56
- var namespace = '.cssstickyheader';
57
- $(window).unbind('scroll resize '.split(' ').join(namespace + ' '));
84
+ var namespace = c.namespace + 'cssstickyheader ';
85
+ $(window).unbind('scroll resize '.split(' ').join(namespace));
58
86
  c.$table
59
- .unbind('update updateAll '.split(' ').join(namespace + ' '))
87
+ .unbind('filterEnd scroll resize '.split(' ').join(namespace))
88
+ .add( c.$table.children('thead').children().children() )
60
89
  .children('thead, caption').css({
61
- "position" : "",
62
- "z-index" : "",
63
- "transform" : "",
64
- "-ms-transform" : "",
65
- "-webkit-transform" : ""
90
+ 'transform' : '',
91
+ '-ms-transform' : '',
92
+ '-webkit-transform' : ''
66
93
  });
67
94
  }
68
95
  });
@@ -1,200 +1,223 @@
1
- /*! tablesorter Editable Content widget - updated 9/15/2014 (core v2.17.8)
1
+ /*! tablesorter Editable Content widget - updated 10/26/2014 (v2.18.0)
2
2
  * Requires tablesorter v2.8+ and jQuery 1.7+
3
3
  * by Rob Garrison
4
4
  */
5
5
  /*jshint browser:true, jquery:true, unused:false */
6
6
  /*global jQuery: false */
7
- ;(function($){
8
- "use strict";
7
+ ;( function( $ ){
8
+ 'use strict';
9
9
 
10
- $.tablesorter.addWidget({
11
- id: 'editable',
12
- options : {
13
- editable_columns : [],
14
- editable_enterToAccept : true,
15
- editable_autoAccept : true,
16
- editable_autoResort : false,
17
- editable_wrapContent : '<div>', // wrap the cell content... makes this widget work in IE, and with autocomplete
18
- editable_trimContent : true, // trim content inside of contenteditable (remove tabs & carriage returns)
19
- editable_validate : null, // function(text, originalText){ return text; }
20
- editable_focused : null, // function(text, columnIndex, $element) {}
21
- editable_blur : null, // function(text, columnIndex, $element) { }
22
- editable_selectAll : false, // true/false or function(text, columnIndex, $element) { return true; }
23
- editable_noEdit : 'no-edit',
24
- editable_editComplete : 'editComplete'
25
- },
26
- init: function(table, thisWidget, c, wo){
27
- if ( !wo.editable_columns.length ) { return; }
28
- var indx, tmp, $t,
29
- cols = [],
30
- editComplete = function($cell, refocus){
31
- $cell
32
- .removeClass('tseditable-last-edited-cell')
33
- .trigger( wo.editable_editComplete, [c] );
34
- // restore focus last cell after updating
35
- if (refocus) {
36
- setTimeout(function(){
37
- $cell.focus();
38
- }, 50);
39
- }
40
- },
41
- selectAll = function(cell){
42
- setTimeout(function(){
43
- // select all text in contenteditable
44
- // see http://stackoverflow.com/a/6150060/145346
45
- var range = document.createRange();
46
- range.selectNodeContents(cell);
47
- var sel = window.getSelection();
48
- sel.removeAllRanges();
49
- sel.addRange(range);
50
- }, 100);
51
- };
52
-
53
- if ( $.type(wo.editable_columns) === "string" && wo.editable_columns.indexOf('-') >= 0 ) {
54
- // editable_columns can contain a range string (i.e. "2-4" )
55
- tmp = wo.editable_columns.split('-');
56
- indx = parseInt(tmp[0],10) || 0;
57
- tmp = parseInt(tmp[1],10) || (c.columns - 1);
58
- if ( tmp > c.columns ) { tmp = c.columns - 1; }
59
- for ( ; indx <= tmp; indx++ ) {
60
- cols.push('td:nth-child(' + (indx + 1) + ')');
10
+ var tse = $.tablesorter.editable = {
11
+
12
+ editComplete: function( c, wo, $cell, refocus ) {
13
+ $cell
14
+ .removeClass( 'tseditable-last-edited-cell' )
15
+ .trigger( wo.editable_editComplete, [ c ] );
16
+ // restore focus last cell after updating
17
+ if ( refocus ) {
18
+ setTimeout( function() {
19
+ $cell.focus();
20
+ }, 50 );
21
+ }
22
+ },
23
+
24
+ selectAll: function( cell ) {
25
+ setTimeout( function() {
26
+ // select all text in contenteditable
27
+ // see http://stackoverflow.com/a/6150060/145346
28
+ var sel, range = document.createRange();
29
+ range.selectNodeContents( cell );
30
+ sel = window.getSelection();
31
+ sel.removeAllRanges();
32
+ sel.addRange( range );
33
+ }, 100 );
34
+ },
35
+
36
+ update: function( c, wo ) {
37
+ var indx, tmp, $t,
38
+ cols = [];
39
+
40
+ if ( $.type( wo.editable_columns ) === 'string' && wo.editable_columns.indexOf( '-' ) >= 0 ) {
41
+ // editable_columns can contain a range string ( i.e. '2-4' )
42
+ tmp = wo.editable_columns.split( /\s*-\s*/ );
43
+ indx = parseInt( tmp[ 0 ], 10 ) || 0;
44
+ tmp = parseInt( tmp[ 1 ], 10 ) || ( c.columns - 1 );
45
+ if ( tmp > c.columns ) {
46
+ tmp = c.columns - 1;
47
+ }
48
+ for ( ; indx <= tmp; indx++ ) {
49
+ cols.push( 'td:nth-child(' + ( indx + 1 ) + ')' );
50
+ }
51
+ } else if ( $.isArray( wo.editable_columns ) ) {
52
+ $.each( wo.editable_columns, function( i, col ) {
53
+ if ( col < c.columns ) {
54
+ cols.push( 'td:nth-child(' + ( col + 1 ) + ')' );
61
55
  }
62
- } else if ( $.isArray(wo.editable_columns) ) {
63
- $.each(wo.editable_columns, function(i, col){
64
- if ( col < c.columns ) {
65
- cols.push('td:nth-child(' + (col + 1) + ')');
56
+ });
57
+ }
58
+ tmp = $( '<div>' ).wrapInner( wo.editable_wrapContent ).children().length || $.isFunction( wo.editable_wrapContent );
59
+ // IE does not allow making TR/TH/TD cells directly editable ( issue #404 )
60
+ // so add a div or span inside ( it's faster than using wrapInner() )
61
+ c.$tbodies.find( cols.join( ',' ) ).not( '.' + wo.editable_noEdit ).each( function() {
62
+ // test for children, if they exist, then make the children editable
63
+ $t = $( this );
64
+
65
+ if ( tmp && $t.children().length === 0 ) {
66
+ $t.wrapInner( wo.editable_wrapContent );
67
+ }
68
+ if ( $t.children().length ) {
69
+ // make all children content editable
70
+ $t.children().not( '.' + wo.editable_noEdit ).each( function() {
71
+ var $this = $( this );
72
+ if ( wo.editable_trimContent ) {
73
+ $this.text( function( i, txt ) {
74
+ return $.trim( txt );
75
+ });
66
76
  }
77
+ $this.prop( 'contenteditable', true );
67
78
  });
79
+ } else {
80
+ if ( wo.editable_trimContent ) {
81
+ $t.text( function( i, txt ) {
82
+ return $.trim( txt );
83
+ });
84
+ }
85
+ $t.prop( 'contenteditable', true );
68
86
  }
69
- tmp = $('<div>').wrapInner(wo.editable_wrapContent).children().length || $.isFunction(wo.editable_wrapContent);
70
- // IE does not allow making TR/TH/TD cells directly editable (issue #404)
71
- // so add a div or span inside ( it's faster than using wrapInner() )
72
- c.$tbodies.find( cols.join(',') ).not( '.' + wo.editable_noEdit ).each(function(){
73
- // test for children, if they exist, then make the children editable
74
- $t = $(this);
75
-
76
- if (tmp && $t.children().length === 0) {
77
- $t.wrapInner( wo.editable_wrapContent );
87
+ });
88
+ },
89
+
90
+ bindEvents: function( c, wo ) {
91
+ c.$table
92
+ .off( 'updateComplete pagerComplete '.split( ' ' ).join( '.tseditable' ) )
93
+ .on( 'updateComplete pagerComplete '.split( ' ' ).join( '.tseditable' ), function() {
94
+ tse.update( c, wo );
95
+ });
96
+
97
+ c.$tbodies
98
+ .off( 'mouseleave focus blur focusout keydown '.split( ' ' ).join( '.tseditable ' ) )
99
+ .on( 'mouseleave.tseditable', function() {
100
+ if ( c.$table.data( 'contentFocused' ) ) {
101
+ // change to 'true' instead of element to allow focusout to process
102
+ c.$table.data( 'contentFocused', true );
103
+ $( ':focus' ).trigger( 'focusout' );
78
104
  }
79
- if ($t.children().length) {
80
- // make all children content editable
81
- $t.children().not('.' + wo.editable_noEdit).each(function(){
82
- var $this = $(this);
83
- if (wo.editable_trimContent) {
84
- $this.text(function(i, txt){
85
- return $.trim(txt);
86
- });
105
+ })
106
+ .on( 'focus.tseditable', '[contenteditable]', function( e ) {
107
+ clearTimeout( $( this ).data( 'timer' ) );
108
+ c.$table.data( 'contentFocused', e.target );
109
+ var $this = $( this ),
110
+ selAll = wo.editable_selectAll,
111
+ column = $this.closest( 'td' ).index(),
112
+ txt = $.trim( $this.text() );
113
+ if ( wo.editable_enterToAccept ) {
114
+ // prevent enter from adding into the content
115
+ $this.on( 'keydown.tseditable', function( e ){
116
+ if ( e.which === 13 ) {
117
+ e.preventDefault();
87
118
  }
88
- $this.prop( 'contenteditable', true );
89
119
  });
90
- } else {
91
- if (wo.editable_trimContent) {
92
- $t.text(function(i, txt){
93
- return $.trim(txt);
94
- });
95
- }
96
- $t.prop( 'contenteditable', true );
97
120
  }
98
- });
99
- c.$tbodies
100
- .on('mouseleave.tseditable', function(){
101
- if ( c.$table.data('contentFocused') ) {
102
- // change to "true" instead of element to allow focusout to process
103
- c.$table.data( 'contentFocused', true );
104
- $(':focus').trigger('focusout');
105
- }
106
- })
107
- .on('focus.tseditable', '[contenteditable]', function(e){
108
- clearTimeout( $(this).data('timer') );
109
- c.$table.data( 'contentFocused', e.target );
110
- var $this = $(this),
111
- selAll = wo.editable_selectAll,
112
- column = $this.closest('td').index(),
113
- txt = $.trim( $this.text() );
114
- if (wo.editable_enterToAccept) {
115
- // prevent enter from adding into the content
116
- $this.on('keydown.tseditable', function(e){
117
- if ( e.which === 13 ) {
118
- e.preventDefault();
119
- }
120
- });
121
+ $this.data({ before : txt, original: txt });
122
+
123
+ if ( typeof wo.editable_focused === 'function' ) {
124
+ wo.editable_focused( txt, column, $this );
125
+ }
126
+
127
+ if ( selAll ) {
128
+ if ( typeof selAll === 'function' ) {
129
+ if ( selAll( txt, column, $this ) ) {
130
+ tse.selectAll( $this[0] );
131
+ }
132
+ } else {
133
+ tse.selectAll( $this[0] );
121
134
  }
122
- $this.data({ before : txt, original: txt });
135
+ }
136
+ })
137
+ .on( 'blur focusout keydown '.split( ' ' ).join( '.tseditable ' ), '[contenteditable]', function( e ) {
138
+ if ( !c.$table.data( 'contentFocused' ) ) { return; }
139
+ var t, validate,
140
+ valid = false,
141
+ $this = $( e.target ),
142
+ txt = $.trim( $this.text() ),
143
+ column = $this.closest( 'td' ).index();
144
+ if ( e.which === 27 ) {
145
+ // user cancelled
146
+ $this.html( $.trim( $this.data( 'original' ) ) ).trigger( 'blur.tseditable' );
147
+ c.$table.data( 'contentFocused', false );
148
+ return false;
149
+ }
150
+ // accept on enter ( if set ), alt-enter ( always ) or if autoAccept is set and element is blurred or unfocused
151
+ t = e.which === 13 && ( wo.editable_enterToAccept || e.altKey ) || wo.editable_autoAccept && e.type !== 'keydown';
152
+ // change if new or user hits enter ( if option set )
153
+ if ( t && $this.data( 'before' ) !== txt ) {
123
154
 
124
- if (typeof wo.editable_focused === 'function') {
125
- wo.editable_focused( txt, column, $this );
155
+ validate = wo.editable_validate;
156
+ valid = txt;
157
+
158
+ if ( typeof( validate ) === 'function' ) {
159
+ valid = validate( txt, $this.data( 'original' ), column, $this );
160
+ } else if ( typeof ( validate = $.tablesorter.getColumnData( c.table, validate, column ) ) === 'function' ) {
161
+ valid = validate( txt, $this.data( 'original' ), column, $this );
126
162
  }
127
163
 
128
- if (selAll) {
129
- if (typeof selAll === 'function') {
130
- if ( selAll( txt, column, $this ) ) {
131
- selectAll($this[0]);
164
+ if ( t && valid !== false ) {
165
+ c.$table.find( '.tseditable-last-edited-cell' ).removeClass( 'tseditable-last-edited-cell' );
166
+ $this
167
+ .addClass( 'tseditable-last-edited-cell' )
168
+ .html( $.trim( valid ) )
169
+ .data( 'before', valid )
170
+ .data( 'original', valid )
171
+ .trigger( 'change' );
172
+ c.$table.trigger( 'updateCell', [ $this.closest( 'td' ), false, function() {
173
+ if ( wo.editable_autoResort ) {
174
+ setTimeout( function() {
175
+ c.$table.trigger( 'sorton', [ c.sortList, function() {
176
+ tse.editComplete( c, wo, c.$table.find( '.tseditable-last-edited-cell' ), true );
177
+ }, true ] );
178
+ }, 10 );
179
+ } else {
180
+ tse.editComplete( c, wo, c.$table.find( '.tseditable-last-edited-cell' ) );
132
181
  }
133
- } else {
134
- selectAll($this[0]);
135
- }
136
- }
137
- })
138
- .on('blur focusout keydown '.split(' ').join('.tseditable '), '[contenteditable]', function(e){
139
- if ( !c.$table.data('contentFocused') ) { return; }
140
- var t, validate,
141
- valid = false,
142
- $this = $(e.target),
143
- txt = $.trim( $this.text() ),
144
- column = $this.closest('td').index();
145
- if ( e.which === 27 ) {
146
- // user cancelled
147
- $this.html( $.trim( $this.data('original') ) ).trigger('blur.tseditable');
148
- c.$table.data( 'contentFocused', false );
182
+ } ] );
149
183
  return false;
150
184
  }
151
- // accept on enter (if set), alt-enter (always) or if autoAccept is set and element is blurred or unfocused
152
- t = e.which === 13 && ( wo.editable_enterToAccept || e.altKey ) || wo.editable_autoAccept && e.type !== 'keydown';
153
- // change if new or user hits enter (if option set)
154
- if ( t && $this.data('before') !== txt ) {
155
-
156
- validate = wo.editable_validate;
157
- valid = txt;
158
-
159
- if (typeof(validate) === "function") {
160
- valid = validate( txt, $this.data('original'), column, $this );
161
- } else if (typeof (validate = $.tablesorter.getColumnData( table, validate, column )) === 'function') {
162
- valid = validate( txt, $this.data('original'), column, $this );
185
+ } else if ( !valid && e.type !== 'keydown' ) {
186
+ clearTimeout( $this.data( 'timer' ) );
187
+ $this.data( 'timer', setTimeout( function() {
188
+ if ( $.isFunction( wo.editable_blur ) ) {
189
+ wo.editable_blur( $.trim( $this.text() ), column, $this );
163
190
  }
191
+ }, 100 ) );
192
+ // restore original content on blur
193
+ $this.html( $.trim( $this.data( 'original' ) ) );
194
+ }
195
+ });
196
+ }
164
197
 
165
- if ( t && valid !== false ) {
166
- c.$table.find('.tseditable-last-edited-cell').removeClass('tseditable-last-edited-cell');
167
- $this
168
- .addClass('tseditable-last-edited-cell')
169
- .html( $.trim( valid ) )
170
- .data('before', valid)
171
- .data('original', valid)
172
- .trigger('change');
173
- c.$table.trigger('updateCell', [ $this.closest('td'), false, function(){
174
- if (wo.editable_autoResort) {
175
- setTimeout(function(){
176
- c.$table.trigger("sorton", [ c.sortList, function(){
177
- editComplete(c.$table.find('.tseditable-last-edited-cell'), true);
178
- }, true ]);
179
- }, 10);
180
- } else {
181
- editComplete(c.$table.find('.tseditable-last-edited-cell'));
182
- }
183
- } ]);
184
- return false;
185
- }
186
- } else if ( !valid && e.type !== 'keydown' ) {
187
- clearTimeout( $this.data('timer') );
188
- $this.data('timer', setTimeout(function(){
189
- if ($.isFunction(wo.editable_blur)) {
190
- wo.editable_blur( $.trim( $this.text() ), column, $this );
191
- }
192
- }, 100));
193
- // restore original content on blur
194
- $this.html( $.trim( $this.data('original') ) );
195
- }
196
- });
198
+ };
199
+
200
+ $.tablesorter.addWidget({
201
+ id: 'editable',
202
+ options : {
203
+ editable_columns : [],
204
+ editable_enterToAccept : true,
205
+ editable_autoAccept : true,
206
+ editable_autoResort : false,
207
+ editable_wrapContent : '<div>', // wrap the cell content... makes this widget work in IE, and with autocomplete
208
+ editable_trimContent : true, // trim content inside of contenteditable ( remove tabs & carriage returns )
209
+ editable_validate : null, // function( text, originalText ){ return text; }
210
+ editable_focused : null, // function( text, columnIndex, $element ) {}
211
+ editable_blur : null, // function( text, columnIndex, $element ) { }
212
+ editable_selectAll : false, // true/false or function( text, columnIndex, $element ) { return true; }
213
+ editable_noEdit : 'no-edit',
214
+ editable_editComplete : 'editComplete'
215
+ },
216
+ init: function( table, thisWidget, c, wo ){
217
+ if ( !wo.editable_columns.length ) { return; }
218
+ tse.update( c, wo );
219
+ tse.bindEvents( c, wo );
197
220
  }
198
221
  });
199
222
 
200
- })(jQuery);
223
+ })( jQuery );