src/cm/media/js/lib/yui/yui_3.10.3/build/datatable-body/datatable-body.js
changeset 525 89ef5ed3c48b
equal deleted inserted replaced
524:322d0feea350 525:89ef5ed3c48b
       
     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"]});