sproutcore 0.9.9 → 0.9.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/History.txt +9 -0
  2. data/Manifest.txt +32 -32
  3. data/app_generators/sproutcore/sproutcore_generator.rb +2 -0
  4. data/app_generators/sproutcore/templates/README +5 -5
  5. data/bin/sc-gen +1 -1
  6. data/bin/sproutcore +1 -0
  7. data/frameworks/sproutcore/Core.js +26 -11
  8. data/frameworks/sproutcore/HISTORY +6 -0
  9. data/frameworks/sproutcore/controllers/array.js +1 -1
  10. data/frameworks/sproutcore/drag/drag.js +2 -2
  11. data/frameworks/sproutcore/english.lproj/panes.css +1 -1
  12. data/frameworks/sproutcore/english.lproj/theme.css +4 -4
  13. data/frameworks/sproutcore/foundation/binding.js +2 -1
  14. data/frameworks/sproutcore/foundation/input_manager.js +9 -8
  15. data/frameworks/sproutcore/foundation/path_module.js +1 -1
  16. data/frameworks/sproutcore/lib/core_views.rb +3 -1
  17. data/frameworks/sproutcore/lib/index.rhtml +2 -2
  18. data/frameworks/sproutcore/lib/menu_views.rb +6 -1
  19. data/frameworks/sproutcore/mixins/observable.js +119 -17
  20. data/frameworks/sproutcore/mixins/scrollable.js +5 -3
  21. data/frameworks/sproutcore/tests/views/view/innerFrame.rhtml +3 -1
  22. data/frameworks/sproutcore/views/button/button.js +15 -0
  23. data/frameworks/sproutcore/views/field/textarea_field.js +8 -0
  24. data/frameworks/sproutcore/views/form.js +39 -33
  25. data/frameworks/sproutcore/views/popup_menu.js +7 -11
  26. data/frameworks/sproutcore/views/view.js +31 -7
  27. data/lib/sproutcore/bundle.rb +44 -11
  28. data/lib/sproutcore/library.rb +5 -5
  29. data/lib/sproutcore/merb/bundle_controller.rb +7 -3
  30. data/lib/sproutcore/version.rb +1 -1
  31. data/{generators → sc_generators}/client/README +1 -1
  32. data/{generators → sc_generators}/client/USAGE +0 -0
  33. data/{generators → sc_generators}/client/client_generator.rb +0 -0
  34. data/{generators → sc_generators}/client/templates/core.js +0 -0
  35. data/{generators → sc_generators}/client/templates/english.lproj/body.css +0 -0
  36. data/{generators → sc_generators}/client/templates/english.lproj/body.rhtml +0 -0
  37. data/{generators → sc_generators}/client/templates/english.lproj/strings.js +0 -0
  38. data/{generators → sc_generators}/client/templates/main.js +0 -0
  39. data/{generators → sc_generators}/controller/USAGE +0 -0
  40. data/{generators → sc_generators}/controller/controller_generator.rb +0 -0
  41. data/{generators → sc_generators}/controller/templates/controller.js +0 -0
  42. data/{generators → sc_generators}/controller/templates/test.rhtml +0 -0
  43. data/{generators → sc_generators}/framework/README +1 -1
  44. data/{generators → sc_generators}/framework/USAGE +0 -0
  45. data/{generators → sc_generators}/framework/framework_generator.rb +0 -0
  46. data/{generators → sc_generators}/framework/templates/core.js +0 -0
  47. data/{generators → sc_generators}/framework/templates/english.lproj/strings.js +0 -0
  48. data/{generators → sc_generators}/language/USAGE +0 -0
  49. data/{generators → sc_generators}/language/language_generator.rb +0 -0
  50. data/{generators → sc_generators}/language/templates/strings.js +0 -0
  51. data/{generators → sc_generators}/model/USAGE +0 -0
  52. data/{generators → sc_generators}/model/model_generator.rb +0 -0
  53. data/{generators → sc_generators}/model/templates/fixture.js +0 -0
  54. data/{generators → sc_generators}/model/templates/model.js +0 -0
  55. data/{generators → sc_generators}/model/templates/test.rhtml +0 -0
  56. data/{generators → sc_generators}/test/USAGE +0 -0
  57. data/{generators → sc_generators}/test/templates/test.rhtml +0 -0
  58. data/{generators → sc_generators}/test/test_generator.rb +0 -0
  59. data/{generators → sc_generators}/view/USAGE +0 -0
  60. data/{generators → sc_generators}/view/templates/test.rhtml +0 -0
  61. data/{generators → sc_generators}/view/templates/view.js +0 -0
  62. data/{generators → sc_generators}/view/view_generator.rb +0 -0
  63. metadata +34 -34
data/History.txt CHANGED
@@ -1,3 +1,12 @@
1
+
2
+ * The build tools can now generate bundles with relative paths. Fixes #19
3
+
4
+ * Fixed minor typos in the generated template files closed #17
5
+
6
+ * Renamed generators to sc_generators. This avoids a conflict with Rails 2.1.
7
+
8
+ * [FIX] Applied bug fixes suggested by typo_pl to make the build tool run on windows.
9
+
1
10
  * [FIX] Changed default language mapping for Japanese from jp to ja.
2
11
 
3
12
  == SproutCore 0.9.8
data/Manifest.txt CHANGED
@@ -219,38 +219,6 @@ frameworks/sproutcore/views/split_divider.js
219
219
  frameworks/sproutcore/views/tab.js
220
220
  frameworks/sproutcore/views/toolbar.js
221
221
  frameworks/sproutcore/views/view.js
222
- generators/client/client_generator.rb
223
- generators/client/README
224
- generators/client/templates/core.js
225
- generators/client/templates/english.lproj/body.css
226
- generators/client/templates/english.lproj/body.rhtml
227
- generators/client/templates/english.lproj/strings.js
228
- generators/client/templates/main.js
229
- generators/client/USAGE
230
- generators/controller/controller_generator.rb
231
- generators/controller/templates/controller.js
232
- generators/controller/templates/test.rhtml
233
- generators/controller/USAGE
234
- generators/framework/framework_generator.rb
235
- generators/framework/README
236
- generators/framework/templates/core.js
237
- generators/framework/templates/english.lproj/strings.js
238
- generators/framework/USAGE
239
- generators/language/language_generator.rb
240
- generators/language/templates/strings.js
241
- generators/language/USAGE
242
- generators/model/model_generator.rb
243
- generators/model/templates/fixture.js
244
- generators/model/templates/model.js
245
- generators/model/templates/test.rhtml
246
- generators/model/USAGE
247
- generators/test/templates/test.rhtml
248
- generators/test/test_generator.rb
249
- generators/test/USAGE
250
- generators/view/templates/test.rhtml
251
- generators/view/templates/view.js
252
- generators/view/USAGE
253
- generators/view/view_generator.rb
254
222
  History.txt
255
223
  jsdoc/app/DocFile.js
256
224
  jsdoc/app/Doclet.js
@@ -303,6 +271,38 @@ Manifest.txt
303
271
  Rakefile
304
272
  README.txt
305
273
  sc-config.rb
274
+ sc_generators/client/client_generator.rb
275
+ sc_generators/client/README
276
+ sc_generators/client/templates/core.js
277
+ sc_generators/client/templates/english.lproj/body.css
278
+ sc_generators/client/templates/english.lproj/body.rhtml
279
+ sc_generators/client/templates/english.lproj/strings.js
280
+ sc_generators/client/templates/main.js
281
+ sc_generators/client/USAGE
282
+ sc_generators/controller/controller_generator.rb
283
+ sc_generators/controller/templates/controller.js
284
+ sc_generators/controller/templates/test.rhtml
285
+ sc_generators/controller/USAGE
286
+ sc_generators/framework/framework_generator.rb
287
+ sc_generators/framework/README
288
+ sc_generators/framework/templates/core.js
289
+ sc_generators/framework/templates/english.lproj/strings.js
290
+ sc_generators/framework/USAGE
291
+ sc_generators/language/language_generator.rb
292
+ sc_generators/language/templates/strings.js
293
+ sc_generators/language/USAGE
294
+ sc_generators/model/model_generator.rb
295
+ sc_generators/model/templates/fixture.js
296
+ sc_generators/model/templates/model.js
297
+ sc_generators/model/templates/test.rhtml
298
+ sc_generators/model/USAGE
299
+ sc_generators/test/templates/test.rhtml
300
+ sc_generators/test/test_generator.rb
301
+ sc_generators/test/USAGE
302
+ sc_generators/view/templates/test.rhtml
303
+ sc_generators/view/templates/view.js
304
+ sc_generators/view/USAGE
305
+ sc_generators/view/view_generator.rb
306
306
  script/destroy
307
307
  script/generate
308
308
  script/txt2html
@@ -1,3 +1,5 @@
1
+ require 'sproutcore/generator_helper'
2
+
1
3
  class SproutcoreGenerator < RubiGen::Base
2
4
 
3
5
  DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'],
@@ -6,7 +6,7 @@ already created one for you.)
6
6
 
7
7
  Then, start the SproutCore Dev Server by running from this directory:
8
8
 
9
- sc_server
9
+ sc-server
10
10
 
11
11
  This takes all the same arguments as mongrel. You can now visit your app
12
12
  by going to:
@@ -53,13 +53,13 @@ you require them.
53
53
 
54
54
  == Deploying your SproutCore App
55
55
 
56
- Normally you will use the sc_server to host your application while you are
56
+ Normally you will use the sc-server to host your application while you are
57
57
  developing your code. Once you are ready to deploy, however, there are two
58
58
  ways you can do it:
59
59
 
60
- ==== 1. Use sc_server in production.
60
+ ==== 1. Use sc-server in production.
61
61
 
62
- The SproutCore server can be run in a production mode that will simply generate and cache web-optimized versions of all of your resources upon request. For a low-traffic or newer site, this approach is an easy way to get your code into production. You just replace your directory with your latest files and the sc_server will start serving the new resources.
62
+ The SproutCore server can be run in a production mode that will simply generate and cache web-optimized versions of all of your resources upon request. For a low-traffic or newer site, this approach is an easy way to get your code into production. You just replace your directory with your latest files and the sc-server will start serving the new resources.
63
63
 
64
64
  ==== 2. Do a static build
65
65
 
@@ -68,7 +68,7 @@ In general, however, loading all of your resources through a Ruby-app is not the
68
68
  If you want real speed, do a static build of your content and serve it through
69
69
  lighttpd or apache. Do the static build, just run:
70
70
 
71
- sc_build all
71
+ sc-build all
72
72
 
73
73
  This will place a directory in tmp/build that contains all of your resources
74
74
  pre-compiled and ready for static serving.
data/bin/sc-gen CHANGED
@@ -22,6 +22,6 @@ require 'sproutcore'
22
22
 
23
23
  ARGV.shift if ['--help', '-h'].include?(ARGV[0])
24
24
 
25
- RubiGen::Base.use_component_sources! [:sproutcore]
25
+ RubiGen::Base.use_component_sources! [:sc]
26
26
  RubiGen::Scripts::Generate.new.run(ARGV, :destination => File.expand_path(Dir.pwd))
27
27
 
data/bin/sproutcore CHANGED
@@ -35,4 +35,5 @@ source = RubiGen::PathSource.new(:application,
35
35
  File.join(File.dirname(__FILE__), "../app_generators"))
36
36
  RubiGen::Base.reset_sources
37
37
  RubiGen::Base.append_sources source
38
+ RubiGen::Base.use_component_sources! [:sc, :app]
38
39
  RubiGen::Scripts::Generate.new.run(ARGV, :generator => 'sproutcore')
@@ -245,13 +245,17 @@ Object.extend(SC,{
245
245
 
246
246
  /** The current Firefox major version number or 0 if not Firefox */
247
247
  Firefox: function() {
248
+ var ret = 0;
248
249
  if (Prototype.Browser.Gecko) {
249
- var ret = parseFloat((navigator.userAgent.match(/Firefox\/(.)/)[1]) || 0);
250
- if (ret < 1) ret = 1;
251
- return ret ;
252
- } else return 0 ;
253
- }(),
254
-
250
+ if(navigator.userAgent.indexOf("Firefox") != -1)
251
+ {
252
+ ret = parseFloat((navigator.userAgent.match(/Firefox\/(.)/)[1]) || 0);
253
+ }
254
+ if (ret < 1) ret = 2; // default to version 2 if it is a Gecko browser.
255
+ }
256
+ return ret ;
257
+ }(),
258
+
255
259
  isWindows: function() {
256
260
  return !!(navigator.appVersion.match(/(Windows)/)) ;
257
261
  }(),
@@ -365,11 +369,22 @@ Object.extend(Object,{
365
369
  // treat it like a bool setting. Simplifies the common case where you need
366
370
  // to make a class name match a bool.
367
371
  Element.setClassName = function(element,className,flag) {
368
- if (flag) {
369
- element.addClassName(className);
370
- } else {
371
- element.removeClassName(className) ;
372
- }
372
+ if(SC.isIE())
373
+ {
374
+ if (flag) {
375
+ Element.addClassName(element,className);
376
+ } else {
377
+ Element.removeClassName(element,className) ;
378
+ }
379
+ }
380
+ else
381
+ {
382
+ if (flag) {
383
+ element.addClassName(className);
384
+ } else {
385
+ element.removeClassName(className) ;
386
+ }
387
+ }
373
388
  } ;
374
389
 
375
390
  // ........................................
@@ -1,5 +1,11 @@
1
1
  == GIT HEAD
2
2
 
3
+
4
+ * SC.Platform.Firefox now returns 2 for any gecko browser that is not Firefox.
5
+
6
+ * set() and a variety of other methods now returns this instead of the set
7
+ value. This makes it possible to do method chaining.
8
+
3
9
  == 0.9.8
4
10
 
5
11
  * [FIX] collection views now update group views appropriately.
@@ -146,7 +146,7 @@ SC.ArrayController = SC.Controller.extend(SC.Array, SC.SelectionSupport,
146
146
  // create clone of content array if needed
147
147
  var contentClone = this.get('contentClone') ;
148
148
  if (!contentClone) {
149
- contentClone = this.set('contentClone', content.clone());
149
+ this.set('contentClone', contentClone = content.clone()) ;
150
150
  }
151
151
 
152
152
  // now, record the removed objects. This may be used later.
@@ -501,8 +501,8 @@ SC.Drag = SC.Object.extend(
501
501
  return null ;
502
502
  },
503
503
 
504
- // Search the parent nodes of the target to find another view matching the drop
505
- // target. Returns null if no matching target is found.
504
+ // Search the parent nodes of the target to find another view matching the
505
+ // drop target. Returns null if no matching target is found.
506
506
  _findNextDropTarget: function(target) {
507
507
  while ((target = target.parentNode) && (target != SC.window)) {
508
508
  if (SC.Drag._dropTargets[target._guid]) return target ;
@@ -34,7 +34,7 @@
34
34
  .sc-theme .pane .shadow {
35
35
  position: relative ;
36
36
  border: none ;
37
- background: #e8e8e8 url('/static/sproutcore/_src/english.lproj/panels/background-thin.png') repeat-x left -1px;
37
+ background: #e8e8e8 static_url('panels/background-thin.png') repeat-x left -1px;
38
38
  border-top: 1px #ddd solid;
39
39
  }
40
40
 
@@ -389,7 +389,7 @@ input.show-hint {
389
389
  vertical-align: middle ;
390
390
  position: relative ;
391
391
  padding-left: 48px;
392
- background: url('/static/sproutcore/_src/english.lproj/images/sc-theme-sprite.png') no-repeat ;
392
+ background: static_url('images/sc-theme-sprite.png') no-repeat ;
393
393
  }
394
394
 
395
395
  .sc-theme.blur .sc-slider-view,
@@ -404,7 +404,7 @@ input.show-hint {
404
404
  .sc-theme .sc-slider-view .outer {
405
405
  display: block;
406
406
  height: 18px;
407
- background: url('/static/sproutcore/_src/english.lproj/images/sc-theme-sprite.png') no-repeat ;
407
+ background: static_url('images/sc-theme-sprite.png') no-repeat ;
408
408
  padding-right: 48px;
409
409
  position: relative ;
410
410
  }
@@ -421,7 +421,7 @@ input.show-hint {
421
421
  .sc-theme .sc-slider-view .inner {
422
422
  display: block;
423
423
  height: 18px;
424
- background: url('/static/sproutcore/_src/english.lproj/images/sc-theme-sprite.png') repeat-x ;
424
+ background: static_url('images/sc-theme-sprite.png') repeat-x ;
425
425
  width: 98px;
426
426
  position: relative ;
427
427
  }
@@ -443,7 +443,7 @@ input.show-hint {
443
443
  margin-left: -9px;
444
444
  left: 50%;
445
445
  top: 0;
446
- background: url('/static/sproutcore/_src/english.lproj/images/sc-theme-sprite.png') no-repeat
446
+ background: static_url('images/sc-theme-sprite.png') no-repeat
447
447
  }
448
448
 
449
449
  .sc-theme.focus .sc-slider-view .sc-handle {
@@ -162,7 +162,8 @@ SC.Binding = SC.Object.extend({
162
162
  this._lastFromValue = value ;
163
163
 
164
164
  // send along to the 'from' source.
165
- var result = tuple[0].set(tuple[1],value) ;
165
+ tuple[0].set(tuple[1],value) ;
166
+ var result = tuple[0].get(tuple[1]);
166
167
  if (result) this._lastFromPropertyRevision = result.propertyRevision ;
167
168
 
168
169
  // now that it has been set, the FROM object might not allow some
@@ -50,7 +50,8 @@ SC.InputManager = SC.Object.extend(
50
50
  return responder[methodName]( event );
51
51
  }
52
52
  }
53
- if (chr && responder.respondsTo('insertText'))
53
+ // if(cmd == "space") chr = " ";
54
+ if ( chr && responder.respondsTo('insertText'))
54
55
  {
55
56
  // if we haven't returned yet and there is plain text, then do an insert of the text.
56
57
  return responder.insertText(chr);
@@ -132,7 +133,7 @@ SC.MODIFIER_KEYS = {
132
133
 
133
134
  SC.FUNCTION_KEYS = {
134
135
  8: 'backspace', 9: 'tab', 13: 'return', 19: 'pause', 27: 'escape',
135
- 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home',
136
+ 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home',
136
137
  37: 'left', 38: 'up', 39: 'right', 40: 'down', 44: 'printscreen',
137
138
  45: 'insert', 46: 'delete', 112: 'f1', 113: 'f2', 114: 'f3', 115: 'f4',
138
139
  116: 'f5', 117: 'f7', 119: 'f8', 120: 'f9', 121: 'f10', 122: 'f11',
@@ -140,12 +141,12 @@ SC.FUNCTION_KEYS = {
140
141
  } ;
141
142
 
142
143
  SC.PRINTABLE_KEYS = {
143
- 48:"0", 49:"1", 50:"2", 51:"3", 52:"4", 53:"5", 54:"6", 55:"7", 56:"8",
144
- 57:"9", 59:";", 61:"=", 65:"a", 66:"b", 67:"c", 68:"d", 69:"e", 70:"f",
145
- 71:"g", 72:"h", 73:"i", 74:"j", 75:"k", 76:"l", 77:"m", 78:"n", 79:"o",
146
- 80:"p", 81:"q", 82:"r", 83:"s", 84:"t", 85:"u", 86:"v", 87:"w", 88:"x",
147
- 89:"y", 90:"z", 107:"+", 109:"-", 110:".", 188:",", 190:".", 191:"/",
148
- 192:"`", 219:"[", 220:"\\", 221:"]", 222:"\""
144
+ 32: ' ', 48:"0", 49:"1", 50:"2", 51:"3", 52:"4", 53:"5", 54:"6", 55:"7",
145
+ 56:"8", 57:"9", 59:";", 61:"=", 65:"a", 66:"b", 67:"c", 68:"d", 69:"e",
146
+ 70:"f", 71:"g", 72:"h", 73:"i", 74:"j", 75:"k", 76:"l", 77:"m", 78:"n",
147
+ 79:"o", 80:"p", 81:"q", 82:"r", 83:"s", 84:"t", 85:"u", 86:"v", 87:"w",
148
+ 88:"x", 89:"y", 90:"z", 107:"+", 109:"-", 110:".", 188:",", 190:".",
149
+ 191:"/", 192:"`", 219:"[", 220:"\\", 221:"]", 222:"\""
149
150
  } ;
150
151
 
151
152
  // make reverse keycode lookup for using in unit tests...
@@ -313,7 +313,7 @@ SC._PathModule = {
313
313
 
314
314
  // find first node with an attribute matching then named value.
315
315
  $P: function(el, attr, value, levels) {
316
- var ret = SC._PathModule.$$A(el, attr, value,levels,1,false) ;
316
+ var ret = SC._PathModule.$$P(el, attr, value,levels,1,false) ;
317
317
  return (ret.length>0) ? ret[0] : null ;
318
318
  },
319
319
 
@@ -114,7 +114,7 @@ view_helper :view do
114
114
  end
115
115
 
116
116
  # render the basic content
117
- content { (@tag == 'img') ? ot : %(#{ot}#{@inner_html}#{ct}) }
117
+ content { (@tag == 'img') ? %(<#{@tag} #{attributes} />) : %(#{ot}#{@inner_html}#{ct}) }
118
118
 
119
119
  end
120
120
 
@@ -194,6 +194,8 @@ view_helper :image_view do
194
194
  property :content
195
195
  property :value
196
196
 
197
+ attribute :src, static_url('blank')
198
+
197
199
  var :tag, 'img'
198
200
  css_class_names << 'sc-image-view'
199
201
  end
@@ -8,8 +8,8 @@
8
8
  # See the comments in this file for more information on what you can
9
9
  # change.
10
10
  -%>
11
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
12
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
11
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
12
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
13
13
  <html>
14
14
  <head>
15
15
  <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
@@ -39,6 +39,10 @@ end
39
39
  # The icon for the menu item. No icon will show if this is not set.
40
40
  #
41
41
  # :shortcut =>
42
+ # You must pass the actualy bit of HTML you want to display as a keyboard
43
+ # shortcut
44
+ #
45
+ # THE FOLLOWING PREVIOUS BEHAVIOR IS CURRENTLY DISABLED
42
46
  # The shortcut key for this menu item. Shortcuts are only active when
43
47
  # the anchorview the popup menu is attached to is part of the in-focus
44
48
  # pane. Shortcuts should be named in the standard input manager
@@ -68,7 +72,8 @@ view_helper :menu_item_view, :extends => :button_view do
68
72
 
69
73
  # HTML
70
74
  var :tag, 'li'
71
- var(:shortcut) { |sc| sc.split('_').map { |x| x.capitalize } * '-' }
75
+ var :shortcut
76
+ # var(:shortcut) { |sc| sc.split('_').map { |x| x.capitalize } * '-' }
72
77
  css_class_names << 'menu-item'
73
78
 
74
79
  @my_href = @href || 'javascript:;'
@@ -159,7 +159,38 @@ SC.Observable = {
159
159
  // used as properties.
160
160
 
161
161
  /**
162
- Use this to get a property value instead of obj.key.
162
+ Retrieves the value of key from the object.
163
+
164
+ This method is generally very similar to using object[key] or object.key,
165
+ however it supports both computed properties and the unknownProperty
166
+ handler.
167
+
168
+ *Computed Properties*
169
+
170
+ Computed properties are methods defined with the property() modifier
171
+ declared at the end, such as:
172
+
173
+ {{{
174
+ fullName: function() {
175
+ return this.getEach('firstName', 'lastName').compact().join(' ');
176
+ }.property('firstName', 'lastName')
177
+ }}}
178
+
179
+ When you call get() on a computed property, the property function will be
180
+ called and the return value will be returned instead of the function
181
+ itself.
182
+
183
+ *Unknown Properties*
184
+
185
+ Likewise, if you try to call get() on a property whose values is
186
+ undefined, the unknownProperty() method will be called on the object.
187
+ If this method reutrns any value other than undefined, it will be returned
188
+ instead. This allows you to implement "virtual" properties that are
189
+ not defined upfront.
190
+
191
+ @param key {String} the property to retrieve
192
+ @returns {Object} the property value or undefined.
193
+
163
194
  */
164
195
  get: function(key) {
165
196
  var ret = this[key] ;
@@ -171,7 +202,51 @@ SC.Observable = {
171
202
  },
172
203
 
173
204
  /**
174
- use this to set a property value instead of obj.key = value.
205
+ Sets the key equal to value.
206
+
207
+ This method is generally very similar to calling object[key] = value or
208
+ object.key = value, except that it provides support for computed
209
+ properties, the unknownProperty() method and property observers.
210
+
211
+ *Computed Properties*
212
+
213
+ If you try to set a value on a key that has a computed property handler
214
+ defined (see the get() method for an example), then set() will call
215
+ that method, passing both the value and key instead of simply changing
216
+ the value itself. This is useful for those times when you need to
217
+ implement a property that is composed of one or more member
218
+ properties.
219
+
220
+ *Unknown Properties*
221
+
222
+ If you try to set a value on a key that is undefined in the target
223
+ object, then the unknownProperty() handler will be called instead. This
224
+ gives you an opportunity to implement complex "virtual" properties that
225
+ are not predefined on the obejct. If unknownProperty() returns
226
+ undefined, then set() will simply set the value on the object.
227
+
228
+ *Property Observers*
229
+
230
+ In addition to changing the property, set() will also register a
231
+ property change with the object. Unless you have placed this call
232
+ inside of a beginPropertyChanges() and endPropertyChanges(), any "local"
233
+ observers (i.e. observer methods declared on the same object), will be
234
+ called immediately. Any "remote" observers (i.e. observer methods
235
+ declared on another object) will be placed in a queue and called at a
236
+ later time in a coelesced manner.
237
+
238
+ *Chaining*
239
+
240
+ In addition to property changes, set() returns the value of the object
241
+ itself so you can do chaining like this:
242
+
243
+ {{{
244
+ record.set('firstName', 'Charles').set('lastName', 'Jolley');
245
+ }}}
246
+
247
+ @param key {String} the property to set
248
+ @param value {Object} the value to set or null.
249
+ @returns {this}
175
250
  */
176
251
  set: function(key, value) {
177
252
  var func = this[key] ;
@@ -188,31 +263,45 @@ SC.Observable = {
188
263
 
189
264
  // post out notifications.
190
265
  this.propertyDidChange(key, ret) ;
191
- return ret ;
266
+ return this ;
192
267
  },
193
268
 
194
269
  /**
195
- sets the property only if the passed value is different from the
270
+ Sets the property only if the passed value is different from the
196
271
  current value. Depending on how expensive a get() is on this property,
197
272
  this may be more efficient.
273
+
274
+ @param key {String} the key to change
275
+ @param value {Object} the value to change
276
+ @returns {this}
198
277
  */
199
278
  setIfChanged: function(key, value) {
200
- return (this.get(key) !== value) ? this.set(key, value) : value ;
279
+ return (this.get(key) !== value) ? this.set(key, value) : this ;
201
280
  },
202
281
 
203
282
  /**
204
- use this to automatically navigate a property path.
283
+ Navigates the property path, returning the value at that point.
284
+
285
+ If any object in the path is undefined, returns undefined.
205
286
  */
206
287
  getPath: function(path) {
207
288
  var tuple = SC.Object.tupleForPropertyPath(path, this) ;
208
- if (tuple[0] == null) return null ;
289
+ if (tuple[0] === null) return undefined ;
209
290
  return tuple[0].get(tuple[1]) ;
210
291
  },
211
292
 
293
+ /**
294
+ Navigates the property path, finally setting the value.
295
+
296
+ @param path {String} the property path to set
297
+ @param value {Object} the value to set
298
+ @returns {this}
299
+ */
212
300
  setPath: function(path, value) {
213
301
  var tuple = SC.Object.tupleForPropertyPath(path, this) ;
214
302
  if (tuple[0] == null) return null ;
215
- return tuple[0].set(tuple[1], value) ;
303
+ tuple[0].set(tuple[1], value) ;
304
+ return this;
216
305
  },
217
306
 
218
307
 
@@ -241,7 +330,8 @@ SC.Observable = {
241
330
  @returns {Number} new value of property
242
331
  */
243
332
  incrementProperty: function(key) {
244
- return this.set(key,(this.get(key) || 0)+1);
333
+ this.set(key,(this.get(key) || 0)+1);
334
+ return this.get(key) ;
245
335
  },
246
336
 
247
337
  /**
@@ -251,7 +341,8 @@ SC.Observable = {
251
341
  @returns {Number} new value of property
252
342
  */
253
343
  decrementProperty: function(key) {
254
- return this.set(key,(this.get(key) || 0) - 1 ) ;
344
+ this.set(key,(this.get(key) || 0) - 1 ) ;
345
+ return this.get(key) ;
255
346
  },
256
347
 
257
348
  /**
@@ -266,7 +357,8 @@ SC.Observable = {
266
357
  if (value === undefined) value = true ;
267
358
  if (alt == undefined) alt = false ;
268
359
  value = (this.get(key) == value) ? alt : value ;
269
- return this.set(key,value);
360
+ this.set(key,value);
361
+ return this.get(key) ;
270
362
  },
271
363
 
272
364
  /**
@@ -275,6 +367,10 @@ SC.Observable = {
275
367
  This is a generic property handler. If you define it, it will be called
276
368
  when the named property is not yet set in the object. The default does
277
369
  nothing.
370
+
371
+ @param key {String} the key that was requested
372
+ @param value {Object} The value if called as a setter, undefined if called as a getter.
373
+ @returns {Object} The new value for key.
278
374
  */
279
375
  unknownProperty: function(key,value) {
280
376
  if (!(value === undefined)) { this[key] = value; }
@@ -313,10 +409,11 @@ SC.Observable = {
313
409
  When you are done making changes, all endPropertyChanges() to allow
314
410
  notification to resume.
315
411
 
316
- @returns {void}
412
+ @returns {this}
317
413
  */
318
414
  beginPropertyChanges: function() {
319
415
  this._kvo().changes++ ;
416
+ return this;
320
417
  },
321
418
 
322
419
  /**
@@ -329,11 +426,12 @@ SC.Observable = {
329
426
  notifications. When you are done making changes, call this method to allow
330
427
  notification to resume.
331
428
 
332
- @returns {void}
429
+ @returns {this}
333
430
  */
334
431
  endPropertyChanges: function() {
335
432
  var kvo = this._kvo() ; kvo.changes--;
336
433
  if (kvo.changes <= 0) this._notifyPropertyObservers() ;
434
+ return this ;
337
435
  },
338
436
 
339
437
  /**
@@ -350,10 +448,11 @@ SC.Observable = {
350
448
  and cause notifications to be delivered more often than you would like.
351
449
 
352
450
  @param key {String} The property key that is about to change.
353
- @returns {void}
451
+ @returns {this}
354
452
  */
355
453
  propertyWillChange: function(key) {
356
454
  this._kvo().changes++ ;
455
+ return this ;
357
456
  },
358
457
 
359
458
  /**
@@ -371,12 +470,13 @@ SC.Observable = {
371
470
 
372
471
  @param key {String} The property key that has just changed.
373
472
  @param value {Object} The new value of the key. May be null.
374
- @returns {void}
473
+ @returns {this}
375
474
  */
376
475
  propertyDidChange: function(key,value) {
377
476
  this._kvo().changed[key] = value ;
378
477
  var kvo = this._kvo() ; kvo.changes--; kvo.revision++ ;
379
478
  if (kvo.changes <= 0) this._notifyPropertyObservers() ;
479
+ return this ;
380
480
  },
381
481
 
382
482
  /**
@@ -389,11 +489,12 @@ SC.Observable = {
389
489
 
390
490
  @param key {String} The property key that has just changed.
391
491
  @param value {Object} The new value of the key. May be null.
392
- @returns {void}
492
+ @returns {this}
393
493
  */
394
494
  notifyPropertyChange: function(key, value) {
395
495
  this.propertyWillChange(key) ;
396
496
  this.propertyDidChange(key, value) ;
497
+ return this;
397
498
  },
398
499
 
399
500
  /**
@@ -406,10 +507,11 @@ SC.Observable = {
406
507
  In those cases, you can simply call this method to notify all property
407
508
  observers immediately. Note that this ignores property groups.
408
509
 
409
- @returns {void}
510
+ @returns {this}
410
511
  */
411
512
  allPropertiesDidChange: function() {
412
513
  this._notifyPropertyObservers(true) ;
514
+ return this ;
413
515
  },
414
516
 
415
517
  /**