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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
-