Upgrade to ExtJS 3.0.0 - Released 07/06/2009
[extjs.git] / docs / source / MixedCollection.html
1 <html>\r
2 <head>\r
3   <title>The source code</title>\r
4     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />\r
5     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>\r
6 </head>\r
7 <body  onload="prettyPrint();">\r
8     <pre class="prettyprint lang-js"><div id="cls-Ext.util.MixedCollection"></div>/**\r
9  * @class Ext.util.MixedCollection\r
10  * @extends Ext.util.Observable\r
11  * A Collection class that maintains both numeric indexes and keys and exposes events.\r
12  * @constructor\r
13  * @param {Boolean} allowFunctions True if the addAll function should add function references to the\r
14  * collection (defaults to false)\r
15  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection\r
16  * and return the key value for that item.  This is used when available to look up the key on items that\r
17  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is\r
18  * equivalent to providing an implementation for the {@link #getKey} method.\r
19  */\r
20 Ext.util.MixedCollection = function(allowFunctions, keyFn){\r
21     this.items = [];\r
22     this.map = {};\r
23     this.keys = [];\r
24     this.length = 0;\r
25     this.addEvents(\r
26         <div id="event-Ext.util.MixedCollection-clear"></div>/**\r
27          * @event clear\r
28          * Fires when the collection is cleared.\r
29          */\r
30         "clear",\r
31         <div id="event-Ext.util.MixedCollection-add"></div>/**\r
32          * @event add\r
33          * Fires when an item is added to the collection.\r
34          * @param {Number} index The index at which the item was added.\r
35          * @param {Object} o The item added.\r
36          * @param {String} key The key associated with the added item.\r
37          */\r
38         "add",\r
39         <div id="event-Ext.util.MixedCollection-replace"></div>/**\r
40          * @event replace\r
41          * Fires when an item is replaced in the collection.\r
42          * @param {String} key he key associated with the new added.\r
43          * @param {Object} old The item being replaced.\r
44          * @param {Object} new The new item.\r
45          */\r
46         "replace",\r
47         <div id="event-Ext.util.MixedCollection-remove"></div>/**\r
48          * @event remove\r
49          * Fires when an item is removed from the collection.\r
50          * @param {Object} o The item being removed.\r
51          * @param {String} key (optional) The key associated with the removed item.\r
52          */\r
53         "remove",\r
54         "sort"\r
55     );\r
56     this.allowFunctions = allowFunctions === true;\r
57     if(keyFn){\r
58         this.getKey = keyFn;\r
59     }\r
60     Ext.util.MixedCollection.superclass.constructor.call(this);\r
61 };\r
62 \r
63 Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {\r
64     allowFunctions : false,\r
65 \r
66 <div id="method-Ext.util.MixedCollection-add"></div>/**\r
67  * Adds an item to the collection. Fires the {@link #add} event when complete.\r
68  * @param {String} key <p>The key to associate with the item, or the new item.</p>\r
69  * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key\r
70  * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection\r
71  * will be able to <i>derive</i> the key for the new item. In this case just pass the new item in\r
72  * this parameter.</p>\r
73  * @param {Object} o The item to add.\r
74  * @return {Object} The item added.\r
75  */\r
76     add: function(key, o){\r
77         if(arguments.length == 1){\r
78             o = arguments[0];\r
79             key = this.getKey(o);\r
80         }\r
81         if(typeof key != 'undefined' && key !== null){\r
82             var old = this.map[key];\r
83             if(typeof old != 'undefined'){\r
84                 return this.replace(key, o);\r
85             }\r
86             this.map[key] = o;\r
87         }\r
88         this.length++;\r
89         this.items.push(o);\r
90         this.keys.push(key);\r
91         this.fireEvent('add', this.length-1, o, key);\r
92         return o;\r
93     },\r
94 \r
95 <div id="method-Ext.util.MixedCollection-getKey"></div>/**\r
96   * MixedCollection has a generic way to fetch keys if you implement getKey.  The default implementation\r
97   * simply returns <tt style="font-weight:bold;">item.id</tt> but you can provide your own implementation\r
98   * to return a different value as in the following examples:\r
99 <pre><code>\r
100 // normal way\r
101 var mc = new Ext.util.MixedCollection();\r
102 mc.add(someEl.dom.id, someEl);\r
103 mc.add(otherEl.dom.id, otherEl);\r
104 //and so on\r
105 \r
106 // using getKey\r
107 var mc = new Ext.util.MixedCollection();\r
108 mc.getKey = function(el){\r
109    return el.dom.id;\r
110 };\r
111 mc.add(someEl);\r
112 mc.add(otherEl);\r
113 \r
114 // or via the constructor\r
115 var mc = new Ext.util.MixedCollection(false, function(el){\r
116    return el.dom.id;\r
117 });\r
118 mc.add(someEl);\r
119 mc.add(otherEl);\r
120 </code></pre>\r
121  * @param {Object} item The item for which to find the key.\r
122  * @return {Object} The key for the passed item.\r
123  */\r
124     getKey : function(o){\r
125          return o.id;\r
126     },\r
127 \r
128 <div id="method-Ext.util.MixedCollection-replace"></div>/**\r
129  * Replaces an item in the collection. Fires the {@link #replace} event when complete.\r
130  * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>\r
131  * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key\r
132  * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection\r
133  * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item\r
134  * with one having the same key value, then just pass the replacement item in this parameter.</p>\r
135  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate\r
136  * with that key.\r
137  * @return {Object}  The new item.\r
138  */\r
139     replace : function(key, o){\r
140         if(arguments.length == 1){\r
141             o = arguments[0];\r
142             key = this.getKey(o);\r
143         }\r
144         var old = this.map[key];\r
145         if(typeof key == "undefined" || key === null || typeof old == "undefined"){\r
146              return this.add(key, o);\r
147         }\r
148         var index = this.indexOfKey(key);\r
149         this.items[index] = o;\r
150         this.map[key] = o;\r
151         this.fireEvent("replace", key, old, o);\r
152         return o;\r
153     },\r
154 \r
155 <div id="method-Ext.util.MixedCollection-addAll"></div>/**\r
156  * Adds all elements of an Array or an Object to the collection.\r
157  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or\r
158  * an Array of values, each of which are added to the collection.\r
159  */\r
160     addAll : function(objs){\r
161         if(arguments.length > 1 || Ext.isArray(objs)){\r
162             var args = arguments.length > 1 ? arguments : objs;\r
163             for(var i = 0, len = args.length; i < len; i++){\r
164                 this.add(args[i]);\r
165             }\r
166         }else{\r
167             for(var key in objs){\r
168                 if(this.allowFunctions || typeof objs[key] != "function"){\r
169                     this.add(key, objs[key]);\r
170                 }\r
171             }\r
172         }\r
173     },\r
174 \r
175 <div id="method-Ext.util.MixedCollection-each"></div>/**\r
176  * Executes the specified function once for every item in the collection, passing the following arguments:\r
177  * <div class="mdetail-params"><ul>\r
178  * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>\r
179  * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>\r
180  * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>\r
181  * </ul></div>\r
182  * The function should return a boolean value. Returning false from the function will stop the iteration.\r
183  * @param {Function} fn The function to execute for each item.\r
184  * @param {Object} scope (optional) The scope in which to execute the function.\r
185  */\r
186     each : function(fn, scope){\r
187         var items = [].concat(this.items); // each safe for removal\r
188         for(var i = 0, len = items.length; i < len; i++){\r
189             if(fn.call(scope || items[i], items[i], i, len) === false){\r
190                 break;\r
191             }\r
192         }\r
193     },\r
194 \r
195 <div id="method-Ext.util.MixedCollection-eachKey"></div>/**\r
196  * Executes the specified function once for every key in the collection, passing each\r
197  * key, and its associated item as the first two parameters.\r
198  * @param {Function} fn The function to execute for each item.\r
199  * @param {Object} scope (optional) The scope in which to execute the function.\r
200  */\r
201     eachKey : function(fn, scope){\r
202         for(var i = 0, len = this.keys.length; i < len; i++){\r
203             fn.call(scope || window, this.keys[i], this.items[i], i, len);\r
204         }\r
205     },\r
206 \r
207     <div id="method-Ext.util.MixedCollection-find"></div>/**\r
208      * Returns the first item in the collection which elicits a true return value from the\r
209      * passed selection function.\r
210      * @param {Function} fn The selection function to execute for each item.\r
211      * @param {Object} scope (optional) The scope in which to execute the function.\r
212      * @return {Object} The first item in the collection which returned true from the selection function.\r
213      */\r
214     find : function(fn, scope){\r
215         for(var i = 0, len = this.items.length; i < len; i++){\r
216             if(fn.call(scope || window, this.items[i], this.keys[i])){\r
217                 return this.items[i];\r
218             }\r
219         }\r
220         return null;\r
221     },\r
222 \r
223 <div id="method-Ext.util.MixedCollection-insert"></div>/**\r
224  * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.\r
225  * @param {Number} index The index to insert the item at.\r
226  * @param {String} key The key to associate with the new item, or the item itself.\r
227  * @param {Object} o (optional) If the second parameter was a key, the new item.\r
228  * @return {Object} The item inserted.\r
229  */\r
230     insert : function(index, key, o){\r
231         if(arguments.length == 2){\r
232             o = arguments[1];\r
233             key = this.getKey(o);\r
234         }\r
235         if(this.containsKey(key)){\r
236             this.suspendEvents();\r
237             this.removeKey(key);\r
238             this.resumeEvents();\r
239         }\r
240         if(index >= this.length){\r
241             return this.add(key, o);\r
242         }\r
243         this.length++;\r
244         this.items.splice(index, 0, o);\r
245         if(typeof key != "undefined" && key !== null){\r
246             this.map[key] = o;\r
247         }\r
248         this.keys.splice(index, 0, key);\r
249         this.fireEvent("add", index, o, key);\r
250         return o;\r
251     },\r
252 \r
253 <div id="method-Ext.util.MixedCollection-remove"></div>/**\r
254  * Remove an item from the collection.\r
255  * @param {Object} o The item to remove.\r
256  * @return {Object} The item removed or false if no item was removed.\r
257  */\r
258     remove : function(o){\r
259         return this.removeAt(this.indexOf(o));\r
260     },\r
261 \r
262 <div id="method-Ext.util.MixedCollection-removeAt"></div>/**\r
263  * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.\r
264  * @param {Number} index The index within the collection of the item to remove.\r
265  * @return {Object} The item removed or false if no item was removed.\r
266  */\r
267     removeAt : function(index){\r
268         if(index < this.length && index >= 0){\r
269             this.length--;\r
270             var o = this.items[index];\r
271             this.items.splice(index, 1);\r
272             var key = this.keys[index];\r
273             if(typeof key != "undefined"){\r
274                 delete this.map[key];\r
275             }\r
276             this.keys.splice(index, 1);\r
277             this.fireEvent("remove", o, key);\r
278             return o;\r
279         }\r
280         return false;\r
281     },\r
282 \r
283 <div id="method-Ext.util.MixedCollection-removeKey"></div>/**\r
284  * Removed an item associated with the passed key fom the collection.\r
285  * @param {String} key The key of the item to remove.\r
286  * @return {Object} The item removed or false if no item was removed.\r
287  */\r
288     removeKey : function(key){\r
289         return this.removeAt(this.indexOfKey(key));\r
290     },\r
291 \r
292 <div id="method-Ext.util.MixedCollection-getCount"></div>/**\r
293  * Returns the number of items in the collection.\r
294  * @return {Number} the number of items in the collection.\r
295  */\r
296     getCount : function(){\r
297         return this.length;\r
298     },\r
299 \r
300 <div id="method-Ext.util.MixedCollection-indexOf"></div>/**\r
301  * Returns index within the collection of the passed Object.\r
302  * @param {Object} o The item to find the index of.\r
303  * @return {Number} index of the item. Returns -1 if not found.\r
304  */\r
305     indexOf : function(o){\r
306         return this.items.indexOf(o);\r
307     },\r
308 \r
309 <div id="method-Ext.util.MixedCollection-indexOfKey"></div>/**\r
310  * Returns index within the collection of the passed key.\r
311  * @param {String} key The key to find the index of.\r
312  * @return {Number} index of the key.\r
313  */\r
314     indexOfKey : function(key){\r
315         return this.keys.indexOf(key);\r
316     },\r
317 \r
318 <div id="method-Ext.util.MixedCollection-item"></div>/**\r
319  * Returns the item associated with the passed key OR index. Key has priority over index.  This is the equivalent\r
320  * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.\r
321  * @param {String/Number} key The key or index of the item.\r
322  * @return {Object} If the item is found, returns the item.  If the item was not found, returns <tt>undefined</tt>.\r
323  * If an item was found, but is a Class, returns <tt>null</tt>.\r
324  */\r
325     item : function(key){\r
326         var mk = this.map[key],\r
327             item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;\r
328         return !Ext.isFunction(item) || this.allowFunctions ? item : null; // for prototype!\r
329     },\r
330 \r
331 <div id="method-Ext.util.MixedCollection-itemAt"></div>/**\r
332  * Returns the item at the specified index.\r
333  * @param {Number} index The index of the item.\r
334  * @return {Object} The item at the specified index.\r
335  */\r
336     itemAt : function(index){\r
337         return this.items[index];\r
338     },\r
339 \r
340 <div id="method-Ext.util.MixedCollection-key"></div>/**\r
341  * Returns the item associated with the passed key.\r
342  * @param {String/Number} key The key of the item.\r
343  * @return {Object} The item associated with the passed key.\r
344  */\r
345     key : function(key){\r
346         return this.map[key];\r
347     },\r
348 \r
349 <div id="method-Ext.util.MixedCollection-contains"></div>/**\r
350  * Returns true if the collection contains the passed Object as an item.\r
351  * @param {Object} o  The Object to look for in the collection.\r
352  * @return {Boolean} True if the collection contains the Object as an item.\r
353  */\r
354     contains : function(o){\r
355         return this.indexOf(o) != -1;\r
356     },\r
357 \r
358 <div id="method-Ext.util.MixedCollection-containsKey"></div>/**\r
359  * Returns true if the collection contains the passed Object as a key.\r
360  * @param {String} key The key to look for in the collection.\r
361  * @return {Boolean} True if the collection contains the Object as a key.\r
362  */\r
363     containsKey : function(key){\r
364         return typeof this.map[key] != "undefined";\r
365     },\r
366 \r
367 <div id="method-Ext.util.MixedCollection-clear"></div>/**\r
368  * Removes all items from the collection.  Fires the {@link #clear} event when complete.\r
369  */\r
370     clear : function(){\r
371         this.length = 0;\r
372         this.items = [];\r
373         this.keys = [];\r
374         this.map = {};\r
375         this.fireEvent("clear");\r
376     },\r
377 \r
378 <div id="method-Ext.util.MixedCollection-first"></div>/**\r
379  * Returns the first item in the collection.\r
380  * @return {Object} the first item in the collection..\r
381  */\r
382     first : function(){\r
383         return this.items[0];\r
384     },\r
385 \r
386 <div id="method-Ext.util.MixedCollection-last"></div>/**\r
387  * Returns the last item in the collection.\r
388  * @return {Object} the last item in the collection..\r
389  */\r
390     last : function(){\r
391         return this.items[this.length-1];\r
392     },\r
393 \r
394     // private\r
395     _sort : function(property, dir, fn){\r
396         var i,\r
397             len,\r
398             dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1,\r
399             c = [], k = this.keys, items = this.items;\r
400             \r
401         fn = fn || function(a, b){\r
402             return a-b;\r
403         };\r
404         for(i = 0, len = items.length; i < len; i++){\r
405             c[c.length] = {key: k[i], value: items[i], index: i};\r
406         }\r
407         c.sort(function(a, b){\r
408             var v = fn(a[property], b[property]) * dsc;\r
409             if(v === 0){\r
410                 v = (a.index < b.index ? -1 : 1);\r
411             }\r
412             return v;\r
413         });\r
414         for(i = 0, len = c.length; i < len; i++){\r
415             items[i] = c[i].value;\r
416             k[i] = c[i].key;\r
417         }\r
418         this.fireEvent("sort", this);\r
419     },\r
420 \r
421     <div id="method-Ext.util.MixedCollection-sort"></div>/**\r
422      * Sorts this collection with the passed comparison function\r
423      * @param {String} direction (optional) "ASC" or "DESC"\r
424      * @param {Function} fn (optional) comparison function\r
425      */\r
426     sort : function(dir, fn){\r
427         this._sort("value", dir, fn);\r
428     },\r
429 \r
430     <div id="method-Ext.util.MixedCollection-keySort"></div>/**\r
431      * Sorts this collection by keys\r
432      * @param {String} direction (optional) "ASC" or "DESC"\r
433      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)\r
434      */\r
435     keySort : function(dir, fn){\r
436         this._sort("key", dir, fn || function(a, b){\r
437             var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();\r
438             return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);\r
439         });\r
440     },\r
441 \r
442     <div id="method-Ext.util.MixedCollection-getRange"></div>/**\r
443      * Returns a range of items in this collection\r
444      * @param {Number} startIndex (optional) defaults to 0\r
445      * @param {Number} endIndex (optional) default to the last item\r
446      * @return {Array} An array of items\r
447      */\r
448     getRange : function(start, end){\r
449         var items = this.items;\r
450         if(items.length < 1){\r
451             return [];\r
452         }\r
453         start = start || 0;\r
454         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);\r
455         var i, r = [];\r
456         if(start <= end){\r
457             for(i = start; i <= end; i++) {\r
458                 r[r.length] = items[i];\r
459             }\r
460         }else{\r
461             for(i = start; i >= end; i--) {\r
462                 r[r.length] = items[i];\r
463             }\r
464         }\r
465         return r;\r
466     },\r
467 \r
468     <div id="method-Ext.util.MixedCollection-filter"></div>/**\r
469      * Filter the <i>objects</i> in this collection by a specific property.\r
470      * Returns a new collection that has been filtered.\r
471      * @param {String} property A property on your objects\r
472      * @param {String/RegExp} value Either string that the property values\r
473      * should start with or a RegExp to test against the property\r
474      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning\r
475      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).\r
476      * @return {MixedCollection} The new filtered collection\r
477      */\r
478     filter : function(property, value, anyMatch, caseSensitive){\r
479         if(Ext.isEmpty(value, false)){\r
480             return this.clone();\r
481         }\r
482         value = this.createValueMatcher(value, anyMatch, caseSensitive);\r
483         return this.filterBy(function(o){\r
484             return o && value.test(o[property]);\r
485         });\r
486     },\r
487 \r
488     <div id="method-Ext.util.MixedCollection-filterBy"></div>/**\r
489      * Filter by a function. Returns a <i>new</i> collection that has been filtered.\r
490      * The passed function will be called with each object in the collection.\r
491      * If the function returns true, the value is included otherwise it is filtered.\r
492      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)\r
493      * @param {Object} scope (optional) The scope of the function (defaults to this)\r
494      * @return {MixedCollection} The new filtered collection\r
495      */\r
496     filterBy : function(fn, scope){\r
497         var r = new Ext.util.MixedCollection();\r
498         r.getKey = this.getKey;\r
499         var k = this.keys, it = this.items;\r
500         for(var i = 0, len = it.length; i < len; i++){\r
501             if(fn.call(scope||this, it[i], k[i])){\r
502                 r.add(k[i], it[i]);\r
503             }\r
504         }\r
505         return r;\r
506     },\r
507 \r
508     <div id="method-Ext.util.MixedCollection-findIndex"></div>/**\r
509      * Finds the index of the first matching object in this collection by a specific property/value.\r
510      * @param {String} property The name of a property on your objects.\r
511      * @param {String/RegExp} value A string that the property values\r
512      * should start with or a RegExp to test against the property.\r
513      * @param {Number} start (optional) The index to start searching at (defaults to 0).\r
514      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.\r
515      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.\r
516      * @return {Number} The matched index or -1\r
517      */\r
518     findIndex : function(property, value, start, anyMatch, caseSensitive){\r
519         if(Ext.isEmpty(value, false)){\r
520             return -1;\r
521         }\r
522         value = this.createValueMatcher(value, anyMatch, caseSensitive);\r
523         return this.findIndexBy(function(o){\r
524             return o && value.test(o[property]);\r
525         }, null, start);\r
526     },\r
527 \r
528     <div id="method-Ext.util.MixedCollection-findIndexBy"></div>/**\r
529      * Find the index of the first matching object in this collection by a function.\r
530      * If the function returns <i>true</i> it is considered a match.\r
531      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).\r
532      * @param {Object} scope (optional) The scope of the function (defaults to this).\r
533      * @param {Number} start (optional) The index to start searching at (defaults to 0).\r
534      * @return {Number} The matched index or -1\r
535      */\r
536     findIndexBy : function(fn, scope, start){\r
537         var k = this.keys, it = this.items;\r
538         for(var i = (start||0), len = it.length; i < len; i++){\r
539             if(fn.call(scope||this, it[i], k[i])){\r
540                 return i;\r
541             }\r
542         }\r
543         return -1;\r
544     },\r
545 \r
546     // private\r
547     createValueMatcher : function(value, anyMatch, caseSensitive){\r
548         if(!value.exec){ // not a regex\r
549             value = String(value);\r
550             value = new RegExp((anyMatch === true ? '' : '^') + Ext.escapeRe(value), caseSensitive ? '' : 'i');\r
551         }\r
552         return value;\r
553     },\r
554 \r
555     <div id="method-Ext.util.MixedCollection-clone"></div>/**\r
556      * Creates a shallow copy of this collection\r
557      * @return {MixedCollection}\r
558      */\r
559     clone : function(){\r
560         var r = new Ext.util.MixedCollection();\r
561         var k = this.keys, it = this.items;\r
562         for(var i = 0, len = it.length; i < len; i++){\r
563             r.add(k[i], it[i]);\r
564         }\r
565         r.getKey = this.getKey;\r
566         return r;\r
567     }\r
568 });\r
569 <div id="method-Ext.util.MixedCollection-get"></div>/**\r
570  * This method calls {@link #item item()}.\r
571  * Returns the item associated with the passed key OR index. Key has priority over index.  This is the equivalent\r
572  * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.\r
573  * @param {String/Number} key The key or index of the item.\r
574  * @return {Object} If the item is found, returns the item.  If the item was not found, returns <tt>undefined</tt>.\r
575  * If an item was found, but is a Class, returns <tt>null</tt>.\r
576  */\r
577 Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;</pre>    \r
578 </body>\r
579 </html>