~michael.nelson/ubuntu-webcatalog/1267731-import-sca-apps-error

« back to all changes in this revision

Viewing changes to src/webcatalog/static/yui/3.10.3/build/datatable-body/datatable-body.js

  • Committer: Tarmac
  • Author(s): Stephen Stewart
  • Date: 2013-06-26 09:19:32 UTC
  • mfrom: (184.1.4 ubuntu-global-nav)
  • Revision ID: tarmac-20130626091932-8urtuli368k8p7ds
[r=beuno,jonas-drange] add ubuntu global nav to apps.ubuntu.com

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
YUI 3.10.3 (build 2fb5187)
 
3
Copyright 2013 Yahoo! Inc. All rights reserved.
 
4
Licensed under the BSD License.
 
5
http://yuilibrary.com/license/
 
6
*/
 
7
 
 
8
YUI.add('datatable-body', function (Y, NAME) {
 
9
 
 
10
/**
 
11
View class responsible for rendering the `<tbody>` section of a table. Used as
 
12
the default `bodyView` for `Y.DataTable.Base` and `Y.DataTable` classes.
 
13
 
 
14
@module datatable
 
15
@submodule datatable-body
 
16
@since 3.5.0
 
17
**/
 
18
var Lang         = Y.Lang,
 
19
    isArray      = Lang.isArray,
 
20
    isNumber     = Lang.isNumber,
 
21
    isString     = Lang.isString,
 
22
    fromTemplate = Lang.sub,
 
23
    htmlEscape   = Y.Escape.html,
 
24
    toArray      = Y.Array,
 
25
    bind         = Y.bind,
 
26
    YObject      = Y.Object,
 
27
    valueRegExp  = /\{value\}/g;
 
28
 
 
29
/**
 
30
View class responsible for rendering the `<tbody>` section of a table. Used as
 
31
the default `bodyView` for `Y.DataTable.Base` and `Y.DataTable` classes.
 
32
 
 
33
Translates the provided `modelList` into a rendered `<tbody>` based on the data
 
34
in the constituent Models, altered or ammended by any special column
 
35
configurations.
 
36
 
 
37
The `columns` configuration, passed to the constructor, determines which
 
38
columns will be rendered.
 
39
 
 
40
The rendering process involves constructing an HTML template for a complete row
 
41
of data, built by concatenating a customized copy of the instance's
 
42
`CELL_TEMPLATE` into the `ROW_TEMPLATE` once for each column.  This template is
 
43
then populated with values from each Model in the `modelList`, aggregating a
 
44
complete HTML string of all row and column data.  A `<tbody>` Node is then created from the markup and any column `nodeFormatter`s are applied.
 
45
 
 
46
Supported properties of the column objects include:
 
47
 
 
48
  * `key` - Used to link a column to an attribute in a Model.
 
49
  * `name` - Used for columns that don't relate to an attribute in the Model
 
50
    (`formatter` or `nodeFormatter` only) if the implementer wants a
 
51
    predictable name to refer to in their CSS.
 
52
  * `cellTemplate` - Overrides the instance's `CELL_TEMPLATE` for cells in this
 
53
    column only.
 
54
  * `formatter` - Used to customize or override the content value from the
 
55
    Model.  These do not have access to the cell or row Nodes and should
 
56
    return string (HTML) content.
 
57
  * `nodeFormatter` - Used to provide content for a cell as well as perform any
 
58
    custom modifications on the cell or row Node that could not be performed by
 
59
    `formatter`s.  Should be used sparingly for better performance.
 
60
  * `emptyCellValue` - String (HTML) value to use if the Model data for a
 
61
    column, or the content generated by a `formatter`, is the empty string,
 
62
    `null`, or `undefined`.
 
63
  * `allowHTML` - Set to `true` if a column value, `formatter`, or
 
64
    `emptyCellValue` can contain HTML.  This defaults to `false` to protect
 
65
    against XSS.
 
66
  * `className` - Space delimited CSS classes to add to all `<td>`s in a column.
 
67
 
 
68
A column `formatter` can be:
 
69
 
 
70
  * a function, as described below.
 
71
  * a string which can be:
 
72
      * the name of a pre-defined formatter function
 
73
        which can be located in the `Y.DataTable.BodyView.Formatters` hash using the
 
74
        value of the `formatter` property as the index.
 
75
      * A template that can use the `{value}` placeholder to include the value
 
76
        for the current cell or the name of any field in the underlaying model
 
77
        also enclosed in curly braces.  Any number and type of these placeholders
 
78
        can be used.
 
79
 
 
80
Column `formatter`s are passed an object (`o`) with the following properties:
 
81
 
 
82
  * `value` - The current value of the column's associated attribute, if any.
 
83
  * `data` - An object map of Model keys to their current values.
 
84
  * `record` - The Model instance.
 
85
  * `column` - The column configuration object for the current column.
 
86
  * `className` - Initially empty string to allow `formatter`s to add CSS
 
87
    classes to the cell's `<td>`.
 
88
  * `rowIndex` - The zero-based row number.
 
89
  * `rowClass` - Initially empty string to allow `formatter`s to add CSS
 
90
    classes to the cell's containing row `<tr>`.
 
91
 
 
92
They may return a value or update `o.value` to assign specific HTML content.  A
 
93
returned value has higher precedence.
 
94
 
 
95
Column `nodeFormatter`s are passed an object (`o`) with the following
 
96
properties:
 
97
 
 
98
  * `value` - The current value of the column's associated attribute, if any.
 
99
  * `td` - The `<td>` Node instance.
 
100
  * `cell` - The `<div>` liner Node instance if present, otherwise, the `<td>`.
 
101
    When adding content to the cell, prefer appending into this property.
 
102
  * `data` - An object map of Model keys to their current values.
 
103
  * `record` - The Model instance.
 
104
  * `column` - The column configuration object for the current column.
 
105
  * `rowIndex` - The zero-based row number.
 
106
 
 
107
They are expected to inject content into the cell's Node directly, including
 
108
any "empty" cell content.  Each `nodeFormatter` will have access through the
 
109
Node API to all cells and rows in the `<tbody>`, but not to the `<table>`, as
 
110
it will not be attached yet.
 
111
 
 
112
If a `nodeFormatter` returns `false`, the `o.td` and `o.cell` Nodes will be
 
113
`destroy()`ed to remove them from the Node cache and free up memory.  The DOM
 
114
elements will remain as will any content added to them.  _It is highly
 
115
advisable to always return `false` from your `nodeFormatter`s_.
 
116
 
 
117
@class BodyView
 
118
@namespace DataTable
 
119
@extends View
 
120
@since 3.5.0
 
121
**/
 
122
Y.namespace('DataTable').BodyView = Y.Base.create('tableBody', Y.View, [], {
 
123
    // -- Instance properties -------------------------------------------------
 
124
 
 
125
    /**
 
126
    HTML template used to create table cells.
 
127
 
 
128
    @property CELL_TEMPLATE
 
129
    @type {HTML}
 
130
    @default '<td {headers} class="{className}">{content}</td>'
 
131
    @since 3.5.0
 
132
    **/
 
133
    CELL_TEMPLATE: '<td {headers} class="{className}">{content}</td>',
 
134
 
 
135
    /**
 
136
    CSS class applied to even rows.  This is assigned at instantiation.
 
137
 
 
138
    For DataTable, this will be `yui3-datatable-even`.
 
139
 
 
140
    @property CLASS_EVEN
 
141
    @type {String}
 
142
    @default 'yui3-table-even'
 
143
    @since 3.5.0
 
144
    **/
 
145
    //CLASS_EVEN: null
 
146
 
 
147
    /**
 
148
    CSS class applied to odd rows.  This is assigned at instantiation.
 
149
 
 
150
    When used by DataTable instances, this will be `yui3-datatable-odd`.
 
151
 
 
152
    @property CLASS_ODD
 
153
    @type {String}
 
154
    @default 'yui3-table-odd'
 
155
    @since 3.5.0
 
156
    **/
 
157
    //CLASS_ODD: null
 
158
 
 
159
    /**
 
160
    HTML template used to create table rows.
 
161
 
 
162
    @property ROW_TEMPLATE
 
163
    @type {HTML}
 
164
    @default '<tr id="{rowId}" data-yui3-record="{clientId}" class="{rowClass}">{content}</tr>'
 
165
    @since 3.5.0
 
166
    **/
 
167
    ROW_TEMPLATE : '<tr id="{rowId}" data-yui3-record="{clientId}" class="{rowClass}">{content}</tr>',
 
168
 
 
169
    /**
 
170
    The object that serves as the source of truth for column and row data.
 
171
    This property is assigned at instantiation from the `host` property of
 
172
    the configuration object passed to the constructor.
 
173
 
 
174
    @property host
 
175
    @type {Object}
 
176
    @default (initially unset)
 
177
    @since 3.5.0
 
178
    **/
 
179
    //TODO: should this be protected?
 
180
    //host: null,
 
181
 
 
182
    /**
 
183
    HTML templates used to create the `<tbody>` containing the table rows.
 
184
 
 
185
    @property TBODY_TEMPLATE
 
186
    @type {HTML}
 
187
    @default '<tbody class="{className}">{content}</tbody>'
 
188
    @since 3.6.0
 
189
    **/
 
190
    TBODY_TEMPLATE: '<tbody class="{className}"></tbody>',
 
191
 
 
192
    // -- Public methods ------------------------------------------------------
 
193
 
 
194
    /**
 
195
    Returns the `<td>` Node from the given row and column index.  Alternately,
 
196
    the `seed` can be a Node.  If so, the nearest ancestor cell is returned.
 
197
    If the `seed` is a cell, it is returned.  If there is no cell at the given
 
198
    coordinates, `null` is returned.
 
199
 
 
200
    Optionally, include an offset array or string to return a cell near the
 
201
    cell identified by the `seed`.  The offset can be an array containing the
 
202
    number of rows to shift followed by the number of columns to shift, or one
 
203
    of "above", "below", "next", or "previous".
 
204
 
 
205
    <pre><code>// Previous cell in the previous row
 
206
    var cell = table.getCell(e.target, [-1, -1]);
 
207
 
 
208
    // Next cell
 
209
    var cell = table.getCell(e.target, 'next');
 
210
    var cell = table.getCell(e.taregt, [0, 1];</pre></code>
 
211
 
 
212
    @method getCell
 
213
    @param {Number[]|Node} seed Array of row and column indexes, or a Node that
 
214
        is either the cell itself or a descendant of one.
 
215
    @param {Number[]|String} [shift] Offset by which to identify the returned
 
216
        cell Node
 
217
    @return {Node}
 
218
    @since 3.5.0
 
219
    **/
 
220
    getCell: function (seed, shift) {
 
221
        var tbody = this.tbodyNode,
 
222
            row, cell, index, rowIndexOffset;
 
223
 
 
224
        if (seed && tbody) {
 
225
            if (isArray(seed)) {
 
226
                row = tbody.get('children').item(seed[0]);
 
227
                cell = row && row.get('children').item(seed[1]);
 
228
            } else if (Y.instanceOf(seed, Y.Node)) {
 
229
                cell = seed.ancestor('.' + this.getClassName('cell'), true);
 
230
            }
 
231
 
 
232
            if (cell && shift) {
 
233
                rowIndexOffset = tbody.get('firstChild.rowIndex');
 
234
                if (isString(shift)) {
 
235
                    // TODO this should be a static object map
 
236
                    switch (shift) {
 
237
                        case 'above'   : shift = [-1, 0]; break;
 
238
                        case 'below'   : shift = [1, 0]; break;
 
239
                        case 'next'    : shift = [0, 1]; break;
 
240
                        case 'previous': shift = [0, -1]; break;
 
241
                    }
 
242
                }
 
243
 
 
244
                if (isArray(shift)) {
 
245
                    index = cell.get('parentNode.rowIndex') +
 
246
                                shift[0] - rowIndexOffset;
 
247
                    row   = tbody.get('children').item(index);
 
248
 
 
249
                    index = cell.get('cellIndex') + shift[1];
 
250
                    cell  = row && row.get('children').item(index);
 
251
                }
 
252
            }
 
253
        }
 
254
 
 
255
        return cell || null;
 
256
    },
 
257
 
 
258
    /**
 
259
    Returns the generated CSS classname based on the input.  If the `host`
 
260
    attribute is configured, it will attempt to relay to its `getClassName`
 
261
    or use its static `NAME` property as a string base.
 
262
 
 
263
    If `host` is absent or has neither method nor `NAME`, a CSS classname
 
264
    will be generated using this class's `NAME`.
 
265
 
 
266
    @method getClassName
 
267
    @param {String} token* Any number of token strings to assemble the
 
268
        classname from.
 
269
    @return {String}
 
270
    @protected
 
271
    @since 3.5.0
 
272
    **/
 
273
    getClassName: function () {
 
274
        var host = this.host,
 
275
            args;
 
276
 
 
277
        if (host && host.getClassName) {
 
278
            return host.getClassName.apply(host, arguments);
 
279
        } else {
 
280
            args = toArray(arguments);
 
281
            args.unshift(this.constructor.NAME);
 
282
            return Y.ClassNameManager.getClassName
 
283
                .apply(Y.ClassNameManager, args);
 
284
        }
 
285
    },
 
286
 
 
287
    /**
 
288
    Returns the Model associated to the row Node or id provided. Passing the
 
289
    Node or id for a descendant of the row also works.
 
290
 
 
291
    If no Model can be found, `null` is returned.
 
292
 
 
293
    @method getRecord
 
294
    @param {String|Node} seed Row Node or `id`, or one for a descendant of a row
 
295
    @return {Model}
 
296
    @since 3.5.0
 
297
    **/
 
298
    getRecord: function (seed) {
 
299
        var modelList = this.get('modelList'),
 
300
            tbody     = this.tbodyNode,
 
301
            row       = null,
 
302
            record;
 
303
 
 
304
        if (tbody) {
 
305
            if (isString(seed)) {
 
306
                seed = tbody.one('#' + seed);
 
307
            }
 
308
 
 
309
            if (Y.instanceOf(seed, Y.Node)) {
 
310
                row = seed.ancestor(function (node) {
 
311
                    return node.get('parentNode').compareTo(tbody);
 
312
                }, true);
 
313
 
 
314
                record = row &&
 
315
                    modelList.getByClientId(row.getData('yui3-record'));
 
316
            }
 
317
        }
 
318
 
 
319
        return record || null;
 
320
    },
 
321
 
 
322
    /**
 
323
    Returns the `<tr>` Node from the given row index, Model, or Model's
 
324
    `clientId`.  If the rows haven't been rendered yet, or if the row can't be
 
325
    found by the input, `null` is returned.
 
326
 
 
327
    @method getRow
 
328
    @param {Number|String|Model} id Row index, Model instance, or clientId
 
329
    @return {Node}
 
330
    @since 3.5.0
 
331
    **/
 
332
    getRow: function (id) {
 
333
        var tbody = this.tbodyNode,
 
334
            row = null;
 
335
 
 
336
        if (tbody) {
 
337
            if (id) {
 
338
                id = this._idMap[id.get ? id.get('clientId') : id] || id;
 
339
            }
 
340
 
 
341
            row = isNumber(id) ?
 
342
                tbody.get('children').item(id) :
 
343
                tbody.one('#' + id);
 
344
        }
 
345
 
 
346
        return row;
 
347
    },
 
348
 
 
349
    /**
 
350
    Creates the table's `<tbody>` content by assembling markup generated by
 
351
    populating the `ROW\_TEMPLATE`, and `CELL\_TEMPLATE` templates with content
 
352
    from the `columns` and `modelList` attributes.
 
353
 
 
354
    The rendering process happens in three stages:
 
355
 
 
356
    1. A row template is assembled from the `columns` attribute (see
 
357
       `_createRowTemplate`)
 
358
 
 
359
    2. An HTML string is built up by concatening the application of the data in
 
360
       each Model in the `modelList` to the row template. For cells with
 
361
       `formatter`s, the function is called to generate cell content. Cells
 
362
       with `nodeFormatter`s are ignored. For all other cells, the data value
 
363
       from the Model attribute for the given column key is used.  The
 
364
       accumulated row markup is then inserted into the container.
 
365
 
 
366
    3. If any column is configured with a `nodeFormatter`, the `modelList` is
 
367
       iterated again to apply the `nodeFormatter`s.
 
368
 
 
369
    Supported properties of the column objects include:
 
370
 
 
371
      * `key` - Used to link a column to an attribute in a Model.
 
372
      * `name` - Used for columns that don't relate to an attribute in the Model
 
373
        (`formatter` or `nodeFormatter` only) if the implementer wants a
 
374
        predictable name to refer to in their CSS.
 
375
      * `cellTemplate` - Overrides the instance's `CELL_TEMPLATE` for cells in
 
376
        this column only.
 
377
      * `formatter` - Used to customize or override the content value from the
 
378
        Model.  These do not have access to the cell or row Nodes and should
 
379
        return string (HTML) content.
 
380
      * `nodeFormatter` - Used to provide content for a cell as well as perform
 
381
        any custom modifications on the cell or row Node that could not be
 
382
        performed by `formatter`s.  Should be used sparingly for better
 
383
        performance.
 
384
      * `emptyCellValue` - String (HTML) value to use if the Model data for a
 
385
        column, or the content generated by a `formatter`, is the empty string,
 
386
        `null`, or `undefined`.
 
387
      * `allowHTML` - Set to `true` if a column value, `formatter`, or
 
388
        `emptyCellValue` can contain HTML.  This defaults to `false` to protect
 
389
        against XSS.
 
390
      * `className` - Space delimited CSS classes to add to all `<td>`s in a
 
391
        column.
 
392
 
 
393
    Column `formatter`s are passed an object (`o`) with the following
 
394
    properties:
 
395
 
 
396
      * `value` - The current value of the column's associated attribute, if
 
397
        any.
 
398
      * `data` - An object map of Model keys to their current values.
 
399
      * `record` - The Model instance.
 
400
      * `column` - The column configuration object for the current column.
 
401
      * `className` - Initially empty string to allow `formatter`s to add CSS
 
402
        classes to the cell's `<td>`.
 
403
      * `rowIndex` - The zero-based row number.
 
404
      * `rowClass` - Initially empty string to allow `formatter`s to add CSS
 
405
        classes to the cell's containing row `<tr>`.
 
406
 
 
407
    They may return a value or update `o.value` to assign specific HTML
 
408
    content.  A returned value has higher precedence.
 
409
 
 
410
    Column `nodeFormatter`s are passed an object (`o`) with the following
 
411
    properties:
 
412
 
 
413
      * `value` - The current value of the column's associated attribute, if
 
414
        any.
 
415
      * `td` - The `<td>` Node instance.
 
416
      * `cell` - The `<div>` liner Node instance if present, otherwise, the
 
417
        `<td>`.  When adding content to the cell, prefer appending into this
 
418
        property.
 
419
      * `data` - An object map of Model keys to their current values.
 
420
      * `record` - The Model instance.
 
421
      * `column` - The column configuration object for the current column.
 
422
      * `rowIndex` - The zero-based row number.
 
423
 
 
424
    They are expected to inject content into the cell's Node directly, including
 
425
    any "empty" cell content.  Each `nodeFormatter` will have access through the
 
426
    Node API to all cells and rows in the `<tbody>`, but not to the `<table>`,
 
427
    as it will not be attached yet.
 
428
 
 
429
    If a `nodeFormatter` returns `false`, the `o.td` and `o.cell` Nodes will be
 
430
    `destroy()`ed to remove them from the Node cache and free up memory.  The
 
431
    DOM elements will remain as will any content added to them.  _It is highly
 
432
    advisable to always return `false` from your `nodeFormatter`s_.
 
433
 
 
434
    @method render
 
435
    @return {BodyView} The instance
 
436
    @chainable
 
437
    @since 3.5.0
 
438
    **/
 
439
    render: function () {
 
440
        var table   = this.get('container'),
 
441
            data    = this.get('modelList'),
 
442
            columns = this.get('columns'),
 
443
            tbody   = this.tbodyNode ||
 
444
                      (this.tbodyNode = this._createTBodyNode());
 
445
 
 
446
        // Needed for mutation
 
447
        this._createRowTemplate(columns);
 
448
 
 
449
        if (data) {
 
450
            tbody.setHTML(this._createDataHTML(columns));
 
451
 
 
452
            this._applyNodeFormatters(tbody, columns);
 
453
        }
 
454
 
 
455
        if (tbody.get('parentNode') !== table) {
 
456
            table.appendChild(tbody);
 
457
        }
 
458
 
 
459
        this._afterRenderCleanup();
 
460
 
 
461
        this.bindUI();
 
462
 
 
463
        return this;
 
464
    },
 
465
 
 
466
    // -- Protected and private methods ---------------------------------------
 
467
    /**
 
468
    Handles changes in the source's columns attribute.  Redraws the table data.
 
469
 
 
470
    @method _afterColumnsChange
 
471
    @param {EventFacade} e The `columnsChange` event object
 
472
    @protected
 
473
    @since 3.5.0
 
474
    **/
 
475
    // TODO: Preserve existing DOM
 
476
    // This will involve parsing and comparing the old and new column configs
 
477
    // and reacting to four types of changes:
 
478
    // 1. formatter, nodeFormatter, emptyCellValue changes
 
479
    // 2. column deletions
 
480
    // 3. column additions
 
481
    // 4. column moves (preserve cells)
 
482
    _afterColumnsChange: function () {
 
483
        this.render();
 
484
    },
 
485
 
 
486
    /**
 
487
    Handles modelList changes, including additions, deletions, and updates.
 
488
 
 
489
    Modifies the existing table DOM accordingly.
 
490
 
 
491
    @method _afterDataChange
 
492
    @param {EventFacade} e The `change` event from the ModelList
 
493
    @protected
 
494
    @since 3.5.0
 
495
    **/
 
496
    _afterDataChange: function () {
 
497
        //var type = e.type.slice(e.type.lastIndexOf(':') + 1);
 
498
 
 
499
        // TODO: Isolate changes
 
500
        this.render();
 
501
    },
 
502
 
 
503
    /**
 
504
    Handles replacement of the modelList.
 
505
 
 
506
    Rerenders the `<tbody>` contents.
 
507
 
 
508
    @method _afterModelListChange
 
509
    @param {EventFacade} e The `modelListChange` event
 
510
    @protected
 
511
    @since 3.6.0
 
512
    **/
 
513
    _afterModelListChange: function () {
 
514
        var handles = this._eventHandles;
 
515
 
 
516
        if (handles.dataChange) {
 
517
            handles.dataChange.detach();
 
518
            delete handles.dataChange;
 
519
            this.bindUI();
 
520
        }
 
521
 
 
522
        if (this.tbodyNode) {
 
523
            this.render();
 
524
        }
 
525
    },
 
526
 
 
527
    /**
 
528
    Iterates the `modelList`, and calls any `nodeFormatter`s found in the
 
529
    `columns` param on the appropriate cell Nodes in the `tbody`.
 
530
 
 
531
    @method _applyNodeFormatters
 
532
    @param {Node} tbody The `<tbody>` Node whose columns to update
 
533
    @param {Object[]} columns The column configurations
 
534
    @protected
 
535
    @since 3.5.0
 
536
    **/
 
537
    _applyNodeFormatters: function (tbody, columns) {
 
538
        var host = this.host,
 
539
            data = this.get('modelList'),
 
540
            formatters = [],
 
541
            linerQuery = '.' + this.getClassName('liner'),
 
542
            rows, i, len;
 
543
 
 
544
        // Only iterate the ModelList again if there are nodeFormatters
 
545
        for (i = 0, len = columns.length; i < len; ++i) {
 
546
            if (columns[i].nodeFormatter) {
 
547
                formatters.push(i);
 
548
            }
 
549
        }
 
550
 
 
551
        if (data && formatters.length) {
 
552
            rows = tbody.get('childNodes');
 
553
 
 
554
            data.each(function (record, index) {
 
555
                var formatterData = {
 
556
                        data      : record.toJSON(),
 
557
                        record    : record,
 
558
                        rowIndex  : index
 
559
                    },
 
560
                    row = rows.item(index),
 
561
                    i, len, col, key, cells, cell, keep;
 
562
 
 
563
 
 
564
                if (row) {
 
565
                    cells = row.get('childNodes');
 
566
                    for (i = 0, len = formatters.length; i < len; ++i) {
 
567
                        cell = cells.item(formatters[i]);
 
568
 
 
569
                        if (cell) {
 
570
                            col = formatterData.column = columns[formatters[i]];
 
571
                            key = col.key || col.id;
 
572
 
 
573
                            formatterData.value = record.get(key);
 
574
                            formatterData.td    = cell;
 
575
                            formatterData.cell  = cell.one(linerQuery) || cell;
 
576
 
 
577
                            keep = col.nodeFormatter.call(host,formatterData);
 
578
 
 
579
                            if (keep === false) {
 
580
                                // Remove from the Node cache to reduce
 
581
                                // memory footprint.  This also purges events,
 
582
                                // which you shouldn't be scoping to a cell
 
583
                                // anyway.  You've been warned.  Incidentally,
 
584
                                // you should always return false. Just sayin.
 
585
                                cell.destroy(true);
 
586
                            }
 
587
                        }
 
588
                    }
 
589
                }
 
590
            });
 
591
        }
 
592
    },
 
593
 
 
594
    /**
 
595
    Binds event subscriptions from the UI and the host (if assigned).
 
596
 
 
597
    @method bindUI
 
598
    @protected
 
599
    @since 3.5.0
 
600
    **/
 
601
    bindUI: function () {
 
602
        var handles     = this._eventHandles,
 
603
            modelList   = this.get('modelList'),
 
604
            changeEvent = modelList.model.NAME + ':change';
 
605
 
 
606
        if (!handles.columnsChange) {
 
607
            handles.columnsChange = this.after('columnsChange',
 
608
                bind('_afterColumnsChange', this));
 
609
        }
 
610
 
 
611
        if (modelList && !handles.dataChange) {
 
612
            handles.dataChange = modelList.after(
 
613
                ['add', 'remove', 'reset', changeEvent],
 
614
                bind('_afterDataChange', this));
 
615
        }
 
616
    },
 
617
 
 
618
    /**
 
619
    Iterates the `modelList` and applies each Model to the `_rowTemplate`,
 
620
    allowing any column `formatter` or `emptyCellValue` to override cell
 
621
    content for the appropriate column.  The aggregated HTML string is
 
622
    returned.
 
623
 
 
624
    @method _createDataHTML
 
625
    @param {Object[]} columns The column configurations to customize the
 
626
                generated cell content or class names
 
627
    @return {HTML} The markup for all Models in the `modelList`, each applied
 
628
                to the `_rowTemplate`
 
629
    @protected
 
630
    @since 3.5.0
 
631
    **/
 
632
    _createDataHTML: function (columns) {
 
633
        var data = this.get('modelList'),
 
634
            html = '';
 
635
 
 
636
        if (data) {
 
637
            data.each(function (model, index) {
 
638
                html += this._createRowHTML(model, index, columns);
 
639
            }, this);
 
640
        }
 
641
 
 
642
        return html;
 
643
    },
 
644
 
 
645
    /**
 
646
    Applies the data of a given Model, modified by any column formatters and
 
647
    supplemented by other template values to the instance's `_rowTemplate` (see
 
648
    `_createRowTemplate`).  The generated string is then returned.
 
649
 
 
650
    The data from Model's attributes is fetched by `toJSON` and this data
 
651
    object is appended with other properties to supply values to {placeholders}
 
652
    in the template.  For a template generated from a Model with 'foo' and 'bar'
 
653
    attributes, the data object would end up with the following properties
 
654
    before being used to populate the `_rowTemplate`:
 
655
 
 
656
      * `clientID` - From Model, used the assign the `<tr>`'s 'id' attribute.
 
657
      * `foo` - The value to populate the 'foo' column cell content.  This
 
658
        value will be the value stored in the Model's `foo` attribute, or the
 
659
        result of the column's `formatter` if assigned.  If the value is '',
 
660
        `null`, or `undefined`, and the column's `emptyCellValue` is assigned,
 
661
        that value will be used.
 
662
      * `bar` - Same for the 'bar' column cell content.
 
663
      * `foo-className` - String of CSS classes to apply to the `<td>`.
 
664
      * `bar-className` - Same.
 
665
      * `rowClass`      - String of CSS classes to apply to the `<tr>`. This
 
666
        will be the odd/even class per the specified index plus any additional
 
667
        classes assigned by column formatters (via `o.rowClass`).
 
668
 
 
669
    Because this object is available to formatters, any additional properties
 
670
    can be added to fill in custom {placeholders} in the `_rowTemplate`.
 
671
 
 
672
    @method _createRowHTML
 
673
    @param {Model} model The Model instance to apply to the row template
 
674
    @param {Number} index The index the row will be appearing
 
675
    @param {Object[]} columns The column configurations
 
676
    @return {HTML} The markup for the provided Model, less any `nodeFormatter`s
 
677
    @protected
 
678
    @since 3.5.0
 
679
    **/
 
680
    _createRowHTML: function (model, index, columns) {
 
681
        var data     = model.toJSON(),
 
682
            clientId = model.get('clientId'),
 
683
            values   = {
 
684
                rowId   : this._getRowId(clientId),
 
685
                clientId: clientId,
 
686
                rowClass: (index % 2) ? this.CLASS_ODD : this.CLASS_EVEN
 
687
            },
 
688
            host = this.host || this,
 
689
            i, len, col, token, value, formatterData;
 
690
 
 
691
        for (i = 0, len = columns.length; i < len; ++i) {
 
692
            col   = columns[i];
 
693
            value = data[col.key];
 
694
            token = col._id || col.key;
 
695
 
 
696
            values[token + '-className'] = '';
 
697
 
 
698
            if (col._formatterFn) {
 
699
                formatterData = {
 
700
                    value    : value,
 
701
                    data     : data,
 
702
                    column   : col,
 
703
                    record   : model,
 
704
                    className: '',
 
705
                    rowClass : '',
 
706
                    rowIndex : index
 
707
                };
 
708
 
 
709
                // Formatters can either return a value
 
710
                value = col._formatterFn.call(host, formatterData);
 
711
 
 
712
                // or update the value property of the data obj passed
 
713
                if (value === undefined) {
 
714
                    value = formatterData.value;
 
715
                }
 
716
 
 
717
                values[token + '-className'] = formatterData.className;
 
718
                values.rowClass += ' ' + formatterData.rowClass;
 
719
            }
 
720
 
 
721
            if (value === undefined || value === null || value === '') {
 
722
                value = col.emptyCellValue || '';
 
723
            }
 
724
 
 
725
            values[token] = col.allowHTML ? value : htmlEscape(value);
 
726
 
 
727
            values.rowClass = values.rowClass.replace(/\s+/g, ' ');
 
728
        }
 
729
 
 
730
        return fromTemplate(this._rowTemplate, values);
 
731
    },
 
732
 
 
733
    /**
 
734
    Creates a custom HTML template string for use in generating the markup for
 
735
    individual table rows with {placeholder}s to capture data from the Models
 
736
    in the `modelList` attribute or from column `formatter`s.
 
737
 
 
738
    Assigns the `_rowTemplate` property.
 
739
 
 
740
    @method _createRowTemplate
 
741
    @param {Object[]} columns Array of column configuration objects
 
742
    @protected
 
743
    @since 3.5.0
 
744
    **/
 
745
    _createRowTemplate: function (columns) {
 
746
        var html         = '',
 
747
            cellTemplate = this.CELL_TEMPLATE,
 
748
            F = Y.DataTable.BodyView.Formatters,
 
749
            i, len, col, key, token, headers, tokenValues, formatter;
 
750
 
 
751
        for (i = 0, len = columns.length; i < len; ++i) {
 
752
            col     = columns[i];
 
753
            key     = col.key;
 
754
            token   = col._id || key;
 
755
            formatter = col.formatter;
 
756
            // Only include headers if there are more than one
 
757
            headers = (col._headers || []).length > 1 ?
 
758
                        'headers="' + col._headers.join(' ') + '"' : '';
 
759
 
 
760
            tokenValues = {
 
761
                content  : '{' + token + '}',
 
762
                headers  : headers,
 
763
                className: this.getClassName('col', token) + ' ' +
 
764
                           (col.className || '') + ' ' +
 
765
                           this.getClassName('cell') +
 
766
                           ' {' + token + '-className}'
 
767
            };
 
768
            if (formatter) {
 
769
                if (Lang.isFunction(formatter)) {
 
770
                    col._formatterFn = formatter;
 
771
                } else if (formatter in F) {
 
772
                    col._formatterFn = F[formatter].call(this.host || this, col);
 
773
                } else {
 
774
                    tokenValues.content = formatter.replace(valueRegExp, tokenValues.content);
 
775
                }
 
776
            }
 
777
 
 
778
            if (col.nodeFormatter) {
 
779
                // Defer all node decoration to the formatter
 
780
                tokenValues.content = '';
 
781
            }
 
782
 
 
783
            html += fromTemplate(col.cellTemplate || cellTemplate, tokenValues);
 
784
        }
 
785
 
 
786
        this._rowTemplate = fromTemplate(this.ROW_TEMPLATE, {
 
787
            content: html
 
788
        });
 
789
    },
 
790
    /**
 
791
    Cleans up temporary values created during rendering.
 
792
    @method _afterRenderCleanup
 
793
    @private
 
794
    */
 
795
    _afterRenderCleanup: function () {
 
796
        var columns = this.get('columns'),
 
797
            i, len = columns.length;
 
798
 
 
799
        for (i = 0;i < len; i+=1) {
 
800
            delete columns[i]._formatterFn;
 
801
        }
 
802
 
 
803
    },
 
804
 
 
805
    /**
 
806
    Creates the `<tbody>` node that will store the data rows.
 
807
 
 
808
    @method _createTBodyNode
 
809
    @return {Node}
 
810
    @protected
 
811
    @since 3.6.0
 
812
    **/
 
813
    _createTBodyNode: function () {
 
814
        return Y.Node.create(fromTemplate(this.TBODY_TEMPLATE, {
 
815
            className: this.getClassName('data')
 
816
        }));
 
817
    },
 
818
 
 
819
    /**
 
820
    Destroys the instance.
 
821
 
 
822
    @method destructor
 
823
    @protected
 
824
    @since 3.5.0
 
825
    **/
 
826
    destructor: function () {
 
827
        (new Y.EventHandle(YObject.values(this._eventHandles))).detach();
 
828
    },
 
829
 
 
830
    /**
 
831
    Holds the event subscriptions needing to be detached when the instance is
 
832
    `destroy()`ed.
 
833
 
 
834
    @property _eventHandles
 
835
    @type {Object}
 
836
    @default undefined (initially unset)
 
837
    @protected
 
838
    @since 3.5.0
 
839
    **/
 
840
    //_eventHandles: null,
 
841
 
 
842
    /**
 
843
    Returns the row ID associated with a Model's clientId.
 
844
 
 
845
    @method _getRowId
 
846
    @param {String} clientId The Model clientId
 
847
    @return {String}
 
848
    @protected
 
849
    **/
 
850
    _getRowId: function (clientId) {
 
851
        return this._idMap[clientId] || (this._idMap[clientId] = Y.guid());
 
852
    },
 
853
 
 
854
    /**
 
855
    Map of Model clientIds to row ids.
 
856
 
 
857
    @property _idMap
 
858
    @type {Object}
 
859
    @protected
 
860
    **/
 
861
    //_idMap,
 
862
 
 
863
    /**
 
864
    Initializes the instance. Reads the following configuration properties in
 
865
    addition to the instance attributes:
 
866
 
 
867
      * `columns` - (REQUIRED) The initial column information
 
868
      * `host`    - The object to serve as source of truth for column info and
 
869
                    for generating class names
 
870
 
 
871
    @method initializer
 
872
    @param {Object} config Configuration data
 
873
    @protected
 
874
    @since 3.5.0
 
875
    **/
 
876
    initializer: function (config) {
 
877
        this.host = config.host;
 
878
 
 
879
        this._eventHandles = {
 
880
            modelListChange: this.after('modelListChange',
 
881
                bind('_afterModelListChange', this))
 
882
        };
 
883
        this._idMap = {};
 
884
 
 
885
        this.CLASS_ODD  = this.getClassName('odd');
 
886
        this.CLASS_EVEN = this.getClassName('even');
 
887
 
 
888
    }
 
889
 
 
890
    /**
 
891
    The HTML template used to create a full row of markup for a single Model in
 
892
    the `modelList` plus any customizations defined in the column
 
893
    configurations.
 
894
 
 
895
    @property _rowTemplate
 
896
    @type {HTML}
 
897
    @default (initially unset)
 
898
    @protected
 
899
    @since 3.5.0
 
900
    **/
 
901
    //_rowTemplate: null
 
902
},{
 
903
    /**
 
904
    Hash of formatting functions for cell contents.
 
905
 
 
906
    This property can be populated with a hash of formatting functions by the developer
 
907
    or a set of pre-defined functions can be loaded via the `datatable-formatters` module.
 
908
 
 
909
    See: [DataTable.BodyView.Formatters](./DataTable.BodyView.Formatters.html)
 
910
    @property Formatters
 
911
    @type Object
 
912
    @since 3.8.0
 
913
    @static
 
914
    **/
 
915
    Formatters: {}
 
916
});
 
917
 
 
918
 
 
919
}, '3.10.3', {"requires": ["datatable-core", "view", "classnamemanager"]});