jquery-datatables-rails 3.3.0 → 3.4.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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/dataTables/bootstrap/3/jquery.dataTables.bootstrap.js +64 -29
- data/app/assets/javascripts/dataTables/extras/dataTables.autoFill.js +806 -648
- data/app/assets/javascripts/dataTables/extras/dataTables.buttons.js +1607 -0
- data/app/assets/javascripts/dataTables/extras/dataTables.colReorder.js +220 -267
- data/app/assets/javascripts/dataTables/extras/dataTables.fixedColumns.js +164 -69
- data/app/assets/javascripts/dataTables/extras/dataTables.fixedHeader.js +469 -870
- data/app/assets/javascripts/dataTables/extras/dataTables.keyTable.js +636 -972
- data/app/assets/javascripts/dataTables/extras/dataTables.responsive.js +472 -187
- data/app/assets/javascripts/dataTables/extras/dataTables.rowReorder.js +619 -0
- data/app/assets/javascripts/dataTables/extras/dataTables.scroller.js +146 -111
- data/app/assets/javascripts/dataTables/extras/dataTables.select.js +1038 -0
- data/app/assets/javascripts/dataTables/jquery.dataTables.api.fnGetColumnData.js +0 -0
- data/app/assets/javascripts/dataTables/jquery.dataTables.api.fnReloadAjax.js +0 -0
- data/app/assets/javascripts/dataTables/jquery.dataTables.foundation.js +37 -61
- data/app/assets/javascripts/dataTables/jquery.dataTables.js +720 -387
- data/app/assets/javascripts/dataTables/jquery.dataTables.sorting.ipAddress.js +44 -0
- data/app/assets/javascripts/dataTables/jquery.dataTables.sorting.numbersHtml.js +0 -0
- data/app/assets/javascripts/dataTables/jquery.dataTables.typeDetection.numbersHtml.js +0 -0
- data/app/assets/stylesheets/dataTables/jquery.dataTables.scss +34 -66
- data/app/assets/stylesheets/dataTables/src/demo_table.css +1 -1
- data/app/assets/stylesheets/dataTables/src/demo_table_jui.css.scss +4 -4
- data/lib/jquery/datatables/rails/version.rb +1 -1
- metadata +24 -19
@@ -1,15 +1,15 @@
|
|
1
|
-
/*! KeyTable
|
2
|
-
* ©
|
1
|
+
/*! KeyTable 2.1.0
|
2
|
+
* ©2009-2015 SpryMedia Ltd - datatables.net/license
|
3
3
|
*/
|
4
4
|
|
5
5
|
/**
|
6
6
|
* @summary KeyTable
|
7
7
|
* @description Spreadsheet like keyboard navigation for DataTables
|
8
|
-
* @version
|
8
|
+
* @version 2.1.0
|
9
9
|
* @file dataTables.keyTable.js
|
10
10
|
* @author SpryMedia Ltd (www.sprymedia.co.uk)
|
11
11
|
* @contact www.sprymedia.co.uk/contact
|
12
|
-
* @copyright Copyright 2009-
|
12
|
+
* @copyright Copyright 2009-2015 SpryMedia Ltd.
|
13
13
|
*
|
14
14
|
* This source file is free software, available under the following license:
|
15
15
|
* MIT license - http://datatables.net/license/mit
|
@@ -21,1155 +21,819 @@
|
|
21
21
|
* For details please refer to: http://www.datatables.net
|
22
22
|
*/
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
(
|
24
|
+
(function( factory ){
|
25
|
+
if ( typeof define === 'function' && define.amd ) {
|
26
|
+
// AMD
|
27
|
+
define( ['jquery', 'datatables.net'], function ( $ ) {
|
28
|
+
return factory( $, window, document );
|
29
|
+
} );
|
30
|
+
}
|
31
|
+
else if ( typeof exports === 'object' ) {
|
32
|
+
// CommonJS
|
33
|
+
module.exports = function (root, $) {
|
34
|
+
if ( ! root ) {
|
35
|
+
root = window;
|
36
|
+
}
|
29
37
|
|
38
|
+
if ( ! $ || ! $.fn.dataTable ) {
|
39
|
+
$ = require('datatables.net')(root, $).$;
|
40
|
+
}
|
30
41
|
|
31
|
-
|
32
|
-
|
42
|
+
return factory( $, root, root.document );
|
43
|
+
};
|
44
|
+
}
|
45
|
+
else {
|
46
|
+
// Browser
|
47
|
+
factory( jQuery, window, document );
|
48
|
+
}
|
49
|
+
}(function( $, window, document, undefined ) {
|
50
|
+
'use strict';
|
51
|
+
var DataTable = $.fn.dataTable;
|
33
52
|
|
34
|
-
KeyTable = function ( oInit )
|
35
|
-
{
|
36
|
-
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
37
|
-
* API parameters
|
38
|
-
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
39
53
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
this.block = false;
|
46
|
-
|
47
|
-
/*
|
48
|
-
* Variable: event
|
49
|
-
* Purpose: Container for all event application methods
|
50
|
-
* Scope: KeyTable - public
|
51
|
-
* Notes: This object contains all the public methods for adding and removing events - these
|
52
|
-
* are dynamically added later on
|
53
|
-
*/
|
54
|
-
this.event = {
|
55
|
-
"remove": {}
|
56
|
-
};
|
54
|
+
var KeyTable = function ( dt, opts ) {
|
55
|
+
// Sanity check that we are using DataTables 1.10 or newer
|
56
|
+
if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) {
|
57
|
+
throw 'KeyTable requires DataTables 1.10.8 or newer';
|
58
|
+
}
|
57
59
|
|
60
|
+
// User and defaults configuration object
|
61
|
+
this.c = $.extend( true, {},
|
62
|
+
DataTable.defaults.keyTable,
|
63
|
+
KeyTable.defaults,
|
64
|
+
opts
|
65
|
+
);
|
58
66
|
|
59
|
-
|
60
|
-
|
61
|
-
|
67
|
+
// Internal settings
|
68
|
+
this.s = {
|
69
|
+
/** @type {DataTable.Api} DataTables' API instance */
|
70
|
+
dt: new DataTable.Api( dt ),
|
62
71
|
|
63
|
-
|
64
|
-
* Function: fnGetCurrentPosition
|
65
|
-
* Purpose: Get the currently focused cell's position
|
66
|
-
* Returns: array int: [ x, y ]
|
67
|
-
* Inputs: void
|
68
|
-
*/
|
69
|
-
this.fnGetCurrentPosition = function ()
|
70
|
-
{
|
71
|
-
return [ _iOldX, _iOldY ];
|
72
|
+
enable: true
|
72
73
|
};
|
73
74
|
|
75
|
+
// DOM items
|
76
|
+
this.dom = {
|
74
77
|
|
75
|
-
/*
|
76
|
-
* Function: fnGetCurrentData
|
77
|
-
* Purpose: Get the currently focused cell's data (innerHTML)
|
78
|
-
* Returns: string: - data requested
|
79
|
-
* Inputs: void
|
80
|
-
*/
|
81
|
-
this.fnGetCurrentData = function ()
|
82
|
-
{
|
83
|
-
return _nOldFocus.innerHTML;
|
84
78
|
};
|
85
79
|
|
80
|
+
// Check if row reorder has already been initialised on this table
|
81
|
+
var settings = this.s.dt.settings()[0];
|
82
|
+
var exisiting = settings.keytable;
|
83
|
+
if ( exisiting ) {
|
84
|
+
return exisiting;
|
85
|
+
}
|
86
86
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
* Returns: node: - focused element
|
91
|
-
* Inputs: void
|
92
|
-
*/
|
93
|
-
this.fnGetCurrentTD = function ()
|
94
|
-
{
|
95
|
-
return _nOldFocus;
|
96
|
-
};
|
87
|
+
settings.keytable = this;
|
88
|
+
this._constructor();
|
89
|
+
};
|
97
90
|
|
98
91
|
|
99
|
-
|
100
|
-
|
101
|
-
*
|
102
|
-
* Returns: -
|
103
|
-
* Inputs: int:x - x coordinate
|
104
|
-
* int:y - y coordinate
|
105
|
-
* Notes: Thanks to Rohan Daxini for the basis of this function
|
92
|
+
$.extend( KeyTable.prototype, {
|
93
|
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
94
|
+
* API methods for DataTables API interface
|
106
95
|
*/
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
{
|
111
|
-
_fnSetFocus( x );
|
112
|
-
}
|
113
|
-
else
|
114
|
-
{
|
115
|
-
_fnSetFocus( _fnCellFromCoords(x, y) );
|
116
|
-
}
|
117
|
-
};
|
118
|
-
|
119
|
-
|
120
|
-
/*
|
121
|
-
* Function: fnBlur
|
122
|
-
* Purpose: Blur the current focus
|
123
|
-
* Returns: -
|
124
|
-
* Inputs: -
|
96
|
+
|
97
|
+
/**
|
98
|
+
* Blur the table's cell focus
|
125
99
|
*/
|
126
|
-
|
100
|
+
blur: function ()
|
127
101
|
{
|
128
|
-
|
129
|
-
}
|
130
|
-
|
102
|
+
this._blur();
|
103
|
+
},
|
131
104
|
|
132
|
-
|
133
|
-
*
|
134
|
-
*
|
135
|
-
|
136
|
-
/*
|
137
|
-
* Variable: _nBody
|
138
|
-
* Purpose: Body node of the table - cached for renference
|
139
|
-
* Scope: KeyTable - private
|
105
|
+
/**
|
106
|
+
* Enable cell focus for the table
|
107
|
+
*
|
108
|
+
* @param {string} state Can be `true`, `false` or `-string navigation-only`
|
140
109
|
*/
|
141
|
-
|
110
|
+
enable: function ( state )
|
111
|
+
{
|
112
|
+
this.s.enable = state;
|
113
|
+
},
|
142
114
|
|
143
|
-
|
144
|
-
*
|
145
|
-
*
|
146
|
-
*
|
115
|
+
/**
|
116
|
+
* Focus on a cell
|
117
|
+
* @param {integer} row Row index
|
118
|
+
* @param {integer} column Column index
|
147
119
|
*/
|
148
|
-
|
120
|
+
focus: function ( row, column )
|
121
|
+
{
|
122
|
+
this._focus( this.s.dt.cell( row, column ) );
|
123
|
+
},
|
149
124
|
|
150
|
-
|
151
|
-
*
|
152
|
-
*
|
153
|
-
*
|
125
|
+
/**
|
126
|
+
* Is the cell focused
|
127
|
+
* @param {object} cell Cell index to check
|
128
|
+
* @returns {boolean} true if focused, false otherwise
|
154
129
|
*/
|
155
|
-
|
156
|
-
|
130
|
+
focused: function ( cell )
|
131
|
+
{
|
132
|
+
var lastFocus = this.s.lastFocus;
|
157
133
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
* Scope: KeyTable - private
|
162
|
-
*/
|
163
|
-
var _that = null;
|
134
|
+
if ( ! lastFocus ) {
|
135
|
+
return false;
|
136
|
+
}
|
164
137
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
* Scope: KeyTable - private
|
169
|
-
*/
|
170
|
-
var _sFocusClass = "focus";
|
138
|
+
var lastIdx = this.s.lastFocus.index();
|
139
|
+
return cell.row === lastIdx.row && cell.column === lastIdx.column;
|
140
|
+
},
|
171
141
|
|
172
|
-
/*
|
173
|
-
* Variable: _bKeyCapture
|
174
|
-
* Purpose: Flag for should KeyTable capture key events or not
|
175
|
-
* Scope: KeyTable - private
|
176
|
-
*/
|
177
|
-
var _bKeyCapture = false;
|
178
142
|
|
179
|
-
/*
|
180
|
-
*
|
181
|
-
* Purpose: Event cache object, one array for each supported event for speed of searching
|
182
|
-
* Scope: KeyTable - private
|
143
|
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
144
|
+
* Constructor
|
183
145
|
*/
|
184
|
-
var _oaoEvents = {
|
185
|
-
"action": [],
|
186
|
-
"esc": [],
|
187
|
-
"focus": [],
|
188
|
-
"blur": []
|
189
|
-
};
|
190
146
|
|
191
|
-
|
192
|
-
*
|
193
|
-
*
|
194
|
-
*
|
195
|
-
* Scope: KeyTable - private
|
147
|
+
/**
|
148
|
+
* Initialise the KeyTable instance
|
149
|
+
*
|
150
|
+
* @private
|
196
151
|
*/
|
197
|
-
|
152
|
+
_constructor: function ()
|
153
|
+
{
|
154
|
+
this._tabInput();
|
198
155
|
|
199
|
-
|
200
|
-
|
201
|
-
|
156
|
+
var that = this;
|
157
|
+
var dt = this.s.dt;
|
158
|
+
var table = $( dt.table().node() );
|
202
159
|
|
160
|
+
// Need to be able to calculate the cell positions relative to the table
|
161
|
+
if ( table.css('position') === 'static' ) {
|
162
|
+
table.css( 'position', 'relative' );
|
163
|
+
}
|
203
164
|
|
204
|
-
|
205
|
-
|
206
|
-
|
165
|
+
// Click to focus
|
166
|
+
$( dt.table().body() ).on( 'click.keyTable', 'th, td', function () {
|
167
|
+
if ( that.s.enable === false ) {
|
168
|
+
return;
|
169
|
+
}
|
207
170
|
|
208
|
-
|
209
|
-
* Key table events
|
210
|
-
*/
|
171
|
+
var cell = dt.cell( this );
|
211
172
|
|
212
|
-
|
213
|
-
|
214
|
-
* Purpose: Create a function (with closure for sKey) event addition API
|
215
|
-
* Returns: function: - template function
|
216
|
-
* Inputs: string:sKey - type of event to detect
|
217
|
-
*/
|
218
|
-
function _fnEventAddTemplate( sKey )
|
219
|
-
{
|
220
|
-
/*
|
221
|
-
* Function: -
|
222
|
-
* Purpose: API function for adding event to cache
|
223
|
-
* Returns: -
|
224
|
-
* Inputs: 1. node:x - target node to add event for
|
225
|
-
* 2. function:y - callback function to apply
|
226
|
-
* or
|
227
|
-
* 1. int:x - x coord. of target cell (can be null for live events)
|
228
|
-
* 2. int:y - y coord. of target cell (can be null for live events)
|
229
|
-
* 3. function:z - callback function to apply
|
230
|
-
* Notes: This function is (interally) overloaded (in as much as javascript allows for
|
231
|
-
* that) - the target cell can be given by either node or coords.
|
232
|
-
*/
|
233
|
-
return function ( x, y, z ) {
|
234
|
-
if ( (x===null || typeof x == "number") &&
|
235
|
-
(y===null || typeof y == "number") &&
|
236
|
-
typeof z == "function" )
|
237
|
-
{
|
238
|
-
_fnEventAdd( sKey, x, y, z );
|
239
|
-
}
|
240
|
-
else if ( typeof x == "object" && typeof y == "function" )
|
241
|
-
{
|
242
|
-
var aCoords = _fnCoordsFromCell( x );
|
243
|
-
_fnEventAdd( sKey, aCoords[0], aCoords[1], y );
|
244
|
-
}
|
245
|
-
else
|
246
|
-
{
|
247
|
-
alert( "Unhandable event type was added: x" +x+ " y:" +y+ " z:" +z );
|
173
|
+
if ( ! cell.any() ) {
|
174
|
+
return;
|
248
175
|
}
|
249
|
-
};
|
250
|
-
}
|
251
176
|
|
177
|
+
that._focus( cell );
|
178
|
+
} );
|
252
179
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
* Returns: int: - number of events removed
|
265
|
-
* Inputs: 1. node:x - target node to remove event from
|
266
|
-
* 2. function:y - callback function to apply
|
267
|
-
* or
|
268
|
-
* 1. int:x - x coord. of target cell (can be null for live events)
|
269
|
-
* 2. int:y - y coord. of target cell (can be null for live events)
|
270
|
-
* 3. function:z - callback function to remove - optional
|
271
|
-
* Notes: This function is (interally) overloaded (in as much as javascript allows for
|
272
|
-
* that) - the target cell can be given by either node or coords and the function
|
273
|
-
* to remove is optional
|
274
|
-
*/
|
275
|
-
return function ( x, y, z ) {
|
276
|
-
if ( (x===null || typeof arguments[0] == "number") &&
|
277
|
-
(y===null || typeof arguments[1] == "number" ) )
|
278
|
-
{
|
279
|
-
if ( typeof arguments[2] == "function" )
|
280
|
-
{
|
281
|
-
_fnEventRemove( sKey, x, y, z );
|
282
|
-
}
|
283
|
-
else
|
284
|
-
{
|
285
|
-
_fnEventRemove( sKey, x, y );
|
180
|
+
// Key events
|
181
|
+
$( document.body ).on( 'keydown.keyTable', function (e) {
|
182
|
+
that._key( e );
|
183
|
+
} );
|
184
|
+
|
185
|
+
// Click blur
|
186
|
+
if ( this.c.blurable ) {
|
187
|
+
$( document.body ).on( 'click.keyTable', function ( e ) {
|
188
|
+
// Click on the search input will blur focus
|
189
|
+
if ( $(e.target).parents( '.dataTables_filter' ).length ) {
|
190
|
+
that._blur();
|
286
191
|
}
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
if ( typeof arguments[1] == "function" )
|
292
|
-
{
|
293
|
-
_fnEventRemove( sKey, aCoords[0], aCoords[1], y );
|
192
|
+
|
193
|
+
// If the click was inside the DataTables container, don't blur
|
194
|
+
if ( $(e.target).parents().filter( dt.table().container() ).length ) {
|
195
|
+
return;
|
294
196
|
}
|
295
|
-
|
296
|
-
|
297
|
-
|
197
|
+
|
198
|
+
// Don't blur in Editor form
|
199
|
+
if ( $(e.target).parents('div.DTE').length ) {
|
200
|
+
return;
|
298
201
|
}
|
299
|
-
}
|
300
|
-
else
|
301
|
-
{
|
302
|
-
alert( "Unhandable event type was removed: x" +x+ " y:" +y+ " z:" +z );
|
303
|
-
}
|
304
|
-
};
|
305
|
-
}
|
306
202
|
|
307
|
-
|
308
|
-
|
309
|
-
{
|
310
|
-
if ( sKey )
|
311
|
-
{
|
312
|
-
this.event[sKey] = _fnEventAddTemplate( sKey );
|
313
|
-
this.event.remove[sKey] = _fnEventRemoveTemplate( sKey );
|
203
|
+
that._blur();
|
204
|
+
} );
|
314
205
|
}
|
315
|
-
}
|
316
206
|
|
207
|
+
if ( this.c.editor ) {
|
208
|
+
dt.on( 'key.kt', function ( e, dt, key, cell, orig ) {
|
209
|
+
that._editor( key, orig );
|
210
|
+
} );
|
211
|
+
}
|
317
212
|
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
213
|
+
// Stave saving
|
214
|
+
if ( dt.settings()[0].oFeatures.bStateSave ) {
|
215
|
+
dt.on( 'stateSaveParams.keyTable', function (e, s, d) {
|
216
|
+
d.keyTable = that.s.lastFocus ?
|
217
|
+
that.s.lastFocus.index() :
|
218
|
+
null;
|
219
|
+
} );
|
220
|
+
}
|
221
|
+
|
222
|
+
dt.on( 'destroy.keyTable', function () {
|
223
|
+
dt.off( '.keyTable' );
|
224
|
+
$( dt.table().body() ).off( 'click.keyTable', 'th, td' );
|
225
|
+
$( document.body )
|
226
|
+
.off( 'keydown.keyTable' )
|
227
|
+
.off( 'click.keyTable' );
|
333
228
|
} );
|
334
|
-
}
|
335
229
|
|
230
|
+
// Initial focus comes from state or options
|
231
|
+
var state = dt.state.loaded();
|
336
232
|
|
337
|
-
|
338
|
-
|
339
|
-
* Purpose: Remove an event from the event cache
|
340
|
-
* Returns: int: - number of matching events removed
|
341
|
-
* Inputs: string:sType - type of event to look for
|
342
|
-
* node:nTarget - target table cell
|
343
|
-
* function:fn - optional - remove this function. If not given all handlers of this
|
344
|
-
* type will be removed
|
345
|
-
*/
|
346
|
-
function _fnEventRemove( sType, x, y, fn )
|
347
|
-
{
|
348
|
-
var iCorrector = 0;
|
349
|
-
|
350
|
-
for ( var i=0, iLen=_oaoEvents[sType].length ; i<iLen-iCorrector ; i++ )
|
351
|
-
{
|
352
|
-
if ( typeof fn != 'undefined' )
|
353
|
-
{
|
354
|
-
if ( _oaoEvents[sType][i-iCorrector].x == x &&
|
355
|
-
_oaoEvents[sType][i-iCorrector].y == y &&
|
356
|
-
_oaoEvents[sType][i-iCorrector].fn == fn )
|
357
|
-
{
|
358
|
-
_oaoEvents[sType].splice( i-iCorrector, 1 );
|
359
|
-
iCorrector++;
|
360
|
-
}
|
361
|
-
}
|
362
|
-
else
|
363
|
-
{
|
364
|
-
if ( _oaoEvents[sType][i-iCorrector].x == x &&
|
365
|
-
_oaoEvents[sType][i-iCorrector].y == y )
|
366
|
-
{
|
367
|
-
_oaoEvents[sType].splice( i, 1 );
|
368
|
-
return 1;
|
369
|
-
}
|
370
|
-
}
|
233
|
+
if ( state && state.keyTable ) {
|
234
|
+
dt.cell( state.keyTable ).focus();
|
371
235
|
}
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
/*
|
377
|
-
* Function: _fnEventFire
|
378
|
-
* Purpose: Look thought the events cache and fire off the event of interest
|
379
|
-
* Returns: int:iFired - number of events fired
|
380
|
-
* Inputs: string:sType - type of event to look for
|
381
|
-
* int:x - x coord of cell
|
382
|
-
* int:y - y coord of ell
|
383
|
-
* Notes: It might be more efficient to return after the first event has been tirggered,
|
384
|
-
* but that would mean that only one function of a particular type can be
|
385
|
-
* subscribed to a particular node.
|
386
|
-
*/
|
387
|
-
function _fnEventFire ( sType, x, y )
|
388
|
-
{
|
389
|
-
var iFired = 0;
|
390
|
-
var aEvents = _oaoEvents[sType];
|
391
|
-
for ( var i=0 ; i<aEvents.length ; i++ )
|
392
|
-
{
|
393
|
-
if ( (aEvents[i].x == x && aEvents[i].y == y ) ||
|
394
|
-
(aEvents[i].x === null && aEvents[i].y == y ) ||
|
395
|
-
(aEvents[i].x == x && aEvents[i].y === null ) ||
|
396
|
-
(aEvents[i].x === null && aEvents[i].y === null )
|
397
|
-
)
|
398
|
-
{
|
399
|
-
aEvents[i].fn( _fnCellFromCoords(x,y), x, y );
|
400
|
-
iFired++;
|
401
|
-
}
|
236
|
+
else if ( this.c.focus ) {
|
237
|
+
dt.cell( this.c.focus ).focus();
|
402
238
|
}
|
403
|
-
|
404
|
-
|
239
|
+
},
|
240
|
+
|
405
241
|
|
406
242
|
|
407
243
|
|
408
|
-
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
409
|
-
*
|
244
|
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
245
|
+
* Private methods
|
410
246
|
*/
|
411
247
|
|
412
|
-
|
413
|
-
*
|
414
|
-
*
|
415
|
-
*
|
416
|
-
* Inputs: node:nTarget - node we want to focus on
|
417
|
-
* bool:bAutoScroll - optional - should we scroll the view port to the display
|
248
|
+
/**
|
249
|
+
* Blur the control
|
250
|
+
*
|
251
|
+
* @private
|
418
252
|
*/
|
419
|
-
function
|
253
|
+
_blur: function ()
|
420
254
|
{
|
421
|
-
|
422
|
-
if ( _nOldFocus == nTarget )
|
423
|
-
{
|
255
|
+
if ( ! this.s.enable || ! this.s.lastFocus ) {
|
424
256
|
return;
|
425
257
|
}
|
426
258
|
|
427
|
-
|
428
|
-
{
|
429
|
-
bAutoScroll = true;
|
430
|
-
}
|
259
|
+
var cell = this.s.lastFocus;
|
431
260
|
|
432
|
-
|
433
|
-
|
434
|
-
{
|
435
|
-
_fnRemoveFocus( _nOldFocus );
|
436
|
-
}
|
261
|
+
$( cell.node() ).removeClass( this.c.className );
|
262
|
+
this.s.lastFocus = null;
|
437
263
|
|
438
|
-
|
439
|
-
|
440
|
-
$(nTarget).parent().addClass( _sFocusClass );
|
441
|
-
|
442
|
-
/* If it's a DataTable then we need to jump the paging to the relevant page */
|
443
|
-
var oSettings;
|
444
|
-
if ( _oDatatable )
|
445
|
-
{
|
446
|
-
oSettings = _oDatatable;
|
447
|
-
var iRow = _fnFindDtCell( nTarget )[1];
|
448
|
-
var bKeyCaptureCache = _bKeyCapture;
|
449
|
-
|
450
|
-
/* Page forwards */
|
451
|
-
while ( iRow >= oSettings.fnDisplayEnd() )
|
452
|
-
{
|
453
|
-
if ( oSettings._iDisplayLength >= 0 )
|
454
|
-
{
|
455
|
-
/* Make sure we are not over running the display array */
|
456
|
-
if ( oSettings._iDisplayStart + oSettings._iDisplayLength < oSettings.fnRecordsDisplay() )
|
457
|
-
{
|
458
|
-
oSettings._iDisplayStart += oSettings._iDisplayLength;
|
459
|
-
}
|
460
|
-
}
|
461
|
-
else
|
462
|
-
{
|
463
|
-
oSettings._iDisplayStart = 0;
|
464
|
-
}
|
465
|
-
_oDatatable.oApi._fnCalculateEnd( oSettings );
|
466
|
-
}
|
264
|
+
this._emitEvent( 'key-blur', [ this.s.dt, cell ] );
|
265
|
+
},
|
467
266
|
|
468
|
-
/* Page backwards */
|
469
|
-
while ( iRow < oSettings._iDisplayStart )
|
470
|
-
{
|
471
|
-
oSettings._iDisplayStart = oSettings._iDisplayLength>=0 ?
|
472
|
-
oSettings._iDisplayStart - oSettings._iDisplayLength :
|
473
|
-
0;
|
474
267
|
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
268
|
+
/**
|
269
|
+
* Get an array of the column indexes that KeyTable can operate on. This
|
270
|
+
* is a merge of the user supplied columns and the visible columns.
|
271
|
+
*
|
272
|
+
* @private
|
273
|
+
*/
|
274
|
+
_columns: function ()
|
275
|
+
{
|
276
|
+
var dt = this.s.dt;
|
277
|
+
var user = dt.columns( this.c.columns ).indexes();
|
278
|
+
var out = [];
|
279
|
+
|
280
|
+
dt.columns( ':visible' ).every( function (i) {
|
281
|
+
if ( user.indexOf( i ) !== -1 ) {
|
282
|
+
out.push( i );
|
480
283
|
}
|
284
|
+
} );
|
481
285
|
|
482
|
-
|
483
|
-
|
286
|
+
return out;
|
287
|
+
},
|
484
288
|
|
485
|
-
/* Restore the key capture */
|
486
|
-
_bKeyCapture = bKeyCaptureCache;
|
487
|
-
}
|
488
289
|
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
iScrollTop = $(document).scrollTop();
|
502
|
-
iScrollLeft = $(document).scrollLeft();
|
503
|
-
iHeight = nTarget.offsetHeight;
|
504
|
-
iWidth = nTarget.offsetWidth;
|
505
|
-
aiPos = _fnGetPos( nTarget );
|
506
|
-
|
507
|
-
/* Take account of scrolling in DataTables 1.7 - remove scrolling since that would add to
|
508
|
-
* the positioning calculation
|
509
|
-
*/
|
510
|
-
if ( _oDatatable && typeof oSettings.oScroll != 'undefined' &&
|
511
|
-
(oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") )
|
512
|
-
{
|
513
|
-
aiPos[1] -= $(oSettings.nTable.parentNode).scrollTop();
|
514
|
-
aiPos[0] -= $(oSettings.nTable.parentNode).scrollLeft();
|
515
|
-
}
|
290
|
+
/**
|
291
|
+
* Perform excel like navigation for Editor by triggering an edit on key
|
292
|
+
* press
|
293
|
+
*
|
294
|
+
* @param {integer} key Key code for the pressed key
|
295
|
+
* @param {object} orig Original event
|
296
|
+
* @private
|
297
|
+
*/
|
298
|
+
_editor: function ( key, orig )
|
299
|
+
{
|
300
|
+
var dt = this.s.dt;
|
301
|
+
var editor = this.c.editor;
|
516
302
|
|
517
|
-
|
518
|
-
if ( aiPos[1]+iHeight > iScrollTop+iViewportHeight )
|
519
|
-
{
|
520
|
-
/* Displayed element if off the bottom of the viewport */
|
521
|
-
_fnSetScrollTop( aiPos[1]+iHeight - iViewportHeight );
|
522
|
-
}
|
523
|
-
else if ( aiPos[1] < iScrollTop )
|
524
|
-
{
|
525
|
-
/* Displayed element if off the top of the viewport */
|
526
|
-
_fnSetScrollTop( aiPos[1] );
|
527
|
-
}
|
303
|
+
orig.stopPropagation();
|
528
304
|
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
else if ( aiPos[0] < iScrollLeft )
|
536
|
-
{
|
537
|
-
/* Displayed element if off the Left of the viewport */
|
538
|
-
_fnSetScrollLeft( aiPos[0] );
|
539
|
-
}
|
305
|
+
editor.inline( this.s.lastFocus.index() );
|
306
|
+
|
307
|
+
// Excel style - select all text
|
308
|
+
var input = $('div.DTE input');
|
309
|
+
if ( input.length ) {
|
310
|
+
input[0].select();
|
540
311
|
}
|
541
312
|
|
542
|
-
|
543
|
-
|
544
|
-
(oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") )
|
545
|
-
{
|
546
|
-
var dtScrollBody = oSettings.nTable.parentNode;
|
547
|
-
iViewportHeight = dtScrollBody.clientHeight;
|
548
|
-
iViewportWidth = dtScrollBody.clientWidth;
|
549
|
-
iScrollTop = dtScrollBody.scrollTop;
|
550
|
-
iScrollLeft = dtScrollBody.scrollLeft;
|
551
|
-
iHeight = nTarget.offsetHeight;
|
552
|
-
iWidth = nTarget.offsetWidth;
|
553
|
-
|
554
|
-
/* Correct for vertical scrolling */
|
555
|
-
if ( nTarget.offsetTop + iHeight > iViewportHeight+iScrollTop )
|
556
|
-
{
|
557
|
-
dtScrollBody.scrollTop = (nTarget.offsetTop + iHeight) - iViewportHeight;
|
558
|
-
}
|
559
|
-
else if ( nTarget.offsetTop < iScrollTop )
|
560
|
-
{
|
561
|
-
dtScrollBody.scrollTop = nTarget.offsetTop;
|
562
|
-
}
|
313
|
+
// Reduce the keys the Keys listens for
|
314
|
+
dt.keys.enable( 'navigation-only' );
|
563
315
|
|
564
|
-
|
565
|
-
|
566
|
-
{
|
567
|
-
|
316
|
+
// On blur of the navigation submit
|
317
|
+
dt.one( 'key-blur.editor', function () {
|
318
|
+
if ( editor.displayed() ) {
|
319
|
+
editor.submit();
|
568
320
|
}
|
569
|
-
|
570
|
-
{
|
571
|
-
dtScrollBody.scrollLeft = nTarget.offsetLeft;
|
572
|
-
}
|
573
|
-
}
|
574
|
-
|
575
|
-
/* Focused - so we want to capture the keys */
|
576
|
-
_fnCaptureKeys();
|
321
|
+
} );
|
577
322
|
|
578
|
-
|
579
|
-
|
580
|
-
|
323
|
+
// Restore full key navigation on close
|
324
|
+
editor.one( 'close', function () {
|
325
|
+
dt.keys.enable( true );
|
326
|
+
dt.off( 'key-blur.editor' );
|
327
|
+
} );
|
328
|
+
},
|
581
329
|
|
582
330
|
|
583
|
-
|
584
|
-
*
|
585
|
-
*
|
586
|
-
*
|
587
|
-
*
|
331
|
+
/**
|
332
|
+
* Emit an event on the DataTable for listeners
|
333
|
+
*
|
334
|
+
* @param {string} name Event name
|
335
|
+
* @param {array} args Event arguments
|
336
|
+
* @private
|
588
337
|
*/
|
589
|
-
function
|
338
|
+
_emitEvent: function ( name, args )
|
590
339
|
{
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
_fnReleaseKeys();
|
596
|
-
}
|
340
|
+
this.s.dt.iterator( 'table', function ( ctx, i ) {
|
341
|
+
$(ctx.nTable).triggerHandler( name, args );
|
342
|
+
} );
|
343
|
+
},
|
597
344
|
|
598
345
|
|
599
|
-
|
600
|
-
*
|
601
|
-
*
|
602
|
-
*
|
603
|
-
*
|
346
|
+
/**
|
347
|
+
* Focus on a particular cell, shifting the table's paging if required
|
348
|
+
*
|
349
|
+
* @param {DataTables.Api|integer} row Can be given as an API instance that
|
350
|
+
* contains the cell to focus or as an integer. As the latter it is the
|
351
|
+
* visible row index - NOT the data index
|
352
|
+
* @param {integer} [column] Not required if a cell is given as the first
|
353
|
+
* parameter. Otherwise this is the column data index for the cell to
|
354
|
+
* focus on
|
355
|
+
* @private
|
604
356
|
*/
|
605
|
-
function
|
357
|
+
_focus: function ( row, column )
|
606
358
|
{
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
359
|
+
var that = this;
|
360
|
+
var dt = this.s.dt;
|
361
|
+
var pageInfo = dt.page.info();
|
362
|
+
var lastFocus = this.s.lastFocus;
|
611
363
|
|
364
|
+
if ( ! this.s.enable ) {
|
365
|
+
return;
|
366
|
+
}
|
612
367
|
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
368
|
+
if ( typeof row !== 'number' ) {
|
369
|
+
// Convert the cell to a row and column
|
370
|
+
var index = row.index();
|
371
|
+
column = index.column;
|
372
|
+
row = dt
|
373
|
+
.rows( { filter: 'applied', order: 'applied' } )
|
374
|
+
.indexes()
|
375
|
+
.indexOf( index.row );
|
376
|
+
|
377
|
+
// For server-side processing normalise the row by adding the start
|
378
|
+
// point, since `rows().indexes()` includes only rows that are
|
379
|
+
// available at the client-side
|
380
|
+
if ( pageInfo.serverSide ) {
|
381
|
+
row += pageInfo.start;
|
382
|
+
}
|
625
383
|
}
|
626
384
|
|
627
|
-
|
628
|
-
|
629
|
-
|
385
|
+
// Is the row on the current page? If not, we need to redraw to show the
|
386
|
+
// page
|
387
|
+
if ( pageInfo.length !== -1 && (row < pageInfo.start || row >= pageInfo.start+pageInfo.length) ) {
|
388
|
+
dt
|
389
|
+
.one( 'draw', function () {
|
390
|
+
that._focus( row, column );
|
391
|
+
} )
|
392
|
+
.page( Math.floor( row / pageInfo.length ) )
|
393
|
+
.draw( false );
|
630
394
|
|
395
|
+
return;
|
396
|
+
}
|
631
397
|
|
398
|
+
// In the available columns?
|
399
|
+
if ( $.inArray( column, this._columns() ) === -1 ) {
|
400
|
+
return;
|
401
|
+
}
|
632
402
|
|
633
|
-
|
634
|
-
|
635
|
-
|
403
|
+
// De-normalise the server-side processing row, so we select the row
|
404
|
+
// in its displayed position
|
405
|
+
if ( pageInfo.serverSide ) {
|
406
|
+
row -= pageInfo.start;
|
407
|
+
}
|
408
|
+
|
409
|
+
var cell = dt.cell( ':eq('+row+')', column, {search: 'applied'} );
|
410
|
+
|
411
|
+
if ( lastFocus ) {
|
412
|
+
// Don't trigger a refocus on the same cell
|
413
|
+
if ( lastFocus.node() === cell.node() ) {
|
414
|
+
return;
|
415
|
+
}
|
416
|
+
|
417
|
+
// Otherwise blur the old focus
|
418
|
+
this._blur();
|
419
|
+
}
|
420
|
+
|
421
|
+
var node = $( cell.node() );
|
422
|
+
node.addClass( this.c.className );
|
423
|
+
|
424
|
+
// Shift viewpoint and page to make cell visible
|
425
|
+
this._scroll( $(window), $(document.body), node, 'offset' );
|
426
|
+
|
427
|
+
var bodyParent = dt.table().body().parentNode;
|
428
|
+
if ( bodyParent !== dt.table().header().parentNode ) {
|
429
|
+
var parent = $(bodyParent.parentNode);
|
430
|
+
|
431
|
+
this._scroll( parent, parent, node, 'position' );
|
432
|
+
}
|
433
|
+
|
434
|
+
// Event and finish
|
435
|
+
this.s.lastFocus = cell;
|
636
436
|
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
437
|
+
this._emitEvent( 'key-focus', [ this.s.dt, cell ] );
|
438
|
+
dt.state.save();
|
439
|
+
},
|
440
|
+
|
441
|
+
|
442
|
+
/**
|
443
|
+
* Handle key press
|
444
|
+
*
|
445
|
+
* @param {object} e Event
|
446
|
+
* @private
|
642
447
|
*/
|
643
|
-
function
|
448
|
+
_key: function ( e )
|
644
449
|
{
|
645
|
-
|
646
|
-
|
647
|
-
{
|
648
|
-
return true;
|
450
|
+
if ( ! this.s.enable ) {
|
451
|
+
return;
|
649
452
|
}
|
650
453
|
|
651
|
-
|
652
|
-
|
653
|
-
{
|
654
|
-
return true;
|
655
|
-
}
|
656
|
-
var
|
657
|
-
x, y,
|
658
|
-
iTableWidth = _nBody.getElementsByTagName('tr')[0].getElementsByTagName('td').length,
|
659
|
-
iTableHeight;
|
660
|
-
|
661
|
-
/* Get table height and width - done here so as to be dynamic (if table is updated) */
|
662
|
-
if ( _oDatatable )
|
663
|
-
{
|
664
|
-
/*
|
665
|
-
* Locate the current node in the DataTable overriding the old positions - the reason for
|
666
|
-
* is is that there might have been some DataTables interaction between the last focus and
|
667
|
-
* now
|
668
|
-
*/
|
669
|
-
iTableHeight = _oDatatable.aiDisplay.length;
|
670
|
-
|
671
|
-
var aDtPos = _fnFindDtCell( _nOldFocus );
|
672
|
-
if ( aDtPos === null )
|
673
|
-
{
|
674
|
-
/* If the table has been updated such that the focused cell can't be seen - do nothing */
|
675
|
-
return;
|
676
|
-
}
|
677
|
-
_iOldX = aDtPos[ 0 ];
|
678
|
-
_iOldY = aDtPos[ 1 ];
|
454
|
+
if ( e.keyCode === 0 || e.ctrlKey || e.metaKey || e.altKey ) {
|
455
|
+
return;
|
679
456
|
}
|
680
|
-
|
681
|
-
|
682
|
-
|
457
|
+
|
458
|
+
// If not focused, then there is no key action to take
|
459
|
+
var cell = this.s.lastFocus;
|
460
|
+
if ( ! cell ) {
|
461
|
+
return;
|
683
462
|
}
|
684
463
|
|
685
|
-
|
686
|
-
var
|
464
|
+
var that = this;
|
465
|
+
var dt = this.s.dt;
|
687
466
|
|
688
|
-
|
689
|
-
{
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
case 27: /* esc */
|
697
|
-
if ( !_fnEventFire( "esc", _iOldX, _iOldY ) )
|
698
|
-
{
|
699
|
-
/* Only lose focus if there isn't an escape handler on the cell */
|
700
|
-
_fnBlur();
|
701
|
-
return;
|
702
|
-
}
|
703
|
-
x = _iOldX;
|
704
|
-
y = _iOldY;
|
467
|
+
// If we are not listening for this key, do nothing
|
468
|
+
if ( this.c.keys && $.inArray( e.keyCode, this.c.keys ) === -1 ) {
|
469
|
+
return;
|
470
|
+
}
|
471
|
+
|
472
|
+
switch( e.keyCode ) {
|
473
|
+
case 9: // tab
|
474
|
+
this._shift( e, e.shiftKey ? 'left' : 'right', true );
|
705
475
|
break;
|
706
476
|
|
707
|
-
case
|
708
|
-
|
709
|
-
|
710
|
-
x = _iOldX - 1;
|
711
|
-
y = _iOldY;
|
712
|
-
} else if ( _iOldY > 0 ) {
|
713
|
-
x = iTableWidth-1;
|
714
|
-
y = _iOldY - 1;
|
715
|
-
} else {
|
716
|
-
/* at start of table */
|
717
|
-
if ( iKey == -1 && _bForm )
|
718
|
-
{
|
719
|
-
/* If we are in a form, return focus to the 'input' element such that tabbing will
|
720
|
-
* follow correctly in the browser
|
721
|
-
*/
|
722
|
-
_bInputFocused = true;
|
723
|
-
_nInput.focus();
|
724
|
-
|
725
|
-
/* This timeout is a little nasty - but IE appears to have some asyhnc behaviour for
|
726
|
-
* focus
|
727
|
-
*/
|
728
|
-
setTimeout( function(){ _bInputFocused = false; }, 0 );
|
729
|
-
_bKeyCapture = false;
|
730
|
-
_fnBlur();
|
731
|
-
return true;
|
732
|
-
}
|
733
|
-
else
|
734
|
-
{
|
735
|
-
return false;
|
736
|
-
}
|
477
|
+
case 27: // esc
|
478
|
+
if ( this.s.blurable && this.s.enable === true ) {
|
479
|
+
this._blur();
|
737
480
|
}
|
738
481
|
break;
|
739
482
|
|
740
|
-
case
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
483
|
+
case 33: // page up (previous page)
|
484
|
+
case 34: // page down (next page)
|
485
|
+
e.preventDefault();
|
486
|
+
var index = dt.cells( {page: 'current'} ).nodes().indexOf( cell.node() );
|
487
|
+
|
488
|
+
dt
|
489
|
+
.one( 'draw', function () {
|
490
|
+
var nodes = dt.cells( {page: 'current'} ).nodes();
|
491
|
+
|
492
|
+
that._focus( dt.cell( index < nodes.length ?
|
493
|
+
nodes[ index ] :
|
494
|
+
nodes[ nodes.length-1 ]
|
495
|
+
) );
|
496
|
+
} )
|
497
|
+
.page( e.keyCode === 33 ? 'previous' : 'next' )
|
498
|
+
.draw( false );
|
747
499
|
break;
|
748
500
|
|
749
|
-
case
|
750
|
-
|
751
|
-
|
501
|
+
case 35: // end (end of current page)
|
502
|
+
case 36: // home (start of current page)
|
503
|
+
e.preventDefault();
|
504
|
+
var indexes = dt.cells( {page: 'current'} ).indexes();
|
505
|
+
|
506
|
+
this._focus( dt.cell(
|
507
|
+
indexes[ e.keyCode === 35 ? indexes.length-1 : 0 ]
|
508
|
+
) );
|
752
509
|
break;
|
753
510
|
|
754
|
-
case
|
755
|
-
|
756
|
-
y = _iOldY - 10;
|
757
|
-
if (y < 0) {
|
758
|
-
y = 0;
|
759
|
-
}
|
511
|
+
case 37: // left arrow
|
512
|
+
this._shift( e, 'left' );
|
760
513
|
break;
|
761
514
|
|
762
|
-
case
|
763
|
-
|
764
|
-
if ( _iOldX < iTableWidth-1 ) {
|
765
|
-
x = _iOldX + 1;
|
766
|
-
y = _iOldY;
|
767
|
-
} else if ( _iOldY < iTableHeight-1 ) {
|
768
|
-
x = 0;
|
769
|
-
y = _iOldY + 1;
|
770
|
-
} else {
|
771
|
-
/* at end of table */
|
772
|
-
if ( iKey == 9 && _bForm )
|
773
|
-
{
|
774
|
-
/* If we are in a form, return focus to the 'input' element such that tabbing will
|
775
|
-
* follow correctly in the browser
|
776
|
-
*/
|
777
|
-
_bInputFocused = true;
|
778
|
-
_nInput.focus();
|
779
|
-
|
780
|
-
/* This timeout is a little nasty - but IE appears to have some asyhnc behaviour for
|
781
|
-
* focus
|
782
|
-
*/
|
783
|
-
setTimeout( function(){ _bInputFocused = false; }, 0 );
|
784
|
-
_bKeyCapture = false;
|
785
|
-
_fnBlur();
|
786
|
-
return true;
|
787
|
-
}
|
788
|
-
else
|
789
|
-
{
|
790
|
-
return false;
|
791
|
-
}
|
792
|
-
}
|
515
|
+
case 38: // up arrow
|
516
|
+
this._shift( e, 'up' );
|
793
517
|
break;
|
794
518
|
|
795
|
-
case
|
796
|
-
|
797
|
-
x = _iOldX;
|
798
|
-
y = _iOldY + 1;
|
799
|
-
} else {
|
800
|
-
return false;
|
801
|
-
}
|
519
|
+
case 39: // right arrow
|
520
|
+
this._shift( e, 'right' );
|
802
521
|
break;
|
803
522
|
|
804
|
-
case
|
805
|
-
|
806
|
-
y = iTableHeight-1;
|
523
|
+
case 40: // down arrow
|
524
|
+
this._shift( e, 'down' );
|
807
525
|
break;
|
808
526
|
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
y = iTableHeight-1;
|
527
|
+
default:
|
528
|
+
// Everything else - pass through only when fully enabled
|
529
|
+
if ( this.s.enable === true ) {
|
530
|
+
this._emitEvent( 'key', [ dt, e.keyCode, this.s.lastFocus, e ] );
|
814
531
|
}
|
815
532
|
break;
|
816
|
-
|
817
|
-
default: /* Nothing we are interested in */
|
818
|
-
return true;
|
819
533
|
}
|
820
|
-
|
821
|
-
_fnSetFocus( _fnCellFromCoords(x, y) );
|
822
|
-
return false;
|
823
|
-
}
|
534
|
+
},
|
824
535
|
|
825
536
|
|
826
|
-
|
827
|
-
*
|
828
|
-
*
|
829
|
-
*
|
830
|
-
*
|
537
|
+
/**
|
538
|
+
* Scroll a container to make a cell visible in it. This can be used for
|
539
|
+
* both DataTables scrolling and native window scrolling.
|
540
|
+
*
|
541
|
+
* @param {jQuery} container Scrolling container
|
542
|
+
* @param {jQuery} scroller Item being scrolled
|
543
|
+
* @param {jQuery} cell Cell in the scroller
|
544
|
+
* @param {string} posOff `position` or `offset` - which to use for the
|
545
|
+
* calculation. `offset` for the document, otherwise `position`
|
546
|
+
* @private
|
831
547
|
*/
|
832
|
-
function
|
548
|
+
_scroll: function ( container, scroller, cell, posOff )
|
833
549
|
{
|
834
|
-
|
835
|
-
|
836
|
-
|
550
|
+
var offset = cell[posOff]();
|
551
|
+
var height = cell.outerHeight();
|
552
|
+
var width = cell.outerWidth();
|
553
|
+
|
554
|
+
var scrollTop = scroller.scrollTop();
|
555
|
+
var scrollLeft = scroller.scrollLeft();
|
556
|
+
var containerHeight = container.height();
|
557
|
+
var containerWidth = container.width();
|
558
|
+
|
559
|
+
// Top correction
|
560
|
+
if ( offset.top < scrollTop ) {
|
561
|
+
scroller.scrollTop( offset.top );
|
837
562
|
}
|
838
|
-
|
563
|
+
|
564
|
+
// Left correction
|
565
|
+
if ( offset.left < scrollLeft ) {
|
566
|
+
scroller.scrollLeft( offset.left );
|
567
|
+
}
|
568
|
+
|
569
|
+
// Bottom correction
|
570
|
+
if ( offset.top + height > scrollTop + containerHeight ) {
|
571
|
+
scroller.scrollTop( offset.top + height - containerHeight );
|
572
|
+
}
|
573
|
+
|
574
|
+
// Right correction
|
575
|
+
if ( offset.left + width > scrollLeft + containerWidth ) {
|
576
|
+
scroller.scrollLeft( offset.left + width - containerWidth );
|
577
|
+
}
|
578
|
+
},
|
839
579
|
|
840
580
|
|
841
|
-
|
842
|
-
*
|
843
|
-
*
|
844
|
-
*
|
845
|
-
*
|
581
|
+
/**
|
582
|
+
* Calculate a single offset movement in the table - up, down, left and
|
583
|
+
* right and then perform the focus if possible
|
584
|
+
*
|
585
|
+
* @param {object} e Event object
|
586
|
+
* @param {string} direction Movement direction
|
587
|
+
* @param {boolean} keyBlurable `true` if the key press can result in the
|
588
|
+
* table being blurred. This is so arrow keys won't blur the table, but
|
589
|
+
* tab will.
|
590
|
+
* @private
|
846
591
|
*/
|
847
|
-
function
|
592
|
+
_shift: function ( e, direction, keyBlurable )
|
848
593
|
{
|
849
|
-
|
850
|
-
|
594
|
+
var that = this;
|
595
|
+
var dt = this.s.dt;
|
596
|
+
var pageInfo = dt.page.info();
|
597
|
+
var rows = pageInfo.recordsDisplay;
|
598
|
+
var currentCell = this.s.lastFocus;
|
599
|
+
var columns = this._columns();
|
600
|
+
|
601
|
+
if ( ! currentCell ) {
|
602
|
+
return;
|
603
|
+
}
|
851
604
|
|
605
|
+
var currRow = dt
|
606
|
+
.rows( { filter: 'applied', order: 'applied' } )
|
607
|
+
.indexes()
|
608
|
+
.indexOf( currentCell.index().row );
|
852
609
|
|
610
|
+
// When server-side processing, `rows().indexes()` only gives the rows
|
611
|
+
// that are available at the client-side, so we need to normalise the
|
612
|
+
// row's current position by the display start point
|
613
|
+
if ( pageInfo.serverSide ) {
|
614
|
+
currRow += pageInfo.start;
|
615
|
+
}
|
853
616
|
|
854
|
-
|
855
|
-
|
856
|
-
|
617
|
+
var currCol = dt
|
618
|
+
.columns( columns )
|
619
|
+
.indexes()
|
620
|
+
.indexOf( currentCell.index().column );
|
857
621
|
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
{
|
867
|
-
if ( _oDatatable )
|
868
|
-
{
|
869
|
-
if ( typeof _oDatatable.aoData[ _oDatatable.aiDisplay[ y ] ] != 'undefined' )
|
870
|
-
{
|
871
|
-
return _oDatatable.aoData[ _oDatatable.aiDisplay[ y ] ].nTr.getElementsByTagName('td')[x];
|
622
|
+
var
|
623
|
+
row = currRow,
|
624
|
+
column = columns[ currCol ]; // row is the display, column is an index
|
625
|
+
|
626
|
+
if ( direction === 'right' ) {
|
627
|
+
if ( currCol >= columns.length - 1 ) {
|
628
|
+
row++;
|
629
|
+
column = columns[0];
|
872
630
|
}
|
873
|
-
else
|
874
|
-
|
875
|
-
return null;
|
631
|
+
else {
|
632
|
+
column = columns[ currCol+1 ];
|
876
633
|
}
|
877
634
|
}
|
878
|
-
else
|
879
|
-
|
880
|
-
|
635
|
+
else if ( direction === 'left' ) {
|
636
|
+
if ( currCol === 0 ) {
|
637
|
+
row--;
|
638
|
+
column = columns[ columns.length - 1 ];
|
639
|
+
}
|
640
|
+
else {
|
641
|
+
column = columns[ currCol-1 ];
|
642
|
+
}
|
643
|
+
}
|
644
|
+
else if ( direction === 'up' ) {
|
645
|
+
row--;
|
646
|
+
}
|
647
|
+
else if ( direction === 'down' ) {
|
648
|
+
row++;
|
881
649
|
}
|
882
|
-
}
|
883
650
|
|
651
|
+
if ( row >= 0 && row < rows && $.inArray( column, columns ) !== -1
|
652
|
+
) {
|
653
|
+
e.preventDefault();
|
884
654
|
|
885
|
-
|
886
|
-
* Function: _fnCoordsFromCell
|
887
|
-
* Purpose: Calculate the x and y position in a table from a TD cell
|
888
|
-
* Returns: array[2] int: [x, y]
|
889
|
-
* Inputs: node:n - TD cell of interest
|
890
|
-
* Notes: Not actually interested in this for DataTables since it might go out of date
|
891
|
-
*/
|
892
|
-
function _fnCoordsFromCell( n )
|
893
|
-
{
|
894
|
-
if ( _oDatatable )
|
895
|
-
{
|
896
|
-
return [
|
897
|
-
$('td', n.parentNode).index(n),
|
898
|
-
$('tr', n.parentNode.parentNode).index(n.parentNode) + _oDatatable._iDisplayStart
|
899
|
-
];
|
655
|
+
this._focus( row, column );
|
900
656
|
}
|
901
|
-
else
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
$('tr', n.parentNode.parentNode).index(n.parentNode)
|
906
|
-
];
|
657
|
+
else if ( ! keyBlurable || ! this.c.blurable ) {
|
658
|
+
// No new focus, but if the table isn't blurable, then don't loose
|
659
|
+
// focus
|
660
|
+
e.preventDefault();
|
907
661
|
}
|
908
|
-
|
662
|
+
else {
|
663
|
+
this._blur();
|
664
|
+
}
|
665
|
+
},
|
909
666
|
|
910
667
|
|
911
|
-
|
912
|
-
*
|
913
|
-
*
|
914
|
-
*
|
915
|
-
*
|
916
|
-
* Notes: This is so nasty, but without browser detection you can't tell which you should set
|
917
|
-
* So on browsers that support both, the scroll top will be set twice. I can live with
|
918
|
-
* that :-)
|
668
|
+
/**
|
669
|
+
* Create a hidden input element that can receive focus on behalf of the
|
670
|
+
* table
|
671
|
+
*
|
672
|
+
* @private
|
919
673
|
*/
|
920
|
-
function
|
674
|
+
_tabInput: function ()
|
921
675
|
{
|
922
|
-
|
923
|
-
|
924
|
-
|
676
|
+
var that = this;
|
677
|
+
var dt = this.s.dt;
|
678
|
+
var tabIndex = this.c.tabIndex !== null ?
|
679
|
+
this.c.tabIndex :
|
680
|
+
dt.settings()[0].iTabIndex;
|
925
681
|
|
682
|
+
if ( tabIndex == -1 ) {
|
683
|
+
return;
|
684
|
+
}
|
926
685
|
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
686
|
+
var div = $('<div><input type="text" tabindex="'+tabIndex+'"/></div>')
|
687
|
+
.css( {
|
688
|
+
position: 'absolute',
|
689
|
+
height: 1,
|
690
|
+
width: 0,
|
691
|
+
overflow: 'hidden'
|
692
|
+
} )
|
693
|
+
.insertBefore( dt.table().node() );
|
694
|
+
|
695
|
+
div.children().on( 'focus', function () {
|
696
|
+
that._focus( dt.cell(':eq(0)', {page: 'current'}) );
|
697
|
+
} );
|
937
698
|
}
|
699
|
+
} );
|
938
700
|
|
939
701
|
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
702
|
+
/**
|
703
|
+
* KeyTable default settings for initialisation
|
704
|
+
*
|
705
|
+
* @namespace
|
706
|
+
* @name KeyTable.defaults
|
707
|
+
* @static
|
708
|
+
*/
|
709
|
+
KeyTable.defaults = {
|
710
|
+
/**
|
711
|
+
* Can focus be removed from the table
|
712
|
+
* @type {Boolean}
|
945
713
|
*/
|
946
|
-
|
947
|
-
{
|
948
|
-
var iLeft = 0;
|
949
|
-
var iTop = 0;
|
950
|
-
|
951
|
-
if (obj.offsetParent)
|
952
|
-
{
|
953
|
-
iLeft = obj.offsetLeft;
|
954
|
-
iTop = obj.offsetTop;
|
955
|
-
obj = obj.offsetParent;
|
956
|
-
while (obj)
|
957
|
-
{
|
958
|
-
iLeft += obj.offsetLeft;
|
959
|
-
iTop += obj.offsetTop;
|
960
|
-
obj = obj.offsetParent;
|
961
|
-
}
|
962
|
-
}
|
963
|
-
return [iLeft,iTop];
|
964
|
-
}
|
714
|
+
blurable: true,
|
965
715
|
|
716
|
+
/**
|
717
|
+
* Class to give to the focused cell
|
718
|
+
* @type {String}
|
719
|
+
*/
|
720
|
+
className: 'focus',
|
966
721
|
|
967
|
-
|
968
|
-
*
|
969
|
-
*
|
970
|
-
*
|
971
|
-
* Inputs: node:nTarget - the node of interest
|
722
|
+
/**
|
723
|
+
* Columns that can be focused. This is automatically merged with the
|
724
|
+
* visible columns as only visible columns can gain focus.
|
725
|
+
* @type {String}
|
972
726
|
*/
|
973
|
-
|
974
|
-
{
|
975
|
-
for ( var i=0, iLen=_oDatatable.aiDisplay.length ; i<iLen ; i++ )
|
976
|
-
{
|
977
|
-
var nTr = _oDatatable.aoData[ _oDatatable.aiDisplay[i] ].nTr;
|
978
|
-
var nTds = nTr.getElementsByTagName('td');
|
979
|
-
for ( var j=0, jLen=nTds.length ; j<jLen ; j++ )
|
980
|
-
{
|
981
|
-
if ( nTds[j] == nTarget )
|
982
|
-
{
|
983
|
-
return [ j, i ];
|
984
|
-
}
|
985
|
-
}
|
986
|
-
}
|
987
|
-
return null;
|
988
|
-
}
|
727
|
+
columns: '', // all
|
989
728
|
|
729
|
+
/**
|
730
|
+
* Editor instance to automatically perform Excel like navigation
|
731
|
+
* @type {Editor}
|
732
|
+
*/
|
733
|
+
editor: null,
|
990
734
|
|
735
|
+
/**
|
736
|
+
* Select a cell to automatically select on start up. `null` for no
|
737
|
+
* automatic selection
|
738
|
+
* @type {cell-selector}
|
739
|
+
*/
|
740
|
+
focus: null,
|
991
741
|
|
992
|
-
|
993
|
-
*
|
742
|
+
/**
|
743
|
+
* Array of keys to listen for
|
744
|
+
* @type {null|array}
|
994
745
|
*/
|
746
|
+
keys: null,
|
995
747
|
|
996
|
-
|
997
|
-
*
|
998
|
-
*
|
999
|
-
* Returns: -
|
1000
|
-
* Inputs: object:oInit - optional - Initalisation object with the following parameters:
|
1001
|
-
* array[2] int:focus - x and y coordinates of the initial target
|
1002
|
-
* or
|
1003
|
-
* node:focus - the node to set initial focus on
|
1004
|
-
* node:table - the table to use, if not given, first table with class 'KeyTable' will be used
|
1005
|
-
* string:focusClass - focusing class to give to table elements
|
1006
|
-
* object:that - focus
|
1007
|
-
* bool:initScroll - scroll the view port on load, default true
|
1008
|
-
* int:tabIndex - the tab index to give the hidden input element
|
748
|
+
/**
|
749
|
+
* Tab index for where the table should sit in the document's tab flow
|
750
|
+
* @type {integer|null}
|
1009
751
|
*/
|
1010
|
-
|
1011
|
-
|
1012
|
-
/* Save scope */
|
1013
|
-
_that = that;
|
752
|
+
tabIndex: null
|
753
|
+
};
|
1014
754
|
|
1015
|
-
/* Capture undefined initialisation and apply the defaults */
|
1016
|
-
if ( typeof oInit == 'undefined' ) {
|
1017
|
-
oInit = {};
|
1018
|
-
}
|
1019
755
|
|
1020
|
-
if ( typeof oInit.focus == 'undefined' ) {
|
1021
|
-
oInit.focus = [0,0];
|
1022
|
-
}
|
1023
756
|
|
1024
|
-
|
1025
|
-
$(oInit.table).addClass('KeyTable');
|
757
|
+
KeyTable.version = "2.1.0";
|
1026
758
|
|
1027
|
-
if ( typeof oInit.focusClass != 'undefined' ) {
|
1028
|
-
_sFocusClass = oInit.focusClass;
|
1029
|
-
}
|
1030
759
|
|
1031
|
-
|
1032
|
-
|
1033
|
-
}
|
760
|
+
$.fn.dataTable.KeyTable = KeyTable;
|
761
|
+
$.fn.DataTable.KeyTable = KeyTable;
|
1034
762
|
|
1035
|
-
if ( typeof oInit.initScroll == 'undefined' ) {
|
1036
|
-
oInit.initScroll = true;
|
1037
|
-
}
|
1038
763
|
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
/* Cache the tbody node of interest */
|
1045
|
-
_nBody = oInit.table.getElementsByTagName('tbody')[0];
|
1046
|
-
|
1047
|
-
/* If the table is inside a form, then we need a hidden input box which can be used by the
|
1048
|
-
* browser to catch the browser tabbing for our table
|
1049
|
-
*/
|
1050
|
-
if ( _bForm )
|
1051
|
-
{
|
1052
|
-
var nDiv = document.createElement('div');
|
1053
|
-
_nInput = document.createElement('input');
|
1054
|
-
nDiv.style.height = "1px"; /* Opera requires a little something */
|
1055
|
-
nDiv.style.width = "0px";
|
1056
|
-
nDiv.style.overflow = "hidden";
|
1057
|
-
if ( typeof oInit.tabIndex != 'undefined' )
|
1058
|
-
{
|
1059
|
-
_nInput.tabIndex = oInit.tabIndex;
|
1060
|
-
}
|
1061
|
-
nDiv.appendChild(_nInput);
|
1062
|
-
oInit.table.parentNode.insertBefore( nDiv, oInit.table.nextSibling );
|
1063
|
-
|
1064
|
-
$(_nInput).focus( function () {
|
1065
|
-
/* See if we want to 'tab into' the table or out */
|
1066
|
-
if ( !_bInputFocused )
|
1067
|
-
{
|
1068
|
-
_bKeyCapture = true;
|
1069
|
-
_bInputFocused = false;
|
1070
|
-
if ( typeof oInit.focus.nodeName != "undefined" )
|
1071
|
-
{
|
1072
|
-
_fnSetFocus( oInit.focus, oInit.initScroll );
|
1073
|
-
}
|
1074
|
-
else
|
1075
|
-
{
|
1076
|
-
_fnSetFocus( _fnCellFromCoords( oInit.focus[0], oInit.focus[1]), oInit.initScroll );
|
1077
|
-
}
|
1078
|
-
|
1079
|
-
/* Need to interup the thread for this to work */
|
1080
|
-
setTimeout( function() { _nInput.blur(); }, 0 );
|
1081
|
-
}
|
1082
|
-
} );
|
1083
|
-
_bKeyCapture = false;
|
1084
|
-
}
|
1085
|
-
else
|
1086
|
-
{
|
1087
|
-
/* Set the initial focus on the table */
|
1088
|
-
if ( typeof oInit.focus.nodeName != "undefined" )
|
1089
|
-
{
|
1090
|
-
_fnSetFocus( oInit.focus, oInit.initScroll );
|
1091
|
-
}
|
1092
|
-
else
|
1093
|
-
{
|
1094
|
-
_fnSetFocus( _fnCellFromCoords( oInit.focus[0], oInit.focus[1]), oInit.initScroll );
|
1095
|
-
}
|
1096
|
-
_fnCaptureKeys();
|
764
|
+
DataTable.Api.register( 'cell.blur()', function () {
|
765
|
+
return this.iterator( 'table', function (ctx) {
|
766
|
+
if ( ctx.keytable ) {
|
767
|
+
ctx.keytable.blur();
|
1097
768
|
}
|
769
|
+
} );
|
770
|
+
} );
|
1098
771
|
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
{
|
1104
|
-
$(_oDatatable.nTable).on( 'click', 'td', _fnClick );
|
772
|
+
DataTable.Api.register( 'cell().focus()', function () {
|
773
|
+
return this.iterator( 'cell', function (ctx, row, column) {
|
774
|
+
if ( ctx.keytable ) {
|
775
|
+
ctx.keytable.focus( row, column );
|
1105
776
|
}
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
777
|
+
} );
|
778
|
+
} );
|
779
|
+
|
780
|
+
DataTable.Api.register( 'keys.disable()', function () {
|
781
|
+
return this.iterator( 'table', function (ctx) {
|
782
|
+
if ( ctx.keytable ) {
|
783
|
+
ctx.keytable.enable( false );
|
1109
784
|
}
|
785
|
+
} );
|
786
|
+
} );
|
1110
787
|
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
{
|
1119
|
-
bTableClick = true;
|
1120
|
-
break;
|
1121
|
-
}
|
1122
|
-
nTarget = nTarget.parentNode;
|
1123
|
-
}
|
1124
|
-
if ( !bTableClick )
|
1125
|
-
{
|
1126
|
-
_fnBlur();
|
1127
|
-
}
|
1128
|
-
} );
|
1129
|
-
}
|
788
|
+
DataTable.Api.register( 'keys.enable()', function ( opts ) {
|
789
|
+
return this.iterator( 'table', function (ctx) {
|
790
|
+
if ( ctx.keytable ) {
|
791
|
+
ctx.keytable.enable( opts === undefined ? true : opts );
|
792
|
+
}
|
793
|
+
} );
|
794
|
+
} );
|
1130
795
|
|
1131
|
-
|
796
|
+
// Cell selector
|
797
|
+
DataTable.ext.selector.cell.push( function ( settings, opts, cells ) {
|
798
|
+
var focused = opts.focused;
|
799
|
+
var kt = settings.keytable;
|
800
|
+
var out = [];
|
1132
801
|
|
1133
|
-
if (
|
1134
|
-
|
1135
|
-
datatable = null;
|
1136
|
-
}
|
1137
|
-
else if ( $.isPlainObject( oInit ) ) {
|
1138
|
-
table = oInit.table;
|
1139
|
-
datatable = oInit.datatable;
|
1140
|
-
}
|
1141
|
-
else {
|
1142
|
-
datatable = new $.fn.dataTable.Api( oInit ).settings()[0];
|
1143
|
-
table = datatable.nTable;
|
802
|
+
if ( ! kt || focused === undefined ) {
|
803
|
+
return cells;
|
1144
804
|
}
|
1145
|
-
/* Initialise our new object */
|
1146
|
-
_fnInit( table, datatable, oInit, this );
|
1147
|
-
};
|
1148
805
|
|
806
|
+
for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
|
807
|
+
if ( (focused === true && kt.focused( cells[i] ) ) ||
|
808
|
+
(focused === false && ! kt.focused( cells[i] ) )
|
809
|
+
) {
|
810
|
+
out.push( cells[i] );
|
811
|
+
}
|
812
|
+
}
|
1149
813
|
|
1150
|
-
|
1151
|
-
|
814
|
+
return out;
|
815
|
+
} );
|
1152
816
|
|
1153
|
-
$.fn.dataTable.KeyTable = KeyTable;
|
1154
|
-
$.fn.DataTable.KeyTable = KeyTable;
|
1155
817
|
|
818
|
+
// Attach a listener to the document which listens for DataTables initialisation
|
819
|
+
// events so we can automatically initialise
|
820
|
+
$(document).on( 'preInit.dt.dtk', function (e, settings, json) {
|
821
|
+
if ( e.namespace !== 'dt' ) {
|
822
|
+
return;
|
823
|
+
}
|
1156
824
|
|
1157
|
-
|
1158
|
-
|
825
|
+
var init = settings.oInit.keys;
|
826
|
+
var defaults = DataTable.defaults.keys;
|
1159
827
|
|
828
|
+
if ( init || defaults ) {
|
829
|
+
var opts = $.extend( {}, init, defaults );
|
1160
830
|
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
}
|
1165
|
-
|
1166
|
-
// Node/CommonJS
|
1167
|
-
factory( require('jquery'), require('datatables') );
|
1168
|
-
}
|
1169
|
-
else if ( jQuery && !jQuery.fn.dataTable.KeyTable ) {
|
1170
|
-
// Otherwise simply initialise as normal, stopping multiple evaluation
|
1171
|
-
factory( jQuery, jQuery.fn.dataTable );
|
1172
|
-
}
|
831
|
+
if ( init !== false ) {
|
832
|
+
new KeyTable( settings, opts );
|
833
|
+
}
|
834
|
+
}
|
835
|
+
} );
|
1173
836
|
|
1174
837
|
|
1175
|
-
|
838
|
+
return KeyTable;
|
839
|
+
}));
|