jquery-tablesorter 1.12.8 → 1.13.0

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