sproutcore 0.9.14 → 0.9.15
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +43 -0
- data/Manifest.txt +12 -3
- data/bin/sc-build +19 -3
- data/bin/sc-install +5 -0
- data/bin/sc-remove +5 -0
- data/bin/sc-update +5 -0
- data/frameworks/prototype/prototype.js +267 -230
- data/frameworks/sproutcore/HISTORY +281 -135
- data/frameworks/sproutcore/controllers/array.js +133 -22
- data/frameworks/sproutcore/controllers/collection.js +4 -5
- data/frameworks/sproutcore/controllers/object.js +8 -2
- data/frameworks/sproutcore/core.js +361 -159
- data/frameworks/sproutcore/{foundation → debug}/unittest.js +3 -3
- data/frameworks/sproutcore/english.lproj/detect-browser +1 -1
- data/frameworks/sproutcore/english.lproj/theme.css +2 -2
- data/frameworks/sproutcore/foundation/application.js +6 -1
- data/frameworks/sproutcore/foundation/benchmark.js +37 -11
- data/frameworks/sproutcore/foundation/date.js +1 -1
- data/frameworks/sproutcore/foundation/enumerator.js +105 -0
- data/frameworks/sproutcore/foundation/object.js +19 -20
- data/frameworks/sproutcore/foundation/responder.js +1 -1
- data/frameworks/sproutcore/foundation/set.js +164 -57
- data/frameworks/sproutcore/foundation/string.js +151 -47
- data/frameworks/sproutcore/foundation/utils.js +84 -3
- data/frameworks/sproutcore/lib/collection_view.rb +1 -0
- data/frameworks/sproutcore/license.js +28 -0
- data/frameworks/sproutcore/mixins/array.js +73 -209
- data/frameworks/sproutcore/mixins/delegate_support.js +1 -1
- data/frameworks/sproutcore/mixins/enumerable.js +1006 -0
- data/frameworks/sproutcore/mixins/observable.js +153 -84
- data/frameworks/sproutcore/mixins/selection_support.js +13 -1
- data/frameworks/sproutcore/models/record.js +74 -27
- data/frameworks/sproutcore/models/store.js +7 -3
- data/frameworks/sproutcore/server/rails_server.js +82 -0
- data/frameworks/sproutcore/server/rest_server.js +178 -0
- data/frameworks/sproutcore/{foundation → server}/server.js +101 -48
- data/frameworks/sproutcore/tests/core/guidFor.rhtml +114 -0
- data/frameworks/sproutcore/tests/foundation/array.rhtml +6 -7
- data/frameworks/sproutcore/tests/foundation/set.rhtml +254 -0
- data/frameworks/sproutcore/tests/mixins/enumerable.rhtml +421 -0
- data/frameworks/sproutcore/tests/mixins/observable.rhtml +127 -0
- data/frameworks/sproutcore/tests/models/model.rhtml +23 -22
- data/frameworks/sproutcore/tests/views/collection/incremental_rendering.rhtml +2 -2
- data/frameworks/sproutcore/tests/views/view/clippingFrame.rhtml +112 -109
- data/frameworks/sproutcore/tests/views/view/frame.rhtml +91 -88
- data/frameworks/sproutcore/validators/date.js +1 -7
- data/frameworks/sproutcore/views/collection/collection.js +7 -2
- data/frameworks/sproutcore/views/list_item.js +141 -3
- data/frameworks/sproutcore/views/split.js +14 -11
- data/frameworks/sproutcore/views/view.js +9 -6
- data/lib/sproutcore/build_tools/html_builder.rb +19 -3
- data/lib/sproutcore/build_tools/resource_builder.rb +9 -3
- data/lib/sproutcore/bundle.rb +21 -0
- data/lib/sproutcore/bundle_manifest.rb +64 -20
- data/lib/sproutcore/helpers/capture_helper.rb +2 -2
- data/lib/sproutcore/library.rb +33 -9
- data/lib/sproutcore/merb/bundle_controller.rb +16 -5
- data/lib/sproutcore/version.rb +1 -1
- data/lib/sproutcore/view_helpers.rb +1 -1
- data/{sc-config.rb → sc-config} +5 -2
- metadata +24 -5
@@ -81,7 +81,7 @@ SC.Benchmark = {
|
|
81
81
|
@param key {String} The benchmark key you used when you called start()
|
82
82
|
@param time {Integer} Only pass if you want to explicitly set the end time. Otherwise start time is now.
|
83
83
|
*/
|
84
|
-
end: function(key, time) {
|
84
|
+
end: function(key, time, runs) {
|
85
85
|
if (!this.enabled) return ;
|
86
86
|
var stat = this._statFor(key) ;
|
87
87
|
var start = stat._starts.pop() ;
|
@@ -94,7 +94,7 @@ SC.Benchmark = {
|
|
94
94
|
if (start == 'ignore') return ;
|
95
95
|
|
96
96
|
stat.amt += (time || Date.now()) - start ;
|
97
|
-
stat.runs
|
97
|
+
stat.runs += (runs || 1) ;
|
98
98
|
|
99
99
|
if (this.verbose) this.log(key) ;
|
100
100
|
},
|
@@ -104,16 +104,17 @@ SC.Benchmark = {
|
|
104
104
|
run with the name you provide the number of times you indicate. Only the
|
105
105
|
function is a required param.
|
106
106
|
*/
|
107
|
-
bench: function(func, key, reps) {
|
107
|
+
bench: function(func, key, reps, context) {
|
108
108
|
if (!key) key = "bench%@".fmt(this._benchCount++) ;
|
109
109
|
if (!reps) reps = 1 ;
|
110
110
|
var ret ;
|
111
111
|
|
112
|
+
var runs = reps;
|
113
|
+
SC.Benchmark.start(key) ;
|
112
114
|
while(--reps >= 0) {
|
113
|
-
|
114
|
-
ret = func();
|
115
|
-
SC.Benchmark.end(key) ;
|
115
|
+
ret = func(context);
|
116
116
|
}
|
117
|
+
SC.Benchmark.end(key, null, runs) ;
|
117
118
|
|
118
119
|
return ret ;
|
119
120
|
},
|
@@ -129,7 +130,7 @@ SC.Benchmark = {
|
|
129
130
|
|
130
131
|
// replace with this helper.
|
131
132
|
object[method] = function() {
|
132
|
-
var key = '%@(%@)'.fmt(method,
|
133
|
+
var key = '%@(%@)'.fmt(method, SC.$A(arguments).join(', ')) ;
|
133
134
|
SC.Benchmark.start(key, topLevelOnly) ;
|
134
135
|
var ret = __func.apply(this, arguments) ;
|
135
136
|
SC.Benchmark.end(key) ;
|
@@ -156,9 +157,24 @@ SC.Benchmark = {
|
|
156
157
|
report: function(key) {
|
157
158
|
if (key) return this._genReport(key) ;
|
158
159
|
var ret = [] ;
|
160
|
+
|
161
|
+
// find the longest stat name...
|
162
|
+
var maxLen = 0 ;
|
163
|
+
for(var key in this.stats) {
|
164
|
+
if (!this.stats.hasOwnProperty(key)) continue ;
|
165
|
+
if (key.length > maxLen) maxLen = key.length;
|
166
|
+
}
|
167
|
+
|
168
|
+
// now gen report...
|
169
|
+
var keys = [] ;
|
159
170
|
for(var key in this.stats) {
|
160
171
|
if (!this.stats.hasOwnProperty(key)) continue ;
|
161
|
-
|
172
|
+
keys.push(key) ;
|
173
|
+
}
|
174
|
+
keys = keys.sort() ;
|
175
|
+
var max = keys.length ;
|
176
|
+
for(var idx=0;idx<max;idx++) {
|
177
|
+
ret.push(this._genReport(keys[idx], maxLen)) ;
|
162
178
|
}
|
163
179
|
return ret.join("\n") ;
|
164
180
|
},
|
@@ -187,11 +203,21 @@ SC.Benchmark = {
|
|
187
203
|
|
188
204
|
// PRIVATE METHODS
|
189
205
|
|
190
|
-
_genReport: function(key) {
|
206
|
+
_genReport: function(key, nameLength) {
|
191
207
|
var stat = this._statFor(key) ;
|
192
|
-
var avg = (stat.runs > 0) ? (Math.floor(stat.amt *
|
208
|
+
var avg = (stat.runs > 0) ? (Math.floor(stat.amt * 100000 / stat.runs) / 100000) : 0 ;
|
193
209
|
|
194
|
-
|
210
|
+
// Generate the name, adding padding spaces if needed.
|
211
|
+
var name = (stat.name || key) ;
|
212
|
+
nameLength = (nameLength) ? nameLength : 0;
|
213
|
+
if (nameLength > name.length) {
|
214
|
+
var toJoin = [name] ;
|
215
|
+
nameLength -= name.length ;
|
216
|
+
while(--nameLength >= 0) toJoin.push(' ') ;
|
217
|
+
name = toJoin.join('') ;
|
218
|
+
}
|
219
|
+
|
220
|
+
return 'BENCH | %@1 | avg: %@4 msec | total: %@2 msec | reps: %@3x '.fmt(name, stat.amt, stat.runs, avg) ;
|
195
221
|
},
|
196
222
|
|
197
223
|
// @private
|
@@ -290,7 +290,7 @@ Object.extend(Date,{
|
|
290
290
|
Object.extend(Date.prototype, {
|
291
291
|
|
292
292
|
// ------------------------------------------------------------------
|
293
|
-
// formatDate (date_object, format
|
293
|
+
// formatDate (date_object, format)
|
294
294
|
// Returns a date in the output format specified.
|
295
295
|
// The format string uses the same abbreviations as in getDateFromFormat()
|
296
296
|
//
|
@@ -0,0 +1,105 @@
|
|
1
|
+
// ==========================================================================
|
2
|
+
// SproutCore -- JavaScript Application Framework
|
3
|
+
// copyright 2006-2008, Sprout Systems, Inc. and contributors.
|
4
|
+
// ==========================================================================
|
5
|
+
|
6
|
+
/**
|
7
|
+
@class
|
8
|
+
|
9
|
+
An object that iterates over all of the values in an object.
|
10
|
+
|
11
|
+
An instance of this object is returned everytime you call the
|
12
|
+
enumerator() method on an object that implements the SC.Enumerable mixin.
|
13
|
+
|
14
|
+
Once you create an enumerator instance, you can call nextObject() on it
|
15
|
+
until you can iterated through the entire collection. Once you have
|
16
|
+
exhausted the enumerator, you can reuse it if you want by calling reset().
|
17
|
+
|
18
|
+
@extends Object
|
19
|
+
@since SproutCore 1.0
|
20
|
+
*/
|
21
|
+
SC.Enumerator = function(enumerableObject) {
|
22
|
+
this.enumerable = enumerableObject ;
|
23
|
+
this.reset() ;
|
24
|
+
return this ;
|
25
|
+
} ;
|
26
|
+
|
27
|
+
SC.Enumerator.prototype = {
|
28
|
+
|
29
|
+
/**
|
30
|
+
Returns the next object in the enumeration or undefined when complete.
|
31
|
+
|
32
|
+
@returns {Object} the next object or undefined
|
33
|
+
*/
|
34
|
+
nextObject: function() {
|
35
|
+
var index = this._index ;
|
36
|
+
var len = this._length;
|
37
|
+
if (index >= len) return undefined ; // nothing to do
|
38
|
+
|
39
|
+
// get the value
|
40
|
+
var ret = this.enumerable.nextObject(index, this._previousObject, this._context) ;
|
41
|
+
this._previousObject = ret ;
|
42
|
+
this._index = index + 1 ;
|
43
|
+
|
44
|
+
if (index >= len) {
|
45
|
+
this._context = SC.Enumerator._pushContext(this._context);
|
46
|
+
}
|
47
|
+
|
48
|
+
return ret ;
|
49
|
+
},
|
50
|
+
|
51
|
+
/**
|
52
|
+
Resets the enumerator to the beginning. This is a nice way to reuse
|
53
|
+
an existing enumerator.
|
54
|
+
|
55
|
+
@returns {Object} this
|
56
|
+
*/
|
57
|
+
reset: function() {
|
58
|
+
var e = this.enumerable ;
|
59
|
+
if (!e) throw $error("Enumerator has been destroyed");
|
60
|
+
|
61
|
+
var len = this._length = (e.get) ? e.get('length') : e.length ;
|
62
|
+
this._index = 0;
|
63
|
+
this._previousObject = null ;
|
64
|
+
this._context = (len > 0) ? SC.Enumerator._popContext() : null;
|
65
|
+
},
|
66
|
+
|
67
|
+
/**
|
68
|
+
Releases the enumerators enumerable object. You cannot use this object
|
69
|
+
anymore. This is not often needed but it is useful when you need to
|
70
|
+
make sure memory gets cleared.
|
71
|
+
|
72
|
+
@returns {Object} null
|
73
|
+
*/
|
74
|
+
destroy: function() {
|
75
|
+
this.enumerable = this._length = this._index = this._previousObject = this._context = null;
|
76
|
+
}
|
77
|
+
|
78
|
+
} ;
|
79
|
+
|
80
|
+
/**
|
81
|
+
Use this method to manually create a new Enumerator object. Usually you
|
82
|
+
will not access this method directly but instead call enumerator() on the
|
83
|
+
item you want to enumerate.
|
84
|
+
|
85
|
+
@param {SC.Enumerable} The enumerable object.
|
86
|
+
@returns {SC.Enumerator} the enumerator
|
87
|
+
*/
|
88
|
+
SC.Enumerator.create = function(enumerableObject) {
|
89
|
+
return new SC.Enumerator(enumerableObject) ;
|
90
|
+
};
|
91
|
+
|
92
|
+
// Private context caching methods. This avoids recreating lots of context
|
93
|
+
// objects.
|
94
|
+
|
95
|
+
SC.Enumerator._popContext = function() {
|
96
|
+
var ret = (this._contextCache) ? this._contextCache.pop() : null ;
|
97
|
+
return ret || {} ;
|
98
|
+
} ;
|
99
|
+
|
100
|
+
SC.Enumerator._pushContext = function(context) {
|
101
|
+
var cache = this._contextCache = this._contextCache || [] ;
|
102
|
+
cache.push(context);
|
103
|
+
return null ;
|
104
|
+
};
|
105
|
+
|
@@ -49,13 +49,12 @@ SC.BENCHMARK_OBJECTS = NO;
|
|
49
49
|
|
50
50
|
*/
|
51
51
|
SC.Object = function(noinit) {
|
52
|
-
if (noinit === SC.Object._noinit_) return ;
|
53
|
-
var ret = SC.Object._init.apply(this
|
52
|
+
if (noinit === SC.Object._noinit_) return this ;
|
53
|
+
var ret = SC.Object._init.apply(this,SC.$A(arguments)) ;
|
54
54
|
return ret ;
|
55
55
|
};
|
56
56
|
|
57
|
-
|
58
|
-
/** @scope SC.Object */ {
|
57
|
+
SC.mixin(SC.Object, /** @scope SC.Object */ {
|
59
58
|
|
60
59
|
_noinit_: '__noinit__',
|
61
60
|
|
@@ -66,7 +65,7 @@ Object.extend(SC.Object,
|
|
66
65
|
@returns {void}
|
67
66
|
*/
|
68
67
|
mixin: function(props) {
|
69
|
-
var ext =
|
68
|
+
var ext = SC.$A(arguments) ;
|
70
69
|
for(var loc=0;loc<ext.length;loc++) {
|
71
70
|
Object.extend(this,ext[loc]);
|
72
71
|
}
|
@@ -86,8 +85,8 @@ Object.extend(SC.Object,
|
|
86
85
|
|
87
86
|
// build function. copy class methods on to it.
|
88
87
|
var ret = function(noinit) {
|
89
|
-
if (noinit && (typeof(noinit) == 'string') && (noinit == SC.Object._noinit_)) return ;
|
90
|
-
var ret = SC.Object._init.apply(this
|
88
|
+
if (noinit && (typeof(noinit) == 'string') && (noinit == SC.Object._noinit_)) return this ;
|
89
|
+
var ret = SC.Object._init.apply(this,SC.$A(arguments));
|
91
90
|
return ret ;
|
92
91
|
};
|
93
92
|
for(var prop in this) { ret[prop] = this[prop]; }
|
@@ -96,16 +95,16 @@ Object.extend(SC.Object,
|
|
96
95
|
var base = new this(SC.Object._noinit_) ;
|
97
96
|
|
98
97
|
// var base = SC.Object._extend({},this.prototype) ;
|
99
|
-
var extensions =
|
98
|
+
var extensions = SC.$A(arguments) ;
|
100
99
|
for(var loc=0;loc<extensions.length;loc++) {
|
101
100
|
base = SC.Object._extend(base, extensions[loc]);
|
102
101
|
}
|
103
102
|
ret.prototype = base ;
|
104
103
|
|
105
104
|
// return new extension
|
106
|
-
ret._guid = SC.
|
105
|
+
ret._guid = SC.generateGuid() ; // each time we extend we get a new guid
|
107
106
|
ret._type = this ;
|
108
|
-
|
107
|
+
|
109
108
|
if (SC.BENCHMARK_OBJECTS) SC.Benchmark.end('SC.Object.extend') ;
|
110
109
|
|
111
110
|
return ret ;
|
@@ -129,7 +128,7 @@ Object.extend(SC.Object,
|
|
129
128
|
@returns {SC.Object} new instance of the receiver class.
|
130
129
|
*/
|
131
130
|
create: function(props) {
|
132
|
-
var ret = new this(
|
131
|
+
var ret = new this(SC.$A(arguments),this) ;
|
133
132
|
return ret ;
|
134
133
|
},
|
135
134
|
|
@@ -254,14 +253,14 @@ Object.extend(SC.Object,
|
|
254
253
|
},
|
255
254
|
|
256
255
|
objectForPropertyPath: function(path,root) {
|
257
|
-
var parts = (
|
256
|
+
var parts = ($type(path) === T_STRING) ? path.split('.') : path ;
|
258
257
|
if (!root) root = window ;
|
259
|
-
var
|
260
|
-
while(
|
261
|
-
|
262
|
-
key =
|
258
|
+
var loc = 0, max = parts.length, key = null;
|
259
|
+
while((loc < max) && (root)) {
|
260
|
+
key = parts[loc++];
|
261
|
+
if (key) root = (root.get) ? root.get(key) : root[key] ;
|
263
262
|
}
|
264
|
-
return (
|
263
|
+
return (loc < max) ? undefined : root ;
|
265
264
|
},
|
266
265
|
|
267
266
|
|
@@ -274,7 +273,7 @@ Object.extend(SC.Object,
|
|
274
273
|
for(var loc=0;loc<extensions.length;loc++) {
|
275
274
|
ret = SC.Object._extend(ret,extensions[loc]) ;
|
276
275
|
}
|
277
|
-
ret._guid = SC.
|
276
|
+
ret._guid = SC.generateGuid() ;
|
278
277
|
ret._type = type ;
|
279
278
|
ret.init() ;
|
280
279
|
|
@@ -422,7 +421,7 @@ SC.Object.prototype = {
|
|
422
421
|
{
|
423
422
|
if ( !methodName ) return false;
|
424
423
|
|
425
|
-
var args =
|
424
|
+
var args = SC.$A(arguments);
|
426
425
|
var name = args.shift();
|
427
426
|
if (this.respondsTo(name))
|
428
427
|
{
|
@@ -734,7 +733,7 @@ SC.Object.prototype = {
|
|
734
733
|
if (interval === undefined) interval = 1 ;
|
735
734
|
var f = methodName ;
|
736
735
|
if (arguments.length > 2) {
|
737
|
-
var args
|
736
|
+
var args =SC.$A(arguments).slice(2,arguments.length);
|
738
737
|
args.unshift(this);
|
739
738
|
if ($type(f) === T_STRING) f = this[methodName] ;
|
740
739
|
f = f.bind.apply(f, args) ;
|
@@ -168,7 +168,7 @@ SC.Responder = SC.Object.extend(
|
|
168
168
|
doCommand: function(method)
|
169
169
|
{
|
170
170
|
var responder = this;
|
171
|
-
var args =
|
171
|
+
var args = SC.$A(arguments);
|
172
172
|
var method = args.shift();
|
173
173
|
var aliases = this._commandAliases[method];
|
174
174
|
var handled = false;
|
@@ -3,41 +3,116 @@
|
|
3
3
|
// copyright 2006-2008, Sprout Systems, Inc. and contributors.
|
4
4
|
// ==========================================================================
|
5
5
|
|
6
|
-
require('mixins/
|
6
|
+
require('mixins/enumerable') ;
|
7
|
+
require('mixins/observable') ;
|
7
8
|
|
8
9
|
/**
|
9
|
-
@class
|
10
|
+
@class
|
11
|
+
|
12
|
+
An unordered collection of objects.
|
10
13
|
|
11
14
|
A Set works a bit like an array except that its items are not ordered.
|
12
|
-
You can create a set to efficiently test for membership for an object.
|
13
|
-
|
14
|
-
|
15
|
+
You can create a set to efficiently test for membership for an object. You
|
16
|
+
can also iterate through a set just like an array, even accessing objects
|
17
|
+
by index, however there is no gaurantee as to their order.
|
18
|
+
|
19
|
+
Note that SC.Set is a primitive object, like an array. It does implement
|
20
|
+
limited key-value observing support but it does not extend from SC.Object
|
21
|
+
so you should not subclass it.
|
22
|
+
|
23
|
+
h1. Creating a Set
|
24
|
+
|
25
|
+
You can create a set like you would most objects using SC.Set.create() or
|
26
|
+
new SC.Set(). Most new sets you create will be empty, but you can also
|
27
|
+
initialize the set with some content by passing an array or other enumerable
|
28
|
+
of objects to the constructor.
|
29
|
+
|
30
|
+
Finally, you can pass in an existing set and the set will be copied. You
|
31
|
+
can also create a copy of a set by calling SC.Set#clone().
|
32
|
+
|
33
|
+
{{{
|
34
|
+
// creates a new empty set
|
35
|
+
var foundNames = SC.Set.create();
|
36
|
+
|
37
|
+
// creates a set with four names in it.
|
38
|
+
var names = SC.Set.create(["Charles", "Peter", "Chris", "Erich"]) ;
|
15
39
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
40
|
+
// creates a copy of the names set.
|
41
|
+
var namesCopy = SC.Set.create(names);
|
42
|
+
|
43
|
+
// same as above.
|
44
|
+
var anotherNamesCopy = names.clone();
|
45
|
+
}}}
|
20
46
|
|
21
|
-
|
47
|
+
h1. Adding/Removing Objects
|
48
|
+
|
49
|
+
You generally add or removed objects from a set using add() or remove().
|
50
|
+
You can add any type of object including primitives such as numbers,
|
51
|
+
strings, and booleans.
|
52
|
+
|
53
|
+
Note that objects can only exist one time in a set. If you call add() on
|
54
|
+
a set with the same object multiple times, the object will only be added
|
55
|
+
once. Likewise, calling remove() with the same object multiple times will
|
56
|
+
remove the object the first time and have no effect on future calls until
|
57
|
+
you add the object to the set again.
|
58
|
+
|
59
|
+
Note that you cannot add/remove null or undefined to a set. Any attempt to
|
60
|
+
do so will be ignored.
|
61
|
+
|
62
|
+
In addition to add/remove you can also call push()/pop(). Push behaves just
|
63
|
+
like add() but pop(), unlike remove() will pick an arbitrary object, remove
|
64
|
+
it and return it. This is a good way to use a set as a job queue when you
|
65
|
+
don't care which order the jobs are executed in.
|
66
|
+
|
67
|
+
h1. Testing for an Object
|
68
|
+
|
69
|
+
To test for an object's presence in a set you simply call SC.Set#contains().
|
70
|
+
This method tests for the object's hash, which is generally the same as the
|
71
|
+
object's _guid but if you implement the hash() method on the object, it will
|
72
|
+
use the return value from that method instead.
|
73
|
+
|
74
|
+
@extends Object
|
75
|
+
@extends SC.Enumerable
|
76
|
+
@since SproutCore 0.9.15
|
77
|
+
*/
|
78
|
+
SC.Set = function(items) {
|
79
|
+
if (items && items.length > 0) {
|
80
|
+
var idx = (items.get) ? items.get('length') : items.length ;
|
81
|
+
if (items.objectAt) {
|
82
|
+
while(--idx >= 0) this.add(items.objectAt(idx)) ;
|
83
|
+
} else {
|
84
|
+
while(--idx >= 0) this.add(items[idx]) ;
|
85
|
+
}
|
86
|
+
}
|
87
|
+
return this ;
|
88
|
+
} ;
|
89
|
+
|
90
|
+
SC.Set.prototype = {
|
91
|
+
|
92
|
+
/**
|
22
93
|
This property will change as the number of objects in the set changes.
|
23
94
|
|
24
95
|
@type number
|
25
|
-
*/
|
96
|
+
*/
|
26
97
|
length: 0,
|
27
98
|
|
28
99
|
/**
|
29
|
-
|
30
|
-
|
31
|
-
@type number
|
100
|
+
Clears the set
|
32
101
|
*/
|
33
|
-
|
102
|
+
clear: function() { this.length = 0; },
|
34
103
|
|
35
104
|
/**
|
36
105
|
Call this method to test for membership.
|
37
106
|
*/
|
38
107
|
contains: function(obj) {
|
39
|
-
|
40
|
-
|
108
|
+
|
109
|
+
// because of the way a set is "reset", the guid for an object may
|
110
|
+
// still be stored as a key, but points to an index that is beyond the
|
111
|
+
// length. Therefore the found idx must both be defined and less than
|
112
|
+
// the current length.
|
113
|
+
if (obj === null) return NO ;
|
114
|
+
var idx = this[SC.hashFor(obj)] ;
|
115
|
+
return ((idx != null) && (idx < this.length)) ;
|
41
116
|
},
|
42
117
|
|
43
118
|
/**
|
@@ -46,70 +121,102 @@ SC.Set = SC.Object.extend(SC.Array,
|
|
46
121
|
If the object is already in the set it will not be added again.
|
47
122
|
|
48
123
|
@param obj {Object} the object to add
|
49
|
-
@returns {
|
124
|
+
@returns {Object} this
|
50
125
|
*/
|
51
126
|
add: function(obj) {
|
52
|
-
if (obj == null) return
|
127
|
+
if (obj == null) return this; // cannot add null to a set.
|
53
128
|
|
54
|
-
var guid =
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
this
|
59
|
-
|
60
|
-
|
129
|
+
var guid = SC.hashFor(obj) ;
|
130
|
+
var idx = this[guid] ;
|
131
|
+
var len = this.length ;
|
132
|
+
if ((idx == null) || (idx >= len)) {
|
133
|
+
this[len] = obj ;
|
134
|
+
this[guid] = len ;
|
135
|
+
this.length = len+1;
|
136
|
+
}
|
137
|
+
return this ;
|
61
138
|
},
|
62
139
|
|
140
|
+
/**
|
141
|
+
Add all the items in the passed array.
|
142
|
+
*/
|
143
|
+
addEach: function(objects) {
|
144
|
+
var idx = objects.length ;
|
145
|
+
while(--idx >= 0) this.add(objects[idx]) ;
|
146
|
+
},
|
147
|
+
|
63
148
|
/**
|
64
149
|
Removes the object from the set if it is found.
|
65
150
|
|
66
151
|
If the object is not in the set, nothing will be changed.
|
67
152
|
|
68
153
|
@param obj {Object} the object to remove
|
69
|
-
@returns {
|
154
|
+
@returns {this} this
|
70
155
|
*/
|
71
156
|
remove: function(obj) {
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
157
|
+
|
158
|
+
if (obj == null) return this ;
|
159
|
+
var guid = SC.hashFor(obj);
|
160
|
+
var idx = this[guid] ;
|
161
|
+
var len = this.length;
|
162
|
+
|
163
|
+
if ((idx == null) || (idx >= len)) return this; // not in set.
|
164
|
+
|
165
|
+
// clear the guid key
|
166
|
+
delete this[guid] ;
|
167
|
+
|
168
|
+
// to clear the index, we will swap the object stored in the last index.
|
169
|
+
// if this is the last object, just reduce the length.
|
170
|
+
if (idx < (len-1)) {
|
171
|
+
var obj = this[idx] = this[len-1];
|
172
|
+
this[SC.hashFor(obj)] = idx ;
|
173
|
+
}
|
174
|
+
|
175
|
+
// reduce the length
|
176
|
+
this.length = len-1;
|
177
|
+
return this ;
|
178
|
+
},
|
179
|
+
|
180
|
+
/**
|
181
|
+
Removes an arbitrary object from the set and returns it.
|
182
|
+
|
183
|
+
@returns {Object} an object from the set or null
|
184
|
+
*/
|
185
|
+
pop: function() {
|
186
|
+
var obj = (this.length > 0) ? this[this.length-1] : null ;
|
187
|
+
if (obj) this.remove(obj) ;
|
188
|
+
return obj ;
|
80
189
|
},
|
81
190
|
|
191
|
+
/**
|
192
|
+
Removes all the items in the passed array.
|
193
|
+
*/
|
194
|
+
removeEach: function(objects) {
|
195
|
+
var idx = objects.length ;
|
196
|
+
while(--idx >= 0) this.remove(objects[idx]) ;
|
197
|
+
},
|
198
|
+
|
82
199
|
// .......................................
|
83
200
|
// PRIVATE
|
84
|
-
|
85
|
-
|
201
|
+
_each: function(iterator) {
|
202
|
+
var len = this.length ;
|
203
|
+
for(var idx=0;idx<len;idx++) iterator(this[idx]) ;
|
86
204
|
},
|
87
205
|
|
88
|
-
|
89
|
-
|
90
|
-
if (!this.hasOwnProperty(key)) continue ;
|
91
|
-
if (key.match(/^@/)) iterator(this[key]) ;
|
92
|
-
}
|
206
|
+
toString: function() {
|
207
|
+
return "SC.Set<%@>".fmt(SC.SC.$A(this)) ;
|
93
208
|
}
|
94
209
|
|
95
|
-
}
|
210
|
+
} ;
|
211
|
+
|
212
|
+
// Make this enumerable and observable
|
213
|
+
SC.mixin(SC.Set.prototype, SC.Enumerable, SC.Observable) ;
|
96
214
|
|
97
|
-
SC.Set.prototype.push = SC.Set.prototype.unshift = SC.Set.prototype.add;
|
98
|
-
SC.Set.prototype.
|
215
|
+
SC.Set.prototype.push = SC.Set.prototype.unshift = SC.Set.prototype.add ;
|
216
|
+
SC.Set.prototype.shift = SC.Set.prototype.pop ;
|
99
217
|
|
100
|
-
SC.Set._create = SC.Set.create ;
|
101
218
|
|
102
219
|
/**
|
103
220
|
To create a set, pass an array of items instead of a hash.
|
104
221
|
*/
|
105
|
-
SC.Set.create = function(items) {
|
106
|
-
if (!items) items = [] ;
|
107
|
-
var hash = {}, loc = items.length ;
|
108
|
-
while(--loc >= 0) {
|
109
|
-
var item = items[loc];
|
110
|
-
if (item == null) continue ;
|
111
|
-
hash[SC.Set.prototype._guidFor(item)] = item ;
|
112
|
-
}
|
113
|
-
hash.length = items.length ;
|
114
|
-
return SC.Set._create(hash) ;
|
115
|
-
} ;
|
222
|
+
SC.Set.create = function(items) { return new SC.Set(items); };
|