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