API Docs for: 1.0.0
Show:

File: src/gallery-quickedit/js/QuickEdit.js

  1. "use strict";
  2.  
  3. /**
  4. * @module gallery-quickedit
  5. */
  6.  
  7. /**
  8. * <p>The QuickEdit plugin provides a new mode for DataTable where all
  9. * values in the table can be edited simultaneously, controlled by the
  10. * column configuration. Each editable cell contains an input field. If
  11. * the user decides to save the changes, then you can extract the changed
  12. * values by calling <code><i>dt</i>.qe.getChanges()</code>.</p>
  13. *
  14. * <p>For a column to be editable in QuickEdit mode, the column
  15. * configuration must include <code>quickEdit</code>. The contents of
  16. * this object define the column's behavior in QuickEdit mode.</p>
  17. *
  18. * <p>To move up or down within a column while in QuickEdit mode, hold down
  19. * the Ctrl key and press the up or down arrow.</p>
  20. *
  21. * <p>If a column should not be editable, but needs to be formatted
  22. * differently in QuickEdit mode, then you must define qeFormatter in
  23. * the column configuration. This is simply a normal cell formatter
  24. * function that will be used in QuickEdit mode. The static functions
  25. * <code>readonly*Formatter</code> provide examples.</p>
  26. *
  27. * <p>The following configuration can be provided as part of
  28. * quickEdit:</p>
  29. *
  30. * <dl>
  31. *
  32. * <dt>changed</dt><dd>Optional. The function to call with the old and new
  33. * value. Should return true if the values are different.</dd>
  34. *
  35. * <dt>formatter</dt><dd>The cell formatter which will render an
  36. * appropriate form field: &lt;input type="text"&gt;, &lt;textarea&gt;,
  37. * or &lt;select&gt;.</dd>
  38. *
  39. * <dt>validation</dt><dd>Validation configuration for every field in
  40. * the column.</dd>
  41. *
  42. * <dt>copyDown</dt><dd>If true, the top cell in the column will have a
  43. * button to copy the value down to the rest of the rows.</dd>
  44. *
  45. * </dl>
  46. *
  47. * <p>The following configuration can be provided as part of
  48. * quickEdit.validation:</p>
  49. *
  50. * <dl>
  51. *
  52. * <dt>css</dt><dd>CSS classes encoding basic validation rules:
  53. * <dl>
  54. * <dt><code>yiv-required</code></dt>
  55. * <dd>Value must not be empty.</dd>
  56. *
  57. * <dt><code>yiv-length:[x,y]</code></dt>
  58. * <dd>String must be at least x characters and at most y characters.
  59. * At least one of x and y must be specified.</dd>
  60. *
  61. * <dt><code>yiv-integer:[x,y]</code></dt>
  62. * <dd>The integer value must be at least x and at most y.
  63. * x and y are both optional.</dd>
  64. *
  65. * <dt><code>yiv-decimal:[x,y]</code></dt>
  66. * <dd>The decimal value must be at least x and at most y. Exponents are
  67. * not allowed. x and y are both optional.</dd>
  68. * </dl>
  69. * </dd>
  70. *
  71. * <dt>fn</dt><dd>A function that will be called with the DataTable as its
  72. * scope and the cell's form element as the argument. Return true if the
  73. * value is valid. Otherwise, call this.qe.displayMessage(...) to display
  74. * an error and return false.</dd>
  75. *
  76. * <dt>msg</dt><dd>A map of types to messages that will be displayed
  77. * when a basic or regex validation rule fails. The valid types are:
  78. * required, min_length, max_length, integer, decimal, and regex.
  79. * There is no default for type regex, so you must specify a message if
  80. * you configure a regex validation.</dd>
  81. *
  82. * <dt>regex</dt><dd>Regular expression that the value must satisfy in
  83. * order to be considered valid.</dd>
  84. *
  85. * </dl>
  86. *
  87. * <p>Custom QuickEdit Formatters</p>
  88. *
  89. * <p>To write a custom cell formatter for QuickEdit mode, you must
  90. * structure the function as follows:</p>
  91. *
  92. * <pre>
  93. * function myQuickEditFormatter(o) {
  94. * &nbsp;&nbsp;var markup =
  95. * &nbsp;&nbsp;&nbsp;&nbsp;'&lt;input type="text" class="{yiv} quickedit-field quickedit-key:{key}" value="{value}"/&gt;' +
  96. * &nbsp;&nbsp;&nbsp;&nbsp;'{cd}' + Y.Plugin.DataTableQuickEdit.error_display_markup;
  97. *
  98. * &nbsp;&nbsp;var qe = o.column.quickEdit;
  99. * &nbsp;&nbsp;return Y.Lang.sub(markup, {
  100. * &nbsp;&nbsp;&nbsp;&nbsp;key: o.column.key,
  101. * &nbsp;&nbsp;&nbsp;&nbsp;value: o.value.toString().replace(/"/g, '&quot;'),
  102. * &nbsp;&nbsp;&nbsp;&nbsp;yiv: qe.validation ? (qe.validation.css || '') : '',
  103. * &nbsp;&nbsp;&nbsp;&nbsp;cd: QuickEdit.copyDownFormatter.call(this, o)
  104. * &nbsp;&nbsp;});
  105. * };
  106. * </pre>
  107. *
  108. * <p>You can use textarea or select instead of input, but you can only
  109. * create a single field.</p>
  110. *
  111. * <p><code>extractMyEditableValue</code> does not have to be a separate
  112. * function. The work should normally be done inline in the formatter
  113. * function, but the name of the sample function makes the point clear.</p>
  114. *
  115. * @main gallery-quickedit
  116. * @class DataTableQuickEdit
  117. * @namespace Plugin
  118. * @extends Plugin.Base
  119. * @constructor
  120. * @param config {Object} Object literal to set component configuration.
  121. */
  122. function QuickEdit(config)
  123. {
  124. QuickEdit.superclass.constructor.call(this, config);
  125. }
  126.  
  127. QuickEdit.NAME = "QuickEditPlugin";
  128. QuickEdit.NS = "qe";
  129.  
  130. QuickEdit.ATTRS =
  131. {
  132. /**
  133. * @attribute changesAlwaysInclude
  134. * @description Record keys to always include in result from getChanges().
  135. * @type Array
  136. */
  137. changesAlwaysInclude:
  138. {
  139. value: [],
  140. validator: Y.Lang.isArray
  141. },
  142.  
  143. /**
  144. * @attribute includeAllRowsInChanges
  145. * @description If true, getChanges() returns a record for every row, even if the record is empty. Set to false if you want getChanges() to only return records that contain data.
  146. * @type Boolean
  147. * @default true
  148. */
  149. includeAllRowsInChanges:
  150. {
  151. value: true,
  152. validator: Y.Lang.isBoolean
  153. },
  154.  
  155. /**
  156. * @attribute includeRowIndexInChanges
  157. * @description If true, getChanges() includes the row index in each record, using the _row_index key.
  158. * @type Boolean
  159. * @default false
  160. */
  161. includeRowIndexInChanges:
  162. {
  163. value: false,
  164. validator: Y.Lang.isBoolean
  165. }
  166. };
  167.  
  168. var quick_edit_re = /quickedit-key:([^\s]+)/,
  169. qe_row_status_prefix = 'quickedit-has',
  170. qe_row_status_pattern = qe_row_status_prefix + '([a-z]+)',
  171. qe_row_status_re = new RegExp(Y.Node.class_re_prefix + qe_row_status_pattern + Y.Node.class_re_suffix),
  172. qe_cell_status_prefix = 'quickedit-has',
  173. qe_cell_status_pattern = qe_cell_status_prefix + '([a-z]+)',
  174. qe_cell_status_re = new RegExp(Y.Node.class_re_prefix + qe_cell_status_pattern + Y.Node.class_re_suffix);
  175.  
  176. /**
  177. * The CSS class that marks the container for the error message inside a cell.
  178. *
  179. * @property error_text_class
  180. * @static
  181. * @type {String}
  182. */
  183. QuickEdit.error_text_class = 'quickedit-message-text';
  184.  
  185. /**
  186. * The markup for the container for the error message inside a cell.
  187. *
  188. * @property error_display_markup
  189. * @static
  190. * @type {String}
  191. */
  192. QuickEdit.error_display_markup = '<div class="quickedit-message-text"></div>';
  193.  
  194. /**
  195. * The CSS class that marks the "Copy Down" button inside a cell.
  196. *
  197. * @property copy_down_button_class
  198. * @static
  199. * @type {String}
  200. */
  201. QuickEdit.copy_down_button_class = 'quickedit-copy-down';
  202.  
  203. /**
  204. * Called with exactly the same arguments as any other cell
  205. * formatter, this function displays an input field.
  206. *
  207. * @method textFormatter
  208. * @static
  209. * @param o {Object} standard DataTable formatter data
  210. */
  211. QuickEdit.textFormatter = function(o)
  212. {
  213. var markup =
  214. '<input type="text" class="{yiv} quickedit-field quickedit-key:{key}" value="{value}"/>' +
  215. '{cd}' + QuickEdit.error_display_markup;
  216.  
  217. var qe = o.column.quickEdit;
  218. return Y.Lang.sub(markup,
  219. {
  220. key: o.column.key,
  221. value: o.value.toString().replace(/"/g, '&quot;'),
  222. yiv: qe.validation ? (qe.validation.css || '') : '',
  223. cd: QuickEdit.copyDownFormatter.call(this, o)
  224. });
  225. };
  226.  
  227. /**
  228. * Called with exactly the same arguments as any other cell
  229. * formatter, this function displays a textarea field.
  230. *
  231. * @method textareaFormatter
  232. * @static
  233. * @param o {Object} standard DataTable formatter data
  234. */
  235. QuickEdit.textareaFormatter = function(o)
  236. {
  237. var markup =
  238. '<textarea class="{yiv} quickedit-field quickedit-key:{key}">{value}</textarea>' +
  239. '{cd}' + QuickEdit.error_display_markup;
  240.  
  241. var qe = o.column.quickEdit;
  242. return Y.Lang.sub(markup,
  243. {
  244. key: o.column.key,
  245. value: o.value,
  246. yiv: qe.validation ? (qe.validation.css || '') : '',
  247. cd: QuickEdit.copyDownFormatter.call(this, o)
  248. });
  249. };
  250.  
  251. /**
  252. * Called with exactly the same arguments as any other cell
  253. * formatter, this function displays an email address without the
  254. * anchor tag. Use this as the column's qeFormatter if the column
  255. * should not be editable in QuickEdit mode.
  256. *
  257. * @method readonlyEmailFormatter
  258. * @static
  259. * @param o {Object} standard DataTable formatter data
  260. */
  261. QuickEdit.readonlyEmailFormatter = function(o)
  262. {
  263. return (o.value || ''); // don't need to check for zero
  264. };
  265.  
  266. /**
  267. * Called with exactly the same arguments as any other cell
  268. * formatter, this function displays a link without the anchor tag.
  269. * Use this as the column's qeFormatter if the column should not be
  270. * editable in QuickEdit mode.
  271. *
  272. * @method readonlyLinkFormatter
  273. * @static
  274. * @param o {Object} standard DataTable formatter data
  275. */
  276. QuickEdit.readonlyLinkFormatter = function(o)
  277. {
  278. return (o.value || ''); // don't need to check for zero
  279. };
  280.  
  281. /*
  282. * Copy value from first cell to all other cells in the column.
  283. *
  284. * @method copyDown
  285. * @private
  286. * @param e {Event} triggering event
  287. */
  288. function copyDown(e)
  289. {
  290. var cell = e.currentTarget.ancestor('.yui3-datatable-cell');
  291. var field = cell.one('.quickedit-field');
  292. if (!field)
  293. {
  294. return;
  295. }
  296.  
  297. var value = Y.Lang.trim(field.get('value'));
  298. if (!value && value !== 0)
  299. {
  300. return;
  301. }
  302.  
  303. while (1)
  304. {
  305. cell = this.getCell(cell, 'below');
  306. if (!cell)
  307. {
  308. break;
  309. }
  310.  
  311. field = cell.one('.quickedit-field');
  312. if (field)
  313. {
  314. field.set('value', value);
  315. }
  316. }
  317. }
  318.  
  319. /**
  320. * Inserts a "Copy down" button if the cell is in the first row of the
  321. * DataTable. Call this at the end of your QuickEdit formatter.
  322. *
  323. * @method copyDownFormatter
  324. * @static
  325. * @param o {Object} cell formatter object
  326. * @param td {Node} cell
  327. */
  328. QuickEdit.copyDownFormatter = function(o, td)
  329. {
  330. if (o.column.quickEdit.copyDown && o.rowIndex === 0)
  331. {
  332. return Y.Lang.sub('<button type="button" title="Copy down" class="{c}">&darr;</button>',
  333. {
  334. c: QuickEdit.copy_down_button_class
  335. });
  336. }
  337. else
  338. {
  339. return '';
  340. }
  341. };
  342.  
  343. function wrapFormatter(editFmt, origFmt)
  344. {
  345. return function(o)
  346. {
  347. if (!o.record && Y.Lang.isString(origFmt))
  348. {
  349. return origFmt;
  350. }
  351. else
  352. {
  353. return (o.record ? editFmt : origFmt).apply(this, arguments);
  354. }
  355. };
  356. }
  357.  
  358. /*
  359. * Shift the focus up/down within a column.
  360. */
  361. function moveFocus(e)
  362. {
  363. var cell = this.getCell(e.target, e.charCode == 38 ? 'above' : 'below');
  364. if (cell)
  365. {
  366. var input = cell.one('.quickedit-field');
  367. if (input)
  368. {
  369. input.focus();
  370. input.select();
  371. e.halt(true);
  372. }
  373. }
  374. }
  375.  
  376. /*
  377. * Parse the column configuration for easy lookup.
  378. */
  379. function parseColumns()
  380. {
  381. var forest = this.get('host').get('columns');
  382. var map = {};
  383.  
  384. function accumulate(list, node)
  385. {
  386. if (Y.Lang.isString(node))
  387. {
  388. var col = { key: node };
  389. list.push(col);
  390. map[node] = col;
  391. }
  392. else if (node.children)
  393. {
  394. list = Y.reduce(node.children, list, accumulate);
  395. }
  396. else
  397. {
  398. list.push(node);
  399. map[ node.key ] = node;
  400. }
  401.  
  402. return list;
  403. }
  404.  
  405. this.column_list = Y.reduce(forest, [], accumulate);
  406. this.column_map = map;
  407. }
  408.  
  409. /*
  410. * Validate the given form fields.
  411. *
  412. * @method validateElements
  413. * @private
  414. * @param e {Array} Array of form fields.
  415. * @return {boolean} true if all validation checks pass
  416. */
  417. function validateElements(
  418. /* NodeList */ list)
  419. {
  420. var host = this.get('host');
  421.  
  422. var status = true;
  423. var count = list.size();
  424. for (var i=0; i<count; i++)
  425. {
  426. var e = list.item(i);
  427. if (!Y.DOM.hasClass(e, 'quickedit-field'))
  428. {
  429. continue;
  430. }
  431.  
  432. var qe = this.column_map[ this._getColumnKey(e) ].quickEdit;
  433. if (!qe)
  434. {
  435. continue;
  436. }
  437. var msg_list = qe.validation ? qe.validation.msg : null;
  438.  
  439. var info = Y.FormManager.validateFromCSSData(e, msg_list);
  440. if (info.error)
  441. {
  442. this.displayMessage(e, info.error, 'error');
  443. status = false;
  444. continue;
  445. }
  446.  
  447. if (info.keepGoing)
  448. {
  449. if (qe.validation &&
  450. qe.validation.regex instanceof RegExp &&
  451. !qe.validation.regex.test(e.get('value')))
  452. {
  453. this.displayMessage(e, msg_list ? msg_list.regex : null, 'error');
  454. status = false;
  455. continue;
  456. }
  457. }
  458.  
  459. if (qe.validation &&
  460. Y.Lang.isFunction(qe.validation.fn) &&
  461. !qe.validation.fn.call(host, e))
  462. {
  463. status = false;
  464. continue;
  465. }
  466. }
  467.  
  468. return status;
  469. }
  470.  
  471. Y.extend(QuickEdit, Y.Plugin.Base,
  472. {
  473. initializer: function(config)
  474. {
  475. var host = this.get('host');
  476.  
  477. this.hasMessages = false;
  478.  
  479. parseColumns.call(this);
  480. this.get('host').after('columnsChange', parseColumns, this);
  481.  
  482. var h = this.afterHostEvent('render', function()
  483. {
  484. host.get('boundingBox').delegate('click', copyDown, '.'+QuickEdit.copy_down_button_class, host);
  485. h.detach();
  486. });
  487. },
  488.  
  489. /**
  490. * Switch to QuickEdit mode. Columns that have quickEdit defined will
  491. * be editable. If the table has paginators, you must hide them.
  492. *
  493. * @method start
  494. */
  495. start: function()
  496. {
  497. this.fire('clearErrorNotification');
  498.  
  499. var host = this.get('host');
  500. this.saveSort = [];
  501. this.saveEdit = [];
  502. this.saveFmt = {};
  503. for (var i=0; i<this.column_list.length; i++)
  504. {
  505. var col = this.column_list[i];
  506. var key = col.key;
  507. this.saveSort.push(col.sortable);
  508. col.sortable = false;
  509. // this.saveEdit.push(col.editor);
  510. // col.editor = null;
  511.  
  512. var qe = col.quickEdit,
  513. qef = col.qeFormatter;
  514. if (/* !col.hidden && */ (qe || qef))
  515. {
  516. var fn = null;
  517. if (qe && Y.Lang.isFunction(qe.formatter))
  518. {
  519. fn = qe.formatter;
  520. }
  521. else if (Y.Lang.isFunction(qef))
  522. {
  523. fn = qef;
  524. }
  525. else
  526. {
  527. fn = QuickEdit.textFormatter;
  528. }
  529.  
  530. if (fn)
  531. {
  532. this.saveFmt[key] =
  533. {
  534. formatter: col.formatter,
  535. nodeFormatter: col.nodeFormatter,
  536. _formatterFn: col._formatterFn,
  537. allowHTML: col.allowHTML
  538. };
  539.  
  540. col.formatter = wrapFormatter.call(this, fn, col.formatter || col.nodeFormatter);
  541. col.nodeFormatter = null;
  542. col.allowHTML = true;
  543. }
  544. }
  545. }
  546.  
  547. var container = host.get('contentBox');
  548. container.addClass(host.getClassName('quickedit'));
  549. this.move_event_handle = container.on('key', moveFocus, 'down:38+ctrl,40+ctrl', host);
  550.  
  551. // trigger re-parsing of columns -- since we saved references to
  552. // the column objects, the original forest has been modified :)
  553. host.set('columns', host.get('columns'));
  554. },
  555.  
  556. /**
  557. * Stop QuickEdit mode. THIS DISCARDS ALL DATA! If you want to save
  558. * the data, call getChanges() BEFORE calling this function. If the
  559. * table has paginators, you must show them.
  560. *
  561. * @method cancel
  562. */
  563. cancel: function()
  564. {
  565. this.fire('clearErrorNotification');
  566.  
  567. for (var i=0; i<this.column_list.length; i++)
  568. {
  569. var col = this.column_list[i];
  570. col.sortable = this.saveSort[i];
  571. // col.editor = this.saveEdit[i];
  572. }
  573. delete this.saveSort;
  574. delete this.saveEdit;
  575.  
  576. Y.each(this.saveFmt, function(fmt, key)
  577. {
  578. var col = this.column_map[key];
  579. col.formatter = fmt.formatter;
  580. col.nodeFormatter = fmt.nodeFormatter;
  581. col._formatterFn = fmt._formatterFn;
  582. col.allowHTML = fmt.allowHTML;
  583. },
  584. this);
  585. delete this.saveFmt;
  586.  
  587. var host = this.get('host');
  588. var container = host.get('contentBox');
  589. container.removeClass(host.getClassName('quickedit'));
  590. if (this.move_event_handle)
  591. {
  592. this.move_event_handle.detach();
  593. delete this.move_event_handle;
  594. }
  595.  
  596. // trigger re-parsing of columns -- since we saved references to
  597. // the column objects, the original forest has been modified :)
  598. host.set('columns', host.get('columns'));
  599. },
  600.  
  601. /**
  602. * Return the changed values. For each row, an object is created with
  603. * only the changed values. The object keys are the column keys. If
  604. * you need values from particular columns to be included always, even
  605. * if the value did not change, include the key `changesAlwaysInclude`
  606. * in the plugin configuration and pass an array of column keys.
  607. * If you need the row indexes, configure `includeRowIndexInChanges`.
  608. *
  609. * If you only want the records with changes, configure
  610. * `includeAllRowsInChanges` to be false. For this to be useful, you
  611. * will need to configure either `changesAlwaysInclude` or
  612. * `includeRowIndexInChanges`.
  613. *
  614. * @method getChanges
  615. * @return {mixed} array of objects if all validations pass, false otherwise
  616. */
  617. getChanges: function()
  618. {
  619. if (!this.validate())
  620. {
  621. return false;
  622. }
  623.  
  624. var changes = [],
  625. always_include = this.get('changesAlwaysInclude'),
  626. include_index = this.get('includeRowIndexInChanges'),
  627. include_all = this.get('includeAllRowsInChanges');
  628.  
  629. var host = this.get('host');
  630. var rows = host._tbodyNode.get('children');
  631. host.get('data').each(function(rec, i)
  632. {
  633. var list = rows.item(i).all('.quickedit-field');
  634.  
  635. var change = {},
  636. changed = false;
  637.  
  638. list.each(function(field)
  639. {
  640. var key = this._getColumnKey(field);
  641. var qe = this.column_map[key].quickEdit;
  642. var prev = rec.get(key);
  643.  
  644. var val = Y.Lang.trim(field.get('value'));
  645. if (qe.changed ? qe.changed(prev, val) :
  646. val !== (prev ? prev.toString() : ''))
  647. {
  648. change[key] = val;
  649. changed = true;
  650. }
  651. },
  652. this);
  653.  
  654. if (changed || include_all)
  655. {
  656. for (var j=0; j<always_include.length; j++)
  657. {
  658. var key = always_include[j];
  659. change[key] = rec.get(key);
  660. }
  661.  
  662. if (include_index)
  663. {
  664. change._row_index = i;
  665. }
  666.  
  667. changes.push(change);
  668. }
  669. },
  670. this);
  671.  
  672. return changes;
  673. },
  674.  
  675. /**
  676. * Validate the QuickEdit data.
  677. *
  678. * @method validate
  679. * @return {boolean} true if all validation checks pass
  680. */
  681. validate: function()
  682. {
  683. this.clearMessages();
  684. var status = true;
  685. var host = this.get('host');
  686.  
  687. var e1 = host._tbodyNode.getElementsByTagName('input');
  688. var e2 = host._tbodyNode.getElementsByTagName('textarea');
  689. var e3 = host._tbodyNode.getElementsByTagName('select');
  690.  
  691. status = validateElements.call(this, e1) && status; // status last to guarantee call
  692. status = validateElements.call(this, e2) && status;
  693. status = validateElements.call(this, e3) && status;
  694.  
  695. if (!status)
  696. {
  697. this.fire('notifyErrors');
  698. }
  699.  
  700. return status;
  701. },
  702.  
  703. /**
  704. * Clear all validation messages in QuickEdit mode.
  705. *
  706. * @method clearMessages
  707. */
  708. clearMessages: function()
  709. {
  710. this.hasMessages = false;
  711.  
  712. this.fire('clearErrorNotification');
  713.  
  714. var host = this.get('host');
  715. host._tbodyNode.getElementsByClassName(qe_row_status_pattern)
  716. .removeClass(qe_row_status_pattern);
  717. host._tbodyNode.getElementsByClassName(qe_cell_status_pattern)
  718. .removeClass(qe_cell_status_pattern);
  719. host._tbodyNode.all('.' + QuickEdit.error_text_class)
  720. .set('innerHTML', '');
  721. },
  722.  
  723. /**
  724. * Display a message for a QuickEdit field. If an existing message with
  725. * a higher precedence is already visible, it will not be replaced.
  726. *
  727. * @method displayMessage
  728. * @param e {Element} form field
  729. * @param msg {String} message to display
  730. * @param type {String} message type: error, warn, success, info
  731. * @param scroll {boolean} If false, does not scroll, even if this is the first message to display.
  732. */
  733. displayMessage: function(
  734. /* element */ e,
  735. /* string */ msg,
  736. /* string */ type,
  737. /* boolean */ scroll)
  738. {
  739. if (Y.Lang.isUndefined(scroll))
  740. {
  741. scroll = true;
  742. }
  743.  
  744. e = Y.one(e);
  745. var row = e.getAncestorByTagName('tr');
  746. if (Y.FormManager.statusTakesPrecedence(this._getElementStatus(row, qe_row_status_re), type))
  747. {
  748. if (!this.hasMessages && scroll)
  749. {
  750. Y.one(row.get('firstChild')).scrollIntoView();
  751. }
  752.  
  753. row.replaceClass(qe_row_status_pattern, qe_row_status_prefix + type);
  754. this.hasMessages = true;
  755. }
  756.  
  757. var cell = e.getAncestorByTagName('td');
  758. if (Y.FormManager.statusTakesPrecedence(this._getElementStatus(cell, qe_cell_status_re), type))
  759. {
  760. if (msg)
  761. {
  762. cell.one('.' + QuickEdit.error_text_class)
  763. .set('innerHTML', msg);
  764. }
  765.  
  766. cell.replaceClass(qe_cell_status_pattern, qe_cell_status_prefix + type);
  767. this.hasMessages = true;
  768. }
  769. },
  770.  
  771. /**
  772. * Return the status of the field.
  773. *
  774. * @method _getElementStatus
  775. * @protected
  776. * @param e {Node} form field
  777. * @param r {RegExp} regex to match against className
  778. * @return {String}
  779. */
  780. _getElementStatus: function(
  781. /* Node */ e,
  782. /* regex */ r)
  783. {
  784. var m = e.get('className').match(r);
  785. return ((m && m.length) ? m[1] : false);
  786. },
  787.  
  788. /**
  789. * Return the column key for the specified field.
  790. *
  791. * @method _getColumnKey
  792. * @protected
  793. * @param e {Node} form field
  794. * @return {String}
  795. */
  796. _getColumnKey: function(
  797. /* Node */ e)
  798. {
  799. var m = quick_edit_re.exec(e.get('className'));
  800. return m[1];
  801. }
  802. });
  803.  
  804. Y.namespace("Plugin");
  805. Y.Plugin.DataTableQuickEdit = QuickEdit;
  806.