sproutcore 0.9.14 → 0.9.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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');