API Docs for: 3.10.3
Show:

File: widget-parent/js/Widget-Parent.js

  1. /**
  2. * Extension enabling a Widget to be a parent of another Widget.
  3. *
  4. * @module widget-parent
  5. */
  6.  
  7. var Lang = Y.Lang,
  8. RENDERED = "rendered",
  9. BOUNDING_BOX = "boundingBox";
  10.  
  11. /**
  12. * Widget extension providing functionality enabling a Widget to be a
  13. * parent of another Widget.
  14. *
  15. * <p>In addition to the set of attributes supported by WidgetParent, the constructor
  16. * configuration object can also contain a <code>children</code> which can be used
  17. * to add child widgets to the parent during construction. The <code>children</code>
  18. * property is an array of either child widget instances or child widget configuration
  19. * objects, and is sugar for the <a href="#method_add">add</a> method. See the
  20. * <a href="#method_add">add</a> for details on the structure of the child widget
  21. * configuration object.
  22. * @class WidgetParent
  23. * @constructor
  24. * @uses ArrayList
  25. * @param {Object} config User configuration object.
  26. */
  27. function Parent(config) {
  28.  
  29. /**
  30. * Fires when a Widget is add as a child. The event object will have a
  31. * 'child' property that returns a reference to the child Widget, as well
  32. * as an 'index' property that returns a reference to the index specified
  33. * when the add() method was called.
  34. * <p>
  35. * Subscribers to the "on" moment of this event, will be notified
  36. * before a child is added.
  37. * </p>
  38. * <p>
  39. * Subscribers to the "after" moment of this event, will be notified
  40. * after a child is added.
  41. * </p>
  42. *
  43. * @event addChild
  44. * @preventable _defAddChildFn
  45. * @param {EventFacade} e The Event Facade
  46. */
  47. this.publish("addChild", {
  48. defaultTargetOnly: true,
  49. defaultFn: this._defAddChildFn
  50. });
  51.  
  52.  
  53. /**
  54. * Fires when a child Widget is removed. The event object will have a
  55. * 'child' property that returns a reference to the child Widget, as well
  56. * as an 'index' property that returns a reference child's ordinal position.
  57. * <p>
  58. * Subscribers to the "on" moment of this event, will be notified
  59. * before a child is removed.
  60. * </p>
  61. * <p>
  62. * Subscribers to the "after" moment of this event, will be notified
  63. * after a child is removed.
  64. * </p>
  65. *
  66. * @event removeChild
  67. * @preventable _defRemoveChildFn
  68. * @param {EventFacade} e The Event Facade
  69. */
  70. this.publish("removeChild", {
  71. defaultTargetOnly: true,
  72. defaultFn: this._defRemoveChildFn
  73. });
  74.  
  75. this._items = [];
  76.  
  77. var children,
  78. handle;
  79.  
  80. if (config && config.children) {
  81.  
  82. children = config.children;
  83. handle = this.after("initializedChange", function (e) {
  84. this._add(children);
  85. handle.detach();
  86. });
  87.  
  88. }
  89.  
  90. // Widget method overlap
  91. Y.after(this._renderChildren, this, "renderUI");
  92. Y.after(this._bindUIParent, this, "bindUI");
  93.  
  94. this.after("selectionChange", this._afterSelectionChange);
  95. this.after("selectedChange", this._afterParentSelectedChange);
  96. this.after("activeDescendantChange", this._afterActiveDescendantChange);
  97.  
  98. this._hDestroyChild = this.after("*:destroy", this._afterDestroyChild);
  99. this.after("*:focusedChange", this._updateActiveDescendant);
  100.  
  101. }
  102.  
  103. Parent.ATTRS = {
  104.  
  105. /**
  106. * @attribute defaultChildType
  107. * @type {String|Object}
  108. *
  109. * @description String representing the default type of the children
  110. * managed by this Widget. Can also supply default type as a constructor
  111. * reference.
  112. */
  113. defaultChildType: {
  114. setter: function (val) {
  115. var returnVal = Y.Attribute.INVALID_VALUE,
  116. FnConstructor = Lang.isString(val) ? Y[val] : val;
  117. if (Lang.isFunction(FnConstructor)) {
  118. returnVal = FnConstructor;
  119. }
  120. return returnVal;
  121. }
  122. },
  123.  
  124. /**
  125. * @attribute activeDescendant
  126. * @type Widget
  127. * @readOnly
  128. *
  129. * @description Returns the Widget's currently focused descendant Widget.
  130. */
  131. activeDescendant: {
  132. readOnly: true
  133. },
  134.  
  135. /**
  136. * @attribute multiple
  137. * @type Boolean
  138. * @default false
  139. * @writeOnce
  140. *
  141. * @description Boolean indicating if multiple children can be selected at
  142. * once. Whether or not multiple selection is enabled is always delegated
  143. * to the value of the <code>multiple</code> attribute of the root widget
  144. * in the object hierarchy.
  145. */
  146. multiple: {
  147. value: false,
  148. validator: Lang.isBoolean,
  149. writeOnce: true,
  150. getter: function (value) {
  151. var root = this.get("root");
  152. return (root && root != this) ? root.get("multiple") : value;
  153. }
  154. },
  155.  
  156.  
  157. /**
  158. * @attribute selection
  159. * @type {ArrayList|Widget}
  160. * @readOnly
  161. *
  162. * @description Returns the currently selected child Widget. If the
  163. * <code>mulitple</code> attribte is set to <code>true</code> will
  164. * return an Y.ArrayList instance containing the currently selected
  165. * children. If no children are selected, will return null.
  166. */
  167. selection: {
  168. readOnly: true,
  169. setter: "_setSelection",
  170. getter: function (value) {
  171. var selection = Lang.isArray(value) ?
  172. (new Y.ArrayList(value)) : value;
  173. return selection;
  174. }
  175. },
  176.  
  177. selected: {
  178. setter: function (value) {
  179.  
  180. // Enforces selection behavior on for parent Widgets. Parent's
  181. // selected attribute can be set to the following:
  182. // 0 - Not selected
  183. // 1 - Fully selected (all children are selected). In order for
  184. // all children to be selected, multiple selection must be
  185. // enabled. Therefore, you cannot set the "selected" attribute
  186. // on a parent Widget to 1 unless multiple selection is enabled.
  187. // 2 - Partially selected, meaning one ore more (but not all)
  188. // children are selected.
  189.  
  190. var returnVal = value;
  191.  
  192. if (value === 1 && !this.get("multiple")) {
  193. Y.log('The selected attribute can only be set to 1 if the "multiple" attribute is set to true.', "error", "widget");
  194. returnVal = Y.Attribute.INVALID_VALUE;
  195. }
  196. return returnVal;
  197. }
  198. }
  199.  
  200. };
  201.  
  202. Parent.prototype = {
  203.  
  204. /**
  205. * The destructor implementation for Parent widgets. Destroys all children.
  206. * @method destructor
  207. */
  208. destructor: function() {
  209. this._destroyChildren();
  210. },
  211.  
  212. /**
  213. * Destroy event listener for each child Widget, responsible for removing
  214. * the destroyed child Widget from the parent's internal array of children
  215. * (_items property).
  216. *
  217. * @method _afterDestroyChild
  218. * @protected
  219. * @param {EventFacade} event The event facade for the attribute change.
  220. */
  221. _afterDestroyChild: function (event) {
  222. var child = event.target;
  223.  
  224. if (child.get("parent") == this) {
  225. child.remove();
  226. }
  227. },
  228.  
  229. /**
  230. * Attribute change listener for the <code>selection</code>
  231. * attribute, responsible for setting the value of the
  232. * parent's <code>selected</code> attribute.
  233. *
  234. * @method _afterSelectionChange
  235. * @protected
  236. * @param {EventFacade} event The event facade for the attribute change.
  237. */
  238. _afterSelectionChange: function (event) {
  239.  
  240. if (event.target == this && event.src != this) {
  241.  
  242. var selection = event.newVal,
  243. selectedVal = 0; // Not selected
  244.  
  245.  
  246. if (selection) {
  247.  
  248. selectedVal = 2; // Assume partially selected, confirm otherwise
  249.  
  250.  
  251. if (Y.instanceOf(selection, Y.ArrayList) &&
  252. (selection.size() === this.size())) {
  253.  
  254. selectedVal = 1; // Fully selected
  255.  
  256. }
  257. }
  258.  
  259. this.set("selected", selectedVal, { src: this });
  260. }
  261. },
  262.  
  263.  
  264. /**
  265. * Attribute change listener for the <code>activeDescendant</code>
  266. * attribute, responsible for setting the value of the
  267. * parent's <code>activeDescendant</code> attribute.
  268. *
  269. * @method _afterActiveDescendantChange
  270. * @protected
  271. * @param {EventFacade} event The event facade for the attribute change.
  272. */
  273. _afterActiveDescendantChange: function (event) {
  274. var parent = this.get("parent");
  275.  
  276. if (parent) {
  277. parent._set("activeDescendant", event.newVal);
  278. }
  279. },
  280.  
  281. /**
  282. * Attribute change listener for the <code>selected</code>
  283. * attribute, responsible for syncing the selected state of all children to
  284. * match that of their parent Widget.
  285. *
  286. *
  287. * @method _afterParentSelectedChange
  288. * @protected
  289. * @param {EventFacade} event The event facade for the attribute change.
  290. */
  291. _afterParentSelectedChange: function (event) {
  292.  
  293. var value = event.newVal;
  294.  
  295. if (this == event.target && event.src != this &&
  296. (value === 0 || value === 1)) {
  297.  
  298. this.each(function (child) {
  299.  
  300. // Specify the source of this change as the parent so that
  301. // value of the parent's "selection" attribute isn't
  302. // recalculated
  303.  
  304. child.set("selected", value, { src: this });
  305.  
  306. }, this);
  307. }
  308. },
  309.  
  310.  
  311. /**
  312. * Default setter for <code>selection</code> attribute changes.
  313. *
  314. * @method _setSelection
  315. * @protected
  316. * @param child {Widget|Array} Widget or Array of Widget instances.
  317. * @return {Widget|Array} Widget or Array of Widget instances.
  318. */
  319. _setSelection: function (child) {
  320.  
  321. var selection = null,
  322. selected;
  323.  
  324. if (this.get("multiple") && !this.isEmpty()) {
  325.  
  326. selected = [];
  327. this.each(function (v) {
  328.  
  329. if (v.get("selected") > 0) {
  330. selected.push(v);
  331. }
  332.  
  333. });
  334.  
  335. if (selected.length > 0) {
  336. selection = selected;
  337. }
  338.  
  339. }
  340. else {
  341.  
  342. if (child.get("selected") > 0) {
  343. selection = child;
  344. }
  345.  
  346. }
  347. return selection;
  348. },
  349.  
  350.  
  351. /**
  352. * Attribute change listener for the <code>selected</code>
  353. * attribute of child Widgets, responsible for setting the value of the
  354. * parent's <code>selection</code> attribute.
  355. *
  356. * @method _updateSelection
  357. * @protected
  358. * @param {EventFacade} event The event facade for the attribute change.
  359. */
  360. _updateSelection: function (event) {
  361.  
  362. var child = event.target,
  363. selection;
  364.  
  365. if (child.get("parent") == this) {
  366.  
  367. if (event.src != "_updateSelection") {
  368.  
  369. selection = this.get("selection");
  370.  
  371. if (!this.get("multiple") && selection && event.newVal > 0) {
  372.  
  373. // Deselect the previously selected child.
  374. // Set src equal to the current context to prevent
  375. // unnecessary re-calculation of the selection.
  376.  
  377. selection.set("selected", 0, { src: "_updateSelection" });
  378.  
  379. }
  380.  
  381. this._set("selection", child);
  382.  
  383. }
  384.  
  385. if (event.src == this) {
  386. this._set("selection", child, { src: this });
  387. }
  388. }
  389.  
  390. },
  391.  
  392. /**
  393. * Attribute change listener for the <code>focused</code>
  394. * attribute of child Widgets, responsible for setting the value of the
  395. * parent's <code>activeDescendant</code> attribute.
  396. *
  397. * @method _updateActiveDescendant
  398. * @protected
  399. * @param {EventFacade} event The event facade for the attribute change.
  400. */
  401. _updateActiveDescendant: function (event) {
  402. var activeDescendant = (event.newVal === true) ? event.target : null;
  403. this._set("activeDescendant", activeDescendant);
  404. },
  405.  
  406. /**
  407. * Creates an instance of a child Widget using the specified configuration.
  408. * By default Widget instances will be created of the type specified
  409. * by the <code>defaultChildType</code> attribute. Types can be explicitly
  410. * defined via the <code>childType</code> property of the configuration object
  411. * literal. The use of the <code>type</code> property has been deprecated, but
  412. * will still be used as a fallback, if <code>childType</code> is not defined,
  413. * for backwards compatibility.
  414. *
  415. * @method _createChild
  416. * @protected
  417. * @param config {Object} Object literal representing the configuration
  418. * used to create an instance of a Widget.
  419. */
  420. _createChild: function (config) {
  421.  
  422. var defaultType = this.get("defaultChildType"),
  423. altType = config.childType || config.type,
  424. child,
  425. Fn,
  426. FnConstructor;
  427.  
  428. if (altType) {
  429. Fn = Lang.isString(altType) ? Y[altType] : altType;
  430. }
  431.  
  432. if (Lang.isFunction(Fn)) {
  433. FnConstructor = Fn;
  434. } else if (defaultType) {
  435. // defaultType is normalized to a function in it's setter
  436. FnConstructor = defaultType;
  437. }
  438.  
  439. if (FnConstructor) {
  440. child = new FnConstructor(config);
  441. } else {
  442. Y.error("Could not create a child instance because its constructor is either undefined or invalid.");
  443. }
  444.  
  445. return child;
  446. },
  447.  
  448. /**
  449. * Default addChild handler
  450. *
  451. * @method _defAddChildFn
  452. * @protected
  453. * @param event {EventFacade} The Event object
  454. * @param child {Widget} The Widget instance, or configuration
  455. * object for the Widget to be added as a child.
  456. * @param index {Number} Number representing the position at
  457. * which the child will be inserted.
  458. */
  459. _defAddChildFn: function (event) {
  460.  
  461. var child = event.child,
  462. index = event.index,
  463. children = this._items;
  464.  
  465. if (child.get("parent")) {
  466. child.remove();
  467. }
  468.  
  469. if (Lang.isNumber(index)) {
  470. children.splice(index, 0, child);
  471. }
  472. else {
  473. children.push(child);
  474. }
  475.  
  476. child._set("parent", this);
  477. child.addTarget(this);
  478.  
  479. // Update index in case it got normalized after addition
  480. // (e.g. user passed in 10, and there are only 3 items, the actual index would be 3. We don't want to pass 10 around in the event facade).
  481. event.index = child.get("index");
  482.  
  483. // TO DO: Remove in favor of using event bubbling
  484. child.after("selectedChange", Y.bind(this._updateSelection, this));
  485. },
  486.  
  487.  
  488. /**
  489. * Default removeChild handler
  490. *
  491. * @method _defRemoveChildFn
  492. * @protected
  493. * @param event {EventFacade} The Event object
  494. * @param child {Widget} The Widget instance to be removed.
  495. * @param index {Number} Number representing the index of the Widget to
  496. * be removed.
  497. */
  498. _defRemoveChildFn: function (event) {
  499.  
  500. var child = event.child,
  501. index = event.index,
  502. children = this._items;
  503.  
  504. if (child.get("focused")) {
  505. child.blur(); // focused is readOnly, so use the public i/f to unset it
  506. }
  507.  
  508. if (child.get("selected")) {
  509. child.set("selected", 0);
  510. }
  511.  
  512. children.splice(index, 1);
  513.  
  514. child.removeTarget(this);
  515. child._oldParent = child.get("parent");
  516. child._set("parent", null);
  517. },
  518.  
  519. /**
  520. * @method _add
  521. * @protected
  522. * @param child {Widget|Object} The Widget instance, or configuration
  523. * object for the Widget to be added as a child.
  524. * @param child {Array} Array of Widget instances, or configuration
  525. * objects for the Widgets to be added as a children.
  526. * @param index {Number} (Optional.) Number representing the position at
  527. * which the child should be inserted.
  528. * @description Adds a Widget as a child. If the specified Widget already
  529. * has a parent it will be removed from its current parent before
  530. * being added as a child.
  531. * @return {Widget|Array} Successfully added Widget or Array containing the
  532. * successfully added Widget instance(s). If no children where added, will
  533. * will return undefined.
  534. */
  535. _add: function (child, index) {
  536.  
  537. var children,
  538. oChild,
  539. returnVal;
  540.  
  541.  
  542. if (Lang.isArray(child)) {
  543.  
  544. children = [];
  545.  
  546. Y.each(child, function (v, k) {
  547.  
  548. oChild = this._add(v, (index + k));
  549.  
  550. if (oChild) {
  551. children.push(oChild);
  552. }
  553. }, this);
  554.  
  555. if (children.length > 0) {
  556. returnVal = children;
  557. }
  558.  
  559. }
  560. else {
  561.  
  562. if (Y.instanceOf(child, Y.Widget)) {
  563. oChild = child;
  564. }
  565. else {
  566. oChild = this._createChild(child);
  567. }
  568.  
  569. if (oChild && this.fire("addChild", { child: oChild, index: index })) {
  570. returnVal = oChild;
  571. }
  572.  
  573. }
  574.  
  575. return returnVal;
  576.  
  577. },
  578.  
  579.  
  580. /**
  581. * @method add
  582. * @param child {Widget|Object} The Widget instance, or configuration
  583. * object for the Widget to be added as a child. The configuration object
  584. * for the child can include a <code>childType</code> property, which is either
  585. * a constructor function or a string which names a constructor function on the
  586. * Y instance (e.g. "Tab" would refer to Y.Tab) (<code>childType</code> used to be
  587. * named <code>type</code>, support for which has been deprecated, but is still
  588. * maintained for backward compatibility. <code>childType</code> takes precedence
  589. * over <code>type</code> if both are defined.
  590. * @param child {Array} Array of Widget instances, or configuration
  591. * objects for the Widgets to be added as a children.
  592. * @param index {Number} (Optional.) Number representing the position at
  593. * which the child should be inserted.
  594. * @description Adds a Widget as a child. If the specified Widget already
  595. * has a parent it will be removed from its current parent before
  596. * being added as a child.
  597. * @return {ArrayList} Y.ArrayList containing the successfully added
  598. * Widget instance(s). If no children where added, will return an empty
  599. * Y.ArrayList instance.
  600. */
  601. add: function () {
  602.  
  603. var added = this._add.apply(this, arguments),
  604. children = added ? (Lang.isArray(added) ? added : [added]) : [];
  605.  
  606. return (new Y.ArrayList(children));
  607.  
  608. },
  609.  
  610.  
  611. /**
  612. * @method remove
  613. * @param index {Number} (Optional.) Number representing the index of the
  614. * child to be removed.
  615. * @description Removes the Widget from its parent. Optionally, can remove
  616. * a child by specifying its index.
  617. * @return {Widget} Widget instance that was successfully removed, otherwise
  618. * undefined.
  619. */
  620. remove: function (index) {
  621.  
  622. var child = this._items[index],
  623. returnVal;
  624.  
  625. if (child && this.fire("removeChild", { child: child, index: index })) {
  626. returnVal = child;
  627. }
  628. return returnVal;
  629.  
  630. },
  631.  
  632.  
  633. /**
  634. * @method removeAll
  635. * @description Removes all of the children from the Widget.
  636. * @return {ArrayList} Y.ArrayList instance containing Widgets that were
  637. * successfully removed. If no children where removed, will return an empty
  638. * Y.ArrayList instance.
  639. */
  640. removeAll: function () {
  641.  
  642. var removed = [],
  643. child;
  644.  
  645. Y.each(this._items.concat(), function () {
  646.  
  647. child = this.remove(0);
  648.  
  649. if (child) {
  650. removed.push(child);
  651. }
  652.  
  653. }, this);
  654.  
  655. return (new Y.ArrayList(removed));
  656.  
  657. },
  658. /**
  659. * Selects the child at the given index (zero-based).
  660. *
  661. * @method selectChild
  662. * @param {Number} i the index of the child to be selected
  663. */
  664. selectChild: function(i) {
  665. this.item(i).set('selected', 1);
  666. },
  667.  
  668. /**
  669. * Selects all children.
  670. *
  671. * @method selectAll
  672. */
  673. selectAll: function () {
  674. this.set("selected", 1);
  675. },
  676.  
  677. /**
  678. * Deselects all children.
  679. *
  680. * @method deselectAll
  681. */
  682. deselectAll: function () {
  683. this.set("selected", 0);
  684. },
  685.  
  686. /**
  687. * Updates the UI in response to a child being added.
  688. *
  689. * @method _uiAddChild
  690. * @protected
  691. * @param child {Widget} The child Widget instance to render.
  692. * @param parentNode {Object} The Node under which the
  693. * child Widget is to be rendered.
  694. */
  695. _uiAddChild: function (child, parentNode) {
  696.  
  697. child.render(parentNode);
  698.  
  699. // TODO: Ideally this should be in Child's render UI.
  700.  
  701. var childBB = child.get("boundingBox"),
  702. siblingBB,
  703. nextSibling = child.next(false),
  704. prevSibling;
  705.  
  706. // Insert or Append to last child.
  707.  
  708. // Avoiding index, and using the current sibling
  709. // state (which should be accurate), means we don't have
  710. // to worry about decorator elements which may be added
  711. // to the _childContainer node.
  712. if (nextSibling && nextSibling.get(RENDERED)) {
  713.  
  714. siblingBB = nextSibling.get(BOUNDING_BOX);
  715. siblingBB.insert(childBB, "before");
  716.  
  717. } else {
  718.  
  719. prevSibling = child.previous(false);
  720.  
  721. if (prevSibling && prevSibling.get(RENDERED)) {
  722.  
  723. siblingBB = prevSibling.get(BOUNDING_BOX);
  724. siblingBB.insert(childBB, "after");
  725.  
  726. } else if (!parentNode.contains(childBB)) {
  727.  
  728. // Based on pull request from andreas-karlsson
  729. // https://github.com/yui/yui3/pull/25#issuecomment-2103536
  730.  
  731. // Account for case where a child was rendered independently of the
  732. // parent-child framework, to a node outside of the parentNode,
  733. // and there are no siblings.
  734.  
  735. parentNode.appendChild(childBB);
  736. }
  737. }
  738.  
  739. },
  740.  
  741. /**
  742. * Updates the UI in response to a child being removed.
  743. *
  744. * @method _uiRemoveChild
  745. * @protected
  746. * @param child {Widget} The child Widget instance to render.
  747. */
  748. _uiRemoveChild: function (child) {
  749. child.get("boundingBox").remove();
  750. },
  751.  
  752. _afterAddChild: function (event) {
  753. var child = event.child;
  754.  
  755. if (child.get("parent") == this) {
  756. this._uiAddChild(child, this._childrenContainer);
  757. }
  758. },
  759.  
  760. _afterRemoveChild: function (event) {
  761. var child = event.child;
  762.  
  763. if (child._oldParent == this) {
  764. this._uiRemoveChild(child);
  765. }
  766. },
  767.  
  768. /**
  769. * Sets up DOM and CustomEvent listeners for the parent widget.
  770. * <p>
  771. * This method in invoked after bindUI is invoked for the Widget class
  772. * using YUI's aop infrastructure.
  773. * </p>
  774. *
  775. * @method _bindUIParent
  776. * @protected
  777. */
  778. _bindUIParent: function () {
  779. this.after("addChild", this._afterAddChild);
  780. this.after("removeChild", this._afterRemoveChild);
  781. },
  782.  
  783. /**
  784. * Renders all child Widgets for the parent.
  785. * <p>
  786. * This method in invoked after renderUI is invoked for the Widget class
  787. * using YUI's aop infrastructure.
  788. * </p>
  789. * @method _renderChildren
  790. * @protected
  791. */
  792. _renderChildren: function () {
  793.  
  794. /**
  795. * <p>By default WidgetParent will render it's children to the parent's content box.</p>
  796. *
  797. * <p>If the children need to be rendered somewhere else, the _childrenContainer property
  798. * can be set to the Node which the children should be rendered to. This property should be
  799. * set before the _renderChildren method is invoked, ideally in your renderUI method,
  800. * as soon as you create the element to be rendered to.</p>
  801. *
  802. * @protected
  803. * @property _childrenContainer
  804. * @value The content box
  805. * @type Node
  806. */
  807. var renderTo = this._childrenContainer || this.get("contentBox");
  808.  
  809. this._childrenContainer = renderTo;
  810.  
  811. this.each(function (child) {
  812. child.render(renderTo);
  813. });
  814. },
  815.  
  816. /**
  817. * Destroys all child Widgets for the parent.
  818. * <p>
  819. * This method is invoked before the destructor is invoked for the Widget
  820. * class using YUI's aop infrastructure.
  821. * </p>
  822. * @method _destroyChildren
  823. * @protected
  824. */
  825. _destroyChildren: function () {
  826.  
  827. // Detach the handler responsible for removing children in
  828. // response to destroying them since:
  829. // 1) It is unnecessary/inefficient at this point since we are doing
  830. // a batch destroy of all children.
  831. // 2) Removing each child will affect our ability to iterate the
  832. // children since the size of _items will be changing as we
  833. // iterate.
  834. this._hDestroyChild.detach();
  835.  
  836. // Need to clone the _items array since
  837. this.each(function (child) {
  838. child.destroy();
  839. });
  840. }
  841. };
  842.  
  843. Y.augment(Parent, Y.ArrayList);
  844.  
  845. Y.WidgetParent = Parent;
  846.