Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / src / core / src / dom / CompositeElementLite.js
1 /**
2  * @class Ext.CompositeElementLite
3  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
4  * members, or to perform collective actions upon the whole set.</p>
5  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.core.Element} and
6  * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.</p>
7  * Example:<pre><code>
8 var els = Ext.select("#some-el div.some-class");
9 // or select directly from an existing element
10 var el = Ext.get('some-el');
11 el.select('div.some-class');
12
13 els.setWidth(100); // all elements become 100 width
14 els.hide(true); // all elements fade out and hide
15 // or
16 els.setWidth(100).hide(true);
17 </code></pre>
18  */
19 Ext.CompositeElementLite = function(els, root){
20     /**
21      * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>
22      * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing
23      * to augment the capabilities of the CompositeElementLite class may use it when adding
24      * methods to the class.</p>
25      * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all
26      * following siblings of selected elements, the code would be</p><code><pre>
27 Ext.override(Ext.CompositeElementLite, {
28     nextAll: function() {
29         var els = this.elements, i, l = els.length, n, r = [], ri = -1;
30
31 //      Loop through all elements in this Composite, accumulating
32 //      an Array of all siblings.
33         for (i = 0; i < l; i++) {
34             for (n = els[i].nextSibling; n; n = n.nextSibling) {
35                 r[++ri] = n;
36             }
37         }
38
39 //      Add all found siblings to this Composite
40         return this.add(r);
41     }
42 });</pre></code>
43      * @type Array
44      * @property elements
45      */
46     this.elements = [];
47     this.add(els, root);
48     this.el = new Ext.core.Element.Flyweight();
49 };
50
51 Ext.CompositeElementLite.prototype = {
52     isComposite: true,
53
54     // private
55     getElement : function(el){
56         // Set the shared flyweight dom property to the current element
57         var e = this.el;
58         e.dom = el;
59         e.id = el.id;
60         return e;
61     },
62
63     // private
64     transformElement : function(el){
65         return Ext.getDom(el);
66     },
67
68     /**
69      * Returns the number of elements in this Composite.
70      * @return Number
71      */
72     getCount : function(){
73         return this.elements.length;
74     },
75     /**
76      * Adds elements to this Composite object.
77      * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.
78      * @return {CompositeElement} This Composite object.
79      */
80     add : function(els, root){
81         var me = this,
82             elements = me.elements;
83         if(!els){
84             return this;
85         }
86         if(typeof els == "string"){
87             els = Ext.core.Element.selectorFunction(els, root);
88         }else if(els.isComposite){
89             els = els.elements;
90         }else if(!Ext.isIterable(els)){
91             els = [els];
92         }
93
94         for(var i = 0, len = els.length; i < len; ++i){
95             elements.push(me.transformElement(els[i]));
96         }
97         return me;
98     },
99
100     invoke : function(fn, args){
101         var me = this,
102             els = me.elements,
103             len = els.length,
104             e,
105             i;
106
107         for(i = 0; i < len; i++) {
108             e = els[i];
109             if(e){
110                 Ext.core.Element.prototype[fn].apply(me.getElement(e), args);
111             }
112         }
113         return me;
114     },
115     /**
116      * Returns a flyweight Element of the dom element object at the specified index
117      * @param {Number} index
118      * @return {Ext.core.Element}
119      */
120     item : function(index){
121         var me = this,
122             el = me.elements[index],
123             out = null;
124
125         if(el){
126             out = me.getElement(el);
127         }
128         return out;
129     },
130
131     // fixes scope with flyweight
132     addListener : function(eventName, handler, scope, opt){
133         var els = this.elements,
134             len = els.length,
135             i, e;
136
137         for(i = 0; i<len; i++) {
138             e = els[i];
139             if(e) {
140                 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
141             }
142         }
143         return this;
144     },
145     /**
146      * <p>Calls the passed function for each element in this composite.</p>
147      * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
148      * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
149      * <b>This is the flyweight (shared) Ext.core.Element instance, so if you require a
150      * a reference to the dom node, use el.dom.</b></div></li>
151      * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
152      * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
153      * </ul>
154      * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
155      * @return {CompositeElement} this
156      */
157     each : function(fn, scope){
158         var me = this,
159             els = me.elements,
160             len = els.length,
161             i, e;
162
163         for(i = 0; i<len; i++) {
164             e = els[i];
165             if(e){
166                 e = this.getElement(e);
167                 if(fn.call(scope || e, e, me, i) === false){
168                     break;
169                 }
170             }
171         }
172         return me;
173     },
174
175     /**
176     * Clears this Composite and adds the elements passed.
177     * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite.
178     * @return {CompositeElement} this
179     */
180     fill : function(els){
181         var me = this;
182         me.elements = [];
183         me.add(els);
184         return me;
185     },
186
187     /**
188      * Filters this composite to only elements that match the passed selector.
189      * @param {String/Function} selector A string CSS selector or a comparison function.
190      * The comparison function will be called with the following arguments:<ul>
191      * <li><code>el</code> : Ext.core.Element<div class="sub-desc">The current DOM element.</div></li>
192      * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
193      * </ul>
194      * @return {CompositeElement} this
195      */
196     filter : function(selector){
197         var els = [],
198             me = this,
199             fn = Ext.isFunction(selector) ? selector
200                 : function(el){
201                     return el.is(selector);
202                 };
203
204         me.each(function(el, self, i) {
205             if (fn(el, i) !== false) {
206                 els[els.length] = me.transformElement(el);
207             }
208         });
209         
210         me.elements = els;
211         return me;
212     },
213
214     /**
215      * Find the index of the passed element within the composite collection.
216      * @param el {Mixed} The id of an element, or an Ext.core.Element, or an HtmlElement to find within the composite collection.
217      * @return Number The index of the passed Ext.core.Element in the composite collection, or -1 if not found.
218      */
219     indexOf : function(el){
220         return Ext.Array.indexOf(this.elements, this.transformElement(el));
221     },
222
223     /**
224     * Replaces the specified element with the passed element.
225     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
226     * to replace.
227     * @param {Mixed} replacement The id of an element or the Element itself.
228     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
229     * @return {CompositeElement} this
230     */
231     replaceElement : function(el, replacement, domReplace){
232         var index = !isNaN(el) ? el : this.indexOf(el),
233             d;
234         if(index > -1){
235             replacement = Ext.getDom(replacement);
236             if(domReplace){
237                 d = this.elements[index];
238                 d.parentNode.insertBefore(replacement, d);
239                 Ext.removeNode(d);
240             }
241             this.elements.splice(index, 1, replacement);
242         }
243         return this;
244     },
245
246     /**
247      * Removes all elements.
248      */
249     clear : function(){
250         this.elements = [];
251     }
252 };
253
254 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
255
256 /**
257  * @private
258  * Copies all of the functions from Ext.core.Element's prototype onto CompositeElementLite's prototype.
259  * This is called twice - once immediately below, and once again after additional Ext.core.Element
260  * are added in Ext JS
261  */
262 Ext.CompositeElementLite.importElementMethods = function() {
263     var fnName,
264         ElProto = Ext.core.Element.prototype,
265         CelProto = Ext.CompositeElementLite.prototype;
266
267     for (fnName in ElProto) {
268         if (typeof ElProto[fnName] == 'function'){
269             (function(fnName) {
270                 CelProto[fnName] = CelProto[fnName] || function() {
271                     return this.invoke(fnName, arguments);
272                 };
273             }).call(CelProto, fnName);
274
275         }
276     }
277 };
278
279 Ext.CompositeElementLite.importElementMethods();
280
281 if(Ext.DomQuery){
282     Ext.core.Element.selectorFunction = Ext.DomQuery.select;
283 }
284
285 /**
286  * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
287  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
288  * {@link Ext.CompositeElementLite CompositeElementLite} object.
289  * @param {String/Array} selector The CSS selector or an array of elements
290  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
291  * @return {CompositeElementLite/CompositeElement}
292  * @member Ext.core.Element
293  * @method select
294  */
295 Ext.core.Element.select = function(selector, root){
296     var els;
297     if(typeof selector == "string"){
298         els = Ext.core.Element.selectorFunction(selector, root);
299     }else if(selector.length !== undefined){
300         els = selector;
301     }else{
302         //<debug>
303         Ext.Error.raise({
304             sourceClass: "Ext.core.Element",
305             sourceMethod: "select",
306             selector: selector,
307             root: root,
308             msg: "Invalid selector specified: " + selector
309         });
310         //</debug>
311     }
312     return new Ext.CompositeElementLite(els);
313 };
314 /**
315  * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
316  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
317  * {@link Ext.CompositeElementLite CompositeElementLite} object.
318  * @param {String/Array} selector The CSS selector or an array of elements
319  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
320  * @return {CompositeElementLite/CompositeElement}
321  * @member Ext
322  * @method select
323  */
324 Ext.select = Ext.core.Element.select;