src/cm/media/js/lib/yui/yui3-3.15.0/build/datatable-keynav/datatable-keynav-debug.js
changeset 602 e16a97fb364a
equal deleted inserted replaced
601:d334a616c023 602:e16a97fb364a
       
     1 YUI.add('datatable-keynav', function (Y, NAME) {
       
     2 
       
     3 /**
       
     4  Provides keyboard navigation of DataTable cells and support for adding other
       
     5  keyboard actions.
       
     6 
       
     7  @module datatable
       
     8  @submodule datatable-keynav
       
     9 */
       
    10 var arrEach = Y.Array.each,
       
    11 
       
    12 /**
       
    13  A DataTable class extension that provides navigation via keyboard, based on
       
    14  WAI-ARIA recommendation for the [Grid widget](http://www.w3.org/WAI/PF/aria-practices/#grid)
       
    15  and extensible to support other actions.
       
    16 
       
    17 
       
    18  @class DataTable.KeyNav
       
    19  @for DataTable
       
    20 */
       
    21     DtKeyNav = function (){};
       
    22 
       
    23 /**
       
    24 Mapping of key codes to friendly key names that can be used in the
       
    25 [keyActions](#property_keyActions) property and [ARIA_ACTIONS](#property_ARIA_ACTIONS)
       
    26 property.
       
    27 
       
    28 It contains aliases for the following keys:
       
    29     <ul>
       
    30     <li>backspace</li>
       
    31     <li>tab</li>
       
    32     <li>enter</li>
       
    33     <li>esc</li>
       
    34     <li>space</li>
       
    35     <li>pgup</li>
       
    36     <li>pgdown</li>
       
    37     <li>end</li>
       
    38     <li>home</li>
       
    39     <li>left</li>
       
    40     <li>up</li>
       
    41     <li>right</li>
       
    42     <li>down</li>
       
    43     <li>f1 .. f12</li>
       
    44     </ul>
       
    45 
       
    46 
       
    47 @property KEY_NAMES
       
    48 @type {Object}
       
    49 @static
       
    50 **/
       
    51 DtKeyNav.KEY_NAMES = {
       
    52      8: 'backspace',
       
    53      9: 'tab',
       
    54     13: 'enter',
       
    55     27: 'esc',
       
    56     32: 'space',
       
    57     33: 'pgup',
       
    58     34: 'pgdown',
       
    59     35: 'end',
       
    60     36: 'home',
       
    61     37: 'left',
       
    62     38: 'up',
       
    63     39: 'right',
       
    64     40: 'down',
       
    65     112:'f1',
       
    66     113:'f2',
       
    67     114:'f3',
       
    68     115:'f4',
       
    69     116:'f5',
       
    70     117:'f6',
       
    71     118:'f7',
       
    72     119:'f8',
       
    73     120:'f9',
       
    74     121:'f10',
       
    75     122:'f11',
       
    76     123:'f12'
       
    77 };
       
    78 
       
    79 /**
       
    80 Mapping of key codes to actions according to the WAI-ARIA suggestion for the
       
    81 [Grid Widget](http://www.w3.org/WAI/PF/aria-practices/#grid).
       
    82 
       
    83 The key for each entry is a key-code or [keyName](#property_KEY_NAMES) while the
       
    84 value can be a function that performs the action or a string.  If a string,
       
    85 it can either correspond to the name of a method in this module (or  any
       
    86 method in a DataTable instance) or the name of an event to fire.
       
    87 @property ARIA_ACTIONS
       
    88 @type Object
       
    89 @static
       
    90  */
       
    91 DtKeyNav.ARIA_ACTIONS = {
       
    92     left:   '_keyMoveLeft',
       
    93     right:  '_keyMoveRight',
       
    94     up:     '_keyMoveUp',
       
    95     down:   '_keyMoveDown',
       
    96     home:   '_keyMoveRowStart',
       
    97     end:    '_keyMoveRowEnd',
       
    98     pgup:   '_keyMoveColTop',
       
    99     pgdown: '_keyMoveColBottom'
       
   100 };
       
   101 
       
   102 DtKeyNav.ATTRS = {
       
   103     /**
       
   104     Cell that's currently either focused or
       
   105     focusable when the DataTable gets the focus.
       
   106 
       
   107     @attribute focusedCell
       
   108     @type Node
       
   109     @default first cell in the table.
       
   110     **/
       
   111     focusedCell: {
       
   112         setter: '_focusedCellSetter'
       
   113     },
       
   114 
       
   115     /**
       
   116     Determines whether it is possible to navigate into the header area.
       
   117     The examples referenced in the document show both behaviors so it seems
       
   118     it is optional.
       
   119 
       
   120     @attribute keyIntoHeaders
       
   121     @type Boolean
       
   122     @default true
       
   123      */
       
   124     keyIntoHeaders: {
       
   125         value: true
       
   126     }
       
   127 
       
   128 };
       
   129 
       
   130 Y.mix( DtKeyNav.prototype, {
       
   131 
       
   132     /**
       
   133     Table of actions to be performed for each key.  It is loaded with a clone
       
   134     of [ARIA_ACTIONS](#property_ARIA_ACTIONS) by default.
       
   135 
       
   136     The key for each entry is either a key-code or an alias from the
       
   137     [KEY_NAMES](#property_KEY_NAMES) table. They can be prefixed with any combination
       
   138     of the modifier keys `alt`, `ctrl`, `meta` or `shift` each followed by a hyphen,
       
   139     such as `"ctrl-shift-up"` (modifiers, if more than one, should appear in alphabetical order).
       
   140 
       
   141     The value for each entry should be a function or the name of a method in
       
   142     the DataTable instance.  The method will receive the original keyboard
       
   143     EventFacade as its only argument.
       
   144 
       
   145     If the value is a string and it cannot be resolved into a method,
       
   146     it will be assumed to be the name of an event to fire. The listener for that
       
   147     event will receive an EventFacade containing references to the cell that has the focus,
       
   148     the row, column and, unless it is a header row, the record it corresponds to.
       
   149     The second argument will be the original EventFacade for the keyboard event.
       
   150 
       
   151      @property keyActions
       
   152      @type {Object}
       
   153      @default Y.DataTable.keyNav.ARIA_ACTIONS
       
   154      */
       
   155 
       
   156     keyActions: null,
       
   157 
       
   158     /**
       
   159     Array containing the event handles to any event that might need to be detached
       
   160     on destruction.
       
   161     @property _keyNavSubscr
       
   162     @type Array
       
   163     @default null,
       
   164     @private
       
   165      */
       
   166     _keyNavSubscr: null,
       
   167 
       
   168     /**
       
   169     Reference to the THead section that holds the headers for the datatable.
       
   170     For a Scrolling DataTable, it is the one visible to the user.
       
   171     @property _keyNavTHead
       
   172     @type Node
       
   173     @default: null
       
   174     @private
       
   175      */
       
   176     _keyNavTHead: null,
       
   177 
       
   178     /**
       
   179     Indicates if the headers of the table are nested or not.
       
   180     Nested headers makes navigation in the headers much harder.
       
   181     @property _keyNavNestedHeaders
       
   182     @default false
       
   183     @private
       
   184      */
       
   185     _keyNavNestedHeaders: false,
       
   186 
       
   187     /**
       
   188     CSS class name prefix for columns, used to search for a cell by key.
       
   189     @property _keyNavColPrefix
       
   190     @type String
       
   191     @default null (initialized via getClassname() )
       
   192     @private
       
   193      */
       
   194     _keyNavColPrefix:null,
       
   195 
       
   196     /**
       
   197     Regular expression to extract the column key from a cell via its CSS class name.
       
   198     @property _keyNavColRegExp
       
   199     @type RegExp
       
   200     @default null (initialized based on _keyNavColPrefix)
       
   201     @private
       
   202      */
       
   203     _keyNavColRegExp:null,
       
   204 
       
   205     initializer: function () {
       
   206         this.onceAfter('render', this._afterKeyNavRender);
       
   207         this._keyNavSubscr = [
       
   208             this.after('focusedCellChange', this._afterKeyNavFocusedCellChange),
       
   209             this.after('focusedChange', this._afterKeyNavFocusedChange)
       
   210         ];
       
   211         this._keyNavColPrefix = this.getClassName('col', '');
       
   212         this._keyNavColRegExp = new RegExp(this._keyNavColPrefix + '(.+?)(\\s|$)');
       
   213         this.keyActions = Y.clone(DtKeyNav.ARIA_ACTIONS);
       
   214 
       
   215     },
       
   216 
       
   217     destructor: function () {
       
   218         arrEach(this._keyNavSubscr, function (evHandle) {
       
   219             if (evHandle && evHandle.detach) {
       
   220                 evHandle.detach();
       
   221             }
       
   222         });
       
   223     },
       
   224 
       
   225     /**
       
   226     Sets the tabIndex on the focused cell and, if the DataTable has the focus,
       
   227     sets the focus on it.
       
   228 
       
   229     @method _afterFocusedCellChange
       
   230     @param e {EventFacade}
       
   231     @private
       
   232     */
       
   233     _afterKeyNavFocusedCellChange: function (e) {
       
   234         var newVal  = e.newVal,
       
   235             prevVal = e.prevVal;
       
   236 
       
   237         if (prevVal) {
       
   238             prevVal.set('tabIndex', -1);
       
   239         }
       
   240 
       
   241         if (newVal) {
       
   242             newVal.set('tabIndex', 0);
       
   243 
       
   244             if (this.get('focused')) {
       
   245                 newVal.scrollIntoView();
       
   246                 newVal.focus();
       
   247             }
       
   248         } else {
       
   249             this.set('focused', null);
       
   250         }
       
   251     },
       
   252 
       
   253     /**
       
   254     When the DataTable gets the focus, it ensures the correct cell regains
       
   255     the focus.
       
   256 
       
   257     @method _afterKeyNavFocusedChange
       
   258     @param e {EventFacade}
       
   259     @private
       
   260     */
       
   261     _afterKeyNavFocusedChange: function (e) {
       
   262         var cell = this.get('focusedCell');
       
   263         if (e.newVal) {
       
   264             if (cell) {
       
   265                 cell.scrollIntoView();
       
   266                 cell.focus();
       
   267             } else {
       
   268                 this._keyMoveFirst();
       
   269             }
       
   270         } else {
       
   271             if (cell) {
       
   272                 cell.blur();
       
   273             }
       
   274         }
       
   275     },
       
   276 
       
   277     /**
       
   278     Subscribes to the events on the DataTable elements once they have been rendered,
       
   279     finds out the header section and makes the top-left element focusable.
       
   280 
       
   281     @method _afterKeyNavRender
       
   282     @private
       
   283      */
       
   284     _afterKeyNavRender: function () {
       
   285         var cbx = this.get('contentBox');
       
   286         this._keyNavSubscr.push(
       
   287             cbx.on('keydown', this._onKeyNavKeyDown, this),
       
   288             cbx.on('click', this._onKeyNavClick, this)
       
   289         );
       
   290         this._keyNavTHead = (this._yScrollHeader || this._tableNode).one('thead');
       
   291         this._keyMoveFirst();
       
   292 
       
   293         // determine if we have nested headers
       
   294         this._keyNavNestedHeaders = (this.get('columns').length !== this.head.theadNode.all('th').size());
       
   295     },
       
   296 
       
   297     /**
       
   298     In response to a click event, it sets the focus on the clicked cell
       
   299 
       
   300     @method _onKeyNavClick
       
   301     @param e {EventFacade}
       
   302     @private
       
   303      */
       
   304     _onKeyNavClick: function (e) {
       
   305         var cell = e.target.ancestor((this.get('keyIntoHeaders') ? 'td, th': 'td'), true);
       
   306         if (cell) {
       
   307             this.focus();
       
   308             this.set('focusedCell', cell);
       
   309         }
       
   310     },
       
   311 
       
   312     /**
       
   313     Responds to a key down event by executing the action set in the
       
   314     [keyActions](#property_keyActions) table.
       
   315 
       
   316     @method _onKeyNavKeyDown
       
   317     @param e {EventFacade}
       
   318     @private
       
   319     */
       
   320     _onKeyNavKeyDown: function (e) {
       
   321         var keyCode = e.keyCode,
       
   322             keyName = DtKeyNav.KEY_NAMES[keyCode] || keyCode,
       
   323             action;
       
   324 
       
   325         arrEach(['alt', 'ctrl', 'meta', 'shift'], function (modifier) {
       
   326             if (e[modifier + 'Key']) {
       
   327                 keyCode = modifier + '-' + keyCode;
       
   328                 keyName = modifier + '-' + keyName;
       
   329             }
       
   330         });
       
   331         action = this.keyActions[keyCode] || this.keyActions[keyName];
       
   332 
       
   333         if (typeof action === 'string') {
       
   334             if (this[action]) {
       
   335                 this[action].call(this, e);
       
   336             } else {
       
   337                 this._keyNavFireEvent(action, e);
       
   338             }
       
   339         } else {
       
   340             action.call(this, e);
       
   341         }
       
   342     },
       
   343 
       
   344     /**
       
   345     If the action associated to a key combination is a string and no method
       
   346     by that name was found in this instance, this method will
       
   347     fire an event using that string and provides extra information
       
   348     to the listener.
       
   349 
       
   350     @method _keyNavFireEvent
       
   351     @param action {String} Name of the event to fire
       
   352     @param e {EventFacade} Original facade from the keydown event.
       
   353     @private
       
   354      */
       
   355     _keyNavFireEvent: function (action, e) {
       
   356         var cell = e.target.ancestor('td, th', true);
       
   357         if (cell) {
       
   358             this.fire(action, {
       
   359                 cell: cell,
       
   360                 row: cell.ancestor('tr'),
       
   361                 record: this.getRecord(cell),
       
   362                 column: this.getColumn(cell.get('cellIndex'))
       
   363             }, e);
       
   364         }
       
   365     },
       
   366 
       
   367     /**
       
   368     Sets the focus on the very first cell in the header of the table.
       
   369 
       
   370     @method _keyMoveFirst
       
   371     @private
       
   372      */
       
   373     _keyMoveFirst: function () {
       
   374         this.set('focusedCell' , (this.get('keyIntoHeaders') ? this._keyNavTHead.one('th') : this._tbodyNode.one('td')), {src:'keyNav'});
       
   375     },
       
   376 
       
   377     /**
       
   378     Sets the focus on the cell to the left of the currently focused one.
       
   379     Does not wrap, following the WAI-ARIA recommendation.
       
   380 
       
   381     @method _keyMoveLeft
       
   382     @param e {EventFacade} Event Facade for the keydown event
       
   383     @private
       
   384     */
       
   385     _keyMoveLeft: function (e) {
       
   386         var cell = this.get('focusedCell'),
       
   387             index = cell.get('cellIndex'),
       
   388             row = cell.ancestor();
       
   389 
       
   390         e.preventDefault();
       
   391 
       
   392         if (index === 0) {
       
   393             return;
       
   394         }
       
   395         cell = row.get('cells').item(index - 1);
       
   396         this.set('focusedCell', cell , {src:'keyNav'});
       
   397     },
       
   398 
       
   399     /**
       
   400     Sets the focus on the cell to the right of the currently focused one.
       
   401     Does not wrap, following the WAI-ARIA recommendation.
       
   402 
       
   403     @method _keyMoveRight
       
   404     @param e {EventFacade} Event Facade for the keydown event
       
   405     @private
       
   406     */
       
   407     _keyMoveRight: function (e) {
       
   408         var cell = this.get('focusedCell'),
       
   409             row = cell.ancestor('tr'),
       
   410             section = row.ancestor(),
       
   411             inHead = section === this._keyNavTHead,
       
   412             nextCell,
       
   413             parent;
       
   414 
       
   415         e.preventDefault();
       
   416 
       
   417         // a little special with nested headers
       
   418         /*
       
   419             +-------------+-------+
       
   420             | ABC         | DE    |
       
   421             +-------+-----+---+---+
       
   422             | AB    |     |   |   |
       
   423             +---+---+     |   |   |
       
   424             | A | B |  C  | D | E |
       
   425             +---+---+-----+---+---+
       
   426         */
       
   427 
       
   428         nextCell = cell.next();
       
   429 
       
   430         if (row.get('rowIndex') !== 0 && inHead && this._keyNavNestedHeaders) {
       
   431             if (nextCell) {
       
   432                 cell = nextCell;
       
   433             } else { //-- B -> C
       
   434                 parent = this._getTHParent(cell);
       
   435 
       
   436                 if (parent && parent.next()) {
       
   437                     cell = parent.next();
       
   438                 } else { //-- E -> ...
       
   439                     return;
       
   440                 }
       
   441             }
       
   442 
       
   443         } else {
       
   444             if (!nextCell) {
       
   445                 return;
       
   446             } else {
       
   447                 cell = nextCell;
       
   448             }
       
   449         }
       
   450 
       
   451         this.set('focusedCell', cell, { src:'keyNav' });
       
   452 
       
   453     },
       
   454 
       
   455     /**
       
   456     Sets the focus on the cell above the currently focused one.
       
   457     It will move into the headers when the top of the data rows is reached.
       
   458     Does not wrap, following the WAI-ARIA recommendation.
       
   459 
       
   460     @method _keyMoveUp
       
   461     @param e {EventFacade} Event Facade for the keydown event
       
   462     @private
       
   463     */
       
   464     _keyMoveUp: function (e) {
       
   465         var cell = this.get('focusedCell'),
       
   466             cellIndex = cell.get('cellIndex'),
       
   467             row = cell.ancestor('tr'),
       
   468             rowIndex = row.get('rowIndex'),
       
   469             section = row.ancestor(),
       
   470             sectionRows = section.get('rows'),
       
   471             inHead = section === this._keyNavTHead,
       
   472             parent;
       
   473 
       
   474         e.preventDefault();
       
   475 
       
   476         if (!inHead) {
       
   477             rowIndex -= section.get('firstChild').get('rowIndex');
       
   478         }
       
   479 
       
   480         if (rowIndex === 0) {
       
   481             if (inHead || !this.get('keyIntoHeaders')) {
       
   482                 return;
       
   483             }
       
   484 
       
   485             section = this._keyNavTHead;
       
   486             sectionRows = section.get('rows');
       
   487 
       
   488             if (this._keyNavNestedHeaders) {
       
   489                 key = this._getCellColumnName(cell);
       
   490                 cell = section.one('.' + this._keyNavColPrefix + key);
       
   491                 cellIndex = cell.get('cellIndex');
       
   492                 row = cell.ancestor('tr');
       
   493             } else {
       
   494                 row = section.get('firstChild');
       
   495                 cell = row.get('cells').item(cellIndex);
       
   496             }
       
   497         } else {
       
   498             if (inHead && this._keyNavNestedHeaders) {
       
   499                 key = this._getCellColumnName(cell);
       
   500                 parent = this._columnMap[key]._parent;
       
   501                 if (parent) {
       
   502                     cell = section.one('#' + parent.id);
       
   503                 }
       
   504             } else {
       
   505                 row = sectionRows.item(rowIndex -1);
       
   506                 cell = row.get('cells').item(cellIndex);
       
   507             }
       
   508         }
       
   509         this.set('focusedCell', cell);
       
   510     },
       
   511 
       
   512     /**
       
   513     Sets the focus on the cell below the currently focused one.
       
   514     It will move into the data rows when the bottom of the header rows is reached.
       
   515     Does not wrap, following the WAI-ARIA recommendation.
       
   516 
       
   517     @method _keyMoveDown
       
   518     @param e {EventFacade} Event Facade for the keydown event
       
   519     @private
       
   520     */
       
   521     _keyMoveDown: function (e) {
       
   522         var cell = this.get('focusedCell'),
       
   523             cellIndex = cell.get('cellIndex'),
       
   524             row = cell.ancestor('tr'),
       
   525             rowIndex = row.get('rowIndex') + 1,
       
   526             section = row.ancestor(),
       
   527             inHead = section === this._keyNavTHead,
       
   528             tbody = (this.body && this.body.tbodyNode),
       
   529             sectionRows = section.get('rows'),
       
   530             key,
       
   531             children;
       
   532 
       
   533         e.preventDefault();
       
   534 
       
   535         if (inHead) { // focused cell is in the header
       
   536             if (this._keyNavNestedHeaders) { // the header is nested
       
   537                 key = this._getCellColumnName(cell);
       
   538                 children = this._columnMap[key].children;
       
   539 
       
   540                 rowIndex += (cell.getAttribute('rowspan') || 1) - 1;
       
   541 
       
   542                 if (children) {
       
   543                     // stay in thead
       
   544                     cell = section.one('#' + children[0].id);
       
   545                 } else {
       
   546                     // moving into tbody
       
   547                     cell = tbody.one('.' + this._keyNavColPrefix + key);
       
   548                     section = tbody;
       
   549                     sectionRows = section.get('rows');
       
   550                 }
       
   551                 cellIndex = cell.get('cellIndex');
       
   552 
       
   553             } else { // the header is not nested
       
   554                 row = tbody.one('tr');
       
   555                 cell = row.get('cells').item(cellIndex);
       
   556             }
       
   557         }
       
   558 
       
   559         // offset row index to tbody
       
   560         rowIndex -= sectionRows.item(0).get('rowIndex');
       
   561 
       
   562 
       
   563         if (rowIndex >= sectionRows.size()) {
       
   564             if (!inHead) { // last row in tbody
       
   565                 return;
       
   566             }
       
   567             section = tbody;
       
   568             row = section.one('tr');
       
   569 
       
   570         } else {
       
   571             row = sectionRows.item(rowIndex);
       
   572         }
       
   573 
       
   574         this.set('focusedCell', row.get('cells').item(cellIndex));
       
   575     },
       
   576 
       
   577     /**
       
   578     Sets the focus on the left-most cell of the row containing the currently focused cell.
       
   579 
       
   580     @method _keyMoveRowStart
       
   581     @param e {EventFacade} Event Facade for the keydown event
       
   582     @private
       
   583      */
       
   584     _keyMoveRowStart: function (e) {
       
   585         var row = this.get('focusedCell').ancestor();
       
   586         this.set('focusedCell', row.get('firstChild'), {src:'keyNav'});
       
   587         e.preventDefault();
       
   588     },
       
   589 
       
   590     /**
       
   591     Sets the focus on the right-most cell of the row containing the currently focused cell.
       
   592 
       
   593     @method _keyMoveRowEnd
       
   594     @param e {EventFacade} Event Facade for the keydown event
       
   595     @private
       
   596      */
       
   597     _keyMoveRowEnd: function (e) {
       
   598         var row = this.get('focusedCell').ancestor();
       
   599         this.set('focusedCell', row.get('lastChild'), {src:'keyNav'});
       
   600         e.preventDefault();
       
   601     },
       
   602 
       
   603     /**
       
   604     Sets the focus on the top-most cell of the column containing the currently focused cell.
       
   605     It would normally be a header cell.
       
   606 
       
   607     @method _keyMoveColTop
       
   608     @param e {EventFacade} Event Facade for the keydown event
       
   609     @private
       
   610      */
       
   611     _keyMoveColTop: function (e) {
       
   612         var cell = this.get('focusedCell'),
       
   613             cellIndex = cell.get('cellIndex'),
       
   614             key, header;
       
   615 
       
   616         e.preventDefault();
       
   617 
       
   618         if (this._keyNavNestedHeaders && this.get('keyIntoHeaders')) {
       
   619             key = this._getCellColumnName(cell);
       
   620             header = this._columnMap[key];
       
   621             while (header._parent) {
       
   622                 header = header._parent;
       
   623             }
       
   624             cell = this._keyNavTHead.one('#' + header.id);
       
   625 
       
   626         } else {
       
   627             cell = (this.get('keyIntoHeaders') ? this._keyNavTHead: this._tbodyNode).get('firstChild').get('cells').item(cellIndex);
       
   628         }
       
   629         this.set('focusedCell', cell , {src:'keyNav'});
       
   630     },
       
   631 
       
   632     /**
       
   633     Sets the focus on the last cell of the column containing the currently focused cell.
       
   634 
       
   635     @method _keyMoveColBottom
       
   636     @param e {EventFacade} Event Facade for the keydown event
       
   637     @private
       
   638      */
       
   639     _keyMoveColBottom: function (e) {
       
   640         var cell = this.get('focusedCell'),
       
   641             cellIndex = cell.get('cellIndex');
       
   642 
       
   643         this.set('focusedCell', this._tbodyNode.get('lastChild').get('cells').item(cellIndex), {src:'keyNav'});
       
   644         e.preventDefault();
       
   645 
       
   646     },
       
   647 
       
   648     /**
       
   649     Setter method for the [focusedCell](#attr_focusedCell) attribute.
       
   650     Checks that the passed value is a Node, either a TD or TH and is
       
   651     contained within the DataTable contentBox.
       
   652 
       
   653     @method _focusedCellSetter
       
   654     @param cell {Node} DataTable cell to receive the focus
       
   655     @return cell or Y.Attribute.INVALID_VALUE
       
   656     @private
       
   657      */
       
   658     _focusedCellSetter: function (cell) {
       
   659         if (cell instanceof Y.Node) {
       
   660             var tag = cell.get('tagName').toUpperCase();
       
   661             if ((tag === 'TD' || tag === 'TH') && this.get('contentBox').contains(cell) ) {
       
   662                 return cell;
       
   663             }
       
   664         } else if (cell === null) {
       
   665             return cell;
       
   666         }
       
   667         return Y.Attribute.INVALID_VALUE;
       
   668     },
       
   669 
       
   670     /**
       
   671      Retrieves the parent cell of the given TH cell. If there is no parent for
       
   672      the provided cell, null is returned.
       
   673      @protected
       
   674      @method _getTHParent
       
   675      @param {Node} thCell Cell to find parent of
       
   676      @return {Node} Parent of the cell provided or null
       
   677      */
       
   678     _getTHParent: function (thCell) {
       
   679         var key = this._getCellColumnName(thCell),
       
   680             parent = this._columnMap[key] && this._columnMap[key]._parent;
       
   681 
       
   682         if (parent) {
       
   683             return thCell.ancestor().ancestor().one('.' + this._keyNavColPrefix + parent.key);
       
   684         }
       
   685 
       
   686         return null;
       
   687     },
       
   688 
       
   689     /**
       
   690      Retrieves the column name based from the data attribute on the cell if
       
   691      available. Other wise, extracts the column name from the classname
       
   692      @protected
       
   693      @method _getCellColumnName
       
   694      @param {Node} cell Cell to get column name from
       
   695      @return String Column name of the provided cell
       
   696      */
       
   697     _getCellColumnName: function (cell) {
       
   698         return cell.getData('yui3-col-id') || this._keyNavColRegExp.exec(cell.get('className'))[1];
       
   699     }
       
   700 });
       
   701 
       
   702 Y.DataTable.KeyNav = DtKeyNav;
       
   703 Y.Base.mix(Y.DataTable, [DtKeyNav]);
       
   704 
       
   705 
       
   706 }, '@VERSION@', {"requires": ["datatable-base"]});