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.
Files changed (270) hide show
  1. data/History.txt +4 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +269 -0
  4. data/README.txt +67 -0
  5. data/Rakefile +4 -0
  6. data/app_generators/sproutcore/USAGE +5 -0
  7. data/app_generators/sproutcore/sproutcore_generator.rb +66 -0
  8. data/app_generators/sproutcore/templates/README +77 -0
  9. data/app_generators/sproutcore/templates/environment.yml +4 -0
  10. data/bin/sc-build +145 -0
  11. data/bin/sc-gen +24 -0
  12. data/bin/sc-server +63 -0
  13. data/bin/sproutcore +21 -0
  14. data/clients/sc_docs/controllers/docs.js +118 -0
  15. data/clients/sc_docs/core.js +19 -0
  16. data/clients/sc_docs/english.lproj/body.css +159 -0
  17. data/clients/sc_docs/english.lproj/body.rhtml +33 -0
  18. data/clients/sc_docs/english.lproj/controls.css +0 -0
  19. data/clients/sc_docs/english.lproj/icons/small/next.png +0 -0
  20. data/clients/sc_docs/english.lproj/icons/small/reset.png +0 -0
  21. data/clients/sc_docs/english.lproj/images/gradients.png +0 -0
  22. data/clients/sc_docs/english.lproj/images/indicator.gif +0 -0
  23. data/clients/sc_docs/english.lproj/images/toolbar.png +0 -0
  24. data/clients/sc_docs/english.lproj/no_docs.rhtml +7 -0
  25. data/clients/sc_docs/english.lproj/strings.js +14 -0
  26. data/clients/sc_docs/english.lproj/warning.rhtml +6 -0
  27. data/clients/sc_docs/fixtures/doc.js +11 -0
  28. data/clients/sc_docs/main.js +21 -0
  29. data/clients/sc_docs/models/doc.js +9 -0
  30. data/clients/sc_docs/tests/controllers/docs.rhtml +21 -0
  31. data/clients/sc_docs/tests/models/doc.rhtml +21 -0
  32. data/clients/sc_docs/tests/views/doc_frame.rhtml +21 -0
  33. data/clients/sc_docs/tests/views/doc_label_view.rhtml +21 -0
  34. data/clients/sc_docs/views/doc_frame.js +33 -0
  35. data/clients/sc_docs/views/doc_label.js +20 -0
  36. data/clients/sc_test_runner/controllers/runner.js +175 -0
  37. data/clients/sc_test_runner/core.js +19 -0
  38. data/clients/sc_test_runner/english.lproj/body.css +151 -0
  39. data/clients/sc_test_runner/english.lproj/body.rhtml +35 -0
  40. data/clients/sc_test_runner/english.lproj/controls.css +0 -0
  41. data/clients/sc_test_runner/english.lproj/icons/small/next.png +0 -0
  42. data/clients/sc_test_runner/english.lproj/icons/small/reset.png +0 -0
  43. data/clients/sc_test_runner/english.lproj/images/gradients.png +0 -0
  44. data/clients/sc_test_runner/english.lproj/images/indicator.gif +0 -0
  45. data/clients/sc_test_runner/english.lproj/images/toolbar.png +0 -0
  46. data/clients/sc_test_runner/english.lproj/no_tests.rhtml +6 -0
  47. data/clients/sc_test_runner/english.lproj/strings.js +14 -0
  48. data/clients/sc_test_runner/english.lproj/warning.rhtml +6 -0
  49. data/clients/sc_test_runner/fixtures/test.js +12 -0
  50. data/clients/sc_test_runner/main.js +26 -0
  51. data/clients/sc_test_runner/models/test.js +11 -0
  52. data/clients/sc_test_runner/views/runner_frame.js +72 -0
  53. data/clients/sc_test_runner/views/test_label.js +20 -0
  54. data/config/hoe.rb +70 -0
  55. data/config/requirements.rb +17 -0
  56. data/environment.yml +9 -0
  57. data/frameworks/prototype/prototype.js +4186 -0
  58. data/frameworks/sproutcore/Core.js +378 -0
  59. data/frameworks/sproutcore/README +3 -0
  60. data/frameworks/sproutcore/controllers/array.js +236 -0
  61. data/frameworks/sproutcore/controllers/collection.js +305 -0
  62. data/frameworks/sproutcore/controllers/controller.js +323 -0
  63. data/frameworks/sproutcore/controllers/object.js +372 -0
  64. data/frameworks/sproutcore/drag/drag.js +549 -0
  65. data/frameworks/sproutcore/drag/drag_data_source.js +32 -0
  66. data/frameworks/sproutcore/drag/drag_source.js +64 -0
  67. data/frameworks/sproutcore/drag/drop_target.js +153 -0
  68. data/frameworks/sproutcore/english.lproj/blank.gif +0 -0
  69. data/frameworks/sproutcore/english.lproj/buttons.css +589 -0
  70. data/frameworks/sproutcore/english.lproj/buttons.png +0 -0
  71. data/frameworks/sproutcore/english.lproj/inline_text_editor.css +21 -0
  72. data/frameworks/sproutcore/english.lproj/menu.css +121 -0
  73. data/frameworks/sproutcore/english.lproj/panels/background-fat.jpg +0 -0
  74. data/frameworks/sproutcore/english.lproj/panels/background-thin.jpg +0 -0
  75. data/frameworks/sproutcore/english.lproj/panels/bottom-edge.png +0 -0
  76. data/frameworks/sproutcore/english.lproj/panels/bottom-left-corner.png +0 -0
  77. data/frameworks/sproutcore/english.lproj/panels/bottom-right-corner.png +0 -0
  78. data/frameworks/sproutcore/english.lproj/panels/left-edge.png +0 -0
  79. data/frameworks/sproutcore/english.lproj/panels/overlay.png +0 -0
  80. data/frameworks/sproutcore/english.lproj/panels/right-edge.png +0 -0
  81. data/frameworks/sproutcore/english.lproj/panels/top-edge.png +0 -0
  82. data/frameworks/sproutcore/english.lproj/panels/top-left-corner.png +0 -0
  83. data/frameworks/sproutcore/english.lproj/panels/top-right-corner.png +0 -0
  84. data/frameworks/sproutcore/english.lproj/panes.css +155 -0
  85. data/frameworks/sproutcore/english.lproj/picker.css +22 -0
  86. data/frameworks/sproutcore/english.lproj/strings.js +15 -0
  87. data/frameworks/sproutcore/english.lproj/tab.css +23 -0
  88. data/frameworks/sproutcore/english.lproj/tests.css +67 -0
  89. data/frameworks/sproutcore/english.lproj/theme.css +77 -0
  90. data/frameworks/sproutcore/foundation/animator.js +670 -0
  91. data/frameworks/sproutcore/foundation/application.js +199 -0
  92. data/frameworks/sproutcore/foundation/array.js +348 -0
  93. data/frameworks/sproutcore/foundation/benchmark.js +211 -0
  94. data/frameworks/sproutcore/foundation/binding.js +384 -0
  95. data/frameworks/sproutcore/foundation/date.js +357 -0
  96. data/frameworks/sproutcore/foundation/error.js +39 -0
  97. data/frameworks/sproutcore/foundation/input_manager.js +153 -0
  98. data/frameworks/sproutcore/foundation/json.js +296 -0
  99. data/frameworks/sproutcore/foundation/mock.js +42 -0
  100. data/frameworks/sproutcore/foundation/node_descriptor.js +56 -0
  101. data/frameworks/sproutcore/foundation/object.js +777 -0
  102. data/frameworks/sproutcore/foundation/observable.js +451 -0
  103. data/frameworks/sproutcore/foundation/page.js +63 -0
  104. data/frameworks/sproutcore/foundation/path_module.js +413 -0
  105. data/frameworks/sproutcore/foundation/responder.js +310 -0
  106. data/frameworks/sproutcore/foundation/routes.js +371 -0
  107. data/frameworks/sproutcore/foundation/run_loop.js +21 -0
  108. data/frameworks/sproutcore/foundation/server.js +491 -0
  109. data/frameworks/sproutcore/foundation/set.js +96 -0
  110. data/frameworks/sproutcore/foundation/string.js +149 -0
  111. data/frameworks/sproutcore/foundation/undo_manager.js +186 -0
  112. data/frameworks/sproutcore/foundation/unittest.js +622 -0
  113. data/frameworks/sproutcore/foundation/utils.js +61 -0
  114. data/frameworks/sproutcore/globals/panels.js +182 -0
  115. data/frameworks/sproutcore/globals/popups.js +60 -0
  116. data/frameworks/sproutcore/globals/window.js +381 -0
  117. data/frameworks/sproutcore/lib/index.rhtml +66 -0
  118. data/frameworks/sproutcore/models/collection.js +395 -0
  119. data/frameworks/sproutcore/models/record.js +622 -0
  120. data/frameworks/sproutcore/models/store.js +295 -0
  121. data/frameworks/sproutcore/panes/dialog.js +16 -0
  122. data/frameworks/sproutcore/panes/manager.js +164 -0
  123. data/frameworks/sproutcore/panes/menu.js +45 -0
  124. data/frameworks/sproutcore/panes/overlay.js +231 -0
  125. data/frameworks/sproutcore/panes/pane.js +90 -0
  126. data/frameworks/sproutcore/panes/panel.js +19 -0
  127. data/frameworks/sproutcore/panes/picker.js +45 -0
  128. data/frameworks/sproutcore/tests/controllers/array.rhtml +86 -0
  129. data/frameworks/sproutcore/tests/controllers/controller.rhtml +273 -0
  130. data/frameworks/sproutcore/tests/controllers/object.rhtml +327 -0
  131. data/frameworks/sproutcore/tests/foundation/application.rhtml +125 -0
  132. data/frameworks/sproutcore/tests/foundation/array.rhtml +221 -0
  133. data/frameworks/sproutcore/tests/foundation/object.rhtml +69 -0
  134. data/frameworks/sproutcore/tests/globals/window.rhtml +45 -0
  135. data/frameworks/sproutcore/tests/panes/pane.rhtml +88 -0
  136. data/frameworks/sproutcore/tests/views/collection.rhtml +137 -0
  137. data/frameworks/sproutcore/tests/views/popup_button.rhtml +115 -0
  138. data/frameworks/sproutcore/tests/views/text_field.rhtml +37 -0
  139. data/frameworks/sproutcore/validators/credit_card.js +92 -0
  140. data/frameworks/sproutcore/validators/date.js +36 -0
  141. data/frameworks/sproutcore/validators/email.js +29 -0
  142. data/frameworks/sproutcore/validators/not_empty.js +24 -0
  143. data/frameworks/sproutcore/validators/number.js +55 -0
  144. data/frameworks/sproutcore/validators/password.js +78 -0
  145. data/frameworks/sproutcore/validators/validator.js +304 -0
  146. data/frameworks/sproutcore/views/button.js +425 -0
  147. data/frameworks/sproutcore/views/checkbox_field.js +30 -0
  148. data/frameworks/sproutcore/views/collection.js +1521 -0
  149. data/frameworks/sproutcore/views/container.js +62 -0
  150. data/frameworks/sproutcore/views/error_explanation.js +45 -0
  151. data/frameworks/sproutcore/views/field.js +214 -0
  152. data/frameworks/sproutcore/views/filter_button.js +29 -0
  153. data/frameworks/sproutcore/views/form.js +591 -0
  154. data/frameworks/sproutcore/views/image.js +141 -0
  155. data/frameworks/sproutcore/views/inline_text_editor.js +96 -0
  156. data/frameworks/sproutcore/views/label.js +176 -0
  157. data/frameworks/sproutcore/views/menu_item.js +90 -0
  158. data/frameworks/sproutcore/views/pagination.js +54 -0
  159. data/frameworks/sproutcore/views/popup_button.js +86 -0
  160. data/frameworks/sproutcore/views/popup_menu.js +137 -0
  161. data/frameworks/sproutcore/views/progress.js +100 -0
  162. data/frameworks/sproutcore/views/radio_field.js +107 -0
  163. data/frameworks/sproutcore/views/radio_group.js +48 -0
  164. data/frameworks/sproutcore/views/segmented.js +80 -0
  165. data/frameworks/sproutcore/views/select_field.js +272 -0
  166. data/frameworks/sproutcore/views/spinner.js +11 -0
  167. data/frameworks/sproutcore/views/tab.js +126 -0
  168. data/frameworks/sproutcore/views/text_field.js +179 -0
  169. data/frameworks/sproutcore/views/textarea_field.js +14 -0
  170. data/frameworks/sproutcore/views/toolbar.js +29 -0
  171. data/frameworks/sproutcore/views/view.js +1389 -0
  172. data/frameworks/sproutcore/views/workspace.js +170 -0
  173. data/generators/client/README +3 -0
  174. data/generators/client/USAGE +12 -0
  175. data/generators/client/client_generator.rb +53 -0
  176. data/generators/client/templates/core.js +19 -0
  177. data/generators/client/templates/english.lproj/body.css +0 -0
  178. data/generators/client/templates/english.lproj/body.rhtml +3 -0
  179. data/generators/client/templates/english.lproj/controls.css +0 -0
  180. data/generators/client/templates/english.lproj/strings.js +14 -0
  181. data/generators/client/templates/main.js +37 -0
  182. data/generators/controller/USAGE +16 -0
  183. data/generators/controller/controller_generator.rb +51 -0
  184. data/generators/controller/templates/controller.js +21 -0
  185. data/generators/controller/templates/test.rhtml +21 -0
  186. data/generators/framework/README +7 -0
  187. data/generators/framework/USAGE +12 -0
  188. data/generators/framework/framework_generator.rb +53 -0
  189. data/generators/framework/templates/core.js +20 -0
  190. data/generators/framework/templates/english.lproj/body.css +0 -0
  191. data/generators/framework/templates/english.lproj/body.rhtml +3 -0
  192. data/generators/framework/templates/english.lproj/controls.css +0 -0
  193. data/generators/framework/templates/english.lproj/strings.js +14 -0
  194. data/generators/language/USAGE +16 -0
  195. data/generators/language/language_generator.rb +47 -0
  196. data/generators/language/templates/strings.js +10 -0
  197. data/generators/model/USAGE +24 -0
  198. data/generators/model/model_generator.rb +55 -0
  199. data/generators/model/templates/fixture.js +11 -0
  200. data/generators/model/templates/model.js +20 -0
  201. data/generators/model/templates/test.rhtml +21 -0
  202. data/generators/test/USAGE +16 -0
  203. data/generators/test/templates/test.rhtml +21 -0
  204. data/generators/test/test_generator.rb +47 -0
  205. data/generators/view/USAGE +16 -0
  206. data/generators/view/templates/test.rhtml +21 -0
  207. data/generators/view/templates/view.js +20 -0
  208. data/generators/view/view_generator.rb +51 -0
  209. data/jsdoc/README.txt +119 -0
  210. data/jsdoc/app/DocFile.js +137 -0
  211. data/jsdoc/app/DocTag.js +110 -0
  212. data/jsdoc/app/Doclet.js +63 -0
  213. data/jsdoc/app/Dumper.js +143 -0
  214. data/jsdoc/app/JsDoc.js +103 -0
  215. data/jsdoc/app/JsHilite.js +45 -0
  216. data/jsdoc/app/JsIO.js +163 -0
  217. data/jsdoc/app/JsParse.js +385 -0
  218. data/jsdoc/app/JsPlate.js +130 -0
  219. data/jsdoc/app/JsTestrun.js +129 -0
  220. data/jsdoc/app/JsToke.js +564 -0
  221. data/jsdoc/app/Symbol.js +298 -0
  222. data/jsdoc/app/Transformer.js +14 -0
  223. data/jsdoc/app/Util.js +97 -0
  224. data/jsdoc/app/js.jar +0 -0
  225. data/jsdoc/app/run.js +144 -0
  226. data/jsdoc/plugins/min.js +316 -0
  227. data/jsdoc/plugins/strip.js +20 -0
  228. data/jsdoc/templates/sproutcore/class.tmpl +438 -0
  229. data/jsdoc/templates/sproutcore/default.css +241 -0
  230. data/jsdoc/templates/sproutcore/index.html +13 -0
  231. data/jsdoc/templates/sproutcore/index.tmpl +21 -0
  232. data/jsdoc/templates/sproutcore/prototype.js +4186 -0
  233. data/jsdoc/templates/sproutcore/publish.js +236 -0
  234. data/jsdoc/templates/sproutcore/splash.html +7 -0
  235. data/lib/sproutcore/build_tools/html_builder.rb +88 -0
  236. data/lib/sproutcore/build_tools/resource_builder.rb +194 -0
  237. data/lib/sproutcore/build_tools.rb +44 -0
  238. data/lib/sproutcore/bundle.rb +517 -0
  239. data/lib/sproutcore/bundle_manifest.rb +397 -0
  240. data/lib/sproutcore/generator_helper.rb +170 -0
  241. data/lib/sproutcore/helpers/capture_helper.rb +42 -0
  242. data/lib/sproutcore/helpers/static_helper.rb +80 -0
  243. data/lib/sproutcore/helpers/tag_helper.rb +110 -0
  244. data/lib/sproutcore/helpers/text_helper.rb +336 -0
  245. data/lib/sproutcore/helpers.rb +3 -0
  246. data/lib/sproutcore/jsdoc.rb +40 -0
  247. data/lib/sproutcore/jsmin.rb +247 -0
  248. data/lib/sproutcore/library.rb +258 -0
  249. data/lib/sproutcore/merb/bundle_controller.rb +179 -0
  250. data/lib/sproutcore/merb/router.rb +43 -0
  251. data/lib/sproutcore/merb.rb +27 -0
  252. data/lib/sproutcore/version.rb +9 -0
  253. data/lib/sproutcore/view_helpers/button_views.rb +302 -0
  254. data/lib/sproutcore/view_helpers/core_views.rb +284 -0
  255. data/lib/sproutcore/view_helpers/form_views.rb +258 -0
  256. data/lib/sproutcore/view_helpers/menu_views.rb +94 -0
  257. data/lib/sproutcore/view_helpers.rb +628 -0
  258. data/lib/sproutcore.rb +30 -0
  259. data/script/destroy +14 -0
  260. data/script/generate +14 -0
  261. data/script/txt2html +74 -0
  262. data/setup.rb +1585 -0
  263. data/spec/spec.opts +1 -0
  264. data/spec/spec_helper.rb +7 -0
  265. data/spec/sproutcore_spec.rb +11 -0
  266. data/tasks/deployment.rake +34 -0
  267. data/tasks/environment.rake +7 -0
  268. data/tasks/rspec.rake +21 -0
  269. data/tasks/website.rake +17 -0
  270. metadata +365 -0
@@ -0,0 +1,372 @@
1
+ // ========================================================================
2
+ // SproutCore
3
+ // copyright 2006-2007 Sprout Systems, Inc.
4
+ // ========================================================================
5
+
6
+ require('controllers/controller') ;
7
+
8
+ /** @class
9
+ An ObjectController gives you a simple way to manage one or more objects
10
+ as a single object.
11
+
12
+ @extends SC.Controller
13
+ */
14
+ SC.ObjectController = SC.Controller.extend(
15
+ /** @scope SC.ObjectController.prototype */
16
+ {
17
+
18
+ // ...............................
19
+ // PROPERTIES
20
+ //
21
+
22
+ /**
23
+ set this to some value and the object controller will project
24
+ its properties.
25
+ */
26
+ content: null,
27
+
28
+ /**
29
+ This will be set to true if the object currently does not have any
30
+ content. You might use this to disable any controls attached to the
31
+ controller.
32
+
33
+ @type Boolean
34
+ */
35
+ hasNoContent: true,
36
+
37
+ /**
38
+ This will be set to true if the content is a single object or an array
39
+ with a single item. You can use this to disabled your UI.
40
+
41
+ @type Boolean
42
+ */
43
+ hasSingleContent: false,
44
+
45
+ /**
46
+ This will be set to true if the content is an array with multiple objects
47
+ in it.
48
+
49
+ @type Boolean
50
+ */
51
+ hasMultipleContent: false,
52
+
53
+ /**
54
+ Set this property to true and multiple content will be treated like a null
55
+ value. This will only impact use of get() and set().
56
+
57
+ @type Boolean
58
+ */
59
+ allowsMultipleContent: true,
60
+
61
+ // ...............................
62
+ // INTERNAL SUPPORT
63
+ //
64
+
65
+ /**
66
+ When this controller commits changes, it will copy its changed values
67
+ to the content object and then call "commitChanges" on the content
68
+ object if that object implements the method.
69
+ */
70
+ performCommitChanges: function() {
71
+
72
+ var content = this.get('content') ;
73
+ var ret = true ;
74
+
75
+ // empty arrays are treated like null values, arrays.len=1 treated like
76
+ // single objects.
77
+ var isArray = false ;
78
+ if (this._isArray(content)) {
79
+ var len = this._lengthFor(content) ;
80
+ if (len == 0) {
81
+ content = null ;
82
+ } else if (len == 1) {
83
+ content = this._objectAt(0, content) ;
84
+ } else if (this.get('allowsMultipleContent')) {
85
+ isArray = true ;
86
+ } else content = null ;
87
+ }
88
+
89
+ if (!this._changes) this._changes = {} ;
90
+
91
+ // cannot commit changes to empty content. Return an error.
92
+ if (!content) {
93
+ return $error("No Content") ;
94
+
95
+ // if content is an array, then loop through each item in the array and
96
+ // get the changed values.
97
+ } else if (isArray) {
98
+
99
+ var loc = this._lengthFor(content) ;
100
+ while(--loc >= 0) {
101
+ var object = this._objectAt(loc, content) ;
102
+ if (!object) continue ;
103
+
104
+ if (object.beginPropertyChanges) object.beginPropertyChanges();
105
+
106
+ // loop through all the keys in changes and get the values...
107
+ for(var key in this._changes) {
108
+ if (!this._changes.hasOwnProperty(key)) continue ;
109
+ var value = this._changes[key];
110
+
111
+ // if the value is an array, get the idx matching the content
112
+ // object. Otherwise, just use the value of the item.
113
+ if(this._isArray(value)) {
114
+ value = this._objectAt(loc, value) ;
115
+ }
116
+
117
+ if (object.set) {
118
+ object.set(key,value) ;
119
+ } else object[key] = value ;
120
+ }
121
+
122
+ if (object.endPropertyChanges) object.endPropertyChanges() ;
123
+ if (object.commitChanges) ret = object.commitChanges() ;
124
+ }
125
+
126
+ // if the content is not an array, then just loop through each changed
127
+ // value and copy it to the object.
128
+ } else {
129
+
130
+ if (content.beginPropertyChanges) content.beginPropertyChanges() ;
131
+ for(var key in this._changes) {
132
+ if (!this._changes.hasOwnProperty(key)) continue;
133
+
134
+ var oldValue = content.get ? content.get(key) : content[key];
135
+ var newValue = this._changes[key];
136
+
137
+ if (newValue != oldValue)
138
+ {
139
+ (content.set) ? content.set('isDirty', true) : (content['isDirty'] = true);
140
+ }
141
+
142
+ if (content.set) {
143
+ content.set(key, newValue);
144
+ } else {
145
+ content[key] = newValue;
146
+ }
147
+ }
148
+
149
+ if (content.endPropertyChanges) content.endPropertyChanges() ;
150
+ if (content.commitChanges) ret = content.commitChanges() ;
151
+ }
152
+
153
+ // if commit was successful, dump changes hash and clear editor.
154
+ if ($ok(ret)) {
155
+ this._changes = {} ;
156
+ //this._valueControllers = {};
157
+ this.editorDidClearChanges() ;
158
+ }
159
+
160
+ return ret ;
161
+ },
162
+
163
+ /** @private */
164
+ performDiscardChanges: function() {
165
+ this._changes = {};
166
+ this._valueControllers = {};
167
+ this.editorDidClearChanges();
168
+ this.allPropertiesDidChange();
169
+ return true ;
170
+ },
171
+
172
+ /** @private */
173
+ unknownProperty: function(key,value)
174
+ {
175
+ if (key == "content")
176
+ {
177
+ // FOR CONTENT KEY:
178
+ // avoid circular references. If you try to set content, just save the
179
+ // value. The propertyObserver will be triggered below to do the rest of
180
+ // the setup as needed.
181
+ if (!(value === undefined)) this[key] = value;
182
+ return this[key];
183
+ }
184
+ else
185
+ {
186
+ // FOR ALL OTHER KEYS:
187
+ // Save the value in our temporary hash and note the changes in the
188
+ // editor.
189
+
190
+ if (!this._changes) this._changes = {} ;
191
+ if (!this._valueControllers) this._valueControllers = {};
192
+
193
+ if (value !== undefined)
194
+ {
195
+ // for changes, save in _changes hash and note that a change is required.
196
+ this._changes[key] = value;
197
+ if (this._valueControllers[key])
198
+ {
199
+ this._valueControllers[key] = null;
200
+ }
201
+ // notifying observers regarless if a controller had been created since they're lazy loaded
202
+ this.propertyWillChange(key + "Controller");
203
+ this.propertyDidChange(key + "Controller");
204
+ this.editorDidChange();
205
+ }
206
+ else
207
+ {
208
+ // are we requesting the controller for a value?
209
+ if (key.slice(key.length-10,key.length) == "Controller")
210
+ {
211
+ // the actual value...
212
+ key = key.slice(0,-10);
213
+ if ( !this._valueControllers[key] )
214
+ {
215
+ this._valueControllers[key] = this.controllerForValue(this._getValueForPropertyKey(key));
216
+ }
217
+ value = this._valueControllers[key];
218
+ }
219
+ else
220
+ {
221
+ // otherwise, get the value.
222
+ // first check the _changes hash, then check the content object.
223
+ value = this._getValueForPropertyKey(key);
224
+ }
225
+ }
226
+ return value;
227
+ }
228
+ },
229
+
230
+ _getValueForPropertyKey: function( key )
231
+ {
232
+ // first check the changes hash for a uncommited value...
233
+ var value = this._changes[key];
234
+ // sweet, no need to proceed.
235
+ if ( value !== undefined ) return value;
236
+
237
+ // ok, we'll need to get the value from the content object
238
+ var obj = this.get('content');
239
+ // no content object... return null.
240
+ if (!obj) return null;
241
+
242
+ if (this._isArray(obj))
243
+ {
244
+ var value = [];
245
+ var len = this._lengthFor(obj);
246
+ if (len > 1)
247
+ {
248
+ // if content is an array with more than one item, collect
249
+ // content from array.
250
+ if (this.get('allowsMultipleContent')) {
251
+ for(var idx=0; idx < len; idx++) {
252
+ var item = this._objectAt(idx, obj) ;
253
+ value.push((item) ? ((item.get) ? item.get(key) : item[key]) : null) ;
254
+ }
255
+ } else {
256
+ value = null;
257
+ }
258
+ }
259
+ else if (len == 1)
260
+ {
261
+ // if content is array with one item, collect from first obj.
262
+ obj = this._objectAt(0,obj) ;
263
+ value = (obj.get) ? obj.get(key) : obj[key] ;
264
+ }
265
+ else
266
+ {
267
+ // if content is empty array, act as if null.
268
+ value = null;
269
+ }
270
+ }
271
+ else
272
+ {
273
+ // content is a single item. Just get the property.
274
+ value = (obj.get) ? obj.get(key) : obj[key] ;
275
+ }
276
+ return value;
277
+ },
278
+
279
+ _lastPropertyRevision: 0,
280
+
281
+ /** @private */
282
+ propertyObserver: function(observer,target,key,value,propertyRevision) {
283
+
284
+ // only handle property once.
285
+ if (propertyRevision <= this._lastPropertyRevision) return ;
286
+ this._lastPropertyRevision = propertyRevision;
287
+
288
+ // save the bound observer.
289
+ if (!this._boundObserver) {
290
+ this._boundObserver = this._contentPropertyObserver.bind(this);
291
+ }
292
+
293
+ // handle changes to the content...
294
+ if (target != this) return ;
295
+ if ((key == 'content') && (value != this._content)) {
296
+ var f = this._boundObserver ;
297
+
298
+ if (this.get('hasChanges')) {
299
+ // if we have uncommitted changes, then discard the changes or raise
300
+ // an exception.
301
+ var er = this.discardChanges() ;
302
+ if (!$ok(er)) throw(er) ;
303
+ } else {
304
+ // no changes, but we want to ensure that we flush the cache
305
+ // of any SC.Controllers we have for the content
306
+ this._valueControllers = {} ;
307
+ }
308
+
309
+ // stop listening to old content.
310
+ if (this._content) {
311
+ var objects = Array.from(this._content) ;
312
+ var loc = objects.length ;
313
+ while(--loc >= 0) {
314
+ var obj = objects[loc] ;
315
+ if (obj && obj.removeObserver) obj.removeObserver('*', f) ;
316
+ }
317
+ }
318
+
319
+ // start listening for changes on the new content object.
320
+ this._content = value ;
321
+ if (this._content) {
322
+ var objects = Array.from(this._content) ;
323
+ var loc = objects.length ;
324
+ while(--loc >= 0) {
325
+ var obj = objects[loc] ;
326
+ if (obj && obj.addObserver) obj.addObserver('*', f) ;
327
+ }
328
+ }
329
+
330
+ // determine the content type.
331
+ var count = 0 ;
332
+ if (this._content) {
333
+ count = (this._isArray(this._content)) ? this._lengthFor(this._content) : 1 ;
334
+ } ;
335
+
336
+ this.beginPropertyChanges() ;
337
+ this.set('hasNoContent',count == 0) ;
338
+ this.set('hasSingleContent',count == 1) ;
339
+ this.set('hasMultipleContent',count > 1) ;
340
+
341
+ // notify everyone that everything is different now.
342
+ this.allPropertiesDidChange() ;
343
+ this.endPropertyChanges() ;
344
+ }
345
+ },
346
+
347
+ // invoked when properties on the content object change. Just forward
348
+ // to controller.
349
+ _contentPropertyObserver: function(target,key,value) {
350
+ this._changeFromContent = true ;
351
+ if (key == '*') {
352
+ this.allPropertiesDidChange() ;
353
+ } else {
354
+ this.propertyWillChange(key) ;
355
+ this.propertyDidChange(key,value) ;
356
+ }
357
+ this._changeFromContent = false ;
358
+ },
359
+
360
+ _lengthFor: function(obj) {
361
+ return ((obj.get) ? obj.get('length') : obj.length) || 0;
362
+ },
363
+
364
+ _objectAt: function(idx, obj) {
365
+ return (obj.objectAt) ? obj.objectAt(idx) : ((obj.get) ? obj.get(idx) : obj[idx]) ;
366
+ },
367
+
368
+ _isArray: function(obj) {
369
+ return ($type(obj) == T_ARRAY) || (obj && obj.objectAt) ;
370
+ }
371
+
372
+ }) ;