sproutcore 1.5.0.pre.3 → 1.5.0.pre.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (308) hide show
  1. data/.rspec +1 -0
  2. data/CHANGELOG +42 -0
  3. data/README.txt +25 -0
  4. data/VERSION.yml +1 -1
  5. data/bin/sc-build +1 -1
  6. data/bin/sc-build-number +1 -1
  7. data/bin/sc-docs +1 -1
  8. data/bin/sc-gen +1 -1
  9. data/bin/sc-init +1 -1
  10. data/bin/sc-manifest +1 -1
  11. data/bin/sc-server +1 -1
  12. data/bin/sproutcore +1 -1
  13. data/lib/buildtasks/build.rake +5 -0
  14. data/lib/buildtasks/manifest.rake +20 -1
  15. data/lib/frameworks/sproutcore/Buildfile +12 -9
  16. data/lib/frameworks/sproutcore/CHANGELOG.md +48 -0
  17. data/lib/frameworks/sproutcore/apps/greenhouse/README +2 -9
  18. data/lib/frameworks/sproutcore/apps/greenhouse/TODO +11 -27
  19. data/lib/frameworks/sproutcore/apps/greenhouse/controllers/library.js +3 -10
  20. data/lib/frameworks/sproutcore/apps/greenhouse/english.lproj/css/main-page.css +4 -31
  21. data/lib/frameworks/sproutcore/apps/greenhouse/english.lproj/dialogs.js +5 -4
  22. data/lib/frameworks/sproutcore/apps/greenhouse/models/dir.js +3 -3
  23. data/lib/frameworks/sproutcore/apps/greenhouse/models/file.js +2 -2
  24. data/lib/frameworks/sproutcore/apps/greenhouse/states/main.js +31 -14
  25. data/lib/frameworks/sproutcore/apps/greenhouse/states/modals.js +2 -1
  26. data/lib/frameworks/sproutcore/apps/greenhouse/states/ready.js +27 -1
  27. data/lib/frameworks/sproutcore/apps/greenhouse/tests/views/list_item.js +1 -0
  28. data/lib/frameworks/sproutcore/apps/greenhouse/tests/views/plist_item.js +20 -0
  29. data/lib/frameworks/sproutcore/apps/greenhouse/theme.js +25 -0
  30. data/lib/frameworks/sproutcore/apps/greenhouse/views/plist_item.js +161 -24
  31. data/lib/frameworks/sproutcore/apps/greenhouse/views/tear_off_picker.js +1 -1
  32. data/lib/frameworks/sproutcore/frameworks/bootstrap/system/browser.js +37 -25
  33. data/lib/frameworks/sproutcore/frameworks/bootstrap/tests/system/browser.js +135 -26
  34. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/controllers/array.js +0 -0
  35. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/controllers/controller.js +0 -0
  36. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/controllers/object.js +1 -1
  37. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/core.js +0 -0
  38. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/ext/object.js +0 -0
  39. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/ext/run_loop.js +1 -0
  40. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/mixins/delegate_support.js +0 -0
  41. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/mixins/responder_context.js +0 -0
  42. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/mixins/selection_support.js +0 -0
  43. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/mixins/string.js +0 -0
  44. data/lib/frameworks/sproutcore/frameworks/core_foundation/panes/keyboard.js +68 -0
  45. data/lib/frameworks/sproutcore/frameworks/core_foundation/panes/layout.js +108 -0
  46. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/panes/main.js +2 -2
  47. data/lib/frameworks/sproutcore/frameworks/core_foundation/panes/manipulation.js +27 -0
  48. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/panes/pane.js +215 -505
  49. data/lib/frameworks/sproutcore/frameworks/core_foundation/panes/template.js +24 -0
  50. data/lib/frameworks/sproutcore/frameworks/core_foundation/panes/visibility.js +11 -0
  51. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/protocols/observable_protocol.js +0 -0
  52. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/protocols/sparse_array_delegate.js +0 -0
  53. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/resources/core.css +0 -0
  54. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/resources/view.css +0 -0
  55. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/system/application.js +0 -0
  56. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/system/browser.js +1 -0
  57. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/system/builder.js +0 -0
  58. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/system/core_query.js +1 -1
  59. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/system/cursor.js +0 -0
  60. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/device.js +210 -0
  61. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/system/event.js +5 -5
  62. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/system/json.js +0 -0
  63. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/system/locale.js +0 -0
  64. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/system/page.js +0 -0
  65. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/system/platform.js +31 -5
  66. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/system/ready.js +1 -1
  67. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/system/render_context.js +0 -0
  68. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/system/responder.js +0 -0
  69. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/system/root_responder.js +83 -110
  70. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/system/selection_set.js +7 -4
  71. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/system/sparse_array.js +0 -0
  72. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/system/theme.js +0 -0
  73. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/system/timer.js +0 -0
  74. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/system/utils/rect.js +0 -0
  75. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/utils.js +151 -0
  76. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/controllers/array/array_case.js +0 -0
  77. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/controllers/array/enum_case.js +0 -0
  78. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/controllers/array/null_case.js +0 -0
  79. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/controllers/array/selection_support.js +26 -0
  80. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/controllers/array/single_case.js +0 -0
  81. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/controllers/object/empty_case.js +0 -0
  82. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/controllers/object/multiple_case.js +0 -0
  83. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/controllers/object/single_case.js +0 -0
  84. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/controllers/object/single_enumerable_case.js +0 -0
  85. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/mixins/responder_context.js +0 -0
  86. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/mixins/string.js +0 -0
  87. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/panes/template.js +14 -0
  88. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/builder.js +0 -0
  89. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/core_query/within.js +0 -0
  90. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/json.js +0 -0
  91. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/locale.js +0 -0
  92. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/render_context/begin.js +0 -0
  93. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/render_context/element.js +0 -0
  94. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/render_context/end.js +0 -0
  95. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/render_context/get.js +0 -0
  96. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/render_context/helpers_attr.js +0 -0
  97. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/render_context/helpers_basic.js +0 -0
  98. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/render_context/helpers_className.js +0 -0
  99. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/render_context/helpers_style.js +0 -0
  100. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/render_context/init.js +0 -0
  101. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/render_context/join.js +0 -0
  102. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/render_context/push_text.js +0 -0
  103. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/render_context/tag.js +0 -0
  104. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/render_context/update.js +0 -0
  105. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/root_responder/makeKeyPane.js +0 -0
  106. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/root_responder/makeMainPane.js +0 -0
  107. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/root_responder/makeMenuPane.js +0 -0
  108. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/root_responder/root_responder.js +0 -0
  109. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/root_responder/targetForAction.js +0 -0
  110. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/selection_set/add.js +0 -0
  111. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/selection_set/copy.js +0 -0
  112. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/selection_set/indexSetForSource.js +0 -0
  113. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/selection_set/isEqual.js +0 -0
  114. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/selection_set/remove.js +0 -0
  115. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/sparse_array.js +0 -0
  116. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/theme.js +0 -0
  117. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/timer/invalidate.js +0 -0
  118. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/timer/invokeLater.js +0 -0
  119. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/timer/isPaused.js +0 -0
  120. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/timer/performAction.js +0 -0
  121. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/timer/schedule.js +0 -0
  122. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/utils/normalizeURL.js +0 -0
  123. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/system/utils/offset.js +268 -0
  124. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/system/utils/rect.js +0 -0
  125. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/main_pane.js +43 -0
  126. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/pane/append_remove.js +107 -18
  127. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/pane/child_view.js +20 -0
  128. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/pane/firstResponder.js +0 -0
  129. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/pane/keyPane.js +0 -0
  130. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/pane/layout.js +0 -0
  131. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/pane/sendEvent.js +0 -0
  132. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/template/checkbox_support.js +32 -0
  133. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/template/text_field_support.js +73 -0
  134. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/template_view/collection.js +65 -0
  135. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/template_view/core.js +67 -0
  136. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/template_view/handlebars.js +295 -0
  137. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/animation.js +19 -18
  138. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/build.js +0 -0
  139. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/build_children.js +0 -0
  140. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/clippingFrame.js +0 -0
  141. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/convertFrames.js +0 -0
  142. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/convertLayouts.js +0 -0
  143. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/createChildViews.js +18 -15
  144. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/createLayer.js +0 -0
  145. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/destroyLayer.js +0 -0
  146. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/didAppendToDocument.js +11 -8
  147. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/findLayerInParentLayer.js +0 -0
  148. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/init.js +0 -0
  149. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/insertBefore.js +0 -0
  150. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/isVisible.js +28 -0
  151. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/isVisibleInWindow.js +0 -0
  152. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/keyboard.js +22 -0
  153. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/layer.js +0 -0
  154. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/layoutChildViews.js +0 -0
  155. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layoutDidChange.js +180 -0
  156. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layoutStyle.js +640 -0
  157. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/parentViewDidChange.js +0 -0
  158. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/prepareContext.js +0 -0
  159. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/removeChild.js +0 -0
  160. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/render.js +125 -0
  161. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/render_delegate_support.js +0 -0
  162. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/replaceChild.js +0 -0
  163. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/static_layout.js +21 -0
  164. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/theme.js +0 -0
  165. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/updateLayer.js +0 -0
  166. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/updateLayerLocation.js +0 -0
  167. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/view.js +0 -0
  168. data/lib/frameworks/sproutcore/frameworks/{amber → core_foundation}/tests/views/view/viewDidResize.js +0 -0
  169. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/template/checkbox_support.js +20 -0
  170. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/template/collection.js +99 -0
  171. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/template/text_field_support.js +35 -0
  172. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/template.js +77 -0
  173. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/animation.js +187 -0
  174. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/base.js +1 -0
  175. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/cursor.js +41 -0
  176. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/enabled.js +57 -0
  177. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/keyboard.js +223 -0
  178. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/layout.js +1150 -0
  179. data/lib/frameworks/sproutcore/frameworks/{amber/views → core_foundation/views/view}/layout_style.js +93 -1
  180. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/manipulation.js +489 -0
  181. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/theming.js +362 -0
  182. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/touch.js +67 -0
  183. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/visibility.js +113 -0
  184. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view.js +1280 -0
  185. data/lib/frameworks/sproutcore/frameworks/datastore/models/child_attribute.js +27 -53
  186. data/lib/frameworks/sproutcore/frameworks/datastore/models/children_attribute.js +13 -19
  187. data/lib/frameworks/sproutcore/frameworks/datastore/models/many_attribute.js +1 -0
  188. data/lib/frameworks/sproutcore/frameworks/datastore/models/record.js +183 -71
  189. data/lib/frameworks/sproutcore/frameworks/datastore/models/record_attribute.js +1 -2
  190. data/lib/frameworks/sproutcore/frameworks/datastore/system/child_array.js +26 -46
  191. data/lib/frameworks/sproutcore/frameworks/datastore/system/nested_store.js +25 -4
  192. data/lib/frameworks/sproutcore/frameworks/datastore/system/store.js +139 -21
  193. data/lib/frameworks/sproutcore/frameworks/datastore/tests/data_sources/fixtures.js +6 -1
  194. data/lib/frameworks/sproutcore/frameworks/datastore/tests/integration/cyclical_relationship.js +1 -1
  195. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/nested_records/{parentless.js → data_store.js} +62 -16
  196. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/nested_records/nested_record.js +51 -17
  197. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/nested_records/nested_record_array.js +36 -10
  198. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/nested_records/nested_record_array_complex.js +11 -11
  199. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/nested_records/nested_record_complex.js +8 -8
  200. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/record/normalize.js +2 -2
  201. data/lib/frameworks/sproutcore/frameworks/{foundation/english.lproj → datetime/resources}/strings.js +1 -0
  202. data/lib/frameworks/sproutcore/frameworks/{foundation → datetime}/system/datetime.js +5 -37
  203. data/lib/frameworks/sproutcore/frameworks/{foundation → datetime}/tests/system/datetime.js +1 -0
  204. data/lib/frameworks/sproutcore/frameworks/debug/core.js +1 -1
  205. data/lib/frameworks/sproutcore/frameworks/designer/controllers/designs.js +1 -2
  206. data/lib/frameworks/sproutcore/frameworks/designer/designers/object_designer.js +3 -3
  207. data/lib/frameworks/sproutcore/frameworks/designer/designers/view_designer.js +3 -3
  208. data/lib/frameworks/sproutcore/frameworks/designer/views/designer_drop_target.js +9 -3
  209. data/lib/frameworks/sproutcore/frameworks/desktop/english.lproj/segmented.css +71 -70
  210. data/lib/frameworks/sproutcore/frameworks/desktop/mixins/collection_row_delegate.js +10 -2
  211. data/lib/frameworks/sproutcore/frameworks/desktop/render_delegates/segment.js +4 -19
  212. data/lib/frameworks/sproutcore/frameworks/desktop/render_delegates/segmented.js +33 -14
  213. data/lib/frameworks/sproutcore/frameworks/desktop/system/drag.js +25 -0
  214. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/date_field/ui.js +1 -1
  215. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/segmented/methods.js +25 -103
  216. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/segmented/ui.js +102 -92
  217. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/tab/ui.js +22 -25
  218. data/lib/frameworks/sproutcore/frameworks/desktop/views/collection.js +38 -25
  219. data/lib/frameworks/sproutcore/frameworks/desktop/views/date_field.js +1 -1
  220. data/lib/frameworks/sproutcore/frameworks/desktop/views/scroll.js +1 -1
  221. data/lib/frameworks/sproutcore/frameworks/desktop/views/segment.js +42 -22
  222. data/lib/frameworks/sproutcore/frameworks/desktop/views/segmented.js +513 -230
  223. data/lib/frameworks/sproutcore/frameworks/desktop/views/select_field.js +0 -2
  224. data/lib/frameworks/sproutcore/frameworks/experimental/README.md +23 -0
  225. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/device_motion/README.md +11 -0
  226. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/device_motion/device.js +215 -0
  227. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/device_motion/platform.js +67 -0
  228. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/polymorphism/README.md +34 -0
  229. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/polymorphism/models/polymorphic_single_attribute.js +183 -0
  230. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/polymorphism/models/record.js +23 -0
  231. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/polymorphism/tests/models/polymorphic/single.js +124 -0
  232. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/auto_resize.js +0 -2
  233. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/inner_frame.js +151 -0
  234. data/lib/frameworks/sproutcore/frameworks/foundation/render_delegates/canvas_image.js +27 -17
  235. data/lib/frameworks/sproutcore/frameworks/foundation/system/image_queue.js +6 -3
  236. data/lib/frameworks/sproutcore/frameworks/foundation/system/logger.js +163 -0
  237. data/lib/frameworks/sproutcore/frameworks/foundation/system/user_defaults.js +6 -3
  238. data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/inline_text_field/beginEditing.js +1 -0
  239. data/lib/frameworks/sproutcore/frameworks/foundation/tests/system/logger.js +44 -0
  240. data/lib/frameworks/sproutcore/frameworks/foundation/tests/views/image/ui.js +200 -167
  241. data/lib/frameworks/sproutcore/frameworks/foundation/tests/views/text_field/ui.js +1 -1
  242. data/lib/frameworks/sproutcore/frameworks/foundation/views/image.js +52 -137
  243. data/lib/frameworks/sproutcore/frameworks/foundation/views/text_field.js +7 -11
  244. data/lib/frameworks/sproutcore/frameworks/handlebars/extensions.js +138 -0
  245. data/lib/frameworks/sproutcore/frameworks/handlebars/handlebars.js +1338 -0
  246. data/lib/frameworks/sproutcore/frameworks/runtime/core.js +32 -26
  247. data/lib/frameworks/sproutcore/frameworks/runtime/debug/test_suites/array/base.js +162 -3
  248. data/lib/frameworks/sproutcore/frameworks/runtime/mixins/array.js +10 -5
  249. data/lib/frameworks/sproutcore/frameworks/runtime/mixins/enumerable.js +123 -3
  250. data/lib/frameworks/sproutcore/frameworks/runtime/mixins/observable.js +12 -5
  251. data/lib/frameworks/sproutcore/frameworks/runtime/private/chain_observer.js +50 -13
  252. data/lib/frameworks/sproutcore/frameworks/runtime/system/logger.js +163 -333
  253. data/lib/frameworks/sproutcore/frameworks/runtime/system/object.js +58 -8
  254. data/lib/frameworks/sproutcore/frameworks/runtime/system/run_loop.js +2 -0
  255. data/lib/frameworks/sproutcore/frameworks/runtime/tests/core/itemType.js +9 -6
  256. data/lib/frameworks/sproutcore/frameworks/runtime/tests/core/makeArray.js +15 -1
  257. data/lib/frameworks/sproutcore/frameworks/runtime/tests/mixins/array.js +2 -0
  258. data/lib/frameworks/sproutcore/frameworks/runtime/tests/mixins/observable/chained.js +31 -0
  259. data/lib/frameworks/sproutcore/frameworks/runtime/tests/mixins/observable/observable.js +18 -0
  260. data/lib/frameworks/sproutcore/frameworks/runtime/tests/system/logger.js +31 -143
  261. data/lib/frameworks/sproutcore/frameworks/runtime/tests/system/object/concatenated_properties.js +71 -0
  262. data/lib/frameworks/sproutcore/frameworks/runtime/tests/system/object/enhance.js +177 -0
  263. data/lib/frameworks/sproutcore/frameworks/table/views/table.js +5 -4
  264. data/lib/frameworks/sproutcore/frameworks/table/views/table_header.js +5 -3
  265. data/lib/frameworks/sproutcore/themes/ace/resources/button/button.js +8 -2
  266. data/lib/frameworks/sproutcore/themes/ace/resources/menu/menu.css +2 -3
  267. data/lib/frameworks/sproutcore/themes/ace/resources/segmented/18px/segmented.css +1 -1
  268. data/lib/frameworks/sproutcore/themes/ace/resources/segmented/24px/segmented.css +1 -1
  269. data/lib/frameworks/sproutcore/themes/ace/resources/segmented/30px/segmented.css +1 -1
  270. data/lib/frameworks/sproutcore/themes/ace/resources/segmented/44px/segmented.css +1 -1
  271. data/lib/frameworks/sproutcore/themes/ace/resources/segmented/segmented.css +2 -2
  272. data/lib/frameworks/sproutcore/themes/standard_theme/english.lproj/segmented.css +62 -62
  273. data/lib/gen/html_app/Buildfile +36 -0
  274. data/lib/gen/html_app/README +1 -0
  275. data/lib/gen/html_app/USAGE +15 -0
  276. data/lib/gen/html_app/templates/apps/@target_name@/@target_name@.js +14 -0
  277. data/lib/gen/html_app/templates/apps/@target_name@/resources/images/.gitkeep +0 -0
  278. data/lib/gen/html_app/templates/apps/@target_name@/resources/stylesheets/@target_name@.css +0 -0
  279. data/lib/gen/html_app/templates/apps/@target_name@/resources/templates/@target_name@.handlebars +1 -0
  280. data/lib/gen/html_project/Buildfile +45 -0
  281. data/lib/gen/html_project/INIT +3 -0
  282. data/lib/gen/html_project/README +1 -0
  283. data/lib/gen/html_project/USAGE +2 -0
  284. data/lib/gen/html_project/templates/@filename@/Buildfile +8 -0
  285. data/lib/gen/html_project/templates/@filename@/README +7 -0
  286. data/lib/sproutcore/builders/handlebars.rb +30 -0
  287. data/lib/sproutcore/builders.rb +1 -1
  288. data/lib/sproutcore/helpers/static_helper.rb +3 -3
  289. data/lib/sproutcore/tools/init.rb +25 -9
  290. data/spec/buildtasks/manifest/prepare_build_tasks/handlebars_spec.rb +39 -0
  291. data/spec/fixtures/builder_tests/apps/handlebars_test/template.handlebars +5 -0
  292. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/templates/demo.handlebars +4 -0
  293. data/spec/lib/builders/handlebars_spec.rb +29 -0
  294. data/vendor/chance/lib/chance/imagers/data_url.rb +20 -7
  295. data/vendor/chance/lib/chance/instance.rb +4 -1
  296. data/vendor/chance/lib/chance/parser.rb +31 -31
  297. data/vendor/chance/lib/chance/slicing.rb +38 -8
  298. metadata +195 -225
  299. data/lib/frameworks/sproutcore/frameworks/amber/system/device.js +0 -143
  300. data/lib/frameworks/sproutcore/frameworks/amber/system/utils.js +0 -174
  301. data/lib/frameworks/sproutcore/frameworks/amber/tests/views/main_pane.js +0 -31
  302. data/lib/frameworks/sproutcore/frameworks/amber/tests/views/pane/sendTouchEvent.js +0 -267
  303. data/lib/frameworks/sproutcore/frameworks/amber/tests/views/view/layoutDidChange.js +0 -149
  304. data/lib/frameworks/sproutcore/frameworks/amber/tests/views/view/layoutStyle.js +0 -602
  305. data/lib/frameworks/sproutcore/frameworks/amber/tests/views/view/render.js +0 -115
  306. data/lib/frameworks/sproutcore/frameworks/amber/views/base.js +0 -1
  307. data/lib/frameworks/sproutcore/frameworks/amber/views/view.js +0 -4003
  308. data/lib/frameworks/sproutcore/frameworks/datastore/models/child_record.js +0 -105
@@ -1,4003 +0,0 @@
1
- // ==========================================================================
2
- // Project: SproutCore - JavaScript Application Framework
3
- // Copyright: ©2006-2011 Strobe Inc. and contributors.
4
- // Portions ©2008-2010 Apple Inc. All rights reserved.
5
- // License: Licensed under MIT license (see license.js)
6
- // ==========================================================================
7
-
8
- sc_require('system/browser');
9
- sc_require('system/event');
10
- sc_require('system/cursor');
11
- sc_require('system/responder') ;
12
- sc_require('system/theme');
13
-
14
- sc_require('mixins/string') ;
15
- sc_require('views/base') ;
16
- sc_require('views/layout_style') ;
17
-
18
- /** Select a horizontal layout for various views.*/
19
- SC.LAYOUT_HORIZONTAL = 'sc-layout-horizontal';
20
-
21
- /** Select a vertical layout for various views.*/
22
- SC.LAYOUT_VERTICAL = 'sc-layout-vertical';
23
-
24
- /** @private */
25
- SC._VIEW_DEFAULT_DIMS = 'marginTop marginLeft'.w();
26
-
27
- /**
28
- Layout properties to take up the full width of a parent view.
29
- */
30
- SC.FULL_WIDTH = { left: 0, right: 0 };
31
-
32
- /**
33
- Layout properties to take up the full height of a parent view.
34
- */
35
- SC.FULL_HEIGHT = { top: 0, bottom: 0 };
36
-
37
- /**
38
- Layout properties to center. Note that you must also specify a width and
39
- height for this to work.
40
- */
41
- SC.ANCHOR_CENTER = { centerX: 0, centerY: 0 };
42
-
43
- /**
44
- Layout property for width, height
45
- */
46
-
47
- SC.LAYOUT_AUTO = 'auto';
48
-
49
- /**
50
- Default property to disable or enable by default the contextMenu
51
- */
52
- SC.CONTEXT_MENU_ENABLED = YES;
53
-
54
- /**
55
- Default property to disable or enable if the focus can jump to the address
56
- bar or not.
57
- */
58
- SC.TABBING_ONLY_INSIDE_DOCUMENT = YES;
59
-
60
- /**
61
- Tells the property (when fetched with themed()) to get its value from the renderer (if any).
62
- */
63
- SC.FROM_THEME = "__FROM_THEME__"; // doesn't really matter what it is, so long as it is unique. Readability is a plus.
64
-
65
- /** @private - custom array used for child views */
66
- SC.EMPTY_CHILD_VIEWS_ARRAY = [];
67
- SC.EMPTY_CHILD_VIEWS_ARRAY.needsClone = YES;
68
-
69
- /**
70
- Map to CSS Transforms
71
- */
72
-
73
- SC.CSS_TRANSFORM_MAP = {
74
- rotate: function(val){
75
- return null;
76
- },
77
-
78
- rotateX: function(val){
79
- if (SC.typeOf(val) === SC.T_NUMBER) val += 'deg';
80
- return 'rotateX('+val+')';
81
- },
82
-
83
- rotateY: function(val){
84
- if (SC.typeOf(val) === SC.T_NUMBER) val += 'deg';
85
- return 'rotateY('+val+')';
86
- },
87
-
88
- rotateZ: function(val){
89
- if (SC.typeOf(val) === SC.T_NUMBER) val += 'deg';
90
- return 'rotateZ('+val+')';
91
- },
92
-
93
- scale: function(val){
94
- if (SC.typeOf(val) === SC.T_ARRAY) val = val.join(', ');
95
- return 'scale('+val+')';
96
- }
97
- };
98
-
99
-
100
-
101
- /**
102
- Properties that can be animated
103
- (Hash for faster lookup)
104
- */
105
- SC.ANIMATABLE_PROPERTIES = {
106
- top: YES,
107
- left: YES,
108
- bottom: YES,
109
- right: YES,
110
- width: YES,
111
- height: YES,
112
- centerX: YES,
113
- centerY: YES,
114
- opacity: YES,
115
- scale: YES,
116
- rotate: YES,
117
- rotateX: YES,
118
- rotateY: YES,
119
- rotateZ: YES
120
- };
121
-
122
-
123
-
124
- /**
125
- @class
126
-
127
- Base class for managing a view. Views provide two functions:
128
-
129
- 1. They translate state and events into drawing instructions for the
130
- web browser and
131
-
132
- 2. They act as first responders for incoming keyboard, mouse, and
133
- touch events.
134
-
135
- h2. View Initialization
136
-
137
- When a view is setup, there are several methods you can override that
138
- will be called at different times depending on how your view is created.
139
- Here is a guide to which method you want to override and when:
140
-
141
- - *init:* override this method for any general object setup (such as
142
- observers, starting timers and animations, etc) that you need to happen
143
- everytime the view is created, regardless of whether or not its layer
144
- exists yet.
145
-
146
- - *render:* override this method to generate or update your HTML to reflect
147
- the current state of your view. This method is called both when your view
148
- is first created and later anytime it needs to be updated.
149
-
150
- - *didCreateLayer:* the render() method is used to generate new HTML.
151
- Override this method to perform any additional setup on the DOM you might
152
- need to do after creating the view. For example, if you need to listen
153
- for events.
154
-
155
- - *willDestroyLayer:* if you implement didCreateLayer() to setup event
156
- listeners, you should implement this method as well to remove the same
157
- just before the DOM for your view is destroyed.
158
-
159
- - *updateLayer:* Normally, when a view needs to update its content, it will
160
- re-render the view using the render() method. If you would like to
161
- override this behavior with your own custom updating code, you can
162
- replace updateLayer() with your own implementation instead.
163
-
164
- - *didAppendToDocument:* in theory all DOM setup could be done
165
- in didCreateLayer() as you already have a DOM element instantiated.
166
- However there is cases where the element has to be first appended to the
167
- Document because there is either a bug on the browser or you are using
168
- plugins which objects are not instantiated until you actually append the
169
- element to the DOM. This will allow you to do things like registering
170
- DOM events on flash or quicktime objects.
171
-
172
- @extends SC.Responder
173
- @extends SC.DelegateSupport
174
- @since SproutCore 1.0
175
- */
176
- SC.View.reopen(
177
- /** @scope SC.View.prototype */ {
178
-
179
- concatenatedProperties: 'outlets displayProperties layoutProperties classNames renderMixin didCreateLayerMixin willDestroyLayerMixin'.w(),
180
-
181
- /**
182
- The current pane.
183
- @property {SC.Pane}
184
- */
185
- pane: function() {
186
- var view = this ;
187
- while (view && !view.isPane) { view = view.get('parentView') ; }
188
- return view ;
189
- }.property('parentView').cacheable(),
190
-
191
- /**
192
- The page this view was instantiated from. This is set by the page object
193
- during instantiation.
194
-
195
- @property {SC.Page}
196
- */
197
- page: null,
198
-
199
- /**
200
- If the view is currently inserted into the DOM of a parent view, this
201
- property will point to the parent of the view.
202
- */
203
- parentView: null,
204
-
205
- /**
206
- Optional background color. Will be applied to the view's element if
207
- set. This property is intended for one-off views that need a background
208
- element. If you plan to create many view instances it is probably better
209
- to use CSS.
210
-
211
- @property {String}
212
- */
213
- backgroundColor: null,
214
-
215
- /**
216
- Activates use of brower's static layout. To activate, set this
217
- property to YES.
218
-
219
- @property {Boolean}
220
- */
221
- useStaticLayout: NO,
222
-
223
- // ..........................................................
224
- // THEME SUPPORT
225
- //
226
-
227
- /**
228
- Names which theme this view should use; the theme named by this property
229
- will be set to the view's 'theme' property.
230
-
231
- Themes are identified by their name. In addition to looking for the
232
- theme globally, SproutCore will look for the theme inside 'baseTheme',
233
- which is almost always the parent view's theme.
234
-
235
- If null (the default), the view will set its 'theme' property to
236
- be equal to 'baseTheme'.
237
-
238
- Example: themeName: 'ace'
239
-
240
- @property {String}
241
- */
242
- themeName: null,
243
-
244
- /**
245
- Selects which theme to use as a 'base theme'. If null, the 'baseTheme'
246
- property will be set to the parent's theme. If there is no parent, the theme
247
- named by SC.defaultTheme is used.
248
-
249
- This property is private for the time being.
250
-
251
- @private
252
- @property {String}
253
- */
254
- baseThemeName: null,
255
-
256
- /**
257
- The SC.Theme instance which this view should use to render.
258
-
259
- Note: the actual code for this function is in _themeProperty for backwards-compatibility:
260
- some older views specify a string value for 'theme', which would override this property,
261
- breaking it.
262
-
263
- @property {SC.Theme}
264
- */
265
- theme: function() {
266
- var base = this.get('baseTheme'), themeName = this.get('themeName');
267
-
268
- // find theme, if possible
269
- if (themeName) {
270
- // Note: theme instance "find" function will search every parent
271
- // _except_ global (which is not a parent)
272
- var theme;
273
- if (base) {
274
- theme = base.find(themeName);
275
- if (theme) return theme;
276
- }
277
-
278
- theme = SC.Theme.find(themeName);
279
- if (theme) return theme;
280
-
281
- // Create a new invisible subtheme. This will cause the themeName to
282
- // be applied as a class name.
283
- return base.invisibleSubtheme(themeName);
284
- }
285
-
286
- // can't find anything, return base.
287
- return base;
288
- }.property('baseTheme', 'themeName').cacheable(),
289
-
290
- /**
291
- Detects when the theme changes. Replaces the layer if necessary.
292
-
293
- Also, because
294
- */
295
- _sc_view_themeDidChange: function() {
296
- if (this._lastTheme === this.get('theme')) return;
297
- this._lastTheme = this.get('theme');
298
-
299
- // invalidate child view base themes, if present
300
- var childViews = this.childViews, len = childViews.length, idx;
301
- for (idx = 0; idx < len; idx++) {
302
- childViews[idx].notifyPropertyChange('baseTheme');
303
- }
304
-
305
- if (this.get('layer')) this.replaceLayer();
306
- }.observes('theme'),
307
-
308
- /**
309
- The SC.Theme instance in which the 'theme' property should look for the theme
310
- named by 'themeName'.
311
-
312
- For example, if 'baseTheme' is SC.AceTheme, and 'themeName' is 'popover',
313
- it will look to see if SC.AceTheme has a child theme named 'popover',
314
- and _then_, if it is not found, look globally.
315
-
316
- @private
317
- @property {SC.Theme}
318
- */
319
- baseTheme: function() {
320
- var parent;
321
- var baseThemeName = this.get('baseThemeName');
322
- if (baseThemeName) {
323
- return SC.Theme.find(baseThemeName);
324
- } else {
325
- parent = this.get('parentView');
326
- var theme = parent && parent.get('theme');
327
- return theme || SC.Theme.find(SC.defaultTheme);
328
- }
329
- }.property('baseThemeName', 'parentView').cacheable(),
330
-
331
- /**
332
- * Returns the named property if it is specified on the view, and
333
- * otherwise returns the named constant from the view's theme.
334
- *
335
- * @param {String} property The property on the view.
336
- * @param {String} constantName The name of the constant on the theme.
337
- */
338
- getThemedProperty: function(property, constantName){
339
- var value = this.get(property);
340
- if (value !== undefined) return value;
341
-
342
- var theme = this.get('theme');
343
- if (!theme) return undefined;
344
-
345
- return theme[constantName];
346
- },
347
-
348
- // ..........................................................
349
- // IS ENABLED SUPPORT
350
- //
351
-
352
- /**
353
- Set to true when the item is enabled. Note that changing this value
354
- will alter the isVisibleInWindow property for this view and any
355
- child views as well as to automatically add or remove a 'disabled' CSS
356
- class name.
357
-
358
- This property is observable and bindable.
359
-
360
- @property {Boolean}
361
- */
362
- isEnabled: YES,
363
- isEnabledBindingDefault: SC.Binding.oneWay().bool(),
364
-
365
- /**
366
- Computed property returns YES if the view and all of its parent views
367
- are enabled in the pane. You should use this property when deciding
368
- whether to respond to an incoming event or not.
369
-
370
- This property is not observable.
371
-
372
- @property {Boolean}
373
- */
374
- isEnabledInPane: function() {
375
- var ret = this.get('isEnabled'), pv ;
376
- if (ret && (pv = this.get('parentView'))) ret = pv.get('isEnabledInPane');
377
- return ret ;
378
- }.property('parentView', 'isEnabled'),
379
-
380
- /** @private
381
- Observes the isEnabled property and resigns first responder if set to NO.
382
- This will avoid cases where, for example, a disabled text field retains
383
- its focus rings.
384
-
385
- @observes isEnabled
386
- */
387
- _sc_view_isEnabledDidChange: function(){
388
- if(!this.get('isEnabled') && this.get('isFirstResponder')){
389
- this.resignFirstResponder();
390
- }
391
- }.observes('isEnabled'),
392
-
393
- // ..........................................................
394
- // MULTITOUCH SUPPORT
395
- //
396
- /**
397
- Set to YES if you want to receive touch events for each distinct touch (rather than only
398
- the first touch start and last touch end).
399
- */
400
- acceptsMultitouch: NO,
401
-
402
- /**
403
- Is YES if the view is currently being touched. NO otherwise.
404
- */
405
- hasTouch: NO,
406
-
407
- // ..........................................................
408
- // IS VISIBLE IN WINDOW SUPPORT
409
- //
410
-
411
- /**
412
- The isVisible property determines if the view is shown in the view
413
- hierarchy it is a part of. A view can have isVisible == YES and still have
414
- isVisibleInWindow == NO. This occurs, for instance, when a parent view has
415
- isVisible == NO. Default is YES.
416
-
417
- The isVisible property is considered part of the layout and so changing it
418
- will trigger a layout update.
419
-
420
- @property {Boolean}
421
- */
422
- isVisible: YES,
423
- isVisibleBindingDefault: SC.Binding.bool(),
424
-
425
- /**
426
- YES only if the view and all of its parent views are currently visible
427
- in the window. This property is used to optimize certain behaviors in
428
- the view. For example, updates to the view layer are not performed
429
- if the view until the view becomes visible in the window.
430
- */
431
- isVisibleInWindow: NO,
432
-
433
- /**
434
- By default we don't disable the context menu. Overriding this property
435
- can enable/disable the context menu per view.
436
- */
437
- isContextMenuEnabled: function() {
438
- return SC.CONTEXT_MENU_ENABLED;
439
- }.property(),
440
-
441
- /**
442
- Recomputes the isVisibleInWindow property based on the visibility of the
443
- view and its parent. If the recomputed value differs from the current
444
- isVisibleInWindow state, this method will also call
445
- recomputIsVisibleInWindow() on its child views as well. As an optional
446
- optimization, you can pass the isVisibleInWindow state of the parentView
447
- if you already know it.
448
-
449
- You will not generally need to call or override this method yourself. It
450
- is used by the SC.View hierarchy to relay window visibility changes up
451
- and down the chain.
452
-
453
- @property {Boolean} parentViewIsVisible
454
- @returns {SC.View} receiver
455
- */
456
- recomputeIsVisibleInWindow: function(parentViewIsVisible) {
457
- var previous = this.get('isVisibleInWindow'),
458
- current = this.get('isVisible'),
459
- parentView;
460
-
461
- // isVisibleInWindow = isVisible && parentView.isVisibleInWindow
462
- // this approach only goes up to the parentView if necessary.
463
- if (current) {
464
- // If we weren't passed in 'parentViewIsVisible' (we generally aren't;
465
- // it's an optimization), then calculate it.
466
- if (parentViewIsVisible === undefined) {
467
- parentView = this.get('parentView');
468
- parentViewIsVisible = parentView ? parentView.get('isVisibleInWindow') : NO;
469
- }
470
- current = current && parentViewIsVisible;
471
- }
472
-
473
- // If our visibility has changed, then set the new value and notify our
474
- // child views to update their value.
475
- if (previous !== current) {
476
- this.set('isVisibleInWindow', current);
477
-
478
- var childViews = this.get('childViews'), len = childViews.length, idx;
479
- for(idx=0;idx<len;idx++) {
480
- childViews[idx].recomputeIsVisibleInWindow(current);
481
- }
482
-
483
- // For historical reasons, we'll also layout the child views if
484
- // necessary.
485
- if (current) {
486
- if (this.get('childViewsNeedLayout')) this.invokeOnce(this.layoutChildViewsIfNeeded);
487
- }
488
- else {
489
- // Also, if we were previously visible and were the first responder,
490
- // resign it. This more appropriately belongs in a
491
- // 'isVisibleInWindow' observer or some such helper method because
492
- // this work is not strictly related to computing the visibility, but
493
- // view performance is critical, so avoiding the extra observer is
494
- // worthwhile.
495
- if (this.get('isFirstResponder')) this.resignFirstResponder();
496
- }
497
- }
498
-
499
- // If we're in this function, then that means one of our ancestor views
500
- // changed, or changed its 'isVisibleInWindow' value. That means that if
501
- // we are out of sync with the layer, then we need to update our state
502
- // now.
503
- //
504
- // For example, say we're isVisible=NO, but we have not yet added the
505
- // 'hidden' class to the layer because of the "don't update the layer if
506
- // we're not visible in the window" check. If any of our parent views
507
- // became visible, our layer would incorrectly be shown!
508
- this.updateLayerIfNeeded(YES);
509
-
510
- return this;
511
- },
512
-
513
-
514
- /** @private
515
- Whenever the view’s visibility changes, we need to recompute whether it is
516
- actually visible inside the window (a view is only visible in the window
517
- if it is marked as visibile and its parent view is as well), in addition
518
- to updating the layer accordingly.
519
- */
520
- _sc_isVisibleDidChange: function() {
521
- // 'isVisible' is effectively a displayProperty, but we'll call
522
- // displayDidChange() manually here instead of declaring it as a
523
- // displayProperty because that avoids having two observers on
524
- // 'isVisible'. A single observer is:
525
- // a. More efficient
526
- // b. More correct, because we can guarantee the order of operations
527
- this.displayDidChange();
528
-
529
- this.recomputeIsVisibleInWindow();
530
- }.observes('isVisible'),
531
-
532
-
533
-
534
- // ..........................................................
535
- // CHILD VIEW SUPPORT
536
- //
537
-
538
- /**
539
- Array of child views. You should never edit this array directly unless
540
- you are implementing createChildViews(). Most of the time, you should
541
- use the accessor methods such as appendChild(), insertBefore() and
542
- removeChild().
543
-
544
- @property {Array}
545
- */
546
- childViews: SC.EMPTY_CHILD_VIEWS_ARRAY,
547
-
548
- /**
549
- Insert the view into the the receiver's childNodes array.
550
-
551
- The view will be added to the childNodes array before the beforeView. If
552
- beforeView is null, then the view will be added to the end of the array.
553
- This will also add the view's rootElement DOM node to the receivers
554
- containerElement DOM node as a child.
555
-
556
- If the specified view already belongs to another parent, it will be
557
- removed from that view first.
558
-
559
- @param {SC.View} view
560
- @param {SC.View} beforeView
561
- @returns {SC.View} the receiver
562
- */
563
- insertBefore: function(view, beforeView) {
564
- view.beginPropertyChanges(); // limit notifications
565
-
566
- // remove view from old parent if needed. Also notify views.
567
- if (view.get('parentView')) view.removeFromParent() ;
568
- if (this.willAddChild) this.willAddChild(view, beforeView) ;
569
- if (view.willAddToParent) view.willAddToParent(this, beforeView) ;
570
-
571
- // set parentView of child
572
- view.set('parentView', this);
573
-
574
- // add to childView's array.
575
- var idx, childViews = this.get('childViews') ;
576
- if (childViews.needsClone) this.set(childViews = []);
577
- idx = (beforeView) ? childViews.indexOf(beforeView) : childViews.length;
578
- if (idx<0) idx = childViews.length ;
579
- childViews.insertAt(idx, view) ;
580
-
581
- // The DOM will need some fixing up, note this on the view.
582
- view.parentViewDidChange() ;
583
- view.layoutDidChange() ;
584
- var pane = view.get('pane');
585
- if(pane && pane.get('isPaneAttached')) {
586
- view._notifyDidAppendToDocument();
587
- }
588
-
589
- // notify views
590
- if (this.didAddChild) this.didAddChild(view, beforeView) ;
591
- if (view.didAddToParent) view.didAddToParent(this, beforeView) ;
592
-
593
- view.endPropertyChanges();
594
-
595
- return this ;
596
- },
597
-
598
- /**
599
- Removes the child view from the parent view.
600
-
601
- @param {SC.View} view
602
- @returns {SC.View} receiver
603
- */
604
- removeChild: function(view) {
605
- if (!view) return this; // nothing to do
606
- if (view.parentView !== this) {
607
- throw "%@.removeChild(%@) must belong to parent".fmt(this,view);
608
- }
609
- // notify views
610
- if (view.willRemoveFromParent) view.willRemoveFromParent() ;
611
- if (this.willRemoveChild) this.willRemoveChild(view) ;
612
-
613
- // update parent node
614
- view.set('parentView', null) ;
615
-
616
- // remove view from childViews array.
617
- var childViews = this.get('childViews'),
618
- idx = childViews.indexOf(view) ;
619
- if (idx>=0) childViews.removeAt(idx);
620
-
621
- // The DOM will need some fixing up, note this on the view.
622
- view.parentViewDidChange() ;
623
-
624
- // notify views
625
- if (this.didRemoveChild) this.didRemoveChild(view);
626
- if (view.didRemoveFromParent) view.didRemoveFromParent(this) ;
627
-
628
- return this ;
629
- },
630
-
631
- /**
632
- Removes all children from the parentView.
633
-
634
- @returns {SC.View} receiver
635
- */
636
- removeAllChildren: function() {
637
- var childViews = this.get('childViews'), view ;
638
- while (view = childViews.objectAt(childViews.get('length')-1)) {
639
- this.removeChild(view) ;
640
- }
641
- return this ;
642
- },
643
-
644
- /**
645
- Removes the view from its parentView, if one is found. Otherwise
646
- does nothing.
647
-
648
- @returns {SC.View} receiver
649
- */
650
- removeFromParent: function() {
651
- var parent = this.get('parentView') ;
652
- if (parent) parent.removeChild(this) ;
653
- return this ;
654
- },
655
-
656
- /**
657
- Replace the oldView with the specified view in the receivers childNodes
658
- array. This will also replace the DOM node of the oldView with the DOM
659
- node of the new view in the receivers DOM.
660
-
661
- If the specified view already belongs to another parent, it will be
662
- removed from that view first.
663
-
664
- @param view {SC.View} the view to insert in the DOM
665
- @param view {SC.View} the view to remove from the DOM.
666
- @returns {SC.View} the receiver
667
- */
668
- replaceChild: function(view, oldView) {
669
- // suspend notifications
670
- view.beginPropertyChanges();
671
- oldView.beginPropertyChanges();
672
- this.beginPropertyChanges();
673
-
674
- this.insertBefore(view,oldView).removeChild(oldView) ;
675
-
676
- // resume notifications
677
- this.endPropertyChanges();
678
- oldView.endPropertyChanges();
679
- view.endPropertyChanges();
680
-
681
- return this;
682
- },
683
-
684
- /**
685
- Replaces the current array of child views with the new array of child
686
- views.
687
-
688
- @param {Array} views views you want to add
689
- @returns {SC.View} receiver
690
- */
691
- replaceAllChildren: function(views) {
692
- var len = views.get('length'), idx;
693
-
694
- this.beginPropertyChanges();
695
- this.destroyLayer().removeAllChildren();
696
- for(idx=0;idx<len;idx++) this.appendChild(views.objectAt(idx));
697
- this.replaceLayer();
698
- this.endPropertyChanges();
699
-
700
- return this ;
701
- },
702
-
703
- /**
704
- Appends the specified view to the end of the receivers childViews array.
705
- This is equivalent to calling insertBefore(view, null);
706
-
707
- @param view {SC.View} the view to insert
708
- @returns {SC.View} the receiver
709
- */
710
- appendChild: function(view) {
711
- return this.insertBefore(view, null);
712
- },
713
-
714
- /**
715
- This method is called whenever the receiver's parentView has changed.
716
- The default implementation of this method marks the view's display
717
- location as dirty so that it will update at the end of the run loop.
718
-
719
- You will not usually need to override or call this method yourself, though
720
- if you manually patch the parentView hierarchy for some reason, you should
721
- call this method to notify the view that it's parentView has changed.
722
-
723
- @returns {SC.View} receiver
724
- */
725
- parentViewDidChange: function() {
726
- this.recomputeIsVisibleInWindow() ;
727
-
728
- this.resetBuildState();
729
- this.set('layerLocationNeedsUpdate', YES) ;
730
- this.invokeOnce(this.updateLayerLocationIfNeeded) ;
731
-
732
- // We also need to iterate down through the view hierarchy and invalidate
733
- // all our child view's caches for 'pane', since it could have changed.
734
- //
735
- // Note: In theory we could try to avoid this invalidation if we
736
- // do this only in cases where we "know" the 'pane' value might
737
- // have changed, but those cases are few and far between.
738
-
739
- this._invalidatePaneCacheForSelfAndAllChildViews();
740
-
741
- return this ;
742
- },
743
-
744
- /** @private
745
- We want to cache the 'pane' property, but it's impossible for us to
746
- declare a dependence on all properties that can affect the value. (For
747
- example, if our grandparent gets attached to a new pane, our pane will
748
- have changed.) So when there's the potential for the pane changing, we
749
- need to invalidate the caches for all our child views, and their child
750
- views, and so on.
751
- */
752
- _invalidatePaneCacheForSelfAndAllChildViews: function () {
753
- var childView, childViews = this.get('childViews'),
754
- len = childViews.length, idx ;
755
-
756
- this.notifyPropertyChange('pane');
757
-
758
- for (idx=0; idx<len; ++idx) {
759
- childView = childViews[idx];
760
- if (childView._invalidatePaneCacheForSelfAndAllChildViews) {
761
- childView._invalidatePaneCacheForSelfAndAllChildViews();
762
- }
763
- }
764
- },
765
-
766
- // ..........................................................
767
- // LAYER SUPPORT
768
- //
769
-
770
- /**
771
- Returns the current layer for the view. The layer for a view is only
772
- generated when the view first becomes visible in the window and even
773
- then it will not be computed until you request this layer property.
774
-
775
- If the layer is not actually set on the view itself, then the layer will
776
- be found by calling this.findLayerInParentLayer().
777
-
778
- You can also set the layer by calling set on this property.
779
-
780
- @property {DOMElement} the layer
781
- */
782
- layer: function(key, value) {
783
- if (value !== undefined) {
784
- this._view_layer = value ;
785
-
786
- // no layer...attempt to discover it...
787
- } else {
788
- value = this._view_layer;
789
- if (!value) {
790
- var parent = this.get('parentView');
791
- if (parent) parent = parent.get('layer');
792
- if (parent) {
793
- this._view_layer = value = this.findLayerInParentLayer(parent);
794
- }
795
- parent = null ; // avoid memory leak
796
- }
797
- }
798
- return value ;
799
- }.property('isVisibleInWindow').cacheable(),
800
-
801
- /**
802
- Get a CoreQuery object for this view's layer, or pass in a selector string
803
- to get a CoreQuery object for a DOM node nested within this layer.
804
-
805
- @param {String} sel a CoreQuery-compatible selector string
806
- @returns {SC.CoreQuery} the CoreQuery object for the DOM node
807
- */
808
- $: function(sel) {
809
- var ret, layer = this.get('layer') ;
810
- // note: SC.$([]) returns an empty CoreQuery object. SC.$() would
811
- // return an object selecting the document.
812
- ret = !layer ? SC.$([]) : (sel === undefined) ? SC.$(layer) : SC.$(sel, layer) ;
813
- layer = null ; // avoid memory leak
814
- return ret ;
815
- },
816
-
817
- /**
818
- Returns the DOM element that should be used to hold child views when they
819
- are added/remove via DOM manipulation. The default implementation simply
820
- returns the layer itself. You can override this to return a DOM element
821
- within the layer.
822
-
823
- @property {DOMElement} the container layer
824
- */
825
- containerLayer: function() {
826
- return this.get('layer') ;
827
- }.property('layer').cacheable(),
828
-
829
- /**
830
- The ID to use when trying to locate the layer in the DOM. If you do not
831
- set the layerId explicitly, then the view's GUID will be used instead.
832
- This ID must be set at the time the view is created.
833
-
834
- @property {String}
835
- @readOnly
836
- */
837
- layerId: function(key, value) {
838
- if (value) this._layerId = value;
839
- if (this._layerId) return this._layerId;
840
- return SC.guidFor(this) ;
841
- }.property().cacheable(),
842
-
843
- _lastLayerId: null,
844
-
845
- /**
846
- Handles changes in the layer id.
847
- */
848
- layerIdDidChange: function() {
849
- var layer = this.get("layer"),
850
- lid = this.get("layerId"),
851
- lastId = this._lastLayerId;
852
- if (lid !== lastId) {
853
- // if we had an earlier one, remove from view hash.
854
- if (lastId && SC.View.views[lastId] === this) {
855
- delete SC.View.views[lastId];
856
- }
857
-
858
- // set the current one as the new old one
859
- this._lastLayerId = lid;
860
-
861
- // and add the new one
862
- SC.View.views[lid] = this;
863
-
864
- // and finally, set the actual layer id.
865
- if (layer) layer.id = lid;
866
- }
867
- }.observes("layerId"),
868
-
869
- /**
870
- Attempts to discover the layer in the parent layer. The default
871
- implementation looks for an element with an ID of layerId (or the view's
872
- guid if layerId is null). You can override this method to provide your
873
- own form of lookup. For example, if you want to discover your layer using
874
- a CSS class name instead of an ID.
875
-
876
- @param {DOMElement} parentLayer the parent's DOM layer
877
- @returns {DOMElement} the discovered layer
878
- */
879
- findLayerInParentLayer: function(parentLayer) {
880
- var layerId = this.get('layerId'),
881
- node, i, ilen, childNodes, elem, usedQuerySelector;
882
-
883
- // first, let's try the fast path...
884
- elem = document.getElementById(layerId) ;
885
-
886
- // TODO: use code generation to only really do this check on IE
887
- if (SC.browser.msie && elem && elem.id !== layerId) elem = null;
888
-
889
- // if no element was found the fast way, search down the parentLayer for
890
- // the element. This code should not be invoked very often. Usually a
891
- // DOM element will be discovered by the first method above.
892
- // This code uses a BFS algorithm as is expected to find the layer right
893
- // below the parent.
894
- if (!elem) {
895
- elem = parentLayer.firstChild ;
896
- var q = [];
897
- q.push(parentLayer);
898
- while (q.length !==0) {
899
- node = q.shift();
900
- if (node.id===layerId) {
901
- return node;
902
- }
903
- childNodes = node.childNodes;
904
- for (i=0, ilen=childNodes.length; i < ilen; ++i) {
905
- q.push(childNodes[i]);
906
- }
907
- }
908
- elem = null;
909
- }
910
-
911
- return elem;
912
- },
913
-
914
- /**
915
- Returns YES if the receiver is a subview of a given view or if it’s
916
- identical to that view. Otherwise, it returns NO.
917
-
918
- @property {SC.View} view
919
- */
920
- isDescendantOf: function(view) {
921
- var parentView = this.get('parentView');
922
-
923
- if(this===view) return YES;
924
- else if(parentView) return parentView.isDescendantOf(view);
925
- else return NO;
926
- },
927
-
928
- /**
929
- This method is invoked whenever a display property changes. It will set
930
- the layerNeedsUpdate method to YES. If you need to perform additional
931
- setup whenever the display changes, you can override this method as well.
932
-
933
- @returns {SC.View} receiver
934
- */
935
- displayDidChange: function() {
936
- this.set('layerNeedsUpdate', YES) ;
937
- return this;
938
- },
939
-
940
- /**
941
- Setting this property to YES will cause the updateLayerIfNeeded method to
942
- be invoked at the end of the runloop. You can also force a view to update
943
- sooner by calling updateLayerIfNeeded() directly. The method will update
944
- the layer only if this property is YES.
945
-
946
- @property {Boolean}
947
- @test in updateLayer
948
- */
949
- layerNeedsUpdate: NO,
950
-
951
- /** @private
952
- Schedules the updateLayerIfNeeded method to run at the end of the runloop
953
- if layerNeedsUpdate is set to YES.
954
- */
955
- _view_layerNeedsUpdateDidChange: function() {
956
- if (this.get('layerNeedsUpdate')) {
957
- this.invokeOnce(this.updateLayerIfNeeded) ;
958
- }
959
- }.observes('layerNeedsUpdate'),
960
-
961
- /**
962
- Updates the layer only if the view is visible onscreen and if
963
- layerNeedsUpdate is set to YES. Normally you will not invoke this method
964
- directly. Instead you set the layerNeedsUpdate property to YES and this
965
- method will be called once at the end of the runloop.
966
-
967
- If you need to update view's layer sooner than the end of the runloop, you
968
- can call this method directly. If your view is not visible in the window
969
- but you want it to update anyway, then call this method, passing YES for
970
- the 'skipIsVisibleInWindowCheck' parameter.
971
-
972
- You should not override this method. Instead override updateLayer() or
973
- render().
974
-
975
- @returns {SC.View} receiver
976
- @test in updateLayer
977
- */
978
- updateLayerIfNeeded: function(skipIsVisibleInWindowCheck) {
979
- var needsUpdate = this.get('layerNeedsUpdate'),
980
- shouldUpdate = needsUpdate && (skipIsVisibleInWindowCheck || this.get('isVisibleInWindow'));
981
- if (shouldUpdate) {
982
- // only update a layer if it already exists
983
- if (this.get('layer')) {
984
- this.beginPropertyChanges() ;
985
- this.set('layerNeedsUpdate', NO) ;
986
- this.updateLayer() ;
987
- this.endPropertyChanges() ;
988
- }
989
- }
990
-
991
- return this ;
992
- },
993
-
994
- /**
995
- This is the core method invoked to update a view layer whenever it has
996
- changed. This method simply creates a render context focused on the
997
- layer element and then calls your render() method.
998
-
999
- You will not usually call or override this method directly. Instead you
1000
- should set the layerNeedsUpdate property to YES to cause this method to
1001
- run at the end of the run loop, or you can call updateLayerIfNeeded()
1002
- to force the layer to update immediately.
1003
-
1004
- Instead of overriding this method, consider overidding the render() method
1005
- instead, which is called both when creating and updating a layer. If you
1006
- do not want your render() method called when updating a layer, then you
1007
- should override this method instead.
1008
-
1009
- @param optionalContext provided only for backwards-compatibility.
1010
-
1011
- @returns {SC.View} receiver
1012
- */
1013
- updateLayer: function(optionalContext) {
1014
- var mixins, idx, len, hasLegacyRenderMethod;
1015
-
1016
- var context = optionalContext || this.renderContext(this.get('layer')) ;
1017
- this._renderLayerSettings(context, NO);
1018
-
1019
- // If the render method takes two parameters, we assume that it is a
1020
- // legacy implementation that takes context and firstTime. If it has only
1021
- // one parameter, we assume it is the render delegates style that requires
1022
- // only context. Note that, for backwards compatibility, the default
1023
- // SC.View implementation of render uses the old style.
1024
- hasLegacyRenderMethod = !this.update;
1025
- // Call render with firstTime set to NO to indicate an update, rather than
1026
- // full re-render, should be performed.
1027
- if (hasLegacyRenderMethod) {
1028
- this.render(context, NO);
1029
- }
1030
- else {
1031
- this.update(context.$());
1032
- }
1033
- if (mixins = this.renderMixin) {
1034
- len = mixins.length;
1035
- for(idx=0; idx<len; ++idx) mixins[idx].call(this, context, NO) ;
1036
- }
1037
-
1038
- context.update() ;
1039
- if (context._innerHTMLReplaced) {
1040
- var pane = this.get('pane');
1041
- if(pane && pane.get('isPaneAttached')) {
1042
- this._notifyDidAppendToDocument();
1043
- }
1044
- }
1045
-
1046
- // If this view uses static layout, then notify that the frame (likely)
1047
- // changed.
1048
- if (this.useStaticLayout) this.viewDidResize();
1049
-
1050
- if (this.didUpdateLayer) this.didUpdateLayer(); // call to update DOM
1051
- if(this.designer && this.designer.viewDidUpdateLayer) {
1052
- this.designer.viewDidUpdateLayer(); //let the designer know
1053
- }
1054
- return this ;
1055
- },
1056
-
1057
- /**
1058
- Creates a new renderContext with the passed tagName or element. You
1059
- can override this method to provide further customization to the context
1060
- if needed. Normally you will not need to call or override this method.
1061
-
1062
- @returns {SC.RenderContext}
1063
- */
1064
- renderContext: function(tagNameOrElement) {
1065
- return SC.RenderContext(tagNameOrElement) ;
1066
- },
1067
-
1068
- /**
1069
- Creates the layer by creating a renderContext and invoking the view's
1070
- render() method. This will only create the layer if the layer does not
1071
- already exist.
1072
-
1073
- When you create a layer, it is expected that your render() method will
1074
- also render the HTML for all child views as well. This method will
1075
- notify the view along with any of its childViews that its layer has been
1076
- created.
1077
-
1078
- @returns {SC.View} receiver
1079
- */
1080
- createLayer: function() {
1081
- if (this.get('layer')) return this ; // nothing to do
1082
-
1083
- var context = this.renderContext(this.get('tagName')) ;
1084
-
1085
- // now prepare the content like normal.
1086
- this.renderToContext(context) ;
1087
- this.set('layer', context.element()) ;
1088
-
1089
- // now notify the view and its child views..
1090
- this._notifyDidCreateLayer() ;
1091
-
1092
- return this ;
1093
- },
1094
-
1095
- /** @private -
1096
- Invokes the receivers didCreateLayer() method if it exists and then
1097
- invokes the same on all child views.
1098
- */
1099
- _notifyDidCreateLayer: function() {
1100
- this.notifyPropertyChange("layer");
1101
- if (this.didCreateLayer) this.didCreateLayer() ;
1102
-
1103
- // Animation prep
1104
- if (SC.platform.supportsCSSTransitions) this.resetAnimation();
1105
-
1106
- // and notify others
1107
- var mixins = this.didCreateLayerMixin, len, idx,
1108
- childViews = this.get('childViews'),
1109
- childView;
1110
- if (mixins) {
1111
- len = mixins.length ;
1112
- for (idx=0; idx<len; ++idx) mixins[idx].call(this) ;
1113
- }
1114
-
1115
- len = childViews.length ;
1116
- for (idx=0; idx<len; ++idx) {
1117
- childView = childViews[idx];
1118
- if (!childView) continue;
1119
-
1120
- // A parent view creating a layer might result in the creation of a
1121
- // child view's DOM node being created via a render context without
1122
- // createLayer() being invoked on the child. In such cases, if anyone
1123
- // had requested 'layer' and it was cached as null, we need to
1124
- // invalidate it.
1125
- childView.notifyPropertyChange('layer');
1126
-
1127
- childView._notifyDidCreateLayer() ;
1128
- }
1129
- },
1130
-
1131
- /**
1132
- Destroys any existing layer along with the layer for any child views as
1133
- well. If the view does not currently have a layer, then this method will
1134
- do nothing.
1135
-
1136
- If you implement willDestroyLayer() on your view or if any mixins
1137
- implement willDestroLayerMixin(), then this method will be invoked on your
1138
- view before your layer is destroyed to give you a chance to clean up any
1139
- event handlers, etc.
1140
-
1141
- If you write a willDestroyLayer() handler, you can assume that your
1142
- didCreateLayer() handler was called earlier for the same layer.
1143
-
1144
- Normally you will not call or override this method yourself, but you may
1145
- want to implement the above callbacks when it is run.
1146
-
1147
- @returns {SC.View} receiver
1148
- */
1149
- destroyLayer: function() {
1150
- var layer = this.get('layer') ;
1151
- if (layer) {
1152
-
1153
- // Now notify the view and its child views. It will also set the
1154
- // layer property to null.
1155
- this._notifyWillDestroyLayer() ;
1156
-
1157
- // do final cleanup
1158
- if (layer.parentNode) layer.parentNode.removeChild(layer) ;
1159
- layer = null ;
1160
- }
1161
- return this ;
1162
- },
1163
-
1164
- /**
1165
- Destroys and recreates the current layer. This can be more efficient than
1166
- modifying individual child views.
1167
-
1168
- @returns {SC.View} receiver
1169
- */
1170
- replaceLayer: function() {
1171
- this.destroyLayer();
1172
- //this.set('layerLocationNeedsUpdate', YES) ;
1173
- this.invokeOnce(this.updateLayerLocation) ;
1174
- },
1175
-
1176
- /** @private -
1177
- Invokes willDestroyLayer() on view and child views. Then sets layer to
1178
- null for receiver.
1179
- */
1180
- _notifyWillDestroyLayer: function() {
1181
- if (this.willDestroyLayer) this.willDestroyLayer() ;
1182
- var mixins = this.willDestroyLayerMixin, len, idx,
1183
- childViews = this.get('childViews') ;
1184
- if (mixins) {
1185
- len = mixins.length ;
1186
- for (idx=0; idx<len; ++idx) mixins[idx].call(this) ;
1187
- }
1188
-
1189
- len = childViews.length ;
1190
- for (idx=0; idx<len; ++idx) childViews[idx]._notifyWillDestroyLayer() ;
1191
-
1192
- this.set('layer', null) ;
1193
- },
1194
-
1195
-
1196
-
1197
- /**
1198
- @private
1199
-
1200
- Renders to a context.
1201
- Rendering only happens for the initial rendering. Further updates happen in updateLayer,
1202
- and are not done to contexts, but to layers.
1203
- Note: You should not generally override nor directly call this method. This method is only
1204
- called by createLayer to set up the layer initially, and by renderChildViews, to write to
1205
- a context.
1206
-
1207
- @param {SC.RenderContext} context the render context.
1208
- @param {Boolean} firstTime Provided for compatibility when rendering legacy views only.
1209
- */
1210
- renderToContext: function(context, firstTime) {
1211
- var hasLegacyRenderMethod, mixins, idx, len;
1212
-
1213
- this.beginPropertyChanges() ;
1214
- this.set('layerNeedsUpdate', NO) ;
1215
-
1216
- if (SC.none(firstTime)) firstTime = YES;
1217
-
1218
- this._renderLayerSettings(context, firstTime);
1219
-
1220
- // If the render method takes two parameters, we assume that it is a
1221
- // legacy implementation that takes context and firstTime. If it has only
1222
- // one parameter, we assume it is the render delegates style that requires
1223
- // only context. Note that, for backwards compatibility, the default
1224
- // SC.View implementation of render uses the old style.
1225
- hasLegacyRenderMethod = !this.update;
1226
-
1227
- // Let the render method handle rendering. If we have a render delegate
1228
- // object set, it will be used there.
1229
- if (hasLegacyRenderMethod) {
1230
- this.render(context, firstTime);
1231
- }
1232
- // This view implements the render delegate protocol.
1233
- else {
1234
- if (firstTime) {
1235
- this.render(context);
1236
- } else {
1237
- this.update(context.$());
1238
- }
1239
- }
1240
-
1241
- if (mixins = this.renderMixin) {
1242
- len = mixins.length;
1243
- for(idx=0; idx<len; ++idx) mixins[idx].call(this, context, firstTime) ;
1244
- }
1245
-
1246
- this.endPropertyChanges() ;
1247
- },
1248
-
1249
- _renderLayerSettings: function(context, firstTime) {
1250
- context.resetClassNames();
1251
- context.resetStyles();
1252
-
1253
- var theme = this.get('theme');
1254
- var themeClassNames = theme.classNames, idx, len = themeClassNames.length;
1255
-
1256
- for (idx = 0; idx < len; idx++) {
1257
- context.addClass(themeClassNames[idx]);
1258
- }
1259
-
1260
- var renderDelegate = this.get('renderDelegate');
1261
- if (renderDelegate && renderDelegate.name) {
1262
- context.addClass(renderDelegate.name);
1263
- }
1264
-
1265
- context.addClass(this.get('classNames'));
1266
-
1267
- var cursor = this.get('cursor');
1268
- if (cursor) context.addClass(cursor.get('className'));
1269
-
1270
- if (this.get('isTextSelectable')) context.addClass('allow-select');
1271
- if (!this.get('isEnabled')) context.addClass('disabled');
1272
- if (!this.get('isVisible')) context.addClass('hidden');
1273
- if (this.get('isFirstResponder')) context.addClass('focus');
1274
- if (this.get('useStaticLayout')) context.addClass('sc-static-layout');
1275
-
1276
- context.id(this.get('layerId'));
1277
- context.attr('role', this.get('ariaRole'));
1278
- if (!this.get('isEnabled')) context.attr('aria-disabled', 'true');
1279
- if (this.get('backgroundColor')) {
1280
- context.css('backgroundColor', this.get('backgroundColor'));
1281
- }
1282
-
1283
- this.renderLayout(context, firstTime);
1284
- },
1285
-
1286
- /**
1287
- @private
1288
-
1289
- Invoked by createLayer() and updateLayer() to actually render a context.
1290
- This method calls the render() method on your view along with any
1291
- renderMixin() methods supplied by mixins you might have added.
1292
-
1293
- You should not override this method directly. Nor should you call it. It is OLD.
1294
-
1295
- @param {SC.RenderContext} context the render context
1296
- @param {Boolean} firstTime YES if this is creating a layer
1297
- @returns {void}
1298
- */
1299
- prepareContext: function(context, firstTime) {
1300
- // eventually, firstTime will be removed because it is ugly.
1301
- // for now, we will sense whether we are doing things the ugly way or not.
1302
- // if ugly, we will allow updates through.
1303
- if (SC.none(firstTime)) firstTime = YES; // the GOOD code path :)
1304
- if (firstTime) {
1305
- this.renderToContext(context);
1306
- } else {
1307
- this.updateLayer(context);
1308
- }
1309
- },
1310
-
1311
- /**
1312
- [RO] Pass this object as the data source for render delegates. This proxy object
1313
- for the view relays requests for properties like 'title' to 'displayTitle'
1314
- as necessary.
1315
-
1316
- If you ever communicate with your view's render delegate, you should pass this
1317
- object as the data source.
1318
-
1319
- The proxy that forwards RenderDelegate requests for properties to the view,
1320
- handling display*, keeps track of the delegate's state, etc.
1321
- */
1322
- renderDelegateProxy: function() {
1323
- return SC.View._RenderDelegateProxy.createForView(this);
1324
- }.property('renderDelegate').cacheable(),
1325
-
1326
- /**
1327
- Your render method should invoke this method to render any child views,
1328
- especially if this is the first time the view will be rendered. This will
1329
- walk down the childView chain, rendering all of the children in a nested
1330
- way.
1331
-
1332
- @param {SC.RenderContext} context the context
1333
- @param {Boolean} firstName true if the layer is being created
1334
- @returns {SC.RenderContext} the render context
1335
- @test in render
1336
- */
1337
- renderChildViews: function(context, firstTime) {
1338
- var cv = this.get('childViews'), len = cv.length, idx, view ;
1339
- for (idx=0; idx<len; ++idx) {
1340
- view = cv[idx] ;
1341
- if (!view) continue;
1342
- context = context.begin(view.get('tagName')) ;
1343
- view.renderToContext(context, firstTime);
1344
- context = context.end() ;
1345
- }
1346
- return context;
1347
- },
1348
-
1349
- /**
1350
- The object to which rendering and updating the HTML representation of this
1351
- view should be delegated.
1352
-
1353
- By default, views are responsible for creating their own HTML
1354
- representation. In some cases, however, you may want to create an object
1355
- that is responsible for rendering all views of a certain type. For example,
1356
- you may want rendering of SC.ButtonView to be controlled by an object that
1357
- is specific to the current theme.
1358
-
1359
- By setting a render delegate, the render and update methods will be called
1360
- on that object instead of the view itself.
1361
-
1362
- For your convenience, the view will provide its displayProperties to the
1363
- RenderDelegate. In some cases, you may have a conflict between the RenderDelegate's
1364
- API and your view's. For instance, you may have a 'value' property that is
1365
- any number, but the render delegate expects a percentage. Make a 'displayValue'
1366
- property, add _it_ to displayProperties instead of 'value', and the Render Delegate
1367
- will automatically use that when it wants to find 'value.'
1368
-
1369
- You can also set the render delegate by using the 'renderDelegateName' property.
1370
-
1371
- @property {Object}
1372
- */
1373
- renderDelegate: function(key, value) {
1374
- if (value) this._setRenderDelegate = value;
1375
- if (this._setRenderDelegate) return this._setRenderDelegate;
1376
-
1377
- // If this view does not have a render delegate but has
1378
- // renderDelegateName set, try to retrieve the render delegate from the
1379
- // theme.
1380
- var renderDelegateName = this.get('renderDelegateName'), renderDelegate;
1381
-
1382
- if (renderDelegateName) {
1383
- renderDelegate = this.get('theme')[renderDelegateName];
1384
- if (!renderDelegate) {
1385
- throw "%@: Unable to locate render delegate \"%@\" in theme.".fmt(this, renderDelegateName);
1386
- }
1387
-
1388
- return renderDelegate;
1389
- }
1390
-
1391
- return null;
1392
- }.property('renderDelegateName', 'theme'),
1393
-
1394
- /**
1395
- The name of the property of the current theme that contains the render
1396
- delegate to use for this view.
1397
-
1398
- By default, views are responsible for creating their own HTML
1399
- representation. You can tell the view to instead delegate rendering to the
1400
- theme by setting this property to the name of the corresponding property
1401
- of the theme.
1402
-
1403
- For example, to tell the view that it should render using the
1404
- SC.ButtonView render delegate, set this property to
1405
- 'buttonRenderDelegate'. When the view is created, it will retrieve the
1406
- buttonRenderDelegate property from its theme and set the renderDelegate
1407
- property to that object.
1408
- */
1409
- renderDelegateName: null,
1410
-
1411
- /**
1412
- Invoked whenever your view needs to create its HTML representation.
1413
-
1414
- You will normally override this method in your subclassed views to
1415
- provide whatever drawing functionality you will need in order to
1416
- render your content.
1417
-
1418
- This method is usually only called once per view. After that, the update
1419
- method will be called to allow you to update the existing HTML
1420
- representation.
1421
-
1422
-
1423
- The default implementation of this method calls renderChildViews().
1424
-
1425
- For backwards compatibility, this method will also call the appropriate
1426
- method on a render delegate object, if your view has one.
1427
-
1428
- @param {SC.RenderContext} context the render context
1429
- @returns {void}
1430
- */
1431
- render: function(context, firstTime) {
1432
- var renderDelegate = this.get('renderDelegate');
1433
-
1434
- if (renderDelegate) {
1435
- if (firstTime) {
1436
- renderDelegate.render(this.get('renderDelegateProxy'), context);
1437
- } else {
1438
- renderDelegate.update(this.get('renderDelegateProxy'), context.$());
1439
- }
1440
- }
1441
-
1442
- if (firstTime) this.renderChildViews(context, firstTime);
1443
- },
1444
-
1445
-
1446
- /** @private -
1447
- Invokes the receivers didAppendLayerToDocument() method if it exists and
1448
- then invokes the same on all child views.
1449
- */
1450
-
1451
- _notifyDidAppendToDocument: function() {
1452
- if (this.didAppendToDocument) this.didAppendToDocument();
1453
-
1454
- var i=0, child, childLen, children = this.get('childViews');
1455
- for(i=0, childLen=children.length; i<childLen; i++) {
1456
- child = children[i];
1457
- if(child._notifyDidAppendToDocument){
1458
- child._notifyDidAppendToDocument();
1459
- }
1460
- }
1461
- },
1462
-
1463
- childViewsObserver: function(){
1464
- var childViews = this.get('childViews'), i, iLen, child;
1465
- for(i=0, iLen = childViews.length; i<iLen; i++){
1466
- child = childViews[i];
1467
- if(child._notifyDidAppendToDocument){
1468
- child._notifyDidAppendToDocument();
1469
- }
1470
- }
1471
- }.observes('childViews'),
1472
-
1473
- // ..........................................................
1474
- // STANDARD RENDER PROPERTIES
1475
- //
1476
-
1477
- /**
1478
- Tag name for the view's outer element. The tag name is only used when
1479
- a layer is first created. If you change the tagName for an element, you
1480
- must destroy and recreate the view layer.
1481
-
1482
- @property {String}
1483
- */
1484
- tagName: 'div',
1485
-
1486
- /**
1487
- The WAI-ARIA role of the control represented by this view. For example, a
1488
- button may have a role of type 'button', or a pane may have a role of
1489
- type 'alertdialog'. This property is used by assistive software to help
1490
- visually challenged users navigate rich web applications.
1491
-
1492
- The full list of valid WAI-ARIA roles is available at:
1493
- http://www.w3.org/TR/wai-aria/roles#roles_categorization
1494
-
1495
- @property {String}
1496
- */
1497
- ariaRole: null,
1498
-
1499
- /**
1500
- Standard CSS class names to apply to the view's outer element. This
1501
- property automatically inherits any class names defined by the view's
1502
- superclasses as well.
1503
-
1504
- @property {Array}
1505
- */
1506
- classNames: ['sc-view'],
1507
-
1508
- /**
1509
- Tool tip property that will be set to the title attribute on the HTML
1510
- rendered element.
1511
-
1512
- @property {String}
1513
- */
1514
- toolTip: null,
1515
-
1516
- /**
1517
- The computed tooltip. This is generated by localizing the toolTip
1518
- property if necessary.
1519
-
1520
- @property {String}
1521
- */
1522
- displayToolTip: function() {
1523
- var ret = this.get('toolTip');
1524
- return (ret && this.get('localize')) ? ret.loc() : (ret || '');
1525
- }.property('toolTip','localize').cacheable(),
1526
-
1527
- /**
1528
- Determines if the user can select text within the view. Normally this is
1529
- set to NO to disable text selection. You should set this to YES if you
1530
- are creating a view that includes editable text. Otherwise, settings this
1531
- to YES will probably make your controls harder to use and it is not
1532
- recommended.
1533
-
1534
- @property {Boolean}
1535
- @readOnly
1536
- */
1537
- isTextSelectable: NO,
1538
-
1539
- /**
1540
- You can set this array to include any properties that should immediately
1541
- invalidate the display. The display will be automatically invalidated
1542
- when one of these properties change.
1543
-
1544
- These are the properties that will be visible to any Render Delegate.
1545
- When the RenderDelegate asks for a property it needs, the view checks the
1546
- displayProperties array. It first looks for the property name prefixed
1547
- by 'display'; for instance, if the render delegate needs a 'title',
1548
- the view will attempt to find 'displayTitle'. If there is no 'displayTitle'
1549
- in displayProperties, the view will then try 'title'. If 'title' is not
1550
- in displayProperties either, an error will be thrown.
1551
-
1552
- This allows you to avoid collisions between your view's API and the Render
1553
- Delegate's API.
1554
-
1555
- Implementation note: 'isVisible' is also effectively a display property,
1556
- but it is not declared as such because the same effect is implemented
1557
- inside _sc_isVisibleDidChange(). This avoids having two observers on
1558
- 'isVisible', which is:
1559
- a. More efficient
1560
- b. More correct, because we can guarantee the order of operations
1561
-
1562
- @property {Array}
1563
- @readOnly
1564
- */
1565
- displayProperties: ['isFirstResponder'],
1566
-
1567
- /**
1568
- You can set this to an SC.Cursor instance; whenever that SC.Cursor's
1569
- 'cursorStyle' changes, the cursor for this view will automatically
1570
- be updated to match. This allows you to coordinate the cursors of
1571
- many views by making them all share the same cursor instance.
1572
-
1573
- For example, SC.SplitView uses this ensure that it and all of its
1574
- children have the same cursor while dragging, so that whether you are
1575
- hovering over the divider or another child of the split view, the
1576
- proper cursor is visible.
1577
-
1578
- @property {SC.Cursor String}
1579
- */
1580
- cursor: function(key, value) {
1581
- var parent;
1582
-
1583
- if (value) this._setCursor = value;
1584
- if (this._setCursor !== undefined) return this._setCursor;
1585
-
1586
- parent = this.get('parentView');
1587
- if (this.get('shouldInheritCursor') && parent) {
1588
- return parent.get('cursor');
1589
- }
1590
-
1591
- return null;
1592
- }.property('parentView', 'shouldInheritCursor').cacheable(),
1593
-
1594
- /**
1595
- A child view without a cursor of its own inherits its parent's cursor by
1596
- default. Set this to NO to prevent this behavior.
1597
-
1598
- @property {Boolean}
1599
- */
1600
- shouldInheritCursor: YES,
1601
-
1602
- // ..........................................................
1603
- // LAYER LOCATION
1604
- //
1605
-
1606
- /**
1607
- Set to YES when the view's layer location is dirty. You can call
1608
- updateLayerLocationIfNeeded() to clear this flag if it is set.
1609
-
1610
- @property {Boolean}
1611
- */
1612
- layerLocationNeedsUpdate: NO,
1613
-
1614
- /**
1615
- Calls updateLayerLocation(), but only if the view's layer location
1616
- currently needs to be updated. This method is called automatically at
1617
- the end of a run loop if you have called parentViewDidChange() at some
1618
- point.
1619
-
1620
- @property {Boolean} force This property is ignored.
1621
- @returns {SC.View} receiver
1622
- @test in updateLayerLocation
1623
- */
1624
- updateLayerLocationIfNeeded: function(force) {
1625
- if (this.get('layerLocationNeedsUpdate')) {
1626
- this.updateLayerLocation() ;
1627
- }
1628
- return this ;
1629
- },
1630
-
1631
- /**
1632
- This method is called when a view changes its location in the view
1633
- hierarchy. This method will update the underlying DOM-location of the
1634
- layer so that it reflects the new location.
1635
-
1636
- @returns {SC.View} receiver
1637
- */
1638
- updateLayerLocation: function() {
1639
- // collect some useful value
1640
- // if there is no node for some reason, just exit
1641
- var node = this.get('layer'),
1642
- parentView = this.get('parentView'),
1643
- parentNode = parentView ? parentView.get('containerLayer') : null ;
1644
-
1645
- // remove node from current parentNode if the node does not match the new
1646
- // parent node.
1647
- if (node && node.parentNode && node.parentNode !== parentNode) {
1648
- node.parentNode.removeChild(node);
1649
- }
1650
-
1651
- // CASE 1: no new parentView. just remove from parent (above).
1652
- if (!parentView) {
1653
- if (node && node.parentNode) node.parentNode.removeChild(node);
1654
-
1655
- // CASE 2: parentView has no layer, view has layer. destroy layer
1656
- // CASE 3: parentView has no layer, view has no layer, nothing to do
1657
- } else if (!parentNode) {
1658
- if (node) {
1659
- if (node.parentNode) node.parentNode.removeChild(node);
1660
- this.destroyLayer();
1661
- }
1662
-
1663
- // CASE 4: parentView has layer, view has no layer. create layer & add
1664
- // CASE 5: parentView has layer, view has layer. move layer
1665
- } else {
1666
- if (!node) {
1667
- this.createLayer() ;
1668
- node = this.get('layer') ;
1669
- if (!node) return; // can't do anything without a node.
1670
- }
1671
-
1672
- var siblings = parentView.get('childViews'),
1673
- nextView = siblings.objectAt(siblings.indexOf(this)+1),
1674
- nextNode = (nextView) ? nextView.get('layer') : null ;
1675
-
1676
- // before we add to parent node, make sure that the nextNode exists...
1677
- if (nextView && (!nextNode || nextNode.parentNode!==parentNode)) {
1678
- nextView.updateLayerLocationIfNeeded() ;
1679
- nextNode = nextView.get('layer') ;
1680
- }
1681
-
1682
- // add to parentNode if needed.
1683
- if ((node.parentNode!==parentNode) || (node.nextSibling!==nextNode)) {
1684
- parentNode.insertBefore(node, nextNode) ;
1685
- }
1686
- }
1687
-
1688
- parentNode = parentView = node = nextNode = null ; // avoid memory leaks
1689
-
1690
- this.set('layerLocationNeedsUpdate', NO) ;
1691
-
1692
- return this ;
1693
- },
1694
-
1695
- // .......................................................
1696
- // SC.RESPONDER SUPPORT
1697
- //
1698
-
1699
- /** @property
1700
- The nextResponder is usually the parentView.
1701
- */
1702
- nextResponder: function() {
1703
- return this.get('parentView') ;
1704
- }.property('parentView').cacheable(),
1705
-
1706
-
1707
- /** @property
1708
- Set to YES if your view is willing to accept first responder status. This
1709
- is used when calculcating key responder loop.
1710
- */
1711
- acceptsFirstResponder: NO,
1712
-
1713
- // ..........................................................
1714
- // KEY RESPONDER
1715
- //
1716
-
1717
- /** @property
1718
- YES if the view is currently first responder and the pane the view belongs
1719
- to is also key pane. While this property is set, you should expect to
1720
- receive keyboard events.
1721
- */
1722
- isKeyResponder: NO,
1723
-
1724
- /**
1725
- This method is invoked just before you lost the key responder status.
1726
- The passed view is the view that is about to gain keyResponder status.
1727
- This gives you a chance to do any early setup. Remember that you can
1728
- gain/lose key responder status either because another view in the same
1729
- pane is becoming first responder or because another pane is about to
1730
- become key.
1731
-
1732
- @param {SC.Responder} responder
1733
- */
1734
- willLoseKeyResponderTo: function(responder) {},
1735
-
1736
- /**
1737
- This method is invoked just before you become the key responder. The
1738
- passed view is the view that is about to lose keyResponder status. You
1739
- can use this to do any setup before the view changes.
1740
- Remember that you can gain/lose key responder status either because
1741
- another view in the same pane is becoming first responder or because
1742
- another pane is about to become key.
1743
-
1744
- @param {SC.Responder} responder
1745
- */
1746
- willBecomeKeyResponderFrom: function(responder) {},
1747
-
1748
- /**
1749
- Invokved just after the responder loses key responder status.
1750
- */
1751
- didLoseKeyResponderTo: function(responder) {},
1752
-
1753
- /**
1754
- Invoked just after the responder gains key responder status.
1755
- */
1756
- didBecomeKeyResponderFrom: function(responder) {},
1757
-
1758
- /**
1759
- This method will process a key input event, attempting to convert it to
1760
- an appropriate action method and sending it up the responder chain. The
1761
- event is converted using the SC.KEY_BINDINGS hash, which maps key events
1762
- into method names. If no key binding is found, then the key event will
1763
- be passed along using an insertText() method.
1764
-
1765
- @param {SC.Event} event
1766
- @returns {Object} object that handled event, if any
1767
- */
1768
- interpretKeyEvents: function(event) {
1769
- var codes = event.commandCodes(), cmd = codes[0], chr = codes[1], ret;
1770
-
1771
- if (!cmd && !chr) return null ; //nothing to do.
1772
-
1773
- // if this is a command key, try to do something about it.
1774
- if (cmd) {
1775
- var methodName = SC.MODIFIED_KEY_BINDINGS[cmd] || SC.BASE_KEY_BINDINGS[cmd.match(/[^_]+$/)[0]];
1776
- if (methodName) {
1777
- var target = this, pane = this.get('pane'), handler = null;
1778
- while(target && !(handler = target.tryToPerform(methodName, event))){
1779
- target = (target===pane)? null: target.get('nextResponder') ;
1780
- }
1781
- return handler ;
1782
- }
1783
- }
1784
-
1785
- if (chr && this.respondsTo('insertText')) {
1786
- // if we haven't returned yet and there is plain text, then do an insert
1787
- // of the text. Since this is not an action, do not send it up the
1788
- // responder chain.
1789
- ret = this.insertText(chr, event);
1790
- return ret ? (ret===YES ? this : ret) : null ; // map YES|NO => this|nil
1791
- }
1792
-
1793
- return null ; //nothing to do.
1794
- },
1795
-
1796
- /**
1797
- This method is invoked by interpretKeyEvents() when you receive a key
1798
- event matching some plain text. You can use this to actually insert the
1799
- text into your application, if needed.
1800
-
1801
- @param {SC.Event} event
1802
- @returns {Object} receiver or object that handled event
1803
- */
1804
- insertText: function(chr) {
1805
- return NO ;
1806
- },
1807
-
1808
- /**
1809
- Recursively travels down the view hierarchy looking for a view that
1810
- implements the key equivalent (returning to YES to indicate it handled
1811
- the event). You can override this method to handle specific key
1812
- equivalents yourself.
1813
-
1814
- The keystring is a string description of the key combination pressed.
1815
- The evt is the event itself. If you handle the equivalent, return YES.
1816
- Otherwise, you should just return sc_super.
1817
-
1818
- @param {String} keystring
1819
- @param {SC.Event} evt
1820
- @returns {Boolean}
1821
- */
1822
- performKeyEquivalent: function(keystring, evt) {
1823
- var ret = NO,
1824
- childViews = this.get('childViews'),
1825
- len = childViews.length,
1826
- idx = -1 ;
1827
- while (!ret && (++idx < len)) {
1828
- ret = childViews[idx].performKeyEquivalent(keystring, evt) ;
1829
- }
1830
- return ret ;
1831
- },
1832
-
1833
- /**
1834
- Optionally points to the next key view that should gain focus when tabbing
1835
- through an interface. If this is not set, then the next key view will
1836
- be set automatically to the next child.
1837
- */
1838
- nextKeyView: null,
1839
-
1840
- /**
1841
- Computes the next valid key view, possibly returning the receiver or null.
1842
- This is the next key view that acceptsFirstResponder.
1843
-
1844
- @property
1845
- @type SC.View
1846
- */
1847
- nextValidKeyView: function() {
1848
- var seen = [],
1849
- rootView = this.get('pane'), ret = this.get('nextKeyView');
1850
-
1851
- if(!ret) ret = rootView._computeNextValidKeyView(this, seen);
1852
-
1853
- if(SC.TABBING_ONLY_INSIDE_DOCUMENT && !ret) {
1854
- ret = rootView._computeNextValidKeyView(rootView, seen);
1855
- }
1856
-
1857
- return ret ;
1858
- }.property('nextKeyView'),
1859
-
1860
- _computeNextValidKeyView: function(currentView, seen) {
1861
- var ret = this.get('nextKeyView'),
1862
- children, i, childLen, child;
1863
- if(this !== currentView && seen.indexOf(currentView)!=-1 &&
1864
- this.get('acceptsFirstResponder') && this.get('isVisibleInWindow')){
1865
- return this;
1866
- }
1867
- seen.push(this); // avoid cycles
1868
-
1869
- // find next sibling
1870
- if (!ret) {
1871
- children = this.get('childViews');
1872
- for(i=0, childLen = children.length; i<childLen; i++){
1873
- child = children[i];
1874
- if(child.get('isVisibleInWindow') && child.get('isVisible')){
1875
- ret = child._computeNextValidKeyView(currentView, seen);
1876
- }
1877
- if (ret) return ret;
1878
- }
1879
- ret = null;
1880
- }
1881
- return ret ;
1882
- },
1883
-
1884
- /**
1885
- Optionally points to the previous key view that should gain focus when
1886
- tabbing through the interface. If this is not set then the previous
1887
- key view will be set automatically to the previous child.
1888
- */
1889
- previousKeyView: null,
1890
-
1891
- /**
1892
- Computes the previous valid key view, possibly returning the receiver or
1893
- null. This is the previous key view that acceptsFirstResponder.
1894
-
1895
- @property
1896
- @type SC.View
1897
- */
1898
- previousValidKeyView: function() {
1899
- var seen = [],
1900
- rootView = this.pane(), ret = this.get('previousKeyView');
1901
- if(!ret) ret = rootView._computePreviousValidKeyView(this, seen);
1902
- return ret ;
1903
- }.property('previousKeyView'),
1904
-
1905
- _computePreviousValidKeyView: function(currentView, seen) {
1906
- var ret = this.get('previousKeyView'),
1907
- children, i, child;
1908
-
1909
- if(this !== currentView && seen.indexOf(currentView)!=-1 &&
1910
- this.get('acceptsFirstResponder') && this.get('isVisibleInWindow')){
1911
- return this;
1912
- }
1913
- seen.push(this); // avoid cycles
1914
-
1915
- // find next sibling
1916
- if (!ret) {
1917
- children = this.get('childViews');
1918
- for(i=children.length-1; 0<=i; i--){
1919
- child = children[i];
1920
- if(child.get('isVisibleInWindow') && child.get('isVisible')){
1921
- ret = child._computePreviousValidKeyView(currentView, seen);
1922
- }
1923
- if (ret) return ret;
1924
- }
1925
- ret = null;
1926
- }
1927
- return ret ;
1928
- },
1929
-
1930
- // .......................................................
1931
- // CORE DISPLAY METHODS
1932
- //
1933
-
1934
- /** @private
1935
- Setup a view, but do not finish waking it up.
1936
- - configure childViews
1937
- - Determine the view's theme
1938
- - Fetch a render delegate from the theme, if necessary
1939
- - register the view with the global views hash, which is used for event
1940
- dispatch
1941
- */
1942
- init: function() {
1943
- var parentView = this.get('parentView'),
1944
- theme = this.get('theme'),
1945
- path, root, idx, len, lp, dp ;
1946
-
1947
- sc_super();
1948
-
1949
- // TODO: This makes it impossible to override
1950
- this.layoutStyleCalculator = SC.View.LayoutStyleCalculator.create({ view: this });
1951
-
1952
- // Register the view for event handling. This hash is used by
1953
- // SC.RootResponder to dispatch incoming events.
1954
- SC.View.views[this.get('layerId')] = this;
1955
-
1956
- var childViews = this.get('childViews');
1957
-
1958
- // setup child views. be sure to clone the child views array first
1959
- this.childViews = childViews ? childViews.slice() : [] ;
1960
- this.createChildViews() ; // setup child Views
1961
- this._hasCreatedChildViews = YES;
1962
-
1963
- // register display property observers ..
1964
- // TODO: Optimize into class setup
1965
- dp = this.get('displayProperties') ;
1966
- idx = dp.length ;
1967
- while (--idx >= 0) {
1968
- this.addObserver(dp[idx], this, this.displayDidChange);
1969
- }
1970
-
1971
- // register for drags
1972
- if (this.get('isDropTarget')) SC.Drag.addDropTarget(this) ;
1973
-
1974
- // register scroll views for autoscroll during drags
1975
- if (this.get('isScrollable')) SC.Drag.addScrollableView(this) ;
1976
-
1977
- this._previousLayout = this.get('layout');
1978
- this._lastTheme = this.get('theme');
1979
- },
1980
-
1981
- /**
1982
- Wakes up the view. The default implementation immediately syncs any
1983
- bindings, which may cause the view to need its display updated. You
1984
- can override this method to perform any additional setup. Be sure to
1985
- call sc_super to setup bindings and to call awake on childViews.
1986
-
1987
- It is best to awake a view before you add it to the DOM. This way when
1988
- the DOM is generated, it will have the correct initial values and will
1989
- not require any additional setup.
1990
-
1991
- @returns {void}
1992
- */
1993
- awake: function() {
1994
- sc_super();
1995
- var childViews = this.get('childViews'), len = childViews.length, idx ;
1996
- for (idx=0; idx<len; ++idx) {
1997
- if (!childViews[idx]) continue ;
1998
- childViews[idx].awake() ;
1999
- }
2000
- },
2001
-
2002
- /**
2003
- You must call this method on a view to destroy the view (and all of its
2004
- child views). This will remove the view from any parent node, then make
2005
- sure that the DOM element managed by the view can be released by the
2006
- memory manager.
2007
- */
2008
- destroy: function() {
2009
- if (this.get('isDestroyed')) return this; // nothing to do
2010
-
2011
- this._destroy(); // core destroy method
2012
-
2013
- // remove from parent if found
2014
- this.removeFromParent() ;
2015
-
2016
- // unregister for drags
2017
- if (this.get('isDropTarget')) SC.Drag.removeDropTarget(this) ;
2018
-
2019
- // unregister for autoscroll during drags
2020
- if (this.get('isScrollable')) SC.Drag.removeScrollableView(this) ;
2021
-
2022
- //Do generic destroy. It takes care of mixins and sets isDestroyed to YES.
2023
- sc_super();
2024
- return this; // done with cleanup
2025
- },
2026
-
2027
- _destroy: function() {
2028
- if (this.get('isDestroyed')) return this ; // nothing to do
2029
-
2030
- // destroy the layer -- this will avoid each child view destroying
2031
- // the layer over and over again...
2032
- this.destroyLayer() ;
2033
-
2034
- // first destroy any children.
2035
- var childViews = this.get('childViews'), len = childViews.length, idx ;
2036
- if (len) {
2037
- childViews = childViews.slice() ;
2038
- for (idx=0; idx<len; ++idx) childViews[idx].destroy() ;
2039
- }
2040
-
2041
- // next remove view from global hash
2042
- delete SC.View.views[this.get('layerId')] ;
2043
- delete this._CQ ;
2044
- delete this.page ;
2045
-
2046
- return this ;
2047
- },
2048
-
2049
- /**
2050
- This method is called when your view is first created to setup any child
2051
- views that are already defined on your class. If any are found, it will
2052
- instantiate them for you.
2053
-
2054
- The default implementation of this method simply steps through your
2055
- childViews array, which is expects to either be empty or to contain View
2056
- designs that can be instantiated
2057
-
2058
- Alternatively, you can implement this method yourself in your own
2059
- subclasses to look for views defined on specific properties and then build
2060
- a childViews array yourself.
2061
-
2062
- Note that when you implement this method yourself, you should never
2063
- instantiate views directly. Instead, you should use
2064
- this.createChildView() method instead. This method can be much faster in
2065
- a production environment than creating views yourself.
2066
-
2067
- @returns {SC.View} receiver
2068
- */
2069
- createChildViews: function() {
2070
- var childViews = this.get('childViews'),
2071
- len = childViews.length,
2072
- idx, key, views, view ;
2073
-
2074
- this.beginPropertyChanges() ;
2075
-
2076
- // swap the array
2077
- for (idx=0; idx<len; ++idx) {
2078
- if (key = (view = childViews[idx])) {
2079
-
2080
- // is this is a key name, lookup view class
2081
- if (typeof key === SC.T_STRING) {
2082
- view = this[key];
2083
- } else key = null ;
2084
-
2085
- if (!view) {
2086
- console.error ("No view with name "+key+" has been found in "+this.toString());
2087
- // skip this one.
2088
- continue;
2089
- }
2090
-
2091
- if (view.isClass) {
2092
- view = this.createChildView(view) ; // instantiate if needed
2093
- if (key) this[key] = view ; // save on key name if passed
2094
- }
2095
- }
2096
- childViews[idx] = view;
2097
- }
2098
-
2099
- this.endPropertyChanges() ;
2100
- return this ;
2101
- },
2102
-
2103
- /**
2104
- Instantiates a view to be added to the childViews array during view
2105
- initialization. You generally will not call this method directly unless
2106
- you are overriding createChildViews(). Note that this method will
2107
- automatically configure the correct settings on the new view instance to
2108
- act as a child of the parent.
2109
-
2110
- @param {Class} viewClass
2111
- @param {Hash} attrs optional attributes to add
2112
- @returns {SC.View} new instance
2113
- @test in createChildViews
2114
- */
2115
- createChildView: function(view, attrs) {
2116
- // attrs should always exist...
2117
- if (!attrs) attrs = {} ;
2118
- // clone the hash that was given so we dont pollute it if it's being reused
2119
- else attrs = SC.clone(attrs);
2120
-
2121
- attrs.owner = attrs.parentView = this ;
2122
- attrs.isVisibleInWindow = this.get('isVisibleInWindow');
2123
- if (!attrs.page) attrs.page = this.page ;
2124
-
2125
- // Now add this to the attributes and create.
2126
- view = view.create(attrs) ;
2127
- return view ;
2128
- },
2129
-
2130
- // ...........................................
2131
- // LAYOUT
2132
- //
2133
-
2134
- /**
2135
- The 'frame' property depends on the 'layout' property as well as the
2136
- parent view’s frame. In order to properly invalidate any cached values,
2137
- we need to invalidate the cache whenever 'layout' changes. However,
2138
- observing 'layout' does not guarantee that; the observer might not be run
2139
- immediately.
2140
-
2141
- In order to avoid any window of opportunity where the cached frame could
2142
- be invalid, we need to force layoutDidChange() to always immediately run
2143
- whenever 'layout' is set.
2144
- */
2145
- propertyDidChange: function(key, value, _keepCache) {
2146
- // If the key is 'layout', we need to call layoutDidChange() immediately
2147
- // so that if the frame has changed any cached values (for both this view
2148
- // and any child views) can be appropriately invalidated.
2149
-
2150
- // To allow layout to be a computed property, we check if any property has
2151
- // changed and if layout is dependent on the property.
2152
- // If it is we call layoutDidChange.
2153
- var layoutChange=false;
2154
- if(typeof this.layout === "function" && this._kvo_dependents) {
2155
- var dependents = this._kvo_dependents[key];
2156
- if(dependents && dependents.indexOf('layout')!=-1) layoutChange = true;
2157
- }
2158
- if(key==='layout' || layoutChange) this.layoutDidChange();
2159
- // Resume notification as usual.
2160
- sc_super();
2161
- },
2162
-
2163
-
2164
- /**
2165
- This convenience method will take the current layout, apply any changes
2166
- you pass and set it again. It is more convenient than having to do this
2167
- yourself sometimes.
2168
-
2169
- You can pass just a key/value pair or a hash with several pairs. You can
2170
- also pass a null value to delete a property.
2171
-
2172
- This method will avoid actually setting the layout if the value you pass
2173
- does not edit the layout.
2174
-
2175
- @param {String|Hash} key
2176
- @param {Object} value
2177
- @returns {SC.View} receiver
2178
- */
2179
- adjust: function(key, value) {
2180
- var layout = SC.clone(this.get('layout')), didChange = NO, cur ;
2181
-
2182
- if (key === undefined) return this ; // nothing to do.
2183
-
2184
- // handle string case
2185
- if (SC.typeOf(key) === SC.T_STRING) {
2186
- cur = layout[key] ;
2187
- if (SC.none(value)) {
2188
- if (cur !== undefined) didChange = YES ;
2189
- delete layout[key] ;
2190
- } else {
2191
- if (cur !== value) didChange = YES ;
2192
- layout[key] = value ;
2193
- }
2194
-
2195
- // handle hash -- do it this way to avoid creating memory unless needed
2196
- } else {
2197
- var hash = key;
2198
- for(key in hash) {
2199
- if (!hash.hasOwnProperty(key)) continue;
2200
- value = hash[key] ;
2201
- cur = layout[key] ;
2202
-
2203
- if (value === null) {
2204
- if (cur !== undefined) didChange = YES ;
2205
- delete layout[key] ;
2206
- } else if (value !== undefined) {
2207
- if (cur !== value) didChange = YES ;
2208
- layout[key] = value ;
2209
- }
2210
- }
2211
- }
2212
- // now set adjusted layout
2213
- if (didChange) this.set('layout', layout) ;
2214
-
2215
- return this ;
2216
- },
2217
-
2218
-
2219
- /**
2220
- Animate a given property using CSS animations.
2221
-
2222
- Takes a key, value and either a duration, or a hash of options.
2223
- The options hash has the following parameters
2224
- - duration: Duration of animation in seconds
2225
- - callback: Callback method to run when animation completes
2226
- - timing: Animation timing function
2227
-
2228
- @param {String|Hash} key
2229
- @param {Object} value
2230
- @params {Number|Hash} duration or options
2231
- @returns {SC.View} receiver
2232
- */
2233
- animate: function(keyOrHash, valueOrOptions, optionsOrCallback, callback) {
2234
- var hash, options;
2235
-
2236
- if (typeof keyOrHash === SC.T_STRING) {
2237
- hash = {};
2238
- hash[keyOrHash] = valueOrOptions;
2239
- options = optionsOrCallback;
2240
- } else {
2241
- hash = keyOrHash;
2242
- options = valueOrOptions;
2243
- callback = optionsOrCallback;
2244
- }
2245
-
2246
- var optionsType = SC.typeOf(options);
2247
- if (optionsType === SC.T_NUMBER) {
2248
- options = { duration: options };
2249
- } else if (optionsType !== SC.T_HASH) {
2250
- throw "Must provide options hash or duration!";
2251
- }
2252
-
2253
- if (callback) options.callback = callback;
2254
-
2255
- var timing = options.timing;
2256
- if (timing) {
2257
- if (typeof timing !== SC.T_STRING) {
2258
- options.timing = "cubic-bezier("+timing[0]+", "+timing[1]+", "+
2259
- timing[2]+", "+timing[3]+")";
2260
- }
2261
- } else {
2262
- options.timing = 'linear';
2263
- }
2264
-
2265
- var layout = SC.clone(this.get('layout')), didChange = NO, value, cur, animValue, curAnim, key;
2266
-
2267
- if (!layout.animate) layout.animate = {};
2268
-
2269
- // Very similar to #adjust
2270
- for(key in hash) {
2271
- if (!hash.hasOwnProperty(key)) continue;
2272
- value = hash[key];
2273
- cur = layout[key];
2274
-
2275
- if (cur !== value) { didChange = YES; }
2276
-
2277
- if (SC.ANIMATABLE_PROPERTIES[key]) {
2278
- curAnim = layout.animate[key];
2279
-
2280
- // loose comparison used instead of (value === null || value === undefined)
2281
- if (value == null) { throw "Can only animate to an actual value!"; }
2282
-
2283
- // FIXME: We should check more than duration
2284
- if (curAnim && curAnim.duration !== options.duration) { didChange = YES; }
2285
-
2286
- layout.animate[key] = options;
2287
- }
2288
-
2289
- layout[key] = value;
2290
-
2291
- }
2292
-
2293
- // now set adjusted layout
2294
- if (didChange) this.set('layout', layout) ;
2295
-
2296
- return this ;
2297
- },
2298
-
2299
- /**
2300
- Resets animation, stopping all existing animations.
2301
- */
2302
- resetAnimation: function() {
2303
- var layout = this.get('layout'),
2304
- animations = layout.animate,
2305
- didChange = NO, key;
2306
-
2307
- if (!animations) return;
2308
-
2309
- var hasAnimations;
2310
-
2311
- for (key in animations) {
2312
- didChange = YES;
2313
- delete animations[key];
2314
- }
2315
-
2316
- if (didChange) {
2317
- this.set('layout', layout);
2318
- this.notifyPropertyChange('layout');
2319
- }
2320
-
2321
- return this;
2322
- },
2323
-
2324
- /**
2325
- Called when animation ends, should not usually be called manually
2326
- */
2327
- transitionDidEnd: function(evt){
2328
- // WARNING: Sometimes this will get called more than once for a property. Not sure why.
2329
- this.get('layoutStyleCalculator').transitionDidEnd(evt);
2330
- },
2331
-
2332
-
2333
- /**
2334
- The layout describes how you want your view to be positions on the
2335
- screen. You can define the following properties:
2336
-
2337
- - left: the left edge
2338
- - top: the top edge
2339
- - right: the right edge
2340
- - bottom: the bottom edge
2341
- - height: the height
2342
- - width: the width
2343
- - centerX: an offset from center X
2344
- - centerY: an offset from center Y
2345
- - minWidth: a minimum width
2346
- - minHeight: a minimum height
2347
- - maxWidth: a maximum width
2348
- - maxHeight: a maximum height
2349
-
2350
- Note that you can only use certain combinations to set layout. For
2351
- example, you may set left/right or left/width, but not left/width/right,
2352
- since that combination doesn't make sense.
2353
-
2354
- Likewise, you may set a minWidth/minHeight, or maxWidth/maxHeight, but
2355
- if you also set the width/height explicitly, then those constraints won't
2356
- matter as much.
2357
-
2358
- Layout is designed to maximize reliance on the browser's rendering
2359
- engine to keep your app up to date.
2360
-
2361
- @test in layoutStyle
2362
- */
2363
- layout: { top: 0, left: 0, bottom: 0, right: 0 },
2364
-
2365
- /**
2366
- Converts a frame from the receiver's offset to the target offset. Both
2367
- the receiver and the target must belong to the same pane. If you pass
2368
- null, the conversion will be to the pane level.
2369
-
2370
- Note that the context of a view's frame is the view's parent frame. In
2371
- other words, if you want to convert the frame of your view to the global
2372
- frame, then you should do:
2373
-
2374
- {{{
2375
- var pv = this.get('parentView'), frame = this.get('frame');
2376
- var newFrame = pv ? pv.convertFrameToView(frame, null) : frame;
2377
- }}}
2378
-
2379
- @param {Rect} frame the source frame
2380
- @param {SC.View} targetView the target view to convert to
2381
- @returns {Rect} converted frame
2382
- @test in converFrames
2383
- */
2384
- convertFrameToView: function(frame, targetView) {
2385
- var myX=0, myY=0, targetX=0, targetY=0, view = this, f ;
2386
-
2387
- // walk up this side
2388
- while (view) {
2389
- f = view.get('frame'); myX += f.x; myY += f.y ;
2390
- view = view.get('layoutView') ;
2391
- }
2392
-
2393
- // walk up other size
2394
- if (targetView) {
2395
- view = targetView ;
2396
- while (view) {
2397
- f = view.get('frame'); targetX += f.x; targetY += f.y ;
2398
- view = view.get('layoutView') ;
2399
- }
2400
- }
2401
-
2402
- // now we can figure how to translate the origin.
2403
- myX = frame.x + myX - targetX ;
2404
- myY = frame.y + myY - targetY ;
2405
- return { x: myX, y: myY, width: frame.width, height: frame.height } ;
2406
- },
2407
-
2408
- /**
2409
- Converts a frame offset in the coordinates of another view system to the
2410
- receiver's view.
2411
-
2412
- Note that the convext of a view's frame is relative to the view's
2413
- parentFrame. For example, if you want to convert the frame of view that
2414
- belongs to another view to the receiver's frame you would do:
2415
-
2416
- {{{
2417
- var frame = view.get('frame');
2418
- var newFrame = this.convertFrameFromView(frame, view.get('parentView'));
2419
- }}}
2420
-
2421
- @param {Rect} frame the source frame
2422
- @param {SC.View} targetView the target view to convert to
2423
- @returns {Rect} converted frame
2424
- @test in converFrames
2425
- */
2426
- convertFrameFromView: function(frame, targetView) {
2427
- var myX=0, myY=0, targetX=0, targetY=0, view = this, f ;
2428
-
2429
- // walk up this side
2430
- //Note: Intentional assignment of variable f
2431
- while (view && (f = view.get('frame'))) {
2432
- myX += f.x; myY += f.y ;
2433
- view = view.get('parentView') ;
2434
- }
2435
-
2436
- // walk up other size
2437
- if (targetView) {
2438
- view = targetView ;
2439
- while(view) {
2440
- f = view.get('frame'); targetX += f.x; targetY += f.y ;
2441
- view = view.get('parentView') ;
2442
- }
2443
- }
2444
-
2445
- // now we can figure how to translate the origin.
2446
- myX = frame.x - myX + targetX ;
2447
- myY = frame.y - myY + targetY ;
2448
- return { x: myX, y: myY, width: frame.width, height: frame.height } ;
2449
- },
2450
-
2451
- /**
2452
- Attempt to scroll the view to visible. This will walk up the parent
2453
- view hierarchy looking looking for a scrollable view. It will then
2454
- call scrollToVisible() on it.
2455
-
2456
- Returns YES if an actual scroll took place, no otherwise.
2457
-
2458
- @returns {Boolean}
2459
- */
2460
- scrollToVisible: function() {
2461
- var pv = this.get('parentView');
2462
- while(pv && !pv.get('isScrollable')) pv = pv.get('parentView');
2463
-
2464
- // found view, first make it scroll itself visible then scroll this.
2465
- if (pv) {
2466
- pv.scrollToVisible();
2467
- return pv.scrollToVisible(this);
2468
- } else return NO ;
2469
- },
2470
-
2471
- /**
2472
- Frame describes the current bounding rect for your view. This is always
2473
- measured from the top-left corner of the parent view.
2474
-
2475
- @property {Rect}
2476
- @test in layoutStyle
2477
- */
2478
- frame: function() {
2479
- return this.computeFrameWithParentFrame(null) ;
2480
- }.property('useStaticLayout').cacheable(), // We depend on the layout, but layoutDidChange will call viewDidChange to check the frame for us
2481
-
2482
- /**
2483
- Computes what the frame of this view would be if the parent were resized
2484
- to the passed dimensions. You can use this method to project the size of
2485
- a frame based on the resize behavior of the parent.
2486
-
2487
- This method is used especially by the scroll view to automatically
2488
- calculate when scrollviews should be visible.
2489
-
2490
- Passing null for the parent dimensions will use the actual current
2491
- parent dimensions. This is the same method used to calculate the current
2492
- frame when it changes.
2493
-
2494
- @param {Rect} pdim the projected parent dimensions
2495
- @returns {Rect} the computed frame
2496
- */
2497
- computeFrameWithParentFrame: function(pdim) {
2498
- var layout = this.get('layout'),
2499
- f = {} , error, layer, AUTO = SC.LAYOUT_AUTO,
2500
- stLayout = this.get('useStaticLayout'),
2501
- pv = this.get('parentView'),
2502
- dH, dW, //shortHand for parentDimensions
2503
- borderTop, borderLeft,
2504
- lR = layout.right,
2505
- lL = layout.left,
2506
- lT = layout.top,
2507
- lB = layout.bottom,
2508
- lW = layout.width,
2509
- lH = layout.height,
2510
- lcX = layout.centerX,
2511
- lcY = layout.centerY;
2512
-
2513
- if (lW !== undefined &&
2514
- lW === SC.LAYOUT_AUTO &&
2515
- stLayout !== undefined && !stLayout) {
2516
- error = SC.Error.desc(("%@.layout() cannot use width:auto if "+
2517
- "staticLayout is disabled").fmt(this), "%@".fmt(this), -1);
2518
- console.error(error.toString()) ;
2519
- throw error ;
2520
- }
2521
-
2522
- if (lH !== undefined &&
2523
- lH === SC.LAYOUT_AUTO &&
2524
- stLayout !== undefined && !stLayout) {
2525
- error = SC.Error.desc(("%@.layout() cannot use height:auto if "+
2526
- "staticLayout is disabled").fmt(this),"%@".fmt(this), -1);
2527
- console.error(error.toString()) ;
2528
- throw error ;
2529
- }
2530
-
2531
- if (stLayout) {
2532
- // need layer to be able to compute rect
2533
- if (layer = this.get('layer')) {
2534
- f = SC.viewportOffset(layer); // x,y
2535
- if (pv) f = pv.convertFrameFromView(f, null);
2536
-
2537
- /*
2538
- TODO Can probably have some better width/height values - CC
2539
- */
2540
- f.width = layer.offsetWidth;
2541
- f.height = layer.offsetHeight;
2542
- return f;
2543
- }
2544
- return null; // can't compute
2545
- }
2546
-
2547
-
2548
- if (!pdim) pdim = this.computeParentDimensions(layout) ;
2549
- dH = pdim.height;
2550
- dW = pdim.width;
2551
-
2552
- // handle left aligned and left/right
2553
- if (!SC.none(lL)) {
2554
- if(SC.isPercentage(lL)){
2555
- f.x = dW*lL;
2556
- }else{
2557
- f.x = lL ;
2558
- }
2559
- if (lW !== undefined) {
2560
- if(lW === AUTO) f.width = AUTO ;
2561
- else if(SC.isPercentage(lW)) f.width = dW*lW ;
2562
- else f.width = lW ;
2563
- } else { // better have lR!
2564
- f.width = dW - f.x ;
2565
- if(lR && SC.isPercentage(lR)) f.width = f.width - (lR*dW) ;
2566
- else f.width = f.width - (lR || 0) ;
2567
- }
2568
- // handle right aligned
2569
- } else if (!SC.none(lR)) {
2570
- if (SC.none(lW)) {
2571
- if (SC.isPercentage(lR)) {
2572
- f.width = dW - (dW*lR) ;
2573
- }
2574
- else f.width = dW - lR ;
2575
- f.x = 0 ;
2576
- } else {
2577
- if(lW === AUTO) f.width = AUTO ;
2578
- else if(SC.isPercentage(lW)) f.width = dW*lW ;
2579
- else f.width = (lW || 0) ;
2580
- if (SC.isPercentage(lW)) f.x = dW - (lR*dW) - f.width ;
2581
- else f.x = dW - lR - f.width ;
2582
- }
2583
-
2584
- // handle centered
2585
- } else if (!SC.none(lcX)) {
2586
- if(lW === AUTO) f.width = AUTO ;
2587
- else if (SC.isPercentage(lW)) f.width = lW*dW ;
2588
- else f.width = (lW || 0) ;
2589
- if(SC.isPercentage(lcX)) f.x = (dW - f.width)/2 + (lcX*dW) ;
2590
- else f.x = (dW - f.width)/2 + lcX ;
2591
- } else {
2592
- f.x = 0 ; // fallback
2593
- if (SC.none(lW)) {
2594
- f.width = dW ;
2595
- } else {
2596
- if(lW === AUTO) f.width = AUTO ;
2597
- if (SC.isPercentage(lW)) f.width = lW*dW ;
2598
- else f.width = (lW || 0) ;
2599
- }
2600
- }
2601
-
2602
- // handle top aligned and top/bottom
2603
- if (!SC.none(lT)) {
2604
- if(SC.isPercentage(lT)) f.y = lT*dH ;
2605
- else f.y = lT ;
2606
- if (lH !== undefined) {
2607
- if(lH === AUTO) f.height = AUTO ;
2608
- else if(SC.isPercentage(lH)) f.height = lH*dH ;
2609
- else f.height = lH ;
2610
- } else { // better have lB!
2611
- if(lB && SC.isPercentage(lB)) f.height = dH - f.y - (lB*dH) ;
2612
- else f.height = dH - f.y - (lB || 0) ;
2613
- }
2614
-
2615
- // handle bottom aligned
2616
- } else if (!SC.none(lB)) {
2617
- if (SC.none(lH)) {
2618
- if (SC.isPercentage(lB)) f.height = dH - (lB*dH) ;
2619
- else f.height = dH - lB ;
2620
- f.y = 0 ;
2621
- } else {
2622
- if(lH === AUTO) f.height = AUTO ;
2623
- if (lH && SC.isPercentage(lH)) f.height = lH*dH ;
2624
- else f.height = (lH || 0) ;
2625
- if (SC.isPercentage(lB)) f.y = dH - (lB*dH) - f.height ;
2626
- else f.y = dH - lB - f.height ;
2627
- }
2628
-
2629
- // handle centered
2630
- } else if (!SC.none(lcY)) {
2631
- if(lH === AUTO) f.height = AUTO ;
2632
- if (lH && SC.isPercentage(lH)) f.height = lH*dH ;
2633
- else f.height = (lH || 0) ;
2634
- if (SC.isPercentage(lcY)) f.y = (dH - f.height)/2 + (lcY*dH) ;
2635
- else f.y = (dH - f.height)/2 + lcY ;
2636
-
2637
- // fallback
2638
- } else {
2639
- f.y = 0 ; // fallback
2640
- if (SC.none(lH)) {
2641
- f.height = dH ;
2642
- } else {
2643
- if(lH === AUTO) f.height = AUTO ;
2644
- if (SC.isPercentage(lH)) f.height = lH*dH ;
2645
- else f.height = lH || 0 ;
2646
- }
2647
- }
2648
-
2649
- f.x = Math.floor(f.x);
2650
- f.y = Math.floor(f.y);
2651
- if(f.height !== AUTO) f.height = Math.floor(f.height);
2652
- if(f.width !== AUTO) f.width = Math.floor(f.width);
2653
-
2654
- // if width or height were set to auto and we have a layer, try lookup
2655
- if (f.height === AUTO || f.width === AUTO) {
2656
- layer = this.get('layer');
2657
- if (f.height === AUTO) f.height = layer ? layer.clientHeight : 0;
2658
- if (f.width === AUTO) f.width = layer ? layer.clientWidth : 0;
2659
- }
2660
-
2661
- // views with SC.Border mixin applied applied
2662
- if (this.get('hasBorder')) {
2663
- borderTop = this.get('borderTop') || 0;
2664
- borderLeft = this.get('borderLeft') || 0;
2665
- f.height -= borderTop+ (this.get('borderBottom') || 0);
2666
- f.y += borderTop;
2667
- f.width -= borderLeft + (this.get('borderRight') || 0);
2668
- f.x += borderLeft;
2669
- }
2670
-
2671
- // Account for special cases inside ScrollView, where we adjust the
2672
- // element's scrollTop/scrollLeft property for performance reasons.
2673
- if (pv && pv.isScrollContainer) {
2674
- pv = pv.get('parentView');
2675
- f.x -= pv.get('horizontalScrollOffset');
2676
- f.y -= pv.get('verticalScrollOffset');
2677
- }
2678
-
2679
- // make sure the width/height fix min/max...
2680
- if (!SC.none(layout.maxHeight) && (f.height > layout.maxHeight)) {
2681
- f.height = layout.maxHeight ;
2682
- }
2683
-
2684
- if (!SC.none(layout.minHeight) && (f.height < layout.minHeight)) {
2685
- f.height = layout.minHeight ;
2686
- }
2687
-
2688
- if (!SC.none(layout.maxWidth) && (f.width > layout.maxWidth)) {
2689
- f.width = layout.maxWidth ;
2690
- }
2691
-
2692
- if (!SC.none(layout.minWidth) && (f.width < layout.minWidth)) {
2693
- f.width = layout.minWidth ;
2694
- }
2695
-
2696
- // make sure width/height are never < 0
2697
- if (f.height < 0) f.height = 0 ;
2698
- if (f.width < 0) f.width = 0 ;
2699
-
2700
- return f;
2701
- },
2702
-
2703
- computeParentDimensions: function(frame) {
2704
- var ret, pv = this.get('parentView'), pf = (pv) ? pv.get('frame') : null ;
2705
-
2706
- if (pf) {
2707
- ret = { width: pf.width, height: pf.height };
2708
- } else {
2709
- var f = frame || {};
2710
- ret = {
2711
- width: (f.left || 0) + (f.width || 0) + (f.right || 0),
2712
- height: (f.top || 0) + (f.height || 0) + (f.bottom || 0)
2713
- };
2714
- }
2715
- return ret ;
2716
- },
2717
-
2718
- /**
2719
- The clipping frame returns the visible portion of the view, taking into
2720
- account the contentClippingFrame of the parent view. Keep in mind that
2721
- the clippingFrame is in the context of the view itself, not it's parent
2722
- view.
2723
-
2724
- Normally this will be calculated based on the intersection of your own
2725
- clippingFrame and your parentView's contentClippingFrame.
2726
-
2727
- @property {Rect}
2728
- */
2729
- clippingFrame: function() {
2730
- var f = this.get('frame'),
2731
- ret = f,
2732
- pv, cf;
2733
-
2734
- if (!f) return null;
2735
- pv = this.get('parentView');
2736
- if (pv) {
2737
- cf = pv.get('contentClippingFrame');
2738
- if (!cf) return f;
2739
- ret = SC.intersectRects(cf, f);
2740
- }
2741
- ret.x -= f.x;
2742
- ret.y -= f.y;
2743
-
2744
- return ret;
2745
- }.property('parentView', 'frame').cacheable(),
2746
-
2747
- /**
2748
- The clipping frame child views should intersect with. Normally this is
2749
- the same as the regular clippingFrame. However, you may override this
2750
- method if you want the child views to actually draw more or less content
2751
- than is actually visible for some reason.
2752
-
2753
- Usually this is only used by the ScrollView to optimize drawing on touch
2754
- devices.
2755
-
2756
- @property {Rect}
2757
- */
2758
- contentClippingFrame: function() {
2759
- return this.get('clippingFrame');
2760
- }.property('clippingFrame').cacheable(),
2761
-
2762
- /** @private
2763
- This method is invoked whenever the clippingFrame changes, notifying
2764
- each child view that its clippingFrame has also changed.
2765
- */
2766
- _sc_view_clippingFrameDidChange: function() {
2767
- var cvs = this.get('childViews'), len = cvs.length, idx, cv ;
2768
- for (idx=0; idx<len; ++idx) {
2769
- cv = cvs[idx] ;
2770
-
2771
- // In SC 1.0 views with static layout did not receive notifications
2772
- // of frame changes because they didn't support frames. In SC 1.1 they
2773
- // do support frames, so they should receive notifications. Also in
2774
- // SC 1.1 SC.StaticLayout is merged into SC.View. The mixin is only
2775
- // for compatibility. This property is defined on the mixin.
2776
- //
2777
- // frame changes should be sent all the time unless this property is
2778
- // present to indicate that we want the old 1.0 API behavior instead.
2779
- //
2780
- if (!cv.useStaticLayout) {
2781
- cv.notifyPropertyChange('clippingFrame') ;
2782
- cv._sc_view_clippingFrameDidChange();
2783
- }
2784
- }
2785
- },
2786
-
2787
- /**
2788
- This method may be called on your view whenever the parent view resizes.
2789
-
2790
- The default version of this method will reset the frame and then call
2791
- viewDidResize(). You will not usually override this method, but you may
2792
- override the viewDidResize() method.
2793
-
2794
- @returns {void}
2795
- @test in viewDidResize
2796
- */
2797
- parentViewDidResize: function() {
2798
- var frameMayHaveChanged, layout, isFixed, isPercentageFunc, isPercentage;
2799
-
2800
- // If this view uses static layout, our "do we think the frame changed?"
2801
- // logic is not applicable and we simply have to assume that the frame may
2802
- // have changed.
2803
- if (this.useStaticLayout) {
2804
- frameMayHaveChanged = YES;
2805
- }
2806
- else {
2807
- layout = this.get('layout');
2808
-
2809
- // only resizes if the layout does something other than left/top - fixed
2810
- // size.
2811
- isFixed = (
2812
- (layout.left !== undefined) && (layout.top !== undefined) &&
2813
- (layout.width !== undefined) && (layout.height !== undefined)
2814
- );
2815
-
2816
-
2817
- // If it's fixed, our frame still could have changed if it's fixed to a
2818
- // percentage of the parent.
2819
- if (isFixed) {
2820
- isPercentageFunc = SC.isPercentage;
2821
- isPercentage = (isPercentageFunc(layout.left) ||
2822
- isPercentageFunc(layout.top) ||
2823
- isPercentageFunc(layout.width) ||
2824
- isPercentageFunc(layout.right) ||
2825
- isPercentageFunc(layout.centerX) ||
2826
- isPercentageFunc(layout.centerY));
2827
- }
2828
-
2829
- frameMayHaveChanged = (!isFixed || isPercentage);
2830
- }
2831
-
2832
- // Do we think there's a chance our frame will have changed as a result?
2833
- if (frameMayHaveChanged) {
2834
- // There's a chance our frame changed. Invoke viewDidResize(), which
2835
- // will notify about our change to 'frame' (if it actually changed) and
2836
- // appropriately notify our child views.
2837
- this.viewDidResize();
2838
- }
2839
- },
2840
-
2841
-
2842
-
2843
- /**
2844
- This method is invoked on your view when the view resizes due to a layout
2845
- change or potentially due to the parent view resizing (if your view’s size
2846
- depends on the size of your parent view). You can override this method
2847
- to implement your own layout if you like, such as performing a grid
2848
- layout.
2849
-
2850
- The default implementation simply notifies about the change to 'frame' and
2851
- then calls parentViewDidResize on all of your children.
2852
-
2853
- @returns {void}
2854
- */
2855
- viewDidResize: function() {
2856
- this._viewFrameDidChange();
2857
-
2858
- // Also notify our children.
2859
- var cv = this.childViews, len, idx, view ;
2860
- for (idx=0; idx<(len= cv.length); ++idx) {
2861
- view = cv[idx];
2862
- view.parentViewDidResize();
2863
- }
2864
- },
2865
-
2866
- /** @private
2867
- Invoked by other views to notify this view that its frame has changed.
2868
-
2869
- This notifies the view that its frame property has changed,
2870
- then propagates those changes to its child views.
2871
- */
2872
- _viewFrameDidChange: function() {
2873
- this.notifyPropertyChange('frame');
2874
- this._sc_view_clippingFrameDidChange();
2875
- },
2876
-
2877
- // Implementation note: As a general rule, paired method calls, such as
2878
- // beginLiveResize/endLiveResize that are called recursively on the tree
2879
- // should reverse the order when doing the final half of the call. This
2880
- // ensures that the calls are propertly nested for any cleanup routines.
2881
- //
2882
- // -> View A.beginXXX()
2883
- // -> View B.beginXXX()
2884
- // -> View C.begitXXX()
2885
- // -> View D.beginXXX()
2886
- //
2887
- // ...later on, endXXX methods are called in reverse order of beginXXX...
2888
- //
2889
- // <- View D.endXXX()
2890
- // <- View C.endXXX()
2891
- // <- View B.endXXX()
2892
- // <- View A.endXXX()
2893
- //
2894
- // See the two methods below for an example implementation.
2895
-
2896
- /**
2897
- Call this method when you plan to begin a live resize. This will
2898
- notify the receiver view and any of its children that are interested
2899
- that the resize is about to begin.
2900
-
2901
- @returns {SC.View} receiver
2902
- @test in viewDidResize
2903
- */
2904
- beginLiveResize: function() {
2905
- // call before children have been notified...
2906
- if (this.willBeginLiveResize) this.willBeginLiveResize() ;
2907
-
2908
- // notify children in order
2909
- var ary = this.get('childViews'), len = ary.length, idx, view ;
2910
- for (idx=0; idx<len; ++idx) {
2911
- view = ary[idx] ;
2912
- if (view.beginLiveResize) view.beginLiveResize();
2913
- }
2914
- return this ;
2915
- },
2916
-
2917
- /**
2918
- Call this method when you are finished with a live resize. This will
2919
- notify the receiver view and any of its children that are interested
2920
- that the live resize has ended.
2921
-
2922
- @returns {SC.View} receiver
2923
- @test in viewDidResize
2924
- */
2925
- endLiveResize: function() {
2926
- // notify children in *reverse* order
2927
- var ary = this.get('childViews'), len = ary.length, idx, view ;
2928
- for (idx=len-1; idx>=0; --idx) { // loop backwards
2929
- view = ary[idx] ;
2930
- if (view.endLiveResize) view.endLiveResize() ;
2931
- }
2932
-
2933
- // call *after* all children have been notified...
2934
- if (this.didEndLiveResize) this.didEndLiveResize() ;
2935
- return this ;
2936
- },
2937
-
2938
- /**
2939
- Setting wantsAcceleratedLayer to YES will use transforms to move the
2940
- layer when available. On some platforms transforms are hardware accelerated.
2941
- */
2942
- wantsAcceleratedLayer: NO,
2943
-
2944
- /**
2945
- Specifies whether transforms can be used to move the layer.
2946
- */
2947
- hasAcceleratedLayer: function(){
2948
- if (this.get('wantsAcceleratedLayer') && SC.platform.supportsAcceleratedLayers) {
2949
- var layout = this.get('layout'),
2950
- animations = layout.animate,
2951
- AUTO = SC.LAYOUT_AUTO,
2952
- key;
2953
-
2954
- if (animations && (animations['top'] || animations['left'])) {
2955
- for (key in animations) {
2956
- // If we're animating other transforms at different speeds, don't use acceleratedLayer
2957
- if (
2958
- SC.CSS_TRANSFORM_MAP[key] &&
2959
- ((animations['top'] && animations['top'].duration !== animations[key].duration) ||
2960
- (animations['left'] && animations['left'].duration !== animations[key].duration))
2961
- ) {
2962
- return NO;
2963
- }
2964
- }
2965
- }
2966
-
2967
- // loose comparison used instead of (layout.X === null || layout.X === undefined)
2968
- if (
2969
- layout.left != null && !SC.isPercentage(layout.left) && layout.left !== AUTO &&
2970
- layout.top != null && !SC.isPercentage(layout.top) && layout.top !== AUTO &&
2971
- layout.width != null && !SC.isPercentage(layout.width) && layout.width !== AUTO &&
2972
- layout.height != null && !SC.isPercentage(layout.height) && layout.height !== AUTO
2973
- ) {
2974
- return YES;
2975
- }
2976
- }
2977
- return NO;
2978
- }.property('wantsAcceleratedLayer').cacheable(),
2979
-
2980
-
2981
- layoutStyleCalculator: null,
2982
-
2983
- /**
2984
- layoutStyle describes the current styles to be written to your element
2985
- based on the layout you defined. Both layoutStyle and frame reset when
2986
- you edit the layout property. Both are read only.
2987
-
2988
- Computes the layout style settings needed for the current anchor.
2989
-
2990
- @property {Hash}
2991
- @readOnly
2992
- */
2993
- layoutStyle: function() {
2994
- var props = {
2995
- layout: this.get('layout'),
2996
- turbo: this.get('hasAcceleratedLayer'),
2997
- staticLayout: this.get('useStaticLayout')
2998
- };
2999
-
3000
- var calculator = this.get('layoutStyleCalculator');
3001
- calculator.set(props);
3002
-
3003
- return calculator.calculate();
3004
- }.property().cacheable(),
3005
-
3006
-
3007
-
3008
- /**
3009
- The view responsible for laying out this view. The default version
3010
- returns the current parent view.
3011
- */
3012
- layoutView: function() {
3013
- return this.get('parentView') ;
3014
- }.property('parentView').cacheable(),
3015
-
3016
- /**
3017
- This method is called whenever a property changes that invalidates the
3018
- layout of the view. Changing the layout will do this automatically, but
3019
- you can add others if you want.
3020
-
3021
- Implementation Note: In a traditional setup, we would simply observe
3022
- 'layout' here, but as described above in the documentation for our custom
3023
- implementation of propertyDidChange(), this method must always run
3024
- immediately after 'layout' is updated to avoid the potential for stale
3025
- (incorrect) cached 'frame' values.
3026
-
3027
- @returns {SC.View} receiver
3028
- */
3029
- layoutDidChange: function() {
3030
- // Did our layout change in a way that could cause us to be resized? If
3031
- // not, then there's no need to invalidate the frames of our child views.
3032
- var previousLayout = this._previousLayout,
3033
- currentLayout = this.get('layout'),
3034
- didResize = YES,
3035
- previousWidth, previousHeight, currentWidth, currentHeight;
3036
-
3037
-
3038
- // Handle old style rotation
3039
- if (!SC.none(currentLayout.rotate)) {
3040
- if (SC.none(currentLayout.rotateX)) {
3041
- currentLayout.rotateX = currentLayout.rotate;
3042
- console.warn('Please set rotateX instead of rotate');
3043
- }
3044
- }
3045
- if (!SC.none(currentLayout.rotateX)) {
3046
- currentLayout.rotate = currentLayout.rotateX;
3047
- } else {
3048
- delete currentLayout.rotate;
3049
- }
3050
-
3051
- var animations = currentLayout.animations;
3052
- if (animations) {
3053
- if (!SC.none(animations.rotate)) {
3054
- if (SC.none(animations.rotateX)) {
3055
- animations.rotateX = animations.rotate;
3056
- console.warn('Please animate rotateX instead of rotate');
3057
- }
3058
- }
3059
- if (!SC.none(animations.rotateX)) {
3060
- animations.rotate = animations.rotateX;
3061
- } else {
3062
- delete animations.rotate;
3063
- }
3064
- }
3065
-
3066
- if (previousLayout && previousLayout !== currentLayout) {
3067
- // This is a simple check to see whether we think the view may have
3068
- // resized. We could look for a number of cases, but for now we'll
3069
- // handle only one simple case: if the width and height are both
3070
- // specified, and they have not changed.
3071
- previousWidth = previousLayout.width;
3072
- if (previousWidth !== undefined) {
3073
- currentWidth = currentLayout.width;
3074
- if (previousWidth === currentWidth) {
3075
- previousHeight = previousLayout.height;
3076
- if (previousLayout !== undefined) {
3077
- currentHeight = currentLayout.height;
3078
- if (previousHeight === currentHeight) didResize = NO;
3079
- }
3080
- }
3081
- }
3082
- }
3083
-
3084
- this.beginPropertyChanges() ;
3085
- this.notifyPropertyChange('hasAcceleratedLayer');
3086
- this.notifyPropertyChange('layoutStyle') ;
3087
- if (didResize) {
3088
- this.viewDidResize();
3089
- }
3090
- else {
3091
- // Even if we didn't resize, our frame might have changed.
3092
- // viewDidResize() handles this in the other case.
3093
- this._viewFrameDidChange();
3094
- }
3095
- this.endPropertyChanges() ;
3096
-
3097
- // notify layoutView...
3098
- var layoutView = this.get('layoutView');
3099
- if (layoutView) {
3100
- layoutView.set('childViewsNeedLayout', YES);
3101
- layoutView.layoutDidChangeFor(this) ;
3102
- if (layoutView.get('childViewsNeedLayout')) {
3103
- layoutView.invokeOnce(layoutView.layoutChildViewsIfNeeded);
3104
- }
3105
- }
3106
-
3107
- this._previousLayout = currentLayout;
3108
-
3109
- return this ;
3110
- },
3111
-
3112
- /**
3113
- This this property to YES whenever the view needs to layout its child
3114
- views. Normally this property is set automatically whenever the layout
3115
- property for a child view changes.
3116
-
3117
- @property {Boolean}
3118
- */
3119
- childViewsNeedLayout: NO,
3120
-
3121
- /**
3122
- One of two methods that are invoked whenever one of your childViews
3123
- layout changes. This method is invoked everytime a child view's layout
3124
- changes to give you a chance to record the information about the view.
3125
-
3126
- Since this method may be called many times during a single run loop, you
3127
- should keep this method pretty short. The other method called when layout
3128
- changes, layoutChildViews(), is invoked only once at the end of
3129
- the run loop. You should do any expensive operations (including changing
3130
- a childView's actual layer) in this other method.
3131
-
3132
- Note that if as a result of running this method you decide that you do not
3133
- need your layoutChildViews() method run later, you can set the
3134
- childViewsNeedsLayout property to NO from this method and the layout
3135
- method will not be called layer.
3136
-
3137
- @param {SC.View} childView the view whose layout has changed.
3138
- @returns {void}
3139
- */
3140
- layoutDidChangeFor: function(childView) {
3141
- var set = this._needLayoutViews ;
3142
- if (!set) set = this._needLayoutViews = SC.CoreSet.create();
3143
- set.add(childView);
3144
- },
3145
-
3146
- /**
3147
- Called your layout method if the view currently needs to layout some
3148
- child views.
3149
-
3150
- @param {Boolean} isVisible if true assume view is visible even if it is not.
3151
- @returns {SC.View} receiver
3152
- @test in layoutChildViews
3153
- */
3154
- layoutChildViewsIfNeeded: function(isVisible) {
3155
- if (!isVisible) isVisible = this.get('isVisibleInWindow');
3156
- if (isVisible && this.get('childViewsNeedLayout')) {
3157
- this.set('childViewsNeedLayout', NO);
3158
- this.layoutChildViews();
3159
- }
3160
- return this ;
3161
- },
3162
-
3163
- /**
3164
- Applies the current layout to the layer. This method is usually only
3165
- called once per runloop. You can override this method to provide your
3166
- own layout updating method if you want, though usually the better option
3167
- is to override the layout method from the parent view.
3168
-
3169
- The default implementation of this method simply calls the renderLayout()
3170
- method on the views that need layout.
3171
-
3172
- @returns {void}
3173
- */
3174
- layoutChildViews: function() {
3175
- var set = this._needLayoutViews,
3176
- len = set ? set.length : 0,
3177
- i;
3178
- for (i = 0; i < len; ++i) {
3179
- set[i].updateLayout();
3180
- }
3181
- set.clear(); // reset & reuse
3182
- },
3183
-
3184
- /**
3185
- Invoked by the layoutChildViews method to update the layout on a
3186
- particular view. This method creates a render context and calls the
3187
- renderLayout() method, which is probably what you want to override instead
3188
- of this.
3189
-
3190
- You will not usually override this method, but you may call it if you
3191
- implement layoutChildViews() in a view yourself.
3192
-
3193
- @returns {SC.View} receiver
3194
- @test in layoutChildViews
3195
- */
3196
- updateLayout: function() {
3197
- var layer = this.get('layer'), context;
3198
- if (layer) {
3199
- context = this.renderContext(layer);
3200
- this.renderLayout(context, NO);
3201
- context.update();
3202
-
3203
- // If this view uses static layout, then notify if the frame changed.
3204
- // (viewDidResize will do a comparison)
3205
- if (this.useStaticLayout) this.viewDidResize();
3206
- }
3207
- layer = null ;
3208
- return this ;
3209
- },
3210
-
3211
- /**
3212
- Default method called by the layout view to actually apply the current
3213
- layout to the layer. The default implementation simply assigns the
3214
- current layoutStyle to the layer. This method is also called whenever
3215
- the layer is first created.
3216
-
3217
- @param {SC.RenderContext} the render context
3218
- @returns {void}
3219
- @test in layoutChildViews
3220
- */
3221
- renderLayout: function(context, firstTime) {
3222
- this.get('layoutStyleCalculator').willRenderAnimations();
3223
- context.addStyle(this.get('layoutStyle'));
3224
- this.get('layoutStyleCalculator').didRenderAnimations();
3225
- },
3226
-
3227
- /** walk like a duck */
3228
- isView: YES,
3229
-
3230
- /**
3231
- Default method called when a selectstart event is triggered. This event is
3232
- only supported by IE. Used in sproutcore to disable text selection and
3233
- IE8 accelerators. The accelerators will be enabled only in
3234
- text selectable views. In FF and Safari we use the css style 'allow-select'.
3235
-
3236
- If you want to enable text selection in certain controls is recommended
3237
- to override this function to always return YES , instead of setting
3238
- isTextSelectable to true.
3239
-
3240
- For example in textfield you dont want to enable textSelection on the text
3241
- hint only on the actual text you are entering. You can achieve that by
3242
- only overriding this method.
3243
-
3244
- @param evt {SC.Event} the selectstart event
3245
- @returns YES if selectable
3246
- */
3247
- selectStart: function(evt) {
3248
- return this.get('isTextSelectable');
3249
- },
3250
-
3251
- /**
3252
- Used to block the contextMenu per view.
3253
-
3254
- @param evt {SC.Event} the contextmenu event
3255
- @returns YES if the contextmenu can show up
3256
- */
3257
- contextMenu: function(evt) {
3258
- if(!this.get('isContextMenuEnabled')) evt.stop();
3259
- return true;
3260
- },
3261
-
3262
- /**
3263
- A boundary set of distances outside which the touch will not be considered "inside" the view anymore.
3264
-
3265
- By default, up to 50px on each side.
3266
- */
3267
- touchBoundary: { left: 50, right: 50, top: 50, bottom: 50 },
3268
-
3269
- /**
3270
- @private
3271
- A computed property based on frame.
3272
- */
3273
- _touchBoundaryFrame: function (){
3274
- return this.get("parentView").convertFrameToView(this.get('frame'), null);
3275
- }.property("frame", "parentView").cacheable(),
3276
-
3277
- /**
3278
- Returns YES if the provided touch is within the boundary.
3279
- */
3280
- touchIsInBoundary: function(touch) {
3281
- var f = this.get("_touchBoundaryFrame"), maxX = 0, maxY = 0, boundary = this.get("touchBoundary");
3282
- var x = touch.pageX, y = touch.pageY;
3283
-
3284
- if (x < f.x) {
3285
- x = f.x - x;
3286
- maxX = boundary.left;
3287
- } else if (x > f.x + f.width) {
3288
- x = x - (f.x + f.width);
3289
- maxX = boundary.right;
3290
- } else {
3291
- x = 0;
3292
- maxX = 1;
3293
- }
3294
-
3295
- if (y < f.y) {
3296
- y = f.y - y;
3297
- maxY = boundary.top;
3298
- } else if (y > f.y + f.height) {
3299
- y = y - (f.y + f.height);
3300
- maxY = boundary.bottom;
3301
- } else {
3302
- y = 0;
3303
- maxY = 1;
3304
- }
3305
-
3306
- if (x > 100 || y > 100) return NO;
3307
- return YES;
3308
- },
3309
-
3310
- ///
3311
- /// BUILDING IN/OUT
3312
- ///
3313
-
3314
- /**
3315
- Call this to append a child while building it in. If the child is not
3316
- buildable, this is the same as calling appendChild.
3317
- */
3318
- buildInChild: function(view) {
3319
- view.willBuildInToView(this);
3320
- this.appendChild(view);
3321
- view.buildInToView(this);
3322
- },
3323
-
3324
- /**
3325
- Call to remove a child after building it out. If the child is not buildable,
3326
- this will simply call removeChild.
3327
- */
3328
- buildOutChild: function(view) {
3329
- view.buildOutFromView(this);
3330
- },
3331
-
3332
- /**
3333
- Called by child view when build in finishes. By default, does nothing.
3334
-
3335
- */
3336
- buildInDidFinishFor: function(child) {
3337
- },
3338
-
3339
- /**
3340
- @private
3341
- Called by child view when build out finishes. By default removes the child view.
3342
- */
3343
- buildOutDidFinishFor: function(child) {
3344
- this.removeChild(child);
3345
- },
3346
-
3347
- /**
3348
- Whether the view is currently building in.
3349
- */
3350
- isBuildingIn: NO,
3351
-
3352
- /**
3353
- Whether the view is currently building out.
3354
- */
3355
- isBuildingOut: NO,
3356
-
3357
- /**
3358
- Implement this, and call didFinishBuildIn when you are done.
3359
- */
3360
- buildIn: function() {
3361
- this.buildInDidFinish();
3362
- },
3363
-
3364
- /**
3365
- Implement this, and call didFinsihBuildOut when you are done.
3366
- */
3367
- buildOut: function() {
3368
- this.buildOutDidFinish();
3369
- },
3370
-
3371
- /**
3372
- This should reset (without animation) any internal states; sometimes called before.
3373
-
3374
- It is usually called before a build in, by the parent view.
3375
- */
3376
- resetBuild: function() {
3377
-
3378
- },
3379
-
3380
- /**
3381
- Implement this if you need to do anything special when cancelling build out;
3382
- note that buildIn will subsequently be called, so you usually won't need to do
3383
- anything.
3384
-
3385
- This is basically called whenever build in happens.
3386
- */
3387
- buildOutDidCancel: function() {
3388
-
3389
- },
3390
-
3391
- /**
3392
- Implement this if you need to do anything special when cancelling build in.
3393
- You probably won't be able to do anything. I mean, what are you gonna do?
3394
-
3395
- If build in was cancelled, it means build out is probably happening.
3396
- So, any timers or anything you had going, you can cancel.
3397
- Then buildOut will happen.
3398
- */
3399
- buildInDidCancel: function() {
3400
-
3401
- },
3402
-
3403
- /**
3404
- Call this when you have built in.
3405
- */
3406
- buildInDidFinish: function() {
3407
- this.isBuildingIn = NO;
3408
- this._buildingInTo.buildInDidFinishFor(this);
3409
- this._buildingInTo = null;
3410
- },
3411
-
3412
- /**
3413
- Call this when you have finished building out.
3414
- */
3415
- buildOutDidFinish: function() {
3416
- this.isBuildingOut = NO;
3417
- this._buildingOutFrom.buildOutDidFinishFor(this);
3418
- this._buildingOutFrom = null;
3419
- },
3420
-
3421
- /**
3422
- Usually called by parentViewDidChange, this resets the build state (calling resetBuild in the process).
3423
- */
3424
- resetBuildState: function() {
3425
- if (this.isBuildingIn) {
3426
- this.buildInDidCancel();
3427
- this.isBuildingIn = NO;
3428
- }
3429
- if (this.isBuildingOut) {
3430
- this.buildOutDidCancel();
3431
- this.isBuildingOut = NO;
3432
- }
3433
-
3434
- // finish cleaning up
3435
- this.buildingInTo = null;
3436
- this.buildingOutFrom = null;
3437
-
3438
- this.resetBuild();
3439
- },
3440
-
3441
- /**
3442
- @private (semi)
3443
- Called by building parent view's buildInChild method. This prepares
3444
- to build in, but unlike buildInToView, this is called _before_ the child
3445
- is appended.
3446
-
3447
- Mostly, this cancels any build out _before_ the view is removed through parent change.
3448
- */
3449
- willBuildInToView: function(view) {
3450
- // stop any current build outs (and if we need to, we also need to build in again)
3451
- if (this.isBuildingOut) {
3452
- this.buildOutDidCancel();
3453
- }
3454
- },
3455
-
3456
- /**
3457
- @private (semi)
3458
- Called by building parent view's buildInChild method.
3459
- */
3460
- buildInToView: function(view) {
3461
- // if we are already building in, do nothing.
3462
- if (this.isBuildingIn) return;
3463
-
3464
- this._buildingInTo = view;
3465
- this.isBuildingOut = NO;
3466
- this.isBuildingIn = YES;
3467
- this.buildIn();
3468
- },
3469
-
3470
- /**
3471
- @private (semi)
3472
- Called by building parent view's buildOutChild method.
3473
-
3474
- The supplied view should always be the parent view.
3475
- */
3476
- buildOutFromView: function(view) {
3477
- // if we are already building out, do nothing.
3478
- if (this.isBuildingOut) return;
3479
-
3480
- // cancel any build ins
3481
- if (this.isBuildingIn) {
3482
- this.buildInDidCancel();
3483
- }
3484
-
3485
- // in any case, we need to build out
3486
- this.isBuildingOut = YES;
3487
- this.isBuildingIn = NO;
3488
- this._buildingOutFrom = view;
3489
- this.buildOut();
3490
- }
3491
- });
3492
-
3493
- SC.View.mixin(/** @scope SC.View */ {
3494
-
3495
- /** @private walk like a duck -- used by SC.Page */
3496
- isViewClass: YES,
3497
-
3498
- /**
3499
- This method works just like extend() except that it will also preserve
3500
- the passed attributes in case you want to use a view builder later, if
3501
- needed.
3502
-
3503
- @param {Hash} attrs Attributes to add to view
3504
- @returns {Class} SC.View subclass to create
3505
- @function
3506
- */
3507
- design: function() {
3508
- if (this.isDesign) return this; // only run design one time
3509
- var ret = this.extend.apply(this, arguments);
3510
- ret.isDesign = YES ;
3511
- if (SC.ViewDesigner) {
3512
- SC.ViewDesigner.didLoadDesign(ret, this, SC.A(arguments));
3513
- }
3514
- return ret ;
3515
- },
3516
-
3517
- extend: function() {
3518
- var last = arguments[arguments.length - 1];
3519
-
3520
- if (last && !SC.none(last.theme)) {
3521
- last.themeName = last.theme;
3522
- delete last.theme;
3523
- }
3524
-
3525
- return SC.Object.extend.apply(this, arguments);
3526
- },
3527
-
3528
- /**
3529
- Helper applies the layout to the prototype.
3530
- */
3531
- layout: function(layout) {
3532
- this.prototype.layout = layout ;
3533
- return this ;
3534
- },
3535
-
3536
- /**
3537
- Convert any layout to a Top, Left, Width, Height layout
3538
- */
3539
- convertLayoutToAnchoredLayout: function(layout, parentFrame){
3540
- var ret = {top: 0, left: 0, width: parentFrame.width, height: parentFrame.height},
3541
- pFW = parentFrame.width, pFH = parentFrame.height, //shortHand for parentDimensions
3542
- lR = layout.right,
3543
- lL = layout.left,
3544
- lT = layout.top,
3545
- lB = layout.bottom,
3546
- lW = layout.width,
3547
- lH = layout.height,
3548
- lcX = layout.centerX,
3549
- lcY = layout.centerY;
3550
-
3551
- // X Conversion
3552
- // handle left aligned and left/right
3553
- if (!SC.none(lL)) {
3554
- if(SC.isPercentage(lL)) ret.left = lL*pFW;
3555
- else ret.left = lL;
3556
- if (lW !== undefined) {
3557
- if(lW === SC.LAYOUT_AUTO) ret.width = SC.LAYOUT_AUTO ;
3558
- else if(SC.isPercentage(lW)) ret.width = lW*pFW ;
3559
- else ret.width = lW ;
3560
- } else {
3561
- if (lR && SC.isPercentage(lR)) ret.width = pFW - ret.left - (lR*pFW);
3562
- else ret.width = pFW - ret.left - (lR || 0);
3563
- }
3564
-
3565
- // handle right aligned
3566
- } else if (!SC.none(lR)) {
3567
-
3568
- // if no width, calculate it from the parent frame
3569
- if (SC.none(lW)) {
3570
- ret.left = 0;
3571
- if(lR && SC.isPercentage(lR)) ret.width = pFW - (lR*pFW);
3572
- else ret.width = pFW - (lR || 0);
3573
-
3574
- // If has width, calculate the left anchor from the width and right and parent frame
3575
- } else {
3576
- if(lW === SC.LAYOUT_AUTO) ret.width = SC.LAYOUT_AUTO ;
3577
- else {
3578
- if (SC.isPercentage(lW)) ret.width = lW*pFW;
3579
- else ret.width = lW;
3580
- if (SC.isPercentage(lR)) ret.left = pFW - (ret.width + lR);
3581
- else ret.left = pFW - (ret.width + lR);
3582
- }
3583
- }
3584
-
3585
- // handle centered
3586
- } else if (!SC.none(lcX)) {
3587
- if(lW && SC.isPercentage(lW)) ret.width = (lW*pFW) ;
3588
- else ret.width = (lW || 0) ;
3589
- ret.left = ((pFW - ret.width)/2);
3590
- if (SC.isPercentage(lcX)) ret.left = ret.left + lcX*pFW;
3591
- else ret.left = ret.left + lcX;
3592
-
3593
- // if width defined, assume left of zero
3594
- } else if (!SC.none(lW)) {
3595
- ret.left = 0;
3596
- if(lW === SC.LAYOUT_AUTO) ret.width = SC.LAYOUT_AUTO ;
3597
- else {
3598
- if(SC.isPercentage(lW)) ret.width = lW*pFW;
3599
- else ret.width = lW;
3600
- }
3601
-
3602
- // fallback, full width.
3603
- } else {
3604
- ret.left = 0;
3605
- ret.width = 0;
3606
- }
3607
-
3608
- // handle min/max
3609
- if (layout.minWidth !== undefined) ret.minWidth = layout.minWidth ;
3610
- if (layout.maxWidth !== undefined) ret.maxWidth = layout.maxWidth ;
3611
-
3612
- // Y Conversion
3613
- // handle left aligned and top/bottom
3614
- if (!SC.none(lT)) {
3615
- if(SC.isPercentage(lT)) ret.top = lT*pFH;
3616
- else ret.top = lT;
3617
- if (lH !== undefined) {
3618
- if(lH === SC.LAYOUT_AUTO) ret.height = SC.LAYOUT_AUTO ;
3619
- else if (SC.isPercentage(lH)) ret.height = lH*pFH;
3620
- else ret.height = lH ;
3621
- } else {
3622
- ret.height = pFH - ret.top;
3623
- if(lB && SC.isPercentage(lB)) ret.height = ret.height - (lB*pFH);
3624
- else ret.height = ret.height - (lB || 0);
3625
- }
3626
-
3627
- // handle bottom aligned
3628
- } else if (!SC.none(lB)) {
3629
-
3630
- // if no height, calculate it from the parent frame
3631
- if (SC.none(lH)) {
3632
- ret.top = 0;
3633
- if (lB && SC.isPercentage(lB)) ret.height = pFH - (lB*pFH);
3634
- else ret.height = pFH - (lB || 0);
3635
-
3636
- // If has height, calculate the top anchor from the height and bottom and parent frame
3637
- } else {
3638
- if(lH === SC.LAYOUT_AUTO) ret.height = SC.LAYOUT_AUTO ;
3639
- else {
3640
- if (SC.isPercentage(lH)) ret.height = lH*pFH;
3641
- else ret.height = lH;
3642
- ret.top = pFH - ret.height;
3643
- if (SC.isPercentage(lB)) ret.top = ret.top - (lB*pFH);
3644
- else ret.top = ret.top - lB;
3645
- }
3646
- }
3647
-
3648
- // handle centered
3649
- } else if (!SC.none(lcY)) {
3650
- if(lH && SC.isPercentage(lH)) ret.height = (lH*pFH) ;
3651
- else ret.height = (lH || 0) ;
3652
- ret.top = ((pFH - ret.height)/2);
3653
- if(SC.isPercentage(lcY)) ret.top = ret.top + lcY*pFH;
3654
- else ret.top = ret.top + lcY;
3655
-
3656
- // if height defined, assume top of zero
3657
- } else if (!SC.none(lH)) {
3658
- ret.top = 0;
3659
- if(lH === SC.LAYOUT_AUTO) ret.height = SC.LAYOUT_AUTO ;
3660
- else if (SC.isPercentage(lH)) ret.height = lH*pFH;
3661
- else ret.height = lH;
3662
-
3663
- // fallback, full height.
3664
- } else {
3665
- ret.top = 0;
3666
- ret.height = 0;
3667
- }
3668
-
3669
- if(ret.top) ret.top = Math.floor(ret.top);
3670
- if(ret.bottom) ret.bottom = Math.floor(ret.bottom);
3671
- if(ret.left) ret.left = Math.floor(ret.left);
3672
- if(ret.right) ret.right = Math.floor(ret.right);
3673
- if(ret.width !== SC.LAYOUT_AUTO) ret.width = Math.floor(ret.width);
3674
- if(ret.height !== SC.LAYOUT_AUTO) ret.height = Math.floor(ret.height);
3675
-
3676
- // handle min/max
3677
- if (layout.minHeight !== undefined) ret.minHeight = layout.minHeight ;
3678
- if (layout.maxHeight !== undefined) ret.maxHeight = layout.maxHeight ;
3679
-
3680
- return ret;
3681
- },
3682
-
3683
- /**
3684
- For now can only convert Top/Left/Width/Height to a Custom Layout
3685
- */
3686
- convertLayoutToCustomLayout: function(layout, layoutParams, parentFrame){
3687
- // TODO: [EG] Create Top/Left/Width/Height to a Custom Layout conversion
3688
- },
3689
-
3690
- /**
3691
- Helper applies the classNames to the prototype
3692
- */
3693
- classNames: function(sc) {
3694
- sc = (this.prototype.classNames || []).concat(sc);
3695
- this.prototype.classNames = sc;
3696
- return this ;
3697
- },
3698
-
3699
- /**
3700
- Help applies the tagName
3701
- */
3702
- tagName: function(tg) {
3703
- this.prototype.tagName = tg;
3704
- return this ;
3705
- },
3706
-
3707
- /**
3708
- Helper adds the childView
3709
- */
3710
- childView: function(cv) {
3711
- var childViews = this.prototype.childViews || [];
3712
- if (childViews === this.superclass.prototype.childViews) {
3713
- childViews = childViews.slice();
3714
- }
3715
- childViews.push(cv) ;
3716
- this.prototype.childViews = childViews;
3717
- return this ;
3718
- },
3719
-
3720
- /**
3721
- Helper adds a binding to a design
3722
- */
3723
- bind: function(keyName, path) {
3724
- var p = this.prototype, s = this.superclass.prototype;
3725
- var bindings = p._bindings ;
3726
- if (!bindings || bindings === s._bindings) {
3727
- bindings = p._bindings = (bindings || []).slice() ;
3728
- }
3729
-
3730
- keyName = keyName + "Binding";
3731
- p[keyName] = path ;
3732
- bindings.push(keyName);
3733
-
3734
- return this ;
3735
- },
3736
-
3737
- /**
3738
- Helper sets a generic property on a design.
3739
- */
3740
- prop: function(keyName, value) {
3741
- this.prototype[keyName] = value;
3742
- return this ;
3743
- },
3744
-
3745
- /**
3746
- Used to construct a localization for a view. The default implementation
3747
- will simply return the passed attributes.
3748
- */
3749
- localization: function(attrs, rootElement) {
3750
- // add rootElement
3751
- if (rootElement) attrs.rootElement = SC.$(rootElement)[0];
3752
- return attrs;
3753
- },
3754
-
3755
- /**
3756
- Creates a view instance, first finding the DOM element you name and then
3757
- using that as the root element. You should not use this method very
3758
- often, but it is sometimes useful if you want to attach to already
3759
- existing HTML.
3760
-
3761
- @param {String|Element} element
3762
- @param {Hash} attrs
3763
- @returns {SC.View} instance
3764
- */
3765
- viewFor: function(element, attrs) {
3766
- var args = SC.$A(arguments); // prepare to edit
3767
- if (SC.none(element)) {
3768
- args.shift(); // remove if no element passed
3769
- } else args[0] = { rootElement: SC.$(element)[0] } ;
3770
- var ret = this.create.apply(this, arguments) ;
3771
- args = args[0] = null;
3772
- return ret ;
3773
- },
3774
-
3775
- /**
3776
- Create a new view with the passed attributes hash. If you have the
3777
- Designer module loaded, this will also create a peer designer if needed.
3778
- */
3779
- create: function() {
3780
- var last = arguments[arguments.length - 1];
3781
-
3782
- if (last && last.theme) {
3783
- last.themeName = last.theme;
3784
- delete last.theme;
3785
- }
3786
-
3787
- var C=this, ret = new C(arguments);
3788
- if (SC.ViewDesigner) {
3789
- SC.ViewDesigner.didCreateView(ret, SC.$A(arguments));
3790
- }
3791
- return ret ;
3792
- },
3793
-
3794
- /**
3795
- Applies the passed localization hash to the component views. Call this
3796
- method before you call create(). Returns the receiver. Typically you
3797
- will do something like this:
3798
-
3799
- view = SC.View.design({...}).loc(localizationHash).create();
3800
-
3801
- @param {Hash} loc
3802
- @param rootElement {String} optional rootElement with prepped HTML
3803
- @returns {SC.View} receiver
3804
- */
3805
- loc: function(loc) {
3806
- var childLocs = loc.childViews;
3807
- delete loc.childViews; // clear out child views before applying to attrs
3808
-
3809
- this.applyLocalizedAttributes(loc) ;
3810
- if (SC.ViewDesigner) {
3811
- SC.ViewDesigner.didLoadLocalization(this, SC.$A(arguments));
3812
- }
3813
-
3814
- // apply localization recursively to childViews
3815
- var childViews = this.prototype.childViews, idx = childViews.length,
3816
- viewClass;
3817
- while(--idx>=0) {
3818
- viewClass = childViews[idx];
3819
- loc = childLocs[idx];
3820
- if (loc && viewClass && viewClass.loc) viewClass.loc(loc) ;
3821
- }
3822
-
3823
- return this; // done!
3824
- },
3825
-
3826
- /**
3827
- Internal method actually updates the localizated attributes on the view
3828
- class. This is overloaded in design mode to also save the attributes.
3829
- */
3830
- applyLocalizedAttributes: function(loc) {
3831
- SC.mixin(this.prototype, loc) ;
3832
- },
3833
-
3834
- views: {}
3835
-
3836
- }) ;
3837
-
3838
- // .......................................................
3839
- // OUTLET BUILDER
3840
- //
3841
-
3842
- /**
3843
- Generates a computed property that will look up the passed property path
3844
- the first time you try to get the value. Use this whenever you want to
3845
- define an outlet that points to another view or object. The root object
3846
- used for the path will be the receiver.
3847
- */
3848
- SC.outlet = function(path, root) {
3849
- return function(key) {
3850
- return (this[key] = SC.objectForPropertyPath(path, (root !== undefined) ? root : this)) ;
3851
- }.property();
3852
- };
3853
-
3854
- /** @private on unload clear cached divs. */
3855
- SC.View.unload = function() {
3856
- // delete view items this way to ensure the views are cleared. The hash
3857
- // itself may be owned by multiple view subclasses.
3858
- var views = SC.View.views;
3859
- if (views) {
3860
- for(var key in views) {
3861
- if (!views.hasOwnProperty(key)) continue ;
3862
- delete views[key];
3863
- }
3864
- }
3865
- } ;
3866
-
3867
- SC.View.runCallback = function(callback){
3868
- var additionalArgs = SC.$A(arguments).slice(1),
3869
- typeOfAction = SC.typeOf(callback.action);
3870
-
3871
- // if the action is a function, just try to call it.
3872
- if (typeOfAction == SC.T_FUNCTION) {
3873
- callback.action.apply(callback.target, additionalArgs);
3874
-
3875
- // otherwise, action should be a string. If it has a period, treat it
3876
- // like a property path.
3877
- } else if (typeOfAction === SC.T_STRING) {
3878
- if (callback.action.indexOf('.') >= 0) {
3879
- var path = callback.action.split('.') ;
3880
- var property = path.pop() ;
3881
-
3882
- var target = SC.objectForPropertyPath(path, window) ;
3883
- var action = target.get ? target.get(property) : target[property];
3884
- if (action && SC.typeOf(action) == SC.T_FUNCTION) {
3885
- action.apply(target, additionalArgs);
3886
- } else {
3887
- throw 'SC.runCallback could not find a function at %@'.fmt(callback.action) ;
3888
- }
3889
-
3890
- // otherwise, try to execute action direction on target or send down
3891
- // responder chain.
3892
- // FIXME: Add support for additionalArgs to this
3893
- // } else {
3894
- // SC.RootResponder.responder.sendAction(callback.action, callback.target, callback.source, callback.source.get("pane"), null, callback.source);
3895
- }
3896
- }
3897
- };
3898
-
3899
- /**
3900
- @class
3901
- @private
3902
- View Render Delegate Proxies are tool SC.Views use to:
3903
-
3904
- a) limit properties the render delegate can access to the displayProperties
3905
- b) look up 'display*' ('displayTitle' instead of 'title') to help deal with
3906
- differences between the render delegate's API and the view's.
3907
-
3908
- RenderDelegateProxies are fully valid data sources for render delegates. They
3909
- act as proxies to the view, interpreting the .get and .didChangeFor commands
3910
- based on the view's displayProperties.
3911
-
3912
- This tool is not useful outside of SC.View itself, and as such, is private.
3913
- */
3914
- SC.View._RenderDelegateProxy = {
3915
-
3916
- // for testing:
3917
- isViewRenderDelegateProxy: YES,
3918
-
3919
- /**
3920
- * Creates a View Render Delegate Proxy for the specified view.
3921
- *
3922
- * Implementation note: this creates a hash of the view's displayProperties
3923
- * array so that the proxy may quickly determine whether a property is a
3924
- * displayProperty or not. This could cause issues if the view's displayProperties
3925
- * array is modified after instantiation.
3926
- *
3927
- * @param {SC.View} view The view this proxy should proxy to.
3928
- * @returns SC.View._RenderDelegateProxy
3929
- */
3930
- createForView: function(view) {
3931
- var ret = SC.beget(this);
3932
-
3933
- // set up displayProperty lookup for performance
3934
- var dp = view.get('displayProperties'), lookup = {};
3935
- for (var idx = 0, len = dp.length; idx < len; idx++) {
3936
- lookup[dp[idx]] = YES;
3937
- }
3938
-
3939
- // also allow the few special properties through
3940
- lookup['theme'] = YES;
3941
-
3942
- ret._displayPropertiesLookup = lookup;
3943
- ret.renderState = {};
3944
-
3945
- ret.view = view;
3946
- return ret;
3947
- },
3948
-
3949
-
3950
- /**
3951
- * Provides the render delegate with any property it needs.
3952
- *
3953
- * This first looks up whether the property exists in the view's
3954
- * displayProperties, and whether it exists prefixed with 'display';
3955
- * for instance, if the render delegate asks for 'title', this will
3956
- * look for 'displayTitle' in the view's displayProperties array.
3957
- *
3958
- * @param {String} property The name of the property the render delegate needs.
3959
- * @returns The value.
3960
- */
3961
- get: function(property) {
3962
- if (this[property] !== undefined) return this[property];
3963
-
3964
- var displayProperty = 'display' + property.capitalize();
3965
-
3966
- if (this._displayPropertiesLookup[displayProperty]) {
3967
- return this.view.get(displayProperty);
3968
- } else if (this._displayPropertiesLookup[property]) {
3969
- return this.view.get(property);
3970
- }
3971
-
3972
- return undefined;
3973
- },
3974
-
3975
- /**
3976
- * Checks if any of the specified properties have changed.
3977
- *
3978
- * For each property passed, this first determines whether to use the
3979
- * 'display' prefix. Then, it calls view.didChangeFor with context and that
3980
- * property name.
3981
- *
3982
- */
3983
- didChangeFor: function(context) {
3984
- var len = arguments.length, idx;
3985
- for (idx = 1; idx < len; idx++) {
3986
- var property = arguments[idx],
3987
- displayProperty = 'display' + property.capitalize();
3988
-
3989
- if (this._displayPropertiesLookup[displayProperty]) {
3990
- if (this.view.didChangeFor(context, displayProperty)) return YES;
3991
- } else if (this._displayPropertiesLookup[property]) {
3992
- if (this.view.didChangeFor(context, property)) return YES;
3993
- }
3994
- }
3995
-
3996
- return NO;
3997
- }
3998
- };
3999
-
4000
- //unload views for IE, trying to collect memory.
4001
- if(SC.browser.msie) SC.Event.add(window, 'unload', SC.View, SC.View.unload) ;
4002
-
4003
-