sproutcore 0.9.14 → 0.9.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/History.txt +43 -0
  2. data/Manifest.txt +12 -3
  3. data/bin/sc-build +19 -3
  4. data/bin/sc-install +5 -0
  5. data/bin/sc-remove +5 -0
  6. data/bin/sc-update +5 -0
  7. data/frameworks/prototype/prototype.js +267 -230
  8. data/frameworks/sproutcore/HISTORY +281 -135
  9. data/frameworks/sproutcore/controllers/array.js +133 -22
  10. data/frameworks/sproutcore/controllers/collection.js +4 -5
  11. data/frameworks/sproutcore/controllers/object.js +8 -2
  12. data/frameworks/sproutcore/core.js +361 -159
  13. data/frameworks/sproutcore/{foundation → debug}/unittest.js +3 -3
  14. data/frameworks/sproutcore/english.lproj/detect-browser +1 -1
  15. data/frameworks/sproutcore/english.lproj/theme.css +2 -2
  16. data/frameworks/sproutcore/foundation/application.js +6 -1
  17. data/frameworks/sproutcore/foundation/benchmark.js +37 -11
  18. data/frameworks/sproutcore/foundation/date.js +1 -1
  19. data/frameworks/sproutcore/foundation/enumerator.js +105 -0
  20. data/frameworks/sproutcore/foundation/object.js +19 -20
  21. data/frameworks/sproutcore/foundation/responder.js +1 -1
  22. data/frameworks/sproutcore/foundation/set.js +164 -57
  23. data/frameworks/sproutcore/foundation/string.js +151 -47
  24. data/frameworks/sproutcore/foundation/utils.js +84 -3
  25. data/frameworks/sproutcore/lib/collection_view.rb +1 -0
  26. data/frameworks/sproutcore/license.js +28 -0
  27. data/frameworks/sproutcore/mixins/array.js +73 -209
  28. data/frameworks/sproutcore/mixins/delegate_support.js +1 -1
  29. data/frameworks/sproutcore/mixins/enumerable.js +1006 -0
  30. data/frameworks/sproutcore/mixins/observable.js +153 -84
  31. data/frameworks/sproutcore/mixins/selection_support.js +13 -1
  32. data/frameworks/sproutcore/models/record.js +74 -27
  33. data/frameworks/sproutcore/models/store.js +7 -3
  34. data/frameworks/sproutcore/server/rails_server.js +82 -0
  35. data/frameworks/sproutcore/server/rest_server.js +178 -0
  36. data/frameworks/sproutcore/{foundation → server}/server.js +101 -48
  37. data/frameworks/sproutcore/tests/core/guidFor.rhtml +114 -0
  38. data/frameworks/sproutcore/tests/foundation/array.rhtml +6 -7
  39. data/frameworks/sproutcore/tests/foundation/set.rhtml +254 -0
  40. data/frameworks/sproutcore/tests/mixins/enumerable.rhtml +421 -0
  41. data/frameworks/sproutcore/tests/mixins/observable.rhtml +127 -0
  42. data/frameworks/sproutcore/tests/models/model.rhtml +23 -22
  43. data/frameworks/sproutcore/tests/views/collection/incremental_rendering.rhtml +2 -2
  44. data/frameworks/sproutcore/tests/views/view/clippingFrame.rhtml +112 -109
  45. data/frameworks/sproutcore/tests/views/view/frame.rhtml +91 -88
  46. data/frameworks/sproutcore/validators/date.js +1 -7
  47. data/frameworks/sproutcore/views/collection/collection.js +7 -2
  48. data/frameworks/sproutcore/views/list_item.js +141 -3
  49. data/frameworks/sproutcore/views/split.js +14 -11
  50. data/frameworks/sproutcore/views/view.js +9 -6
  51. data/lib/sproutcore/build_tools/html_builder.rb +19 -3
  52. data/lib/sproutcore/build_tools/resource_builder.rb +9 -3
  53. data/lib/sproutcore/bundle.rb +21 -0
  54. data/lib/sproutcore/bundle_manifest.rb +64 -20
  55. data/lib/sproutcore/helpers/capture_helper.rb +2 -2
  56. data/lib/sproutcore/library.rb +33 -9
  57. data/lib/sproutcore/merb/bundle_controller.rb +16 -5
  58. data/lib/sproutcore/version.rb +1 -1
  59. data/lib/sproutcore/view_helpers.rb +1 -1
  60. data/{sc-config.rb → sc-config} +5 -2
  61. 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
- SC.Benchmark.start(key) ;
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, $A(arguments).join(', ')) ;
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
- ret.push(this._genReport(key)) ;
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 * 1000 / stat.runs) / 1000) : 0 ;
208
+ var avg = (stat.runs > 0) ? (Math.floor(stat.amt * 100000 / stat.runs) / 100000) : 0 ;
193
209
 
194
- return 'BENCH %@ msec: %@ (%@x)'.fmt(avg, (stat.name || key), stat.runs) ;
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, naturalLanguage)
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,$A(arguments)) ;
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
- Object.extend(SC.Object,
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 = $A(arguments) ;
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,$A(arguments));
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 = $A(arguments) ;
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._nextGUID++ ; // each time we extend, get a new guid.
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($A(arguments),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 = (typeof(path) == "string") ? path.split('.') : path ;
256
+ var parts = ($type(path) === T_STRING) ? path.split('.') : path ;
258
257
  if (!root) root = window ;
259
- var key = parts.shift() ;
260
- while(key && root) {
261
- root = (root.get) ? root.get(key) : root[key];
262
- key = parts.shift();
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 (parts.length > 0) ? undefined : root ;
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._nextGUID++ ;
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 = $A(arguments);
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 =$A(arguments).slice(2,arguments.length);
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 = $A(arguments);
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/array') ;
6
+ require('mixins/enumerable') ;
7
+ require('mixins/observable') ;
7
8
 
8
9
  /**
9
- @class An unordered collection for keeping objects.
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
- @extends SC.Object
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
- SC.Set = SC.Object.extend(SC.Array,
18
- /** @scope SC.Set.prototype */
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
- Changes each time an object is updated or removed. Observe this to be notified of changes to the set.
30
-
31
- @type number
100
+ Clears the set
32
101
  */
33
- revision: 0,
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
- if (obj === null) return false ;
40
- return this[this._guidFor(obj)] === obj ;
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 {Boolean} YES if the object as added.
124
+ @returns {Object} this
50
125
  */
51
126
  add: function(obj) {
52
- if (obj == null) return NO; // cannot add null to a set.
127
+ if (obj == null) return this; // cannot add null to a set.
53
128
 
54
- var guid = this._guidFor(obj) ;
55
- if (this[guid] == null) {
56
- this[this._guidFor(obj)] = obj ;
57
- this.incrementProperty('length') ;
58
- this.incrementProperty('revision') ;
59
- return YES ;
60
- } else return NO ;
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 {Boolean} YES if the object was removed.
154
+ @returns {this} this
70
155
  */
71
156
  remove: function(obj) {
72
- if (obj == null) return NO ;
73
- var guid = this._guidFor(obj);
74
- if (this[guid] === obj) {
75
- delete this[this._guidFor(obj)] ;
76
- this.decrementProperty('length') ;
77
- this.incrementProperty('revision') ;
78
- return YES ;
79
- } else return NO;
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
- _guidFor: function(obj) {
85
- return '@' + SC.guidFor(obj);
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
- _each: function(iterator) {
89
- for (var key in this) {
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.pop = SC.Set.prototype.shift = SC.Set.prototype.remove;
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); };