sproutcore 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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,1389 @@
1
+ // ========================================================================
2
+ // SproutCore
3
+ // copyright 2006-2007 Sprout Systems, Inc.
4
+ // ========================================================================
5
+
6
+ require('foundation/object') ;
7
+ require('foundation/responder') ;
8
+ require('foundation/node_descriptor') ;
9
+ require('foundation/binding');
10
+ require('foundation/path_module');
11
+
12
+ BENCHMARK_OUTLETS = NO ;
13
+ SC.FIXED = 'fixed';
14
+ SC.FLEXIBLE = 'flexible';
15
+
16
+ /**
17
+ @class Manages a DOM element for display.
18
+
19
+ Views are how you interact with the DOM.
20
+
21
+ @extends SC.Responder
22
+ */
23
+ SC.View = SC.Responder.extend(SC.PathModule,
24
+ /** @scope SC.View.prototype */
25
+ {
26
+
27
+ // ..........................................
28
+ // VIEW API
29
+ //
30
+ // The methods in this section are used to manage actual views. You can
31
+ // basically interact with child elements in two ways. One using an API
32
+ // similar to the DOM API. Alternatively, you can treat the view like an
33
+ // array and use standard iterators.
34
+ //
35
+
36
+ /*
37
+ insert the view before the specified view. pass null to insert at the
38
+ end.
39
+ */
40
+ insertBefore: function(view, beforeView) {
41
+ this._insertBefore(view,beforeView,true);
42
+ },
43
+
44
+ _insertBefore: function(view, beforeView, updateDom) {
45
+ // verify that beforeView is a child.
46
+ if (beforeView) {
47
+ if (beforeView.parentNode != this) throw "insertBefore() beforeView must belong to the receiver" ;
48
+ if (beforeView == view) throw "insertBefore() views cannot be the same";
49
+ }
50
+
51
+ if (view.parentNode) view.removeFromParent() ;
52
+ this.willAddChild(this, beforeView) ;
53
+ view.willAddToParent(this, beforeView) ;
54
+
55
+ // patch in the view.
56
+ if (beforeView) {
57
+ view.set('previousSibling', beforeView.previousSibling) ;
58
+ view.set('nextSibling', beforeView) ;
59
+ beforeView.set('previousSibling', view) ;
60
+ } else {
61
+ view.set('previousSibling', this.lastChild) ;
62
+ view.set('nextSibling', null) ;
63
+ this.set('lastChild', view) ;
64
+ }
65
+
66
+ if (view.previousSibling) view.previousSibling.set('nextSibling',view);
67
+ if (view.previousSibling == null) this.set('firstChild',view) ;
68
+ view.set('parentNode', this) ;
69
+
70
+ // Update DOM. -- ANIMATE
71
+ // Note that this code is not called when outlets are first configured.
72
+ // The assumption is that the created view already belongs to the
73
+ // document somwhere.
74
+ if (updateDom) {
75
+ var beforeElement = (beforeView) ? beforeView.rootElement : null;
76
+ (this.containerElement || this.rootElement).insertBefore(view.rootElement,beforeElement);
77
+
78
+ // regenerate the childNodes array.
79
+ this._rebuildChildNodes();
80
+
81
+ // update parent state.
82
+ view._updateIsVisibleInWindow() ;
83
+ }
84
+
85
+
86
+ // call notices.
87
+ view.didAddToParent(this, beforeView) ;
88
+ this.didAddChild(view, beforeView) ;
89
+
90
+ return this ;
91
+ },
92
+
93
+ // remove the current child
94
+ removeChild: function(view) {
95
+ if (!view) return ;
96
+ if (view.parentNode != this) throw "removeChild: view must belong to parent";
97
+
98
+ view.willRemoveFromParent() ;
99
+ this.willRemoveChild(view) ;
100
+
101
+ // unpatch.
102
+ if (view.previousSibling) {
103
+ view.previousSibling.set('nextSibling', view.nextSibling);
104
+ } else this.set('firstChild', view.nextSibling) ;
105
+
106
+ if (view.nextSibling) {
107
+ view.nextSibling.set('previousSibling', view.previousSibling) ;
108
+ } else this.set('lastChild', view.previousSibling) ;
109
+
110
+ // Update DOM -- ANIMATE
111
+ var el = (this.containerElement || this.rootElement);
112
+ if (el && (view.rootElement.parentNode == el) && (el != document)) {
113
+ el.removeChild(view.rootElement);
114
+ }
115
+
116
+ // regenerate the childNodes array.
117
+ this._rebuildChildNodes();
118
+
119
+ // update parent state.
120
+ view._updateIsVisibleInWindow() ;
121
+
122
+
123
+ view.set('nextSibling', null);
124
+ view.set('previousSibling', null);
125
+ view.set('parentNode', null) ;
126
+ view.didRemoveFromParent(this) ;
127
+ this.didRemoveChild(view);
128
+ },
129
+
130
+ // replace the oldView with the new view.
131
+ replaceChild: function(view, oldView) {
132
+ this.insertBefore(view,oldView) ; this.removeChild(oldView) ;
133
+ },
134
+
135
+ // remove the receiver from the parent view. Safe to call even if there
136
+ // is no parent node.
137
+ removeFromParent: function() {
138
+ if (this.parentNode) this.parentNode.removeChild(this) ;
139
+ },
140
+
141
+ // add a child to the end of the current views.
142
+ appendChild: function(view) {
143
+ this.insertBefore(view,null) ;
144
+ },
145
+
146
+ // this array contains the childViews associated with this view. You should
147
+ // always access this via a GET.
148
+ childNodes: [],
149
+
150
+ // the first child in the chain.
151
+ firstChild: null,
152
+
153
+ // the last child in the chain.
154
+ lastChild: null,
155
+
156
+ // the next child view. access via a get()
157
+ nextSibling: null,
158
+
159
+ // the previous view
160
+ previousSibling: null,
161
+
162
+ // the parent node. null if not in the hierarchy.
163
+ parentNode: null,
164
+
165
+
166
+ pane: function()
167
+ {
168
+ var view = this;
169
+ while(view = view.get('parentNode'))
170
+ {
171
+ if (view.get('isPane') ) break;
172
+ }
173
+ return view;
174
+ }.property(),
175
+
176
+
177
+ // This will remove all child views.
178
+ clear: function() {
179
+ while(this.firstChild) this.removeChild(this.firstChild) ;
180
+ },
181
+
182
+ // This callback is invoke just before your view is added to a new parent.
183
+ willAddToParent: function(parent, beforeView) {},
184
+
185
+ // This callback is invoked just after your view added to a new parent.
186
+ didAddToParent: function(parent, beforeView) {},
187
+
188
+ // This callback is invoked just before your view is removed from a parent.
189
+ willRemoveFromParent: function() {},
190
+
191
+ // This callback is invoked just after your view is remove from a parent.
192
+ didRemoveFromParent: function(oldParent) {},
193
+
194
+ // This callback is invoked just before a new child is added to view.
195
+ willAddChild: function(child, beforeView) {},
196
+
197
+ // This callback is invoked just after a new child is added to a view.
198
+ didAddChild: function(child, beforeView) {},
199
+
200
+ // This callback is invoke just before a child is removed from a view.
201
+ willRemoveChild: function(child) {},
202
+
203
+ // This callback is invoked this just after a child is removed from a view.
204
+ didRemoveChild: function(child) {},
205
+
206
+
207
+ nextKeyView: null,
208
+ previousKeyView: null,
209
+
210
+ nextValidKeyView: function()
211
+ {
212
+ var view = this;
213
+ while (view = view.get('nextKeyView'))
214
+ {
215
+ if (view.get('isVisible') && view.get('acceptsFirstResponder')) return view;
216
+ }
217
+ return null;
218
+ },
219
+ previousValidKeyView: function()
220
+ {
221
+ var view = this;
222
+ while (view = view.get('previousKeyView'))
223
+ {
224
+ if (view.get('isVisible') && view.get('acceptsFirstResponder')) return view;
225
+ }
226
+ return null;
227
+ },
228
+
229
+
230
+ // ..........................................
231
+ // SC.Responder implementation
232
+ //
233
+
234
+ nextResponder: function()
235
+ {
236
+ return this.parentNode;
237
+ }.property('parentNode'),
238
+
239
+ // recursively travels down the view hierarchy looking for a view that returns true to performKeyEquivalent
240
+ performKeyEquivalent: function(keystring, evt)
241
+ {
242
+ var child = this.get('firstChild');
243
+ while (child)
244
+ {
245
+ if (child.performKeyEquivalent(keystring, evt)) return true;
246
+ child = child.get('nextSibling');
247
+ }
248
+ return false;
249
+ },
250
+
251
+ // ..........................................
252
+ // ELEMENT API
253
+ //
254
+ // The methods in this section provide compatibility with the most common
255
+ // Prototype methods used on elements. These methods are generally primitives
256
+ // for modifying the underlying DOM element. You should only use them for
257
+ // INTERNAL VIEW CODE.
258
+
259
+ // returns the CSS classNames for the element.
260
+ classNames: function() {
261
+ return Element.classNames(this.rootElement);
262
+ }.property(),
263
+
264
+ // return true if the element has the classname.
265
+ hasClassName: function(className) {
266
+ var ret = Element.hasClassName(this.rootElement,className) ;
267
+ this.propertyDidChange('classNames') ;
268
+ return ret ;
269
+ },
270
+
271
+ // add the specified class name.
272
+ addClassName: function(className) {
273
+ var ret = Element.addClassName(this.rootElement,className) ;
274
+ this.propertyDidChange('classNames') ;
275
+ return ret ;
276
+ },
277
+
278
+ // remove the specified class name.
279
+ removeClassName: function(className) {
280
+ var ret = Element.removeClassName(this.rootElement,className) ;
281
+ this.propertyDidChange('classNames') ;
282
+ return ret ;
283
+ },
284
+
285
+ setClassName: function(className, flag) {
286
+ (!!flag) ? this.addClassName(className) : this.removeClassName(className);
287
+ },
288
+
289
+ // toggler specified class name..
290
+ toggleClassName: function(className) {
291
+ var ret = Element.toggleClassName(this.rootElement,className) ;
292
+ this.propertyDidChange('classNames') ;
293
+ return ret ;
294
+ },
295
+
296
+ // scroll the main window to the selected element.
297
+ scrollTo: function() {
298
+ Element.scrollTo(this.rootElement) ;
299
+ },
300
+
301
+ // get the named style. (see also style properties)
302
+ getStyle: function(style) {
303
+ return Element.getStyle(this.rootElement,style) ;
304
+ },
305
+
306
+ // set the passed styles.
307
+ setStyle: function(styles, camelized) {
308
+ return Element.setStyle(this.rootElement, styles, camelized) ;
309
+ },
310
+
311
+ // use this method to update the HTML of an element. This takes care of
312
+ // nasties like processing scripts and inserting HTML into a table. You can
313
+ // also use asHTML, which builds on this method.
314
+ update: function(html) {
315
+ Element.update((this.containerElement || this.rootElement),html) ;
316
+ this.propertyDidChange('asHTML') ;
317
+ },
318
+
319
+ // this works like the element getAttribute() except it is standardized
320
+ // across all browsers.
321
+ getAttribute: function(attrName) {
322
+ return Element.readAttribute(this.rootElement,attrName) ;
323
+ },
324
+
325
+ setAttribute: function(attrName, value) {
326
+ this.rootElement.setAttribute(atrrName, value) ;
327
+ },
328
+
329
+ hasAttribute: function(attrName) {
330
+ return Element.hasAttribute(this.rootElement, attrName) ;
331
+ },
332
+
333
+ // ..........................................
334
+ // DOM API
335
+ //
336
+ // The methods in this section give you some low-level control over how the
337
+ // view interacts with the DOM. You do not normally need to work with this.
338
+
339
+ // This is the DOM element actually managed by this view. This will be set
340
+ // by the view when it is created. Changing it afterwards will likely
341
+ // break things.
342
+ rootElement: null,
343
+
344
+ // Normally when you add child views to your view, their DOM elements will
345
+ // be set as direct children of the root element. However you can
346
+ // choose instead to designate an alertnative child node using this
347
+ // property. Set this to a selector string to begin with. The first time
348
+ // it is access, the view will convert it to an actual element. It is not
349
+ // currently safe to edit this property once the view has been created.
350
+ containerElement: null,
351
+
352
+ // ..........................................
353
+ // VIEW LAYOUT
354
+ //
355
+ // The following methods can be used to implement automatic resizing.
356
+ // The frame and bounds provides a simple way for you to compute the
357
+ // location and size of your views. You can then use the automatic
358
+ // resizing.
359
+
360
+
361
+ /**
362
+ Convert a point _from_ the offset parent of the passed view to the current view.
363
+
364
+ This is a useful utility for converting points in the coordinate system of
365
+ another view to the coordinate system of the receiver. Pass null for
366
+ targetView to convert a point from a window offset. This is the inverse of
367
+ convertFrameToView().
368
+
369
+ Note that if your view is not visible on the screen, this may not work.
370
+
371
+ @param {Point} f The point or frame to convert
372
+ @param {SC.Vew} targetView The view to convert from. Pass null to convert from window coordinates.
373
+
374
+ @returns {Point} The converted point or frame
375
+ */
376
+ convertFrameFromView: function(f, targetView) {
377
+
378
+ // first, convert to root level offset.
379
+ var thisOffset = Element.viewportOffset(this.get('offsetParent')) ;
380
+ var thatOffset = (targetView) ? Element.viewportOffset(targetView.get('offsetParent')) : [0,0] ;
381
+
382
+ // now get adjustment.
383
+ var adjustX = thatOffset[0] - thisOffset[0] ;
384
+ var adjustY = thatOffset[1] - thisOffset[1] ;
385
+ return { x: (f.x + adjustX), y: (f.y + adjustY), width: f.width, height: f.height };
386
+ },
387
+
388
+ /**
389
+ Convert a point _to_ the offset parent of the passed view from the current view.
390
+
391
+ This is a useful utility for converting points in the coordinate system of
392
+ the receiver to the coordinate system of another view. Pass null for
393
+ targetView to convert a point to a window offset. This is the inverse of
394
+ convertFrameFromView().
395
+
396
+ Note that if your view is not visible on the screen, this may not work.
397
+
398
+ @param {Point} f The point or frame to convert
399
+ @param {SC.Vew} targetView The view to convert to. Pass null to convert to window coordinates.
400
+
401
+ @returns {Point} The converted point or frame
402
+ */
403
+ convertFrameToView: function(f, sourceView) {
404
+ // first, convert to root level offset.
405
+ var thisOffset = Element.viewportOffset(this.get('offsetParent')) ;
406
+ var thatOffset = (sourceView) ? Element.viewportOffset(sourceView.get('offsetParent')) : [0,0] ;
407
+
408
+ // now get adjustment.
409
+ var adjustX = thisOffset[0] - thatOffset[0] ;
410
+ var adjustY = thisOffset[1] - thatOffset[1] ;
411
+ return { x: (f.x + adjustX), y: (f.y + adjustY), width: f.width, height: f.height };
412
+ },
413
+
414
+ // if a view isPositioned, then you can manually control the size and
415
+ // origin of the view using the frame property. If isPositioned is false,
416
+ // then this view will be sized and positioned by the browser using CSS.
417
+ // You can read the current frame, but you cannot make edits.
418
+ //
419
+ // You can edit the innerFrame of a view anytime, even if the element is
420
+ // not positioned.
421
+ //
422
+ isPositioned: false,
423
+
424
+ changePositionObserver: function() {
425
+ var isPositioned = this.get('isPositioned') ;
426
+ if (this._wasPositioned == isPositioned) return ;
427
+
428
+ // make absolute positioned. Also get default frame.
429
+ if (isPositioned) {
430
+ var el = this.rootElement;
431
+ this.cacheFrame();
432
+
433
+ this.setStyle({
434
+ position: 'absolute',
435
+ top: Math.floor(this._frame.y) + 'px',
436
+ left: Math.floor(this._frame.x) + 'px',
437
+ width: Math.floor(this._frame.width) + 'px',
438
+ height: Math.floor(this._frame.height) + 'px'
439
+ }) ;
440
+
441
+ } else {
442
+ var el = this.rootElement;
443
+ el.style.position =
444
+ el.style.top =
445
+ el.style.left =
446
+ el.style.width =
447
+ el.style.height = '' ;
448
+ this._frame = null ;
449
+ }
450
+ this._wasPositioned = isPositioned ;
451
+ }.observes('isPositioned'),
452
+
453
+ // Normally we don't get the dimensions of a view until you actually ask
454
+ // for them. However, sometimes you need to get the frame before you
455
+ // remove the view from the parent, etc. This will cache the frame.
456
+ cacheFrame: function() {
457
+ if (this._frame || this._frameCached) return ; // don't cache twice
458
+
459
+ var el = this.rootElement ;
460
+ this._frame = Element.getDimensions(el);
461
+ this._frame.x = el.offsetLeft ;
462
+ this._frame.y = el.offsetTop ;
463
+ this._frameCached = true ;
464
+ },
465
+
466
+ // if you cached the frame, you can use this to clear that cache so that it
467
+ // will now track with the frame in the document.
468
+ flushFrameCache: function() {
469
+ if (this._frameCached) {
470
+ this._frame = null ;
471
+ this._frameCached = false;
472
+ }
473
+ },
474
+
475
+ // This property returns a DOM ELEMENT that is the offset parent for
476
+ // this view's frame coordinates. Depending on your CSS, this parent
477
+ // may or may not match with the parent view.
478
+ offsetParent: function() {
479
+ return Position.offsetParent(this.rootElement) ;
480
+ }.property(),
481
+
482
+ // This property is used to set the internal padding of an element. The
483
+ // innerFrame is an offset from the outer frame. Changing these settings
484
+ // will adjust the height, width, and padding of the element.
485
+ innerFrame: function(key, value) {
486
+
487
+ // get the basic inner framce
488
+ var el = this.rootElement ;
489
+ var f = {
490
+ x: parseInt(this.getStyle('padding-left'),0) || 0,
491
+ y: parseInt(this.getStyle('padding-top'), 0) || 0,
492
+ width: parseInt(this.getStyle('width'), 0) || 0,
493
+ height: parseInt(this.getStyle('height'),0) || 0
494
+ } ;
495
+
496
+ // get the current frame size.
497
+ var size = {
498
+ width: f.x + f.width + parseInt(this.getStyle('padding-right'),0),
499
+ height: f.y + f.height + parseInt(this.getStyle('padding-bottom'),0)
500
+ };
501
+
502
+ // now update the innerFrame if needed. Change only the bits that are
503
+ // passed in.
504
+ if (value !== undefined) {
505
+ var style = {} ;
506
+ var didResize = false ;
507
+ var clearFrame = false ;
508
+
509
+ // reposition X
510
+ if (value.x !== undefined) {
511
+ f.x = value.x ;
512
+ style.paddingLeft = Math.floor(f.x) + 'px' ;
513
+ }
514
+
515
+ // reposition Y
516
+ if (value.y !== undefined) {
517
+ f.y = value.y ;
518
+ style.paddingTop = Math.floor(f.y) + 'px' ;
519
+ }
520
+
521
+ // resize Width
522
+ // adjust both the element width and padding right so that the overall
523
+ // frame size does not change.
524
+ if (value.width !== undefined) {
525
+ didResize = true ;
526
+ f.width = value.width ;
527
+ style.width = Math.floor(f.width).toString() + 'px' ;
528
+
529
+ var padding = size.width - f.width - f.x ;
530
+ if (padding < 0) {
531
+ clearFrame = true ;
532
+ padding = 0 ;
533
+ }
534
+ style.paddingRight = Math.floor(padding).toString() + 'px' ;
535
+ }
536
+
537
+ // Resize Height
538
+ // adjust both the element height and padding bottom so that the
539
+ // overall frame size does not change.
540
+ if (value.height !== undefined) {
541
+ didResize = true ;
542
+ f.height = value.height ;
543
+ style.height = Math.floor(f.height).toString() + 'px' ;
544
+
545
+ var padding = size.height - f.height - f.y ;
546
+ if (padding < 0) {
547
+ clearFrame = true ;
548
+ padding = 0 ;
549
+ }
550
+ style.paddingBottom = Math.floor(padding).toString() + 'px' ;
551
+ }
552
+
553
+ // now apply style change
554
+ this.setStyle(style) ;
555
+
556
+ // if the user sets an innerFrame size that cannot fit within the
557
+ // current outer frame, then the outer frame will be adjusted to fit.
558
+ // clear the frame so that this can happen.
559
+ if (clearFrame) {
560
+ this.propertyWillChange('frame') ;
561
+ this._frame = null ;
562
+ this.propertyDidChange('frame') ;
563
+ }
564
+
565
+ // also notify children so they can resize also.
566
+ if (didResize) this.resizeChildrenWithOldSize(size) ;
567
+ }
568
+
569
+ // finally return the frame.
570
+ return f ;
571
+ }.property('frame'),
572
+
573
+ innerSize: function(key, value) {
574
+ if (value !== undefined) {
575
+ this.set('innerFrame',{ width: value.width, height: value.height }) ;
576
+ }
577
+ return this.get('innerFrame') ;
578
+ }.property('innerFrame'),
579
+
580
+ innerOrigin: function(key, value) {
581
+ if (value !== undefined) {
582
+ this.set('innerFrame',{ x: value.x, y: value.y }) ;
583
+ }
584
+ return this.get('innerFrame') ;
585
+ }.property('innerFrame'),
586
+
587
+ // This property identifies the height and offset of your view with
588
+ // respect to the parent view and its bounds. To resize your view, edit
589
+ // this property.
590
+ //
591
+ // This method is carefully constructed to use the computed CSS style
592
+ // until you actually override it by setting your own size and location
593
+ // at which point it will use its own settings.
594
+ frame: function(key, value) {
595
+
596
+ // build frame
597
+ var el = this.rootElement ;
598
+ var f = Object.clone(this._frame) ;
599
+ if (f.x === undefined) f.x = el.offsetLeft ;
600
+ if (f.y === undefined) f.y = el.offsetTop ;
601
+
602
+ // get the current size if needed.
603
+ var size ;
604
+ if ((f.width === undefined) || (f.height === undefined)) {
605
+ var isVisibleInWindow = this.get('isVisibleInWindow') ;
606
+
607
+ // if not visible in window, move parent node into window and get
608
+ // dim and offset. If the element has no parentNode, then just move
609
+ // the element in.
610
+ if (!isVisibleInWindow) {
611
+ var pn = el.parentNode || el ;
612
+ var pnParent = pn.parentNode ;
613
+ var pnSib = pn.nextSibling ;
614
+ SC.window.rootElement.insertBefore(pn, null) ;
615
+ }
616
+
617
+ size = Element.getDimensions(el);
618
+ f.width = size.width ;
619
+ f.height = size.height;
620
+
621
+ if (!isVisibleInWindow) {
622
+ if (pnParent) {
623
+ pnParent.insertBefore(pn, pnSib) ;
624
+ } else SC.window.removeChild(pn) ;
625
+ }
626
+ } else size = f ;
627
+
628
+ // now update the frame if needed. Only actually change the style for
629
+ // those parts of the frame that were passed in.
630
+ if (value !== undefined) {
631
+ var style = {} ;
632
+ var didResize = false ;
633
+
634
+ // reposition X
635
+ if (value.x !== undefined) {
636
+ f.x = value.x ;
637
+ style.left = Math.floor(f.x) + 'px' ;
638
+ }
639
+
640
+ // reposition Y
641
+ if (value.y !== undefined) {
642
+ f.y = value.y ;
643
+ style.top = Math.floor(f.y) + 'px' ;
644
+ }
645
+
646
+ // Resize width
647
+ if (value.width !== undefined) {
648
+ didResize = true ;
649
+ f.width = value.width ;
650
+ var padding = parseInt(this.getStyle('padding-left'),0) + parseInt(this.getStyle('padding-right'),0) ;
651
+ style.width = (Math.floor(f.width) - padding).toString() + 'px' ;
652
+ }
653
+
654
+ // Resize Height
655
+ if (value.height !== undefined) {
656
+ didResize = true ;
657
+ f.height = value.height ;
658
+ var padding = parseInt(this.getStyle('padding-top'),0) + parseInt(this.getStyle('padding-bottom'),0) ;
659
+ style.height = (Math.floor(f.height) - padding).toString() + 'px' ;
660
+ }
661
+
662
+ // now apply style change and save new frame.
663
+ this.setStyle(style) ;
664
+ this._frame = Object.clone(f) ;
665
+
666
+ // also notify children so they can resize also.
667
+ if (didResize) this.resizeChildrenWithOldSize(size) ;
668
+ }
669
+
670
+ // finally return the frame.
671
+ return f ;
672
+ }.property('innerFrame'),
673
+
674
+ size: function(key, value) {
675
+ if (value !== undefined) {
676
+ this.set('frame',{ width: value.width, height: value.height }) ;
677
+ }
678
+ return this.get('frame') ;
679
+ }.property('frame'),
680
+
681
+ origin: function(key, value) {
682
+ if (value !== undefined) {
683
+ this.set('frame',{ x: value.x, y: value.y }) ;
684
+ }
685
+ return this.get('frame') ;
686
+ }.property('frame'),
687
+
688
+ /**
689
+ The current scroll frame for the view.
690
+
691
+ This will tell you the total scroll height and width of the view as well
692
+ as any current scroll offset. You can also set the x and y properties of
693
+ the scrollFrame. Any changes to height and width will be ignored.
694
+
695
+ @returns frame
696
+ */
697
+ scrollFrame: function(key, value) {
698
+ var el = this.rootElement ;
699
+ if (value !== undefined) {
700
+ el.scrollTop = value.y ;
701
+ el.scrollLeft = value.x ;
702
+ }
703
+
704
+ return { x: el.scrollLeft, y: el.scrollTop, height: el.scrollHeight, width: el.scrollWidth } ;
705
+ }.property('frame'),
706
+
707
+ // called on the view when you need to resize your child views. Normally
708
+ // this will call resizeWithOldParentSize() on the child views, but you
709
+ // can override this to do whatever funky layout to want.
710
+ resizeChildrenWithOldSize: function(oldSize) {
711
+ var child = this.get('firstChild') ;
712
+ while(child) {
713
+ child.resizeWithOldParentSize(oldSize) ;
714
+ child = child.get('nextSibling') ;
715
+ }
716
+ },
717
+
718
+ // called by the parentNode when it is resized. If you define the
719
+ // resizeOptions property, then this will respect those properties,
720
+ // otherwise it will let the browser do all the resizing and simply informs
721
+ // the child views that they need to resize also.
722
+ resizeWithOldParentSize: function(oldSize) {
723
+ var opts = this.get('resizeOptions') ;
724
+
725
+ // if there are no options, then just notify the children and return.
726
+ if (opts == null) {
727
+ if (this.firstChild) {
728
+ var oldSize = (this._frame) ? { width: this._frame.width, height: this._frame.height } : this.get('size') ;
729
+ this.resizeChildrenWithOldSize(oldSize) ;
730
+ }
731
+ return ;
732
+ }
733
+
734
+ // if there are options, then handle the resizing. This will
735
+ // notify the children also.
736
+ if (this.get('isPositioned')) this.set('isPositioned',true) ;
737
+
738
+ var f = Object.clone(this.get('frame')) ;
739
+ var newSize = this.get('parentNode').get('size') ;
740
+
741
+ var adjust = function(props, apts, newSize, oldSize) {
742
+ var loc ;
743
+
744
+ // first, compute the dimensions for old size.
745
+ var dims = [f[apts[0]], f[apts[1]]] ;
746
+ dims.push(oldSize - (dims[0] + dims[1])) ;
747
+
748
+ // next, subtract the dimensions of fixed elements from the old and
749
+ // new sizes.
750
+ for(loc=0;loc < 3;loc++) {
751
+ if (opts[props[loc]] != SC.FLEXIBLE) {
752
+ newSize -= dims[loc]; oldSize -= dims[loc] ;
753
+ }
754
+ }
755
+
756
+ // finally, adjust the flexible area as a percentage of the limited
757
+ // dimensions.
758
+ for(loc=0;loc < 2; loc++) {
759
+ if (opts[props[loc]] == SC.FLEXIBLE) {
760
+ f[apts[loc]] = newSize * dims[loc] / oldSize ;
761
+ }
762
+ }
763
+ };
764
+
765
+ // handle horizontal
766
+ adjust(['left','width','right'], ['x','width'], newSize.width, oldSize.width) ;
767
+
768
+ adjust(['top','height','bottom'], ['y','height'], newSize.height, oldSize.height) ;
769
+
770
+ this.set('frame',f) ;
771
+ },
772
+
773
+ // These properties are provide simple control for autoresizing. If you
774
+ // set these, then resizeWithOldParentSize() will autoresize for you.
775
+ // The allowed options are: SC.FLEXIBLE, SC.FIXED.
776
+ resizeOptions: null,
777
+
778
+ // ..........................................
779
+ // PROPERTIES
780
+ //
781
+
782
+ // set isVisible to false to hide a view or true to display it. You can
783
+ // optionally setup a visibleAnimation that will be used to transition the
784
+ // view in and out.
785
+ //
786
+ // If you would instead like to be notified when the view's actual
787
+ // visibility state changes (i.e. when animations are complete) bind to
788
+ // isDisplayVisible.
789
+ isVisible: true,
790
+ isVisibleBindingDefault: SC.Binding.Flag,
791
+
792
+ // [RO] This property reflects the current display visibility of the view.
793
+ // Usually, this property will mirror the current state of the isVisible
794
+ // property. However, if your view animates its visibility in and out, then
795
+ // this will not become false until the animation completes.
796
+ displayIsVisible: true,
797
+
798
+ // This property is set to true only when the view is (a) in the main DOM
799
+ // hierarchy and (b) all parent nodes are visible and (c) the receiver node
800
+ // is visible.
801
+ isVisibleInWindow: true,
802
+
803
+ // Localize boolean. This is used if you need toolTips.
804
+ localize: false,
805
+
806
+ // Tool tip gets applied to the title attribute if set.
807
+ toolTip: '',
808
+
809
+ // set this to the HTML you want to use when creating a new element. You
810
+ // can specify the HTML as a string of text, using the NodeDescriptor, or
811
+ // by pointing directly to an element.
812
+ emptyElement: "<div></div>",
813
+
814
+ // Set to true and the view will display in a lightbox when you show it.
815
+ isPanel: false,
816
+
817
+ // Set to true if the view should be modal when shown as a panel.
818
+ isModal: true,
819
+
820
+ // Enable visible animation by default.
821
+ isAnimationEnabled: true,
822
+
823
+ // General support for animation. Just call this method and it will build
824
+ // and play an animation starting from the current state. The second param
825
+ // is optional. It should either be a hash of animator options or an
826
+ // animator object returned by a previous call to transitionTo().
827
+ //
828
+ transitionTo: function(target,animator,opts) {
829
+ var animatorOptions = opts || {} ;
830
+
831
+ // Create or reset the animator.
832
+ if (animator && !animator._isAnimator) {
833
+ var finalStyle = animator ;
834
+ if (!this.get("isAnimationEnabled")) {
835
+ animatorOptions = Object.clone(animatorOptions) ;
836
+ animatorOptions.duration = 1;
837
+ }
838
+ if (animatorOptions.duration) {
839
+ animatorOptions.duration = parseInt(animatorOptions.duration,0) ;
840
+ }
841
+
842
+ animator = Animator.apply(this.rootElement, finalStyle, animatorOptions);
843
+ animator._isAnimator = true ;
844
+ }
845
+
846
+ // trigger animation
847
+ if (animator) {
848
+ animator.jumpTo(animator.state) ;
849
+ animator.seekTo(target) ;
850
+ }
851
+ return animator ;
852
+ },
853
+
854
+ // returns the contents of the element as HTML. Accounts for browser
855
+ // bugs.
856
+ asHTML: function(key, value) {
857
+ if (value !== undefined) {
858
+ // Safari2 has a bad habit of sometimes not actually changing its
859
+ // innerHTML. This will make sure the innerHTML get's changed properly.
860
+ if (SC.isSafari() && !SC.isSafari3()) {
861
+ var el = (this.containerElement || this.rootElement) ; var reps = 0 ;
862
+ var f = function() {
863
+ el.innerHTML = '' ; el.innerHTML = value ;
864
+ if ((reps++ < 5) && (value.length>0) && (el.innerHTML == '')) setTimeout(f,1) ;
865
+ };
866
+ f();
867
+ } else (this.containerElement || this.rootElement).innerHTML = value;
868
+ } else value = (this.containerElement || this.rootElement).innerHTML ;
869
+ return value ;
870
+ }.property(),
871
+
872
+ // returns the contents of the element as plain text. Accounts for browser
873
+ // bugs.
874
+ asText: function(key, value) {
875
+ if (value !== undefined) {
876
+ if (value == null) value = '' ;
877
+ this.asHTML(key,value.toString().escapeHTML()) ;
878
+ }
879
+ return this.asHTML().unescapeHTML() ;
880
+ }.property(),
881
+
882
+
883
+
884
+
885
+ //
886
+ // Command methods (used by the command manager)
887
+ //
888
+
889
+ /**
890
+ * Queries to see if the view has function matching the passed name .
891
+ * @param {String} name The name of the function
892
+ * @return Boolean
893
+ **/
894
+ hasNamedFunction: function( name )
895
+ {
896
+ return ( this[name] && ($type(this[name]) == T_FUNCTION) );
897
+ },
898
+ /**
899
+ * Queries to see if the view has a named command.
900
+ * @param {String} name The name of the command
901
+ * @return Boolean
902
+ **/
903
+ hasCommand: function( name )
904
+ {
905
+ return this.hasNamedFunction(name);
906
+ },
907
+ /**
908
+ * Queries to see if the view has a validator for the named command.
909
+ * @param {String} name The name of the command
910
+ * @return Boolean
911
+ **/
912
+ hasCommandValidator: function( name )
913
+ {
914
+ var name = this._commandValidatorForCommand(name);
915
+ return this.hasNamedFunction(name);
916
+ },
917
+
918
+ /**
919
+ * Queries to see if the view is capable of executing a named command.
920
+ * The view must have a method named after the command and, if there is a command validator method, it must pass validation.
921
+ * @param {String} name The name of the command
922
+ * @return Boolean
923
+ **/
924
+ canExecuteCommand: function( name )
925
+ {
926
+ var hasCommand = this.hasCommand(name);
927
+ var hasCommandValidator = this.hasCommandValidator(name);
928
+ // can't execute what you haven't got...
929
+ if ( !hasCommand ) return false;
930
+ // got it and not validating before usage...
931
+ if ( hasCommand && !hasCommandValidator ) return true;
932
+ // ok... we got it, and we need to check before using...
933
+ if ( hasCommand && hasCommandValidator )
934
+ {
935
+ return this.executeCommandValidator(name);
936
+ }
937
+ },
938
+ /**
939
+ * Executes the command (if permitted).
940
+ * @param {String} name The name of the command
941
+ * @return Boolean Either the return value of executing the command, or false.
942
+ **/
943
+ executeCommand: function( name )
944
+ {
945
+ return this.canExecuteCommand(name) ? this.executeCommandWithoutValidation(name) : false;
946
+ },
947
+ /**
948
+ * Executes the command without performing any validation.
949
+ * @param {String} name The name of the command
950
+ * @return Boolean The return value of executing the command.
951
+ **/
952
+ executeCommandWithoutValidation: function( name )
953
+ {
954
+ return this[name]();
955
+ },
956
+ /**
957
+ * Executes the command validator.
958
+ * @param {String} name The name of the command to be validated.
959
+ * @return Boolean Wether or not the command can be executed.
960
+ **/
961
+ executeCommandValidator: function( name )
962
+ {
963
+ var name = this._commandValidatorForCommand(name);
964
+ return this[name]();
965
+ },
966
+
967
+ /**
968
+ * Utility to construct the command alidator method name.
969
+ * @private
970
+ * @param {String} name The name of the command
971
+ * @return String
972
+ **/
973
+ _commandValidatorForCommand: function( name )
974
+ {
975
+ return "can" + name.capitalize();
976
+ },
977
+
978
+
979
+
980
+
981
+ // ..........................................
982
+ // SUPPORT METHODS
983
+ //
984
+ init: function() {
985
+ this._frame = {} ;
986
+ arguments.callee.base.call(this) ;
987
+
988
+ // configure them outlets.
989
+ var r = SC.idt.active ; var idtStart ; var idtSt ;
990
+ if (r) { idtSt = new Date().getTime(); }
991
+ this.configureOutlets() ;
992
+ if (r) { SC.idt.conf_t += ((new Date().getTime()) - idtSt); }
993
+
994
+ var toolTip = this.get('toolTip') ;
995
+ if(toolTip && (toolTip != '')) this._updateToolTipObserver();
996
+
997
+ // despite what was written in the comments for the containerElement property, it was not being converted
998
+ // from a sring to an element on access... doing so here...
999
+ // shouldn't be a bottleneck since if containerElement is set, you are likely to need the DOM element at some point.
1000
+ if ( this.containerElement && (SC.typeOf(this.containerElement) == T_STRING) )
1001
+ {
1002
+ this.containerElement = this.$sel(this.containerElement);
1003
+ }
1004
+
1005
+ if (this.get('isDropTarget')) SC.Drag.addDropTarget(this) ;
1006
+ },
1007
+
1008
+ // this method looks through your outlets array and will try to
1009
+ // reconfigure any missing ones.
1010
+ configureOutlets: function() {
1011
+
1012
+ if (!this.outlets || (this.outlets.length <= 0)) return ;
1013
+
1014
+ // lookup outlets as selector paths or execute the function if there
1015
+ // is one.
1016
+ this.beginPropertyChanges(); // bundle changes
1017
+ for(var oloc=0;oloc < this.outlets.length;oloc++) {
1018
+ var view = this.outlet(this.outlets[oloc]) ;
1019
+
1020
+ // if the HTML for the view is already in the DOM, then walk up the
1021
+ // DOM tree to find the first parent element managed by a view (incl
1022
+ // the receiver. Add the view to the list of child views also.
1023
+ if (view && view.rootElement && view.rootElement.parentNode) {
1024
+ var node = view.rootElement.parentNode;
1025
+ var parentView ;
1026
+ while(node && (node != this.rootElement) && !(parentView = $view(node))) node = node.parentNode;
1027
+ if (node == this.rootElement) parentView = this;
1028
+ if (parentView) parentView._insertBefore(view,null,false) ;
1029
+ }
1030
+ this._rebuildChildNodes() ; // this is not done with _insertBefore.
1031
+
1032
+ // update parent state.
1033
+ if (view && view._updateIsVisibleInWindow) {
1034
+ view._updateIsVisibleInWindow() ;
1035
+ }
1036
+ }
1037
+ this.endPropertyChanges() ;
1038
+ },
1039
+
1040
+ // ..........................................
1041
+ // VISIBILITY METHODS
1042
+ //
1043
+
1044
+ // Calling this method will show the view. Don't call this method
1045
+ // directly but instead set the isVisible property to true. You can
1046
+ // override this method to provide your own show capabilities.
1047
+ show: function() {
1048
+ Element.show(this.rootElement) ;
1049
+ this.removeClassName('hidden') ;
1050
+ this.set('displayIsVisible',true) ;
1051
+ },
1052
+
1053
+ // This is the primitive method for hiding a view. It will be called when
1054
+ // isVisible is set to false after an animation runs or immediate if no
1055
+ // animation is defined.
1056
+ hide: function() {
1057
+ Element.hide(this.rootElement) ;
1058
+ this.addClassName('hidden') ;
1059
+ this.set('displayIsVisible', false) ;
1060
+ },
1061
+
1062
+ // ..........................................
1063
+ // DEPRECATED. DO NOT USE
1064
+ //
1065
+
1066
+ // deprecated. Included only for compatibility.
1067
+ animateVisible: function(key, value) {
1068
+ if (value !== undefined) return this.set('isAnimationEnabled',value) ;
1069
+ return this.get('isAnimationEnabled');
1070
+ }.property('isAnimationEnabled'),
1071
+
1072
+
1073
+ // ..........................................
1074
+ // PRIVATE METHODS
1075
+ //
1076
+
1077
+ // this will set the rootElement, cleaning up any old element.
1078
+ _attachRootElement: function(el) {
1079
+ if (this.rootElement) this.rootElement._configured = null ;
1080
+ this.rootElement = el ;
1081
+ el._configured = this._guid ;
1082
+ },
1083
+
1084
+ // This method is called internally after you add or remove a child view.
1085
+ // It will rebuild the childNodes array to reflect all children.
1086
+ _rebuildChildNodes: function() {
1087
+ var ret = [] ; var view = this.firstChild;
1088
+ while(view) { ret.push(view); view = view.nextSibling; }
1089
+ this.set('childNodes', ret) ;
1090
+ },
1091
+
1092
+ _toolTipObserver: function() {
1093
+ var toolTip = this.get('toolTip') ;
1094
+ if (this.get('localize')) toolTip = toolTip.loc() ;
1095
+ this.rootElement.title = toolTip ;
1096
+ }.observes("toolTip"),
1097
+
1098
+ _isVisibleObserver: function() {
1099
+ var flag = this.get('isVisible') ;
1100
+ if ((this._isVisible === undefined) || (flag != this._isVisible)) {
1101
+ this._isVisible = flag ;
1102
+ if (flag) {
1103
+ this._show() ;
1104
+ } else this._hide() ;
1105
+
1106
+ // update parent state.
1107
+ this._updateIsVisibleInWindow() ;
1108
+ }
1109
+ }.observes('isVisible'),
1110
+
1111
+ _updateIsVisibleInWindow: function(parentNodeState) {
1112
+ if (parentNodeState === undefined) {
1113
+ var parentNode = this.get('parentNode') ;
1114
+ parentNodeState = (parentNode) ? parentNode.get('isVisibleInWindow') : false ;
1115
+ }
1116
+
1117
+ var visible = parentNodeState && this.get('isVisible') ;
1118
+
1119
+ // if state changes, update and notify children.
1120
+ if (visible != this.get('isVisibleInWindow')) {
1121
+ this.set('isVisibleInWindow', visible) ;
1122
+ var child = this.get('firstChild') ;
1123
+ while(child) {
1124
+ child._updateIsVisibleInWindow(visible) ;
1125
+ child = child.get('nextSibling') ;
1126
+ }
1127
+ }
1128
+ },
1129
+
1130
+ // Calling this method will show the view. Don't call this method
1131
+ // directly but instead set the isVisible property to true. You can
1132
+ // override this method to provide your own show capabilities.
1133
+ _show: function(anchorView, triggerEvent) {
1134
+ // compatibility
1135
+ if (this.showView) return this.showView() ;
1136
+
1137
+ // if this is a type of pane, call the pane manager.
1138
+ var paneType = this.get('paneType') ;
1139
+ if (this.get('isPanel')) paneType = SC.PANEL_PANE; // compatibility
1140
+ if (paneType) {
1141
+ if (anchorView === undefined) anchorView = null ;
1142
+ if (triggerEvent === undefined) triggerEvent = null ;
1143
+ SC.PaneManager.manager().showPaneView(this, paneType, anchorView, triggerEvent) ;
1144
+ this.set('displayIsVisible', true) ;
1145
+
1146
+ // if an animation is defined and animations are configured, use that.
1147
+ // the displayIsVisible property will be set to true when the animation
1148
+ // completes.
1149
+ } else if (this.visibleAnimation && this.get('isAnimationEnabled')) {
1150
+ this._transitionVisibleTo(1.0) ;
1151
+
1152
+ // at this point the animation has been reset to the beginng. Run the
1153
+ // core show() method immediately so the animation will be visible.
1154
+ this.show() ;
1155
+
1156
+ // otherwise, just change over visible settings.
1157
+ } else {
1158
+ this._visibleAnimator = null ;
1159
+ this.show() ;
1160
+ }
1161
+
1162
+ return this ;
1163
+ },
1164
+
1165
+ _hide: function() {
1166
+ // compatibility
1167
+ if (this.hideView) return this.hideView() ;
1168
+
1169
+ // if this is a type of pane, call the pane manager.
1170
+ var isPane = (!!this.get('paneType')) || this.get('isPanel') ;
1171
+ if (isPane) {
1172
+ SC.PaneManager.manager().hidePaneView(this) ;
1173
+ this.set('displayIsVisible', false) ;
1174
+
1175
+ // if an animation is defined and animations are configured, use that.
1176
+ // the displayIsVisible property will be set to false when the animation
1177
+ // completes.
1178
+ } else if (this.visibleAnimation && this.get('isAnimationEnabled')) {
1179
+ this._transitionVisibleTo(0.0) ;
1180
+
1181
+ // otherwise, just change over visible settings.
1182
+ } else {
1183
+ this._visibleAnimator = null;
1184
+ this.hide();
1185
+ }
1186
+
1187
+ return this ;
1188
+ },
1189
+
1190
+
1191
+ _transitionVisibleTo: function(target) {
1192
+ var a ;
1193
+
1194
+ // if an animator already exists, just transition to the new state.
1195
+ if (this._visibleAnimator) {
1196
+ this.transitionTo(target,this._visibleAnimator);
1197
+
1198
+ // otherwise, build the animator from the options passed. Patch in our
1199
+ // own onComplete handler.
1200
+ } else {
1201
+ var opts = this.visibleAnimation ;
1202
+ var style = [opts.hidden,opts.visible] ;
1203
+ opts.onComplete =
1204
+ this._animateVisibleDidComplete.bind(this,opts.onComplete) ;
1205
+ this._visibleAnimator = this.transitionTo(target,style,opts);
1206
+ }
1207
+ },
1208
+
1209
+ // This is called when the animation completes. Finish cleaning up the
1210
+ // visibility section.
1211
+ _animateVisibleDidComplete: function(chainFunc) {
1212
+ if (!this.get('isVisible')) this.hide() ;
1213
+ if (chainFunc) chainFunc(this) ;
1214
+ },
1215
+
1216
+ _firstResponderObserver: function(target, key, value) {
1217
+ this.setClassName('focus',value) ;
1218
+ }.observes('isFirstResponder'),
1219
+
1220
+ _dropTargetObserver: function() {
1221
+ if (this.get('isDropTarget')) {
1222
+ SC.Drag.addDropTarget(this) ;
1223
+ } else SC.Drag.removeDropTarget(this) ;
1224
+ }.observes('isDropTarget'),
1225
+
1226
+ // .............................................
1227
+ // SPECIAL TYPES OF VIEWS
1228
+ //
1229
+
1230
+ // This will show the pane as a popup or picker (depending on the paneType
1231
+ // you have set.) This works just like setting isVisible to true, except
1232
+ // that it also passes the anchorView and triggerEvent you pass in.
1233
+ popup: function(anchorView, triggerEvent) {
1234
+
1235
+ // this will bypass the normal observer machinery, calling the private
1236
+ // _show method ourselves. To avoid triggering _show twice, we patch up
1237
+ // the internal _isVisible property.
1238
+ this._isVisible = true ;
1239
+ this._show(anchorView, triggerEvent) ;
1240
+ this.set('isVisible', true);
1241
+ },
1242
+
1243
+ // This can be used to manually add observers to the rootElement for the
1244
+ // methods in the passed map. You generally don't want to do this since we
1245
+ // handle event propgation through the responder chain.
1246
+ configureObserverMethods: function(methodMap) {
1247
+ for(var name in methodMap) {
1248
+ if (!methodMap.hasOwnProperty(name)) continue ;
1249
+ if (this[name]) {
1250
+ var method = this[name].bindAsEventListener(this);
1251
+ Event.observe(this.rootElement,methodMap[name],method) ;
1252
+ }
1253
+ }
1254
+ },
1255
+
1256
+ toString: function() {
1257
+ var el = this.rootElement ;
1258
+ var attrs = el.attributes ;
1259
+ attrs = (attrs) ? $A(attrs).map(function(atr) { return [atr.nodeName,atr.nodeValue].join("="); }).join(' ') : '';
1260
+ var tagName = (!!el.tagName) ? el.tagName.toLowerCase() : 'document' ;
1261
+ return "View(<%@>)".format([tagName,attrs].join(' ')) ;
1262
+ }
1263
+
1264
+ }) ;
1265
+
1266
+ // Class Methods
1267
+ SC.View.mixin({
1268
+
1269
+ // this is the global registry of views. It's used to map elements back
1270
+ // to the views that own them.
1271
+ _view: {},
1272
+
1273
+ findViewForElement: function(el) {
1274
+ var guid = el._configured ;
1275
+ return (guid) ? SC.View._view[guid] : null ;
1276
+ },
1277
+
1278
+ // ..........................................
1279
+ // SETUP
1280
+ //
1281
+ // This works much like create except that it works on the passed in
1282
+ // element instead of trying to find something new. If you pass null for
1283
+ // the first parameter, then a new element will be created with the html
1284
+ // you set in content.
1285
+ viewFor: function(el,config) {
1286
+ if (el) el = $(el) ;
1287
+
1288
+ var r = SC.idt.active ; var vStart ;
1289
+ if (r) SC.idt.v_count++;
1290
+
1291
+ if (r) vStart = new Date().getTime() ;
1292
+
1293
+ // find or build the element.
1294
+ if (!el) {
1295
+ var emptyElement = this.prototype._cachedEmptyElement || this.prototype.emptyElement;
1296
+
1297
+ // if the emptyElement is a string not starting with '<', treat it like
1298
+ // an id and find it in the doc. If an element is found, cache it for
1299
+ // future use.
1300
+ var isString = typeof(emptyElement) == 'string' ;
1301
+ if (isString && (emptyElement.slice(0,1) != '<')) {
1302
+ var el = $sel(emptyElement) ;
1303
+ if (el) {
1304
+ this.prototype.emptyElement = emptyElement = el ;
1305
+ isString = false ;
1306
+ }
1307
+ }
1308
+
1309
+ // if still a string, then use it to create HTML. Save the generated
1310
+ // element so that we can avoid doing this over again.
1311
+ if (isString) {
1312
+ SC._ViewCreator.innerHTML = emptyElement ;
1313
+ el = $(SC._ViewCreator.firstChild) ;
1314
+ SC.NodeCache.appendChild(el) ;
1315
+ this.prototype._cachedEmptyElement = el.cloneNode(true) ;
1316
+
1317
+ } else if (typeof(emptyElement) == "object") {
1318
+ if (emptyElement.tagName) {
1319
+ el = emptyElement.cloneNode(true) ;
1320
+ } else el = SC.NodeDescriptor.create(emptyElement) ;
1321
+ }
1322
+ }
1323
+ if (r) SC.idt.vc_t += (new Date().getTime()) - vStart ;
1324
+
1325
+ // configure only once.
1326
+ if (el && el._configured) return SC.View.findViewForElement(el);
1327
+
1328
+ // Now that we have found an element, instantiate the view.
1329
+ var args = $A(arguments) ; args[0] = { rootElement: el } ;
1330
+ if (r) vStart = new Date().getTime();
1331
+ var ret = new this(args,this) ; // create instance.
1332
+ if (r) SC.idt.v_t += (new Date().getTime()) - vStart;
1333
+ el._configured = ret._guid ;
1334
+
1335
+ // return the view.
1336
+ SC.View._view[ret._guid] = ret ;
1337
+ return ret ;
1338
+ },
1339
+
1340
+ // create in the view work is like viewFor but with 'null' for el
1341
+ create: function(configs) {
1342
+ var args = $A(arguments) ;
1343
+ args.unshift(null) ;
1344
+ return this.viewFor.apply(this,args) ;
1345
+ },
1346
+
1347
+ // define your view as an outlet.
1348
+ outletFor: function(path) {
1349
+ var view = this ;
1350
+ var _func = function() {
1351
+ if (path === null) return view.viewFor(null) ;
1352
+
1353
+
1354
+ var ret = (this.$$sel) ? this.$$sel(path) : $$sel(path) ;
1355
+ if (ret) {
1356
+ var owner = this ;
1357
+ var newRet = [] ;
1358
+ for(var loc=0;loc<ret.length;loc++) {
1359
+ newRet.push(view.viewFor(ret[loc], { owner: owner }));
1360
+ }
1361
+ ret = newRet ;
1362
+ ret = (ret.length == 0) ? null : ((ret.length == 1) ? ret[0] : ret);
1363
+ }
1364
+
1365
+ return ret ;
1366
+ } ;
1367
+
1368
+ var func ;
1369
+ if (BENCHMARK_OUTLETS) {
1370
+ func = function() {
1371
+ var that = this ;
1372
+ return SC.Benchmark._bench(function() {
1373
+ return _func.call(that);
1374
+ }, "OUTLET(%@)".format(path)) ;
1375
+ };
1376
+ } else func = _func ;
1377
+ func.isOutlet = true ;
1378
+ return func ;
1379
+ }
1380
+
1381
+ }) ;
1382
+
1383
+ SC.View.elementFor = SC.View.viewFor ; // Old Sprout Compatibility.
1384
+
1385
+ // This div is used to create nodes. It should normally remain empty.
1386
+ SC._ViewCreator = document.createElement('div') ;
1387
+
1388
+ // This div can be used to hold elements you don't want on the page right now.
1389
+ SC.NodeCache = document.createElement('div') ;