Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / 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);