]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/static/AdminLTE-2.3.7/plugins/datatables/extensions/KeyTable/js/dataTables.keyTable.js
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / pybind / mgr / dashboard / static / AdminLTE-2.3.7 / plugins / datatables / extensions / KeyTable / js / dataTables.keyTable.js
1 /*! KeyTable 1.2.1
2 * ©2010-2014 SpryMedia Ltd - datatables.net/license
3 */
4
5 /**
6 * @summary KeyTable
7 * @description Spreadsheet like keyboard navigation for DataTables
8 * @version 1.2.1
9 * @file dataTables.keyTable.js
10 * @author SpryMedia Ltd (www.sprymedia.co.uk)
11 * @contact www.sprymedia.co.uk/contact
12 * @copyright Copyright 2009-2014 SpryMedia Ltd.
13 *
14 * This source file is free software, available under the following license:
15 * MIT license - http://datatables.net/license/mit
16 *
17 * This source file is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
20 *
21 * For details please refer to: http://www.datatables.net
22 */
23
24 // Global scope for KeyTable for backwards compatibility. Will be removed in 1.3
25 var KeyTable;
26
27
28 (function(window, document, undefined) {
29
30
31 var factory = function( $, DataTable ) {
32 "use strict";
33
34 KeyTable = function ( oInit )
35 {
36 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
37 * API parameters
38 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
39
40 /*
41 * Variable: block
42 * Purpose: Flag whether or not KeyTable events should be processed
43 * Scope: KeyTable - public
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 };
57
58
59 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
60 * API methods
61 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
62
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 };
73
74
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 };
85
86
87 /*
88 * Function: fnGetCurrentTD
89 * Purpose: Get the currently focused cell
90 * Returns: node: - focused element
91 * Inputs: void
92 */
93 this.fnGetCurrentTD = function ()
94 {
95 return _nOldFocus;
96 };
97
98
99 /*
100 * Function: fnSetPosition
101 * Purpose: Set the position of the focused cell
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
106 */
107 this.fnSetPosition = function( x, y )
108 {
109 if ( typeof x == 'object' && x.nodeName )
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: -
125 */
126 this.fnBlur = function()
127 {
128 _fnBlur();
129 };
130
131
132 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
133 * Private parameters
134 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
135
136 /*
137 * Variable: _nBody
138 * Purpose: Body node of the table - cached for renference
139 * Scope: KeyTable - private
140 */
141 var _nBody = null;
142
143 /*
144 * Variable:
145 * Purpose:
146 * Scope: KeyTable - private
147 */
148 var _nOldFocus = null;
149
150 /*
151 * Variable: _iOldX and _iOldY
152 * Purpose: X and Y coords of the old elemet that was focused on
153 * Scope: KeyTable - private
154 */
155 var _iOldX = null;
156 var _iOldY = null;
157
158 /*
159 * Variable: _that
160 * Purpose: Scope saving for 'this' after a jQuery event
161 * Scope: KeyTable - private
162 */
163 var _that = null;
164
165 /*
166 * Variable: sFocusClass
167 * Purpose: Class that should be used for focusing on a cell
168 * Scope: KeyTable - private
169 */
170 var _sFocusClass = "focus";
171
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
179 /*
180 * Variable: _oaoEvents
181 * Purpose: Event cache object, one array for each supported event for speed of searching
182 * Scope: KeyTable - private
183 */
184 var _oaoEvents = {
185 "action": [],
186 "esc": [],
187 "focus": [],
188 "blur": []
189 };
190
191 /*
192 * Variable: _oDatatable
193 * Purpose: DataTables settings object for if we are actually using a
194 * DataTables table
195 * Scope: KeyTable - private
196 */
197 var _oDatatable = null;
198
199 var _bForm;
200 var _nInput;
201 var _bInputFocused = false;
202
203
204 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
205 * Private methods
206 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
207
208 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
209 * Key table events
210 */
211
212 /*
213 * Function: _fnEventAddTemplate
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 );
248 }
249 };
250 }
251
252
253 /*
254 * Function: _fnEventRemoveTemplate
255 * Purpose: Create a function (with closure for sKey) event removal API
256 * Returns: function: - template function
257 * Inputs: string:sKey - type of event to detect
258 */
259 function _fnEventRemoveTemplate( sKey )
260 {
261 /*
262 * Function: -
263 * Purpose: API function for removing event from cache
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 );
286 }
287 }
288 else if ( typeof arguments[0] == "object" )
289 {
290 var aCoords = _fnCoordsFromCell( x );
291 if ( typeof arguments[1] == "function" )
292 {
293 _fnEventRemove( sKey, aCoords[0], aCoords[1], y );
294 }
295 else
296 {
297 _fnEventRemove( sKey, aCoords[0], aCoords[1] );
298 }
299 }
300 else
301 {
302 alert( "Unhandable event type was removed: x" +x+ " y:" +y+ " z:" +z );
303 }
304 };
305 }
306
307 /* Use the template functions to add the event API functions */
308 for ( var sKey in _oaoEvents )
309 {
310 if ( sKey )
311 {
312 this.event[sKey] = _fnEventAddTemplate( sKey );
313 this.event.remove[sKey] = _fnEventRemoveTemplate( sKey );
314 }
315 }
316
317
318 /*
319 * Function: _fnEventAdd
320 * Purpose: Add an event to the internal cache
321 * Returns: -
322 * Inputs: string:sType - type of event to add, given by the available elements in _oaoEvents
323 * int:x - x-coords to add event to - can be null for "blanket" event
324 * int:y - y-coords to add event to - can be null for "blanket" event
325 * function:fn - callback function for when triggered
326 */
327 function _fnEventAdd( sType, x, y, fn )
328 {
329 _oaoEvents[sType].push( {
330 "x": x,
331 "y": y,
332 "fn": fn
333 } );
334 }
335
336
337 /*
338 * Function: _fnEventRemove
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 }
371 }
372 return iCorrector;
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 }
402 }
403 return iFired;
404 }
405
406
407
408 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
409 * Focus functions
410 */
411
412 /*
413 * Function: _fnSetFocus
414 * Purpose: Set focus on a node, and remove from an old node if needed
415 * Returns: -
416 * Inputs: node:nTarget - node we want to focus on
417 * bool:bAutoScroll - optional - should we scroll the view port to the display
418 */
419 function _fnSetFocus( nTarget, bAutoScroll )
420 {
421 /* If node already has focus, just ignore this call */
422 if ( _nOldFocus == nTarget )
423 {
424 return;
425 }
426
427 if ( typeof bAutoScroll == 'undefined' )
428 {
429 bAutoScroll = true;
430 }
431
432 /* Remove old focus (with blur event if needed) */
433 if ( _nOldFocus !== null )
434 {
435 _fnRemoveFocus( _nOldFocus );
436 }
437
438 /* Add the new class to highlight the focused cell */
439 $(nTarget).addClass( _sFocusClass );
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 }
467
468 /* Page backwards */
469 while ( iRow < oSettings._iDisplayStart )
470 {
471 oSettings._iDisplayStart = oSettings._iDisplayLength>=0 ?
472 oSettings._iDisplayStart - oSettings._iDisplayLength :
473 0;
474
475 if ( oSettings._iDisplayStart < 0 )
476 {
477 oSettings._iDisplayStart = 0;
478 }
479 _oDatatable.oApi._fnCalculateEnd( oSettings );
480 }
481
482 /* Re-draw the table */
483 _oDatatable.oApi._fnDraw( oSettings );
484
485 /* Restore the key capture */
486 _bKeyCapture = bKeyCaptureCache;
487 }
488
489 /* Cache the information that we are interested in */
490 var aNewPos = _fnCoordsFromCell( nTarget );
491 _nOldFocus = nTarget;
492 _iOldX = aNewPos[0];
493 _iOldY = aNewPos[1];
494
495 var iViewportHeight, iViewportWidth, iScrollTop, iScrollLeft, iHeight, iWidth, aiPos;
496 if ( bAutoScroll )
497 {
498 /* Scroll the viewport such that the new cell is fully visible in the rendered window */
499 iViewportHeight = $(window).height();
500 iViewportWidth = $(window).width();
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 }
516
517 /* Correct viewport positioning for vertical scrolling */
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 }
528
529 /* Correct viewport positioning for horizontal scrolling */
530 if ( aiPos[0]+iWidth > iScrollLeft+iViewportWidth )
531 {
532 /* Displayed element is off the bottom of the viewport */
533 _fnSetScrollLeft( aiPos[0]+iWidth - iViewportWidth );
534 }
535 else if ( aiPos[0] < iScrollLeft )
536 {
537 /* Displayed element if off the Left of the viewport */
538 _fnSetScrollLeft( aiPos[0] );
539 }
540 }
541
542 /* Take account of scrolling in DataTables 1.7 */
543 if ( _oDatatable && typeof oSettings.oScroll != 'undefined' &&
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 }
563
564 /* Correct for horizontal scrolling */
565 if ( nTarget.offsetLeft + iWidth > iViewportWidth+iScrollLeft )
566 {
567 dtScrollBody.scrollLeft = (nTarget.offsetLeft + iWidth) - iViewportWidth;
568 }
569 else if ( nTarget.offsetLeft < iScrollLeft )
570 {
571 dtScrollBody.scrollLeft = nTarget.offsetLeft;
572 }
573 }
574
575 /* Focused - so we want to capture the keys */
576 _fnCaptureKeys();
577
578 /* Fire of the focus event if there is one */
579 _fnEventFire( "focus", _iOldX, _iOldY );
580 }
581
582
583 /*
584 * Function: _fnBlur
585 * Purpose: Blur focus from the whole table
586 * Returns: -
587 * Inputs: -
588 */
589 function _fnBlur()
590 {
591 _fnRemoveFocus( _nOldFocus );
592 _iOldX = null;
593 _iOldY = null;
594 _nOldFocus = null;
595 _fnReleaseKeys();
596 }
597
598
599 /*
600 * Function: _fnRemoveFocus
601 * Purpose: Remove focus from a cell and fire any blur events which are attached
602 * Returns: -
603 * Inputs: node:nTarget - cell of interest
604 */
605 function _fnRemoveFocus( nTarget )
606 {
607 $(nTarget).removeClass( _sFocusClass );
608 $(nTarget).parent().removeClass( _sFocusClass );
609 _fnEventFire( "blur", _iOldX, _iOldY );
610 }
611
612
613 /*
614 * Function: _fnClick
615 * Purpose: Focus on the element that has been clicked on by the user
616 * Returns: -
617 * Inputs: event:e - click event
618 */
619 function _fnClick ( e )
620 {
621 var nTarget = this;
622 while ( nTarget.nodeName != "TD" )
623 {
624 nTarget = nTarget.parentNode;
625 }
626
627 _fnSetFocus( nTarget );
628 _fnCaptureKeys();
629 }
630
631
632
633 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
634 * Key events
635 */
636
637 /*
638 * Function: _fnKey
639 * Purpose: Deal with a key events, be it moving the focus or return etc.
640 * Returns: bool: - allow browser default action
641 * Inputs: event:e - key event
642 */
643 function _fnKey ( e )
644 {
645 /* If user or system has blocked KeyTable from doing anything, just ignore this event */
646 if ( _that.block || !_bKeyCapture )
647 {
648 return true;
649 }
650
651 /* If a modifier key is pressed (exapct shift), ignore the event */
652 if ( e.metaKey || e.altKey || e.ctrlKey )
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 ];
679 }
680 else
681 {
682 iTableHeight = _nBody.getElementsByTagName('tr').length;
683 }
684
685 /* Capture shift+tab to match the left arrow key */
686 var iKey = (e.keyCode == 9 && e.shiftKey) ? -1 : e.keyCode;
687
688 switch( iKey )
689 {
690 case 13: /* return */
691 e.preventDefault();
692 e.stopPropagation();
693 _fnEventFire( "action", _iOldX, _iOldY );
694 return true;
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;
705 break;
706
707 case -1:
708 case 37: /* left arrow */
709 if ( _iOldX > 0 ) {
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 }
737 }
738 break;
739
740 case 38: /* up arrow */
741 if ( _iOldY > 0 ) {
742 x = _iOldX;
743 y = _iOldY - 1;
744 } else {
745 return false;
746 }
747 break;
748
749 case 36: /* home */
750 x = _iOldX;
751 y = 0;
752 break;
753
754 case 33: /* page up */
755 x = _iOldX;
756 y = _iOldY - 10;
757 if (y < 0) {
758 y = 0;
759 }
760 break;
761
762 case 9: /* tab */
763 case 39: /* right arrow */
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 }
793 break;
794
795 case 40: /* down arrow */
796 if ( _iOldY < iTableHeight-1 ) {
797 x = _iOldX;
798 y = _iOldY + 1;
799 } else {
800 return false;
801 }
802 break;
803
804 case 35: /* end */
805 x = _iOldX;
806 y = iTableHeight-1;
807 break;
808
809 case 34: /* page down */
810 x = _iOldX;
811 y = _iOldY+10;
812 if (y > iTableHeight-1) {
813 y = iTableHeight-1;
814 }
815 break;
816
817 default: /* Nothing we are interested in */
818 return true;
819 }
820
821 _fnSetFocus( _fnCellFromCoords(x, y) );
822 return false;
823 }
824
825
826 /*
827 * Function: _fnCaptureKeys
828 * Purpose: Start capturing key events for this table
829 * Returns: -
830 * Inputs: -
831 */
832 function _fnCaptureKeys( )
833 {
834 if ( !_bKeyCapture )
835 {
836 _bKeyCapture = true;
837 }
838 }
839
840
841 /*
842 * Function: _fnReleaseKeys
843 * Purpose: Stop capturing key events for this table
844 * Returns: -
845 * Inputs: -
846 */
847 function _fnReleaseKeys( )
848 {
849 _bKeyCapture = false;
850 }
851
852
853
854 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
855 * Support functions
856 */
857
858 /*
859 * Function: _fnCellFromCoords
860 * Purpose: Calulate the target TD cell from x and y coordinates
861 * Returns: node: - TD target
862 * Inputs: int:x - x coordinate
863 * int:y - y coordinate
864 */
865 function _fnCellFromCoords( x, y )
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];
872 }
873 else
874 {
875 return null;
876 }
877 }
878 else
879 {
880 return $('tr:eq('+y+')>td:eq('+x+')', _nBody )[0];
881 }
882 }
883
884
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 ];
900 }
901 else
902 {
903 return [
904 $('td', n.parentNode).index(n),
905 $('tr', n.parentNode.parentNode).index(n.parentNode)
906 ];
907 }
908 }
909
910
911 /*
912 * Function: _fnSetScrollTop
913 * Purpose: Set the vertical scrolling position
914 * Returns: -
915 * Inputs: int:iPos - scrolltop
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 :-)
919 */
920 function _fnSetScrollTop( iPos )
921 {
922 document.documentElement.scrollTop = iPos;
923 document.body.scrollTop = iPos;
924 }
925
926
927 /*
928 * Function: _fnSetScrollLeft
929 * Purpose: Set the horizontal scrolling position
930 * Returns: -
931 * Inputs: int:iPos - scrollleft
932 */
933 function _fnSetScrollLeft( iPos )
934 {
935 document.documentElement.scrollLeft = iPos;
936 document.body.scrollLeft = iPos;
937 }
938
939
940 /*
941 * Function: _fnGetPos
942 * Purpose: Get the position of an object on the rendered page
943 * Returns: array[2] int: [left, right]
944 * Inputs: node:obj - element of interest
945 */
946 function _fnGetPos ( obj )
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 }
965
966
967 /*
968 * Function: _fnFindDtCell
969 * Purpose: Get the coords. of a cell from the DataTables internal information
970 * Returns: array[2] int: [x, y] coords. or null if not found
971 * Inputs: node:nTarget - the node of interest
972 */
973 function _fnFindDtCell( nTarget )
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 }
989
990
991
992 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
993 * Initialisation
994 */
995
996 /*
997 * Function: _fnInit
998 * Purpose: Initialise the KeyTable
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
1009 */
1010 function _fnInit( table, datatable, oInit, that )
1011 {
1012 /* Save scope */
1013 _that = that;
1014
1015 /* Capture undefined initialisation and apply the defaults */
1016 if ( typeof oInit == 'undefined' ) {
1017 oInit = {};
1018 }
1019
1020 if ( typeof oInit.focus == 'undefined' ) {
1021 oInit.focus = [0,0];
1022 }
1023
1024 oInit.table = table;
1025 $(oInit.table).addClass('KeyTable');
1026
1027 if ( typeof oInit.focusClass != 'undefined' ) {
1028 _sFocusClass = oInit.focusClass;
1029 }
1030
1031 if ( typeof datatable != 'undefined' ) {
1032 _oDatatable = datatable;
1033 }
1034
1035 if ( typeof oInit.initScroll == 'undefined' ) {
1036 oInit.initScroll = true;
1037 }
1038
1039 if ( typeof oInit.form == 'undefined' ) {
1040 oInit.form = false;
1041 }
1042 _bForm = oInit.form;
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();
1097 }
1098
1099 /* Add event listeners */
1100 $(document).bind( "keydown", _fnKey );
1101
1102 if ( _oDatatable )
1103 {
1104 $(_oDatatable.nTable).on( 'click', 'td', _fnClick );
1105 }
1106 else
1107 {
1108 $(_nBody).on( 'click', 'td', _fnClick );
1109 }
1110
1111 /* Loose table focus when click outside the table */
1112 $(document).click( function(e) {
1113 var nTarget = e.target;
1114 var bTableClick = false;
1115 while ( nTarget )
1116 {
1117 if ( nTarget == oInit.table )
1118 {
1119 bTableClick = true;
1120 break;
1121 }
1122 nTarget = nTarget.parentNode;
1123 }
1124 if ( !bTableClick )
1125 {
1126 _fnBlur();
1127 }
1128 } );
1129 }
1130
1131 var table, datatable;
1132
1133 if ( oInit === undefined ) {
1134 table = $('table.KeyTable')[0];
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;
1144 }
1145 /* Initialise our new object */
1146 _fnInit( table, datatable, oInit, this );
1147 };
1148
1149
1150 KeyTable.version = "1.2.1";
1151
1152
1153 $.fn.dataTable.KeyTable = KeyTable;
1154 $.fn.DataTable.KeyTable = KeyTable;
1155
1156
1157 return KeyTable;
1158 }; // /factory
1159
1160
1161 // Define as an AMD module if possible
1162 if ( typeof define === 'function' && define.amd ) {
1163 define( ['jquery', 'datatables'], factory );
1164 }
1165 else if ( typeof exports === 'object' ) {
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 }
1173
1174
1175 })(window, document);