You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
877 lines
25 KiB
877 lines
25 KiB
2 years ago
|
/*
|
||
|
YUI 3.17.2 (build 9c3c78e)
|
||
|
Copyright 2014 Yahoo! Inc. All rights reserved.
|
||
|
Licensed under the BSD License.
|
||
|
http://yuilibrary.com/license/
|
||
|
*/
|
||
|
|
||
|
YUI.add('widget-parent', function (Y, NAME) {
|
||
|
|
||
|
/**
|
||
|
* Extension enabling a Widget to be a parent of another Widget.
|
||
|
*
|
||
|
* @module widget-parent
|
||
|
*/
|
||
|
|
||
|
var Lang = Y.Lang,
|
||
|
RENDERED = "rendered",
|
||
|
BOUNDING_BOX = "boundingBox";
|
||
|
|
||
|
/**
|
||
|
* Widget extension providing functionality enabling a Widget to be a
|
||
|
* parent of another Widget.
|
||
|
*
|
||
|
* <p>In addition to the set of attributes supported by WidgetParent, the constructor
|
||
|
* configuration object can also contain a <code>children</code> which can be used
|
||
|
* to add child widgets to the parent during construction. The <code>children</code>
|
||
|
* property is an array of either child widget instances or child widget configuration
|
||
|
* objects, and is sugar for the <a href="#method_add">add</a> method. See the
|
||
|
* <a href="#method_add">add</a> for details on the structure of the child widget
|
||
|
* configuration object.
|
||
|
* @class WidgetParent
|
||
|
* @constructor
|
||
|
* @uses ArrayList
|
||
|
* @param {Object} config User configuration object.
|
||
|
*/
|
||
|
function Parent(config) {
|
||
|
|
||
|
/**
|
||
|
* Fires when a Widget is add as a child. The event object will have a
|
||
|
* 'child' property that returns a reference to the child Widget, as well
|
||
|
* as an 'index' property that returns a reference to the index specified
|
||
|
* when the add() method was called.
|
||
|
* <p>
|
||
|
* Subscribers to the "on" moment of this event, will be notified
|
||
|
* before a child is added.
|
||
|
* </p>
|
||
|
* <p>
|
||
|
* Subscribers to the "after" moment of this event, will be notified
|
||
|
* after a child is added.
|
||
|
* </p>
|
||
|
*
|
||
|
* @event addChild
|
||
|
* @preventable _defAddChildFn
|
||
|
* @param {EventFacade} e The Event Facade
|
||
|
*/
|
||
|
this.publish("addChild", {
|
||
|
defaultTargetOnly: true,
|
||
|
defaultFn: this._defAddChildFn
|
||
|
});
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Fires when a child Widget is removed. The event object will have a
|
||
|
* 'child' property that returns a reference to the child Widget, as well
|
||
|
* as an 'index' property that returns a reference child's ordinal position.
|
||
|
* <p>
|
||
|
* Subscribers to the "on" moment of this event, will be notified
|
||
|
* before a child is removed.
|
||
|
* </p>
|
||
|
* <p>
|
||
|
* Subscribers to the "after" moment of this event, will be notified
|
||
|
* after a child is removed.
|
||
|
* </p>
|
||
|
*
|
||
|
* @event removeChild
|
||
|
* @preventable _defRemoveChildFn
|
||
|
* @param {EventFacade} e The Event Facade
|
||
|
*/
|
||
|
this.publish("removeChild", {
|
||
|
defaultTargetOnly: true,
|
||
|
defaultFn: this._defRemoveChildFn
|
||
|
});
|
||
|
|
||
|
this._items = [];
|
||
|
|
||
|
var children,
|
||
|
handle;
|
||
|
|
||
|
if (config && config.children) {
|
||
|
|
||
|
children = config.children;
|
||
|
|
||
|
handle = this.after("initializedChange", function (e) {
|
||
|
this._add(children);
|
||
|
handle.detach();
|
||
|
});
|
||
|
|
||
|
}
|
||
|
|
||
|
// Widget method overlap
|
||
|
Y.after(this._renderChildren, this, "renderUI");
|
||
|
Y.after(this._bindUIParent, this, "bindUI");
|
||
|
|
||
|
this.after("selectionChange", this._afterSelectionChange);
|
||
|
this.after("selectedChange", this._afterParentSelectedChange);
|
||
|
this.after("activeDescendantChange", this._afterActiveDescendantChange);
|
||
|
|
||
|
this._hDestroyChild = this.after("*:destroy", this._afterDestroyChild);
|
||
|
this.after("*:focusedChange", this._updateActiveDescendant);
|
||
|
|
||
|
}
|
||
|
|
||
|
Parent.ATTRS = {
|
||
|
|
||
|
/**
|
||
|
* @attribute defaultChildType
|
||
|
* @type {String|Object}
|
||
|
*
|
||
|
* @description String representing the default type of the children
|
||
|
* managed by this Widget. Can also supply default type as a constructor
|
||
|
* reference.
|
||
|
*/
|
||
|
defaultChildType: {
|
||
|
setter: function (val) {
|
||
|
|
||
|
var returnVal = Y.Attribute.INVALID_VALUE,
|
||
|
FnConstructor = Lang.isString(val) ? Y[val] : val;
|
||
|
|
||
|
if (Lang.isFunction(FnConstructor)) {
|
||
|
returnVal = FnConstructor;
|
||
|
}
|
||
|
|
||
|
return returnVal;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* @attribute activeDescendant
|
||
|
* @type Widget
|
||
|
* @readOnly
|
||
|
*
|
||
|
* @description Returns the Widget's currently focused descendant Widget.
|
||
|
*/
|
||
|
activeDescendant: {
|
||
|
readOnly: true
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* @attribute multiple
|
||
|
* @type Boolean
|
||
|
* @default false
|
||
|
* @writeOnce
|
||
|
*
|
||
|
* @description Boolean indicating if multiple children can be selected at
|
||
|
* once. Whether or not multiple selection is enabled is always delegated
|
||
|
* to the value of the <code>multiple</code> attribute of the root widget
|
||
|
* in the object hierarchy.
|
||
|
*/
|
||
|
multiple: {
|
||
|
value: false,
|
||
|
validator: Lang.isBoolean,
|
||
|
writeOnce: true,
|
||
|
getter: function (value) {
|
||
|
var root = this.get("root");
|
||
|
return (root && root != this) ? root.get("multiple") : value;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @attribute selection
|
||
|
* @type {ArrayList|Widget}
|
||
|
* @readOnly
|
||
|
*
|
||
|
* @description Returns the currently selected child Widget. If the
|
||
|
* <code>mulitple</code> attribte is set to <code>true</code> will
|
||
|
* return an Y.ArrayList instance containing the currently selected
|
||
|
* children. If no children are selected, will return null.
|
||
|
*/
|
||
|
selection: {
|
||
|
readOnly: true,
|
||
|
setter: "_setSelection",
|
||
|
getter: function (value) {
|
||
|
var selection = Lang.isArray(value) ?
|
||
|
(new Y.ArrayList(value)) : value;
|
||
|
return selection;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
selected: {
|
||
|
setter: function (value) {
|
||
|
|
||
|
// Enforces selection behavior on for parent Widgets. Parent's
|
||
|
// selected attribute can be set to the following:
|
||
|
// 0 - Not selected
|
||
|
// 1 - Fully selected (all children are selected). In order for
|
||
|
// all children to be selected, multiple selection must be
|
||
|
// enabled. Therefore, you cannot set the "selected" attribute
|
||
|
// on a parent Widget to 1 unless multiple selection is enabled.
|
||
|
// 2 - Partially selected, meaning one ore more (but not all)
|
||
|
// children are selected.
|
||
|
|
||
|
var returnVal = value;
|
||
|
|
||
|
if (value === 1 && !this.get("multiple")) {
|
||
|
returnVal = Y.Attribute.INVALID_VALUE;
|
||
|
}
|
||
|
|
||
|
return returnVal;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
Parent.prototype = {
|
||
|
|
||
|
/**
|
||
|
* The destructor implementation for Parent widgets. Destroys all children.
|
||
|
* @method destructor
|
||
|
*/
|
||
|
destructor: function() {
|
||
|
this._destroyChildren();
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Destroy event listener for each child Widget, responsible for removing
|
||
|
* the destroyed child Widget from the parent's internal array of children
|
||
|
* (_items property).
|
||
|
*
|
||
|
* @method _afterDestroyChild
|
||
|
* @protected
|
||
|
* @param {EventFacade} event The event facade for the attribute change.
|
||
|
*/
|
||
|
_afterDestroyChild: function (event) {
|
||
|
var child = event.target;
|
||
|
|
||
|
if (child.get("parent") == this) {
|
||
|
child.remove();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Attribute change listener for the <code>selection</code>
|
||
|
* attribute, responsible for setting the value of the
|
||
|
* parent's <code>selected</code> attribute.
|
||
|
*
|
||
|
* @method _afterSelectionChange
|
||
|
* @protected
|
||
|
* @param {EventFacade} event The event facade for the attribute change.
|
||
|
*/
|
||
|
_afterSelectionChange: function (event) {
|
||
|
|
||
|
if (event.target == this && event.src != this) {
|
||
|
|
||
|
var selection = event.newVal,
|
||
|
selectedVal = 0; // Not selected
|
||
|
|
||
|
|
||
|
if (selection) {
|
||
|
|
||
|
selectedVal = 2; // Assume partially selected, confirm otherwise
|
||
|
|
||
|
|
||
|
if (Y.instanceOf(selection, Y.ArrayList) &&
|
||
|
(selection.size() === this.size())) {
|
||
|
|
||
|
selectedVal = 1; // Fully selected
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
this.set("selected", selectedVal, { src: this });
|
||
|
|
||
|
}
|
||
|
},
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Attribute change listener for the <code>activeDescendant</code>
|
||
|
* attribute, responsible for setting the value of the
|
||
|
* parent's <code>activeDescendant</code> attribute.
|
||
|
*
|
||
|
* @method _afterActiveDescendantChange
|
||
|
* @protected
|
||
|
* @param {EventFacade} event The event facade for the attribute change.
|
||
|
*/
|
||
|
_afterActiveDescendantChange: function (event) {
|
||
|
var parent = this.get("parent");
|
||
|
|
||
|
if (parent) {
|
||
|
parent._set("activeDescendant", event.newVal);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Attribute change listener for the <code>selected</code>
|
||
|
* attribute, responsible for syncing the selected state of all children to
|
||
|
* match that of their parent Widget.
|
||
|
*
|
||
|
*
|
||
|
* @method _afterParentSelectedChange
|
||
|
* @protected
|
||
|
* @param {EventFacade} event The event facade for the attribute change.
|
||
|
*/
|
||
|
_afterParentSelectedChange: function (event) {
|
||
|
|
||
|
var value = event.newVal;
|
||
|
|
||
|
if (this == event.target && event.src != this &&
|
||
|
(value === 0 || value === 1)) {
|
||
|
|
||
|
this.each(function (child) {
|
||
|
|
||
|
// Specify the source of this change as the parent so that
|
||
|
// value of the parent's "selection" attribute isn't
|
||
|
// recalculated
|
||
|
|
||
|
child.set("selected", value, { src: this });
|
||
|
|
||
|
}, this);
|
||
|
|
||
|
}
|
||
|
|
||
|
},
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Default setter for <code>selection</code> attribute changes.
|
||
|
*
|
||
|
* @method _setSelection
|
||
|
* @protected
|
||
|
* @param child {Widget|Array} Widget or Array of Widget instances.
|
||
|
* @return {Widget|Array} Widget or Array of Widget instances.
|
||
|
*/
|
||
|
_setSelection: function (child) {
|
||
|
|
||
|
var selection = null,
|
||
|
selected;
|
||
|
|
||
|
if (this.get("multiple") && !this.isEmpty()) {
|
||
|
|
||
|
selected = [];
|
||
|
|
||
|
this.each(function (v) {
|
||
|
|
||
|
if (v.get("selected") > 0) {
|
||
|
selected.push(v);
|
||
|
}
|
||
|
|
||
|
});
|
||
|
|
||
|
if (selected.length > 0) {
|
||
|
selection = selected;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
if (child.get("selected") > 0) {
|
||
|
selection = child;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return selection;
|
||
|
|
||
|
},
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Attribute change listener for the <code>selected</code>
|
||
|
* attribute of child Widgets, responsible for setting the value of the
|
||
|
* parent's <code>selection</code> attribute.
|
||
|
*
|
||
|
* @method _updateSelection
|
||
|
* @protected
|
||
|
* @param {EventFacade} event The event facade for the attribute change.
|
||
|
*/
|
||
|
_updateSelection: function (event) {
|
||
|
|
||
|
var child = event.target,
|
||
|
selection;
|
||
|
|
||
|
if (child.get("parent") == this) {
|
||
|
|
||
|
if (event.src != "_updateSelection") {
|
||
|
|
||
|
selection = this.get("selection");
|
||
|
|
||
|
if (!this.get("multiple") && selection && event.newVal > 0) {
|
||
|
|
||
|
// Deselect the previously selected child.
|
||
|
// Set src equal to the current context to prevent
|
||
|
// unnecessary re-calculation of the selection.
|
||
|
|
||
|
selection.set("selected", 0, { src: "_updateSelection" });
|
||
|
|
||
|
}
|
||
|
|
||
|
this._set("selection", child);
|
||
|
|
||
|
}
|
||
|
|
||
|
if (event.src == this) {
|
||
|
this._set("selection", child, { src: this });
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Attribute change listener for the <code>focused</code>
|
||
|
* attribute of child Widgets, responsible for setting the value of the
|
||
|
* parent's <code>activeDescendant</code> attribute.
|
||
|
*
|
||
|
* @method _updateActiveDescendant
|
||
|
* @protected
|
||
|
* @param {EventFacade} event The event facade for the attribute change.
|
||
|
*/
|
||
|
_updateActiveDescendant: function (event) {
|
||
|
var activeDescendant = (event.newVal === true) ? event.target : null;
|
||
|
this._set("activeDescendant", activeDescendant);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Creates an instance of a child Widget using the specified configuration.
|
||
|
* By default Widget instances will be created of the type specified
|
||
|
* by the <code>defaultChildType</code> attribute. Types can be explicitly
|
||
|
* defined via the <code>childType</code> property of the configuration object
|
||
|
* literal. The use of the <code>type</code> property has been deprecated, but
|
||
|
* will still be used as a fallback, if <code>childType</code> is not defined,
|
||
|
* for backwards compatibility.
|
||
|
*
|
||
|
* @method _createChild
|
||
|
* @protected
|
||
|
* @param config {Object} Object literal representing the configuration
|
||
|
* used to create an instance of a Widget.
|
||
|
*/
|
||
|
_createChild: function (config) {
|
||
|
|
||
|
var defaultType = this.get("defaultChildType"),
|
||
|
altType = config.childType || config.type,
|
||
|
child,
|
||
|
Fn,
|
||
|
FnConstructor;
|
||
|
|
||
|
if (altType) {
|
||
|
Fn = Lang.isString(altType) ? Y[altType] : altType;
|
||
|
}
|
||
|
|
||
|
if (Lang.isFunction(Fn)) {
|
||
|
FnConstructor = Fn;
|
||
|
} else if (defaultType) {
|
||
|
// defaultType is normalized to a function in it's setter
|
||
|
FnConstructor = defaultType;
|
||
|
}
|
||
|
|
||
|
if (FnConstructor) {
|
||
|
child = new FnConstructor(config);
|
||
|
} else {
|
||
|
Y.error("Could not create a child instance because its constructor is either undefined or invalid.");
|
||
|
}
|
||
|
|
||
|
return child;
|
||
|
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Default addChild handler
|
||
|
*
|
||
|
* @method _defAddChildFn
|
||
|
* @protected
|
||
|
* @param event {EventFacade} The Event object
|
||
|
* @param child {Widget} The Widget instance, or configuration
|
||
|
* object for the Widget to be added as a child.
|
||
|
* @param index {Number} Number representing the position at
|
||
|
* which the child will be inserted.
|
||
|
*/
|
||
|
_defAddChildFn: function (event) {
|
||
|
|
||
|
var child = event.child,
|
||
|
index = event.index,
|
||
|
children = this._items;
|
||
|
|
||
|
if (child.get("parent")) {
|
||
|
child.remove();
|
||
|
}
|
||
|
|
||
|
if (Lang.isNumber(index)) {
|
||
|
children.splice(index, 0, child);
|
||
|
}
|
||
|
else {
|
||
|
children.push(child);
|
||
|
}
|
||
|
|
||
|
child._set("parent", this);
|
||
|
child.addTarget(this);
|
||
|
|
||
|
// Update index in case it got normalized after addition
|
||
|
// (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).
|
||
|
event.index = child.get("index");
|
||
|
|
||
|
// TO DO: Remove in favor of using event bubbling
|
||
|
child.after("selectedChange", Y.bind(this._updateSelection, this));
|
||
|
},
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Default removeChild handler
|
||
|
*
|
||
|
* @method _defRemoveChildFn
|
||
|
* @protected
|
||
|
* @param event {EventFacade} The Event object
|
||
|
* @param child {Widget} The Widget instance to be removed.
|
||
|
* @param index {Number} Number representing the index of the Widget to
|
||
|
* be removed.
|
||
|
*/
|
||
|
_defRemoveChildFn: function (event) {
|
||
|
|
||
|
var child = event.child,
|
||
|
index = event.index,
|
||
|
children = this._items;
|
||
|
|
||
|
if (child.get("focused")) {
|
||
|
child.blur(); // focused is readOnly, so use the public i/f to unset it
|
||
|
}
|
||
|
|
||
|
if (child.get("selected")) {
|
||
|
child.set("selected", 0);
|
||
|
}
|
||
|
|
||
|
children.splice(index, 1);
|
||
|
|
||
|
child.removeTarget(this);
|
||
|
child._oldParent = child.get("parent");
|
||
|
child._set("parent", null);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* @method _add
|
||
|
* @protected
|
||
|
* @param child {Widget|Object} The Widget instance, or configuration
|
||
|
* object for the Widget to be added as a child.
|
||
|
* @param child {Array} Array of Widget instances, or configuration
|
||
|
* objects for the Widgets to be added as a children.
|
||
|
* @param index {Number} (Optional.) Number representing the position at
|
||
|
* which the child should be inserted.
|
||
|
* @description Adds a Widget as a child. If the specified Widget already
|
||
|
* has a parent it will be removed from its current parent before
|
||
|
* being added as a child.
|
||
|
* @return {Widget|Array} Successfully added Widget or Array containing the
|
||
|
* successfully added Widget instance(s). If no children where added, will
|
||
|
* will return undefined.
|
||
|
*/
|
||
|
_add: function (child, index) {
|
||
|
|
||
|
var children,
|
||
|
oChild,
|
||
|
returnVal;
|
||
|
|
||
|
|
||
|
if (Lang.isArray(child)) {
|
||
|
|
||
|
children = [];
|
||
|
|
||
|
Y.each(child, function (v, k) {
|
||
|
|
||
|
oChild = this._add(v, (index + k));
|
||
|
|
||
|
if (oChild) {
|
||
|
children.push(oChild);
|
||
|
}
|
||
|
|
||
|
}, this);
|
||
|
|
||
|
|
||
|
if (children.length > 0) {
|
||
|
returnVal = children;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
if (Y.instanceOf(child, Y.Widget)) {
|
||
|
oChild = child;
|
||
|
}
|
||
|
else {
|
||
|
oChild = this._createChild(child);
|
||
|
}
|
||
|
|
||
|
if (oChild && this.fire("addChild", { child: oChild, index: index })) {
|
||
|
returnVal = oChild;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return returnVal;
|
||
|
|
||
|
},
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @method add
|
||
|
* @param child {Widget|Object} The Widget instance, or configuration
|
||
|
* object for the Widget to be added as a child. The configuration object
|
||
|
* for the child can include a <code>childType</code> property, which is either
|
||
|
* a constructor function or a string which names a constructor function on the
|
||
|
* Y instance (e.g. "Tab" would refer to Y.Tab) (<code>childType</code> used to be
|
||
|
* named <code>type</code>, support for which has been deprecated, but is still
|
||
|
* maintained for backward compatibility. <code>childType</code> takes precedence
|
||
|
* over <code>type</code> if both are defined.
|
||
|
* @param child {Array} Array of Widget instances, or configuration
|
||
|
* objects for the Widgets to be added as a children.
|
||
|
* @param index {Number} (Optional.) Number representing the position at
|
||
|
* which the child should be inserted.
|
||
|
* @description Adds a Widget as a child. If the specified Widget already
|
||
|
* has a parent it will be removed from its current parent before
|
||
|
* being added as a child.
|
||
|
* @return {ArrayList} Y.ArrayList containing the successfully added
|
||
|
* Widget instance(s). If no children where added, will return an empty
|
||
|
* Y.ArrayList instance.
|
||
|
*/
|
||
|
add: function () {
|
||
|
|
||
|
var added = this._add.apply(this, arguments),
|
||
|
children = added ? (Lang.isArray(added) ? added : [added]) : [];
|
||
|
|
||
|
return (new Y.ArrayList(children));
|
||
|
|
||
|
},
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @method remove
|
||
|
* @param index {Number} (Optional.) Number representing the index of the
|
||
|
* child to be removed.
|
||
|
* @description Removes the Widget from its parent. Optionally, can remove
|
||
|
* a child by specifying its index.
|
||
|
* @return {Widget} Widget instance that was successfully removed, otherwise
|
||
|
* undefined.
|
||
|
*/
|
||
|
remove: function (index) {
|
||
|
|
||
|
var child = this._items[index],
|
||
|
returnVal;
|
||
|
|
||
|
if (child && this.fire("removeChild", { child: child, index: index })) {
|
||
|
returnVal = child;
|
||
|
}
|
||
|
|
||
|
return returnVal;
|
||
|
|
||
|
},
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @method removeAll
|
||
|
* @description Removes all of the children from the Widget.
|
||
|
* @return {ArrayList} Y.ArrayList instance containing Widgets that were
|
||
|
* successfully removed. If no children where removed, will return an empty
|
||
|
* Y.ArrayList instance.
|
||
|
*/
|
||
|
removeAll: function () {
|
||
|
|
||
|
var removed = [],
|
||
|
child;
|
||
|
|
||
|
Y.each(this._items.concat(), function () {
|
||
|
|
||
|
child = this.remove(0);
|
||
|
|
||
|
if (child) {
|
||
|
removed.push(child);
|
||
|
}
|
||
|
|
||
|
}, this);
|
||
|
|
||
|
return (new Y.ArrayList(removed));
|
||
|
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Selects the child at the given index (zero-based).
|
||
|
*
|
||
|
* @method selectChild
|
||
|
* @param {Number} i the index of the child to be selected
|
||
|
*/
|
||
|
selectChild: function(i) {
|
||
|
this.item(i).set('selected', 1);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Selects all children.
|
||
|
*
|
||
|
* @method selectAll
|
||
|
*/
|
||
|
selectAll: function () {
|
||
|
this.set("selected", 1);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Deselects all children.
|
||
|
*
|
||
|
* @method deselectAll
|
||
|
*/
|
||
|
deselectAll: function () {
|
||
|
this.set("selected", 0);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Updates the UI in response to a child being added.
|
||
|
*
|
||
|
* @method _uiAddChild
|
||
|
* @protected
|
||
|
* @param child {Widget} The child Widget instance to render.
|
||
|
* @param parentNode {Object} The Node under which the
|
||
|
* child Widget is to be rendered.
|
||
|
*/
|
||
|
_uiAddChild: function (child, parentNode) {
|
||
|
|
||
|
child.render(parentNode);
|
||
|
|
||
|
// TODO: Ideally this should be in Child's render UI.
|
||
|
|
||
|
var childBB = child.get("boundingBox"),
|
||
|
siblingBB,
|
||
|
nextSibling = child.next(false),
|
||
|
prevSibling;
|
||
|
|
||
|
// Insert or Append to last child.
|
||
|
|
||
|
// Avoiding index, and using the current sibling
|
||
|
// state (which should be accurate), means we don't have
|
||
|
// to worry about decorator elements which may be added
|
||
|
// to the _childContainer node.
|
||
|
|
||
|
if (nextSibling && nextSibling.get(RENDERED)) {
|
||
|
|
||
|
siblingBB = nextSibling.get(BOUNDING_BOX);
|
||
|
siblingBB.insert(childBB, "before");
|
||
|
|
||
|
} else {
|
||
|
|
||
|
prevSibling = child.previous(false);
|
||
|
|
||
|
if (prevSibling && prevSibling.get(RENDERED)) {
|
||
|
|
||
|
siblingBB = prevSibling.get(BOUNDING_BOX);
|
||
|
siblingBB.insert(childBB, "after");
|
||
|
|
||
|
} else if (!parentNode.contains(childBB)) {
|
||
|
|
||
|
// Based on pull request from andreas-karlsson
|
||
|
// https://github.com/yui/yui3/pull/25#issuecomment-2103536
|
||
|
|
||
|
// Account for case where a child was rendered independently of the
|
||
|
// parent-child framework, to a node outside of the parentNode,
|
||
|
// and there are no siblings.
|
||
|
|
||
|
parentNode.appendChild(childBB);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Updates the UI in response to a child being removed.
|
||
|
*
|
||
|
* @method _uiRemoveChild
|
||
|
* @protected
|
||
|
* @param child {Widget} The child Widget instance to render.
|
||
|
*/
|
||
|
_uiRemoveChild: function (child) {
|
||
|
child.get("boundingBox").remove();
|
||
|
},
|
||
|
|
||
|
_afterAddChild: function (event) {
|
||
|
var child = event.child;
|
||
|
|
||
|
if (child.get("parent") == this) {
|
||
|
this._uiAddChild(child, this._childrenContainer);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_afterRemoveChild: function (event) {
|
||
|
var child = event.child;
|
||
|
|
||
|
if (child._oldParent == this) {
|
||
|
this._uiRemoveChild(child);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Sets up DOM and CustomEvent listeners for the parent widget.
|
||
|
* <p>
|
||
|
* This method in invoked after bindUI is invoked for the Widget class
|
||
|
* using YUI's aop infrastructure.
|
||
|
* </p>
|
||
|
*
|
||
|
* @method _bindUIParent
|
||
|
* @protected
|
||
|
*/
|
||
|
_bindUIParent: function () {
|
||
|
this.after("addChild", this._afterAddChild);
|
||
|
this.after("removeChild", this._afterRemoveChild);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Renders all child Widgets for the parent.
|
||
|
* <p>
|
||
|
* This method in invoked after renderUI is invoked for the Widget class
|
||
|
* using YUI's aop infrastructure.
|
||
|
* </p>
|
||
|
* @method _renderChildren
|
||
|
* @protected
|
||
|
*/
|
||
|
_renderChildren: function () {
|
||
|
|
||
|
/**
|
||
|
* <p>By default WidgetParent will render it's children to the parent's content box.</p>
|
||
|
*
|
||
|
* <p>If the children need to be rendered somewhere else, the _childrenContainer property
|
||
|
* can be set to the Node which the children should be rendered to. This property should be
|
||
|
* set before the _renderChildren method is invoked, ideally in your renderUI method,
|
||
|
* as soon as you create the element to be rendered to.</p>
|
||
|
*
|
||
|
* @protected
|
||
|
* @property _childrenContainer
|
||
|
* @value The content box
|
||
|
* @type Node
|
||
|
*/
|
||
|
var renderTo = this._childrenContainer || this.get("contentBox");
|
||
|
|
||
|
this._childrenContainer = renderTo;
|
||
|
|
||
|
this.each(function (child) {
|
||
|
child.render(renderTo);
|
||
|
});
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Destroys all child Widgets for the parent.
|
||
|
* <p>
|
||
|
* This method is invoked before the destructor is invoked for the Widget
|
||
|
* class using YUI's aop infrastructure.
|
||
|
* </p>
|
||
|
* @method _destroyChildren
|
||
|
* @protected
|
||
|
*/
|
||
|
_destroyChildren: function () {
|
||
|
|
||
|
// Detach the handler responsible for removing children in
|
||
|
// response to destroying them since:
|
||
|
// 1) It is unnecessary/inefficient at this point since we are doing
|
||
|
// a batch destroy of all children.
|
||
|
// 2) Removing each child will affect our ability to iterate the
|
||
|
// children since the size of _items will be changing as we
|
||
|
// iterate.
|
||
|
this._hDestroyChild.detach();
|
||
|
|
||
|
// Need to clone the _items array since
|
||
|
this.each(function (child) {
|
||
|
child.destroy();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
Y.augment(Parent, Y.ArrayList);
|
||
|
|
||
|
Y.WidgetParent = Parent;
|
||
|
|
||
|
|
||
|
}, '3.17.2', {"requires": ["arraylist", "base-build", "widget"]});
|