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
@@ -1,129 +1,218 @@
1
- //@license
2
1
  // ==========================================================================
3
- // SproutCore -- JavaScript Application Framework
4
- // copyright 2006-2008, Sprout Systems, Inc. and contributors.
5
- //
6
- // Permission is hereby granted, free of charge, to any person obtaining a
7
- // copy of this software and associated documentation files (the "Software"),
8
- // to deal in the Software without restriction, including without limitation
9
- // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
- // and/or sell copies of the Software, and to permit persons to whom the
11
- // Software is furnished to do so, subject to the following conditions:
12
- //
13
- // The above copyright notice and this permission notice shall be included in
14
- // all copies or substantial portions of the Software.
15
- //
16
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22
- // DEALINGS IN THE SOFTWARE.
23
- //
24
- // For more information about SproutCore, visit http://www.sproutcore.com
25
- //
26
- //
27
- // ==========================================================================
28
- //@license
29
-
30
- // ==========================================================================
31
- // Utility Classes
2
+ // SproutCore
32
3
  // Author: Charles Jolley
33
- // copyright 2006, Sprout Systems, Inc.
34
- //
35
- // This file contains a number of utility methods and classes used throughout
36
- // SproutCore. This should be loaded after your load Prototype but before you
37
- // load any other SproutCore objects. In general, this is the only
38
- // dependency most SproutCore objects will have.
39
- //
4
+ // copyright 2006-2008, Sprout Systems, Inc.
40
5
  // ==========================================================================
41
6
 
42
- if (!window.SC) {
43
- /**
44
- @namespace
45
-
46
- All objects live in the SproutCore namespace, which is also availabe in the
47
- abbreviation SC.
48
- */
49
- SC = {};
50
- SproutCore = SC ;
51
- }
52
-
53
- // this makes for some nicer to read code
54
- var YES = true ; var NO = false ;
55
-
56
7
  // this is used by the JavascriptCompile class on the server side. You can
57
8
  // use this to automatically determine the order javascript files need to be
58
9
  // included in. On the client side, this is a NOP.
59
- function require(file) { return null ; }
60
-
61
- // implement window.console.log() for IE.
62
- if (!window.console) {
63
- window.console = {
64
- _output: [],
65
- log: function(str) { this._output.push(str) ; },
66
- tail: function(lines) {
67
- if (!lines) lines = 1 ;
68
- var loc = this._output.length - lines ;
69
- if (loc < 0) loc = 0 ;
70
- var ret = [] ;
71
- while(loc < this._output.length) {
72
- ret.push(this._output[loc]) ; loc++ ;
73
- }
74
- return ret.join("\n");
75
- }
76
- } ;
10
+ var require = require || function require() { } ;
11
+ require('license') ;
12
+
13
+ // ........................................
14
+ // GLOBAL CONSTANTS
15
+ //
16
+ // Most global constants should be defined inside of the SC namespace.
17
+ // However the following two are useful enough and generally benign enough
18
+ // to put into the global object.
19
+ var YES = true ;
20
+ var NO = false ;
21
+
22
+ // prevent a console.log from blowing things up if we are on a browser that
23
+ // does not support it
24
+ if (typeof console === 'undefined') {
25
+ var console = console || window.console || {} ;
26
+ console.log = console.info = console.warn = console.error = function(){};
77
27
  }
78
- window.logCount = 0 ;
79
28
 
80
29
  // ........................................
81
- // GENERAL UTILITIES
30
+ // BOOTSTRAP
82
31
  //
32
+ // The root namespace and some common utility methods are defined here. The
33
+ // rest of the methods go into the mixin defined below.
83
34
 
84
- Object.extend(SC,{
35
+ /**
36
+ @namespace
37
+
38
+ The SproutCore namespace. All SproutCore methods and functions are defined
39
+ inside of this namespace. You generally should not add new properties to
40
+ this namespace as it may be overwritten by future versions of SproutCore.
41
+
42
+ You can also use the shorthand "SC" instead of "SproutCore".
43
+
44
+ SproutCore-Base is a framework that provides core functions for SproutCore
45
+ including cross-platform functions, support for property observing and
46
+ objects. It's focus is on small size and performance. You can use this
47
+ in place of or along-side other cross-platform libraries such as jQuery or
48
+ Prototype.
49
+
50
+ The core Base framework is based on the jQuery API with a number of
51
+ performance optimizations.
52
+ */
53
+ var SC = SC || {} ;
54
+ var SproutCore = SproutCore || SC ;
55
+
56
+ /**
57
+ Adds properties to a target object.
58
+
59
+ Takes the root object and adds the attributes for any additional
60
+ arguments passed. This can also perform a deep copy if the first param
61
+ is a bool that is YES. This is generally not very safe though and not
62
+ advised.
63
+
64
+ @param deep {Boolean} optional parameter. If true, triggers a deep copy.
65
+ @param target {Object} the target object to extend
66
+ @param properties {Object} one or more objects with properties to copy.
67
+ @returns {Object} the target object.
68
+ @static
69
+ */
70
+ SC.mixin = function() {
71
+ // copy reference to target object
72
+ var target = arguments[0] || {};
73
+ var idx = 1;
74
+ var length = arguments.length ;
75
+ var deep = NO ;
76
+ var options ;
85
77
 
86
- _downloadFrames: 0, // count of download frames inserted into document
78
+ // Handle case where we have only one item...extend SC
79
+ if (length === 1) {
80
+ target = this || {};
81
+ idx=0;
87
82
 
88
- download: function(path) {
89
- var tempDLIFrame=document.createElement('iframe');
90
- var frameId = 'DownloadFrame_' + this._downloadFrames;
91
- tempDLIFrame.setAttribute('id',frameId);
92
- tempDLIFrame.style.border='10px';
93
- tempDLIFrame.style.width='0px';
94
- tempDLIFrame.style.height='0px';
95
- tempDLIFrame.style.position='absolute';
96
- tempDLIFrame.style.top='-10000px';
97
- tempDLIFrame.style.left='-10000px';
98
- // Don't set the iFrame content yet if this is Safari
99
- if (!(SC.isSafari())) {
100
- tempDLIFrame.setAttribute('src',path);
83
+ // Handle a deep copy situation
84
+ } else if ((target===YES) || (target===NO)) {
85
+ deep = target;
86
+ target = arguments[1] || {};
87
+ idx = 2; // skip the boolean and the target
88
+ }
89
+
90
+ // Handle case when target is a string or something (possible in deep
91
+ // copy)
92
+ if ( typeof target != "object" && typeof target != "function" ) {
93
+ target = {};
94
+ }
95
+
96
+ // extend SC itself if only one argument is passed
97
+ if ( length === idx ) {
98
+ target = this;
99
+ idx = idx-1;
100
+ }
101
+
102
+ for ( ; idx < length; idx++ ) {
103
+ if (!(options = arguments[idx])) continue ;
104
+ for(var key in options) {
105
+ if (!options.hasOwnProperty(key)) continue ;
106
+
107
+ var src = target[key];
108
+ var copy = options[key] ;
109
+ if (target===copy) continue ; // prevent never-ending loop
110
+
111
+ // Recurse if we're merging object values
112
+ if ( deep && copy && (typeof copy === "object") && !copy.nodeType ) {
113
+ copy = SC.extend(deep,
114
+ src || (copy.length != null ? [ ] : { }), copy) ;
115
+ }
116
+
117
+ if (copy !== undefined) target[key] = copy ;
101
118
  }
102
- document.getElementsByTagName('body')[0].appendChild(tempDLIFrame);
103
- if (SC.isSafari()) {
104
- tempDLIFrame.setAttribute('src',path);
119
+ }
120
+
121
+ return target;
122
+ } ;
123
+
124
+ /**
125
+ Alternative to mixin. Provided for compatibility with jQuery.
126
+ @function
127
+ */
128
+ SC.extend = SC.mixin ;
129
+
130
+
131
+ // Enough with the bootstrap code. Let's define some core functions
132
+ SC.mixin(/** @scope SC */ {
133
+
134
+ // ........................................
135
+ // GLOBAL CONSTANTS
136
+ //
137
+ T_ERROR: 'error',
138
+ T_OBJECT: 'object',
139
+ T_NULL: 'null',
140
+ T_CLASS: 'class',
141
+ T_HASH: 'hash',
142
+ T_FUNCTION: 'function',
143
+ T_UNDEFINED: 'undefined',
144
+ T_NUMBER: 'number',
145
+ T_BOOL: 'boolean',
146
+ T_ARRAY: 'array',
147
+ T_STRING: 'string',
148
+
149
+ // ........................................
150
+ // CORE HELPER METHODS
151
+ //
152
+
153
+
154
+ /**
155
+ Creates a clone of the passed object. This function can take just about
156
+ any type of object and create a clone of it, including primitive values
157
+ (which are not actually cloned because they are immutable).
158
+
159
+ If the passed object implements the clone() method, then this function
160
+ will simply call that method and return the result.
161
+
162
+ @param object {Object} the object to clone
163
+ @returns {Object} the cloned object
164
+ */
165
+ clone: function(object) {
166
+ var ret = object ;
167
+ switch (SC.typeOf(object)) {
168
+ case T_ARRAY:
169
+ if (object.clone && SC.typeOf(object.clone) === SC.T_FUNCTION) {
170
+ ret = object.clone() ;
171
+ } else ret = object.slice() ;
172
+ break ;
173
+
174
+ case T_HASH:
175
+ case T_OBJECT:
176
+ if (object.clone && SC.typeOf(object.clone) === SC.T_FUNCTION) {
177
+ ret = object.clone() ;
178
+ } else {
179
+ ret = {} ;
180
+ for(var key in object) ret[key] = object[key] ;
181
+ }
105
182
  }
106
- this._downloadFrames = this._downloadFrames + 1;
107
- if (!(SC.isSafari())) {
108
- var r = function() {
109
- document.body.removeChild(document.getElementById(frameId));
110
- frameId = null;
111
- } ;
112
- var t = r.invokeLater(null, 2000);
183
+
184
+ return ret ;
185
+ },
186
+
187
+ /**
188
+ Call this method during setup of your app to queue up methods to be
189
+ called once the entire document has finished loading. If you call this
190
+ method once the document has already loaded, then the function will be
191
+ called immediately.
192
+
193
+ Any function you register with this method will be called just before
194
+ main.
195
+
196
+ @param target {Object} optional target object. Or just pass a method.
197
+ @param method {Function} the method to call.
198
+ @return {void}
199
+ */
200
+ callOnLoad: function(target, method) {
201
+
202
+ // normalize parameters
203
+ if (method === undefined) { method = target; target = null; }
204
+ if (typeof(method) === 'string') {
205
+ if (target) {
206
+ method = target[method] ;
207
+ } else {
208
+ throw "You must pass a function to callOnLoad() (got: "+method+")";
209
+ }
113
210
  }
114
- //remove possible IE7 leak
115
- tempDLIFrame = null;
116
- },
117
-
118
- // Call this method during setup of your app to queue up methods to be
119
- // called once the entire document has finished loading. If you call this
120
- // method once the document has already loaded, then the function will be
121
- // called immediately.
122
- callOnLoad: function(func) {
123
- if (SC._onloadQueueFlushed) func.call(document);
124
- var queue = SC._onloadQueue || [] ;
125
- queue.push(func) ; SC._onloadQueue = queue ;
126
- queue = null;
211
+
212
+ // invoke the method if the queue is flushed.
213
+ if (SC._onloadQueueFlushed) method.apply(target || window.document) ;
214
+ var queue = SC._onloadQueue = (SC._onloadQueue || []) ;
215
+ queue.push([target, method]) ;
127
216
  },
128
217
 
129
218
  // To flush the callOnLoad queue, you need to set window.onload=SC.didLoad
@@ -142,12 +231,17 @@ Object.extend(SC,{
142
231
  if (window.callOnLoad instanceof Array) {
143
232
  queue = window.callOnLoad ;
144
233
  } else if (window.callOnLoad instanceof Function) {
145
- queue = [window.callOnLoad] ;
234
+ queue = [window, window.callOnLoad] ;
146
235
  }
147
236
  } else queue = [] ;
148
237
  queue = queue.concat(SC._onloadQueue) ;
149
238
  var func = null ;
150
- while(func = queue.shift()) func.call(document) ;
239
+ while(func = queue.shift()) {
240
+ if (SC.typeOf(func) === T_FUNCTION) {
241
+ func.call(document) ;
242
+ } else func[1].call(func[0] || document) ;
243
+ }
244
+
151
245
  SC._onloadQueueFlushed = true ;
152
246
 
153
247
  // start the app; call main.
@@ -161,29 +255,35 @@ Object.extend(SC,{
161
255
  }
162
256
 
163
257
  SC.runLoop.endRunLoop();
258
+
164
259
  //remove possible IE7 leak
165
260
  b = null;
166
261
  queue = null;
167
262
  func = null;
168
263
  },
169
264
 
170
- // this will take a URL of any type and convert it to a fully qualified URL.
171
- normalizeURL: function(url) {
172
- if (url.slice(0,1) == '/') {
173
- url = window.location.protocol + '//' + window.location.host + url ;
174
- } else if ((url.slice(0,5) == 'http:') || (url.slice(0,6) == 'https:')) {
175
- // no change
176
- } else {
177
- url = window.location.href + '/' + url ;
178
- }
179
- return url ;
180
- },
181
-
182
- // use this instead of typeOf() to get the type of item. The return values
183
- // are: 'string', 'number', 'function', 'class', 'object', 'hash', 'null',
184
- // 'undefined', 'boolean'.
185
- // 'object' will be returned for any items inheriting from SC.Object. 'hash'
186
- // is any other type of object.
265
+ /**
266
+ Returns a consistant type for the passed item.
267
+
268
+ Use this instead of the built-in typeOf() to get the type of an item.
269
+ It will return the same result across all browsers and includes a bit
270
+ more detail. Here is what will be returned:
271
+
272
+ | Return Value Constant | Meaning |
273
+ | SC.T_STRING | String primitive |
274
+ | SC.T_NUMBER | Number primitive |
275
+ | SC.T_BOOLEAN | Boolean primitive |
276
+ | SC.T_NULL | Null value |
277
+ | SC.T_UNDEFINED | Undefined value |
278
+ | SC.T_FUNCTION | A function |
279
+ | SC.T_ARRAY | An instance of Array |
280
+ | SC.T_CLASS | A SproutCore class (created using SC.Object.extend()) |
281
+ | SC.T_OBJECT | A SproutCore object instance |
282
+ | SC.T_HASH | A JavaScript object not inheriting from SC.Object |
283
+
284
+ @param item {Object} the item to check
285
+ @returns {String} the type
286
+ */
187
287
  typeOf: function(item) {
188
288
  if (item === undefined) return T_UNDEFINED ;
189
289
  if (item === null) return T_NULL ;
@@ -193,7 +293,12 @@ Object.extend(SC,{
193
293
  ret = T_ARRAY ;
194
294
  } else if (item instanceof Function) {
195
295
  ret = (item.isClass) ? T_CLASS : T_FUNCTION ;
196
- } else if (item instanceof SC.Error) {
296
+
297
+ // NB: typeOf() may be called before SC.Error has had a chance to load
298
+ // so this code checks for the presence of SC.Error first just to make
299
+ // sure. No error instance can exist before the class loads anyway so
300
+ // this is safe.
301
+ } else if (SC.Error && (item instanceof SC.Error)) {
197
302
  ret = T_ERROR ;
198
303
  } else if (item.isObject === true) {
199
304
  ret = T_OBJECT ;
@@ -202,35 +307,121 @@ Object.extend(SC,{
202
307
  return ret ;
203
308
  },
204
309
 
205
- // this will compare two values to see if they are equal. If you have two
206
- // values of unknown type, this is faster across all browsers than ===.
207
- isEqual: function(a,b) {
208
- if (a === null) {
209
- return b === null ;
210
- } else if (a === undefined) {
211
- return b === undefined ;
212
- } else if (typeof(a) == typeof(b)) {
213
- return a == b ;
214
- }
215
- },
216
-
310
+ /**
311
+ Returns YES if the passed object is an array or array-like.
312
+
313
+ Unlike SC.$type this method returns true even if the passed object is
314
+ not formally array but appears to be array-like (i.e. has a length
315
+ property, responds to .objectAt, etc.)
316
+
317
+ @param obj {Object} the object to test
318
+ @returns {Boolean}
319
+ */
217
320
  isArray: function( obj )
218
321
  {
219
- return ($type(obj) === T_ARRAY) || (obj && obj.objectAt);
322
+ return ($type(obj) === T_ARRAY) || (obj && ((obj.length!==undefined) || obj.objectAt));
220
323
  },
221
324
 
222
- _nextGUID: 0,
223
-
325
+ /**
326
+ Converts the passed object to an Array. If the object appears to be
327
+ array-like, a new array will be cloned from it. Otherwise, a new array
328
+ will be created with the item itself as the only item in the array.
329
+
330
+ This is an alias for Array.from() as well.
331
+
332
+ @param object {Object} any enumerable or array-like object.
333
+ @returns {Array} Array of items
334
+ */
335
+ $A: function(obj) {
336
+
337
+ // null or undefined
338
+ if (obj == null) return [] ;
339
+
340
+ // primitive
341
+ if (obj.slice instanceof Function) return obj.slice() ;
342
+
343
+ // enumerable
344
+ if (obj.toArray) return obj.toArray() ;
345
+
346
+ // not array-like
347
+ if (obj.length===undefined || $type(obj) === SC.T_FUNCTION) return [obj];
348
+
349
+ // when all else fails, do a manual convert...
350
+ var len = obj.length;
351
+ var ret = [] ;
352
+ for(var idx=0;idx<len;idx++) ret[idx] = obj[idx];
353
+ return ret ;
354
+ },
355
+
224
356
  /**
225
357
  Returns a unique GUID for the object. If the object does not yet have
226
358
  a guid, one will be assigned to it. You can call this on any object,
227
359
  SC.Object-based or not, but be aware that it will add a _guid property.
360
+
361
+ @param obj {Object} any object, string, number or primitive
362
+ @returns {String} the unique guid for this instance.
228
363
  */
229
364
  guidFor: function(obj) {
230
- if (obj == null) return 0 ;
231
- return obj._guid ? obj._guid : (obj._guid = SC._nextGUID++);
365
+ if (obj === undefined) return "(undefined)" ;
366
+ if (obj === null) return '(null)' ;
367
+ if (obj._guid) return obj._guid ;
368
+
369
+ switch($type(obj)) {
370
+ case T_NUMBER:
371
+ return this._numberGuids[obj] = this._numberGuids[obj] || ("#" + obj);
372
+ break ;
373
+ case T_STRING:
374
+ return this._stringGuids[obj] = this._stringGuids[obj] || ("$" + obj);
375
+ break ;
376
+ case T_BOOL:
377
+ return (obj) ? "(true)" : "(false)" ;
378
+ break;
379
+ default:
380
+ return obj._guid = SC.generateGuid();
381
+ }
232
382
  },
233
383
 
384
+ generateGuid: function() { return ("@" + (SC._nextGUID++)); },
385
+ _nextGUID: 0, _numberGuids: [], _stringGuids: {},
386
+
387
+ /**
388
+ Returns a unique hash code for the object. If the object implements
389
+ a hash() method, the value of that method will be returned. Otherwise,
390
+ this will return the same value as guidFor().
391
+
392
+ Unlike guidFor(), this method allows you to implement logic in your
393
+ code to cause two separate instances of the same object to be treated as
394
+ if they were equal for comparisons and other functions.
395
+
396
+ IMPORTANT: If you implement a hash() method, it MUST NOT return a number
397
+ or a string that contains only a number. Typically hash codes are strings
398
+ that begin with a "%".
399
+
400
+ @param obj {Object} the object
401
+ @returns {String} the hash code for this instance.
402
+ */
403
+ hashFor: function(obj) {
404
+ return (obj && obj.hash && $type(obj.hash) === T_FUNCTION) ? obj.hash() : this.guidFor(obj) ;
405
+ },
406
+
407
+ /**
408
+ This will compare the two object values using their hash codes.
409
+
410
+ @param a {Object} first value to compare
411
+ @param b {Object} the second value to compare
412
+ @returns {Boolean} YES if the two have equal hash code values.
413
+
414
+ */
415
+ isEqual: function(a,b) {
416
+ // shortcut a few places.
417
+ if (a === null) {
418
+ return b === null ;
419
+ } else if (a === undefined) {
420
+ return b === undefined ;
421
+
422
+ // finally, check their hash-codes
423
+ } else return SC.hashFor(a) === SC.hashFor(b) ;
424
+ },
234
425
 
235
426
  /**
236
427
  Convenience method to inspect an object by converting it to a hash.
@@ -332,6 +523,9 @@ Object.extend(SC,{
332
523
 
333
524
  });
334
525
 
526
+ /** Alias for SC.typeOf() */
527
+ SC.$type = SC.typeOf ;
528
+
335
529
  /** @deprecated Use guidFor() instead. */
336
530
  SC.getGUID = SC.guidFor ;
337
531
 
@@ -346,23 +540,31 @@ SC.Platform.Browser = function() {
346
540
  }
347
541
  }() ;
348
542
 
349
- T_ERROR = 'error' ;
350
- T_OBJECT = 'object' ;
351
- T_NULL = 'null';
352
- T_CLASS = 'class' ;
353
- T_HASH = 'hash' ;
354
- T_FUNCTION = 'function' ;
355
- T_UNDEFINED = 'undefined' ;
356
- T_NUMBER = 'number' ;
357
- T_BOOL = 'boolean' ;
358
- T_ARRAY = 'array' ;
359
- T_STRING = 'string' ;
543
+ // Export the type variables into the global space.
544
+ var T_ERROR = SC.T_ERROR ;
545
+ var T_OBJECT = SC.T_OBJECT ;
546
+ var T_NULL = SC.T_NULL ;
547
+ var T_CLASS = SC.T_CLASS ;
548
+ var T_HASH = SC.T_HASH ;
549
+ var T_FUNCTION = SC.T_FUNCTION ;
550
+ var T_UNDEFINED = SC.T_UNDEFINED ;
551
+ var T_NUMBER = SC.T_NUMBER ;
552
+ var T_BOOL = SC.T_BOOL ;
553
+ var T_ARRAY = SC.T_ARRAY ;
554
+ var T_STRING = SC.T_STRING ;
360
555
 
361
- $type = SC.typeOf ;
362
556
 
557
+ // ........................................
558
+ // GLOBAL EXPORTS
559
+ //
560
+ // Global exports will be made optional in the future so you can avoid
561
+ // polluting the global namespace.
562
+
563
+ $type = SC.typeOf ;
363
564
  $I = SC.inspect ;
364
565
 
365
- Object.extend(Object,{
566
+ // Legacy. Will retire.
567
+ SC.mixin(Object,{
366
568
 
367
569
  // this will serialize a general JSON object into a URI.
368
570
  serialize: function(obj) {
@@ -344,7 +344,7 @@ Test.Unit.Assertions.prototype = {
344
344
  },
345
345
  assertEnumEqual: function(expected, actual) {
346
346
  var message = arguments[2] || "assertEnumEqual";
347
- try { $A(expected).length == $A(actual).length &&
347
+ try { SC.$A(expected).length == SC.$A(actual).length &&
348
348
  expected.zip(actual).all(function(pair) { return pair[0] == pair[1]; }) ?
349
349
  this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) +
350
350
  ', actual ' + Test.Unit.inspect(actual)); }
@@ -456,7 +456,7 @@ Test.Unit.Assertions.prototype = {
456
456
  }
457
457
  },
458
458
  assertElementsMatch: function() {
459
- var expressions = $A(arguments), elements = $A(expressions.shift());
459
+ var expressions = SC.$A(arguments), elements = SC.$A(expressions.shift());
460
460
  if (elements.length != expressions.length) {
461
461
  this.fail('assertElementsMatch: size mismatch: ' + elements.length + ' elements, ' + expressions.length + ' expressions');
462
462
  return false;
@@ -573,7 +573,7 @@ Test.setupBDDExtensionMethods = function(){
573
573
  Test.BDDMethods = {};
574
574
  $H(METHODMAP).each(function(pair) {
575
575
  Test.BDDMethods[pair.key] = function() {
576
- var args = $A(arguments);
576
+ var args = SC.$A(arguments);
577
577
  var scope = args.shift();
578
578
  makeAssertion.apply(scope, [pair.value, args, this]); };
579
579
  });
@@ -5,7 +5,7 @@
5
5
  // the browser, language, and platform. You can then call
6
6
  // SC.setupBrowserClassNames() in a script tag after the body is defined to
7
7
  // load the class names into the body.
8
- if (!SC) var SC = {} ;
8
+ var SC = SC || {} ;
9
9
  SC.browser = (function() {
10
10
 
11
11
  var userAgent = navigator.userAgent.toLowerCase();
@@ -55,12 +55,12 @@ body.sc-theme {
55
55
  border-top: 1px solid white;
56
56
  }
57
57
 
58
- .sc-theme .sc-collection-view .sel {
58
+ .sc-theme .sc-collection-view .sc-collection-item.sel {
59
59
  background-color: #ddd;
60
60
  border-top: 1px solid #eee;
61
61
  }
62
62
 
63
- .sc-theme.focus .sc-collection-view.focus .sel {
63
+ .sc-theme.focus .sc-collection-view.focus .sc-collection-item.sel {
64
64
  background-color: #40007e;
65
65
  color: white ;
66
66
  border-top: 1px solid #84788f;
@@ -105,7 +105,12 @@ SC.Application = SC.Responder.extend(
105
105
  if (!action || ($type(action) != T_STRING)) return null;
106
106
 
107
107
  // an explicit target was passed...
108
- if (target) return target.respondsTo(action) ? target : null ;
108
+ if (target) {
109
+ if ($type(target) === T_STRING) {
110
+ target = SC.Object.objectForPropertyPath(target) ;
111
+ }
112
+ return target.respondsTo(action) ? target : null ;
113
+ }
109
114
 
110
115
  // ok, no target was passed... try to find one in the responder chain
111
116
  var keyPane = this.get('keyPane');