~ubuntu-branches/ubuntu/utopic/moodle/utopic

« back to all changes in this revision

Viewing changes to lib/yuilib/3.9.1/build/datatable-scroll/datatable-scroll-debug.js

  • Committer: Package Import Robot
  • Author(s): Thijs Kinkhorst
  • Date: 2014-05-12 16:10:38 UTC
  • mfrom: (36.1.3 sid)
  • Revision ID: package-import@ubuntu.com-20140512161038-puyqf65k4e0s8ytz
Tags: 2.6.3-1
New upstream release.

Show diffs side-by-side

added added

removed removed

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