sproutcore 0.9.0
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.
- data/History.txt +4 -0
- data/License.txt +20 -0
- data/Manifest.txt +269 -0
- data/README.txt +67 -0
- data/Rakefile +4 -0
- data/app_generators/sproutcore/USAGE +5 -0
- data/app_generators/sproutcore/sproutcore_generator.rb +66 -0
- data/app_generators/sproutcore/templates/README +77 -0
- data/app_generators/sproutcore/templates/environment.yml +4 -0
- data/bin/sc-build +145 -0
- data/bin/sc-gen +24 -0
- data/bin/sc-server +63 -0
- data/bin/sproutcore +21 -0
- data/clients/sc_docs/controllers/docs.js +118 -0
- data/clients/sc_docs/core.js +19 -0
- data/clients/sc_docs/english.lproj/body.css +159 -0
- data/clients/sc_docs/english.lproj/body.rhtml +33 -0
- data/clients/sc_docs/english.lproj/controls.css +0 -0
- data/clients/sc_docs/english.lproj/icons/small/next.png +0 -0
- data/clients/sc_docs/english.lproj/icons/small/reset.png +0 -0
- data/clients/sc_docs/english.lproj/images/gradients.png +0 -0
- data/clients/sc_docs/english.lproj/images/indicator.gif +0 -0
- data/clients/sc_docs/english.lproj/images/toolbar.png +0 -0
- data/clients/sc_docs/english.lproj/no_docs.rhtml +7 -0
- data/clients/sc_docs/english.lproj/strings.js +14 -0
- data/clients/sc_docs/english.lproj/warning.rhtml +6 -0
- data/clients/sc_docs/fixtures/doc.js +11 -0
- data/clients/sc_docs/main.js +21 -0
- data/clients/sc_docs/models/doc.js +9 -0
- data/clients/sc_docs/tests/controllers/docs.rhtml +21 -0
- data/clients/sc_docs/tests/models/doc.rhtml +21 -0
- data/clients/sc_docs/tests/views/doc_frame.rhtml +21 -0
- data/clients/sc_docs/tests/views/doc_label_view.rhtml +21 -0
- data/clients/sc_docs/views/doc_frame.js +33 -0
- data/clients/sc_docs/views/doc_label.js +20 -0
- data/clients/sc_test_runner/controllers/runner.js +175 -0
- data/clients/sc_test_runner/core.js +19 -0
- data/clients/sc_test_runner/english.lproj/body.css +151 -0
- data/clients/sc_test_runner/english.lproj/body.rhtml +35 -0
- data/clients/sc_test_runner/english.lproj/controls.css +0 -0
- data/clients/sc_test_runner/english.lproj/icons/small/next.png +0 -0
- data/clients/sc_test_runner/english.lproj/icons/small/reset.png +0 -0
- data/clients/sc_test_runner/english.lproj/images/gradients.png +0 -0
- data/clients/sc_test_runner/english.lproj/images/indicator.gif +0 -0
- data/clients/sc_test_runner/english.lproj/images/toolbar.png +0 -0
- data/clients/sc_test_runner/english.lproj/no_tests.rhtml +6 -0
- data/clients/sc_test_runner/english.lproj/strings.js +14 -0
- data/clients/sc_test_runner/english.lproj/warning.rhtml +6 -0
- data/clients/sc_test_runner/fixtures/test.js +12 -0
- data/clients/sc_test_runner/main.js +26 -0
- data/clients/sc_test_runner/models/test.js +11 -0
- data/clients/sc_test_runner/views/runner_frame.js +72 -0
- data/clients/sc_test_runner/views/test_label.js +20 -0
- data/config/hoe.rb +70 -0
- data/config/requirements.rb +17 -0
- data/environment.yml +9 -0
- data/frameworks/prototype/prototype.js +4186 -0
- data/frameworks/sproutcore/Core.js +378 -0
- data/frameworks/sproutcore/README +3 -0
- data/frameworks/sproutcore/controllers/array.js +236 -0
- data/frameworks/sproutcore/controllers/collection.js +305 -0
- data/frameworks/sproutcore/controllers/controller.js +323 -0
- data/frameworks/sproutcore/controllers/object.js +372 -0
- data/frameworks/sproutcore/drag/drag.js +549 -0
- data/frameworks/sproutcore/drag/drag_data_source.js +32 -0
- data/frameworks/sproutcore/drag/drag_source.js +64 -0
- data/frameworks/sproutcore/drag/drop_target.js +153 -0
- data/frameworks/sproutcore/english.lproj/blank.gif +0 -0
- data/frameworks/sproutcore/english.lproj/buttons.css +589 -0
- data/frameworks/sproutcore/english.lproj/buttons.png +0 -0
- data/frameworks/sproutcore/english.lproj/inline_text_editor.css +21 -0
- data/frameworks/sproutcore/english.lproj/menu.css +121 -0
- data/frameworks/sproutcore/english.lproj/panels/background-fat.jpg +0 -0
- data/frameworks/sproutcore/english.lproj/panels/background-thin.jpg +0 -0
- data/frameworks/sproutcore/english.lproj/panels/bottom-edge.png +0 -0
- data/frameworks/sproutcore/english.lproj/panels/bottom-left-corner.png +0 -0
- data/frameworks/sproutcore/english.lproj/panels/bottom-right-corner.png +0 -0
- data/frameworks/sproutcore/english.lproj/panels/left-edge.png +0 -0
- data/frameworks/sproutcore/english.lproj/panels/overlay.png +0 -0
- data/frameworks/sproutcore/english.lproj/panels/right-edge.png +0 -0
- data/frameworks/sproutcore/english.lproj/panels/top-edge.png +0 -0
- data/frameworks/sproutcore/english.lproj/panels/top-left-corner.png +0 -0
- data/frameworks/sproutcore/english.lproj/panels/top-right-corner.png +0 -0
- data/frameworks/sproutcore/english.lproj/panes.css +155 -0
- data/frameworks/sproutcore/english.lproj/picker.css +22 -0
- data/frameworks/sproutcore/english.lproj/strings.js +15 -0
- data/frameworks/sproutcore/english.lproj/tab.css +23 -0
- data/frameworks/sproutcore/english.lproj/tests.css +67 -0
- data/frameworks/sproutcore/english.lproj/theme.css +77 -0
- data/frameworks/sproutcore/foundation/animator.js +670 -0
- data/frameworks/sproutcore/foundation/application.js +199 -0
- data/frameworks/sproutcore/foundation/array.js +348 -0
- data/frameworks/sproutcore/foundation/benchmark.js +211 -0
- data/frameworks/sproutcore/foundation/binding.js +384 -0
- data/frameworks/sproutcore/foundation/date.js +357 -0
- data/frameworks/sproutcore/foundation/error.js +39 -0
- data/frameworks/sproutcore/foundation/input_manager.js +153 -0
- data/frameworks/sproutcore/foundation/json.js +296 -0
- data/frameworks/sproutcore/foundation/mock.js +42 -0
- data/frameworks/sproutcore/foundation/node_descriptor.js +56 -0
- data/frameworks/sproutcore/foundation/object.js +777 -0
- data/frameworks/sproutcore/foundation/observable.js +451 -0
- data/frameworks/sproutcore/foundation/page.js +63 -0
- data/frameworks/sproutcore/foundation/path_module.js +413 -0
- data/frameworks/sproutcore/foundation/responder.js +310 -0
- data/frameworks/sproutcore/foundation/routes.js +371 -0
- data/frameworks/sproutcore/foundation/run_loop.js +21 -0
- data/frameworks/sproutcore/foundation/server.js +491 -0
- data/frameworks/sproutcore/foundation/set.js +96 -0
- data/frameworks/sproutcore/foundation/string.js +149 -0
- data/frameworks/sproutcore/foundation/undo_manager.js +186 -0
- data/frameworks/sproutcore/foundation/unittest.js +622 -0
- data/frameworks/sproutcore/foundation/utils.js +61 -0
- data/frameworks/sproutcore/globals/panels.js +182 -0
- data/frameworks/sproutcore/globals/popups.js +60 -0
- data/frameworks/sproutcore/globals/window.js +381 -0
- data/frameworks/sproutcore/lib/index.rhtml +66 -0
- data/frameworks/sproutcore/models/collection.js +395 -0
- data/frameworks/sproutcore/models/record.js +622 -0
- data/frameworks/sproutcore/models/store.js +295 -0
- data/frameworks/sproutcore/panes/dialog.js +16 -0
- data/frameworks/sproutcore/panes/manager.js +164 -0
- data/frameworks/sproutcore/panes/menu.js +45 -0
- data/frameworks/sproutcore/panes/overlay.js +231 -0
- data/frameworks/sproutcore/panes/pane.js +90 -0
- data/frameworks/sproutcore/panes/panel.js +19 -0
- data/frameworks/sproutcore/panes/picker.js +45 -0
- data/frameworks/sproutcore/tests/controllers/array.rhtml +86 -0
- data/frameworks/sproutcore/tests/controllers/controller.rhtml +273 -0
- data/frameworks/sproutcore/tests/controllers/object.rhtml +327 -0
- data/frameworks/sproutcore/tests/foundation/application.rhtml +125 -0
- data/frameworks/sproutcore/tests/foundation/array.rhtml +221 -0
- data/frameworks/sproutcore/tests/foundation/object.rhtml +69 -0
- data/frameworks/sproutcore/tests/globals/window.rhtml +45 -0
- data/frameworks/sproutcore/tests/panes/pane.rhtml +88 -0
- data/frameworks/sproutcore/tests/views/collection.rhtml +137 -0
- data/frameworks/sproutcore/tests/views/popup_button.rhtml +115 -0
- data/frameworks/sproutcore/tests/views/text_field.rhtml +37 -0
- data/frameworks/sproutcore/validators/credit_card.js +92 -0
- data/frameworks/sproutcore/validators/date.js +36 -0
- data/frameworks/sproutcore/validators/email.js +29 -0
- data/frameworks/sproutcore/validators/not_empty.js +24 -0
- data/frameworks/sproutcore/validators/number.js +55 -0
- data/frameworks/sproutcore/validators/password.js +78 -0
- data/frameworks/sproutcore/validators/validator.js +304 -0
- data/frameworks/sproutcore/views/button.js +425 -0
- data/frameworks/sproutcore/views/checkbox_field.js +30 -0
- data/frameworks/sproutcore/views/collection.js +1521 -0
- data/frameworks/sproutcore/views/container.js +62 -0
- data/frameworks/sproutcore/views/error_explanation.js +45 -0
- data/frameworks/sproutcore/views/field.js +214 -0
- data/frameworks/sproutcore/views/filter_button.js +29 -0
- data/frameworks/sproutcore/views/form.js +591 -0
- data/frameworks/sproutcore/views/image.js +141 -0
- data/frameworks/sproutcore/views/inline_text_editor.js +96 -0
- data/frameworks/sproutcore/views/label.js +176 -0
- data/frameworks/sproutcore/views/menu_item.js +90 -0
- data/frameworks/sproutcore/views/pagination.js +54 -0
- data/frameworks/sproutcore/views/popup_button.js +86 -0
- data/frameworks/sproutcore/views/popup_menu.js +137 -0
- data/frameworks/sproutcore/views/progress.js +100 -0
- data/frameworks/sproutcore/views/radio_field.js +107 -0
- data/frameworks/sproutcore/views/radio_group.js +48 -0
- data/frameworks/sproutcore/views/segmented.js +80 -0
- data/frameworks/sproutcore/views/select_field.js +272 -0
- data/frameworks/sproutcore/views/spinner.js +11 -0
- data/frameworks/sproutcore/views/tab.js +126 -0
- data/frameworks/sproutcore/views/text_field.js +179 -0
- data/frameworks/sproutcore/views/textarea_field.js +14 -0
- data/frameworks/sproutcore/views/toolbar.js +29 -0
- data/frameworks/sproutcore/views/view.js +1389 -0
- data/frameworks/sproutcore/views/workspace.js +170 -0
- data/generators/client/README +3 -0
- data/generators/client/USAGE +12 -0
- data/generators/client/client_generator.rb +53 -0
- data/generators/client/templates/core.js +19 -0
- data/generators/client/templates/english.lproj/body.css +0 -0
- data/generators/client/templates/english.lproj/body.rhtml +3 -0
- data/generators/client/templates/english.lproj/controls.css +0 -0
- data/generators/client/templates/english.lproj/strings.js +14 -0
- data/generators/client/templates/main.js +37 -0
- data/generators/controller/USAGE +16 -0
- data/generators/controller/controller_generator.rb +51 -0
- data/generators/controller/templates/controller.js +21 -0
- data/generators/controller/templates/test.rhtml +21 -0
- data/generators/framework/README +7 -0
- data/generators/framework/USAGE +12 -0
- data/generators/framework/framework_generator.rb +53 -0
- data/generators/framework/templates/core.js +20 -0
- data/generators/framework/templates/english.lproj/body.css +0 -0
- data/generators/framework/templates/english.lproj/body.rhtml +3 -0
- data/generators/framework/templates/english.lproj/controls.css +0 -0
- data/generators/framework/templates/english.lproj/strings.js +14 -0
- data/generators/language/USAGE +16 -0
- data/generators/language/language_generator.rb +47 -0
- data/generators/language/templates/strings.js +10 -0
- data/generators/model/USAGE +24 -0
- data/generators/model/model_generator.rb +55 -0
- data/generators/model/templates/fixture.js +11 -0
- data/generators/model/templates/model.js +20 -0
- data/generators/model/templates/test.rhtml +21 -0
- data/generators/test/USAGE +16 -0
- data/generators/test/templates/test.rhtml +21 -0
- data/generators/test/test_generator.rb +47 -0
- data/generators/view/USAGE +16 -0
- data/generators/view/templates/test.rhtml +21 -0
- data/generators/view/templates/view.js +20 -0
- data/generators/view/view_generator.rb +51 -0
- data/jsdoc/README.txt +119 -0
- data/jsdoc/app/DocFile.js +137 -0
- data/jsdoc/app/DocTag.js +110 -0
- data/jsdoc/app/Doclet.js +63 -0
- data/jsdoc/app/Dumper.js +143 -0
- data/jsdoc/app/JsDoc.js +103 -0
- data/jsdoc/app/JsHilite.js +45 -0
- data/jsdoc/app/JsIO.js +163 -0
- data/jsdoc/app/JsParse.js +385 -0
- data/jsdoc/app/JsPlate.js +130 -0
- data/jsdoc/app/JsTestrun.js +129 -0
- data/jsdoc/app/JsToke.js +564 -0
- data/jsdoc/app/Symbol.js +298 -0
- data/jsdoc/app/Transformer.js +14 -0
- data/jsdoc/app/Util.js +97 -0
- data/jsdoc/app/js.jar +0 -0
- data/jsdoc/app/run.js +144 -0
- data/jsdoc/plugins/min.js +316 -0
- data/jsdoc/plugins/strip.js +20 -0
- data/jsdoc/templates/sproutcore/class.tmpl +438 -0
- data/jsdoc/templates/sproutcore/default.css +241 -0
- data/jsdoc/templates/sproutcore/index.html +13 -0
- data/jsdoc/templates/sproutcore/index.tmpl +21 -0
- data/jsdoc/templates/sproutcore/prototype.js +4186 -0
- data/jsdoc/templates/sproutcore/publish.js +236 -0
- data/jsdoc/templates/sproutcore/splash.html +7 -0
- data/lib/sproutcore/build_tools/html_builder.rb +88 -0
- data/lib/sproutcore/build_tools/resource_builder.rb +194 -0
- data/lib/sproutcore/build_tools.rb +44 -0
- data/lib/sproutcore/bundle.rb +517 -0
- data/lib/sproutcore/bundle_manifest.rb +397 -0
- data/lib/sproutcore/generator_helper.rb +170 -0
- data/lib/sproutcore/helpers/capture_helper.rb +42 -0
- data/lib/sproutcore/helpers/static_helper.rb +80 -0
- data/lib/sproutcore/helpers/tag_helper.rb +110 -0
- data/lib/sproutcore/helpers/text_helper.rb +336 -0
- data/lib/sproutcore/helpers.rb +3 -0
- data/lib/sproutcore/jsdoc.rb +40 -0
- data/lib/sproutcore/jsmin.rb +247 -0
- data/lib/sproutcore/library.rb +258 -0
- data/lib/sproutcore/merb/bundle_controller.rb +179 -0
- data/lib/sproutcore/merb/router.rb +43 -0
- data/lib/sproutcore/merb.rb +27 -0
- data/lib/sproutcore/version.rb +9 -0
- data/lib/sproutcore/view_helpers/button_views.rb +302 -0
- data/lib/sproutcore/view_helpers/core_views.rb +284 -0
- data/lib/sproutcore/view_helpers/form_views.rb +258 -0
- data/lib/sproutcore/view_helpers/menu_views.rb +94 -0
- data/lib/sproutcore/view_helpers.rb +628 -0
- data/lib/sproutcore.rb +30 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +74 -0
- data/setup.rb +1585 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/sproutcore_spec.rb +11 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/environment.rake +7 -0
- data/tasks/rspec.rake +21 -0
- data/tasks/website.rake +17 -0
- metadata +365 -0
|
@@ -0,0 +1,777 @@
|
|
|
1
|
+
// ========================================================================
|
|
2
|
+
// SproutCore
|
|
3
|
+
// copyright 2006-2007 Sprout Systems, Inc.
|
|
4
|
+
// ========================================================================
|
|
5
|
+
|
|
6
|
+
require('Core') ;
|
|
7
|
+
require('foundation/benchmark') ;
|
|
8
|
+
require('foundation/observable') ;
|
|
9
|
+
|
|
10
|
+
SC.BENCHMARK_OBJECTS = NO;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
@class Root Object for SproutCore framework.
|
|
14
|
+
|
|
15
|
+
This is the root object of the SproutCore framework. All other objects
|
|
16
|
+
inherit from you. SC.Object among other things implements support for
|
|
17
|
+
class inheritance, observers, and bindings. You should use SC.Object to
|
|
18
|
+
create your own subclasses as well.
|
|
19
|
+
|
|
20
|
+
Please see online documentation for more information about this.
|
|
21
|
+
|
|
22
|
+
I should be able to put a long list of things here I suppose.
|
|
23
|
+
|
|
24
|
+
Do bullet
|
|
25
|
+
points
|
|
26
|
+
work?
|
|
27
|
+
|
|
28
|
+
@extends SC.Observable
|
|
29
|
+
*/
|
|
30
|
+
SC.Object = function(noinit) {
|
|
31
|
+
if (noinit == SC.Object._noinit_) return ;
|
|
32
|
+
var ret = SC.Object._init.apply(this,$A(arguments)) ;
|
|
33
|
+
return ret ;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
Object.extend(SC.Object, {
|
|
37
|
+
|
|
38
|
+
_noinit_: '__noinit__',
|
|
39
|
+
|
|
40
|
+
// Use this to add class methods to an object.
|
|
41
|
+
mixin: function() {
|
|
42
|
+
var ext = $A(arguments) ;
|
|
43
|
+
for(var loc=0;loc<ext.length;loc++) {
|
|
44
|
+
Object.extend(this,ext[loc]);
|
|
45
|
+
}
|
|
46
|
+
return this ;
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
// this will created a new version of the passed object, updating the
|
|
50
|
+
// prototype to use the new code.
|
|
51
|
+
extend: function() {
|
|
52
|
+
|
|
53
|
+
if (SC.BENCHMARK_OBJECTS) SC.Benchmark.start('SC.Object.extend') ;
|
|
54
|
+
|
|
55
|
+
// build function. copy class methods on to it.
|
|
56
|
+
var ret = function(noinit) {
|
|
57
|
+
if (noinit && (typeof(noinit) == 'string') && (noinit == SC.Object._noinit_)) return ;
|
|
58
|
+
var ret = SC.Object._init.apply(this,$A(arguments));
|
|
59
|
+
return ret ;
|
|
60
|
+
};
|
|
61
|
+
for(var prop in this) { ret[prop] = this[prop]; }
|
|
62
|
+
|
|
63
|
+
// extend the prototype with passed arguments.
|
|
64
|
+
var base = new this(SC.Object._noinit_) ;
|
|
65
|
+
|
|
66
|
+
// var base = SC.Object._extend({},this.prototype) ;
|
|
67
|
+
var extensions = $A(arguments) ;
|
|
68
|
+
for(var loc=0;loc<extensions.length;loc++) {
|
|
69
|
+
base = SC.Object._extend(base, extensions[loc]);
|
|
70
|
+
}
|
|
71
|
+
ret.prototype = base ;
|
|
72
|
+
|
|
73
|
+
// return new extension
|
|
74
|
+
ret._guid = SC._nextGUID++ ; // each time we extend, get a new guid.
|
|
75
|
+
ret._type = this ;
|
|
76
|
+
|
|
77
|
+
if (SC.BENCHMARK_OBJECTS) SC.Benchmark.end('SC.Object.extend') ;
|
|
78
|
+
|
|
79
|
+
return ret ;
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
// create a new instance of the object.
|
|
83
|
+
create: function() {
|
|
84
|
+
var ret = new this($A(arguments),this) ;
|
|
85
|
+
return ret ;
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
// takes an array of configs and returns instances of each object.
|
|
89
|
+
createArray: function(array) {
|
|
90
|
+
var obj = this ;
|
|
91
|
+
return array.map(function(props) { return obj.create(props); }) ;
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
// configure this object as an outlet
|
|
95
|
+
outlet: function() {
|
|
96
|
+
var obj = this ;
|
|
97
|
+
return function() {
|
|
98
|
+
var ret = obj.create() ; ret.owner = this ; return ret ;
|
|
99
|
+
} ;
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
isClass: true,
|
|
103
|
+
|
|
104
|
+
// ..........................................
|
|
105
|
+
// PROPERTY SUPPORT METHODS
|
|
106
|
+
//
|
|
107
|
+
// get the tuple for a property path (the object and key).
|
|
108
|
+
tupleForPropertyPath: function(path,root) {
|
|
109
|
+
if (path.constructor == Array) return path ;
|
|
110
|
+
|
|
111
|
+
// * = the rest is a chained property.
|
|
112
|
+
var parts = path.split('*') ; var key = null ;
|
|
113
|
+
if (parts && parts.length > 1) {
|
|
114
|
+
key = parts.pop(); path = parts.join('*') ;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// get object path. property is last part if * was nt found.
|
|
118
|
+
parts = path.split('.') ;
|
|
119
|
+
if (!key) key = parts.pop() ;
|
|
120
|
+
|
|
121
|
+
// convert path to object.
|
|
122
|
+
var obj = this.objectForPropertyPath(parts,root) ;
|
|
123
|
+
return (obj && key) ? [obj,key] : null ;
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
objectForPropertyPath: function(path,root) {
|
|
127
|
+
var parts = (typeof(path) == "string") ? path.split('.') : path ;
|
|
128
|
+
if (!root) root = window ;
|
|
129
|
+
var key = parts.shift() ;
|
|
130
|
+
while(key && root) {
|
|
131
|
+
root = (root.get) ? root.get(key) : root[key];
|
|
132
|
+
key = parts.shift();
|
|
133
|
+
}
|
|
134
|
+
return (parts.length > 0) ? undefined : root ;
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
// ..........................................
|
|
139
|
+
// INTERNAL SUPPORT METHODS
|
|
140
|
+
//
|
|
141
|
+
_init: function(extensions,type) {
|
|
142
|
+
|
|
143
|
+
var ret = this ;
|
|
144
|
+
for(var loc=0;loc<extensions.length;loc++) {
|
|
145
|
+
ret = SC.Object._extend(ret,extensions[loc]) ;
|
|
146
|
+
}
|
|
147
|
+
ret._guid = SC._nextGUID++ ;
|
|
148
|
+
ret._type = type ;
|
|
149
|
+
ret.init() ;
|
|
150
|
+
|
|
151
|
+
return ret ;
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
// This will extend the base object with the properties passed in the second
|
|
155
|
+
// var, maintaining super's for the functions and concatinating cprops.
|
|
156
|
+
_extend: function(base,ext) { return this._extendAllProps(false, base, ext); },
|
|
157
|
+
|
|
158
|
+
_extendAllProps: function(allProperties, base,ext) {
|
|
159
|
+
var cprops = base._cprops ; var f = Prototype.emptyFunction ;
|
|
160
|
+
|
|
161
|
+
// first, save any concat props.
|
|
162
|
+
var concats = {} ;
|
|
163
|
+
if (cprops) for(var cloc=0;cloc<cprops.length;cloc++) {
|
|
164
|
+
var p = cprops[cloc]; var p1 = base[p]; var p2 = ext[p] ;
|
|
165
|
+
p1 = (p1 && p2) ? p1.concat(p2) : (p1 || p2) ;
|
|
166
|
+
concats[p] = p1 ;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// deal with observers, bindings, and properties only if they are not
|
|
170
|
+
// defined already in the ext. If they are defined in ext, they will be
|
|
171
|
+
// concated using the above code.
|
|
172
|
+
var bindings = (ext._bindings) ? null : (base._bindings || []).slice() ;
|
|
173
|
+
var observers = (ext._observers) ? null : (base._observers || []).slice();
|
|
174
|
+
var properties = (ext._properties) ? null : (base._properties || []).slice() ;
|
|
175
|
+
|
|
176
|
+
// now copy properties, add superclass to func.
|
|
177
|
+
for(var key in ext) {
|
|
178
|
+
// avoid copying builtin methods
|
|
179
|
+
if (!allProperties && !ext.hasOwnProperty(key)) continue ;
|
|
180
|
+
|
|
181
|
+
// add super to funcs. Be sure not to set the base of a func to itself
|
|
182
|
+
// to avoid infinite loops.
|
|
183
|
+
var value = (concats.hasOwnProperty(key) ? concats[key] : null) || ext[key] ;
|
|
184
|
+
if (value && (value instanceof Function) && (!value.base)) {
|
|
185
|
+
if (value != base[key]) value.base = base[key] || f;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Possibly add to a bindings.
|
|
189
|
+
var keyLen = key.length ;
|
|
190
|
+
if (bindings && (key.slice(keyLen-7,keyLen) == "Binding")) {
|
|
191
|
+
bindings.push(key) ;
|
|
192
|
+
|
|
193
|
+
// Also add observers and properties for functions...
|
|
194
|
+
} else if (value && (value instanceof Function)) {
|
|
195
|
+
if (observers && value.propertyPaths) {
|
|
196
|
+
observers.push(key) ;
|
|
197
|
+
} else if (properties && value.dependentKeys) {
|
|
198
|
+
properties.push(key) ;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// copy property
|
|
203
|
+
base[key] = value ;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// copy bindings, observers, and properties if defined.
|
|
207
|
+
if (bindings) base._bindings = bindings;
|
|
208
|
+
if (observers) base._observers = observers ;
|
|
209
|
+
if (properties) base._properties = properties ;
|
|
210
|
+
|
|
211
|
+
//console.log('bindings: %@ -- observers: %@ -- properties: %@'.format(base._bindings,base._observers,base._properties)) ;
|
|
212
|
+
|
|
213
|
+
return base ;
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
toString: function() { return 'Object('+ SC.getGUID(this) +')'; },
|
|
217
|
+
|
|
218
|
+
// Returns true if the receiver is a subclass of the named class. If the
|
|
219
|
+
// receiver is the class passed, this will return false. See kindOf().
|
|
220
|
+
subclassOf: function(scClass) {
|
|
221
|
+
if (this == scClass) return false ;
|
|
222
|
+
var t = this._type ;
|
|
223
|
+
while(t) {
|
|
224
|
+
if (t == scClass) return true ;
|
|
225
|
+
t = t._type ;
|
|
226
|
+
}
|
|
227
|
+
return false ;
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
// Returns true if the receiver is the passed class or is any subclass.
|
|
231
|
+
kindOf: function(scClass) {
|
|
232
|
+
if (this == scClass) return true ;
|
|
233
|
+
return this.subclassOf(scClass);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
}) ;
|
|
237
|
+
|
|
238
|
+
SC.idt = { count: 0, t: 0.0, keys: 0, observers: 0, bindings: 0, pv: 0, observers_t: 0, bindings_t: 0, pv_t: 0, conf_t: 0, b1_t: 0, b2_t: 0, b3_t: 0, e_count: 0, e_t: 0, v_count: 0, v_t: 0, vc_t: 0 ,active: false } ;
|
|
239
|
+
|
|
240
|
+
SC.report = function() {
|
|
241
|
+
var c = SC.idt.count ;
|
|
242
|
+
var e = SC.idt.e_count ;
|
|
243
|
+
var v = SC.idt.v_count ;
|
|
244
|
+
var ret = [] ;
|
|
245
|
+
ret.push('CREATED: ' + c + ' (avg time: '+(Math.floor(SC.idt.t * 100 / c) / 100)+' msec)') ;
|
|
246
|
+
ret.push('EXTENDED: ' + e + ' (avg time: '+(Math.floor(SC.idt.e_t * 100 / e) / 100)+' msec)') ;
|
|
247
|
+
ret.push('AVG KEYS: ' + (Math.floor(SC.idt.keys * 100 / c) / 100)) ;
|
|
248
|
+
ret.push('AVG OBSERVERS: ' + (Math.floor(SC.idt.observers * 100 / c) / 100) + ' ('+ (Math.floor(SC.idt.observers_t * 100 / c) / 100) + ' msec)') ;
|
|
249
|
+
ret.push('AVG BINDINGS: ' + (Math.floor(SC.idt.bindings * 100 / c) / 100) + ' ('+ (Math.floor(SC.idt.bindings_t * 100 / c) / 100) + ' msec)') ;
|
|
250
|
+
ret.push('AVG PV: ' + (Math.floor(SC.idt.pv * 100 / c) / 100) + ' ('+ (Math.floor(SC.idt.pv_t * 100 / c) / 100) + ' msec)') ;
|
|
251
|
+
ret.push('AVG CONFIGURE OUTLETS: ' + (Math.floor(SC.idt.conf_t * 100 / c) / 100) + ' msec') ;
|
|
252
|
+
ret.push('AVG B1: ' + (Math.floor(SC.idt.b1_t * 100 / c) / 100) + ' msec') ;
|
|
253
|
+
ret.push('EXT: ' + SC.idt.ext_c + ' (avg time: ' + (Math.floor(SC.idt.ext_t * 100 / SC.idt.ext_c) / 100) + ' msec)') ;
|
|
254
|
+
ret.push('VIEWS: ' + v + ' (avg time: ' + (Math.floor(SC.idt.v_t * 100 / v) / 100) + ' msec)') ;
|
|
255
|
+
ret.push('VIEW CREATE: ' + (Math.floor(SC.idt.vc_t * 100 / v) / 100) + ' msec)') ;
|
|
256
|
+
console.log(ret.join("\n")) ;
|
|
257
|
+
return ret.join("\n") ;
|
|
258
|
+
} ;
|
|
259
|
+
|
|
260
|
+
// ..........................................
|
|
261
|
+
// DEFAULT OBJECT INSTANCE
|
|
262
|
+
//
|
|
263
|
+
SC.Object.prototype = {
|
|
264
|
+
|
|
265
|
+
isObject: true,
|
|
266
|
+
|
|
267
|
+
respondsTo: function( name )
|
|
268
|
+
{
|
|
269
|
+
return !!(name && this[name] && ($type(this[name]) == T_FUNCTION));
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
tryToPerform: function( name, args )
|
|
273
|
+
{
|
|
274
|
+
if ( !name ) return false;
|
|
275
|
+
|
|
276
|
+
var args = $A(arguments);
|
|
277
|
+
var name = args.shift();
|
|
278
|
+
if (this.respondsTo(name))
|
|
279
|
+
{
|
|
280
|
+
return this[name].apply(this,args);
|
|
281
|
+
}
|
|
282
|
+
return false;
|
|
283
|
+
},
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
this function is called automatically when a new object instance is
|
|
287
|
+
created. You can use this to configure your child elements if you want.
|
|
288
|
+
Be sure to invoke the base method.
|
|
289
|
+
*/
|
|
290
|
+
init: function() {
|
|
291
|
+
|
|
292
|
+
// This keySource is used to fix a bug in FF that will not iterate through
|
|
293
|
+
// keys you add to an HTMLElement. The key values are still accessible,
|
|
294
|
+
// they just aren't visible to the for...in loop. viewType is the hash
|
|
295
|
+
// of values that was applied to the HTMLElement, so its a 1:1 substitute.
|
|
296
|
+
var keySource = this.viewType || this ;
|
|
297
|
+
var loc ; var keys ; var key ; var value ;
|
|
298
|
+
|
|
299
|
+
var r = SC.idt.active ; var idtStart ; var idtSt ;
|
|
300
|
+
if (r) {
|
|
301
|
+
SC.idt.count++;
|
|
302
|
+
idtStart = new Date().getTime() ;
|
|
303
|
+
} ;
|
|
304
|
+
|
|
305
|
+
// Add Observers
|
|
306
|
+
if (keys = keySource._observers) for(loc=0;loc<keys.length;loc++) {
|
|
307
|
+
key = keys[loc] ; value = this[key] ;
|
|
308
|
+
|
|
309
|
+
if (r) {
|
|
310
|
+
SC.idt.keys++ ;
|
|
311
|
+
SC.idt.observers++ ;
|
|
312
|
+
idtSt = new Date().getTime() ;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
var propertyPaths = null ;
|
|
316
|
+
if ((value instanceof Function) && value.propertyPaths) {
|
|
317
|
+
propertyPaths = value.propertyPaths ;
|
|
318
|
+
value = value.bind(this) ;
|
|
319
|
+
} else if (typeof(value) == "string") {
|
|
320
|
+
propertyPaths = [value] ;
|
|
321
|
+
value = this.propertyObserver.bind(this,key.slice(0,-8)) ;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// a property path string was found. Convert this to an object/path
|
|
325
|
+
// and observe.
|
|
326
|
+
if (propertyPaths) for(var ploc=0;ploc<propertyPaths.length;ploc++) {
|
|
327
|
+
var propertyPath = propertyPaths[ploc] ;
|
|
328
|
+
var object = null;
|
|
329
|
+
|
|
330
|
+
// most common case. refers to local property.
|
|
331
|
+
if (propertyPath.indexOf('.') == -1) {
|
|
332
|
+
this.addObserver(propertyPath, value) ;
|
|
333
|
+
|
|
334
|
+
// deal with more exotic cases
|
|
335
|
+
} else switch(propertyPath.slice(0,1)) {
|
|
336
|
+
// start with a . or * means this is a chained local path.
|
|
337
|
+
case '*':
|
|
338
|
+
case '.':
|
|
339
|
+
propertyPath = propertyPath.slice(1,propertyPath.length) ;
|
|
340
|
+
this.addObserver(propertyPath, value) ;
|
|
341
|
+
break ;
|
|
342
|
+
|
|
343
|
+
// this is an absolute path. so hook 'er up.
|
|
344
|
+
default:
|
|
345
|
+
SC.Observers.addObserver(propertyPath, value) ;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (r) SC.idt.observers_t += (new Date().getTime()) - idtSt ;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Add Bindings
|
|
353
|
+
this.bindings = [] ;
|
|
354
|
+
if (keys = keySource._bindings) for(loc=0;loc<keys.length;loc++) {
|
|
355
|
+
key = keys[loc] ; value = this[key] ;
|
|
356
|
+
|
|
357
|
+
if (r) {
|
|
358
|
+
SC.idt.keys++ ;
|
|
359
|
+
SC.idt.bindings++ ;
|
|
360
|
+
idtSt = new Date().getTime() ;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// get propertyKey
|
|
364
|
+
var propertyKey = key.slice(0,-7) ; // contentBinding => content
|
|
365
|
+
this[key] = this.bind(propertyKey, value) ;
|
|
366
|
+
if (r) SC.idt.bindings_t += (new Date().getTime()) - idtSt ;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Add Properties
|
|
370
|
+
if (keys = keySource._properties) for(loc=0;loc<keys.length;loc++) {
|
|
371
|
+
key = keys[loc] ; value = this[key] ;
|
|
372
|
+
if (value && value.dependentKeys && (value.dependentKeys.length > 0)) {
|
|
373
|
+
args = value.dependentKeys.slice() ;
|
|
374
|
+
args.unshift(key) ;
|
|
375
|
+
this.registerDependentKey.apply(this,args) ;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (r) { SC.idt.t += ((new Date().getTime()) - idtStart); }
|
|
380
|
+
},
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
EXPERIMENTAL: You can use this to call super in any method. This
|
|
384
|
+
currently does not work in some versions of Safari.
|
|
385
|
+
*/
|
|
386
|
+
$super: function(args) {
|
|
387
|
+
var caller = SC.Object.prototype.$super.caller;
|
|
388
|
+
if (!caller) throw "$super cannot determine the caller method" ;
|
|
389
|
+
if (caller.base) caller.base.apply(this, arguments) ;
|
|
390
|
+
},
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
Use this to add class methods to an object.
|
|
394
|
+
*/
|
|
395
|
+
mixin: function() { return SC.Object.mixin.apply(this,arguments) ; },
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
Returns all the keys defined on this object, excluding any defined in
|
|
399
|
+
parent classes unless you pass all.
|
|
400
|
+
|
|
401
|
+
@param {Boolean} all OPTIONAL: if YES return all keys, NO return only keys belonging to object itself. Defaults to NO.
|
|
402
|
+
*/
|
|
403
|
+
keys: function(all) {
|
|
404
|
+
var ret = []; for(var key in this) {
|
|
405
|
+
if (all || ret.hasOwnProperty(key)) ret.push(key);
|
|
406
|
+
}; return ret ;
|
|
407
|
+
},
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
returns true if the receiver is an instance of the named class. See also
|
|
411
|
+
kindOf().
|
|
412
|
+
*/
|
|
413
|
+
instanceOf: function(scClass) {
|
|
414
|
+
return this._type == scClass ;
|
|
415
|
+
},
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
Returns true if the receiver is an instance of the named class or any
|
|
419
|
+
subclass of the named class. See also instanceOf().
|
|
420
|
+
*/
|
|
421
|
+
kindOf: function(scClass) {
|
|
422
|
+
var t = this._type ;
|
|
423
|
+
while(t) {
|
|
424
|
+
if (t == scClass) return true ;
|
|
425
|
+
t = t._type ;
|
|
426
|
+
}
|
|
427
|
+
return false ;
|
|
428
|
+
},
|
|
429
|
+
|
|
430
|
+
/** @private */
|
|
431
|
+
_enumerator: function() {
|
|
432
|
+
|
|
433
|
+
// build in enumerator.
|
|
434
|
+
if (this.objectAtIndex) {
|
|
435
|
+
ret = {
|
|
436
|
+
nextObject: function() {
|
|
437
|
+
var loc = this.loc ;
|
|
438
|
+
return (loc>=0) ? this.obj.objectAtIndex(--loc) : null ;
|
|
439
|
+
},
|
|
440
|
+
loc: this.get('length') || 0,
|
|
441
|
+
obj: this
|
|
442
|
+
} ;
|
|
443
|
+
|
|
444
|
+
// or just return one that does nothing.
|
|
445
|
+
} else {
|
|
446
|
+
ret = { nextObject: function() { return null; } } ;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return ret ;
|
|
450
|
+
},
|
|
451
|
+
|
|
452
|
+
// ..........................................
|
|
453
|
+
// OUTLETS
|
|
454
|
+
//
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
A view may contain outlets. Outlets are a way to find and connect to
|
|
458
|
+
elements within the view.
|
|
459
|
+
*/
|
|
460
|
+
awake: function(key) {
|
|
461
|
+
// if a key is passed, convert that from an outlet and awake it. otherwise
|
|
462
|
+
// awake self.
|
|
463
|
+
if (key !== undefined) {
|
|
464
|
+
var obj = this.outlet(key) ;
|
|
465
|
+
if (obj) obj.awake() ;
|
|
466
|
+
return ;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (this._awake) return ;
|
|
470
|
+
this._awake = true ;
|
|
471
|
+
|
|
472
|
+
// it would be cool to do this in a recursive way, but sadly we cannot
|
|
473
|
+
// without a stack overflow problem. Just loop through outlets and collect
|
|
474
|
+
// items to awake.
|
|
475
|
+
this.bindings.invoke('relay') ;
|
|
476
|
+
|
|
477
|
+
if (this.outlets && this.outlets.length) {
|
|
478
|
+
var stack = [] ;
|
|
479
|
+
var working = [this, this.outlets.slice()] ;
|
|
480
|
+
while(working) {
|
|
481
|
+
// find the next item to work on.
|
|
482
|
+
var next = working[1].pop() ;
|
|
483
|
+
var obj = working[0] ;
|
|
484
|
+
|
|
485
|
+
// an item was found in the array. Process it.
|
|
486
|
+
if (next) {
|
|
487
|
+
next = obj[next] ;
|
|
488
|
+
if (next) {
|
|
489
|
+
// awake these bindings.
|
|
490
|
+
if (next.bindings) next.bindings.invoke('relay') ;
|
|
491
|
+
|
|
492
|
+
// next has outlets itself. Start a new context and process them.
|
|
493
|
+
if (next.outlets && next.outlets.length > 0) {
|
|
494
|
+
stack.push(working) ;
|
|
495
|
+
working = [next,next.outlets.slice()] ;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// no more items found in the current array. pop the stack.
|
|
500
|
+
} else working = stack.pop() ;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
},
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
name your outlets here or name the outlets "somethingOutlet". See
|
|
508
|
+
also outletFor in views.
|
|
509
|
+
*/
|
|
510
|
+
outlets: [],
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
This method works just like get() except if the value of the property
|
|
514
|
+
is an outlet-packaged View, the view will be created first. You can use
|
|
515
|
+
this to create lazy outlets. (DOC-INCOMPLETE)
|
|
516
|
+
|
|
517
|
+
@param {String} key The key to read as an outlet.
|
|
518
|
+
*/
|
|
519
|
+
outlet: function(key) {
|
|
520
|
+
var value = this[key] ; // get the current value.
|
|
521
|
+
|
|
522
|
+
// if its an outlet, then configure it first.
|
|
523
|
+
if (value && (value instanceof Function) && value.isOutlet == true) {
|
|
524
|
+
if (!this._originalOutlets) this._originalOutlets = {} ;
|
|
525
|
+
this._originalOutlets[key] = value ;
|
|
526
|
+
|
|
527
|
+
value = value.call(this) ;
|
|
528
|
+
this.set(key, value) ;
|
|
529
|
+
} else if (typeof(value) == "string") {
|
|
530
|
+
if (!this._originalOutlets) this._originalOutlets = {} ;
|
|
531
|
+
this._originalOutlets[key] = value ;
|
|
532
|
+
|
|
533
|
+
value = (this.$$sel) ? this.$$sel(value) : $$sel(value) ;
|
|
534
|
+
if (value) value = (value.length > 0) ? ((value.length == 1) ? value[0] : value) : null ;
|
|
535
|
+
this.set(key, value) ;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
return value ;
|
|
539
|
+
},
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
DEPRECATED: Will be removed in SproutCore 1.0.
|
|
543
|
+
*/
|
|
544
|
+
invokeLater: function(period) {
|
|
545
|
+
var args = $A(arguments) ; args.unshift(this) ;
|
|
546
|
+
return SC.runLoop.schedule.apply(SC.runLoop,args) ;
|
|
547
|
+
},
|
|
548
|
+
|
|
549
|
+
_cprops: ['_cprops','outlets','_bindings','_observers','_properties']
|
|
550
|
+
|
|
551
|
+
} ;
|
|
552
|
+
|
|
553
|
+
Object.extend(SC.Object.prototype, SC.Observable) ;
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
function logChange(target,key,value) {
|
|
557
|
+
console.log("CHANGE: " + target + "["+key+"]=" + value) ;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// ........................................................................
|
|
561
|
+
// FUNCTION ENHANCEMENTS
|
|
562
|
+
//
|
|
563
|
+
// Enhance function.
|
|
564
|
+
Object.extend(Function.prototype,{
|
|
565
|
+
|
|
566
|
+
// Declare a function as a property. This makes it something that can be
|
|
567
|
+
// accessed via get/set.
|
|
568
|
+
property: function() {
|
|
569
|
+
this.dependentKeys = $A(arguments) ;
|
|
570
|
+
this.isProperty = true; return this;
|
|
571
|
+
},
|
|
572
|
+
|
|
573
|
+
// Declare that a function should observe an object at the named path. Note
|
|
574
|
+
// that the path is used only to construct the observation one time.
|
|
575
|
+
observes: function(propertyPaths) {
|
|
576
|
+
this.propertyPaths = $A(arguments);
|
|
577
|
+
return this;
|
|
578
|
+
},
|
|
579
|
+
|
|
580
|
+
typeConverter: function() {
|
|
581
|
+
this.isTypeConverter = true; return this ;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
}) ;
|
|
585
|
+
|
|
586
|
+
// ........................................................................
|
|
587
|
+
// OBSERVER QUEUE
|
|
588
|
+
//
|
|
589
|
+
// This queue is used to hold observers when the object you tried to observe
|
|
590
|
+
// does not exist yet. This queue is flushed just before any property
|
|
591
|
+
// notification is sent.
|
|
592
|
+
SC.Observers = {
|
|
593
|
+
queue: {},
|
|
594
|
+
|
|
595
|
+
addObserver: function(propertyPath, func) {
|
|
596
|
+
// try to get the tuple for this.
|
|
597
|
+
if (typeof(propertyPath) == "string") {
|
|
598
|
+
var tuple = SC.Object.tupleForPropertyPath(propertyPath) ;
|
|
599
|
+
} else {
|
|
600
|
+
var tuple = propertyPath;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
if (tuple) {
|
|
604
|
+
tuple[0].addObserver(tuple[1],func) ;
|
|
605
|
+
} else {
|
|
606
|
+
var ary = this.queue[propertyPath] || [] ;
|
|
607
|
+
ary.push(func) ;
|
|
608
|
+
this.queue[propertyPath] = ary ;
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
|
|
612
|
+
removeObserver: function(propertyPath, func) {
|
|
613
|
+
var tuple = SC.Object.tupleForPropertyPath(propertyPath) ;
|
|
614
|
+
if (tuple) {
|
|
615
|
+
tuple[0].removeObserver(tuple[1],func) ;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
var ary = this.queue[propertyPath] ;
|
|
619
|
+
if (ary) {
|
|
620
|
+
ary = ary.without(func) ;
|
|
621
|
+
this.queue[propertyPath] = ary ;
|
|
622
|
+
}
|
|
623
|
+
},
|
|
624
|
+
|
|
625
|
+
flush: function() {
|
|
626
|
+
var newQueue = {} ;
|
|
627
|
+
for(var path in this.queue) {
|
|
628
|
+
var funcs = this.queue[path] ;
|
|
629
|
+
var tuple = SC.Object.tupleForPropertyPath(path) ;
|
|
630
|
+
if (tuple) {
|
|
631
|
+
var loc = funcs.length ;
|
|
632
|
+
while(--loc >= 0) {
|
|
633
|
+
var func = funcs[loc] ;
|
|
634
|
+
tuple[0].addObserver(tuple[1],func) ;
|
|
635
|
+
}
|
|
636
|
+
} else newQueue[path] = funcs ;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// set queue to remaining items
|
|
640
|
+
this.queue = newQueue ;
|
|
641
|
+
}
|
|
642
|
+
} ;
|
|
643
|
+
|
|
644
|
+
// ........................................................................
|
|
645
|
+
// NOTIFCATION QUEUE
|
|
646
|
+
//
|
|
647
|
+
// Property notifications are placed into this queue first and then processed
|
|
648
|
+
// to keep the stack size down.
|
|
649
|
+
SC.NotificationQueue = {
|
|
650
|
+
queue: [],
|
|
651
|
+
maxFlush: 5000, // max time you can spend flushing before we reschedule.
|
|
652
|
+
_flushing: false,
|
|
653
|
+
add: function(target, func, args) { this.queue.push([target, func, args]);},
|
|
654
|
+
flush: function() {
|
|
655
|
+
if (this._flushing) return ;
|
|
656
|
+
this._flushing = true ;
|
|
657
|
+
|
|
658
|
+
var start = new Date().getTime() ;
|
|
659
|
+
var now = start ;
|
|
660
|
+
var n = null ;
|
|
661
|
+
while(((now - start) < this.maxFlush) && (n = this.queue.pop())) {
|
|
662
|
+
try {
|
|
663
|
+
var t = n[0] || n[1] ;
|
|
664
|
+
n[1].apply(t,n[2]) ;
|
|
665
|
+
}
|
|
666
|
+
catch(e) {
|
|
667
|
+
console.log("Exception while notify("+n[1]+"): " + e) ;
|
|
668
|
+
} // catch
|
|
669
|
+
now = Date.now() ;
|
|
670
|
+
}
|
|
671
|
+
this._flushing = false ;
|
|
672
|
+
|
|
673
|
+
if (this.queue.length > 0) {
|
|
674
|
+
setTimeout(this._reflush,1);
|
|
675
|
+
}
|
|
676
|
+
},
|
|
677
|
+
|
|
678
|
+
_reflush: function() { SC.NotificationQueue.flush(); }
|
|
679
|
+
} ;
|
|
680
|
+
|
|
681
|
+
// ........................................................................
|
|
682
|
+
// CHAIN OBSERVER
|
|
683
|
+
//
|
|
684
|
+
|
|
685
|
+
// ChainObservers are used to automatically monitor a property several
|
|
686
|
+
// layers deep.
|
|
687
|
+
// org.plan.name = SC._ChainObserver.create({
|
|
688
|
+
// target: this, property: 'org',
|
|
689
|
+
// next: SC._ChainObserver.create({
|
|
690
|
+
// property: 'plan',
|
|
691
|
+
// next: SC._ChainObserver.create({
|
|
692
|
+
// property: 'name', func: myFunc
|
|
693
|
+
// })
|
|
694
|
+
// })
|
|
695
|
+
// })
|
|
696
|
+
//
|
|
697
|
+
SC._ChainObserver = SC.Object.extend({
|
|
698
|
+
|
|
699
|
+
isChainObserver: true,
|
|
700
|
+
|
|
701
|
+
// the object this link in the chain is observing.
|
|
702
|
+
target: null,
|
|
703
|
+
|
|
704
|
+
// the property on the object this link is observing
|
|
705
|
+
property: null,
|
|
706
|
+
|
|
707
|
+
// if not null, the next link in the chain. The target property
|
|
708
|
+
// of this next link will be set to the value of this link.
|
|
709
|
+
next: null,
|
|
710
|
+
|
|
711
|
+
// if not null, the function to be called when the property value changes.
|
|
712
|
+
func: null,
|
|
713
|
+
|
|
714
|
+
propertyObserver: function(observing,target,key,value) {
|
|
715
|
+
if ((key == 'target') && (value != this._target)) {
|
|
716
|
+
var func = this.boundObserver() ;
|
|
717
|
+
if (this._target && this._target.removeObserver) {
|
|
718
|
+
this._target.removeObserver(this.property,func);
|
|
719
|
+
}
|
|
720
|
+
this._target = value ;
|
|
721
|
+
if (this._target && this._target.addObserver) {
|
|
722
|
+
this._target.addObserver(this.property,func) ;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// the target property has probably changed since the target has
|
|
726
|
+
// changed. notify myself of this.
|
|
727
|
+
if (!(observing == 'init')) this.targetPropertyObserver() ;
|
|
728
|
+
}
|
|
729
|
+
},
|
|
730
|
+
|
|
731
|
+
boundObserver: function() {
|
|
732
|
+
if (!this._boundObserver) {
|
|
733
|
+
this._boundObserver = this.targetPropertyObserver.bind(this) ;
|
|
734
|
+
}
|
|
735
|
+
return this._boundObserver ;
|
|
736
|
+
},
|
|
737
|
+
|
|
738
|
+
// invoked when the target property changes. act based
|
|
739
|
+
targetPropertyObserver: function() {
|
|
740
|
+
|
|
741
|
+
// get the new target property value. if the target or property is
|
|
742
|
+
// not valid, just use null.
|
|
743
|
+
var value = (this.target && this.target.get && this.property) ? this.target.get(this.property) : null ;
|
|
744
|
+
|
|
745
|
+
if (value !== this._lastTargetProperty) {
|
|
746
|
+
this._lastTargetProperty = value ;
|
|
747
|
+
// if we have another item in the chain, just forward the change.
|
|
748
|
+
if (this.next) {
|
|
749
|
+
this.next.set('target',value) ;
|
|
750
|
+
|
|
751
|
+
// otherwise, invoke the function.
|
|
752
|
+
} else if (this.func) this.func(this.target,this.property,value) ;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
},
|
|
756
|
+
|
|
757
|
+
// hookup observer.
|
|
758
|
+
init: function() {
|
|
759
|
+
arguments.callee.base.call(this) ;
|
|
760
|
+
this.propertyObserver('init',this,'target',this.get('target')) ;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
}) ;
|
|
764
|
+
|
|
765
|
+
// Class Methods.
|
|
766
|
+
SC._ChainObserver.mixin({
|
|
767
|
+
createChain: function(target,keys,func) {
|
|
768
|
+
var property = keys.shift() ;
|
|
769
|
+
var nextTarget = (target && property && target.get) ? target.get(property) : null ;
|
|
770
|
+
var next = (keys && keys.length>0) ? this.createChain(nextTarget,keys,func) : null ;
|
|
771
|
+
|
|
772
|
+
return this.create({
|
|
773
|
+
target: target, property: property,
|
|
774
|
+
next: next, func: ((next) ? null : func)
|
|
775
|
+
}) ;
|
|
776
|
+
}
|
|
777
|
+
}) ;
|