Upgrade to ExtJS 4.0.7 - Released 10/19/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.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      * @property {HTMLElement} elements
58      */
59     this.elements = [];
60     this.add(els, root);
61     this.el = new Ext.Element.Flyweight();
62 };
63
64 Ext.CompositeElementLite.prototype = {
65     isComposite: true,
66
67     // private
68     getElement : function(el){
69         // Set the shared flyweight dom property to the current element
70         var e = this.el;
71         e.dom = el;
72         e.id = el.id;
73         return e;
74     },
75
76     // private
77     transformElement : function(el){
78         return Ext.getDom(el);
79     },
80
81     /**
82      * Returns the number of elements in this Composite.
83      * @return Number
84      */
85     getCount : function(){
86         return this.elements.length;
87     },
88     /**
89      * Adds elements to this Composite object.
90      * @param {HTMLElement[]/Ext.CompositeElement} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.
91      * @return {Ext.CompositeElement} This Composite object.
92      */
93     add : function(els, root){
94         var me = this,
95             elements = me.elements;
96         if(!els){
97             return this;
98         }
99         if(typeof els == "string"){
100             els = Ext.Element.selectorFunction(els, root);
101         }else if(els.isComposite){
102             els = els.elements;
103         }else if(!Ext.isIterable(els)){
104             els = [els];
105         }
106
107         for(var i = 0, len = els.length; i < len; ++i){
108             elements.push(me.transformElement(els[i]));
109         }
110         return me;
111     },
112
113     invoke : function(fn, args){
114         var me = this,
115             els = me.elements,
116             len = els.length,
117             e,
118             i;
119
120         for(i = 0; i < len; i++) {
121             e = els[i];
122             if(e){
123                 Ext.Element.prototype[fn].apply(me.getElement(e), args);
124             }
125         }
126         return me;
127     },
128     /**
129      * Returns a flyweight Element of the dom element object at the specified index
130      * @param {Number} index
131      * @return {Ext.Element}
132      */
133     item : function(index){
134         var me = this,
135             el = me.elements[index],
136             out = null;
137
138         if(el){
139             out = me.getElement(el);
140         }
141         return out;
142     },
143
144     // fixes scope with flyweight
145     addListener : function(eventName, handler, scope, opt){
146         var els = this.elements,
147             len = els.length,
148             i, e;
149
150         for(i = 0; i<len; i++) {
151             e = els[i];
152             if(e) {
153                 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
154             }
155         }
156         return this;
157     },
158     /**
159      * <p>Calls the passed function for each element in this composite.</p>
160      * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
161      * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
162      * <b>This is the flyweight (shared) Ext.Element instance, so if you require a
163      * a reference to the dom node, use el.dom.</b></div></li>
164      * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
165      * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
166      * </ul>
167      * @param {Object} [scope] The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
168      * @return {Ext.CompositeElement} this
169      */
170     each : function(fn, scope){
171         var me = this,
172             els = me.elements,
173             len = els.length,
174             i, e;
175
176         for(i = 0; i<len; i++) {
177             e = els[i];
178             if(e){
179                 e = this.getElement(e);
180                 if(fn.call(scope || e, e, me, i) === false){
181                     break;
182                 }
183             }
184         }
185         return me;
186     },
187
188     /**
189     * Clears this Composite and adds the elements passed.
190     * @param {HTMLElement[]/Ext.CompositeElement} els Either an array of DOM elements, or another Composite from which to fill this Composite.
191     * @return {Ext.CompositeElement} this
192     */
193     fill : function(els){
194         var me = this;
195         me.elements = [];
196         me.add(els);
197         return me;
198     },
199
200     /**
201      * Filters this composite to only elements that match the passed selector.
202      * @param {String/Function} selector A string CSS selector or a comparison function.
203      * The comparison function will be called with the following arguments:<ul>
204      * <li><code>el</code> : Ext.Element<div class="sub-desc">The current DOM element.</div></li>
205      * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
206      * </ul>
207      * @return {Ext.CompositeElement} this
208      */
209     filter : function(selector){
210         var els = [],
211             me = this,
212             fn = Ext.isFunction(selector) ? selector
213                 : function(el){
214                     return el.is(selector);
215                 };
216
217         me.each(function(el, self, i) {
218             if (fn(el, i) !== false) {
219                 els[els.length] = me.transformElement(el);
220             }
221         });
222
223         me.elements = els;
224         return me;
225     },
226
227     /**
228      * Find the index of the passed element within the composite collection.
229      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
230      * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found.
231      */
232     indexOf : function(el){
233         return Ext.Array.indexOf(this.elements, this.transformElement(el));
234     },
235
236     /**
237     * Replaces the specified element with the passed element.
238     * @param {String/HTMLElement/Ext.Element/Number} el The id of an element, the Element itself, the index of the element in this composite
239     * to replace.
240     * @param {String/Ext.Element} replacement The id of an element or the Element itself.
241     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
242     * @return {Ext.CompositeElement} this
243     */
244     replaceElement : function(el, replacement, domReplace){
245         var index = !isNaN(el) ? el : this.indexOf(el),
246             d;
247         if(index > -1){
248             replacement = Ext.getDom(replacement);
249             if(domReplace){
250                 d = this.elements[index];
251                 d.parentNode.insertBefore(replacement, d);
252                 Ext.removeNode(d);
253             }
254             Ext.Array.splice(this.elements, index, 1, replacement);
255         }
256         return this;
257     },
258
259     /**
260      * Removes all elements.
261      */
262     clear : function(){
263         this.elements = [];
264     }
265 };
266
267 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
268
269 /**
270  * @private
271  * Copies all of the functions from Ext.Element's prototype onto CompositeElementLite's prototype.
272  * This is called twice - once immediately below, and once again after additional Ext.Element
273  * are added in Ext JS
274  */
275 Ext.CompositeElementLite.importElementMethods = function() {
276     var fnName,
277         ElProto = Ext.Element.prototype,
278         CelProto = Ext.CompositeElementLite.prototype;
279
280     for (fnName in ElProto) {
281         if (typeof ElProto[fnName] == 'function'){
282             (function(fnName) {
283                 CelProto[fnName] = CelProto[fnName] || function() {
284                     return this.invoke(fnName, arguments);
285                 };
286             }).call(CelProto, fnName);
287
288         }
289     }
290 };
291
292 Ext.CompositeElementLite.importElementMethods();
293
294 if(Ext.DomQuery){
295     Ext.Element.selectorFunction = Ext.DomQuery.select;
296 }
297
298 /**
299  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
300  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
301  * {@link Ext.CompositeElementLite CompositeElementLite} object.
302  * @param {String/HTMLElement[]} selector The CSS selector or an array of elements
303  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
304  * @return {Ext.CompositeElementLite/Ext.CompositeElement}
305  * @member Ext.Element
306  * @method select
307  */
308 Ext.Element.select = function(selector, root){
309     var els;
310     if(typeof selector == "string"){
311         els = Ext.Element.selectorFunction(selector, root);
312     }else if(selector.length !== undefined){
313         els = selector;
314     }else{
315         //<debug>
316         Ext.Error.raise({
317             sourceClass: "Ext.Element",
318             sourceMethod: "select",
319             selector: selector,
320             root: root,
321             msg: "Invalid selector specified: " + selector
322         });
323         //</debug>
324     }
325     return new Ext.CompositeElementLite(els);
326 };
327 /**
328  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
329  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
330  * {@link Ext.CompositeElementLite CompositeElementLite} object.
331  * @param {String/HTMLElement[]} selector The CSS selector or an array of elements
332  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
333  * @return {Ext.CompositeElementLite/Ext.CompositeElement}
334  * @member Ext
335  * @method select
336  */
337 Ext.select = Ext.Element.select;
338