provide installation instructions
[extjs.git] / source / core / CompositeElement.js
1 /*\r
2  * Ext JS Library 2.2.1\r
3  * Copyright(c) 2006-2009, Ext JS, LLC.\r
4  * licensing@extjs.com\r
5  * \r
6  * http://extjs.com/license\r
7  */\r
8 \r
9 /**\r
10  * @class Ext.CompositeElement\r
11  * Standard composite class. Creates a Ext.Element for every element in the collection.\r
12  * <br><br>\r
13  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Ext.Element. All Ext.Element\r
14  * actions will be performed on all the elements in this collection.</b>\r
15  * <br><br>\r
16  * All methods return <i>this</i> and can be chained.\r
17  <pre><code>\r
18  var els = Ext.select("#some-el div.some-class", true);\r
19  // or select directly from an existing element\r
20  var el = Ext.get('some-el');\r
21  el.select('div.some-class', true);\r
22 \r
23  els.setWidth(100); // all elements become 100 width\r
24  els.hide(true); // all elements fade out and hide\r
25  // or\r
26  els.setWidth(100).hide(true);\r
27  </code></pre>\r
28  */\r
29 Ext.CompositeElement = function(els){\r
30     this.elements = [];\r
31     this.addElements(els);\r
32 };\r
33 Ext.CompositeElement.prototype = {\r
34     isComposite: true,\r
35     addElements : function(els){\r
36         if(!els) return this;\r
37         if(typeof els == "string"){\r
38             els = Ext.Element.selectorFunction(els);\r
39         }\r
40         var yels = this.elements;\r
41         var index = yels.length-1;\r
42         for(var i = 0, len = els.length; i < len; i++) {\r
43                 yels[++index] = Ext.get(els[i]);\r
44         }\r
45         return this;\r
46     },\r
47 \r
48     /**\r
49     * Clears this composite and adds the elements returned by the passed selector.\r
50     * @param {String/Array} els A string CSS selector, an array of elements or an element\r
51     * @return {CompositeElement} this\r
52     */\r
53     fill : function(els){\r
54         this.elements = [];\r
55         this.add(els);\r
56         return this;\r
57     },\r
58 \r
59     /**\r
60     * Filters this composite to only elements that match the passed selector.\r
61     * @param {String} selector A string CSS selector\r
62     * @return {CompositeElement} this\r
63     */\r
64     filter : function(selector){\r
65         var els = [];\r
66         this.each(function(el){\r
67             if(el.is(selector)){\r
68                 els[els.length] = el.dom;\r
69             }\r
70         });\r
71         this.fill(els);\r
72         return this;\r
73     },\r
74 \r
75     invoke : function(fn, args){\r
76         var els = this.elements;\r
77         for(var i = 0, len = els.length; i < len; i++) {\r
78                 Ext.Element.prototype[fn].apply(els[i], args);\r
79         }\r
80         return this;\r
81     },\r
82     /**\r
83     * Adds elements to this composite.\r
84     * @param {String/Array} els A string CSS selector, an array of elements or an element\r
85     * @return {CompositeElement} this\r
86     */\r
87     add : function(els){\r
88         if(typeof els == "string"){\r
89             this.addElements(Ext.Element.selectorFunction(els));\r
90         }else if(els.length !== undefined){\r
91             this.addElements(els);\r
92         }else{\r
93             this.addElements([els]);\r
94         }\r
95         return this;\r
96     },\r
97     /**\r
98     * Calls the passed function passing (el, this, index) for each element in this composite.\r
99     * @param {Function} fn The function to call\r
100     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)\r
101     * @return {CompositeElement} this\r
102     */\r
103     each : function(fn, scope){\r
104         var els = this.elements;\r
105         for(var i = 0, len = els.length; i < len; i++){\r
106             if(fn.call(scope || els[i], els[i], this, i) === false) {\r
107                 break;\r
108             }\r
109         }\r
110         return this;\r
111     },\r
112 \r
113     /**\r
114      * Returns the Element object at the specified index\r
115      * @param {Number} index\r
116      * @return {Ext.Element}\r
117      */\r
118     item : function(index){\r
119         return this.elements[index] || null;\r
120     },\r
121 \r
122     /**\r
123      * Returns the first Element\r
124      * @return {Ext.Element}\r
125      */\r
126     first : function(){\r
127         return this.item(0);\r
128     },\r
129 \r
130     /**\r
131      * Returns the last Element\r
132      * @return {Ext.Element}\r
133      */\r
134     last : function(){\r
135         return this.item(this.elements.length-1);\r
136     },\r
137 \r
138     /**\r
139      * Returns the number of elements in this composite\r
140      * @return Number\r
141      */\r
142     getCount : function(){\r
143         return this.elements.length;\r
144     },\r
145 \r
146     /**\r
147      * Returns true if this composite contains the passed element\r
148      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.\r
149      * @return Boolean\r
150      */\r
151     contains : function(el){\r
152         return this.indexOf(el) !== -1;\r
153     },\r
154 \r
155     /**\r
156      * Find the index of the passed element within the composite collection.\r
157      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.\r
158      * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found.\r
159      */\r
160     indexOf : function(el){\r
161         return this.elements.indexOf(Ext.get(el));\r
162     },\r
163 \r
164 \r
165     /**\r
166     * Removes the specified element(s).\r
167     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite\r
168     * or an array of any of those.\r
169     * @param {Boolean} removeDom (optional) True to also remove the element from the document\r
170     * @return {CompositeElement} this\r
171     */\r
172     removeElement : function(el, removeDom){\r
173         if(Ext.isArray(el)){\r
174             for(var i = 0, len = el.length; i < len; i++){\r
175                 this.removeElement(el[i]);\r
176             }\r
177             return this;\r
178         }\r
179         var index = typeof el == 'number' ? el : this.indexOf(el);\r
180         if(index !== -1 && this.elements[index]){\r
181             if(removeDom){\r
182                 var d = this.elements[index];\r
183                 if(d.dom){\r
184                     d.remove();\r
185                 }else{\r
186                     Ext.removeNode(d);\r
187                 }\r
188             }\r
189             this.elements.splice(index, 1);\r
190         }\r
191         return this;\r
192     },\r
193 \r
194     /**\r
195     * Replaces the specified element with the passed element.\r
196     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite\r
197     * to replace.\r
198     * @param {Mixed} replacement The id of an element or the Element itself.\r
199     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.\r
200     * @return {CompositeElement} this\r
201     */\r
202     replaceElement : function(el, replacement, domReplace){\r
203         var index = typeof el == 'number' ? el : this.indexOf(el);\r
204         if(index !== -1){\r
205             if(domReplace){\r
206                 this.elements[index].replaceWith(replacement);\r
207             }else{\r
208                 this.elements.splice(index, 1, Ext.get(replacement))\r
209             }\r
210         }\r
211         return this;\r
212     },\r
213 \r
214     /**\r
215      * Removes all elements.\r
216      */\r
217     clear : function(){\r
218         this.elements = [];\r
219     }\r
220 };\r
221 (function(){\r
222 Ext.CompositeElement.createCall = function(proto, fnName){\r
223     if(!proto[fnName]){\r
224         proto[fnName] = function(){\r
225             return this.invoke(fnName, arguments);\r
226         };\r
227     }\r
228 };\r
229 for(var fnName in Ext.Element.prototype){\r
230     if(typeof Ext.Element.prototype[fnName] == "function"){\r
231         Ext.CompositeElement.createCall(Ext.CompositeElement.prototype, fnName);\r
232     }\r
233 };\r
234 })();\r
235 \r
236 /**\r
237  * @class Ext.CompositeElementLite\r
238  * @extends Ext.CompositeElement\r
239  * Flyweight composite class. Reuses the same Ext.Element for element operations.\r
240  <pre><code>\r
241  var els = Ext.select("#some-el div.some-class");\r
242  // or select directly from an existing element\r
243  var el = Ext.get('some-el');\r
244  el.select('div.some-class');\r
245 \r
246  els.setWidth(100); // all elements become 100 width\r
247  els.hide(true); // all elements fade out and hide\r
248  // or\r
249  els.setWidth(100).hide(true);\r
250  </code></pre><br><br>\r
251  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Ext.Element. All Ext.Element\r
252  * actions will be performed on all the elements in this collection.</b>\r
253  */\r
254 Ext.CompositeElementLite = function(els){\r
255     Ext.CompositeElementLite.superclass.constructor.call(this, els);\r
256     this.el = new Ext.Element.Flyweight();\r
257 };\r
258 Ext.extend(Ext.CompositeElementLite, Ext.CompositeElement, {\r
259     addElements : function(els){\r
260         if(els){\r
261             if(Ext.isArray(els)){\r
262                 this.elements = this.elements.concat(els);\r
263             }else{\r
264                 var yels = this.elements;\r
265                 var index = yels.length-1;\r
266                 for(var i = 0, len = els.length; i < len; i++) {\r
267                     yels[++index] = els[i];\r
268                 }\r
269             }\r
270         }\r
271         return this;\r
272     },\r
273     invoke : function(fn, args){\r
274         var els = this.elements;\r
275         var el = this.el;\r
276         for(var i = 0, len = els.length; i < len; i++) {\r
277             el.dom = els[i];\r
278                 Ext.Element.prototype[fn].apply(el, args);\r
279         }\r
280         return this;\r
281     },\r
282     /**\r
283      * Returns a flyweight Element of the dom element object at the specified index\r
284      * @param {Number} index\r
285      * @return {Ext.Element}\r
286      */\r
287     item : function(index){\r
288         if(!this.elements[index]){\r
289             return null;\r
290         }\r
291         this.el.dom = this.elements[index];\r
292         return this.el;\r
293     },\r
294 \r
295     // fixes scope with flyweight\r
296     addListener : function(eventName, handler, scope, opt){\r
297         var els = this.elements;\r
298         for(var i = 0, len = els.length; i < len; i++) {\r
299             Ext.EventManager.on(els[i], eventName, handler, scope || els[i], opt);\r
300         }\r
301         return this;\r
302     },\r
303 \r
304     /**\r
305     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element\r
306     * passed is the flyweight (shared) Ext.Element instance, so if you require a\r
307     * a reference to the dom node, use el.dom.</b>\r
308     * @param {Function} fn The function to call\r
309     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)\r
310     * @return {CompositeElement} this\r
311     */\r
312     each : function(fn, scope){\r
313         var els = this.elements;\r
314         var el = this.el;\r
315         for(var i = 0, len = els.length; i < len; i++){\r
316             el.dom = els[i];\r
317                 if(fn.call(scope || el, el, this, i) === false){\r
318                 break;\r
319             }\r
320         }\r
321         return this;\r
322     },\r
323 \r
324     indexOf : function(el){\r
325         return this.elements.indexOf(Ext.getDom(el));\r
326     },\r
327 \r
328     replaceElement : function(el, replacement, domReplace){\r
329         var index = typeof el == 'number' ? el : this.indexOf(el);\r
330         if(index !== -1){\r
331             replacement = Ext.getDom(replacement);\r
332             if(domReplace){\r
333                 var d = this.elements[index];\r
334                 d.parentNode.insertBefore(replacement, d);\r
335                 Ext.removeNode(d);\r
336             }\r
337             this.elements.splice(index, 1, replacement);\r
338         }\r
339         return this;\r
340     }\r
341 });\r
342 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;\r
343 if(Ext.DomQuery){\r
344     Ext.Element.selectorFunction = Ext.DomQuery.select;\r
345 }\r
346 \r
347 Ext.Element.select = function(selector, unique, root){\r
348     var els;\r
349     if(typeof selector == "string"){\r
350         els = Ext.Element.selectorFunction(selector, root);\r
351     }else if(selector.length !== undefined){\r
352         els = selector;\r
353     }else{\r
354         throw "Invalid selector";\r
355     }\r
356     if(unique === true){\r
357         return new Ext.CompositeElement(els);\r
358     }else{\r
359         return new Ext.CompositeElementLite(els);\r
360     }\r
361 };\r
362 /**\r
363  * Selects elements based on the passed CSS selector to enable working on them as 1.\r
364  * @param {String/Array} selector The CSS selector or an array of elements\r
365  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)\r
366  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root\r
367  * @return {CompositeElementLite/CompositeElement}\r
368  * @member Ext\r
369  * @method select\r
370  */\r
371 Ext.select = Ext.Element.select;