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