diff -r 322d0feea350 -r 89ef5ed3c48b src/cm/media/js/lib/yui/yui_3.10.3/docs/datatable/migration.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cm/media/js/lib/yui/yui_3.10.3/docs/datatable/migration.html Tue Jul 16 14:29:46 2013 +0200 @@ -0,0 +1,1185 @@ + + + + + 3.5.0+ Migration Guide + + + + + + + + + + +
+
+

+
+ + Jump to Table of Contents + + +

3.5.0+ Migration Guide

+
+
+
+
+
+

+ DataTable and supporting modules were rebuilt in version 3.5.0. The + new architecture is not fully backward compatible with + versions 3.4.1 and prior. This guide is to help answer questions that + may come up when upgrading to the latest version. +

+ +

+ This guide focuses on 3.4.1 API compatibility. It does not + describe new features added in 3.5.0 (there were a lot). + Refer to the updated DataTable user guide for + that. +

+ +

+ If you are unable to upgrade due to unresolvable issues, you can use the + datatable-deprecated + module suite, which is equivalent to the 3.4.1 implementation. But be + aware that these modules will be removed in a future version of YUI. +

+
+ +

Overview of API changes from 3.4.1

+ +

+ The architectural change resulted in the deprecation, replacement, or + removal of nearly all attributes and properties from the version 3.4.1 + implementation. Here is a quick list of the changes most likely to affect + your upgrade: +

+ +
    +
  1. + new Y.DataTable.Base(...) → + new Y.DataTable({ ... }) +
  2. +
  3. +
    +
    +
    new Y.DataTable.Base({
    +    columnset: [ ... ],
    +    recordset: [ ... ]
    +});
    + +
    +

    +
    +
    new Y.DataTable({
    +    columns: [ ... ],
    +    data   : [ ... ]
    +});
    + +
    +
    +
  4. +
  5. + (cells rendered as HTML by default) → + columns: [ { key: 'email', allowHTML: true }, ... ] +
  6. +
  7. + table.plug(Y.Plugin.DataTableSort)(plugin not needed) + See below or read + the user guide +
  8. +
  9. + table.plug(Y.Plugin.DataTableScroll, ...) → + (plugin not needed) + See below or read + the user guide +
  10. +
  11. + columnset: [ { formatter: function (o) { ... } } ] + → + (formatter changes) + See below or read + the user guide +
  12. +
  13. + record.getValue(fieldName) → + record.get(fieldName) +
  14. +
+ +

Instantiation and Instance Configuration Changes

+ +

+ As of 3.5.0, Y.DataTable is no longer just a namespace, but is now the + preferred constructor for DataTable instances. +

+ +
var table = new Y.DataTable({
+    // Column configuration looks much the same except the attribute name
+    columns: [
+        { key: 'name', label: 'Name', sortable: true, width: '200px' },
+        {
+            key: 'birthdate',
+            label: 'Age',
+            sortable: true,
+            formatter: function (o) {
+                var now = new Date(),
+                    years = now.getYear() - o.value.getYear();
+
+                now.setYear(o.value.getYear());
+
+                return years - (o.value < now);
+            }
+        }
+    ],
+    // Passing in row data looks much the same except the attribute name
+    data: [
+        { name: 'Tom Brokaw',     birthdate: new Date(1940, 1, 6) },
+        { name: 'Peter Jennings', birthdate: new Date(1938, 6, 29) },
+        { name: 'Katie Couric',   birthdate: new Date(1957, 1, 7) },
+        { name: 'Brian Williams', birthdate: new Date(1958, 4, 5) },
+        { name: 'Matt Lauer',     birthdate: new Date(1957, 11, 30) }
+    ]
+}).render('#over-there');
+ + +

+ The Y.DataTable.Base class still exists, but is useful primarily as a + superclass for extension. The attributes of Y.DataTable.Base are the + same as those of Y.DataTable, less any attributes added by class + extensions (see below). +

+ +

+ Configuration attributes that have changed from 3.4.1 are: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AttributeChange
columnset + Deprecated. Use columns. columnset will + behave as an alias for columns for a short time, but will be + removed in future versions. See below. +
recordset + Deprecated. Use data. recordset will + behave as an alias for data for a short time, but will be + removed in future versions. See below. +
tdValueTemplate + Removed. Use column formatter, cellTemplate, + or override the Y.DataTable.BodyView instance's CELL_TEMPLATE. +
thValueTemplate + Removed. Use column label, headerTemplate, + or override the Y.DataTable.HeaderView instance's CELL_TEMPLATE. +
trTemplate + Removed. Use column nodeFormatter or override + the Y.DataTable.BodyView instance's ROW_TEMPLATE. +
+ +

Table and Cell Formatting Changes

+ +

+ The following changes apply to table and column cell formatting: +

+ +
    +
  • + If cell values include HTML, add allowHTML: true to the column + configuration. HTML is escaped by default for security. +
  • +
  • + o.classnames in the formatter parameter is now o.className. +
  • +
  • + o.column in the formatter parameter is now the + plain JavaScript object containing the column configurations rather + than a Y.Column instance. +
  • +
  • + o.record in the formatter parameter is now a Model + instance instead of a Y.Record instance. +
  • +
  • + this.createCell(o), o.td, o.tr, and o.tbody no longer exist on + the parameter passed to formatter functions. Use + nodeFormatters. +
  • +
  • + o.headers is now at o.column._headers, but is read-only for + formatter functions. If you need to change the cell's headers + attribute, add a {placeholder} for them to a custom cellTemplate for + the column, or use a nodeFormatter. +
  • +
  • + The table's tdValueTemplate, thValueTemplate, and trTemplate no + longer exist, nor do the DataTable instance properties tdTemplate and + thTemplate. Use formatter strings or functions to customize the + content of data cells in a column and label strings to customize the + content of header cells. To modify the <td> or <th> entirely, set + the column configuration cellTemplate or headerTemplate. +
  • +
+ +
+
+

3.4.1

+ +
var table = new Y.DataTable.Base({
+    columnset: [
+        { key: "id",
+          emptyCellValue: "<em>new</em>" },
+        { key: "name" },
+        { key: "price", formatter: "${value}" },
+        { key: "price",
+          formatter: function (o) {
+            if (o.value > 4) {
+                o.classnames += "spendy";
+            }
+            return '$' + o.value.toFixed(2);
+          }
+        },
+        { key: "price",
+          formatter: function (o) {
+            var cell = this.createCell(o);
+
+            if (o.value > 4) {
+                cell.addClass('spendy');
+            }
+
+            cell.setHTML('$' + o.value);
+          }
+        }
+    ],
+    data: [
+        { id: 1, name: "Bread", price: 3.45 },
+        { id: 2, name: "Milk",  price: 4.99 },
+        { id: 3, name: "Eggs",  price: 2.75 }
+    ]
+}).render("#over-there");
+ +
+
+

3.5.0

+ +
var table = new Y.DataTable({
+    columns: [
+        { key: "id",
+          emptyCellValue: "<em>new</em>",
+          allowHTML: true },
+        { key: "name" },
+        { key: "price", formatter: "${value}" },
+        { key: "price",
+          formatter: function (o) {
+            if (o.value > 4) {
+                o.className += "spendy";
+            }
+            return '$' + o.value.toFixed(2);
+          }
+        },
+        { key: "price",
+          nodeFormatter: function (o) {
+            if (o.value > 4) {
+                o.cell.addClass('spendy');
+            }
+
+            o.cell.setHTML('$' + o.value);
+
+            return false;
+          }
+        }
+    ],
+    data: [
+        { id: 1, name: "Bread", price: 3.45 },
+        { id: 2, name: "Milk",  price: 4.99 },
+        { id: 3, name: "Eggs",  price: 2.75 }
+    ]
+}).render("#over-there");
+ +
+
+ +

+ Read the Formatting Cell Data section in + the DataTable user guide for more details. +

+ +

Column Configuration Changes

+ +

+ As of 3.5.0, the Y.Columnset and Y.Column classes have been deprecated. + The DataTable configuration attribute columnset has been deprecated in + favor of the columns attribute. The columnset attribute has been + retained for partial backward compatibility. Columns are now + stored as an array of simple JavaScript objects rather than class instances. +

+ +
var table = new Y.DataTable({
+    columnset: [ 'name', 'age' ],
+    ...
+});
+
+// columnset passes through to columns
+var columns = table.get('columns'); // => Array, not Columnset instance
+
+table.set('columnset', [ ... (new column configurations) ... ]);
+
+// backward compatibility stops here
+var columnset = table.get('columnset'); // => Array, not Columnset instance
+ + +

+ Strings passed into the column configuration will become objects with those + strings as the value of the key property. +

+ +

+ See the Column configuration section in + the user guide for more details. +

+ +

Row Data Configuration Changes

+ +

+ As of 3.5.0, DataTable uses Y.Model and Y.ModelList to store row data. + Y.Recordset and Y.Record haven't been deprecated, but may be in the + future. The recordset attribute has been retained for partial + backward compatibility. The data ModelList can be assigned to, but + retrieving the value of the attribute will return the ModelList, not + a Y.Recordset instance. +

+ +
var table = new Y.DataTable({
+    columnset: [ ... ],
+    recordset: [
+        { name: 'Tom Brokaw',     birthdate: new Date(1940, 1, 6) },
+        { name: 'Peter Jennings', birthdate: new Date(1938, 6, 29) },
+        { name: 'Katie Couric',   birthdate: new Date(1957, 1, 7) },
+        ...
+    ]
+});
+
+// recordset passes through to data.
+var data = table.get('data'); // => ModelList instance
+
+table.set('recordset', [ ... (new data records) ... ]);
+
+// backward compatibility stops here
+var recordset = table.get('recordset'); // => ModelList, not Recordset
+ + +

+ Y.Record stores all values in a single attribute named data, where Y.Model uses individual attributes for each value. +

+ +
+
+

3.4.1

+ +
// Y.Record
+var record = oldTable.get('recordset').item(0);
+
+record.getValue('birthdate'); // => Date(1940, 1, 6)
+record.get('data').birthdate; // => same
+ +
+
+

3.5.0

+
// Y.Model
+var model = newTable.getRecord(0);
+
+model.get('birthdate'); // => Date(1940, 1, 6)
+ +
+
+ +

+ This change breaks column/record keys that contain periods. + Attribute treats periods as subproperty indicators, so periods are no + longer allowed; use an alternate character. In the case of remote data + that is parsed by Y.Plugin.DataSourceJSONSchema, use the locator + configuration for fields to extract subproperty values. A benefit to doing + this is that you may not need to specify a column label. +

+ +
+
+

3.4.1

+ +
var table = new Y.DataTable.Base({
+    columnset: [
+        { key: "id" },
+        { key: "Product.Name",  label: "Product" },
+        { key: "Product.Price", label: "Price" }
+    ],
+    caption: "Price List"
+}).plug(Y.Plugin.DataTableDataSource, {
+    datasource: new Y.DataSource.IO({
+        source: "/service/price-list"
+    }).plug(Y.Plugin.DataSourceJSONSchema, {
+        schema: {
+            resultListLocator: "items",
+            resultFields: [
+                { key: "id" },
+                { key: "Product.Name" },
+                { key: "Product.Price" }
+            ]
+        }
+    })
+});
+
+table.datasource.load(...);
+ +
+
+

3.5.0

+ +
var table = new Y.DataTable({
+    columns: [ "id", "Product", "Price" ],
+    caption: "Price List"
+}).plug(Y.Plugin.DataTableDataSource, {
+    datasource: new Y.DataSource.IO({
+        source: "/service/price-list"
+    }).plug(Y.Plugin.DataSourceJSONSchema, {
+        schema: {
+            resultListLocator: "items",
+            resultFields: [
+                { key: "id" },
+                { key: "Product",
+                  locator: "Product.Name" },
+                { key: "Price",
+                  locator: "Product.Price" }
+            ]
+        }
+    })
+});
+
+table.datasource.load(...);
+ +
+
+ +

+ If you are using any Recordset plugins, your code will need to be modified. + Some loss of functionality may result. +

+ +
+
Y.Plugin.RecordsetSort
+
+ This plugin was formerly applied by the Y.Plugin.DataTableSort plugin + to the DataTable's Recordset instance. Sorting is now enabled + through a class extension. +
+ +
Y.Plugin.RecordsetFilter
+
+ The default ModelList implementation only supports a filter(function) + method. If you were relying on this plugin's grep or reject + methods or the filter(key, value) functionality, you will need to + replace that functionality through custom code. +
+ +
Y.Plugin.RecordsetIndexer
+
+ The default ModelList implementation does not support multiple custom + indexes, though it does maintain an index for id (or another, + assigned primary key attribute) and clientId. See + the Model user guide for more + information on customizing the primary index. If multiple custom + indexes are required, DataTable supports the use of + custom Model subclasses to store the record + data. +
+ + +

+ See the Data Configuration section of the DataTable + user guide for more information. +

+ +

Feature Configuration Changes

+ +

+ The two optional features available for DataTable in 3.4.1 were sorting and + scrolling. Both features exist in 3.5.0, but are implemented as class + extensions for Y.DataTable. Simply including the datatable-sort or + datatable-scroll module in your use(...) will enable the feature for + all new DataTables. +

+ +
YUI().use('datatable-sort', 'datatable-scroll', function (Y) {
+
+    // Create a DataTable that is sortable by the "name" column, and is
+    // configured to scroll vertically within 300px.  Because scrollable is
+    // set to "y", not "x" or "xy", it will not attempt to scroll horizontally.
+    // Instead the table width will be set to 100%.
+    var table = new Y.DataTable({
+        columns   : [ { key: 'name', sortable: true }, ... ],
+        data      : [ ... ],
+        scrollable: "y",
+        height    : "300px",
+        width     : "100%"
+    });
+
+    // No plugins necessary
+    table.render('#over-there');
+});
+ + +

Column Sorting Changes

+ +

+ Configuring sortable columns may be done as it was in 3.4.1, by setting the + column configuration property sortable: true, but may also be done by + setting the DataTable's sortable configuration to true or an array of + column names. +

+ +
+
+

3.4.1

+ +
// Assumes use('datatable-sort') or use('datatable')
+var table = new Y.DataTable.Base({
+    columnset: [
+        { key: "id" },
+        { key: "name", sortable: true },
+        { key: "price", sortable: true }
+    ],
+    recordset: [
+        { id: 1, name: "Bread", price: 3.45 },
+        { id: 2, name: "Milk",  price: 4.99 },
+        { id: 3, name: "Eggs",  price: 2.75 },
+        ...
+    ],
+    caption: "Price List"
+});
+
+table.plug(Y.Plugin.DataTableSort);
+
+table.render('#sort-demo');
+
+// Sorting API is on the Recordset's plugin
+table.get("recordset").sort.sort("name");
+ +
+
+

3.5.0

+ +
// Assumes use('datatable-sort') or use('datatable')
+var table = new Y.DataTable({
+    columns: [ "id", "name", "price" ],
+    data: [
+        { id: 1, name: "Bread", price: 3.45 },
+        { id: 2, name: "Milk",  price: 4.99 },
+        { id: 3, name: "Eggs",  price: 2.75 },
+        ...
+    ],
+    sortable: [ "name", "price" ]
+});
+
+table.render('#sort-demo');
+
+// Sort method is on the instance
+table.sort("name");
+
+//-------------------------------------------------
+// Alternate methods
+//-------------------------------------------------
+
+var table = new Y.DataTable({
+    columns: [ "id", "name", "price" ],
+    data: [ ... ],
+    sortable: true // makes all columns sortable
+});
+
+// OR the old way works, too
+var table = new Y.DataTable({
+    columns: [
+        { key: "id" },
+        { key: "name", sortable: true },
+        { key: "price", sortable: true }
+    ],
+    data: [ ... ]
+});
+ +
+
+ +

+ Since there is no plugin, the sort method is now on the DataTable instance + itself, as are the related attributes. In particular, the lastSortedBy + attribute from the plugin implementation has been replaced by the sortBy + attribute added by the class extension. +

+ +

+ As of 3.5.0, DataTables configured with sortBy will have their rows + sorted automatically upon inserting into the table. You do not need to + presort data for the initial render. +

+ +

+ The trigger attribute of the sorting plugin was not retained in + the 3.5.0 class extension. If you require an alternate triggering + event, detach and replace the table's _sortHandle property after + render(). +

+ +
var table = new Y.DataTable({ ... }).render("#over-there");
+
+table._sortHandle.detach();
+table._sortHandle = table.delegate(["dblclick", "keydown"],
+    table._onUITriggerSort, ".yui3-datatable-sortable-column", table);
+ + +

+ See the Column Sorting section of the + user guide for details about the APIs and attributes. +

+ +

Scrollable Table Changes

+ +

+ Like sorting, the scrolling functionality has been moved to a class + extension, making it unnecessary to plug the DataTable instance with the + (now deprecated) Y.Plugin.DataTableScroll plugin. +

+ +

+ datatable-scroll is no longer included in the datatable + rollup, and must be explicitly included in your use() statement. +

+ +

+ To enable scrolling in 3.5.0, set the table's scrollable attribute to "x", + "y", or "xy". The configured height and width for the DataTable are + used to bound the overall widget dimesions. Scrolling will only be applied + on the axis or axes specified in scrollable. However, if scrollable is + set to "y", but the height isn't set, it will not be made scrollable. + Likewise for "x" and width. +

+ +
+
+

3.4.1

+ +
// Assumes use("datatable-scroll") or use("datatable")
+var table = new Y.DataTable.Base({
+    columnset: ["id", "name", "price"],
+    recordset: [
+        { id: 1, name: "Bread", price: 3.45 },
+        { id: 2, name: "Milk",  price: 4.99 },
+        { id: 3, name: "Eggs",  price: 2.75 },
+        ...
+    ]
+});
+
+table.plug(Y.Plugin.DataTableScroll, {
+    height: "175px"
+});
+
+table.render("#over-there");
+ +
+
+

3.5.0

+ +
// Assumes use("datatable-scroll")
+var table = new Y.DataTable({
+    columns: ["id", "name", "price"],
+    data: [
+        { id: 1, name: "Bread", price: 3.45 },
+        { id: 2, name: "Milk",  price: 4.99 },
+        { id: 3, name: "Eggs",  price: 2.75 },
+        ...
+    ],
+    scrollable: "y",
+    height: "175px"
+}).render("#over-there");
+ +
+
+ +

Markup and CSS Changes

+ +

+ DataTable in 3.5.0 applies more CSS classes to Nodes, stamps fewer nodes + with guid ids, and does not render header and cell liner <div>s. +

+ +

+ Below are examples of the same table rendered in 3.4.1 and 3.5.0. The 3.5.0 + table has comments indicating markup added by feature modules. +

+ +

3.4.1

+
<div id="(guid)" class="yui3-widget yui3-datatable">
+  <div id="(guid)" class="yui3-datatable-content">
+    <table>
+
+      <caption>
+        Example table with simple columns
+      </caption>
+
+      <colgroup>
+        <col>
+        <col>
+        <col>
+      </colgroup>
+
+    <thead class="yui3-datatable-columns">
+      <tr id="" class="yui3-datatable-first yui3-datatable-last">
+        <th id="(guid)" rowspan="1" colspan="1" class="yui3-column-id" abbr="">
+          <div class="yui3-datatable-liner">
+            id
+          </div>
+        </th>
+        <th id="(guid)" rowspan="1" colspan="1" class="yui3-column-name" abbr="">
+          <div class="yui3-datatable-liner">
+            name
+          </div>
+        </th>
+        <th id="(guid)" rowspan="1" colspan="1" class="yui3-column-price" abbr="">
+          <div class="yui3-datatable-liner">
+            price
+          </div>
+        </th>
+      </tr>
+    </thead>
+
+    <tbody class="yui3-datatable-msg">
+    </tbody>
+
+    <tbody class="yui3-datatable-data" id="(guid)">
+      <tr id="(guid)" class="yui3-datatable-even">
+        <td headers="(guid)" class="yui3-column-id">
+          <div class="yui3-datatable-liner">
+            1
+          </div>
+        </td>
+        <td headers="(guid)" class="yui3-column-name">
+          <div class="yui3-datatable-liner">
+            Bread
+          </div>
+        </td>
+        <td headers="(guid)" class="yui3-column-price">
+          <div class="yui3-datatable-liner">
+            3.45
+          </div>
+        </td>
+      </tr>
+      <tr id="(guid)" class="yui3-datatable-odd">
+        <td headers="(guid)" class="yui3-column-id">
+          <div class="yui3-datatable-liner">
+            2
+          </div>
+        </td>
+        <td headers="(guid)" class="yui3-column-name">
+          <div class="yui3-datatable-liner">
+            Milk
+          </div>
+        </td>
+        <td headers="(guid)" class="yui3-column-price">
+          <div class="yui3-datatable-liner">
+            4.99
+          </div>
+        </td>
+      </tr>
+      <tr id="(guid)" class="yui3-datatable-even">
+        <td headers="(guid)" class="yui3-column-id">
+          <div class="yui3-datatable-liner">
+            3
+          </div>
+        </td>
+        <td headers="(guid)" class="yui3-column-name">
+            <div class="yui3-datatable-liner">
+              Eggs
+            </div>
+          </td>
+          <td headers="(guid)" class="yui3-column-price">
+            <div class="yui3-datatable-liner">
+              2.75
+            </div>
+          </td>
+        </tr>
+      </tbody>
+
+    </table>
+  </div>
+</div>
+ + +

3.5.0

+
<div id="(guid)" class="yui3-widget yui3-datatable">
+  <div id="(guid)" class="yui3-datatable-content">
+    <table cellspacing="0" class="yui3-datatable-table" id="(guid)">
+
+      <caption class="yui3-datatable-caption">
+        Price List
+      </caption>
+  
+      <!-- colgroup only renders if datatable-column-widths is use()d.
+           Note, datatable-column-widths is included in the datatable rollup -->
+      <colgroup id="(guid)">
+        <col>
+        <col>
+        <col>
+      </colgroup>
+  
+      <thead class="yui3-datatable-columns" id="(guid)">
+        <tr>
+          <th id="(guid)" colspan="1" rowspan="1" class="yui3-datatable-header yui3-datatable-first-header yui3-datatable-col-id" scope="col" data-yui3-col-id="id">
+            id
+          </th>
+          <th id="(guid)" colspan="1" rowspan="1" class="yui3-datatable-header yui3-datatable-col-name" scope="col" data-yui3-col-id="name">
+            name
+          </th>
+          <th id="(guid)" colspan="1" rowspan="1" class="yui3-datatable-header yui3-datatable-col-price" scope="col" data-yui3-col-id="price">
+            price
+          </th>
+        </tr>
+      </thead>
+
+      <!-- The message tbody only renders if datatable-message is use()d.
+           Note, datatable-message is included in the datatable rollup -->
+      <tbody class="yui3-datatable-message" id="(guid)">
+        <tr>
+          <td class="yui3-datatable-message-content" colspan="3">
+            No data to display
+          </td>
+        </tr>
+      </tbody>
+
+      <tbody class="yui3-datatable-data">
+        <tr id="(guid)" data-yui3-record="record_1" class="yui3-datatable-even">
+          <td class="yui3-datatable-col-id  yui3-datatable-cell">
+            1
+          </td>
+          <td class="yui3-datatable-col-name  yui3-datatable-cell">
+            Bread
+          </td>
+          <td class="yui3-datatable-col-price  yui3-datatable-cell">
+            3.45
+          </td>
+        </tr>
+        <tr id="(guid)" data-yui3-record="record_2" class="yui3-datatable-odd">
+          <td class="yui3-datatable-col-id  yui3-datatable-cell">
+            2</td>
+          <td class="yui3-datatable-col-name  yui3-datatable-cell">
+            Milk
+          </td>
+          <td class="yui3-datatable-col-price  yui3-datatable-cell">
+            4.99
+          </td>
+        </tr>
+        <tr id="(guid)" data-yui3-record="record_3" class="yui3-datatable-even">
+          <td class="yui3-datatable-col-id  yui3-datatable-cell">
+            3
+          </td>
+          <td class="yui3-datatable-col-name  yui3-datatable-cell">
+            Eggs
+          </td>
+          <td class="yui3-datatable-col-price  yui3-datatable-cell">
+            2.75
+          </td>
+        </tr>
+      </tbody>
+
+    </table>
+  </div>
+</div>
+ + +

What Did I Miss?

+ +

+ Obviously, there were a lot of changes to DataTable from 3.4.1 to 3.5.0. + It's entirely likely that I have missed something here. If you experience + trouble with your upgrade and find this migration guide is missing + something important, please file a + ticket and I'll update it as soon as possible. +

+ +

+ Additional resources to help with the upgrade include the + forums, and the #yui IRC channel on freenode.net. +

+
+
+
+ +
+ +
+
+
+ + + + + + + + + + +