sproutcore 0.9.9 → 0.9.10
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +9 -0
- data/Manifest.txt +32 -32
- data/app_generators/sproutcore/sproutcore_generator.rb +2 -0
- data/app_generators/sproutcore/templates/README +5 -5
- data/bin/sc-gen +1 -1
- data/bin/sproutcore +1 -0
- data/frameworks/sproutcore/Core.js +26 -11
- data/frameworks/sproutcore/HISTORY +6 -0
- data/frameworks/sproutcore/controllers/array.js +1 -1
- data/frameworks/sproutcore/drag/drag.js +2 -2
- data/frameworks/sproutcore/english.lproj/panes.css +1 -1
- data/frameworks/sproutcore/english.lproj/theme.css +4 -4
- data/frameworks/sproutcore/foundation/binding.js +2 -1
- data/frameworks/sproutcore/foundation/input_manager.js +9 -8
- data/frameworks/sproutcore/foundation/path_module.js +1 -1
- data/frameworks/sproutcore/lib/core_views.rb +3 -1
- data/frameworks/sproutcore/lib/index.rhtml +2 -2
- data/frameworks/sproutcore/lib/menu_views.rb +6 -1
- data/frameworks/sproutcore/mixins/observable.js +119 -17
- data/frameworks/sproutcore/mixins/scrollable.js +5 -3
- data/frameworks/sproutcore/tests/views/view/innerFrame.rhtml +3 -1
- data/frameworks/sproutcore/views/button/button.js +15 -0
- data/frameworks/sproutcore/views/field/textarea_field.js +8 -0
- data/frameworks/sproutcore/views/form.js +39 -33
- data/frameworks/sproutcore/views/popup_menu.js +7 -11
- data/frameworks/sproutcore/views/view.js +31 -7
- data/lib/sproutcore/bundle.rb +44 -11
- data/lib/sproutcore/library.rb +5 -5
- data/lib/sproutcore/merb/bundle_controller.rb +7 -3
- data/lib/sproutcore/version.rb +1 -1
- data/{generators → sc_generators}/client/README +1 -1
- data/{generators → sc_generators}/client/USAGE +0 -0
- data/{generators → sc_generators}/client/client_generator.rb +0 -0
- data/{generators → sc_generators}/client/templates/core.js +0 -0
- data/{generators → sc_generators}/client/templates/english.lproj/body.css +0 -0
- data/{generators → sc_generators}/client/templates/english.lproj/body.rhtml +0 -0
- data/{generators → sc_generators}/client/templates/english.lproj/strings.js +0 -0
- data/{generators → sc_generators}/client/templates/main.js +0 -0
- data/{generators → sc_generators}/controller/USAGE +0 -0
- data/{generators → sc_generators}/controller/controller_generator.rb +0 -0
- data/{generators → sc_generators}/controller/templates/controller.js +0 -0
- data/{generators → sc_generators}/controller/templates/test.rhtml +0 -0
- data/{generators → sc_generators}/framework/README +1 -1
- data/{generators → sc_generators}/framework/USAGE +0 -0
- data/{generators → sc_generators}/framework/framework_generator.rb +0 -0
- data/{generators → sc_generators}/framework/templates/core.js +0 -0
- data/{generators → sc_generators}/framework/templates/english.lproj/strings.js +0 -0
- data/{generators → sc_generators}/language/USAGE +0 -0
- data/{generators → sc_generators}/language/language_generator.rb +0 -0
- data/{generators → sc_generators}/language/templates/strings.js +0 -0
- data/{generators → sc_generators}/model/USAGE +0 -0
- data/{generators → sc_generators}/model/model_generator.rb +0 -0
- data/{generators → sc_generators}/model/templates/fixture.js +0 -0
- data/{generators → sc_generators}/model/templates/model.js +0 -0
- data/{generators → sc_generators}/model/templates/test.rhtml +0 -0
- data/{generators → sc_generators}/test/USAGE +0 -0
- data/{generators → sc_generators}/test/templates/test.rhtml +0 -0
- data/{generators → sc_generators}/test/test_generator.rb +0 -0
- data/{generators → sc_generators}/view/USAGE +0 -0
- data/{generators → sc_generators}/view/templates/test.rhtml +0 -0
- data/{generators → sc_generators}/view/templates/view.js +0 -0
- data/{generators → sc_generators}/view/view_generator.rb +0 -0
- 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
|
@@ -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
|
-
|
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
|
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
|
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
|
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
|
-
|
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! [:
|
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
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
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
|
369
|
-
|
370
|
-
|
371
|
-
|
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
|
-
|
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
|
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
|
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:
|
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:
|
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:
|
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:
|
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
|
-
|
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
|
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
|
-
|
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",
|
144
|
-
57:"9", 59:";", 61:"=", 65:"a", 66:"b", 67:"c", 68:"d", 69:"e",
|
145
|
-
71:"g", 72:"h", 73:"i", 74:"j", 75:"k", 76:"l", 77:"m", 78:"n",
|
146
|
-
80:"p", 81:"q", 82:"r", 83:"s", 84:"t", 85:"u", 86:"v", 87:"w",
|
147
|
-
89:"y", 90:"z", 107:"+", 109:"-", 110:".", 188:",", 190:".",
|
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.$$
|
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') ?
|
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
|
12
|
-
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-
|
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
|
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
|
-
|
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
|
-
|
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
|
266
|
+
return this ;
|
192
267
|
},
|
193
268
|
|
194
269
|
/**
|
195
|
-
|
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) :
|
279
|
+
return (this.get(key) !== value) ? this.set(key, value) : this ;
|
201
280
|
},
|
202
281
|
|
203
282
|
/**
|
204
|
-
|
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]
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
510
|
+
@returns {this}
|
410
511
|
*/
|
411
512
|
allPropertiesDidChange: function() {
|
412
513
|
this._notifyPropertyObservers(true) ;
|
514
|
+
return this ;
|
413
515
|
},
|
414
516
|
|
415
517
|
/**
|