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