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,591 @@
1
+ // ========================================================================
2
+ // SproutCore
3
+ // copyright 2006-2007 Sprout Systems, Inc.
4
+ // ========================================================================
5
+
6
+ require('views/view') ;
7
+ require('views/button') ;
8
+ require('views/text_field') ;
9
+
10
+ // FormView provides a simple way for you to "stage" input by capturing
11
+ // data from your views into the form before it is set on your actual
12
+ // content object.
13
+ //
14
+ // HOW IT WORKS
15
+ //
16
+ // On startup, FormView will walk any child views looking for any views
17
+ // with the property "fieldKey". Any views with this key will be saved as
18
+ // fields on the form. You can access their value directly on the form by
19
+ // getting the value of fieldKey.
20
+ //
21
+ // Used this way, you can easily combine multiple views into a single,
22
+ // bindable object. To gain the full functionality however, you should use
23
+ // the content commit capability.
24
+ //
25
+ // IMPORTANT: FormView defines some properties of its own. If you try to
26
+ // name your fields with properties already declared in FormView, they will
27
+ // be ignored and a warning will be logged to the console.
28
+ //
29
+ // CONTENT COMMIT
30
+ //
31
+ // If you set the content object on a form view, then the form fields will
32
+ // automatically be bound to the same named keys on the content object. When
33
+ // the content object value's change, the fields on the form will update
34
+ // automatically. However, changing values in the fields will not update
35
+ // the content object until the form is committed.
36
+ //
37
+ // You can commit a form promgramatically by calling the commit() method on
38
+ // the form view. Alternatively, you can add a button to the form with the
39
+ // outlet name submitButton and it will be automatically wired to submit
40
+ // the form. You can also create a button named resetButton, it will be
41
+ // automatically wired to reset the form.
42
+ //
43
+ SC.FormView = SC.View.extend({
44
+
45
+ // PROPERTIES
46
+
47
+ // set this to point to an object and the value of the object will be used
48
+ // to auto-populate the form. When the form is committed, its values will
49
+ // applied to this content object.
50
+ content: null,
51
+ contentBindingDefault: SC.Binding.Single,
52
+
53
+ // this is set to true when the values of the field have changed since the
54
+ // last commit or reset.
55
+ isDirty: false,
56
+
57
+ // this is set to true while a form is in the process of committing changes
58
+ // from the form. This is useful when your content is an object controller
59
+ // that actually commits the form to the server.
60
+ isCommitting: true,
61
+
62
+ // set to false to disable form input. this will set the isEnabled property
63
+ // of all fields to false.
64
+ isEnabled: true,
65
+
66
+ passThroughToContent: false,
67
+
68
+ // computed property returns true if you have no errors.
69
+ isValid: function() {
70
+ return this.get('errors').length == 0;
71
+ }.property('errors'),
72
+
73
+ // RO - computed property returns true if the form can current be committed.
74
+ // The default version returns true if the form is enabled, valid, and
75
+ // dirty. You can override this with your own changes if you prefer some
76
+ // other behavior.
77
+ canCommit: function() {
78
+ return this.get('isValid') && this.get('isEnabled');
79
+ }.property('isValid','isEnabled'),
80
+
81
+ // Set this to an error or array of errors to be included in the
82
+ // overall errors property.
83
+ generalErrors: null,
84
+
85
+ // RO - this property returns any current errors on the form. Note that if
86
+ // you are implementing your own view, you should set your own value to
87
+ // an insteand of SC.FieldError and it will be displayed.
88
+ errors: function() {
89
+ if (!this._fields) return [] ;
90
+
91
+ // compute cached errors.
92
+ if (!this._errors) {
93
+ var fview =this ;
94
+ this._errors = [] ;
95
+ this.get('fieldKeys').each(function(k) {
96
+ var value = fview.get(k) ;
97
+ if ($type(value) == T_ERROR) fview._errors.push(value) ;
98
+ }) ;
99
+ }
100
+
101
+ // return set of errors.
102
+ return this._errors.concat(this.get('generalErrors') || []) ;
103
+ }.property('generalErrors'),
104
+
105
+
106
+ fieldKeys: function() {
107
+ if (!this._fieldKeys && this._fields) {
108
+ var keys = [];
109
+ for(var key in this._fields) {
110
+ if (!this._fields.hasOwnProperty(key)) continue ;
111
+ keys.push(key) ;
112
+ }
113
+ this._fieldKeys = keys ;
114
+ }
115
+ return this._fieldKeys ;
116
+ }.property(),
117
+
118
+ // SUPPORT METHODS
119
+
120
+ // Call this method to perform a full validation on the form fields.
121
+ // Returns true if the form is valid, false if it is not. If validation
122
+ // fails, the errors property will be set to the approrpiate value.
123
+ validate: function() {
124
+ if (!this._fields) return true; // ok if now fields.
125
+
126
+ // validate all fields.
127
+ for(var key in this._fields) {
128
+ if (this._fields.hasOwnProperty(key)) {
129
+ var field = this._fields[key] ;
130
+ if (field.validateSubmit) field.validateSubmit() ;
131
+ }
132
+ }
133
+
134
+ // check for errors
135
+ return this.get('isValid') ;
136
+ },
137
+
138
+ // This action can be called by a button to commit change to the form.
139
+ commit: function() {
140
+
141
+
142
+ // validate the form. Return false if validation fails.
143
+ if (!this.validate()) return false ;
144
+
145
+ var ret = true ;
146
+ var content = this.get('content') ;
147
+ if (!content || !this._fields) return ;
148
+
149
+ // disable form during commit.
150
+ var wasEnabled = this.get('isEnabled') ;
151
+ this.beginPropertyChanges() ;
152
+ this.set('isEnabled', false) ;
153
+ this.set('isCommitting',true) ;
154
+ this.endPropertyChanges() ;
155
+
156
+ ret = this.get('passThroughToContent') ? this._commitChanges() : this._copyContentAndCommitChanges();
157
+
158
+ // clean up property settings.
159
+ this.beginPropertyChanges() ;
160
+ this.set('isCommitting',false) ;
161
+ this.set('isEnabled',wasEnabled) ;
162
+ this.endPropertyChanges() ;
163
+
164
+ return ret ;
165
+ },
166
+
167
+ _copyContentAndCommitChanges: function()
168
+ {
169
+ var ret = true ;
170
+ var content = this.get('content');
171
+ if (!content || !this._fields) return false;
172
+
173
+ // copy all the properties back to the content object.
174
+ // if the content object throws an error for some reason, catch it
175
+ // and log it. Also add it to the list of errors.
176
+ try {
177
+ content.beginPropertyChanges();
178
+ for (var key in this._fields)
179
+ {
180
+ if (key.match(/Button$/)) continue; // ignore buttons.
181
+
182
+ if (this._fields.hasOwnProperty(key)) {
183
+ var newValue = this.get(key);
184
+ content.set(key,newValue);
185
+ }
186
+ }
187
+ content.endPropertyChanges();
188
+
189
+ // attempt to save changes...
190
+ ret = this._commitChanges();
191
+
192
+ // once a commit is complete, set isDirty to false. If the commit
193
+ // fails or an exception occurs, then don't set to false.
194
+ this.set('isDirty', !ret) ;
195
+ }
196
+ catch(e) {
197
+ console.log("commit() exception: " + e) ;
198
+ ret = false ;
199
+ }
200
+
201
+ return ret;
202
+ },
203
+ _commitChanges: function()
204
+ {
205
+ var content = this.get('content');
206
+ var success = false;
207
+
208
+ // If the content object supports a commit method, call it so it can
209
+ // commit changes to the server.
210
+ if (content && content.commit) {
211
+ success = content.commit(this);
212
+ } else if (content && content.commitChanges) {
213
+ success = content.commitChanges();
214
+ }
215
+
216
+ return success;
217
+ },
218
+
219
+
220
+ // This action will reset the form, copying the current values from the
221
+ // content object onto the field values.
222
+ reset: function()
223
+ {
224
+ if (!this._fields) return; // EXIT POINT
225
+
226
+ var content = this.get('content');
227
+
228
+ if (content && content.discardChanges) content.discardChanges();
229
+
230
+ this.beginPropertyChanges();
231
+ for(var key in this._fields) {
232
+ if (this._fields.hasOwnProperty(key)) {
233
+ var value = (content) ? content.get(key) : null;
234
+ this.set(key, value);
235
+ }
236
+ }
237
+ this.set('isDirty',false);
238
+ this.endPropertyChanges();
239
+
240
+ //if (content && content.discardChanges) content.discardChanges();
241
+ },
242
+
243
+ // This method will crawl through its child views looking for any view
244
+ // with the fieldKey property set. This does not go inside of other
245
+ // FormViews.
246
+ rebuildFields: function() {
247
+ this.beginPropertyChanges();
248
+
249
+ // if fields are already registered, remove them. Do it this way so
250
+ // that we can remove observer from the target object as well.
251
+ if (this._fields) {
252
+ for (var key in this._fields) {
253
+ if (this._fields.hasOwnProperty(key)) this.removeField(key) ;
254
+ }
255
+ }
256
+
257
+ // reset the fields hash.
258
+ this._fields = {} ;
259
+ this._buttons = {} ;
260
+ this._values = {} ;
261
+
262
+ // now rebuild field nodes for children.
263
+ this._rebuildFieldsForNode(this, true) ;
264
+ this.endPropertyChanges() ;
265
+ },
266
+
267
+ // You can add a field manually by calling this method.
268
+ // key - the key to respond to.
269
+ // field - the view to map to the key. This should be a child view of the
270
+ // form, but it is not required.
271
+ addField: function(key, field) {
272
+
273
+ // if the key is already defined on the form view, then we can't use it
274
+ // as a field. Throw an exception.
275
+ if (this[key] !== undefined) {
276
+ throw "FormView cannot add the field '%@' because that property already exists. Try using another name.".fmt(key);
277
+ }
278
+
279
+ // if this field is a submitButton or resetButton and the actio is
280
+ // not set, set it...
281
+ var form = this ;
282
+ if (key == 'submitButton' && (field.action == SC.ButtonView.prototype.action)) {
283
+ field.action = function() { form.commit(); } ;
284
+ }
285
+
286
+ if (key =="resetButton" && (field.action == SC.ButtonView.prototype.action)) {
287
+ field.action = function() { form.reset(); } ;
288
+ }
289
+
290
+ // save this field in the key.
291
+ this._fields[key] = field ;
292
+ if (key.substr(-6,6) == "Button") {
293
+ this._buttons[key] = field ;
294
+ };
295
+
296
+ // also add property of field to cache and notify of change.
297
+ this.propertyWillChange(key) ;
298
+ this.setValueForField(key, field.get('value'));
299
+ this.propertyDidChange(key,this.getValueForField(key)) ;
300
+
301
+ // and add us as an observer.
302
+ field.addObserver('value', this._fieldValueObserver_b()) ;
303
+ field.set('ownerForm',this) ;
304
+
305
+ this.propertyWillChange('fieldKeys') ;
306
+ this._fieldKeys = null ;
307
+ this.propertyDidChange('fieldKeys', null) ;
308
+ },
309
+
310
+ // This will remove the field with the named key from the list of fields.
311
+ removeField: function(key) {
312
+ // first remove the form as an observer to this field.
313
+ var field = this._fields[key] ;
314
+ if (field) {
315
+ field.removeObserver('value', this._fieldValueObserver_b());
316
+ field.set('ownerForm',null) ;
317
+ }
318
+
319
+ // now delete the field from our hash and cache and notify.
320
+ this.propertyWillChange(key) ;
321
+ delete this._fields[key] ;
322
+ delete this._values[key] ;
323
+ delete this._buttons[key] ;
324
+ this.propertyDidChange(key, null) ;
325
+
326
+
327
+ this.propertyWillChange('fieldKeys') ;
328
+ this._fieldKeys = null ;
329
+ this.propertyDidChange('fieldKeys', null) ;
330
+ },
331
+
332
+ // public accessor for retrieving the field View object
333
+ getField: function(key) {
334
+ return this._fields[key];
335
+ },
336
+
337
+
338
+ // KEYBOARD SUPPORT METHODS
339
+
340
+ // Process keyboard events...
341
+ keyDown: function(evt) {
342
+ return this.interpretKeyEvents(evt) ; // start bubbling key events...
343
+ },
344
+ keyUp: function() {},
345
+
346
+ // Handle default button.
347
+ insertNewline: function(sender, evt) {
348
+
349
+ // find the default button to use by scanning for the isDefault button.
350
+ var button = this._findDefaultButton(this) ;
351
+
352
+ // if not isDefault-button was found, look for the submitButton:
353
+ if (!button && this._fields && this._fields.submitButton) {
354
+ button = this._fields.submitButton ;
355
+ }
356
+
357
+ if (button && button.triggerAction) button.triggerAction(evt) ;
358
+ return true ;
359
+ },
360
+
361
+ // search child views looking for the default button view.
362
+ _findDefaultButton: function(view) {
363
+ if (view.triggerAction && view.get('isDefault')) return view;
364
+ view = view.firstChild;
365
+ while(view) {
366
+ var ret = this._findDefaultButton(view) ;
367
+ if (ret) return ret ;
368
+ view = view.nextSibling ;
369
+ }
370
+
371
+ return null ;
372
+ },
373
+
374
+ // INTERNAL SUPPORT METHODS
375
+
376
+ // This is called anytime you try to get or set an unknown property. When
377
+ // you get a property, this will look in the fields as well.
378
+ unknownProperty: function(key, value) {
379
+
380
+ var field = (this._fields) ? this._fields[key] : null ;
381
+
382
+ // setter
383
+ if (value !== undefined) {
384
+ if (field) {
385
+
386
+ var oldValue = this.getValueForField(key);
387
+
388
+ // save in our own cache first. This way when we get notified of
389
+ // the new value by the field, we won't renotify everyone else.
390
+ this.setValueForField(key, value);
391
+
392
+ // set the value on the field itself as well...
393
+ field.set('value',value) ;
394
+
395
+ // notify errors if the newValue changed to or from an error.
396
+ var isOldError = $type(oldValue) == T_ERROR ;
397
+ var isNewError = $type(value) == T_ERROR ;
398
+
399
+ if (isOldError != isNewError) {
400
+ this.propertyWillChange('errors') ;
401
+ this._errors = null ;
402
+ this.propertyDidChange('errors', null) ;
403
+ }
404
+
405
+
406
+ // if this is not on a field, just do the normal thing if no field fnd.
407
+ } else this[key] = value ;
408
+
409
+ // getter
410
+ } else {
411
+
412
+ // return the cached value if there is one.
413
+ if (field) {
414
+ if (this.getValueForField(key) === undefined) {
415
+ this.setValueForField(key, field.get('value'));
416
+ }
417
+ return this.getValueForField(key);
418
+ }
419
+
420
+ }
421
+
422
+ return value ;
423
+ },
424
+
425
+ getValueForField: function( key )
426
+ {
427
+ if (this.get('passThroughToContent')) {
428
+ var content = this.get('content');
429
+ return (content && content.get) ? content.get(key) : undefined;
430
+ } else {
431
+ return this._values[key];
432
+ }
433
+ },
434
+ setValueForField: function( key, value )
435
+ {
436
+ if (this.get('passThroughToContent')) {
437
+ var content = this.get('content');
438
+ if (content && content.get && content.set && (content.get(key) !== value))
439
+ {
440
+ content.set(key, value);
441
+ }
442
+ } else {
443
+ this._values[key] = value;
444
+ }
445
+ return value;
446
+ },
447
+
448
+ // When the form is first created, go find all the fields and save them.
449
+ init: function() {
450
+ arguments.callee.base.apply(this,arguments) ;
451
+
452
+ // disable the normal submission system so we can take over.
453
+ if (this.rootElement && this.rootElement.tagName.toLowerCase() == "form") {
454
+ this.rootElement.onsubmit = function() { return false; } ;
455
+
456
+ }
457
+ this.rebuildFields() ;
458
+ },
459
+
460
+ _rebuildFieldsForNode: function(node, _force) {
461
+ if (node.fieldKey) this.addField(node.fieldKey, node) ;
462
+
463
+ // other form views may be fields in your current form, but we do not
464
+ // examine the children of the other form views because the form views own
465
+ // those fields.
466
+ if ((_force != true) && (node instanceof SC.FormView)) return ;
467
+
468
+ // examine children.
469
+ var nodes = (node.childNodesForFormField) ? node.childNodesForFormField() : node.get('childNodes');
470
+ var loc = nodes.length ;
471
+ while(--loc >= 0) {
472
+ node = nodes[loc] ;
473
+ this._rebuildFieldsForNode(node, false);
474
+ }
475
+ },
476
+
477
+ // this observer is added to all field's value property. This simply
478
+ // notifies any observers of the form that the field's property has changed.
479
+ _fieldValueObserver: function(field, key, value) {
480
+ if (!(key = field.fieldKey)) return ; // only notifiy fields with keys...
481
+
482
+ // return if the new value is the same as the old value. This avoids
483
+ // infinite loops when the value is changed by our own setter.
484
+ var oldValue = this.getValueForField(key);
485
+ if (oldValue == value) return ;
486
+
487
+ // value did change so...
488
+
489
+ this.beginPropertyChanges() ;
490
+
491
+ // notify changes to field.
492
+ this.propertyWillChange(key) ;
493
+ this.setValueForField(key, value); // save the changed value.
494
+ this.propertyDidChange(key, value) ;
495
+
496
+ // notify errors if the newValue changed to or from an error.
497
+ var isOldError = $type(oldValue) == T_ERROR ;
498
+ var isNewError = $type(value) == T_ERROR ;
499
+
500
+ if (isOldError != isNewError) {
501
+ this.propertyWillChange('errors') ;
502
+ this._errors = null ;
503
+ this.propertyDidChange('errors', null) ;
504
+ }
505
+
506
+ // make form dirty.
507
+ if (!this.get('isDirty')) this.set('isDirty',true) ;
508
+
509
+ this.endPropertyChanges() ;
510
+ },
511
+
512
+ // returns a bound observer function...
513
+ _fieldValueObserver_b: function() {
514
+ return this._bound_fieldValueObserver = (this._bound_fieldValueObserver || this._fieldValueObserver.bind(this)) ;
515
+ },
516
+
517
+ // this observer gets called anytime any property changes on the content
518
+ // object, even those that are not mapped to fields on the form. This
519
+ // code simply checks for a change and then updates.
520
+ _contentPropertyObserver: function(content, key, value) {
521
+ if (!this._fields || !content) return ;
522
+ var fields = this._fields ;
523
+
524
+ // if the key changed is one we match, just update with the new value...
525
+ // NOTE: it is important to call didChangeFor() everytime this is called.
526
+ // otherwise this might not notify changes property.
527
+ if (fields[key] && content.didChangeFor(this,key)) {
528
+ this.set(key, value) ;
529
+
530
+ // otherwise, if the key changes is "*", then check all fields.
531
+ } else if (key == "*") {
532
+ for(var key in fields) {
533
+ if (fields.hasOwnProperty(key) && content.didChangeFor(this,key)) {
534
+ this.set(key,content.get(key)) ;
535
+ } // if (fields)
536
+ } // for(var key)
537
+ } // else if...
538
+ },
539
+
540
+ // returns the bound observer function...
541
+ _contentPropertyObserver_b: function() {
542
+ return this._bound_contentPropertyObserver = (this._bound_contentPropertyObserver || this._contentPropertyObserver.bind(this)) ;
543
+ },
544
+
545
+ _isEnabledObserver: function() {
546
+ var fields = this._fields ;
547
+ if (!fields) return ;
548
+ var enabled = this.get('isEnabled') ;
549
+ var canCommit = this.get('canCommit') ;
550
+
551
+ for(var key in fields) {
552
+ if (fields.hasOwnProperty(key)) {
553
+ var field = fields[key] ;
554
+ if (field.set) if (key == 'submitButton') {
555
+ field.set('isEnabled', canCommit) ;
556
+ } else field.set('isEnabled',enabled) ;
557
+ }
558
+ }
559
+ }.observes('isEnabled'),
560
+
561
+ // Automatically observe the content properties and add/remove form as
562
+ // observer.
563
+ _contentObserver: function() {
564
+ var content = this.get('content') ;
565
+ if (content == this._content) return ; // bail if content is same.
566
+
567
+ var func = this._contentPropertyObserver_b() ;
568
+
569
+ // if there was an older content, remove our observer.
570
+ if (this._content) this._content.removeObserver('*',func) ;
571
+
572
+ // now, add observer to new content
573
+ this._content = content ;
574
+ if (!content) return ; // EXIT POINT
575
+ content.addObserver('*', func) ;
576
+
577
+ // reset the form to the content values
578
+ this.reset() ;
579
+
580
+ }.observes('content'),
581
+
582
+ _canCommitObserver: function() {
583
+ var buttons = this._buttons ;
584
+ var canCommit = this.get('canCommit') ;
585
+ if (buttons && buttons.submitButton) {
586
+ var button = buttons.submitButton ;
587
+ if (button.set) button.set('isEnabled',canCommit) ;
588
+ }
589
+ }.observes('canCommit')
590
+
591
+ });