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