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