src/cm/media/js/lib/yui/yui_3.10.3/build/datatable-scroll/datatable-scroll-debug.js
changeset 525 89ef5ed3c48b
equal deleted inserted replaced
524:322d0feea350 525:89ef5ed3c48b
       
     1 /*
       
     2 YUI 3.10.3 (build 2fb5187)
       
     3 Copyright 2013 Yahoo! Inc. All rights reserved.
       
     4 Licensed under the BSD License.
       
     5 http://yuilibrary.com/license/
       
     6 */
       
     7 
       
     8 YUI.add('datatable-scroll', function (Y, NAME) {
       
     9 
       
    10 /**
       
    11 Adds the ability to make the table rows scrollable while preserving the header
       
    12 placement.
       
    13 
       
    14 @module datatable-scroll
       
    15 @for DataTable
       
    16 @since 3.5.0
       
    17 **/
       
    18 var YLang = Y.Lang,
       
    19     isString = YLang.isString,
       
    20     isNumber = YLang.isNumber,
       
    21     isArray  = YLang.isArray,
       
    22 
       
    23     Scrollable;
       
    24 
       
    25 // Returns the numeric value portion of the computed style, defaulting to 0
       
    26 function styleDim(node, style) {
       
    27     return parseInt(node.getComputedStyle(style), 10) || 0;
       
    28 }
       
    29 
       
    30 /**
       
    31 _API docs for this extension are included in the DataTable class._
       
    32 
       
    33 Adds the ability to make the table rows scrollable while preserving the header
       
    34 placement.
       
    35 
       
    36 There are two types of scrolling, horizontal (x) and vertical (y).  Horizontal
       
    37 scrolling is achieved by wrapping the entire table in a scrollable container.
       
    38 Vertical scrolling is achieved by splitting the table headers and data into two
       
    39 separate tables, the latter of which is wrapped in a vertically scrolling
       
    40 container.  In this case, column widths of header cells and data cells are kept
       
    41 in sync programmatically.
       
    42 
       
    43 Since the split table synchronization can be costly at runtime, the split is only
       
    44 done if the data in the table stretches beyond the configured `height` value.
       
    45 
       
    46 To activate or deactivate scrolling, set the `scrollable` attribute to one of
       
    47 the following values:
       
    48 
       
    49  * `false` - (default) Scrolling is disabled.
       
    50  * `true` or 'xy' - If `height` is set, vertical scrolling will be activated, if
       
    51             `width` is set, horizontal scrolling will be activated.
       
    52  * 'x' - Activate horizontal scrolling only. Requires the `width` attribute is
       
    53          also set.
       
    54  * 'y' - Activate vertical scrolling only. Requires the `height` attribute is
       
    55          also set.
       
    56 
       
    57 @class DataTable.Scrollable
       
    58 @for DataTable
       
    59 @since 3.5.0
       
    60 **/
       
    61 Y.DataTable.Scrollable = Scrollable = function () {};
       
    62 
       
    63 Scrollable.ATTRS = {
       
    64     /**
       
    65     Activates or deactivates scrolling in the table.  Acceptable values are:
       
    66 
       
    67      * `false` - (default) Scrolling is disabled.
       
    68      * `true` or 'xy' - If `height` is set, vertical scrolling will be
       
    69        activated, if `width` is set, horizontal scrolling will be activated.
       
    70      * 'x' - Activate horizontal scrolling only. Requires the `width` attribute
       
    71        is also set.
       
    72      * 'y' - Activate vertical scrolling only. Requires the `height` attribute
       
    73        is also set.
       
    74 
       
    75     @attribute scrollable
       
    76     @type {String|Boolean}
       
    77     @value false
       
    78     @since 3.5.0
       
    79     **/
       
    80     scrollable: {
       
    81         value: false,
       
    82         setter: '_setScrollable'
       
    83     }
       
    84 };
       
    85 
       
    86 Y.mix(Scrollable.prototype, {
       
    87 
       
    88     /**
       
    89     Scrolls a given row or cell into view if the table is scrolling.  Pass the
       
    90     `clientId` of a Model from the DataTable's `data` ModelList or its row
       
    91     index to scroll to a row or a [row index, column index] array to scroll to
       
    92     a cell.  Alternately, to scroll to any element contained within the table's
       
    93     scrolling areas, pass its ID, or the Node itself (though you could just as
       
    94     well call `node.scrollIntoView()` yourself, but hey, whatever).
       
    95 
       
    96     @method scrollTo
       
    97     @param {String|Number|Number[]|Node} id A row clientId, row index, cell
       
    98             coordinate array, id string, or Node
       
    99     @return {DataTable}
       
   100     @chainable
       
   101     @since 3.5.0
       
   102     **/
       
   103     scrollTo: function (id) {
       
   104         var target;
       
   105 
       
   106         if (id && this._tbodyNode && (this._yScrollNode || this._xScrollNode)) {
       
   107             if (isArray(id)) {
       
   108                 target = this.getCell(id);
       
   109             } else if (isNumber(id)) {
       
   110                 target = this.getRow(id);
       
   111             } else if (isString(id)) {
       
   112                 target = this._tbodyNode.one('#' + id);
       
   113             } else if (id instanceof Y.Node &&
       
   114                     // TODO: ancestor(yScrollNode, xScrollNode)
       
   115                     id.ancestor('.yui3-datatable') === this.get('boundingBox')) {
       
   116                 target = id;
       
   117             }
       
   118 
       
   119             if(target) {
       
   120                 target.scrollIntoView();
       
   121             }
       
   122         }
       
   123 
       
   124         return this;
       
   125     },
       
   126 
       
   127     //--------------------------------------------------------------------------
       
   128     // Protected properties and methods
       
   129     //--------------------------------------------------------------------------
       
   130 
       
   131     /**
       
   132     Template for the `<table>` that is used to fix the caption in place when
       
   133     the table is horizontally scrolling.
       
   134 
       
   135     @property _CAPTION_TABLE_TEMPLATE
       
   136     @type {HTML}
       
   137     @value '<table class="{className}" role="presentation"></table>'
       
   138     @protected
       
   139     @since 3.5.0
       
   140     **/
       
   141     _CAPTION_TABLE_TEMPLATE: '<table class="{className}" role="presentation"></table>',
       
   142 
       
   143     /**
       
   144     Template used to create sizable element liners around header content to
       
   145     synchronize fixed header column widths.
       
   146 
       
   147     @property _SCROLL_LINER_TEMPLATE
       
   148     @type {HTML}
       
   149     @value '<div class="{className}"></div>'
       
   150     @protected
       
   151     @since 3.5.0
       
   152     **/
       
   153     _SCROLL_LINER_TEMPLATE: '<div class="{className}"></div>',
       
   154 
       
   155     /**
       
   156     Template for the virtual scrollbar needed in "y" and "xy" scrolling setups.
       
   157 
       
   158     @property _SCROLLBAR_TEMPLATE
       
   159     @type {HTML}
       
   160     @value '<div class="{className}"><div></div></div>'
       
   161     @protected
       
   162     @since 3.5.0
       
   163     **/
       
   164     _SCROLLBAR_TEMPLATE: '<div class="{className}"><div></div></div>',
       
   165 
       
   166     /**
       
   167     Template for the `<div>` that is used to contain the table when the table is
       
   168     horizontally scrolling.
       
   169 
       
   170     @property _X_SCROLLER_TEMPLATE
       
   171     @type {HTML}
       
   172     @value '<div class="{className}"></div>'
       
   173     @protected
       
   174     @since 3.5.0
       
   175     **/
       
   176     _X_SCROLLER_TEMPLATE: '<div class="{className}"></div>',
       
   177 
       
   178     /**
       
   179     Template for the `<table>` used to contain the fixed column headers for
       
   180     vertically scrolling tables.
       
   181 
       
   182     @property _Y_SCROLL_HEADER_TEMPLATE
       
   183     @type {HTML}
       
   184     @value '<table cellspacing="0" role="presentation" aria-hidden="true" class="{className}"></table>'
       
   185     @protected
       
   186     @since 3.5.0
       
   187     **/
       
   188     _Y_SCROLL_HEADER_TEMPLATE: '<table cellspacing="0" aria-hidden="true" class="{className}"></table>',
       
   189 
       
   190     /**
       
   191     Template for the `<div>` that is used to contain the rows when the table is
       
   192     vertically scrolling.
       
   193 
       
   194     @property _Y_SCROLLER_TEMPLATE
       
   195     @type {HTML}
       
   196     @value '<div class="{className}"><div class="{scrollerClassName}"></div></div>'
       
   197     @protected
       
   198     @since 3.5.0
       
   199     **/
       
   200     _Y_SCROLLER_TEMPLATE: '<div class="{className}"><div class="{scrollerClassName}"></div></div>',
       
   201 
       
   202     /**
       
   203     Adds padding to the last cells in the fixed header for vertically scrolling
       
   204     tables.  This padding is equal in width to the scrollbar, so can't be
       
   205     relegated to a stylesheet.
       
   206 
       
   207     @method _addScrollbarPadding
       
   208     @protected
       
   209     @since 3.5.0
       
   210     **/
       
   211     _addScrollbarPadding: function () {
       
   212         var fixedHeader = this._yScrollHeader,
       
   213             headerClass = '.' + this.getClassName('header'),
       
   214             scrollbarWidth, rows, header, i, len;
       
   215 
       
   216         if (fixedHeader) {
       
   217             scrollbarWidth = Y.DOM.getScrollbarWidth() + 'px';
       
   218             rows = fixedHeader.all('tr');
       
   219 
       
   220             for (i = 0, len = rows.size(); i < len; i += +header.get('rowSpan')) {
       
   221                 header = rows.item(i).all(headerClass).pop();
       
   222                 header.setStyle('paddingRight', scrollbarWidth);
       
   223             }
       
   224         }
       
   225     },
       
   226 
       
   227     /**
       
   228     Reacts to changes in the `scrollable` attribute by updating the `_xScroll`
       
   229     and `_yScroll` properties and syncing the scrolling structure accordingly.
       
   230 
       
   231     @method _afterScrollableChange
       
   232     @param {EventFacade} e The relevant change event (ignored)
       
   233     @protected
       
   234     @since 3.5.0
       
   235     **/
       
   236     _afterScrollableChange: function () {
       
   237         var scroller = this._xScrollNode;
       
   238 
       
   239         if (this._xScroll && scroller) {
       
   240             if (this._yScroll && !this._yScrollNode) {
       
   241                 scroller.setStyle('paddingRight',
       
   242                     Y.DOM.getScrollbarWidth() + 'px');
       
   243             } else if (!this._yScroll && this._yScrollNode) {
       
   244                 scroller.setStyle('paddingRight', '');
       
   245             }
       
   246         }
       
   247 
       
   248         this._syncScrollUI();
       
   249     },
       
   250 
       
   251     /**
       
   252     Reacts to changes in the `caption` attribute by adding, removing, or
       
   253     syncing the caption table when the table is set to scroll.
       
   254 
       
   255     @method _afterScrollCaptionChange
       
   256     @param {EventFacade} e The relevant change event (ignored)
       
   257     @protected
       
   258     @since 3.5.0
       
   259     **/
       
   260     _afterScrollCaptionChange: function () {
       
   261         if (this._xScroll || this._yScroll) {
       
   262             this._syncScrollUI();
       
   263         }
       
   264     },
       
   265 
       
   266     /**
       
   267     Reacts to changes in the `columns` attribute of vertically scrolling tables
       
   268     by refreshing the fixed headers, scroll container, and virtual scrollbar
       
   269     position.
       
   270 
       
   271     @method _afterScrollColumnsChange
       
   272     @param {EventFacade} e The relevant change event (ignored)
       
   273     @protected
       
   274     @since 3.5.0
       
   275     **/
       
   276     _afterScrollColumnsChange: function () {
       
   277         if (this._xScroll || this._yScroll) {
       
   278             if (this._yScroll && this._yScrollHeader) {
       
   279                 this._syncScrollHeaders();
       
   280             }
       
   281 
       
   282             this._syncScrollUI();
       
   283         }
       
   284     },
       
   285 
       
   286     /**
       
   287     Reacts to changes in vertically scrolling table's `data` ModelList by
       
   288     synchronizing the fixed column header widths and virtual scrollbar height.
       
   289 
       
   290     @method _afterScrollDataChange
       
   291     @param {EventFacade} e The relevant change event (ignored)
       
   292     @protected
       
   293     @since 3.5.0
       
   294     **/
       
   295     _afterScrollDataChange: function () {
       
   296         if (this._xScroll || this._yScroll) {
       
   297             this._syncScrollUI();
       
   298         }
       
   299     },
       
   300 
       
   301     /**
       
   302     Reacts to changes in the `height` attribute of vertically scrolling tables
       
   303     by updating the height of the `<div>` wrapping the data table and the
       
   304     virtual scrollbar.  If `scrollable` was set to "y" or "xy" but lacking a
       
   305     declared `height` until the received change, `_syncScrollUI` is called to
       
   306     create the fixed headers etc.
       
   307 
       
   308     @method _afterScrollHeightChange
       
   309     @param {EventFacade} e The relevant change event (ignored)
       
   310     @protected
       
   311     @since 3.5.0
       
   312     **/
       
   313     _afterScrollHeightChange: function () {
       
   314         if (this._yScroll) {
       
   315             this._syncScrollUI();
       
   316         }
       
   317     },
       
   318 
       
   319     /* (not an API doc comment on purpose)
       
   320     Reacts to the sort event (if the table is also sortable) by updating the
       
   321     fixed header classes to match the data table's headers.
       
   322 
       
   323     THIS IS A HACK that will be removed immediately after the 3.5.0 release.
       
   324     If you're reading this and the current version is greater than 3.5.0, I
       
   325     should be publicly scolded.
       
   326     */
       
   327     _afterScrollSort: function () {
       
   328         var headers, headerClass;
       
   329 
       
   330         if (this._yScroll && this._yScrollHeader) {
       
   331             headerClass = '.' + this.getClassName('header');
       
   332             headers = this._theadNode.all(headerClass);
       
   333 
       
   334             this._yScrollHeader.all(headerClass).each(function (header, i) {
       
   335                 header.set('className', headers.item(i).get('className'));
       
   336             });
       
   337         }
       
   338     },
       
   339 
       
   340     /**
       
   341     Reacts to changes in the width of scrolling tables by expanding the width of
       
   342     the `<div>` wrapping the data table for horizontally scrolling tables or
       
   343     upding the position of the virtual scrollbar for vertically scrolling
       
   344     tables.
       
   345 
       
   346     @method _afterScrollWidthChange
       
   347     @param {EventFacade} e The relevant change event (ignored)
       
   348     @protected
       
   349     @since 3.5.0
       
   350     **/
       
   351     _afterScrollWidthChange: function () {
       
   352         if (this._xScroll || this._yScroll) {
       
   353             this._syncScrollUI();
       
   354         }
       
   355     },
       
   356 
       
   357     /**
       
   358     Binds virtual scrollbar interaction to the `_yScrollNode`'s `scrollTop` and
       
   359     vice versa.
       
   360 
       
   361     @method _bindScrollbar
       
   362     @protected
       
   363     @since 3.5.0
       
   364     **/
       
   365     _bindScrollbar: function () {
       
   366         var scrollbar = this._scrollbarNode,
       
   367             scroller  = this._yScrollNode;
       
   368 
       
   369         if (scrollbar && scroller && !this._scrollbarEventHandle) {
       
   370             this._scrollbarEventHandle = new Y.Event.Handle([
       
   371                 scrollbar.on('scroll', this._syncScrollPosition, this),
       
   372                 scroller.on('scroll', this._syncScrollPosition, this)
       
   373             ]);
       
   374         }
       
   375     },
       
   376 
       
   377     /**
       
   378     Binds to the window resize event to update the vertical scrolling table
       
   379     headers and wrapper `<div>` dimensions.
       
   380 
       
   381     @method _bindScrollResize
       
   382     @protected
       
   383     @since 3.5.0
       
   384     **/
       
   385     _bindScrollResize: function () {
       
   386         if (!this._scrollResizeHandle) {
       
   387             // TODO: sync header widths and scrollbar position.  If the height
       
   388             // of the headers has changed, update the scrollbar dims as well.
       
   389             this._scrollResizeHandle = Y.on('resize',
       
   390                 this._syncScrollUI, null, this);
       
   391         }
       
   392     },
       
   393 
       
   394     /**
       
   395     Attaches internal subscriptions to keep the scrolling structure up to date
       
   396     with changes in the table's `data`, `columns`, `caption`, or `height`.  The
       
   397     `width` is taken care of already.
       
   398 
       
   399     This executes after the table's native `bindUI` method.
       
   400 
       
   401     @method _bindScrollUI
       
   402     @protected
       
   403     @since 3.5.0
       
   404     **/
       
   405     _bindScrollUI: function () {
       
   406         this.after({
       
   407             columnsChange: Y.bind('_afterScrollColumnsChange', this),
       
   408             heightChange : Y.bind('_afterScrollHeightChange', this),
       
   409             widthChange  : Y.bind('_afterScrollWidthChange', this),
       
   410             captionChange: Y.bind('_afterScrollCaptionChange', this),
       
   411             scrollableChange: Y.bind('_afterScrollableChange', this),
       
   412             // FIXME: this is a last minute hack to work around the fact that
       
   413             // DT doesn't use a tableView to render table content that can be
       
   414             // replaced with a scrolling table view.  This must be removed asap!
       
   415             sort         : Y.bind('_afterScrollSort', this)
       
   416         });
       
   417 
       
   418         this.after(['dataChange', '*:add', '*:remove', '*:reset', '*:change'],
       
   419             Y.bind('_afterScrollDataChange', this));
       
   420     },
       
   421 
       
   422     /**
       
   423     Clears the lock and timer used to manage synchronizing the scroll position
       
   424     between the vertical scroll container and the virtual scrollbar.
       
   425 
       
   426     @method _clearScrollLock
       
   427     @protected
       
   428     @since 3.5.0
       
   429     **/
       
   430     _clearScrollLock: function () {
       
   431         if (this._scrollLock) {
       
   432             this._scrollLock.cancel();
       
   433             delete this._scrollLock;
       
   434         }
       
   435     },
       
   436 
       
   437     /**
       
   438     Creates a virtual scrollbar from the `_SCROLLBAR_TEMPLATE`, assigning it to
       
   439     the `_scrollbarNode` property.
       
   440 
       
   441     @method _createScrollbar
       
   442     @return {Node} The created Node
       
   443     @protected
       
   444     @since 3.5.0
       
   445     **/
       
   446     _createScrollbar: function () {
       
   447         var scrollbar = this._scrollbarNode;
       
   448 
       
   449         if (!scrollbar) {
       
   450             scrollbar = this._scrollbarNode = Y.Node.create(
       
   451                 Y.Lang.sub(this._SCROLLBAR_TEMPLATE, {
       
   452                     className: this.getClassName('scrollbar')
       
   453                 }));
       
   454 
       
   455             // IE 6-10 require the scrolled area to be visible (at least 1px)
       
   456             // or they don't respond to clicking on the scrollbar rail or arrows
       
   457             scrollbar.setStyle('width', (Y.DOM.getScrollbarWidth() + 1) + 'px');
       
   458         }
       
   459 
       
   460         return scrollbar;
       
   461     },
       
   462 
       
   463     /**
       
   464     Creates a separate table to contain the caption when the table is
       
   465     configured to scroll vertically or horizontally.
       
   466 
       
   467     @method _createScrollCaptionTable
       
   468     @return {Node} The created Node
       
   469     @protected
       
   470     @since 3.5.0
       
   471     **/
       
   472     _createScrollCaptionTable: function () {
       
   473         if (!this._captionTable) {
       
   474             this._captionTable = Y.Node.create(
       
   475                 Y.Lang.sub(this._CAPTION_TABLE_TEMPLATE, {
       
   476                     className: this.getClassName('caption', 'table')
       
   477                 }));
       
   478 
       
   479             this._captionTable.empty();
       
   480         }
       
   481 
       
   482         return this._captionTable;
       
   483     },
       
   484 
       
   485     /**
       
   486     Populates the `_xScrollNode` property by creating the `<div>` Node described
       
   487     by the `_X_SCROLLER_TEMPLATE`.
       
   488 
       
   489     @method _createXScrollNode
       
   490     @return {Node} The created Node
       
   491     @protected
       
   492     @since 3.5.0
       
   493     **/
       
   494     _createXScrollNode: function () {
       
   495         if (!this._xScrollNode) {
       
   496             this._xScrollNode = Y.Node.create(
       
   497                 Y.Lang.sub(this._X_SCROLLER_TEMPLATE, {
       
   498                     className: this.getClassName('x','scroller')
       
   499                 }));
       
   500         }
       
   501 
       
   502         return this._xScrollNode;
       
   503     },
       
   504 
       
   505     /**
       
   506     Populates the `_yScrollHeader` property by creating the `<table>` Node
       
   507     described by the `_Y_SCROLL_HEADER_TEMPLATE`.
       
   508 
       
   509     @method _createYScrollHeader
       
   510     @return {Node} The created Node
       
   511     @protected
       
   512     @since 3.5.0
       
   513     **/
       
   514     _createYScrollHeader: function () {
       
   515         var fixedHeader = this._yScrollHeader;
       
   516 
       
   517         if (!fixedHeader) {
       
   518             fixedHeader = this._yScrollHeader = Y.Node.create(
       
   519                 Y.Lang.sub(this._Y_SCROLL_HEADER_TEMPLATE, {
       
   520                     className: this.getClassName('scroll','columns')
       
   521                 }));
       
   522         }
       
   523 
       
   524         return fixedHeader;
       
   525     },
       
   526 
       
   527     /**
       
   528     Populates the `_yScrollNode` property by creating the `<div>` Node described
       
   529     by the `_Y_SCROLLER_TEMPLATE`.
       
   530 
       
   531     @method _createYScrollNode
       
   532     @return {Node} The created Node
       
   533     @protected
       
   534     @since 3.5.0
       
   535     **/
       
   536     _createYScrollNode: function () {
       
   537         var scrollerClass;
       
   538 
       
   539         if (!this._yScrollNode) {
       
   540             scrollerClass = this.getClassName('y', 'scroller');
       
   541 
       
   542             this._yScrollContainer = Y.Node.create(
       
   543                 Y.Lang.sub(this._Y_SCROLLER_TEMPLATE, {
       
   544                     className: this.getClassName('y','scroller','container'),
       
   545                     scrollerClassName: scrollerClass
       
   546                 }));
       
   547 
       
   548             this._yScrollNode = this._yScrollContainer
       
   549                 .one('.' + scrollerClass);
       
   550         }
       
   551 
       
   552         return this._yScrollContainer;
       
   553     },
       
   554 
       
   555     /**
       
   556     Removes the nodes used to create horizontal and vertical scrolling and
       
   557     rejoins the caption to the main table if needed.
       
   558 
       
   559     @method _disableScrolling
       
   560     @protected
       
   561     @since 3.5.0
       
   562     **/
       
   563     _disableScrolling: function () {
       
   564         this._removeScrollCaptionTable();
       
   565         this._disableXScrolling();
       
   566         this._disableYScrolling();
       
   567         this._unbindScrollResize();
       
   568 
       
   569         this._uiSetWidth(this.get('width'));
       
   570     },
       
   571 
       
   572     /**
       
   573     Removes the nodes used to allow horizontal scrolling.
       
   574 
       
   575     @method _disableXScrolling
       
   576     @protected
       
   577     @since 3.5.0
       
   578     **/
       
   579     _disableXScrolling: function () {
       
   580         this._removeXScrollNode();
       
   581     },
       
   582 
       
   583     /**
       
   584     Removes the nodes used to allow vertical scrolling.
       
   585 
       
   586     @method _disableYScrolling
       
   587     @protected
       
   588     @since 3.5.0
       
   589     **/
       
   590     _disableYScrolling: function () {
       
   591         this._removeYScrollHeader();
       
   592         this._removeYScrollNode();
       
   593         this._removeYScrollContainer();
       
   594         this._removeScrollbar();
       
   595     },
       
   596 
       
   597     /**
       
   598     Cleans up external event subscriptions.
       
   599 
       
   600     @method destructor
       
   601     @protected
       
   602     @since 3.5.0
       
   603     **/
       
   604     destructor: function () {
       
   605         this._unbindScrollbar();
       
   606         this._unbindScrollResize();
       
   607         this._clearScrollLock();
       
   608     },
       
   609 
       
   610     /**
       
   611     Sets up event handlers and AOP advice methods to bind the DataTable's natural
       
   612     behaviors with the scrolling APIs and state.
       
   613 
       
   614     @method initializer
       
   615     @param {Object} config The config object passed to the constructor (ignored)
       
   616     @protected
       
   617     @since 3.5.0
       
   618     **/
       
   619     initializer: function () {
       
   620         this._setScrollProperties();
       
   621 
       
   622         this.after(['scrollableChange', 'heightChange', 'widthChange'],
       
   623             this._setScrollProperties);
       
   624 
       
   625         this.after('renderView', Y.bind('_syncScrollUI', this));
       
   626 
       
   627         Y.Do.after(this._bindScrollUI, this, 'bindUI');
       
   628     },
       
   629 
       
   630     /**
       
   631     Removes the table used to house the caption when the table is scrolling.
       
   632 
       
   633     @method _removeScrollCaptionTable
       
   634     @protected
       
   635     @since 3.5.0
       
   636     **/
       
   637     _removeScrollCaptionTable: function () {
       
   638         if (this._captionTable) {
       
   639             if (this._captionNode) {
       
   640                 this._tableNode.prepend(this._captionNode);
       
   641             }
       
   642 
       
   643             this._captionTable.remove().destroy(true);
       
   644 
       
   645             delete this._captionTable;
       
   646         }
       
   647     },
       
   648 
       
   649     /**
       
   650     Removes the `<div>` wrapper used to contain the data table when the table
       
   651     is horizontally scrolling.
       
   652 
       
   653     @method _removeXScrollNode
       
   654     @protected
       
   655     @since 3.5.0
       
   656     **/
       
   657     _removeXScrollNode: function () {
       
   658         var scroller = this._xScrollNode;
       
   659 
       
   660         if (scroller) {
       
   661             scroller.replace(scroller.get('childNodes').toFrag());
       
   662             scroller.remove().destroy(true);
       
   663 
       
   664             delete this._xScrollNode;
       
   665         }
       
   666     },
       
   667 
       
   668     /**
       
   669     Removes the `<div>` wrapper used to contain the data table and fixed header
       
   670     when the table is vertically scrolling.
       
   671 
       
   672     @method _removeYScrollContainer
       
   673     @protected
       
   674     @since 3.5.0
       
   675     **/
       
   676     _removeYScrollContainer: function () {
       
   677         var scroller = this._yScrollContainer;
       
   678 
       
   679         if (scroller) {
       
   680             scroller.replace(scroller.get('childNodes').toFrag());
       
   681             scroller.remove().destroy(true);
       
   682 
       
   683             delete this._yScrollContainer;
       
   684         }
       
   685     },
       
   686 
       
   687     /**
       
   688     Removes the `<table>` used to contain the fixed column headers when the
       
   689     table is vertically scrolling.
       
   690 
       
   691     @method _removeYScrollHeader
       
   692     @protected
       
   693     @since 3.5.0
       
   694     **/
       
   695     _removeYScrollHeader: function () {
       
   696         if (this._yScrollHeader) {
       
   697             this._yScrollHeader.remove().destroy(true);
       
   698 
       
   699             delete this._yScrollHeader;
       
   700         }
       
   701     },
       
   702 
       
   703     /**
       
   704     Removes the `<div>` wrapper used to contain the data table when the table
       
   705     is vertically scrolling.
       
   706 
       
   707     @method _removeYScrollNode
       
   708     @protected
       
   709     @since 3.5.0
       
   710     **/
       
   711     _removeYScrollNode: function () {
       
   712         var scroller = this._yScrollNode;
       
   713 
       
   714         if (scroller) {
       
   715             scroller.replace(scroller.get('childNodes').toFrag());
       
   716             scroller.remove().destroy(true);
       
   717 
       
   718             delete this._yScrollNode;
       
   719         }
       
   720     },
       
   721 
       
   722     /**
       
   723     Removes the virtual scrollbar used by scrolling tables.
       
   724 
       
   725     @method _removeScrollbar
       
   726     @protected
       
   727     @since 3.5.0
       
   728     **/
       
   729     _removeScrollbar: function () {
       
   730         if (this._scrollbarNode) {
       
   731             this._scrollbarNode.remove().destroy(true);
       
   732 
       
   733             delete this._scrollbarNode;
       
   734         }
       
   735         if (this._scrollbarEventHandle) {
       
   736             this._scrollbarEventHandle.detach();
       
   737 
       
   738             delete this._scrollbarEventHandle;
       
   739         }
       
   740     },
       
   741 
       
   742     /**
       
   743     Accepts (case insensitive) values "x", "y", "xy", `true`, and `false`.
       
   744     `true` is translated to "xy" and upper case values are converted to lower
       
   745     case.  All other values are invalid.
       
   746 
       
   747     @method _setScrollable
       
   748     @param {String|Boolea} val Incoming value for the `scrollable` attribute
       
   749     @return {String}
       
   750     @protected
       
   751     @since 3.5.0
       
   752     **/
       
   753     _setScrollable: function (val) {
       
   754         if (val === true) {
       
   755             val = 'xy';
       
   756         }
       
   757 
       
   758         if (isString(val)) {
       
   759             val = val.toLowerCase();
       
   760         }
       
   761 
       
   762         return (val === false || val === 'y' || val === 'x' || val === 'xy') ?
       
   763             val :
       
   764             Y.Attribute.INVALID_VALUE;
       
   765     },
       
   766 
       
   767     /**
       
   768     Assigns the `_xScroll` and `_yScroll` properties to true if an
       
   769     appropriate value is set in the `scrollable` attribute and the `height`
       
   770     and/or `width` is set.
       
   771 
       
   772     @method _setScrollProperties
       
   773     @protected
       
   774     @since 3.5.0
       
   775     **/
       
   776     _setScrollProperties: function () {
       
   777         var scrollable = this.get('scrollable') || '',
       
   778             width      = this.get('width'),
       
   779             height     = this.get('height');
       
   780 
       
   781         this._xScroll = width  && scrollable.indexOf('x') > -1;
       
   782         this._yScroll = height && scrollable.indexOf('y') > -1;
       
   783     },
       
   784 
       
   785     /**
       
   786     Keeps the virtual scrollbar and the scrolling `<div>` wrapper around the
       
   787     data table in vertically scrolling tables in sync.
       
   788 
       
   789     @method _syncScrollPosition
       
   790     @param {DOMEventFacade} e The scroll event
       
   791     @protected
       
   792     @since 3.5.0
       
   793     **/
       
   794     _syncScrollPosition: function (e) {
       
   795         var scrollbar = this._scrollbarNode,
       
   796             scroller  = this._yScrollNode,
       
   797             source    = e.currentTarget,
       
   798             other;
       
   799 
       
   800         if (scrollbar && scroller) {
       
   801             if (this._scrollLock && this._scrollLock.source !== source) {
       
   802                 return;
       
   803             }
       
   804 
       
   805             this._clearScrollLock();
       
   806             this._scrollLock = Y.later(300, this, this._clearScrollLock);
       
   807             this._scrollLock.source = source;
       
   808 
       
   809             other = (source === scrollbar) ? scroller : scrollbar;
       
   810             other.set('scrollTop', source.get('scrollTop'));
       
   811         }
       
   812     },
       
   813 
       
   814     /**
       
   815     Splits the caption from the data `<table>` if the table is configured to
       
   816     scroll.  If not, rejoins the caption to the data `<table>` if it needs to
       
   817     be.
       
   818 
       
   819     @method _syncScrollCaptionUI
       
   820     @protected
       
   821     @since 3.5.0
       
   822     **/
       
   823     _syncScrollCaptionUI: function () {
       
   824         var caption      = this._captionNode,
       
   825             table        = this._tableNode,
       
   826             captionTable = this._captionTable,
       
   827             id;
       
   828 
       
   829         if (caption) {
       
   830             id = caption.getAttribute('id');
       
   831 
       
   832             if (!captionTable) {
       
   833                 captionTable = this._createScrollCaptionTable();
       
   834 
       
   835                 this.get('contentBox').prepend(captionTable);
       
   836             }
       
   837 
       
   838             if (!caption.get('parentNode').compareTo(captionTable)) {
       
   839                 captionTable.empty().insert(caption);
       
   840 
       
   841                 if (!id) {
       
   842                     id = Y.stamp(caption);
       
   843                     caption.setAttribute('id', id);
       
   844                 }
       
   845 
       
   846                 table.setAttribute('aria-describedby', id);
       
   847             }
       
   848         } else if (captionTable) {
       
   849             this._removeScrollCaptionTable();
       
   850         }
       
   851     },
       
   852 
       
   853     /**
       
   854     Assigns widths to the fixed header columns to match the columns in the data
       
   855     table.
       
   856 
       
   857     @method _syncScrollColumnWidths
       
   858     @protected
       
   859     @since 3.5.0
       
   860     **/
       
   861     _syncScrollColumnWidths: function () {
       
   862         var widths = [];
       
   863 
       
   864         if (this._theadNode && this._yScrollHeader) {
       
   865             // Capture dims and assign widths in two passes to avoid reflows for
       
   866             // each access of clientWidth/getComputedStyle
       
   867             this._theadNode.all('.' + this.getClassName('header'))
       
   868                 .each(function (header) {
       
   869                     widths.push(
       
   870                         // FIXME: IE returns the col.style.width from
       
   871                         // getComputedStyle even if the column has been
       
   872                         // compressed below that width, so it must use
       
   873                         // clientWidth. FF requires getComputedStyle because it
       
   874                         // uses fractional widths that round up to an overall
       
   875                         // cell/table width 1px greater than the data table's
       
   876                         // cell/table width, resulting in misaligned columns or
       
   877                         // fixed header bleed through. I can't think of a
       
   878                         // *reasonable* way to capture the correct width without
       
   879                         // a sniff.  Math.min(cW - p, getCS(w)) was imperfect
       
   880                         // and punished all browsers, anyway.
       
   881                         (Y.UA.ie && Y.UA.ie < 8) ?
       
   882                             (header.get('clientWidth') -
       
   883                              styleDim(header, 'paddingLeft') -
       
   884                              styleDim(header, 'paddingRight')) + 'px' :
       
   885                             header.getComputedStyle('width'));
       
   886             });
       
   887 
       
   888             this._yScrollHeader.all('.' + this.getClassName('scroll', 'liner'))
       
   889                 .each(function (liner, i) {
       
   890                     liner.setStyle('width', widths[i]);
       
   891                 });
       
   892         }
       
   893     },
       
   894 
       
   895     /**
       
   896     Creates matching headers in the fixed header table for vertically scrolling
       
   897     tables and synchronizes the column widths.
       
   898 
       
   899     @method _syncScrollHeaders
       
   900     @protected
       
   901     @since 3.5.0
       
   902     **/
       
   903     _syncScrollHeaders: function () {
       
   904         var fixedHeader   = this._yScrollHeader,
       
   905             linerTemplate = this._SCROLL_LINER_TEMPLATE,
       
   906             linerClass    = this.getClassName('scroll', 'liner'),
       
   907             headerClass   = this.getClassName('header'),
       
   908             headers       = this._theadNode.all('.' + headerClass);
       
   909 
       
   910         if (this._theadNode && fixedHeader) {
       
   911             fixedHeader.empty().appendChild(
       
   912                 this._theadNode.cloneNode(true));
       
   913 
       
   914             // Prevent duplicate IDs and assign ARIA attributes to hide
       
   915             // from screen readers
       
   916             fixedHeader.all('[id]').removeAttribute('id');
       
   917 
       
   918             fixedHeader.all('.' + headerClass).each(function (header, i) {
       
   919                 var liner = Y.Node.create(Y.Lang.sub(linerTemplate, {
       
   920                             className: linerClass
       
   921                         })),
       
   922                     refHeader = headers.item(i);
       
   923 
       
   924                 // Can't assign via skin css because sort (and potentially
       
   925                 // others) might override the padding values.
       
   926                 liner.setStyle('padding',
       
   927                     refHeader.getComputedStyle('paddingTop') + ' ' +
       
   928                     refHeader.getComputedStyle('paddingRight') + ' ' +
       
   929                     refHeader.getComputedStyle('paddingBottom') + ' ' +
       
   930                     refHeader.getComputedStyle('paddingLeft'));
       
   931 
       
   932                 liner.appendChild(header.get('childNodes').toFrag());
       
   933 
       
   934                 header.appendChild(liner);
       
   935             }, this);
       
   936 
       
   937             this._syncScrollColumnWidths();
       
   938 
       
   939             this._addScrollbarPadding();
       
   940         }
       
   941     },
       
   942 
       
   943     /**
       
   944     Wraps the table for X and Y scrolling, if necessary, if the `scrollable`
       
   945     attribute is set.  Synchronizes dimensions and DOM placement of all
       
   946     scrolling related nodes.
       
   947 
       
   948     @method _syncScrollUI
       
   949     @protected
       
   950     @since 3.5.0
       
   951     **/
       
   952     _syncScrollUI: function () {
       
   953         var x = this._xScroll,
       
   954             y = this._yScroll,
       
   955             xScroller  = this._xScrollNode,
       
   956             yScroller  = this._yScrollNode,
       
   957             scrollLeft = xScroller && xScroller.get('scrollLeft'),
       
   958             scrollTop  = yScroller && yScroller.get('scrollTop');
       
   959 
       
   960         this._uiSetScrollable();
       
   961 
       
   962         // TODO: Probably should split this up into syncX, syncY, and syncXY
       
   963         if (x || y) {
       
   964             if ((this.get('width') || '').slice(-1) === '%') {
       
   965                 this._bindScrollResize();
       
   966             } else {
       
   967                 this._unbindScrollResize();
       
   968             }
       
   969 
       
   970             this._syncScrollCaptionUI();
       
   971         } else {
       
   972             this._disableScrolling();
       
   973         }
       
   974 
       
   975         if (this._yScrollHeader) {
       
   976             this._yScrollHeader.setStyle('display', 'none');
       
   977         }
       
   978 
       
   979         if (x) {
       
   980             if (!y) {
       
   981                 this._disableYScrolling();
       
   982             }
       
   983 
       
   984             this._syncXScrollUI(y);
       
   985         }
       
   986 
       
   987         if (y) {
       
   988             if (!x) {
       
   989                 this._disableXScrolling();
       
   990             }
       
   991 
       
   992             this._syncYScrollUI(x);
       
   993         }
       
   994 
       
   995         // Restore scroll position
       
   996         if (scrollLeft && this._xScrollNode) {
       
   997             this._xScrollNode.set('scrollLeft', scrollLeft);
       
   998         }
       
   999         if (scrollTop && this._yScrollNode) {
       
  1000             this._yScrollNode.set('scrollTop', scrollTop);
       
  1001         }
       
  1002     },
       
  1003 
       
  1004     /**
       
  1005     Wraps the table in a scrolling `<div>` of the configured width for "x"
       
  1006     scrolling.
       
  1007 
       
  1008     @method _syncXScrollUI
       
  1009     @param {Boolean} xy True if the table is configured with scrollable ="xy"
       
  1010     @protected
       
  1011     @since 3.5.0
       
  1012     **/
       
  1013     _syncXScrollUI: function (xy) {
       
  1014         var scroller     = this._xScrollNode,
       
  1015             yScroller    = this._yScrollContainer,
       
  1016             table        = this._tableNode,
       
  1017             width        = this.get('width'),
       
  1018             bbWidth      = this.get('boundingBox').get('offsetWidth'),
       
  1019             scrollbarWidth = Y.DOM.getScrollbarWidth(),
       
  1020             borderWidth, tableWidth;
       
  1021 
       
  1022         if (!scroller) {
       
  1023             scroller = this._createXScrollNode();
       
  1024 
       
  1025             // Not using table.wrap() because IE went all crazy, wrapping the
       
  1026             // table in the last td in the table itself.
       
  1027             (yScroller || table).replace(scroller).appendTo(scroller);
       
  1028         }
       
  1029 
       
  1030         // Can't use offsetHeight - clientHeight because IE6 returns
       
  1031         // clientHeight of 0 intially.
       
  1032         borderWidth = styleDim(scroller, 'borderLeftWidth') +
       
  1033                       styleDim(scroller, 'borderRightWidth');
       
  1034 
       
  1035         scroller.setStyle('width', '');
       
  1036         this._uiSetDim('width', '');
       
  1037         if (xy && this._yScrollContainer) {
       
  1038             this._yScrollContainer.setStyle('width', '');
       
  1039         }
       
  1040 
       
  1041         // Lock the table's unconstrained width to avoid configured column
       
  1042         // widths being ignored
       
  1043         if (Y.UA.ie && Y.UA.ie < 8) {
       
  1044             // Have to assign a style and trigger a reflow to allow the
       
  1045             // subsequent clearing of width + reflow to expand the table to
       
  1046             // natural width in IE 6
       
  1047             table.setStyle('width', width);
       
  1048             table.get('offsetWidth');
       
  1049         }
       
  1050         table.setStyle('width', '');
       
  1051         tableWidth = table.get('offsetWidth');
       
  1052         table.setStyle('width', tableWidth + 'px');
       
  1053 
       
  1054         this._uiSetDim('width', width);
       
  1055 
       
  1056         // Can't use 100% width because the borders add additional width
       
  1057         // TODO: Cache the border widths, though it won't prevent a reflow
       
  1058         scroller.setStyle('width', (bbWidth - borderWidth) + 'px');
       
  1059 
       
  1060         // expand the table to fill the assigned width if it doesn't
       
  1061         // already overflow the configured width
       
  1062         if ((scroller.get('offsetWidth') - borderWidth) > tableWidth) {
       
  1063             // Assumes the wrapped table doesn't have borders
       
  1064             if (xy) {
       
  1065                 table.setStyle('width', (scroller.get('offsetWidth') -
       
  1066                      borderWidth - scrollbarWidth) + 'px');
       
  1067             } else {
       
  1068                 table.setStyle('width', '100%');
       
  1069             }
       
  1070         }
       
  1071     },
       
  1072 
       
  1073     /**
       
  1074     Wraps the table in a scrolling `<div>` of the configured height (accounting
       
  1075     for the caption if there is one) if "y" scrolling is enabled.  Otherwise,
       
  1076     unwraps the table if necessary.
       
  1077 
       
  1078     @method _syncYScrollUI
       
  1079     @param {Boolean} xy True if the table is configured with scrollable = "xy"
       
  1080     @protected
       
  1081     @since 3.5.0
       
  1082     **/
       
  1083     _syncYScrollUI: function (xy) {
       
  1084         var yScroller    = this._yScrollContainer,
       
  1085             yScrollNode  = this._yScrollNode,
       
  1086             xScroller    = this._xScrollNode,
       
  1087             fixedHeader  = this._yScrollHeader,
       
  1088             scrollbar    = this._scrollbarNode,
       
  1089             table        = this._tableNode,
       
  1090             thead        = this._theadNode,
       
  1091             captionTable = this._captionTable,
       
  1092             boundingBox  = this.get('boundingBox'),
       
  1093             contentBox   = this.get('contentBox'),
       
  1094             width        = this.get('width'),
       
  1095             height       = boundingBox.get('offsetHeight'),
       
  1096             scrollbarWidth = Y.DOM.getScrollbarWidth(),
       
  1097             outerScroller;
       
  1098 
       
  1099         if (captionTable && !xy) {
       
  1100             captionTable.setStyle('width', width || '100%');
       
  1101         }
       
  1102 
       
  1103         if (!yScroller) {
       
  1104             yScroller = this._createYScrollNode();
       
  1105 
       
  1106             yScrollNode = this._yScrollNode;
       
  1107 
       
  1108             table.replace(yScroller).appendTo(yScrollNode);
       
  1109         }
       
  1110 
       
  1111         outerScroller = xy ? xScroller : yScroller;
       
  1112 
       
  1113         if (!xy) {
       
  1114             table.setStyle('width', '');
       
  1115         }
       
  1116 
       
  1117         // Set the scroller height
       
  1118         if (xy) {
       
  1119             // Account for the horizontal scrollbar in the overall height
       
  1120             height -= scrollbarWidth;
       
  1121         }
       
  1122 
       
  1123         yScrollNode.setStyle('height',
       
  1124             (height - outerScroller.get('offsetTop') -
       
  1125             // because IE6 is returning clientHeight 0 initially
       
  1126             styleDim(outerScroller, 'borderTopWidth') -
       
  1127             styleDim(outerScroller, 'borderBottomWidth')) + 'px');
       
  1128 
       
  1129         // Set the scroller width
       
  1130         if (xy) {
       
  1131             // For xy scrolling tables, the table should expand freely within
       
  1132             // the x scroller
       
  1133             yScroller.setStyle('width',
       
  1134                 (table.get('offsetWidth') + scrollbarWidth) + 'px');
       
  1135         } else {
       
  1136             this._uiSetYScrollWidth(width);
       
  1137         }
       
  1138 
       
  1139         if (captionTable && !xy) {
       
  1140             captionTable.setStyle('width', yScroller.get('offsetWidth') + 'px');
       
  1141         }
       
  1142 
       
  1143         // Allow headerless scrolling
       
  1144         if (thead && !fixedHeader) {
       
  1145             fixedHeader = this._createYScrollHeader();
       
  1146 
       
  1147             yScroller.prepend(fixedHeader);
       
  1148 
       
  1149             this._syncScrollHeaders();
       
  1150         }
       
  1151 
       
  1152         if (fixedHeader) {
       
  1153             this._syncScrollColumnWidths();
       
  1154 
       
  1155             fixedHeader.setStyle('display', '');
       
  1156             // This might need to come back if FF has issues
       
  1157             //fixedHeader.setStyle('width', '100%');
       
  1158                 //(yScroller.get('clientWidth') + scrollbarWidth) + 'px');
       
  1159 
       
  1160             if (!scrollbar) {
       
  1161                 scrollbar = this._createScrollbar();
       
  1162 
       
  1163                 this._bindScrollbar();
       
  1164 
       
  1165                 contentBox.prepend(scrollbar);
       
  1166             }
       
  1167 
       
  1168             this._uiSetScrollbarHeight();
       
  1169             this._uiSetScrollbarPosition(outerScroller);
       
  1170         }
       
  1171     },
       
  1172 
       
  1173     /**
       
  1174     Assigns the appropriate class to the `boundingBox` to identify the DataTable
       
  1175     as horizontally scrolling, vertically scrolling, or both (adds both classes).
       
  1176 
       
  1177     Classes added are "yui3-datatable-scrollable-x" or "...-y"
       
  1178 
       
  1179     @method _uiSetScrollable
       
  1180     @protected
       
  1181     @since 3.5.0
       
  1182     **/
       
  1183     _uiSetScrollable: function () {
       
  1184         this.get('boundingBox')
       
  1185             .toggleClass(this.getClassName('scrollable','x'), this._xScroll)
       
  1186             .toggleClass(this.getClassName('scrollable','y'), this._yScroll);
       
  1187     },
       
  1188 
       
  1189     /**
       
  1190     Updates the virtual scrollbar's height to avoid overlapping with the fixed
       
  1191     headers.
       
  1192 
       
  1193     @method _uiSetScrollbarHeight
       
  1194     @protected
       
  1195     @since 3.5.0
       
  1196     **/
       
  1197     _uiSetScrollbarHeight: function () {
       
  1198         var scrollbar   = this._scrollbarNode,
       
  1199             scroller    = this._yScrollNode,
       
  1200             fixedHeader = this._yScrollHeader;
       
  1201 
       
  1202         if (scrollbar && scroller && fixedHeader) {
       
  1203             scrollbar.get('firstChild').setStyle('height',
       
  1204                 this._tbodyNode.get('scrollHeight') + 'px');
       
  1205 
       
  1206             scrollbar.setStyle('height',
       
  1207                 (parseFloat(scroller.getComputedStyle('height')) -
       
  1208                  parseFloat(fixedHeader.getComputedStyle('height'))) + 'px');
       
  1209         }
       
  1210     },
       
  1211 
       
  1212     /**
       
  1213     Updates the virtual scrollbar's placement to avoid overlapping the fixed
       
  1214     headers or the data table.
       
  1215 
       
  1216     @method _uiSetScrollbarPosition
       
  1217     @param {Node} scroller Reference node to position the scrollbar over
       
  1218     @protected
       
  1219     @since 3.5.0
       
  1220     **/
       
  1221     _uiSetScrollbarPosition: function (scroller) {
       
  1222         var scrollbar     = this._scrollbarNode,
       
  1223             fixedHeader   = this._yScrollHeader;
       
  1224 
       
  1225         if (scrollbar && scroller && fixedHeader) {
       
  1226             scrollbar.setStyles({
       
  1227                 // Using getCS instead of offsetHeight because FF uses
       
  1228                 // fractional values, but reports ints to offsetHeight, so
       
  1229                 // offsetHeight is unreliable.  It is probably fine to use
       
  1230                 // offsetHeight in this case but this was left in place after
       
  1231                 // fixing an off-by-1px issue in FF 10- by fixing the caption
       
  1232                 // font style so FF picked it up.
       
  1233                 top: (parseFloat(fixedHeader.getComputedStyle('height')) +
       
  1234                       styleDim(scroller, 'borderTopWidth') +
       
  1235                       scroller.get('offsetTop')) + 'px',
       
  1236 
       
  1237                 // Minus 1 because IE 6-10 require the scrolled area to be
       
  1238                 // visible by at least 1px or it won't respond to clicks on the
       
  1239                 // scrollbar rail or endcap arrows.
       
  1240                 left: (scroller.get('offsetWidth') -
       
  1241                        Y.DOM.getScrollbarWidth() - 1 -
       
  1242                        styleDim(scroller, 'borderRightWidth')) + 'px'
       
  1243             });
       
  1244         }
       
  1245     },
       
  1246 
       
  1247     /**
       
  1248     Assigns the width of the `<div>` wrapping the data table in vertically
       
  1249     scrolling tables.
       
  1250 
       
  1251     If the table can't compress to the specified width, the container is
       
  1252     expanded accordingly.
       
  1253 
       
  1254     @method _uiSetYScrollWidth
       
  1255     @param {String} width The CSS width to attempt to set
       
  1256     @protected
       
  1257     @since 3.5.0
       
  1258     **/
       
  1259     _uiSetYScrollWidth: function (width) {
       
  1260         var scroller = this._yScrollContainer,
       
  1261             table    = this._tableNode,
       
  1262             tableWidth, borderWidth, scrollerWidth, scrollbarWidth;
       
  1263 
       
  1264         if (scroller && table) {
       
  1265             scrollbarWidth = Y.DOM.getScrollbarWidth();
       
  1266 
       
  1267             if (width) {
       
  1268                 // Assumes no table border
       
  1269                 borderWidth = scroller.get('offsetWidth') -
       
  1270                               scroller.get('clientWidth') +
       
  1271                               scrollbarWidth; // added back at the end
       
  1272 
       
  1273                 // The table's rendered width might be greater than the
       
  1274                 // configured width
       
  1275                 scroller.setStyle('width', width);
       
  1276 
       
  1277                 // Have to subtract the border width from the configured width
       
  1278                 // because the scroller's width will need to be reduced by the
       
  1279                 // border width as well during the width reassignment below.
       
  1280                 scrollerWidth = scroller.get('clientWidth') - borderWidth;
       
  1281 
       
  1282                 // Assumes no table borders
       
  1283                 table.setStyle('width', scrollerWidth + 'px');
       
  1284 
       
  1285                 tableWidth = table.get('offsetWidth');
       
  1286 
       
  1287                 // Expand the scroll node width if the table can't fit.
       
  1288                 // Otherwise, reassign the scroller a pixel width that
       
  1289                 // accounts for the borders.
       
  1290                 scroller.setStyle('width',
       
  1291                     (tableWidth + scrollbarWidth) + 'px');
       
  1292             } else {
       
  1293                 // Allow the table to expand naturally
       
  1294                 table.setStyle('width', '');
       
  1295                 scroller.setStyle('width', '');
       
  1296 
       
  1297                 scroller.setStyle('width',
       
  1298                     (table.get('offsetWidth') + scrollbarWidth) + 'px');
       
  1299             }
       
  1300         }
       
  1301     },
       
  1302 
       
  1303     /**
       
  1304     Detaches the scroll event subscriptions used to maintain scroll position
       
  1305     parity between the scrollable `<div>` wrapper around the data table and the
       
  1306     virtual scrollbar for vertically scrolling tables.
       
  1307 
       
  1308     @method _unbindScrollbar
       
  1309     @protected
       
  1310     @since 3.5.0
       
  1311     **/
       
  1312     _unbindScrollbar: function () {
       
  1313         if (this._scrollbarEventHandle) {
       
  1314             this._scrollbarEventHandle.detach();
       
  1315         }
       
  1316     },
       
  1317 
       
  1318     /**
       
  1319     Detaches the resize event subscription used to maintain column parity for
       
  1320     vertically scrolling tables with percentage widths.
       
  1321 
       
  1322     @method _unbindScrollResize
       
  1323     @protected
       
  1324     @since 3.5.0
       
  1325     **/
       
  1326     _unbindScrollResize: function () {
       
  1327         if (this._scrollResizeHandle) {
       
  1328             this._scrollResizeHandle.detach();
       
  1329             delete this._scrollResizeHandle;
       
  1330         }
       
  1331     }
       
  1332 
       
  1333     /**
       
  1334     Indicates horizontal table scrolling is enabled.
       
  1335 
       
  1336     @property _xScroll
       
  1337     @type {Boolean}
       
  1338     @default undefined (not initially set)
       
  1339     @private
       
  1340     @since 3.5.0
       
  1341     **/
       
  1342     //_xScroll: null,
       
  1343 
       
  1344     /**
       
  1345     Indicates vertical table scrolling is enabled.
       
  1346 
       
  1347     @property _yScroll
       
  1348     @type {Boolean}
       
  1349     @default undefined (not initially set)
       
  1350     @private
       
  1351     @since 3.5.0
       
  1352     **/
       
  1353     //_yScroll: null,
       
  1354 
       
  1355     /**
       
  1356     Fixed column header `<table>` Node for vertical scrolling tables.
       
  1357 
       
  1358     @property _yScrollHeader
       
  1359     @type {Node}
       
  1360     @default undefined (not initially set)
       
  1361     @protected
       
  1362     @since 3.5.0
       
  1363     **/
       
  1364     //_yScrollHeader: null,
       
  1365 
       
  1366     /**
       
  1367     Overflow Node used to contain the data rows in a vertically scrolling table.
       
  1368 
       
  1369     @property _yScrollNode
       
  1370     @type {Node}
       
  1371     @default undefined (not initially set)
       
  1372     @protected
       
  1373     @since 3.5.0
       
  1374     **/
       
  1375     //_yScrollNode: null,
       
  1376 
       
  1377     /**
       
  1378     Overflow Node used to contain the table headers and data in a horizontally
       
  1379     scrolling table.
       
  1380 
       
  1381     @property _xScrollNode
       
  1382     @type {Node}
       
  1383     @default undefined (not initially set)
       
  1384     @protected
       
  1385     @since 3.5.0
       
  1386     **/
       
  1387     //_xScrollNode: null
       
  1388 }, true);
       
  1389 
       
  1390 Y.Base.mix(Y.DataTable, [Scrollable]);
       
  1391 
       
  1392 
       
  1393 }, '3.10.3', {"requires": ["datatable-base", "datatable-column-widths", "dom-screen"], "skinnable": true});