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