~bac/juju-gui/trunkcopy

« back to all changes in this revision

Viewing changes to lib/yui/build/datatable-head/datatable-head.js

  • Committer: kapil.foss at gmail
  • Date: 2012-07-13 18:45:59 UTC
  • Revision ID: kapil.foss@gmail.com-20120713184559-2xl7be17egsrz0c9
reshape

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
YUI 3.5.1 (build 22)
3
 
Copyright 2012 Yahoo! Inc. All rights reserved.
4
 
Licensed under the BSD License.
5
 
http://yuilibrary.com/license/
6
 
*/
7
 
YUI.add('datatable-head', function(Y) {
8
 
 
9
 
/**
10
 
View class responsible for rendering the `<thead>` section of a table. Used as
11
 
the default `headerView` for `Y.DataTable.Base` and `Y.DataTable` classes.
12
 
 
13
 
@module datatable
14
 
@submodule datatable-head
15
 
@since 3.5.0
16
 
**/
17
 
var Lang = Y.Lang,
18
 
    fromTemplate = Lang.sub,
19
 
    isArray = Lang.isArray,
20
 
    toArray = Y.Array,
21
 
 
22
 
    ClassNameManager = Y.ClassNameManager,
23
 
    _getClassName    = ClassNameManager.getClassName;
24
 
 
25
 
/**
26
 
View class responsible for rendering the `<thead>` section of a table. Used as
27
 
the default `headerView` for `Y.DataTable.Base` and `Y.DataTable` classes.
28
 
 
29
 
Translates the provided array of column configuration objects into a rendered
30
 
`<thead>` based on the data in those objects.
31
 
    
32
 
 
33
 
The structure of the column data is expected to be a single array of objects,
34
 
where each object corresponds to a `<th>`.  Those objects may contain a
35
 
`children` property containing a similarly structured array to indicate the
36
 
nested cells should be grouped under the parent column's colspan in a separate
37
 
row of header cells. E.g.
38
 
 
39
 
<pre><code>
40
 
new Y.DataTable.HeaderView({
41
 
  container: tableNode,
42
 
  columns: [
43
 
    { key: 'id' }, // no nesting
44
 
    { key: 'name', children: [
45
 
      { key: 'firstName', label: 'First' },
46
 
      { key: 'lastName',  label: 'Last' } ] }
47
 
  ]
48
 
}).render();
49
 
</code></pre>
50
 
 
51
 
This would translate to the following visualization:
52
 
 
53
 
<pre>
54
 
---------------------
55
 
|    |     name     |
56
 
|    |---------------
57
 
| id | First | Last |
58
 
---------------------
59
 
</pre>
60
 
 
61
 
Supported properties of the column objects include:
62
 
 
63
 
  * `label`     - The HTML content of the header cell.
64
 
  * `key`       - If `label` is not specified, the `key` is used for content.
65
 
  * `children`  - Array of columns to appear below this column in the next
66
 
                  row.
67
 
  * `headerTemplate` - Overrides the instance's `CELL_TEMPLATE` for cells in this
68
 
    column only.
69
 
  * `abbr`      - The content of the 'abbr' attribute of the `<th>`
70
 
  * `className` - Adds this string of CSS classes to the column header
71
 
 
72
 
Through the life of instantiation and rendering, the column objects will have
73
 
the following properties added to them:
74
 
 
75
 
  * `id`       - (Defaulted by DataTable) The id to assign the rendered column
76
 
  * `_colspan` - To supply the `<th>` attribute
77
 
  * `_rowspan` - To supply the `<th>` attribute
78
 
  * `_parent`  - (Added by DataTable) If the column is a child of another
79
 
    column, this points to its parent column
80
 
 
81
 
The column object is also used to provide values for {placeholder} tokens in the
82
 
instance's `CELL_TEMPLATE`, so you can modify the template and include other
83
 
column object properties to populate them.
84
 
 
85
 
@class HeaderView
86
 
@namespace DataTable
87
 
@extends View
88
 
@since 3.5.0
89
 
**/
90
 
Y.namespace('DataTable').HeaderView = Y.Base.create('tableHeader', Y.View, [], {
91
 
    // -- Instance properties -------------------------------------------------
92
 
 
93
 
    /**
94
 
    Template used to create the table's header cell markup.  Override this to
95
 
    customize how header cell markup is created.
96
 
 
97
 
    @property CELL_TEMPLATE
98
 
    @type {HTML}
99
 
    @default '<th id="{id}" colspan="{_colspan}" rowspan="{_rowspan}" class="{className}" scope="col" {_id}{abbr}>{content}</th>'
100
 
    @since 3.5.0
101
 
    **/
102
 
    CELL_TEMPLATE:
103
 
        '<th id="{id}" colspan="{_colspan}" rowspan="{_rowspan}" class="{className}" scope="col" {_id}{abbr}>{content}</th>',
104
 
 
105
 
    /**
106
 
    The data representation of the header rows to render.  This is assigned by
107
 
    parsing the `columns` configuration array, and is used by the render()
108
 
    method.
109
 
 
110
 
    @property columns
111
 
    @type {Array[]}
112
 
    @default (initially unset)
113
 
    @since 3.5.0
114
 
    **/
115
 
    //TODO: should this be protected?
116
 
    //columns: null,
117
 
 
118
 
    /**
119
 
    Template used to create the table's header row markup.  Override this to
120
 
    customize the row markup.
121
 
 
122
 
    @property ROW_TEMPLATE
123
 
    @type {HTML}
124
 
    @default '<tr>{content}</tr>'
125
 
    @since 3.5.0
126
 
    **/
127
 
    ROW_TEMPLATE:
128
 
        '<tr>{content}</tr>',
129
 
 
130
 
    /**
131
 
    The object that serves as the source of truth for column and row data.
132
 
    This property is assigned at instantiation from the `source` property of
133
 
    the configuration object passed to the constructor.
134
 
 
135
 
    @property source
136
 
    @type {Object}
137
 
    @default (initially unset)
138
 
    @since 3.5.0
139
 
    **/
140
 
    //TODO: should this be protected?
141
 
    //source: null,
142
 
 
143
 
 
144
 
    // -- Public methods ------------------------------------------------------
145
 
 
146
 
    /**
147
 
    Builds a CSS class name from the provided tokens.  If the instance is
148
 
    created with `cssPrefix` or `source` in the configuration, it will use this
149
 
    prefix (the `_cssPrefix` of the `source` object) as the base token.  This
150
 
    allows class instances to generate markup with class names that correspond
151
 
    to the parent class that is consuming them.
152
 
 
153
 
    @method getClassName
154
 
    @param {String} token* Any number of tokens to include in the class name
155
 
    @return {String} The generated class name
156
 
    @since 3.5.0
157
 
    **/
158
 
    getClassName: function () {
159
 
        var args = toArray(arguments);
160
 
        args.unshift(this._cssPrefix);
161
 
        args.push(true);
162
 
 
163
 
        return _getClassName.apply(ClassNameManager, args);
164
 
    },
165
 
 
166
 
    /**
167
 
    Creates the `<thead>` Node content by assembling markup generated by
168
 
    populating the `ROW_TEMPLATE` and `CELL_TEMPLATE` templates with content
169
 
    from the `columns` property.
170
 
    
171
 
    @method render
172
 
    @return {HeaderView} The instance
173
 
    @chainable
174
 
    @since 3.5.0
175
 
    **/
176
 
    render: function () {
177
 
        var thead    = this.get('container'),
178
 
            columns  = this.columns,
179
 
            defaults = {
180
 
                _colspan: 1,
181
 
                _rowspan: 1,
182
 
                abbr: ''
183
 
            },
184
 
            i, len, j, jlen, col, html, content, values;
185
 
 
186
 
        if (thead && columns) {
187
 
            html = '';
188
 
 
189
 
            if (columns.length) {
190
 
                for (i = 0, len = columns.length; i < len; ++i) {
191
 
                    content = '';
192
 
 
193
 
                    for (j = 0, jlen = columns[i].length; j < jlen; ++j) {
194
 
                        col = columns[i][j];
195
 
                        values = Y.merge(
196
 
                            defaults,
197
 
                            col, {
198
 
                                className: this.getClassName('header'),
199
 
                                content  : col.label || col.key ||
200
 
                                           ("Column " + (j + 1))
201
 
                            }
202
 
                        );
203
 
 
204
 
                        values._id = col._id ?
205
 
                            ' data-yui3-col-id="' + col._id + '"' : '';
206
 
                        
207
 
                        if (col.abbr) {
208
 
                            values.abbr = ' abbr="' + col.abbr + '"';
209
 
                        }
210
 
 
211
 
                        if (col.className) {
212
 
                            values.className += ' ' + col.className;
213
 
                        }
214
 
 
215
 
                        if (col._first) {
216
 
                            values.className += ' ' + this.getClassName('first', 'header');
217
 
                        }
218
 
 
219
 
                        if (col._id) {
220
 
                            values.className +=
221
 
                                ' ' + this.getClassName('col', col._id);
222
 
                        }
223
 
 
224
 
                        content += fromTemplate(
225
 
                            col.headerTemplate || this.CELL_TEMPLATE, values);
226
 
                    }
227
 
 
228
 
                    html += fromTemplate(this.ROW_TEMPLATE, {
229
 
                        content: content
230
 
                    });
231
 
                }
232
 
            }
233
 
 
234
 
            thead.setContent(html);
235
 
        }
236
 
 
237
 
        this.bindUI();
238
 
 
239
 
        return this;
240
 
    },
241
 
 
242
 
    // -- Protected and private properties and methods ------------------------
243
 
    /**
244
 
    The base token for classes created with the `getClassName` method.
245
 
 
246
 
    @property _cssPrefix
247
 
    @type {String}
248
 
    @default 'yui3-table'
249
 
    @protected
250
 
    @since 3.5.0
251
 
    **/
252
 
    _cssPrefix: ClassNameManager.getClassName('table'),
253
 
 
254
 
    /**
255
 
    Handles changes in the source's columns attribute.  Redraws the headers.
256
 
 
257
 
    @method _afterColumnsChange
258
 
    @param {EventFacade} e The `columnsChange` event object
259
 
    @protected
260
 
    @since 3.5.0
261
 
    **/
262
 
    _afterColumnsChange: function (e) {
263
 
        this.columns = this._parseColumns(e.newVal);
264
 
 
265
 
        this.render();
266
 
    },
267
 
 
268
 
    /**
269
 
    Binds event subscriptions from the UI and the source (if assigned).
270
 
 
271
 
    @method bindUI
272
 
    @protected
273
 
    @since 3.5.0
274
 
    **/
275
 
    bindUI: function () {
276
 
        if (this.source && !this._eventHandles.columnsChange) {
277
 
            // TODO: How best to decouple this?
278
 
            this._eventHandles.columnsChange =
279
 
                this.source.after('columnsChange',
280
 
                    Y.bind('_afterColumnsChange', this));
281
 
        }
282
 
    },
283
 
 
284
 
    /**
285
 
    Destroys the instance.
286
 
 
287
 
    @method destructor
288
 
    @protected
289
 
    @since 3.5.0
290
 
    **/
291
 
    destructor: function () {
292
 
        (new Y.EventHandle(Y.Object.values(this._eventHandles))).detach();
293
 
    },
294
 
 
295
 
    /**
296
 
    Holds the event subscriptions needing to be detached when the instance is
297
 
    `destroy()`ed.
298
 
 
299
 
    @property _eventHandles
300
 
    @type {Object}
301
 
    @default undefined (initially unset)
302
 
    @protected
303
 
    @since 3.5.0
304
 
    **/
305
 
    //_eventHandles: null,
306
 
 
307
 
    /**
308
 
    Initializes the instance. Reads the following configuration properties:
309
 
 
310
 
      * `columns` - (REQUIRED) The initial column information
311
 
      * `cssPrefix` - The base string for classes generated by `getClassName`
312
 
      * `source` - The object to serve as source of truth for column info
313
 
 
314
 
    @method initializer
315
 
    @param {Object} config Configuration data
316
 
    @protected
317
 
    @since 3.5.0
318
 
    **/
319
 
    initializer: function (config) {
320
 
        config || (config = {});
321
 
 
322
 
        var cssPrefix = config.cssPrefix || (config.source || {}).cssPrefix;
323
 
 
324
 
        this.source  = config.source;
325
 
        this.columns = this._parseColumns(config.columns);
326
 
 
327
 
        this._eventHandles = [];
328
 
 
329
 
        if (cssPrefix) {
330
 
            this._cssPrefix = cssPrefix;
331
 
        }
332
 
    },
333
 
 
334
 
    /**
335
 
    Translate the input column format into a structure useful for rendering a
336
 
    `<thead>`, rows, and cells.  The structure of the input is expected to be a
337
 
    single array of objects, where each object corresponds to a `<th>`.  Those
338
 
    objects may contain a `children` property containing a similarly structured
339
 
    array to indicate the nested cells should be grouped under the parent
340
 
    column's colspan in a separate row of header cells. E.g.
341
 
 
342
 
    <pre><code>
343
 
    [
344
 
      { key: 'id' }, // no nesting
345
 
      { key: 'name', children: [
346
 
        { key: 'firstName', label: 'First' },
347
 
        { key: 'lastName',  label: 'Last' } ] }
348
 
    ]
349
 
    </code></pre>
350
 
 
351
 
    would indicate two header rows with the first column 'id' being assigned a
352
 
    `rowspan` of `2`, the 'name' column appearing in the first row with a
353
 
    `colspan` of `2`, and the 'firstName' and 'lastName' columns appearing in
354
 
    the second row, below the 'name' column.
355
 
 
356
 
    <pre>
357
 
    ---------------------
358
 
    |    |     name     |
359
 
    |    |---------------
360
 
    | id | First | Last |
361
 
    ---------------------
362
 
    </pre>
363
 
 
364
 
    Supported properties of the column objects include:
365
 
 
366
 
      * `label`    - The HTML content of the header cell.
367
 
      * `key`      - If `label` is not specified, the `key` is used for content.
368
 
      * `children` - Array of columns to appear below this column in the next
369
 
                     row.
370
 
      * `abbr`     - The content of the 'abbr' attribute of the `<th>`
371
 
      * `headerTemplate` - Overrides the instance's `CELL_TEMPLATE` for cells
372
 
        in this column only.
373
 
 
374
 
    The output structure is basically a simulation of the `<thead>` structure
375
 
    with arrays for rows and objects for cells.  Column objects have the
376
 
    following properties added to them:
377
 
    
378
 
      * `id`       - (Defaulted by DataTable) The id to assign the rendered
379
 
                     column
380
 
      * `_colspan` - Per the `<th>` attribute
381
 
      * `_rowspan` - Per the `<th>` attribute
382
 
      * `_parent`  - (Added by DataTable) If the column is a child of another
383
 
        column, this points to its parent column
384
 
 
385
 
    The column object is also used to provide values for {placeholder}
386
 
    replacement in the `CELL_TEMPLATE`, so you can modify the template and
387
 
    include other column object properties to populate them.
388
 
 
389
 
    @method _parseColumns
390
 
    @param {Object[]} data Array of column object data
391
 
    @return {Array[]} An array of arrays corresponding to the header row
392
 
            structure to render
393
 
    @protected
394
 
    @since 3.5.0
395
 
    **/
396
 
    _parseColumns: function (data) {
397
 
        var columns = [],
398
 
            stack = [],
399
 
            rowSpan = 1,
400
 
            entry, row, col, children, parent, i, len, j;
401
 
        
402
 
        if (isArray(data) && data.length) {
403
 
            // First pass, assign colspans and calculate row count for
404
 
            // non-nested headers' rowspan
405
 
            stack.push([data, -1]);
406
 
 
407
 
            while (stack.length) {
408
 
                entry = stack[stack.length - 1];
409
 
                row   = entry[0];
410
 
                i     = entry[1] + 1;
411
 
 
412
 
                for (len = row.length; i < len; ++i) {
413
 
                    col = row[i];
414
 
                    children = col.children;
415
 
 
416
 
                    Y.stamp(col);
417
 
 
418
 
                    if (isArray(children) && children.length) {
419
 
                        stack.push([children, -1]);
420
 
                        entry[1] = i;
421
 
 
422
 
                        rowSpan = Math.max(rowSpan, stack.length);
423
 
 
424
 
                        // break to let the while loop process the children
425
 
                        break;
426
 
                    } else {
427
 
                        col._colspan = 1;
428
 
                    }
429
 
                }
430
 
 
431
 
                if (i >= len) {
432
 
                    // All columns in this row are processed
433
 
                    if (stack.length > 1) {
434
 
                        entry  = stack[stack.length - 2];
435
 
                        parent = entry[0][entry[1]];
436
 
 
437
 
                        parent._colspan = 0;
438
 
 
439
 
                        for (i = 0, len = row.length; i < len; ++i) {
440
 
                            // Can't use .length because in 3+ rows, colspan
441
 
                            // needs to aggregate the colspans of children
442
 
                            parent._colspan += row[i]._colspan;
443
 
                        }
444
 
                    }
445
 
                    stack.pop();
446
 
                }
447
 
            }
448
 
 
449
 
            // Second pass, build row arrays and assign rowspan
450
 
            for (i = 0; i < rowSpan; ++i) {
451
 
                columns.push([]);
452
 
            }
453
 
 
454
 
            stack.push([data, -1]);
455
 
 
456
 
            while (stack.length) {
457
 
                entry = stack[stack.length - 1];
458
 
                row   = entry[0];
459
 
                i     = entry[1] + 1;
460
 
 
461
 
                for (len = row.length; i < len; ++i) {
462
 
                    col = row[i];
463
 
                    children = col.children;
464
 
 
465
 
                    columns[stack.length - 1].push(col);
466
 
 
467
 
                    entry[1] = i;
468
 
 
469
 
                    // collect the IDs of parent cols
470
 
                    col._headers = [col.id];
471
 
 
472
 
                    for (j = stack.length - 2; j >= 0; --j) {
473
 
                        parent = stack[j][0][stack[j][1]];
474
 
 
475
 
                        col._headers.unshift(parent.id);
476
 
                    }
477
 
 
478
 
                    if (children && children.length) {
479
 
                        // parent cells must assume rowspan 1 (long story)
480
 
 
481
 
                        // break to let the while loop process the children
482
 
                        stack.push([children, -1]);
483
 
                        break;
484
 
                    } else {
485
 
                        col._rowspan = rowSpan - stack.length + 1;
486
 
                    }
487
 
                }
488
 
 
489
 
                if (i >= len) {
490
 
                    // All columns in this row are processed
491
 
                    stack.pop();
492
 
                }
493
 
            }
494
 
        }
495
 
 
496
 
        for (i = 0, len = columns.length; i < len; i += col._rowspan) {
497
 
            col = columns[i][0];
498
 
 
499
 
            col._first = true;
500
 
        }
501
 
 
502
 
        return columns;
503
 
    }
504
 
});
505
 
 
506
 
 
507
 
}, '3.5.1' ,{requires:['datatable-core', 'view', 'classnamemanager']});