sproutit-sproutcore 1.0.0.20090416161445 → 1.0.0.20090720093355

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 (754) hide show
  1. data/Buildfile +4 -2
  2. data/frameworks/sproutcore/Buildfile +3 -2
  3. data/frameworks/sproutcore/README +2 -1
  4. data/frameworks/sproutcore/apps/docs/core.js +27 -0
  5. data/frameworks/sproutcore/apps/docs/design/Doc Viewer.graffle/QuickLook/Preview.pdf +0 -0
  6. data/frameworks/sproutcore/apps/docs/design/Doc Viewer.graffle/QuickLook/Thumbnail.tiff +0 -0
  7. data/frameworks/sproutcore/apps/docs/design/Doc Viewer.graffle/data.plist +14378 -0
  8. data/frameworks/sproutcore/apps/docs/design/Doc Viewer.graffle/image10.png +0 -0
  9. data/frameworks/sproutcore/apps/docs/design/Doc Viewer.graffle/image11.png +0 -0
  10. data/frameworks/sproutcore/apps/docs/design/Doc Viewer.graffle/image13.png +0 -0
  11. data/frameworks/sproutcore/apps/docs/design/Doc Viewer.graffle/image14.png +0 -0
  12. data/frameworks/sproutcore/apps/docs/design/Doc Viewer.graffle/image8.png +0 -0
  13. data/frameworks/sproutcore/apps/docs/design/Doc Viewer.graffle/image9.tiff +0 -0
  14. data/frameworks/sproutcore/apps/docs/english.lproj/loading.rhtml +9 -0
  15. data/frameworks/sproutcore/apps/docs/english.lproj/main_page.js +22 -0
  16. data/frameworks/sproutcore/apps/{sc_jsdoc → docs}/english.lproj/strings.js +7 -7
  17. data/frameworks/sproutcore/apps/docs/main.js +30 -0
  18. data/frameworks/sproutcore/apps/tests/controllers/detail.js +16 -0
  19. data/frameworks/sproutcore/apps/tests/controllers/source.js +29 -0
  20. data/frameworks/sproutcore/apps/tests/controllers/target.js +26 -0
  21. data/frameworks/sproutcore/apps/tests/controllers/targets.js +65 -26
  22. data/frameworks/sproutcore/apps/tests/controllers/tests.js +14 -19
  23. data/frameworks/sproutcore/apps/tests/core.js +114 -16
  24. data/frameworks/sproutcore/apps/tests/data_source.js +96 -0
  25. data/frameworks/sproutcore/apps/tests/english.lproj/main_page.css +22 -2
  26. data/frameworks/sproutcore/apps/tests/english.lproj/main_page.js +168 -22
  27. data/frameworks/sproutcore/apps/tests/english.lproj/strings.js +14 -5
  28. data/frameworks/sproutcore/apps/tests/fixtures/target.js +81 -37
  29. data/frameworks/sproutcore/apps/tests/fixtures/test.js +38 -37
  30. data/frameworks/sproutcore/apps/tests/main.js +9 -20
  31. data/frameworks/sproutcore/apps/tests/models/target.js +74 -31
  32. data/frameworks/sproutcore/apps/tests/models/test.js +30 -2
  33. data/frameworks/sproutcore/{frameworks/desktop/mixins/collection_item.js → apps/tests/states/no_targets.js} +16 -12
  34. data/frameworks/sproutcore/apps/tests/states/ready.js +56 -0
  35. data/frameworks/sproutcore/apps/tests/states/ready_detail.js +41 -0
  36. data/frameworks/sproutcore/apps/tests/states/ready_empty.js +48 -0
  37. data/frameworks/sproutcore/apps/tests/states/ready_list.js +41 -0
  38. data/frameworks/sproutcore/apps/tests/states/ready_loading.js +44 -0
  39. data/frameworks/sproutcore/apps/tests/states/ready_no_tests.js +31 -0
  40. data/frameworks/sproutcore/apps/tests/states/start.js +39 -0
  41. data/frameworks/sproutcore/apps/tests/tests/controllers/{test.js → detail.js} +3 -3
  42. data/frameworks/sproutcore/apps/tests/tests/controllers/source.js +15 -0
  43. data/frameworks/sproutcore/apps/tests/tests/controllers/target.js +15 -0
  44. data/frameworks/sproutcore/apps/tests/tests/controllers/targets.js +3 -3
  45. data/frameworks/sproutcore/apps/tests/tests/views/offset_checkbox.js +15 -0
  46. data/frameworks/sproutcore/apps/tests/views/offset_checkbox.js +26 -0
  47. data/frameworks/sproutcore/design/CollectionView State Charts.graffle +4848 -0
  48. data/frameworks/sproutcore/design/Design Charts.graffle +8788 -6375
  49. data/frameworks/sproutcore/design/SproutCore Design Template.graffle/QuickLook/Preview.pdf +0 -0
  50. data/frameworks/sproutcore/design/SproutCore Design Template.graffle/QuickLook/Thumbnail.tiff +0 -0
  51. data/frameworks/sproutcore/design/SproutCore Design Template.graffle/data.plist +1452 -0
  52. data/frameworks/sproutcore/design/SproutCore Design Template.graffle/image8.png +0 -0
  53. data/frameworks/sproutcore/design/TestRunner Design.graffle/QuickLook/Preview.pdf +0 -0
  54. data/frameworks/sproutcore/design/TestRunner Design.graffle/QuickLook/Thumbnail.tiff +0 -0
  55. data/frameworks/sproutcore/design/TestRunner Design.graffle/data.plist +24187 -0
  56. data/frameworks/sproutcore/design/TestRunner Design.graffle/image10.png +0 -0
  57. data/frameworks/sproutcore/design/TestRunner Design.graffle/image11.png +0 -0
  58. data/frameworks/sproutcore/design/TestRunner Design.graffle/image13.png +0 -0
  59. data/frameworks/sproutcore/design/TestRunner Design.graffle/image15.png +0 -0
  60. data/frameworks/sproutcore/design/TestRunner Design.graffle/image16.png +0 -0
  61. data/frameworks/sproutcore/design/TestRunner Design.graffle/image17.png +0 -0
  62. data/frameworks/sproutcore/design/TestRunner Design.graffle/image18.png +0 -0
  63. data/frameworks/sproutcore/design/TestRunner Design.graffle/image19.png +0 -0
  64. data/frameworks/sproutcore/design/TestRunner Design.graffle/image22.tiff +0 -0
  65. data/frameworks/sproutcore/design/TestRunner Design.graffle/image23.png +0 -0
  66. data/frameworks/sproutcore/design/TestRunner Design.graffle/image24.png +0 -0
  67. data/frameworks/sproutcore/design/TestRunner Design.graffle/image25.png +0 -0
  68. data/frameworks/sproutcore/design/TestRunner Design.graffle/image30.png +0 -0
  69. data/frameworks/sproutcore/design/TestRunner Design.graffle/image31.png +0 -0
  70. data/frameworks/sproutcore/design/TestRunner Design.graffle/image8.png +0 -0
  71. data/frameworks/sproutcore/design/TestRunner Design.graffle/image9.png +0 -0
  72. data/frameworks/sproutcore/frameworks/datastore/data_sources/cascade.js +2 -2
  73. data/frameworks/sproutcore/frameworks/datastore/data_sources/data_source.js +66 -49
  74. data/frameworks/sproutcore/frameworks/datastore/data_sources/fixtures.js +146 -31
  75. data/frameworks/sproutcore/frameworks/datastore/data_sources/fixtures_with_queries.js +238 -0
  76. data/frameworks/sproutcore/frameworks/datastore/models/many_attribute.js +27 -11
  77. data/frameworks/sproutcore/frameworks/datastore/models/record.js +163 -32
  78. data/frameworks/sproutcore/frameworks/datastore/models/record_attribute.js +67 -5
  79. data/frameworks/sproutcore/frameworks/datastore/system/many_array.js +157 -0
  80. data/frameworks/sproutcore/frameworks/datastore/system/nested_store.js +202 -19
  81. data/frameworks/sproutcore/frameworks/datastore/system/query.js +929 -78
  82. data/frameworks/sproutcore/frameworks/datastore/system/record_array.js +143 -5
  83. data/frameworks/sproutcore/frameworks/datastore/system/store.js +443 -125
  84. data/frameworks/sproutcore/frameworks/datastore/tests/data_sources/fixtures.js +38 -3
  85. data/frameworks/sproutcore/frameworks/datastore/tests/models/many_attribute.js +94 -0
  86. data/frameworks/sproutcore/frameworks/datastore/tests/models/record/core_methods.js +30 -0
  87. data/frameworks/sproutcore/frameworks/datastore/tests/models/record/normalize.js +238 -0
  88. data/frameworks/sproutcore/frameworks/datastore/tests/models/record_attribute.js +105 -16
  89. data/frameworks/sproutcore/frameworks/datastore/tests/system/many_array/core_methods.js +178 -0
  90. data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/chain.js +1 -1
  91. data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/commitChanges.js +9 -8
  92. data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/commitChangesFromNestedStore.js +6 -6
  93. data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/dataHashDidChange.js +6 -6
  94. data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/discardChanges.js +3 -3
  95. data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/readDataHash.js +7 -7
  96. data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/readEditableDataHash.js +4 -4
  97. data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/removeDataHash.js +7 -7
  98. data/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/writeDataHash.js +7 -7
  99. data/frameworks/sproutcore/frameworks/datastore/tests/system/query/compare_records.js +126 -0
  100. data/frameworks/sproutcore/frameworks/datastore/tests/system/query/evaluation.js +165 -0
  101. data/frameworks/sproutcore/frameworks/datastore/tests/system/query/evaluation_of_records.js +82 -0
  102. data/frameworks/sproutcore/frameworks/datastore/tests/system/query/find_all.js +362 -0
  103. data/frameworks/sproutcore/frameworks/datastore/tests/system/query/parsing.js +170 -0
  104. data/frameworks/sproutcore/frameworks/datastore/tests/system/query/record_type_is.js +43 -0
  105. data/frameworks/sproutcore/frameworks/datastore/tests/system/query/registered_comparisons.js +60 -0
  106. data/frameworks/sproutcore/frameworks/datastore/tests/system/query/registered_query_extensions.js +67 -0
  107. data/frameworks/sproutcore/frameworks/datastore/tests/system/record_array/core_methods.js +2 -13
  108. data/frameworks/sproutcore/frameworks/datastore/tests/system/store/commitRecord.js +3 -4
  109. data/frameworks/sproutcore/frameworks/datastore/tests/system/store/core_methods.js +73 -0
  110. data/frameworks/sproutcore/frameworks/datastore/tests/system/store/createRecord.js +15 -0
  111. data/frameworks/sproutcore/frameworks/datastore/tests/system/store/dataSourceCallbacks.js +4 -2
  112. data/frameworks/sproutcore/frameworks/datastore/tests/system/store/destroyRecord.js +1 -1
  113. data/frameworks/sproutcore/frameworks/datastore/tests/system/store/pushChanges.js +2 -2
  114. data/frameworks/sproutcore/frameworks/datastore/tests/system/store/recordDidChange.js +1 -1
  115. data/frameworks/sproutcore/frameworks/datastore/tests/system/store/retrieveRecord.js +2 -2
  116. data/frameworks/sproutcore/frameworks/debug/core.js +60 -0
  117. data/frameworks/sproutcore/frameworks/deprecated/core.js +0 -2
  118. data/frameworks/sproutcore/frameworks/deprecated/server/server.js +0 -1
  119. data/frameworks/sproutcore/frameworks/deprecated/system/browser.js +0 -2
  120. data/frameworks/sproutcore/frameworks/deprecated/system/classic_responder.js +0 -2
  121. data/frameworks/sproutcore/frameworks/deprecated/system/event.js +0 -2
  122. data/frameworks/sproutcore/frameworks/deprecated/system/misc.js +0 -2
  123. data/frameworks/sproutcore/frameworks/deprecated/system/object.js +0 -2
  124. data/frameworks/sproutcore/frameworks/deprecated/system/path_module.js +0 -1
  125. data/frameworks/sproutcore/frameworks/deprecated/system/string.js +0 -2
  126. data/frameworks/sproutcore/frameworks/designer/coders/design.js +1 -2
  127. data/frameworks/sproutcore/frameworks/designer/coders/localization.js +1 -2
  128. data/frameworks/sproutcore/frameworks/designer/coders/object.js +1 -1
  129. data/frameworks/sproutcore/frameworks/designer/controllers/page_design.js +1 -1
  130. data/frameworks/sproutcore/frameworks/designer/ext/page.js +0 -2
  131. data/frameworks/sproutcore/frameworks/designer/ext/view.js +0 -2
  132. data/frameworks/sproutcore/frameworks/designer/views/controls/button.js +2 -3
  133. data/frameworks/sproutcore/frameworks/designer/views/designer.js +24 -8
  134. data/frameworks/sproutcore/frameworks/designer/views/label.js +1 -2
  135. data/frameworks/sproutcore/frameworks/designer/views/mixins/button.js +0 -2
  136. data/frameworks/sproutcore/frameworks/designer/views/tab.js +1 -2
  137. data/frameworks/sproutcore/frameworks/desktop/english.lproj/alert.css +2 -2
  138. data/frameworks/sproutcore/frameworks/desktop/english.lproj/drag.css +6 -0
  139. data/frameworks/sproutcore/frameworks/desktop/english.lproj/list_item.css +63 -10
  140. data/frameworks/sproutcore/frameworks/desktop/english.lproj/menu_item_view.css +5 -4
  141. data/frameworks/sproutcore/frameworks/desktop/english.lproj/modal.css +5 -0
  142. data/frameworks/sproutcore/frameworks/desktop/english.lproj/panel.css +1 -0
  143. data/frameworks/sproutcore/frameworks/desktop/english.lproj/slider.css +5 -0
  144. data/frameworks/sproutcore/frameworks/desktop/english.lproj/split_divider.css +1 -0
  145. data/frameworks/sproutcore/frameworks/desktop/english.lproj/tab.css +1 -1
  146. data/frameworks/sproutcore/frameworks/desktop/mixins/collection_row_delegate.js +61 -0
  147. data/frameworks/sproutcore/frameworks/desktop/mixins/collection_view_delegate.js +136 -79
  148. data/frameworks/sproutcore/frameworks/desktop/panes/alert.js +55 -24
  149. data/frameworks/sproutcore/frameworks/desktop/panes/menu.js +295 -147
  150. data/frameworks/sproutcore/frameworks/desktop/panes/palette.js +1 -1
  151. data/frameworks/sproutcore/frameworks/desktop/panes/panel.js +1 -1
  152. data/frameworks/sproutcore/frameworks/desktop/panes/picker.js +18 -20
  153. data/frameworks/sproutcore/frameworks/desktop/panes/sheet.js +2 -2
  154. data/frameworks/sproutcore/frameworks/desktop/protocols/drop_target.js +4 -4
  155. data/frameworks/sproutcore/frameworks/desktop/system/drag.js +337 -231
  156. data/frameworks/sproutcore/frameworks/desktop/system/root_responder.js +3 -3
  157. data/frameworks/sproutcore/frameworks/desktop/tests/integration/dialog.js +1 -1
  158. data/frameworks/sproutcore/frameworks/desktop/tests/panes/alert/ui.js +2 -2
  159. data/frameworks/sproutcore/frameworks/desktop/tests/panes/menu/methods.js +46 -1
  160. data/frameworks/sproutcore/frameworks/desktop/tests/panes/menu/ui.js +4 -2
  161. data/frameworks/sproutcore/frameworks/desktop/tests/panes/palette/ui.js +5 -6
  162. data/frameworks/sproutcore/frameworks/desktop/tests/panes/panel/ui.js +11 -11
  163. data/frameworks/sproutcore/frameworks/desktop/tests/panes/picker/ui.js +11 -7
  164. data/frameworks/sproutcore/frameworks/desktop/tests/panes/sheet/ui.js +9 -9
  165. data/frameworks/sproutcore/frameworks/desktop/tests/views/button/ui.js +19 -0
  166. data/frameworks/sproutcore/frameworks/desktop/tests/views/checkbox/methods.js +0 -1
  167. data/frameworks/sproutcore/frameworks/desktop/tests/views/collection/content.js +249 -0
  168. data/frameworks/sproutcore/frameworks/desktop/tests/views/collection/deleteSelection.js +82 -0
  169. data/frameworks/sproutcore/frameworks/desktop/tests/views/collection/deselect.js +199 -0
  170. data/frameworks/sproutcore/frameworks/desktop/tests/views/collection/itemViewForContentIndex.js +288 -0
  171. data/frameworks/sproutcore/frameworks/desktop/tests/views/collection/layerIdFor.js +65 -0
  172. data/frameworks/sproutcore/frameworks/desktop/tests/views/collection/length.js +88 -0
  173. data/frameworks/sproutcore/frameworks/desktop/tests/views/collection/mouse.js +165 -0
  174. data/frameworks/sproutcore/frameworks/desktop/tests/views/collection/nowShowing.js +121 -0
  175. data/frameworks/sproutcore/frameworks/desktop/tests/views/collection/reload.js +177 -0
  176. data/frameworks/sproutcore/frameworks/desktop/tests/views/collection/select.js +240 -0
  177. data/frameworks/sproutcore/frameworks/desktop/tests/views/collection/selectNextItem.js +191 -0
  178. data/frameworks/sproutcore/frameworks/desktop/tests/views/collection/selectPreviousItem.js +197 -39
  179. data/frameworks/sproutcore/frameworks/desktop/tests/views/collection/selection.js +141 -0
  180. data/frameworks/sproutcore/frameworks/desktop/tests/views/collection/ui_diagram.js +182 -0
  181. data/frameworks/sproutcore/frameworks/desktop/tests/views/list/rowDelegate.js +183 -0
  182. data/frameworks/sproutcore/frameworks/desktop/tests/views/list/rowHeightForContentIndex.js +133 -0
  183. data/frameworks/sproutcore/frameworks/desktop/tests/views/list/rowOffsetForContentIndex.js +132 -0
  184. data/frameworks/sproutcore/frameworks/desktop/tests/views/list/ui_outline.js +56 -0
  185. data/frameworks/sproutcore/frameworks/desktop/tests/views/list/ui_row_heights.js +167 -0
  186. data/frameworks/sproutcore/frameworks/desktop/tests/views/list/ui_simple.js +127 -0
  187. data/frameworks/sproutcore/frameworks/desktop/tests/views/list_item.js +30 -1
  188. data/frameworks/sproutcore/frameworks/desktop/tests/views/menu_item/ui.js +8 -8
  189. data/frameworks/sproutcore/frameworks/desktop/tests/views/progress/ui.js +9 -9
  190. data/frameworks/sproutcore/frameworks/desktop/tests/views/scroller/methods.js +45 -6
  191. data/frameworks/sproutcore/frameworks/desktop/tests/views/segmented/methods.js +2 -1
  192. data/frameworks/sproutcore/frameworks/desktop/tests/views/segmented/ui.js +17 -1
  193. data/frameworks/sproutcore/frameworks/desktop/tests/views/select_field/ui.js +44 -29
  194. data/frameworks/sproutcore/frameworks/desktop/tests/views/stacked/ui_comments.js +231 -0
  195. data/frameworks/sproutcore/frameworks/desktop/tests/views/web/ui.js +1 -1
  196. data/frameworks/sproutcore/frameworks/desktop/views/button.js +15 -4
  197. data/frameworks/sproutcore/frameworks/desktop/views/checkbox.js +8 -1
  198. data/frameworks/sproutcore/frameworks/desktop/views/collection.js +1739 -1123
  199. data/frameworks/sproutcore/frameworks/desktop/views/form.js +0 -1
  200. data/frameworks/sproutcore/frameworks/desktop/views/grid.js +13 -11
  201. data/frameworks/sproutcore/frameworks/desktop/views/list.js +405 -571
  202. data/frameworks/sproutcore/frameworks/desktop/views/list_item.js +211 -74
  203. data/frameworks/sproutcore/frameworks/desktop/views/menu_item.js +319 -169
  204. data/frameworks/sproutcore/frameworks/desktop/views/popup_button.js +57 -51
  205. data/frameworks/sproutcore/frameworks/desktop/views/progress.js +2 -2
  206. data/frameworks/sproutcore/frameworks/desktop/views/scene.js +150 -2
  207. data/frameworks/sproutcore/frameworks/desktop/views/scroll.js +92 -50
  208. data/frameworks/sproutcore/frameworks/desktop/views/scroller.js +86 -63
  209. data/frameworks/sproutcore/frameworks/desktop/views/segmented.js +38 -22
  210. data/frameworks/sproutcore/frameworks/desktop/views/select_field.js +51 -12
  211. data/frameworks/sproutcore/frameworks/desktop/views/slider.js +2 -0
  212. data/frameworks/sproutcore/frameworks/desktop/views/source_list.js +17 -1087
  213. data/frameworks/sproutcore/frameworks/desktop/views/source_list_group.js +3 -3
  214. data/frameworks/sproutcore/frameworks/desktop/views/split.js +35 -9
  215. data/frameworks/sproutcore/frameworks/desktop/views/stacked.js +101 -0
  216. data/frameworks/sproutcore/frameworks/desktop/views/tab.js +23 -22
  217. data/frameworks/sproutcore/frameworks/desktop/views/toolbar.js +1 -1
  218. data/frameworks/sproutcore/frameworks/foundation/controllers/array.js +382 -363
  219. data/frameworks/sproutcore/frameworks/foundation/controllers/controller.js +7 -279
  220. data/frameworks/sproutcore/frameworks/foundation/controllers/object.js +212 -310
  221. data/frameworks/sproutcore/frameworks/foundation/controllers/tree.js +109 -0
  222. data/frameworks/sproutcore/frameworks/foundation/core.js +25 -0
  223. data/frameworks/sproutcore/frameworks/foundation/debug/control_test_pane.js +30 -8
  224. data/frameworks/sproutcore/frameworks/foundation/english.lproj/bootstrap.rhtml +19 -4
  225. data/frameworks/sproutcore/frameworks/foundation/english.lproj/core.css +219 -3
  226. data/frameworks/sproutcore/frameworks/foundation/english.lproj/debug/control-test-pane.css +1 -0
  227. data/frameworks/sproutcore/frameworks/foundation/english.lproj/label.css +30 -0
  228. data/frameworks/sproutcore/frameworks/foundation/english.lproj/strings.js +15 -0
  229. data/frameworks/sproutcore/frameworks/{desktop → foundation}/english.lproj/text_field.css +19 -3
  230. data/frameworks/sproutcore/frameworks/foundation/english.lproj/view.css +6 -1
  231. data/frameworks/sproutcore/frameworks/foundation/license.js +19 -0
  232. data/frameworks/sproutcore/frameworks/foundation/mixins/button.js +15 -7
  233. data/frameworks/sproutcore/frameworks/foundation/mixins/collection_content.js +171 -0
  234. data/frameworks/sproutcore/frameworks/foundation/mixins/control.js +5 -5
  235. data/frameworks/sproutcore/frameworks/foundation/mixins/inline_text_field.js +462 -0
  236. data/frameworks/sproutcore/frameworks/foundation/mixins/selection_support.js +162 -84
  237. data/frameworks/sproutcore/frameworks/foundation/mixins/static_layout.js +33 -2
  238. data/frameworks/sproutcore/frameworks/foundation/mixins/string.js +17 -3
  239. data/frameworks/sproutcore/frameworks/foundation/mixins/tree_item_content.js +159 -0
  240. data/frameworks/sproutcore/frameworks/foundation/panes/pane.js +49 -20
  241. data/frameworks/sproutcore/frameworks/foundation/private/tree_item_observer.js +887 -0
  242. data/frameworks/sproutcore/frameworks/foundation/system/application.js +36 -0
  243. data/frameworks/sproutcore/frameworks/foundation/system/benchmark.js +310 -62
  244. data/frameworks/sproutcore/frameworks/foundation/system/datetime.js +729 -0
  245. data/frameworks/sproutcore/frameworks/foundation/system/event.js +57 -21
  246. data/frameworks/sproutcore/frameworks/foundation/system/ready.js +11 -5
  247. data/frameworks/sproutcore/frameworks/foundation/system/render_context.js +55 -16
  248. data/frameworks/sproutcore/frameworks/foundation/system/request.js +152 -27
  249. data/frameworks/sproutcore/frameworks/foundation/system/responder.js +120 -0
  250. data/frameworks/sproutcore/frameworks/foundation/system/responder_context.js +243 -0
  251. data/frameworks/sproutcore/frameworks/foundation/system/root_responder.js +29 -6
  252. data/frameworks/sproutcore/frameworks/foundation/system/routes.js +143 -102
  253. data/frameworks/sproutcore/frameworks/foundation/system/user_defaults.js +9 -2
  254. data/frameworks/sproutcore/frameworks/foundation/system/utils.js +104 -9
  255. data/frameworks/sproutcore/frameworks/foundation/tests/controllers/array/array_case.js +182 -0
  256. data/frameworks/sproutcore/frameworks/foundation/tests/controllers/array/enum_case.js +193 -0
  257. data/frameworks/sproutcore/frameworks/foundation/tests/controllers/array/null_case.js +64 -0
  258. data/frameworks/sproutcore/frameworks/foundation/tests/controllers/array/single_case.js +136 -0
  259. data/frameworks/sproutcore/frameworks/foundation/tests/controllers/object/empty_case.js +82 -0
  260. data/frameworks/sproutcore/frameworks/foundation/tests/controllers/object/multiple_case.js +111 -0
  261. data/frameworks/sproutcore/frameworks/foundation/tests/controllers/object/single_case.js +193 -0
  262. data/frameworks/sproutcore/frameworks/foundation/tests/controllers/object/single_enumerable_case.js +179 -0
  263. data/frameworks/sproutcore/frameworks/foundation/tests/controllers/tree/outline_case.js +108 -0
  264. data/frameworks/sproutcore/frameworks/foundation/tests/mixins/button/keyEquivalents.js +35 -0
  265. data/frameworks/sproutcore/frameworks/foundation/tests/mixins/staticLayout.js +128 -0
  266. data/frameworks/sproutcore/frameworks/foundation/tests/mixins/string.js +17 -0
  267. data/frameworks/sproutcore/frameworks/foundation/tests/private/tree_item_observer/flat_case.js +325 -0
  268. data/frameworks/sproutcore/frameworks/foundation/tests/private/tree_item_observer/group_case.js +718 -0
  269. data/frameworks/sproutcore/frameworks/foundation/tests/private/tree_item_observer/outline_case.js +484 -0
  270. data/frameworks/sproutcore/frameworks/foundation/tests/system/core_query/jquery_core.js +28 -28
  271. data/frameworks/sproutcore/frameworks/foundation/tests/system/core_query/jquery_selector.js +1 -1
  272. data/frameworks/sproutcore/frameworks/foundation/tests/system/datetime.js +151 -0
  273. data/frameworks/sproutcore/frameworks/foundation/tests/system/render_context/get.js +2 -2
  274. data/frameworks/sproutcore/frameworks/foundation/tests/system/render_context/helpers_attr.js +1 -1
  275. data/frameworks/sproutcore/frameworks/foundation/tests/system/render_context/helpers_basic.js +1 -1
  276. data/frameworks/sproutcore/frameworks/foundation/tests/system/render_context/helpers_className.js +12 -12
  277. data/frameworks/sproutcore/frameworks/foundation/tests/system/render_context/helpers_style.js +1 -1
  278. data/frameworks/sproutcore/frameworks/foundation/tests/system/request.js +52 -14
  279. data/frameworks/sproutcore/frameworks/foundation/tests/system/root_responder/root_responder.js +27 -23
  280. data/frameworks/sproutcore/frameworks/foundation/tests/system/timer/schedule.js +1 -1
  281. data/frameworks/sproutcore/frameworks/foundation/tests/validators/date.js +12 -10
  282. data/frameworks/sproutcore/frameworks/foundation/tests/views/label/ui.js +148 -0
  283. data/frameworks/sproutcore/frameworks/foundation/tests/views/pane/append_remove.js +1 -1
  284. data/frameworks/sproutcore/frameworks/{desktop → foundation}/tests/views/text_field/methods.js +0 -0
  285. data/frameworks/sproutcore/frameworks/{desktop → foundation}/tests/views/text_field/ui.js +53 -1
  286. data/frameworks/sproutcore/frameworks/foundation/tests/views/view/clippingFrame.js +1 -0
  287. data/frameworks/sproutcore/frameworks/foundation/tests/views/view/convertFrames.js +1 -1
  288. data/frameworks/sproutcore/frameworks/foundation/tests/views/view/createChildViews.js +35 -0
  289. data/frameworks/sproutcore/frameworks/foundation/tests/views/view/findLayerInParentLayer.js +1 -1
  290. data/frameworks/sproutcore/frameworks/foundation/tests/views/view/isVisible.js +51 -0
  291. data/frameworks/sproutcore/frameworks/foundation/tests/views/view/isVisibleInWindow.js +12 -1
  292. data/frameworks/sproutcore/frameworks/foundation/tests/views/view/layoutStyle.js +83 -3
  293. data/frameworks/sproutcore/frameworks/foundation/tests/views/view/prepareContext.js +1 -1
  294. data/frameworks/sproutcore/frameworks/foundation/tests/views/view/updateLayer.js +4 -0
  295. data/frameworks/sproutcore/frameworks/foundation/views/container.js +1 -1
  296. data/frameworks/sproutcore/frameworks/foundation/views/field.js +27 -16
  297. data/frameworks/sproutcore/frameworks/foundation/views/image.js +4 -1
  298. data/frameworks/sproutcore/frameworks/foundation/views/label.js +16 -6
  299. data/frameworks/sproutcore/frameworks/{desktop → foundation}/views/text_field.js +39 -15
  300. data/frameworks/sproutcore/frameworks/foundation/views/view.js +328 -83
  301. data/frameworks/sproutcore/frameworks/runtime/README +1 -0
  302. data/frameworks/sproutcore/frameworks/runtime/core.js +110 -31
  303. data/frameworks/sproutcore/frameworks/runtime/debug/test_suites/array/base.js +238 -0
  304. data/frameworks/sproutcore/frameworks/runtime/debug/test_suites/array/indexOf.js +33 -0
  305. data/frameworks/sproutcore/frameworks/runtime/debug/test_suites/array/insertAt.js +121 -0
  306. data/frameworks/sproutcore/frameworks/runtime/debug/test_suites/array/objectAt.js +34 -0
  307. data/frameworks/sproutcore/frameworks/runtime/debug/test_suites/array/popObject.js +50 -0
  308. data/frameworks/sproutcore/frameworks/runtime/debug/test_suites/array/pushObject.js +46 -0
  309. data/frameworks/sproutcore/frameworks/runtime/debug/test_suites/array/rangeObserver.js +371 -0
  310. data/frameworks/sproutcore/frameworks/runtime/debug/test_suites/array/removeAt.js +100 -0
  311. data/frameworks/sproutcore/frameworks/runtime/debug/test_suites/array/removeObject.js +49 -0
  312. data/frameworks/sproutcore/frameworks/runtime/debug/test_suites/array/replace.js +94 -0
  313. data/frameworks/sproutcore/frameworks/runtime/debug/test_suites/array/shiftObject.js +50 -0
  314. data/frameworks/sproutcore/frameworks/runtime/debug/test_suites/array/unshiftObject.js +47 -0
  315. data/frameworks/sproutcore/frameworks/runtime/mixins/array.js +320 -110
  316. data/frameworks/sproutcore/frameworks/runtime/mixins/copyable.js +64 -0
  317. data/frameworks/sproutcore/frameworks/runtime/mixins/delegate_support.js +44 -6
  318. data/frameworks/sproutcore/frameworks/runtime/mixins/enumerable.js +142 -77
  319. data/frameworks/sproutcore/frameworks/runtime/mixins/freezable.js +104 -0
  320. data/frameworks/sproutcore/frameworks/runtime/mixins/observable.js +298 -142
  321. data/frameworks/sproutcore/frameworks/runtime/private/chain_observer.js +17 -11
  322. data/frameworks/sproutcore/frameworks/runtime/private/observer_queue.js +55 -15
  323. data/frameworks/sproutcore/frameworks/runtime/private/observer_set.js +29 -5
  324. data/frameworks/sproutcore/frameworks/runtime/protocols/observable_protocol.js +40 -0
  325. data/frameworks/sproutcore/frameworks/runtime/system/binding.js +39 -15
  326. data/frameworks/sproutcore/frameworks/runtime/system/index_set.js +1166 -0
  327. data/frameworks/sproutcore/frameworks/runtime/system/object.js +33 -15
  328. data/frameworks/sproutcore/frameworks/runtime/system/range_observer.js +201 -35
  329. data/frameworks/sproutcore/frameworks/runtime/system/run_loop.js +42 -15
  330. data/frameworks/sproutcore/frameworks/runtime/system/selection_set.js +649 -0
  331. data/frameworks/sproutcore/frameworks/runtime/system/set.js +183 -54
  332. data/frameworks/sproutcore/frameworks/runtime/system/sparse_array.js +20 -11
  333. data/frameworks/sproutcore/frameworks/runtime/tests/core/clone.js +2 -2
  334. data/frameworks/sproutcore/frameworks/runtime/tests/core/compare.js +44 -0
  335. data/frameworks/sproutcore/frameworks/runtime/tests/core/console.js +16 -0
  336. data/frameworks/sproutcore/frameworks/runtime/tests/core/keys.js +1 -1
  337. data/frameworks/sproutcore/frameworks/runtime/tests/core/makeArray.js +1 -1
  338. data/frameworks/sproutcore/frameworks/runtime/tests/core/objectForPropertyPath.js +5 -5
  339. data/frameworks/sproutcore/frameworks/runtime/tests/mixins/array.js +57 -0
  340. data/frameworks/sproutcore/frameworks/runtime/tests/mixins/enumerable.js +21 -2
  341. data/frameworks/sproutcore/frameworks/runtime/tests/mixins/observable/observable.js +249 -129
  342. data/frameworks/sproutcore/frameworks/runtime/tests/mixins/observable/propertyChanges.js +11 -2
  343. data/frameworks/sproutcore/frameworks/runtime/tests/private/observer_queue/isObservingSuspended.js +55 -0
  344. data/frameworks/sproutcore/frameworks/runtime/tests/system/binding.js +81 -6
  345. data/frameworks/sproutcore/frameworks/runtime/tests/system/index_set/add.js +195 -0
  346. data/frameworks/sproutcore/frameworks/runtime/tests/system/index_set/clone.js +43 -0
  347. data/frameworks/sproutcore/frameworks/runtime/tests/system/index_set/contains.js +74 -0
  348. data/frameworks/sproutcore/frameworks/runtime/tests/system/index_set/create.js +42 -0
  349. data/frameworks/sproutcore/frameworks/runtime/tests/system/index_set/indexAfter.js +38 -0
  350. data/frameworks/sproutcore/frameworks/runtime/tests/system/index_set/indexBefore.js +38 -0
  351. data/frameworks/sproutcore/frameworks/runtime/tests/system/index_set/intersects.js +74 -0
  352. data/frameworks/sproutcore/frameworks/runtime/tests/system/index_set/max.js +40 -0
  353. data/frameworks/sproutcore/frameworks/runtime/tests/system/index_set/min.js +40 -0
  354. data/frameworks/sproutcore/frameworks/runtime/tests/system/index_set/rangeStartForIndex.js +36 -0
  355. data/frameworks/sproutcore/frameworks/runtime/tests/system/index_set/remove.js +189 -0
  356. data/frameworks/sproutcore/frameworks/runtime/tests/system/index_set/without.js +89 -0
  357. data/frameworks/sproutcore/frameworks/runtime/tests/system/object/base.js +1 -1
  358. data/frameworks/sproutcore/frameworks/runtime/tests/system/range_observer/create.js +59 -0
  359. data/frameworks/sproutcore/frameworks/runtime/tests/system/range_observer/destroy.js +75 -0
  360. data/frameworks/sproutcore/frameworks/runtime/tests/system/range_observer/objectPropertyDidChange.js +117 -0
  361. data/frameworks/sproutcore/frameworks/runtime/tests/system/range_observer/rangeDidChange.js +110 -0
  362. data/frameworks/sproutcore/frameworks/runtime/tests/system/range_observer/update.js +65 -0
  363. data/frameworks/sproutcore/frameworks/runtime/tests/system/run_loop.js +3 -3
  364. data/frameworks/sproutcore/frameworks/runtime/tests/system/selection_set/add.js +92 -0
  365. data/frameworks/sproutcore/frameworks/runtime/tests/system/selection_set/copy.js +17 -0
  366. data/frameworks/sproutcore/frameworks/runtime/tests/system/selection_set/indexSetForSource.js +85 -0
  367. data/frameworks/sproutcore/frameworks/runtime/tests/system/selection_set/isEqual.js +60 -0
  368. data/frameworks/sproutcore/frameworks/runtime/tests/system/selection_set/remove.js +87 -0
  369. data/frameworks/sproutcore/frameworks/runtime/tests/system/set.js +4 -25
  370. data/frameworks/sproutcore/frameworks/runtime/tests/system/sparse_array.js +39 -1
  371. data/frameworks/sproutcore/frameworks/testing/core.js +183 -0
  372. data/frameworks/sproutcore/frameworks/testing/english.lproj/runner.css +126 -0
  373. data/frameworks/sproutcore/frameworks/testing/extras.js +0 -26
  374. data/frameworks/sproutcore/frameworks/testing/qunit.js +33 -25
  375. data/frameworks/sproutcore/frameworks/testing/system/dump.js +205 -0
  376. data/frameworks/sproutcore/frameworks/testing/system/equiv.js +201 -0
  377. data/frameworks/sproutcore/frameworks/testing/system/plan.js +691 -0
  378. data/frameworks/sproutcore/frameworks/testing/system/runner.js +209 -0
  379. data/frameworks/sproutcore/frameworks/testing/system/suite.js +228 -0
  380. data/frameworks/sproutcore/frameworks/testing/utils.js +8 -1
  381. data/frameworks/sproutcore/lib/index.rhtml +4 -1
  382. data/frameworks/sproutcore/themes/standard_theme/Source/icons/16/10.png +0 -0
  383. data/frameworks/sproutcore/themes/standard_theme/Source/icons/16/100.png +0 -0
  384. data/frameworks/sproutcore/themes/standard_theme/Source/icons/16/102.png +0 -0
  385. data/frameworks/sproutcore/themes/standard_theme/Source/icons/16/110.png +0 -0
  386. data/frameworks/sproutcore/themes/standard_theme/Source/icons/16/120.png +0 -0
  387. data/frameworks/sproutcore/themes/standard_theme/Source/icons/16/127.png +0 -0
  388. data/frameworks/sproutcore/themes/standard_theme/Source/icons/16/18.png +0 -0
  389. data/frameworks/sproutcore/themes/standard_theme/Source/icons/16/19.png +0 -0
  390. data/frameworks/sproutcore/themes/standard_theme/Source/icons/16/2.png +0 -0
  391. data/frameworks/sproutcore/themes/standard_theme/Source/icons/16/24.png +0 -0
  392. data/frameworks/sproutcore/themes/standard_theme/Source/icons/16/26.png +0 -0
  393. data/frameworks/sproutcore/themes/standard_theme/Source/icons/16/27.png +0 -0
  394. data/frameworks/sproutcore/themes/standard_theme/Source/icons/16/28.png +0 -0
  395. data/frameworks/sproutcore/themes/standard_theme/Source/icons/16/29.png +0 -0
  396. data/frameworks/sproutcore/themes/standard_theme/Source/icons/16/30.png +0 -0
  397. data/frameworks/sproutcore/themes/standard_theme/Source/icons/16/31.png +0 -0
  398. data/frameworks/sproutcore/themes/standard_theme/Source/icons/16/33.png +0 -0
  399. data/frameworks/sproutcore/themes/standard_theme/Source/icons/16/37.png +0 -0
  400. data/frameworks/sproutcore/themes/standard_theme/Source/icons/16/41.png +0 -0
  401. data/frameworks/sproutcore/themes/standard_theme/Source/icons/16/99.png +0 -0
  402. data/frameworks/sproutcore/themes/standard_theme/Source/icons/24/10.png +0 -0
  403. data/frameworks/sproutcore/themes/standard_theme/Source/icons/24/100.png +0 -0
  404. data/frameworks/sproutcore/themes/standard_theme/Source/icons/24/102.png +0 -0
  405. data/frameworks/sproutcore/themes/standard_theme/Source/icons/24/110.png +0 -0
  406. data/frameworks/sproutcore/themes/standard_theme/Source/icons/24/120.png +0 -0
  407. data/frameworks/sproutcore/themes/standard_theme/Source/icons/24/127.png +0 -0
  408. data/frameworks/sproutcore/themes/standard_theme/Source/icons/24/18.png +0 -0
  409. data/frameworks/sproutcore/themes/standard_theme/Source/icons/24/19.png +0 -0
  410. data/frameworks/sproutcore/themes/standard_theme/Source/icons/24/2.png +0 -0
  411. data/frameworks/sproutcore/themes/standard_theme/Source/icons/24/24.png +0 -0
  412. data/frameworks/sproutcore/themes/standard_theme/Source/icons/24/26.png +0 -0
  413. data/frameworks/sproutcore/themes/standard_theme/Source/icons/24/27.png +0 -0
  414. data/frameworks/sproutcore/themes/standard_theme/Source/icons/24/28.png +0 -0
  415. data/frameworks/sproutcore/themes/standard_theme/Source/icons/24/29.png +0 -0
  416. data/frameworks/sproutcore/themes/standard_theme/Source/icons/24/30.png +0 -0
  417. data/frameworks/sproutcore/themes/standard_theme/Source/icons/24/31.png +0 -0
  418. data/frameworks/sproutcore/themes/standard_theme/Source/icons/24/33.png +0 -0
  419. data/frameworks/sproutcore/themes/standard_theme/Source/icons/24/37.png +0 -0
  420. data/frameworks/sproutcore/themes/standard_theme/Source/icons/24/41.png +0 -0
  421. data/frameworks/sproutcore/themes/standard_theme/Source/icons/24/99.png +0 -0
  422. data/frameworks/sproutcore/themes/standard_theme/Source/icons/32/10.png +0 -0
  423. data/frameworks/sproutcore/themes/standard_theme/Source/icons/32/100.png +0 -0
  424. data/frameworks/sproutcore/themes/standard_theme/Source/icons/32/102.png +0 -0
  425. data/frameworks/sproutcore/themes/standard_theme/Source/icons/32/110.png +0 -0
  426. data/frameworks/sproutcore/themes/standard_theme/Source/icons/32/120.png +0 -0
  427. data/frameworks/sproutcore/themes/standard_theme/Source/icons/32/127.png +0 -0
  428. data/frameworks/sproutcore/themes/standard_theme/Source/icons/32/18.png +0 -0
  429. data/frameworks/sproutcore/themes/standard_theme/Source/icons/32/19.png +0 -0
  430. data/frameworks/sproutcore/themes/standard_theme/Source/icons/32/2.png +0 -0
  431. data/frameworks/sproutcore/themes/standard_theme/Source/icons/32/24.png +0 -0
  432. data/frameworks/sproutcore/themes/standard_theme/Source/icons/32/26.png +0 -0
  433. data/frameworks/sproutcore/themes/standard_theme/Source/icons/32/27.png +0 -0
  434. data/frameworks/sproutcore/themes/standard_theme/Source/icons/32/28.png +0 -0
  435. data/frameworks/sproutcore/themes/standard_theme/Source/icons/32/29.png +0 -0
  436. data/frameworks/sproutcore/themes/standard_theme/Source/icons/32/30.png +0 -0
  437. data/frameworks/sproutcore/themes/standard_theme/Source/icons/32/31.png +0 -0
  438. data/frameworks/sproutcore/themes/standard_theme/Source/icons/32/33.png +0 -0
  439. data/frameworks/sproutcore/themes/standard_theme/Source/icons/32/37.png +0 -0
  440. data/frameworks/sproutcore/themes/standard_theme/Source/icons/32/41.png +0 -0
  441. data/frameworks/sproutcore/themes/standard_theme/Source/icons/32/99.png +0 -0
  442. data/frameworks/sproutcore/themes/standard_theme/Source/icons/48/10.png +0 -0
  443. data/frameworks/sproutcore/themes/standard_theme/Source/icons/48/18.png +0 -0
  444. data/frameworks/sproutcore/themes/standard_theme/Source/icons/48/19.png +0 -0
  445. data/frameworks/sproutcore/themes/standard_theme/Source/icons/48/2.png +0 -0
  446. data/frameworks/sproutcore/themes/standard_theme/Source/sc-theme-repeat-x-2.psd +0 -0
  447. data/frameworks/sproutcore/themes/standard_theme/Source/sc-theme-repeat-x.psd +0 -0
  448. data/frameworks/sproutcore/themes/standard_theme/Source/sc-theme-sprite.psd +0 -0
  449. data/frameworks/sproutcore/themes/standard_theme/Source/sc-theme-ysprite.psd +0 -0
  450. data/frameworks/sproutcore/themes/standard_theme/Source/shared-icons.psd +0 -0
  451. data/frameworks/sproutcore/themes/standard_theme/Source/sproutcore-logo.psd +0 -0
  452. data/frameworks/sproutcore/themes/standard_theme/Source/sticky-note.psd +0 -0
  453. data/frameworks/sproutcore/themes/standard_theme/english.lproj/button.css +191 -193
  454. data/frameworks/sproutcore/themes/standard_theme/english.lproj/checkbox.css +11 -10
  455. data/frameworks/sproutcore/themes/standard_theme/english.lproj/collection.css +85 -4
  456. data/frameworks/sproutcore/themes/standard_theme/english.lproj/core.css +15 -2
  457. data/frameworks/sproutcore/themes/standard_theme/english.lproj/images/sc-theme-repeat-x.png +0 -0
  458. data/frameworks/sproutcore/themes/standard_theme/english.lproj/label.css +0 -26
  459. data/frameworks/sproutcore/themes/standard_theme/english.lproj/list_item.css +30 -0
  460. data/frameworks/sproutcore/themes/standard_theme/english.lproj/progress.css +9 -6
  461. data/frameworks/sproutcore/themes/standard_theme/english.lproj/radio.css +20 -11
  462. data/frameworks/sproutcore/themes/standard_theme/english.lproj/segmented.css +192 -54
  463. data/frameworks/sproutcore/themes/standard_theme/english.lproj/slider.css +56 -24
  464. data/frameworks/sproutcore/themes/standard_theme/english.lproj/tab.css +13 -7
  465. data/frameworks/sproutcore/themes/standard_theme/english.lproj/text_field.css +1 -4
  466. data/frameworks/sproutcore/themes/standard_theme/english.lproj/toolbar.css +4 -1
  467. data/lib/sproutcore/builders/minify.rb +2 -2
  468. data/lib/sproutcore/builders/test.rb +1 -1
  469. data/lib/sproutcore/buildfile.rb +1 -0
  470. data/lib/sproutcore/rack/dev.rb +1 -1
  471. data/lib/sproutcore/rack/filesystem.rb +265 -0
  472. data/lib/sproutcore/rack/proxy.rb +11 -3
  473. data/lib/sproutcore/rack/service.rb +11 -1
  474. data/lib/sproutcore/tools.rb +11 -1
  475. data/lib/sproutcore/tools/server.rb +6 -4
  476. data/vendor/jsdoc/README.txt +151 -0
  477. data/vendor/jsdoc/changes.txt +47 -0
  478. metadata +263 -308
  479. data/frameworks/sproutcore/apps/sc_jsdoc/controllers/docs.js +0 -149
  480. data/frameworks/sproutcore/apps/sc_jsdoc/core.js +0 -16
  481. data/frameworks/sproutcore/apps/sc_jsdoc/english.lproj/body.css +0 -17
  482. data/frameworks/sproutcore/apps/sc_jsdoc/english.lproj/body.js +0 -99
  483. data/frameworks/sproutcore/apps/sc_jsdoc/english.lproj/images/sproutcore-logo.png +0 -0
  484. data/frameworks/sproutcore/apps/sc_jsdoc/main.js +0 -27
  485. data/frameworks/sproutcore/apps/sc_jsdoc/models/doc.js +0 -21
  486. data/frameworks/sproutcore/apps/sc_qunit/controllers/runner.js +0 -209
  487. data/frameworks/sproutcore/apps/sc_qunit/core.js +0 -16
  488. data/frameworks/sproutcore/apps/sc_qunit/english.lproj/body.css +0 -17
  489. data/frameworks/sproutcore/apps/sc_qunit/english.lproj/body.js +0 -107
  490. data/frameworks/sproutcore/apps/sc_qunit/english.lproj/images/sproutcore-logo.png +0 -0
  491. data/frameworks/sproutcore/apps/sc_qunit/english.lproj/strings.js +0 -15
  492. data/frameworks/sproutcore/apps/sc_qunit/main.js +0 -18
  493. data/frameworks/sproutcore/apps/sc_qunit/models/test.js +0 -24
  494. data/frameworks/sproutcore/apps/sc_qunit/views/test_iframe.js +0 -52
  495. data/frameworks/sproutcore/apps/tests/controllers/test.js +0 -20
  496. data/frameworks/sproutcore/frameworks/desktop/english.lproj/images/blank.gif +0 -0
  497. data/frameworks/sproutcore/frameworks/desktop/english.lproj/menu.css +0 -83
  498. data/frameworks/sproutcore/frameworks/desktop/english.lproj/palette.css +0 -3
  499. data/frameworks/sproutcore/frameworks/desktop/tests/views/collection/methods.js +0 -10
  500. data/frameworks/sproutcore/frameworks/desktop/tests/views/list/methods.js +0 -10
  501. data/frameworks/sproutcore/frameworks/desktop/tests/views/list/ui.js +0 -110
  502. data/frameworks/sproutcore/frameworks/foundation/mixins/responder.js +0 -156
  503. data/frameworks/sproutcore/frameworks/foundation/tests/controllers/array.js +0 -118
  504. data/frameworks/sproutcore/frameworks/foundation/tests/controllers/controller.js +0 -268
  505. data/frameworks/sproutcore/frameworks/foundation/tests/controllers/object.js +0 -433
  506. data/frameworks/sproutcore/frameworks/runtime/tests/system/array.js +0 -263
  507. data/frameworks/sproutcore/frameworks/testing/tests/debug/qunit.js +0 -25
  508. data/spec/buildtasks/manifest/spec_helper.rb +0 -35
  509. data/spec/buildtasks/target_spec.rb +0 -214
  510. data/spec/fixtures/builder_tests/Buildfile +0 -15
  511. data/spec/fixtures/builder_tests/apps/combine_test/a.js +0 -1
  512. data/spec/fixtures/builder_tests/apps/combine_test/b.js +0 -1
  513. data/spec/fixtures/builder_tests/apps/combine_test/c.js +0 -1
  514. data/spec/fixtures/builder_tests/apps/combine_test/english.lproj/a.css +0 -1
  515. data/spec/fixtures/builder_tests/apps/combine_test/english.lproj/b.css +0 -1
  516. data/spec/fixtures/builder_tests/apps/combine_test/english.lproj/c.css +0 -1
  517. data/spec/fixtures/builder_tests/apps/html_test/english.lproj/bar1_sample.rhtml +0 -2
  518. data/spec/fixtures/builder_tests/apps/html_test/english.lproj/erb_sample.html.erb +0 -1
  519. data/spec/fixtures/builder_tests/apps/html_test/english.lproj/icons/image.png +0 -0
  520. data/spec/fixtures/builder_tests/apps/html_test/english.lproj/image.jpg +0 -0
  521. data/spec/fixtures/builder_tests/apps/html_test/english.lproj/rhtml_sample.rhtml +0 -1
  522. data/spec/fixtures/builder_tests/apps/html_test/english.lproj/strings.js +0 -4
  523. data/spec/fixtures/builder_tests/apps/html_test/english.lproj/style.css +0 -0
  524. data/spec/fixtures/builder_tests/apps/html_test/french.lproj/french-icons/fr.png +0 -0
  525. data/spec/fixtures/builder_tests/apps/html_test/french.lproj/strings.js +0 -4
  526. data/spec/fixtures/builder_tests/apps/html_test/lib/layout_template.rhtml +0 -1
  527. data/spec/fixtures/builder_tests/apps/html_test/scripts.js +0 -0
  528. data/spec/fixtures/builder_tests/apps/javascript_test/sc_static.js +0 -15
  529. data/spec/fixtures/builder_tests/apps/javascript_test/sc_super.js +0 -4
  530. data/spec/fixtures/builder_tests/apps/javascript_test/strings.js +0 -7
  531. data/spec/fixtures/builder_tests/apps/sass_test/sample.sass +0 -3
  532. data/spec/fixtures/builder_tests/apps/strings_test/lproj/strings.js +0 -8
  533. data/spec/fixtures/builder_tests/apps/stylesheet_test/build_directives.css +0 -9
  534. data/spec/fixtures/builder_tests/apps/stylesheet_test/sc_static.css +0 -12
  535. data/spec/fixtures/builder_tests/apps/test_test/lib/alt_layout.rhtml +0 -1
  536. data/spec/fixtures/builder_tests/apps/test_test/lib/test_layout.rhtml +0 -3
  537. data/spec/fixtures/builder_tests/apps/test_test/tests/qunit_test.js +0 -1
  538. data/spec/fixtures/builder_tests/apps/test_test/tests/qunit_test2.js +0 -1
  539. data/spec/fixtures/builder_tests/apps/test_test/tests/rhtml_test.rhtml +0 -4
  540. data/spec/fixtures/builder_tests/frameworks/debug/core.js +0 -0
  541. data/spec/fixtures/builder_tests/frameworks/debug/english.lproj/dummy.css +0 -0
  542. data/spec/fixtures/builder_tests/frameworks/qunit/core.js +0 -0
  543. data/spec/fixtures/builder_tests/frameworks/qunit/english.lproj/dummy.css +0 -0
  544. data/spec/fixtures/builder_tests/frameworks/req_target_1/english.lproj/req_style_1.css +0 -0
  545. data/spec/fixtures/builder_tests/frameworks/req_target_1/english.lproj/strings.js +0 -4
  546. data/spec/fixtures/builder_tests/frameworks/req_target_1/english.lproj/test.rhtml +0 -1
  547. data/spec/fixtures/builder_tests/frameworks/req_target_1/req_js_1.js +0 -0
  548. data/spec/fixtures/builder_tests/frameworks/req_target_2/english.lproj/req_style_2.css +0 -0
  549. data/spec/fixtures/builder_tests/frameworks/req_target_2/english.lproj/test.rhtml +0 -1
  550. data/spec/fixtures/builder_tests/frameworks/req_target_2/javascript.js +0 -1
  551. data/spec/fixtures/builder_tests/frameworks/req_target_2/lib/alt_layout.rhtml +0 -0
  552. data/spec/fixtures/builder_tests/frameworks/req_target_2/req_js_2.js +0 -0
  553. data/spec/fixtures/builder_tests/themes/sample_theme/Buildfile +0 -1
  554. data/spec/fixtures/buildfiles/basic/Buildfile +0 -16
  555. data/spec/fixtures/buildfiles/basic/task_module.rake +0 -6
  556. data/spec/fixtures/buildfiles/installed/Buildfile +0 -7
  557. data/spec/fixtures/buildfiles/installed/Buildfile2 +0 -5
  558. data/spec/fixtures/buildfiles/project_test/Buildfile +0 -4
  559. data/spec/fixtures/buildfiles/project_test/not_project/Buildfile +0 -2
  560. data/spec/fixtures/buildfiles/project_test/not_project/child/PLACEHOLDER +0 -0
  561. data/spec/fixtures/entry_for_project/Buildfile +0 -1
  562. data/spec/fixtures/entry_for_project/apps/test_app/frameworks/nested/PLACEHOLDER +0 -0
  563. data/spec/fixtures/entry_for_project/frameworks/shared/PLACEHOLDER +0 -0
  564. data/spec/fixtures/find_targets/custom/Buildfile +0 -8
  565. data/spec/fixtures/find_targets/custom/bars/bar1/bars/bar1/PLACEHOLDER +0 -0
  566. data/spec/fixtures/find_targets/custom/bars/bar1/bars/bar2/PLACEHOLDER +0 -0
  567. data/spec/fixtures/find_targets/custom/bars/bar1/foos/foo1/PLACEHOLDER +0 -0
  568. data/spec/fixtures/find_targets/custom/bars/bar1/foos/foo2/PLACEHOLDER +0 -0
  569. data/spec/fixtures/find_targets/custom/foos/custom_foos/Buildfile +0 -5
  570. data/spec/fixtures/find_targets/custom/foos/custom_foos/custom_foodir/foo1/PLACEHOLDER +0 -0
  571. data/spec/fixtures/find_targets/custom/foos/custom_foos/custom_foodir/foo2/PLACEHOLDER +0 -0
  572. data/spec/fixtures/find_targets/custom/foos/custom_foos/foos/not_foo1/PLACEHOLDER +0 -0
  573. data/spec/fixtures/find_targets/custom/foos/foo1/bars/bar1/PLACEHOLDER +0 -0
  574. data/spec/fixtures/find_targets/custom/foos/foo1/bars/bar2/PLACEHOLDER +0 -0
  575. data/spec/fixtures/find_targets/nested/Buildfile +0 -8
  576. data/spec/fixtures/find_targets/nested/apps/app1/Buildfile +0 -1
  577. data/spec/fixtures/find_targets/nested/apps/app1/apps/nested_app/PLACEHOLDER +0 -0
  578. data/spec/fixtures/find_targets/standard/Apps/app1/frameworks/framework1/PLACEHOLDER +0 -0
  579. data/spec/fixtures/find_targets/standard/Apps/app1/frameworks/framework2/PLACEHOLDER +0 -0
  580. data/spec/fixtures/find_targets/standard/clients/client1/PLACEHOLDER +0 -0
  581. data/spec/fixtures/find_targets/standard/frameworks/framework1/frameworks/framework1/PLACEHOLDER +0 -0
  582. data/spec/fixtures/find_targets/standard/frameworks/framework2/PLACEHOLDER +0 -0
  583. data/spec/fixtures/find_targets/standard/themes/theme1/PLACEHOLDER +0 -0
  584. data/spec/fixtures/find_targets/standard/themes/theme2/PLACEHOLDER +0 -0
  585. data/spec/fixtures/languages/apps/caps_long_names/English.lproj/PLACEHOLDER +0 -0
  586. data/spec/fixtures/languages/apps/caps_long_names/FreNCH.lproj/PLACEHOLDER +0 -0
  587. data/spec/fixtures/languages/apps/caps_long_names/UnknOWN.lproj/PLACEHOLDER +0 -0
  588. data/spec/fixtures/languages/apps/long_names/english.lproj/PLACEHOLDER +0 -0
  589. data/spec/fixtures/languages/apps/long_names/french.lproj/PLACEHOLDER +0 -0
  590. data/spec/fixtures/languages/apps/long_names/german.lproj/PLACEHOLDER +0 -0
  591. data/spec/fixtures/languages/apps/long_names/italian.lproj/PLACEHOLDER +0 -0
  592. data/spec/fixtures/languages/apps/long_names/japanese.lproj/PLACEHOLDER +0 -0
  593. data/spec/fixtures/languages/apps/long_names/spanish.lproj/PLACEHOLDER +0 -0
  594. data/spec/fixtures/languages/apps/long_names/unknown.lproj/PLACEHOLDER +0 -0
  595. data/spec/fixtures/languages/apps/no_names/PLACEHOLDER +0 -0
  596. data/spec/fixtures/languages/apps/short_names/de.lproj/PLACEHOLDER +0 -0
  597. data/spec/fixtures/languages/apps/short_names/en-CA.lproj/PLACEHOLDER +0 -0
  598. data/spec/fixtures/languages/apps/short_names/en-GB.lproj/PLACEHOLDER +0 -0
  599. data/spec/fixtures/languages/apps/short_names/en-US.lproj/PLACEHOLDER +0 -0
  600. data/spec/fixtures/languages/apps/short_names/en.lproj/PLACEHOLDER +0 -0
  601. data/spec/fixtures/languages/apps/short_names/es.lproj/PLACEHOLDER +0 -0
  602. data/spec/fixtures/languages/apps/short_names/foo.lproj/PLACEHOLDER +0 -0
  603. data/spec/fixtures/languages/apps/short_names/fr.lproj/PLACEHOLDER +0 -0
  604. data/spec/fixtures/languages/apps/short_names/it.lproj/PLACEHOLDER +0 -0
  605. data/spec/fixtures/languages/apps/short_names/ja.lproj/PLACEHOLDER +0 -0
  606. data/spec/fixtures/ordered_entries/apps/no_requires/1.js +0 -1
  607. data/spec/fixtures/ordered_entries/apps/no_requires/B.js +0 -1
  608. data/spec/fixtures/ordered_entries/apps/no_requires/a.js +0 -1
  609. data/spec/fixtures/ordered_entries/apps/no_requires/a/a.js +0 -1
  610. data/spec/fixtures/ordered_entries/apps/no_requires/a/b.js +0 -1
  611. data/spec/fixtures/ordered_entries/apps/no_requires/b/a.js +0 -1
  612. data/spec/fixtures/ordered_entries/apps/no_requires/c.js +0 -1
  613. data/spec/fixtures/ordered_entries/apps/no_requires/core.js +0 -1
  614. data/spec/fixtures/ordered_entries/apps/no_requires/english.lproj/B.css +0 -0
  615. data/spec/fixtures/ordered_entries/apps/no_requires/english.lproj/a.css +0 -0
  616. data/spec/fixtures/ordered_entries/apps/no_requires/english.lproj/a/a.css +0 -0
  617. data/spec/fixtures/ordered_entries/apps/no_requires/english.lproj/a/b.css +0 -0
  618. data/spec/fixtures/ordered_entries/apps/no_requires/english.lproj/b/a.css +0 -0
  619. data/spec/fixtures/ordered_entries/apps/no_requires/english.lproj/c.css +0 -0
  620. data/spec/fixtures/ordered_entries/apps/no_requires/lproj/strings.js +0 -1
  621. data/spec/fixtures/ordered_entries/apps/no_requires/utils.js +0 -1
  622. data/spec/fixtures/ordered_entries/apps/with_requires/a.js +0 -2
  623. data/spec/fixtures/ordered_entries/apps/with_requires/b.js +0 -3
  624. data/spec/fixtures/ordered_entries/apps/with_requires/c.js +0 -2
  625. data/spec/fixtures/ordered_entries/apps/with_requires/english.lproj/a.css +0 -2
  626. data/spec/fixtures/ordered_entries/apps/with_requires/english.lproj/b.css +0 -2
  627. data/spec/fixtures/ordered_entries/apps/with_requires/english.lproj/c.css +0 -2
  628. data/spec/fixtures/ordered_entries/apps/with_requires/english.lproj/d.js +0 -1
  629. data/spec/fixtures/real_world/Buildfile +0 -12
  630. data/spec/fixtures/real_world/apps/account/README +0 -1
  631. data/spec/fixtures/real_world/apps/calendar/README +0 -1
  632. data/spec/fixtures/real_world/apps/contacts/README_BEFORE_EDITING +0 -1
  633. data/spec/fixtures/real_world/apps/files/README +0 -1
  634. data/spec/fixtures/real_world/apps/mail/README +0 -1
  635. data/spec/fixtures/real_world/apps/mobile_photos/README +0 -1
  636. data/spec/fixtures/real_world/apps/photos/README +0 -1
  637. data/spec/fixtures/real_world/apps/uploader/README +0 -1
  638. data/spec/fixtures/real_world/frameworks/core_files/PLACEHOLDER +0 -0
  639. data/spec/fixtures/real_world/frameworks/core_photos/PLACEHOLDER +0 -0
  640. data/spec/fixtures/real_world/frameworks/shared/PLACEHOLDER +0 -0
  641. data/spec/fixtures/real_world/frameworks/sproutcore/Buildfile +0 -26
  642. data/spec/fixtures/real_world/frameworks/sproutcore/README +0 -1
  643. data/spec/fixtures/real_world/frameworks/sproutcore/apps/docs/PLACEHOLDER +0 -0
  644. data/spec/fixtures/real_world/frameworks/sproutcore/apps/test_runner/PLACEHOLDER +0 -0
  645. data/spec/fixtures/real_world/frameworks/sproutcore/core.js +0 -0
  646. data/spec/fixtures/real_world/frameworks/sproutcore/debug/debug-resource.html +0 -0
  647. data/spec/fixtures/real_world/frameworks/sproutcore/debug/sample_debug.js +0 -0
  648. data/spec/fixtures/real_world/frameworks/sproutcore/demo2.js +0 -0
  649. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/debug/sample_debug-loc.js +0 -0
  650. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/demo.css +0 -4
  651. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/demo.html +0 -1
  652. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/demo2.sass +0 -0
  653. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/file_extension_test.haml +0 -0
  654. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/file_extension_test.html.erb +0 -1
  655. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/file_extension_test.rhtml +0 -0
  656. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/fixtures/sample_fixtures-loc.js +0 -0
  657. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/has_require.css +0 -4
  658. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/no_require.css +0 -1
  659. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/no_sc_resource.rhtml +0 -1
  660. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/protocols/sample-loc.js +0 -0
  661. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/sc_resource.css +0 -6
  662. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/sc_resource.rhtml +0 -3
  663. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/strings.js +0 -1
  664. data/spec/fixtures/real_world/frameworks/sproutcore/english.lproj/tests/sample-loc.js +0 -0
  665. data/spec/fixtures/real_world/frameworks/sproutcore/fixtures/sample-json-fixture.json +0 -1
  666. data/spec/fixtures/real_world/frameworks/sproutcore/fixtures/sample_fixtures.js +0 -0
  667. data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/application/PLACEHOLDER +0 -0
  668. data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/costello/core.js +0 -0
  669. data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/data_store/PLACEHOLDER +0 -0
  670. data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/debug/PLACEHOLDER +0 -0
  671. data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/desktop/PLACEHOLDER +0 -0
  672. data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/empty_theme/PLACEHOLDER +0 -0
  673. data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/foundation/PLACEHOLDER +0 -0
  674. data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/mobile/PLACEHOLDER +0 -0
  675. data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/qunit/PLACEHOLDER +0 -0
  676. data/spec/fixtures/real_world/frameworks/sproutcore/frameworks/uploader/PLACEHOLDER +0 -0
  677. data/spec/fixtures/real_world/frameworks/sproutcore/french.lproj/french-resource.js +0 -0
  678. data/spec/fixtures/real_world/frameworks/sproutcore/french.lproj/strings.js +0 -1
  679. data/spec/fixtures/real_world/frameworks/sproutcore/german.lproj/german-resource.js +0 -0
  680. data/spec/fixtures/real_world/frameworks/sproutcore/german.lproj/strings.js +0 -0
  681. data/spec/fixtures/real_world/frameworks/sproutcore/has_require.js +0 -4
  682. data/spec/fixtures/real_world/frameworks/sproutcore/lib/index.html +0 -1
  683. data/spec/fixtures/real_world/frameworks/sproutcore/no_require.js +0 -1
  684. data/spec/fixtures/real_world/frameworks/sproutcore/protocols/sample.js +0 -0
  685. data/spec/fixtures/real_world/frameworks/sproutcore/sc_resource.js +0 -6
  686. data/spec/fixtures/real_world/frameworks/sproutcore/tests/nested/sample1.js +0 -0
  687. data/spec/fixtures/real_world/frameworks/sproutcore/tests/nested/sample2.js +0 -0
  688. data/spec/fixtures/real_world/frameworks/sproutcore/tests/sample.js +0 -0
  689. data/spec/fixtures/real_world/frameworks/sproutcore/tests/sample.rhtml +0 -1
  690. data/spec/fixtures/real_world/frameworks/sproutcore/themes/standard_theme/README +0 -0
  691. data/spec/fixtures/real_world/frameworks/sproutcore/views/view.js +0 -1
  692. data/spec/fixtures/real_world/generators/sample_custom/Buildfile +0 -0
  693. data/spec/fixtures/real_world/generators/sample_custom/templates/{filename}.js +0 -1
  694. data/spec/fixtures/recursive_project/Buildfile +0 -8
  695. data/spec/fixtures/recursive_project/frameworks/sproutcore/frameworks/costello/PLACEHOLDER +0 -0
  696. data/spec/lib/builders/combine_spec.rb +0 -67
  697. data/spec/lib/builders/html_spec.rb +0 -577
  698. data/spec/lib/builders/javascript_spec.rb +0 -81
  699. data/spec/lib/builders/sass_spec.rb +0 -43
  700. data/spec/lib/builders/spec_helper.rb +0 -30
  701. data/spec/lib/builders/strings_spec.rb +0 -52
  702. data/spec/lib/builders/stylesheet_spec.rb +0 -63
  703. data/spec/lib/builders/test_index_spec.rb +0 -44
  704. data/spec/lib/builders/test_spec.rb +0 -135
  705. data/spec/lib/buildfile/config_for_spec.rb +0 -81
  706. data/spec/lib/buildfile/define_spec.rb +0 -59
  707. data/spec/lib/buildfile/dup_spec.rb +0 -65
  708. data/spec/lib/buildfile/invoke_spec.rb +0 -130
  709. data/spec/lib/buildfile/load_spec.rb +0 -49
  710. data/spec/lib/buildfile/task/dup_spec.rb +0 -55
  711. data/spec/lib/buildfile/task_defined_spec.rb +0 -17
  712. data/spec/lib/buildfile_commands/build_task_spec.rb +0 -19
  713. data/spec/lib/buildfile_commands/config_spec.rb +0 -97
  714. data/spec/lib/buildfile_commands/import_spec.rb +0 -17
  715. data/spec/lib/buildfile_commands/namespace_spec.rb +0 -18
  716. data/spec/lib/buildfile_commands/proxies_spec.rb +0 -38
  717. data/spec/lib/buildfile_commands/replace_task_spec.rb +0 -29
  718. data/spec/lib/buildfile_commands/task_spec.rb +0 -36
  719. data/spec/lib/helpers/packing_optimizer/optimize_spec.rb +0 -26
  720. data/spec/lib/models/hash_struct/deep_clone_spec.rb +0 -27
  721. data/spec/lib/models/hash_struct/has_options_spec.rb +0 -32
  722. data/spec/lib/models/hash_struct/hash_spec.rb +0 -64
  723. data/spec/lib/models/hash_struct/merge_spec.rb +0 -26
  724. data/spec/lib/models/hash_struct/method_missing.rb +0 -41
  725. data/spec/lib/models/manifest/add_entry_spec.rb +0 -36
  726. data/spec/lib/models/manifest/add_transform_spec.rb +0 -90
  727. data/spec/lib/models/manifest/build_spec.rb +0 -78
  728. data/spec/lib/models/manifest/entry_for_spec.rb +0 -94
  729. data/spec/lib/models/manifest/find_entry.rb +0 -84
  730. data/spec/lib/models/manifest/prepare_spec.rb +0 -62
  731. data/spec/lib/models/manifest_entry/cacheable_url_spec.rb +0 -31
  732. data/spec/lib/models/manifest_entry/prepare_spec.rb +0 -54
  733. data/spec/lib/models/project/add_target_spec.rb +0 -44
  734. data/spec/lib/models/project/buildfile_spec.rb +0 -35
  735. data/spec/lib/models/project/find_targets_for_spec.rb +0 -77
  736. data/spec/lib/models/project/load_nearest_project_spec.rb +0 -23
  737. data/spec/lib/models/project/target_for_spec.rb +0 -33
  738. data/spec/lib/models/project/targets_spec.rb +0 -62
  739. data/spec/lib/models/target/compute_build_number_spec.rb +0 -125
  740. data/spec/lib/models/target/config_spec.rb +0 -30
  741. data/spec/lib/models/target/expand_required_targets_spec.rb +0 -48
  742. data/spec/lib/models/target/installed_languages_spec.rb +0 -47
  743. data/spec/lib/models/target/lproj_for_spec.rb +0 -38
  744. data/spec/lib/models/target/manifest_for_spec.rb +0 -42
  745. data/spec/lib/models/target/parent_target_spec.rb +0 -21
  746. data/spec/lib/models/target/prepare_spec.rb +0 -53
  747. data/spec/lib/models/target/required_targets_spec.rb +0 -119
  748. data/spec/lib/models/target/target_for_spec.rb +0 -56
  749. data/spec/lib/tools/build_number_spec.rb +0 -28
  750. data/spec/lib/tools/gen_spec.rb +0 -207
  751. data/spec/lib/tools/tools_spec.rb +0 -78
  752. data/spec/spec_helper.rb +0 -138
  753. data/vendor/github_gem_lint.rb +0 -22
  754. data/vendor/yui-compressor/yuicompressor-2.4.2.jar +0 -0
@@ -0,0 +1,231 @@
1
+ // ==========================================================================
2
+ // Project: SproutCore - JavaScript Application Framework
3
+ // Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
4
+ // portions copyright @2009 Apple, Inc.
5
+ // License: Licened under MIT license (see license.js)
6
+ // ==========================================================================
7
+
8
+ /* Test SC.StackedView with a Comments example. */
9
+
10
+ htmlbody(["<style>",
11
+ '.sc-stacked-view { border-bottom: 1px red solid; }',
12
+ '.comment-view.sel { background-color: #ccc; }',
13
+ '.comment-view { margin: 0 10px; padding: 10px 0; border-bottom: 1px #ccc solid; }',
14
+ '.comment-view p { padding-top: 28px; padding-bottom: 32px; }',
15
+ '</style>'].join("\n"));
16
+
17
+ var CommentView = SC.View.extend(SC.StaticLayout, SC.Control, {
18
+
19
+ useStaticLayout: YES,
20
+
21
+ content: null,
22
+
23
+ classNames: 'comment-view',
24
+
25
+ contentPropertyDidChange: function(target, key) {
26
+
27
+ // update everything!
28
+ if (key === '*') {
29
+ this.updateFromLabel().updateCommentLabel();
30
+ } else if (key === 'from' || key === 'date') {
31
+ this.updateFromLabel();
32
+ } else if (key === 'comment') {
33
+ this.updateCommentLabel();
34
+ }
35
+
36
+ if (this.owner && this.owner.updateHeight) this.owner.updateHeight();
37
+ },
38
+
39
+ updateFromLabel: function() {
40
+ var content = this.get('content'),
41
+ from = content ? content.get('from') : 'Anonymous',
42
+ date = content ? content.get('date') : 'some date',
43
+ str = "%@ wrote %@: ".fmt(from, date);
44
+ this.fromLabel.set('value', str);
45
+ return this ;
46
+ },
47
+
48
+ updateCommentLabel: function() {
49
+ var content = this.get('content'),
50
+ comment = content ? content.get('comment') : '(No Comment)';
51
+ this.commentLabel.set('value', comment);
52
+ },
53
+
54
+ // ..........................................................
55
+ // BASE VIEWS
56
+ //
57
+
58
+ childViews: 'fromLabel commentLabel editButton replyButton'.w(),
59
+
60
+ fromLabel: SC.LabelView.extend({
61
+ tagName: "label",
62
+ layout: { left: 0, top: 0, right: 0, height: 18 }
63
+ }),
64
+
65
+ commentLabel: SC.LabelView.extend(SC.StaticLayout, {
66
+ useStaticLayout: YES,
67
+ tagName: "p"
68
+ }),
69
+
70
+ editButton: SC.ButtonView.extend({
71
+ title: "Edit",
72
+ titleMinWidth: 0,
73
+ layout: { bottom: 4, right: 58, height: 21, width: 50 }
74
+ }),
75
+
76
+ replyButton: SC.ButtonView.extend({
77
+ title: "Reply",
78
+ titleMinWidth: 0,
79
+ layout: { bottom: 4, right: 4, height: 21, width: 50 }
80
+ })
81
+
82
+ });
83
+
84
+ var content = [
85
+ SC.Object.create({
86
+ from: "Joe",
87
+ date: "Today",
88
+ comment: "I think this new class is a great idea! I've always wanted less control but more flexibility."
89
+ }),
90
+ SC.Object.create({
91
+ from: "Joe",
92
+ date: "Today",
93
+ comment: "I think this new class is a great idea! I've always wanted less control but more flexibility."
94
+ }),
95
+ SC.Object.create({
96
+ from: "Joe",
97
+ date: "Today",
98
+ comment: "I think this new class is a great idea! I've always wanted less control but more flexibility."
99
+ }),
100
+ SC.Object.create({
101
+ from: "Joe",
102
+ date: "Today",
103
+ comment: "I think this new class is a great idea! I've always wanted less control but more flexibility."
104
+ }),
105
+ SC.Object.create({
106
+ from: "Jane",
107
+ date: "Yesterday",
108
+ comment: "If only you would rewrite this in Flash. Then I could get really excited about it. I like it when Adobe controls my destiny."
109
+ })
110
+ ];
111
+
112
+ var extra = SC.Object.create({
113
+ from: "Charles",
114
+ date: "Tomorrow",
115
+ comment: "Hello from the world of tomorrow!"
116
+ });
117
+
118
+ var pane = SC.ControlTestPane.design()
119
+ .add("basic", SC.StackedView, {
120
+ layout: { top: 0, left: 0, right: 0, height: 600 },
121
+ content: content,
122
+ exampleView: CommentView
123
+ });
124
+
125
+ pane.show(); // add a test to show the test pane
126
+ window.pane = pane ;
127
+
128
+ // ..........................................................
129
+ // BASIC TESTS
130
+ //
131
+ module("Basic Tests", pane.standardSetup());
132
+
133
+ test("removing an item should delete childView and adjust height", function() {
134
+ var view = pane.view('basic'),
135
+ item = content[0];
136
+
137
+ equals(view.getPath('childViews.length'), content.length, 'precond - should have child views equal to current content');
138
+ var oldHeight = view.get('frame').height; // save height.
139
+
140
+ SC.run(function() { content.removeAt(0); }); // remove first item
141
+
142
+
143
+ equals(view.getPath('childViews.length'), content.length, 'view should remove childView for removed content items');
144
+ var newHeight = view.get('frame').height;
145
+ ok(newHeight < oldHeight, 'view height should adjust to reflect new content. (old height: %@ current height: %@)'.fmt(oldHeight, newHeight));
146
+
147
+ // restore old content
148
+ SC.run(function() { content.insertAt(0, item); });
149
+
150
+ });
151
+
152
+ window.content = content ;
153
+
154
+ test("inserting an item should add childView and adjust height", function() {
155
+ var view = pane.view('basic'),
156
+ item = extra; // we will insert another one
157
+
158
+ equals(view.getPath('childViews.length'), content.length, 'precond - should have child views equal to current content');
159
+ var oldHeight = view.get('frame').height; // save height.
160
+
161
+ SC.run(function() { content.pushObject(item); }); // add another item
162
+
163
+
164
+ equals(view.getPath('childViews.length'), content.length, 'view should add childView for added content item');
165
+ var newHeight = view.get('frame').height;
166
+ ok(newHeight > oldHeight, 'view height should adjust to reflect new content. (old height: %@ current height: %@)'.fmt(oldHeight, newHeight));
167
+
168
+ // restore
169
+ SC.run(function() { content.popObject(); });
170
+ });
171
+
172
+
173
+ test("editing an item should automatically adjust the height", function() {
174
+ var view = pane.view('basic'),
175
+ item = content[0],
176
+ childView = view.childViews[0],
177
+ old = item.get('comment');
178
+
179
+ ok(childView, 'stacked view should have child view');
180
+ equals(childView.get('content'), item, 'first childView should own first content item');
181
+
182
+ var height = view.get('frame').height; // save old height
183
+
184
+ // change comment
185
+ SC.run(function() { item.set('comment', 'This is a new comment'); });
186
+
187
+ // should have updated UI and adjusted height of collection
188
+ equals(childView.$().find('p').text(), 'This is a new comment', 'Item view should now contain comment');
189
+
190
+ var newHeight = view.get('frame').height;
191
+ ok(newHeight < height, 'view height should adjust to reflect new content. (old height: %@ current height: %@)'.fmt(height, newHeight));
192
+
193
+ // restore
194
+ SC.run(function() { item.set('comment', old); });
195
+
196
+ newHeight = view.get('frame').height;
197
+ equals(newHeight, height, 'view height should restore to old height when content is edited again. (old height: %@ current height: %@)'.fmt(height, newHeight));
198
+
199
+ });
200
+
201
+ // ..........................................................
202
+ // SPECIAL CASES
203
+ //
204
+
205
+ // tests specific bug where a series of many edits strung together would
206
+ // cause the height to get out of sync.
207
+ test("adding, removing then editing items should still keep height the same", function() {
208
+
209
+ var view = pane.view('basic'),
210
+ item = content[0],
211
+ old = item.get('comment'),
212
+ height = view.get('frame').height; // save old height
213
+
214
+ SC.run(function() { content.removeAt(0); });
215
+ SC.run(function() { content.insertAt(0, item); });
216
+ SC.run(function() { content.pushObject(extra); });
217
+ SC.run(function() { content.popObject(); });
218
+
219
+ SC.run(function() { item.set('comment', 'Short comment'); });
220
+ SC.run(function() { item.set('comment', old); });
221
+
222
+ var newHeight = view.get('frame').height;
223
+ equals(newHeight, height, 'view height should restore to old height when content is edited again. (old height: %@ current height: %@)'.fmt(height, newHeight));
224
+
225
+ });
226
+
227
+
228
+
229
+
230
+
231
+
@@ -83,7 +83,7 @@
83
83
  SC.Event.add(view.$('iframe')[0], 'load', this,
84
84
  function() {
85
85
  ok(view.$().height() > 150, "height of view should change based on content");
86
- ok(view.$().width() > 250, "width of view should change based on content");
86
+ ok(view.$().width() > 200, "width of view should change based on content");
87
87
  start();
88
88
  });
89
89
  });
@@ -160,7 +160,7 @@ SC.ButtonView = SC.View.extend(SC.Control, SC.Button, SC.StaticLayout,
160
160
  // display properties that should automatically cause a refresh.
161
161
  // isCancel and isDefault also cause a refresh but this is implemented as
162
162
  // a separate observer (see below)
163
- displayProperties: ['href', 'icon', 'title', 'value'],
163
+ displayProperties: ['href', 'icon', 'title', 'value', 'toolTip'],
164
164
 
165
165
  render: function(context, firstTime) {
166
166
  // add href attr if tagName is anchor...
@@ -169,6 +169,13 @@ SC.ButtonView = SC.View.extend(SC.Control, SC.Button, SC.StaticLayout,
169
169
  if (!href || (href.length === 0)) href = "javascript"+":;";
170
170
  context.attr('href', href);
171
171
  }
172
+
173
+ // If there is a toolTip set, grab it and localize if necessary.
174
+ var toolTip = this.get('toolTip') ;
175
+ if(SC.typeOf(toolTip) === SC.T_STRING) {
176
+ context.attr('title', this.get('localize') ? toolTip.loc() : toolTip) ;
177
+ context.attr('alt', this.get('localize') ? toolTip.loc() : toolTip) ;
178
+ }
172
179
 
173
180
  // add some standard attributes & classes.
174
181
  var classes = this._TEMPORARY_CLASS_HASH;
@@ -178,10 +185,14 @@ SC.ButtonView = SC.View.extend(SC.Control, SC.Button, SC.StaticLayout,
178
185
  context.attr('role', 'button')
179
186
  .setClass(classes).addClass(this.get('theme'));
180
187
  // render inner html
181
- context = context.begin('span')
182
- .addClass('sc-button-inner').addStyle('minWidth', this.get('titleMinWidth'));
188
+ if(firstTime){
189
+ context = context.push("<span class='sc-button-inner' style = 'min-width:%@px'>"
190
+ .fmt(this.get('titleMinWidth')));
183
191
  this.renderTitle(context, firstTime) ; // from button mixin
184
- context = context.end();
192
+ context.push("</span>") ;
193
+ }else{
194
+ this.renderTitle(context, firstTime) ;
195
+ }
185
196
  },
186
197
 
187
198
  /** @private {String} used to store a previously defined key equiv */
@@ -26,7 +26,7 @@ SC.CheckboxView = SC.FieldView.extend(SC.StaticLayout, SC.Button,
26
26
  tagName: 'label',
27
27
 
28
28
  render: function(context, firstTime) {
29
- var dt ;
29
+ var dt, elem ;
30
30
 
31
31
  // add checkbox -- set name to view guid to separate it from others
32
32
  if (firstTime) {
@@ -42,6 +42,13 @@ SC.CheckboxView = SC.FieldView.extend(SC.StaticLayout, SC.Button,
42
42
  // since we don't want to regenerate the contents each time
43
43
  // actually search for and update the displayTitle.
44
44
  } else {
45
+
46
+ if (elem = this.$input()[0]) {
47
+ if (this.get('isEnabled')) elem.disabled=NO;
48
+ else elem.disabled=YES;
49
+ elem = null ; // avoid memory leaks
50
+ }
51
+
45
52
  dt = this.get('displayTitle');
46
53
  if (dt !== this._field_currentDisplayTitle) {
47
54
  this._field_currentDisplayTitle = dt;
@@ -8,13 +8,6 @@
8
8
  sc_require('mixins/collection_view_delegate') ;
9
9
  sc_require('views/list_item');
10
10
 
11
- SC.BENCHMARK_UPDATE_CHILDREN = YES ;
12
- SC.BENCHMARK_RENDER = YES ;
13
- SC.ENABLE_COLLECTION_PARTIAL_RENDER = YES ;
14
- SC.DEBUG_PARTIAL_RENDER = NO ;
15
- SC.SANITY_CHECK_PARTIAL_RENDER = YES ;
16
- SC.VALIDATE_COLLECTION_CONSISTANCY = NO ;
17
-
18
11
  /**
19
12
  Special drag operation passed to delegate if the collection view proposes
20
13
  to perform a reorder event.
@@ -29,16 +22,12 @@ SC.HORIZONTAL_ORIENTATION = 'horizontal';
29
22
  /** Selection points should be selected using vertical orientation. */
30
23
  SC.VERTICAL_ORIENTATION = 'vertical' ;
31
24
 
32
- /** Enables an optimization using zombie group views. This option is configurable for perf testing purposes. You should not change it. */
33
- SC.ZOMBIE_GROUPS_ENABLED = YES ;
34
-
35
- /** Enables an optimization that removes the root element from the DOM during
36
- a render and then readds it when complete. This option is configurable for
37
- perf testing purposes. You should not change it. */
38
- SC.REMOVE_COLLECTION_ROOT_ELEMENT_DURING_RENDER = NO ;
25
+ SC.BENCHMARK_RELOAD = NO ;
39
26
 
40
27
  /**
41
28
  @class
29
+
30
+ TODO: Document SC.CollectionView
42
31
 
43
32
  Renders a collection of views from a source array of model objects.
44
33
 
@@ -54,16 +43,20 @@ SC.REMOVE_COLLECTION_ROOT_ELEMENT_DURING_RENDER = NO ;
54
43
  property if you want to monitor selection. (be sure to set the isEnabled
55
44
  property to allow selection.)
56
45
 
57
- @extends SC.ClassicView
46
+ @extends SC.View
58
47
  @extends SC.CollectionViewDelegate
59
-
48
+ @extends SC.CollectionContent
49
+ @since SproutCore 0.9
60
50
  */
61
- SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
62
- /** @scope SC.CollectionView.prototype */
63
- {
51
+ SC.CollectionView = SC.View.extend(
52
+ SC.CollectionViewDelegate,
53
+ SC.CollectionContent,
54
+ /** @scope SC.CollectionView.prototype */ {
64
55
 
65
56
  classNames: ['sc-collection-view'],
66
57
 
58
+ ACTION_DELAY: 200,
59
+
67
60
  // ......................................
68
61
  // PROPERTIES
69
62
  //
@@ -82,57 +75,62 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
82
75
  Usually you will want to bind this property to a controller property
83
76
  that actually contains the array of objects you to display.
84
77
 
85
- @type SC.Array
78
+ @type {SC.Array}
86
79
  */
87
- content: [],
80
+ content: null,
88
81
 
89
82
  /** @private */
90
83
  contentBindingDefault: SC.Binding.multiple(),
91
84
 
92
85
  /**
93
- The array of currently selected objects.
94
-
95
- This array should contain the currently selected content objects. It is
96
- modified automatically by the collection view when the user changes the
97
- selection on the collection.
86
+ The current length of the content.
98
87
 
99
- Any item views representing content objects in this array will have their
100
- isSelected property set to YES automatically.
88
+ @property
89
+ @type {Numer}
90
+ */
91
+ length: 0,
92
+
93
+ /**
94
+ The set of indexes that are currently tracked by the collection view.
95
+ This property is used to determine the range of items the collection view
96
+ should monitor for changes.
101
97
 
102
- The CollectionView can deal with selection arrays that contain content
103
- objects that do not belong to the content array itself. Sometimes this
104
- will happen if you share the same selection across multiple collection
105
- views.
98
+ The default implementation of this property returns an index set covering
99
+ the entire range of the content. It changes automatically whenever the
100
+ length changes.
106
101
 
107
- Usually you will want to bind this property to a controller property that
108
- actually manages the selection for your display.
102
+ Note that the returned index set for this property will always be frozen.
103
+ To change the nowShowing index set, you must create a new index set and
104
+ apply it.
109
105
 
110
- @type Array
106
+ @property
107
+ @type {SC.IndexSet}
111
108
  */
112
- selection: [],
113
-
114
- /** @private */
115
- selectionBindingDefault: SC.Binding.multiple(),
109
+ nowShowing: function() {
110
+ var ret = this.computeNowShowing();
111
+ return ret ? ret.frozenCopy() : null;
112
+ }.property('length', 'clippingFrame').cacheable(),
116
113
 
117
114
  /**
118
- Delegate used to implement fine-grained control over collection view
119
- behaviors.
115
+ Indexes of selected content objects. This SC.SelectionSet is modified
116
+ automatically by the collection view when the user changes the selection
117
+ on the collection.
120
118
 
121
- You can assign a delegate object to this property that will be consulted
122
- for various decisions regarding drag and drop, selection behavior, and
123
- even rendering. The object you place here must implement some or all of
124
- the SC.CollectionViewDelegate mixin.
119
+ Any item views representing content objects in this set will have their
120
+ isSelected property set to YES automatically.
121
+
122
+ @type {SC.SelectionSet}
125
123
  */
126
- delegate: null,
124
+ selection: null,
127
125
 
128
126
  /**
129
127
  Allow user to select content using the mouse and keyboard.
130
128
 
131
129
  Set this property to NO to disallow the user from selecting items. If you
132
- have items in your selection property, they will still be reflected
130
+ have items in your selectedIndexes property, they will still be reflected
133
131
  visually.
134
132
 
135
- @type Boolean
133
+ @type {Boolean}
136
134
  */
137
135
  isSelectable: YES,
138
136
 
@@ -144,10 +142,10 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
144
142
 
145
143
  The collection view will set the isEnabled property of its item views to
146
144
  reflect the same view of this property. Whenever isEnabled is false,
147
- the collection view will also be not selectable or editable, regardless of the
148
- settings for isEditable & isSelectable.
145
+ the collection view will also be not selectable or editable, regardless of
146
+ the settings for isEditable & isSelectable.
149
147
 
150
- @type Boolean
148
+ @type {Boolean}
151
149
  */
152
150
  isEnabled: YES,
153
151
 
@@ -162,7 +160,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
162
160
  the user will not be able to reorder, add, or delete items regardless of
163
161
  the canReorderContent and canDeleteContent and isDropTarget properties.
164
162
 
165
- @type Boolean
163
+ @type {Boolean}
166
164
  */
167
165
  isEditable: YES,
168
166
 
@@ -176,7 +174,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
176
174
  If you also accept drops, this will allow the user to drop items into
177
175
  specific points in the list. Otherwise items will be added to the end.
178
176
 
179
- @type Boolean
177
+ @type {Boolean}
180
178
  */
181
179
  canReorderContent: NO,
182
180
 
@@ -189,13 +187,25 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
189
187
  If true the user will be allowed to delete selected items using the delete
190
188
  key. Otherwise deletes will not be permitted.
191
189
 
192
- @type Boolean
190
+ @type {Boolean}
193
191
  */
194
192
  canDeleteContent: NO,
195
193
 
196
194
  /** @private */
197
195
  canDeleteContentBindingDefault: SC.Binding.bool(),
198
196
 
197
+ /**
198
+ Allow user to edit the content by double clicking on it or hitting return.
199
+ This will only work if isEditable is YES and the item view implements
200
+ the beginEditing() method.
201
+
202
+ @type {Boolean}
203
+ */
204
+ canEditContent: NO,
205
+
206
+ /** @private */
207
+ canEditContentBindingDefault: SC.Binding.bool(),
208
+
199
209
  /**
200
210
  Accept drops for data other than reordering.
201
211
 
@@ -203,7 +213,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
203
213
  cause it to be registered as a drop target, activating the other drop
204
214
  machinery.
205
215
 
206
- @type Boolean
216
+ @type {Boolean}
207
217
  */
208
218
  isDropTarget: NO,
209
219
 
@@ -212,9 +222,9 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
212
222
 
213
223
  If set to true, then selection will use a toggle instead of the normal
214
224
  click behavior. Command modifiers will be ignored and instead clicking
215
- once will enable an item and clicking on it again will disable it.
225
+ once will select an item and clicking on it again will deselect it.
216
226
 
217
- @type Boolean
227
+ @type {Boolean}
218
228
  */
219
229
  useToggleSelection: NO,
220
230
 
@@ -225,12 +235,12 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
225
235
  object and double clicking will trigger the action method on the
226
236
  collection view.
227
237
 
228
- If you set this property to true, then clicking on a view will both select
238
+ If you set this property to YES, then clicking on a view will both select
229
239
  it (if isSelected is true) and trigger the action method.
230
240
 
231
241
  Use this if you are using the collection view as a menu of items.
232
242
 
233
- @type Boolean
243
+ @property {Boolean}
234
244
  */
235
245
  actOnSelect: NO,
236
246
 
@@ -244,7 +254,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
244
254
  the mouse is released, so you can perform, for instance, a drag operation
245
255
  without actually selecting the target item.
246
256
 
247
- @type Boolean
257
+ @property {Boolean}
248
258
  */
249
259
  selectOnMouseDown: YES,
250
260
 
@@ -277,9 +287,34 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
277
287
 
278
288
  /**
279
289
  If set, this key will be used to get the example view for a given
280
- content object.
290
+ content object. The exampleView property will be ignored.
291
+
292
+ @property {String}
281
293
  */
282
294
  contentExampleViewKey: null,
295
+
296
+ /**
297
+ The view class to use when creating new group item views.
298
+
299
+ The collection view will automatically create an instance of the view
300
+ class you set here for each item in its content array. You should provide
301
+ your own subclass for this property to display the type of content you
302
+ want.
303
+
304
+ If you leave this set to null then the regular example view will be used
305
+ with the isGroupView property set to YES on the item view.
306
+
307
+ @property {SC.View}
308
+ */
309
+ groupExampleView: null,
310
+
311
+ /**
312
+ If set, this key will be used to get the example view for a given
313
+ content object. The groupExampleView property will be ignored.
314
+
315
+ @property {String}
316
+ */
317
+ contentGroupExampleViewKey: null,
283
318
 
284
319
  /**
285
320
  Invoked when the user double clicks on an item (or single clicks of
@@ -317,19 +352,6 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
317
352
  */
318
353
  target: null,
319
354
 
320
- /**
321
- Set to YES whenever the content needs to update its children. If you
322
- set this property, it will cause the view to update its children at the
323
- end of the runloop or the next time it becomes visible.
324
-
325
- Generally you will not need to change this property. Instead you should
326
- call methods such as contentPropertyDidChange() or updateChildren()
327
- directly instead.
328
-
329
- @property {Boolean}
330
- */
331
- isDirty: NO,
332
-
333
355
  /**
334
356
  Property on content items to use for display.
335
357
 
@@ -355,20 +377,6 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
355
377
  */
356
378
  acceptsFirstResponder: NO,
357
379
 
358
- /**
359
- If your layout uses a grid or horizontal-based layout, then make sure this
360
- property is always up to date with the current number of items per row.
361
-
362
- The CollectionView will use this property to support keyboard navigation
363
- using the arrow keys.
364
-
365
- If your collection view is simply a vertical list of items then you do not
366
- need to change this property.
367
-
368
- @property {Number}
369
- */
370
- itemsPerRow: 1,
371
-
372
380
  // ..........................................................
373
381
  // SUBCLASS METHODS
374
382
  //
@@ -387,406 +395,1036 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
387
395
  computeLayout: function() { return null; },
388
396
 
389
397
  /**
390
- Override to return the range of items to render for a given frame.
391
-
392
- You can override this method to implement support for incremenetal
393
- rendering. The range you return here will be used to limit the number of
394
- actual item views that are created by the collection view.
395
-
396
- If you do not want to support incremental rendering, just return null.
397
-
398
- @param {Rect} frame The frame you should use to determine the range.
399
- @returns {Range} A hash that indicates the range of content objects to
400
- render. ({ start: X, length: Y })
401
- */
402
- contentRangeInFrame: function(frame) { return null; },
403
-
404
- /**
405
- Override to adjust the position of the itemView's layout property at the
406
- specified content index.
407
-
408
- Note: if useAdjust is NO, you should set the view's layout property in
409
- such a way that no change notifications are triggered. For example,
410
-
411
- {{{
412
- var newLayout = this.myComputeLayoutForIndex(contentIndex) ;
413
- if (useAdjust) itemView.adjust(newLayout) ;
414
- else itemView.layout = newLayout ; // DON'T TRIGGER OBSERVERS!!!
415
- }}}
398
+ Override to compute the layout of the itemView for the content at the
399
+ specified index. This layout will be applied to the view just before it
400
+ is rendered.
416
401
 
417
402
  @param {Number} contentIndex the index of content beind rendered by
418
403
  itemView
419
- @returns {Rect} a layout rectangle
404
+ @returns {Hash} a view layout
420
405
  */
421
- itemViewLayoutAtContentIndex: function(contentIndex) {},
422
-
423
- // ..........................................................
424
- // CONTENT CHANGES
425
- //
406
+ layoutForContentIndex: function(contentIndex) {
407
+ return null ;
408
+ },
426
409
 
427
- /** @private
428
- Whenever content array changes, start observing the [] property. Also
429
- set childrenNeedFullUpdate to YES, which will trigger an update.
430
- */
431
- _collection_contentDidChange: function() {
432
- var content = this.get('content') ;
433
- if (content === this._content) return this; // nothing to do
434
-
435
- var func = this._collection_contentPropertyDidChange ;
410
+ /**
411
+ Override to return an IndexSet with the indexes that are at least
412
+ partially visible in the passed rectangle. This method is used by the
413
+ default implementation of computeNowShowing() to determine the new
414
+ nowShowing range after a scroll.
436
415
 
437
- // remove old observer, add new observer
438
- if (this._content) this._content.removeObserver('[]', this, func) ;
439
- if (content) content.addObserver('[]', this, func) ;
416
+ Override this method to implement incremental rendering.
440
417
 
441
- // cache
442
- this._content = content;
443
- this._contentPropertyRevision = null ;
418
+ The default simply returns the current content length.
444
419
 
445
- // trigger property change handler...
446
- var rev = (content) ? content.propertyRevision : -1 ;
447
- this._collection_contentPropertyDidChange(this, '[]', content, rev) ;
448
- }.observes('content'),
449
-
450
- /** @private
451
- Called whenever the content array or any items in the content array
452
- changes. update children if this is a new property revision.
453
- */
454
- _collection_contentPropertyDidChange: function(target, key, value, rev) {
455
- if (!this._updatingContent && (!rev || (rev != this._contentPropertyRevision))) {
456
- this._contentPropertyRevision = rev ;
457
- this._updatingContent = true ;
458
- this.contentPropertyDidChange(target, key);
459
- this._updatingContent = false ;
460
- }
420
+ @param {Rect} rect the visible rect
421
+ @returns {SC.IndexSet} now showing indexes
422
+ */
423
+ contentIndexesInRect: function(rect) {
424
+ return SC.IndexSet.create(0, this.get('length'));
461
425
  },
462
426
 
463
427
  /**
464
- Invoked whenever a the content array changes. The default implementation
465
- will possibly recompute the view's layout size and the marks it as dirty
466
- so that it can update its children.
467
- */
468
- contentPropertyDidChange: function(target, key) {
469
- this.adjust(this.computeLayout()) ;
470
- this.set('isDirty', YES) ;
471
- this.invalidateNowShowingRange() ;
472
- return this ;
428
+ Compute the nowShowing index set. The default implementation simply
429
+ returns the full range. Override to implement incremental rendering.
430
+
431
+ You should not normally call this method yourself. Instead get the
432
+ nowShowing property.
433
+
434
+ @returns {SC.IndexSet} new now showing range
435
+ */
436
+ computeNowShowing: function() {
437
+ var r = this.contentIndexesInRect(this.get('clippingFrame')),
438
+ content = SC.makeArray(this.get('content')),
439
+ len = content.get('length');
440
+
441
+ // default show all.
442
+ if (!r) r = SC.IndexSet.create(0, len);
443
+
444
+ // make sure the index set doesn't contain any indexes greater than the
445
+ // actual content.
446
+ if (r.get('max') > len) r.remove(len, r.get('max')-len);
447
+
448
+ return r ;
473
449
  },
474
450
 
475
- /** @private
476
- Anytime isDirty changes to YES or our visibility in window changes,
477
- schedule a full update.
478
- */
479
- _collection_isDirtyDidChange: function() {
480
- // don't test isVisibleInWindow here for a 10% perf gain
481
- if (this.get('isDirty')) {
482
- // using invokeOnce here doubles rendering speed!
483
- this.invokeOnce(this.displayDidChange) ;
484
- }
485
- }.observes('isDirty', 'isVisibleInWindow'),
486
-
487
- // ..........................................................
488
- // SELECTION CHANGES
489
- //
490
-
491
- /** @private
492
- Whenever selection array changes, start observing the [] property. Also
493
- set childrenNeedFullUpdate to YES, which will trigger an update.
494
- */
495
- _collection_selectionDidChange: function() {
496
- var selection = this.get('selection') ;
497
- if (selection === this._selection) return this; // nothing to do
451
+ /**
452
+ Override to show the insertion point during a drag.
453
+
454
+ Called during a drag to show the insertion point. Passed value is the
455
+ item view that you should display the insertion point before. If the
456
+ passed value is null, then you should show the insertion point AFTER that
457
+ last item view returned by the itemViews property.
498
458
 
499
- var func = this._collection_selectionPropertyDidChange ;
459
+ Once this method is called, you are guaranteed to also recieve a call to
460
+ hideInsertionPoint() at some point in the future.
500
461
 
501
- // remove old observer, add new observer
502
- if (this._selection) this._selection.removeObserver('[]', this, func) ;
503
- if (selection) selection.addObserver('[]', this, func) ;
462
+ The default implementation of this method does nothing.
504
463
 
505
- // cache
506
- this._selection = selection;
507
- this._selectionPropertyRevision = null ;
464
+ @param itemView {SC.ClassicView} view the insertion point should appear directly before. If null, show insertion point at end.
465
+ @param dropOperation {Number} the drop operation. will be SC.DROP_BEFORE, SC.DROP_AFTER, or SC.DROP_ON
508
466
 
509
- // trigger property change handler...
510
- var rev = (selection) ? selection.propertyRevision : -1 ;
511
- this._collection_selectionPropertyDidChange(this, '[]', selection, rev) ;
512
- }.observes('selection'),
513
-
514
- /** @private
515
- Called whenever the content array or any items in the selection array
516
- changes. update children if this is a new property revision.
517
- */
518
- _collection_selectionPropertyDidChange: function(target, key, value, rev) {
519
- if (!this._updatingSelection && (!rev || (rev != this._selectionPropertyRevision))) {
520
- this._selectionPropertyRevision = rev ;
521
- this._updatingSelection = true ;
522
- this.selectionPropertyDidChange(target, key);
523
- this._updatingSelection = false ;
524
- }
467
+ @returns {void}
468
+ */
469
+ showInsertionPoint: function(itemView, dropOperation) {
525
470
  },
526
471
 
527
472
  /**
528
- Invoked whenever a the selection array changes. The default
529
- implementation will possibly recompute the view's layout size and the
530
- marks it as dirty so that it can update its children.
531
- */
532
- selectionPropertyDidChange: function(target, key) {
533
- this.adjust(this.computeLayout()) ;
534
- this.set('isDirty', YES) ;
535
- this.invalidateNowShowingRange() ;
536
- return this ;
473
+ Override to hide the insertion point when a drag ends.
474
+
475
+ Called during a drag to hide the insertion point. This will be called
476
+ when the user exits the view, cancels the drag or completes the drag. It
477
+ will not be called when the insertion point changes during a drag.
478
+
479
+ You should expect to receive one or more calls to
480
+ showInsertionPointBefore() during a drag followed by at least one call to
481
+ this method at the end. Your method should not raise an error if it is
482
+ called more than once.
483
+
484
+ @returns {void}
485
+ */
486
+ hideInsertionPoint: function() {
537
487
  },
538
488
 
539
489
  // ..........................................................
540
- // NOW SHOWING RANGE
490
+ // DELEGATE SUPPORT
541
491
  //
542
492
 
493
+
543
494
  /**
544
- The currently visible range. This is invalidated anytime the clipping
545
- frame changes or anytime the view is resized. This in turn may cause
546
- the collection view to do a 'fast' revalidation of its content.
495
+ Delegate used to implement fine-grained control over collection view
496
+ behaviors.
547
497
 
548
- @property
549
- @type Range
550
- */
551
- nowShowingRange: function() {
552
- // console.log(this.get('clippingFrame'));
553
- var r = this.contentRangeInFrame(this.get('clippingFrame')) ;
554
- if (!r) r = { start: 0, length: 0 } ; // default
555
-
556
- // make sure the range isn't greater than the content length
557
- var content = SC.makeArray(this.get('content'));
558
- r.length = Math.min(SC.maxRange(r), content.get('length')) - r.start ;
498
+ You can assign a delegate object to this property that will be consulted
499
+ for various decisions regarding drag and drop, selection behavior, and
500
+ even rendering. The object you place here must implement some or all of
501
+ the SC.CollectionViewDelegate mixin.
559
502
 
560
- return r ;
561
- }.property('content', 'clippingFrame').cacheable(),
503
+ If you do not supply a delegate but the content object you set implements
504
+ the SC.CollectionViewDelegate mixin, then the content will be
505
+ automatically set as the delegate. Usually you will work with a
506
+ CollectionView in this way rather than setting a delegate explicitly.
507
+
508
+ @type {SC.CollectionViewDelegate}
509
+ */
510
+ delegate: null,
562
511
 
563
512
  /**
564
- Call this method if the nowShowingRange should be recalculated for some
565
- reason. Usually the nowShowingRange will invalidate and recalculate on
566
- its own but you can force the property to need an update if you
567
- prefer.
513
+ The delegate responsible for handling selection changes. This property
514
+ will be either the delegate, content, or the collection view itself,
515
+ whichever implements the SC.CollectionViewDelegate mixin.
516
+
517
+ @property
518
+ @type {Object}
568
519
  */
569
- invalidateNowShowingRange: function() {
570
- this.notifyPropertyChange('nowShowingRange') ;
571
- },
520
+ selectionDelegate: function() {
521
+ var del = this.get('delegate'), content = this.get('content');
522
+ return this.delegateFor('isCollectionViewDelegate', del, content);
523
+ }.property('delegate', 'content').cacheable(),
572
524
 
573
- /** @private
574
- Observer triggers whenever the nowShowingRange changes. If the range has
575
- actually changed and we are on screen, then schedule fast update.
576
- Otherwise, just mark as dirty.
577
- */
578
- nowShowingRangeDidChange: function() {
579
- var range = this.get('nowShowingRange') ;
580
- var old = this._collection_nowShowingRange ;
581
- if (!old || !SC.rangesEqual(range, old)) {
582
- this._collection_nowShowingRange = range ;
583
- if (this.get('isVisibleInWindow')) this.displayDidChange() ;
584
- else this.set('isDirty', YES);
585
- }
586
- }.observes('nowShowingRange'),
525
+ /**
526
+ The delegate responsible for providing additional display information
527
+ about the content. If you bind a collection view to a controller, this
528
+ the content will usually also be the content delegate, though you
529
+ could implement your own delegate if you prefer.
530
+
531
+ @property
532
+ @type {Object}
533
+ */
534
+ contentDelegate: function() {
535
+ var del = this.get('delegate'), content = this.get('content');
536
+ return this.delegateFor('isCollectionContent', del, content);
537
+ }.property('delegate', 'content').cacheable(),
587
538
 
588
- // ......................................
589
- // RENDERING
590
- //
539
+ // ..........................................................
540
+ // CONTENT CHANGES
541
+ //
591
542
 
592
- itemViewAtContentIndex: function(contentIndex) {
593
- var range = this.get('nowShowingRange') ;
594
- var itemView = this.createExampleView() ;
595
- var key, content = SC.makeArray(this.get('content')) ;
596
- var selection = SC.makeArray(this.get('selection')) ;
597
- content = content.objectAt(contentIndex) ;
598
- if (!content) return null ;
599
-
600
- var guids = this._itemViewGuids, guid;
601
- if (!guids) this._itemViewGuids = guids = {};
602
-
603
- // use cache of item view guids to avoid creating temporary objects
604
- guid = SC.guidFor(content);
605
- if (!(key = guids[guid])) {
606
- key = guids[guid] = SC.guidFor(this)+'_'+guid;
543
+ /**
544
+ Called whenever the content array or an item in the content array or a
545
+ property on an item in the content array changes. Reloads the appropriate
546
+ item view when the content array itself changes or calls
547
+ contentPropertyDidChange() if a property changes.
548
+
549
+ Normally you will not call this method directly though you may override
550
+ it if you need to change the way changes to observed ranges are handled.
551
+
552
+ @param {SC.Array} content the content array generating the change
553
+ @param {Object} object the changed object
554
+ @param {String} key the changed property or '[]' or an array change
555
+ @param {SC.IndexSet} indexes affected indexes or null for all items
556
+ @returns {void}
557
+ */
558
+ contentRangeDidChange: function(content, object, key, indexes) {
559
+ if (!object && (key === '[]')) {
560
+ this.reload(indexes); // note: if indexes == null, reloads all
561
+ } else {
562
+ this.contentPropertyDidChange(object, key, indexes);
607
563
  }
564
+ },
565
+
566
+ /**
567
+ Called whenever a property on an item in the content array changes. This
568
+ is only called if you have set observesContentProperties to YES.
608
569
 
609
- itemView.set('content', content) ;
610
- itemView.layerId = key ; // NOTE: cannot use .set here, layerId is RO
611
- itemView.set('isVisible', SC.valueInRange(contentIndex, range)) ;
612
- itemView.set('isSelected', (selection.indexOf(content) == -1) ? NO : YES) ;
570
+ Override this property if you want to do some custom work whenever a
571
+ property on a content object changes.
572
+
573
+ The default implementation does nothing.
613
574
 
614
- // NOTE: *must* set the layout silently...
615
- itemView.layout = this.itemViewLayoutAtContentIndex(contentIndex) ;
616
- itemView.set('parentView', this) ;
617
- return itemView ;
575
+ @param {Object} target the object that changed
576
+ @param {String} key the property that changed value
577
+ @param {SC.IndexSet} indexes the indexes in the content array affected
578
+ @returns {void}
579
+ */
580
+ contentPropertyDidChange: function(target, key, indexes) {
581
+ // Default Does Nothing
618
582
  },
619
583
 
620
- /**
621
- Find the first content item view for the passed event.
584
+ /**
585
+ Called whenever the view needs to updates it's contentRangeObserver to
586
+ reflect the current nowShowing index set. You will not usually call this
587
+ method yourself but you may override it if you need to provide some
588
+ custom range observer behavior.
589
+
590
+ Note that if you do implement this method, you are expected to maintain
591
+ the range observer object yourself. If a range observer has not been
592
+ created yet, this method should create it. If an observer already exists
593
+ this method should udpate it.
622
594
 
623
- This method will go up the view chain, starting with the view that was the
624
- target of the passed event, looking for a child item. This will become
625
- the view that is selected by the mouse event.
595
+ When you create a new range observer, the oberver must eventually call
596
+ contentRangeDidChange() for the collection view to function properly.
626
597
 
627
- This method only works for mouseDown & mouseUp events. mouseMoved events
628
- do not have a target.
598
+ If you override this method you probably also need to override
599
+ destroyRangeObserver() to cleanup any existing range observer.
629
600
 
630
- @param {SC.Event} evt An event
631
- @returns {SC.View} the item view or null
601
+ @returns {void}
632
602
  */
633
- itemViewForEvent: function(evt) {
634
- var responder = this.getPath('pane.rootResponder') ;
635
-
636
- if (!responder) return null ; // fast path
637
-
638
- // need to materialize an itemView under the mouse if possible
639
- var baseGuid = SC.guidFor(this) ;
640
- var baseGuidLen = baseGuid.length ;
641
- var element = evt.target ;
642
- var elementId = element.id.slice(0, baseGuidLen) ;
643
- while (elementId !== baseGuid) {
644
- element = element.parentNode ;
645
- elementId = element.id.slice(0, baseGuidLen) ;
646
- }
603
+ updateContentRangeObserver: function() {
604
+ var nowShowing = this.get('nowShowing'),
605
+ observer = this._cv_contentRangeObserver,
606
+ content = this.get('content');
647
607
 
648
- if (element.id.length === baseGuidLen) {
649
- return null ; // we found ourself, so we're not over a child view
650
- }
608
+ if (!content) return ; // nothing to do
651
609
 
652
- // okay, found the DOM node for the view, go ahead and create it
653
- // first, find the content...
654
- var contentGuid = element.id.slice(baseGuidLen+1) ;
655
- var nowShowingRange = this.get('nowShowingRange') ;
656
- var content = SC.makeArray(this.get('content')) ;
657
- var idx = SC.minRange(nowShowingRange) ;
658
- var max = SC.maxRange(nowShowingRange) ;
659
- var c = content.objectAt(idx) ;
660
- while (SC.guidFor(c) !== contentGuid) {
661
- c = content.objectAt(idx++) ;
662
- if (idx == max) return null ; // couldn't find the content...
610
+ if (observer) {
611
+ content.updateRangeObserver(observer, nowShowing);
612
+ } else {
613
+ var func = this.contentRangeDidChange;
614
+ observer = content.addRangeObserver(nowShowing, this, func, null);
615
+ this._cv_contentRangeObserver = observer ;
663
616
  }
664
617
 
665
- // then create the view for that content
666
- var itemView = this.createExampleView() ;
667
- var selection = SC.makeArray(this.get('selection')) ;
668
- itemView.set('content', c) ;
669
- itemView.layerId = element.id ; // cannot use .set here, layerId is RO
670
- SC.View.views[itemView.layerId] = itemView ; // register for event handling
671
- itemView.set('isVisible', SC.valueInRange(idx, nowShowingRange)) ;
672
- itemView.set('isSelected', (selection.indexOf(c) == -1) ? NO : YES) ;
673
-
674
- // NOTE: *must* set the layout silently...
675
- itemView.layout = this.itemViewLayoutAtContentIndex(idx) ;
676
- itemView.set('parentView', this) ;
677
-
678
- // prevent normal, non-materialized view behavior
679
- // TODO: isMaterialized should do this automatically in SC.View
680
- itemView.layerLocationNeedsUpdate = NO ;
681
- itemView.childViewsNeedLayout = NO ;
682
- itemView.layerNeedsUpdate = NO ;
683
-
684
- // NOTE: still have to search for view, because itemView could contain
685
- // nested views, and the mouseDown should go to them first...
686
- var view = responder.targetViewForEvent(evt) ;
687
- if (!view) return null ; // workaround for error on IE8, see Ticket #169
688
-
689
- // work up the view hierarchy to find a match...
690
- do {
691
- // item clicked was the ContainerView itself... i.e. the user clicked
692
- // outside the child items nothing to return...
693
- if (view == this) return null ;
694
-
695
- // sweet!... the view is not only in the collection, but it says we can
696
- // hit it. hit it and quit it...
697
- if (!view.hitTest || view.hitTest(evt)) return view ;
698
- } while (view = view.get('parentView')) ;
699
-
700
- // nothing was found...
701
- return null ;
702
618
  },
703
619
 
704
- createExampleView: function(content) {
705
- var exampleViewKey = this.get('contentExampleViewKey') ;
706
- var ExampleView = (exampleViewKey) ?
707
- content.get(exampleViewKey) :
708
- this.get('exampleView') ;
620
+ /**
621
+ Called whever the view needs to invalidate the current content range
622
+ observer. This is called whenever the content array changes. You will
623
+ not usually call this method yourself but you may override it if you
624
+ provide your own range observer behavior.
625
+
626
+ Note that if you override this method you should probably also override
627
+ updateRangeObserver() to create or update a range oberver as needed.
709
628
 
710
- if (ExampleView) {
711
- return ExampleView.create({
712
- classNames: ['sc-collection-item'],
713
- owner: this,
714
- displayDelegate: this,
715
- parentView: this,
716
- isVisible: YES,
717
- isMaterialized: YES
718
- });
719
- } else throw "You must define an exampleView class to render collection items with" ;
720
- },
721
-
722
- // ......................................
723
- // SELECTION
724
- //
725
-
726
- /** @private
727
- Finds the smallest index of a content object in the selected array.
629
+ @returns {void}
728
630
  */
729
- _indexOfSelectionTop: function() {
730
- var content = this.get('content');
731
- var sel = this.get('selection');
732
- if (!content || !sel) return - 1;
733
-
734
- // find the first item in the selection
735
- var contentLength = content.get('length') ;
736
- var indexOfSelected = contentLength ; var idx = sel.length ;
737
- while(--idx >= 0) {
738
- var curIndex = content.indexOf(sel[idx]) ;
739
- if ((curIndex >= 0) && (curIndex < indexOfSelected)) indexOfSelected = curIndex ;
631
+ removeContentRangeObserver: function() {
632
+ var content = this.get('content'),
633
+ observer = this._cv_contentRangeObserver ;
634
+
635
+ if (observer) {
636
+ if (content) content.removeRangeObserver(observer);
637
+ this._cv_contentRangeObserver = null ;
740
638
  }
639
+ },
640
+
641
+ /**
642
+ Called whenever the content length changes. This will invalidate the
643
+ length property of the view itself causing the nowShowing to recompute
644
+ which will in turn update the UI accordingly.
741
645
 
742
- return (indexOfSelected >= contentLength) ? -1 : indexOfSelected ;
646
+ @returns {void}
647
+ */
648
+ contentLengthDidChange: function() {
649
+ var content = this.get('content');
650
+ this.set('length', content ? content.get('length') : 0);
743
651
  },
744
652
 
745
653
  /** @private
746
- Finds the largest index of a content object in the selection array.
654
+ Whenever content property changes to a new value:
655
+
656
+ - remove any old observers
657
+ - setup new observers (maybe wait until end of runloop to do this?)
658
+ - recalc height/reload content
659
+ - set content as delegate if delegate was old content
660
+ - reset selection
661
+
662
+ Whenever content array mutates:
663
+
664
+ - possibly stop observing property changes on objects, observe new objs
665
+ - reload effected item views
666
+ - update layout for receiver
747
667
  */
748
- _indexOfSelectionBottom: function() {
749
- var content = this.get('content');
750
- var sel = this.get('selection');
751
- if (!content || !sel) return - 1;
668
+ _cv_contentDidChange: function() {
669
+ var content = this.get('content'),
670
+ lfunc = this.contentLengthDidChange ;
671
+
672
+ if (content === this._content) return this; // nothing to do
673
+
674
+ // cleanup old content
675
+ this.removeContentRangeObserver();
676
+ if (this._content) {
677
+ this._content.removeObserver('length', this, lfunc);
678
+ }
752
679
 
753
- var indexOfSelected = -1 ; var idx = sel.length ;
754
- while(--idx >= 0) {
755
- var curIndex = content.indexOf(sel[idx]) ;
756
- if (curIndex > indexOfSelected) indexOfSelected = curIndex ;
680
+ // cache
681
+ this._content = content;
682
+
683
+ // add new observers - range observer will be added lazily
684
+ if (content) {
685
+ content.addObserver('length', this, lfunc);
757
686
  }
758
687
 
759
- return (indexOfSelected < 0) ? -1 : indexOfSelected ;
760
- },
688
+ // notify all items changed
689
+ this.contentLengthDidChange();
690
+ this.contentRangeDidChange(content, null, '[]', null);
691
+
692
+ }.observes('content'),
761
693
 
762
- /**
763
- Select one or more items before the current selection, optionally
764
- extending the current selection. Also scrolls the selected item into
765
- view.
694
+ // ..........................................................
695
+ // ITEM VIEWS
696
+ //
697
+
698
+ /** @private
699
+
700
+ The indexes that need to be reloaded. Must be one of YES, NO, or an
701
+ SC.IndexSet.
702
+
703
+ */
704
+ _invalidIndexes: NO,
705
+
706
+ /**
707
+ Regenerates the item views for the content items at the specified indexes.
708
+ If you pass null instead of an index set, regenerates all item views.
766
709
 
767
- Selection does not wrap around.
710
+ This method is called automatically whenever the content array changes in
711
+ an observable way, but you can call its yourself also if you need to
712
+ refresh the collection view for some reason.
768
713
 
769
- @param extend {Boolean} (Optional) If true, the selection will be extended
770
- instead of replaced. Defaults to false.
771
- @param numberOfItems {Integer} (Optional) The number of previous to be
772
- selected. Defaults to 1
773
- @returns {void}
714
+ Note that if the length of the content is shorter than the child views
715
+ and you call this method, then the child views will be removed no matter
716
+ what the index.
717
+
718
+ @param {SC.IndexSet} indexes
719
+ @returns {SC.CollectionView} receiver
774
720
  */
775
- selectPreviousItem: function(extend, numberOfItems) {
776
- if (SC.none(numberOfItems)) numberOfItems = 1 ;
777
- if (SC.none(extend)) extend = false ;
721
+ reload: function(indexes) {
722
+ var invalid = this._invalidIndexes ;
723
+ if (indexes && invalid !== YES) {
724
+ if (invalid) invalid.add(indexes);
725
+ else invalid = this._invalidIndexes = indexes.clone();
726
+
727
+ } else this._invalidIndexes = YES ; // force a total reload
778
728
 
779
- var content = this.get('content');
780
- var contentLength = content.get('length') ;
729
+ if (this.get('isVisibleInWindow')) this.invokeOnce(this.reloadIfNeeded);
781
730
 
782
- // if extending, then we need to do some fun stuff to build the array
783
- var selTop, selBottom, anchor ;
784
- if (extend) {
785
- selTop = this._indexOfSelectionTop() ;
786
- selBottom = this._indexOfSelectionBottom() ;
787
- anchor = SC.none(this._selectionAnchor) ? selTop : this._selectionAnchor ;
788
- this._selectionAnchor = anchor ;
731
+ return this ;
732
+ },
733
+
734
+ /**
735
+ Invoked once per runloop to actually reload any needed item views.
736
+ You can call this method at any time to actually force the reload to
737
+ happen immediately if any item views need to be reloaded.
738
+
739
+ Note that this method will also invoke two other callback methods if you
740
+ define them on your subclass:
741
+
742
+ - *willReload()* is called just before the items are reloaded
743
+ - *didReload()* is called jsut after items are reloaded
744
+
745
+ You can use these two methods to setup and teardown caching, which may
746
+ reduce overall cost of a reload. Each method will be passed an index set
747
+ of items that are reloaded or null if all items are reloaded.
748
+
749
+ @returns {SC.CollectionView} receiver
750
+ */
751
+ reloadIfNeeded: function() {
752
+ var invalid = this._invalidIndexes;
753
+ if (!invalid || !this.get('isVisibleInWindow')) return this ; // delay
754
+ this._invalidIndexes = NO ;
755
+
756
+ var content = this.get('content'),
757
+ len = content ? content.get('length'): 0,
758
+ layout = this.computeLayout(),
759
+ bench = SC.BENCHMARK_RELOAD,
760
+ nowShowing = this.get('nowShowing'),
761
+ itemViews = this._sc_itemViews,
762
+ containerView = this.get('containerView') || this,
763
+ views, idx, cvlen, view, childViews, layer ;
764
+
765
+ // if the set is defined but it contains the entire nowShowing range, just
766
+ // replace
767
+ if (invalid.isIndexSet && invalid.contains(nowShowing)) invalid = YES ;
768
+ if (this.willReload) this.willReload(invalid === YES ? null : invalid);
769
+
770
+ // if an index set, just update indexes
771
+ if (invalid.isIndexSet) {
772
+ childViews = containerView.get('childViews');
773
+ cvlen = childViews.get('length');
774
+
775
+ if (bench) {
776
+ SC.Benchmark.start(bench="%@#reloadIfNeeded (Partial)".fmt(this),YES);
777
+ }
778
+
779
+ invalid.forEach(function(idx) {
780
+
781
+ // get the existing item view, if there is one
782
+ var existing = itemViews ? itemViews[idx] : null;
783
+
784
+ // if nowShowing, then reload the item view.
785
+ if (nowShowing.contains(idx)) {
786
+ view = this.itemViewForContentIndex(idx, YES);
787
+ if (existing && existing.parentView === containerView) {
788
+
789
+ // if the existing view has a layer, remove it immediately from
790
+ // the parent. This is necessary because the old and new views
791
+ // will use the same layerId
792
+ layer = existing.get('layer');
793
+ if (layer && layer.parentNode) {
794
+ layer.parentNode.removeChild(layer);
795
+ }
796
+ layer = null ; // avoid leaks
797
+
798
+ containerView.replaceChild(view, existing);
799
+ } else {
800
+ containerView.appendChild(view);
801
+ }
802
+
803
+ // if not nowShowing, then remove the item view if needed
804
+ } else if (existing && existing.parentView === containerView) {
805
+ delete itemViews[idx];
806
+ containerView.removeChild(existing);
807
+ }
808
+ },this);
809
+
810
+ if (bench) SC.Benchmark.end(bench);
811
+
812
+ // if set is NOT defined, replace entire content with nowShowing
813
+ } else {
814
+
815
+ if (bench) {
816
+ SC.Benchmark.start(bench="%@#reloadIfNeeded (Full)".fmt(this),YES);
817
+ }
818
+
819
+ // truncate cached item views since they will all be removed from the
820
+ // container anyway.
821
+ if (itemViews) itemViews.length = 0 ;
822
+
823
+ views = [];
824
+ nowShowing.forEach(function(idx) {
825
+ views.push(this.itemViewForContentIndex(idx, YES));
826
+ }, this);
827
+
828
+ // below is an optimized version of:
829
+ //this.replaceAllChildren(views);
830
+ containerView.beginPropertyChanges();
831
+ containerView.destroyLayer().removeAllChildren();
832
+ containerView.set('childViews', views); // quick swap
833
+ containerView.replaceLayer();
834
+ containerView.endPropertyChanges();
835
+
836
+ if (bench) SC.Benchmark.end(bench);
837
+
838
+ }
839
+
840
+ // adjust my own layout if computed
841
+ if (layout) this.adjust(layout);
842
+ if (this.didReload) this.didReload(invalid === YES ? null : invalid);
843
+
844
+
845
+ return this ;
846
+ },
847
+
848
+ displayProperties: 'isFirstResponder isEnabled isActive'.w(),
849
+
850
+ /** @private
851
+ If we're asked to render the receiver view for the first time but the
852
+ child views still need to be added, go ahead and add them.
853
+ */
854
+ render: function(context, firstTime) {
855
+ if (firstTime && this._needsReload) this.reloadIfNeeded ;
856
+
857
+ // add classes for other state.
858
+ context.setClass('focus', this.get('isFirstResponder'));
859
+ context.setClass('disabled', !this.get('isEnabled'));
860
+ context.setClass('active', this.get('isActive'));
861
+
862
+ return sc_super();
863
+ },
864
+
865
+
866
+ _TMP_ATTRS: {},
867
+ _COLLECTION_CLASS_NAMES: 'sc-collection-item'.w(),
868
+ _GROUP_COLLECTION_CLASS_NAMES: 'sc-collection-item sc-group-item'.w(),
869
+
870
+ /**
871
+ Returns the item view for the content object at the specified index. Call
872
+ this method instead of accessing child views directly whenever you need
873
+ to get the view associated with a content index.
874
+
875
+ Although this method take two parameters, you should almost always call
876
+ it with just the content index. The other two parameters are used
877
+ internally by the CollectionView.
878
+
879
+ If you need to change the way the collection view manages item views
880
+ you can override this method as well. If you just want to change the
881
+ default options used when creating item views, override createItemView()
882
+ instead.
883
+
884
+ Note that if you override this method, then be sure to implement this
885
+ method so that it uses a cache to return the same item view for a given
886
+ index unless "force" is YES. In that case, generate a new item view and
887
+ replace the old item view in your cache with the new item view.
888
+
889
+ @param {Number} idx the content index
890
+ @param {Boolean} rebuild internal use only
891
+ @returns {SC.View} instantiated view
892
+ */
893
+ itemViewForContentIndex: function(idx, rebuild) {
894
+
895
+ // return from cache if possible
896
+ var content = this.get('content'),
897
+ itemViews = this._sc_itemViews,
898
+ item = content.objectAt(idx),
899
+ del = this.get('contentDelegate'),
900
+ groupIndexes = del.contentGroupIndexes(this, content),
901
+ isGroupView = NO,
902
+ key, ret, E, layout, layerId;
903
+
904
+ // use cache if available
905
+ if (!itemViews) itemViews = this._sc_itemViews = [] ;
906
+ if (!rebuild && (ret = itemViews[idx])) return ret ;
907
+
908
+ // otherwise generate...
909
+
910
+ // first, determine the class to use
911
+ isGroupView = groupIndexes && groupIndexes.contains(idx);
912
+ if (isGroupView) isGroupView = del.contentIndexIsGroup(this, content,idx);
913
+ if (isGroupView) {
914
+ key = this.get('contentGroupExampleViewKey');
915
+ if (key && item) E = item.get(key);
916
+ if (!E) E = this.get('groupExampleView') || this.get('exampleView');
917
+
918
+ } else {
919
+ key = this.get('contentExampleViewKey');
920
+ if (key && item) E = item.get(key);
921
+ if (!E) E = this.get('exampleView');
922
+ }
923
+
924
+ // collect some other state
925
+ var attrs = this._TMP_ATTRS;
926
+ attrs.contentIndex = idx;
927
+ attrs.content = item ;
928
+ attrs.owner = attrs.displayDelegate = this;
929
+ attrs.parentView = this.get('containerView') || this ;
930
+ attrs.page = this.page ;
931
+ attrs.layerId = this.layerIdFor(idx, item);
932
+ attrs.isEnabled = del.contentIndexIsEnabled(this, content, idx);
933
+ attrs.isSelected = del.contentIndexIsSelected(this, content, idx);
934
+ attrs.outlineLevel = del.contentIndexOutlineLevel(this, content, idx);
935
+ attrs.disclosureState = del.contentIndexDisclosureState(this, content, idx);
936
+ attrs.isGroupView = isGroupView;
937
+ attrs.isVisibleInWindow = this.isVisibleInWindow;
938
+ if (isGroupView) attrs.classNames = this._GROUP_COLLECTION_CLASS_NAMES;
939
+ else attrs.classNames = this._COLLECTION_CLASS_NAMES;
940
+
941
+ layout = this.layoutForContentIndex(idx);
942
+ if (layout) {
943
+ attrs.layout = layout;
944
+ } else {
945
+ delete attrs.layout ;
946
+ }
947
+
948
+ ret = this.createItemView(E, idx, attrs);
949
+ itemViews[idx] = ret ;
950
+ return ret ;
951
+ },
952
+
953
+ _TMP_LAYERID: [],
954
+
955
+ /**
956
+ Primitive to instantiate an item view. You will be passed the class
957
+ and a content index. You can override this method to perform any other
958
+ one time setup.
959
+
960
+ Note that item views may be created somewhat frequently so keep this fast.
961
+
962
+ *IMPORTANT:* The attrs hash passed is reused each time this method is
963
+ called. If you add properties to this hash be sure to delete them before
964
+ returning from this method.
965
+
966
+ @param {Class} exampleClass example view class
967
+ @param {Number} idx the content index
968
+ @param {Hash} attrs expected attributes
969
+ @returns {SC.View} item view instance
970
+ */
971
+ createItemView: function(exampleClass, idx, attrs) {
972
+ return exampleClass.create(attrs);
973
+ },
974
+
975
+ /**
976
+ Generates a layerId for the passed index and item. Usually the default
977
+ implementation is suitable.
978
+
979
+ @param {Number} idx the content index
980
+ @returns {String} layer id, must be suitable for use in HTML id attribute
981
+ */
982
+ layerIdFor: function(idx) {
983
+ var ret = this._TMP_LAYERID;
984
+ ret[0] = SC.guidFor(this);
985
+ ret[1] = idx;
986
+ return ret.join('-');
987
+ },
988
+
989
+ /**
990
+ Extracts the content index from the passed layerID. If the layer id does
991
+ not belong to the receiver or if no value could be extracted, returns NO.
992
+
993
+ @param {String} id the layer id
994
+ */
995
+ contentIndexForLayerId: function(id) {
996
+ if (!id || !(id = id.toString())) return null ; // nothing to do
997
+
998
+ var base = this._baseLayerId;
999
+ if (!base) base = this._baseLayerId = SC.guidFor(this)+"-";
1000
+
1001
+ // no match
1002
+ if ((id.length <= base.length) || (id.indexOf(base) !== 0)) return null ;
1003
+ var ret = Number(id.slice(id.lastIndexOf('-')+1));
1004
+ return isNaN(ret) ? null : ret ;
1005
+ },
1006
+
1007
+
1008
+ /**
1009
+ Find the first content item view for the passed event.
1010
+
1011
+ This method will go up the view chain, starting with the view that was the
1012
+ target of the passed event, looking for a child item. This will become
1013
+ the view that is selected by the mouse event.
1014
+
1015
+ This method only works for mouseDown & mouseUp events. mouseMoved events
1016
+ do not have a target.
1017
+
1018
+ @param {SC.Event} evt An event
1019
+ @returns {SC.View} the item view or null
1020
+ */
1021
+ itemViewForEvent: function(evt) {
1022
+ var responder = this.getPath('pane.rootResponder') ;
1023
+ if (!responder) return null ; // fast path
1024
+
1025
+ var base = SC.guidFor(this) + '-',
1026
+ baseLen = base.length,
1027
+ element = evt.target,
1028
+ layer = this.get('layer'),
1029
+ contentIndex = null,
1030
+ id, itemView, ret ;
1031
+
1032
+ // walk up the element hierarchy until we find this or an element with an
1033
+ // id matching the base guid (i.e. a collection item)
1034
+ while (element && element !== document && element !== layer) {
1035
+ id = element ? element.getAttribute('id') : null ;
1036
+ if (id && (contentIndex = this.contentIndexForLayerId(id)) !== null) {
1037
+ break;
1038
+ }
1039
+ element = element.parentNode ;
1040
+ }
1041
+
1042
+ // no matching element found?
1043
+ if (contentIndex===null || (element === layer)) {
1044
+ element = layer = null; // avoid memory leaks
1045
+ return null;
1046
+ }
1047
+
1048
+ // okay, found the DOM node for the view, go ahead and create it
1049
+ // first, find the contentIndex
1050
+ if (contentIndex >= this.get('length')) {
1051
+ throw "layout for item view %@ was found when item view does not exist (%@)".fmt(id, this);
1052
+ }
1053
+
1054
+ return this.itemViewForContentIndex(contentIndex);
1055
+ },
1056
+
1057
+ // ..........................................................
1058
+ // DISCLOSURE SUPPORT
1059
+ //
1060
+
1061
+ /**
1062
+ Expands any items in the passed selection array that have a disclosure
1063
+ state.
1064
+
1065
+ @param {SC.IndexSet} indexes the indexes to expand
1066
+ @returns {SC.CollectionView} receiver
1067
+ */
1068
+ expand: function(indexes) {
1069
+ if (!indexes) return this; // nothing to do
1070
+ var del = this.get('contentDelegate'),
1071
+ content = this.get('content');
1072
+
1073
+ indexes.forEach(function(i) {
1074
+ var state = del.contentIndexDisclosureState(this, content, i);
1075
+ if (state === SC.BRANCH_CLOSED) del.contentIndexExpand(this,content,i);
1076
+ }, this);
1077
+ return this;
1078
+ },
1079
+
1080
+ /**
1081
+ Collapses any items in the passed selection array that have a disclosure
1082
+ state.
1083
+
1084
+ @param {SC.IndexSet} indexes the indexes to expand
1085
+ @returns {SC.CollectionView} receiver
1086
+ */
1087
+ collapse: function(indexes) {
1088
+ if (!indexes) return this; // nothing to do
1089
+ var del = this.get('contentDelegate'),
1090
+ content = this.get('content');
1091
+
1092
+ indexes.forEach(function(i) {
1093
+ var state = del.contentIndexDisclosureState(this, content, i);
1094
+ if (state === SC.BRANCH_OPEN) del.contentIndexCollapse(this,content,i);
1095
+ }, this);
1096
+ return this;
1097
+ },
1098
+
1099
+ // ..........................................................
1100
+ // SELECTION SUPPORT
1101
+ //
1102
+
1103
+ /** @private
1104
+
1105
+ Called whenever the selection object is changed to a new value. Begins
1106
+ observing the selection for changes.
1107
+
1108
+ */
1109
+ _cv_selectionDidChange: function() {
1110
+ var sel = this.get('selection'),
1111
+ last = this._cv_selection,
1112
+ func = this._cv_selectionContentDidChange;
1113
+
1114
+ if (sel === last) return this; // nothing to do
1115
+ if (last) last.removeObserver('[]', this, func);
1116
+ if (sel) sel.addObserver('[]', this, func);
1117
+
1118
+ this._cv_selection = sel ;
1119
+ this._cv_selectionContentDidChange();
1120
+ }.observes('selection'),
1121
+
1122
+ /** @private
1123
+
1124
+ Called whenever the selection object or its content changes. This will
1125
+ repaint any items that changed their selection state.
1126
+
1127
+ */
1128
+ _cv_selectionContentDidChange: function() {
1129
+ var sel = this.get('selection'),
1130
+ last = this._cv_selindexes, // clone of last known indexes
1131
+ content = this.get('content'),
1132
+ diff ;
1133
+
1134
+ // save new last
1135
+ this._cv_selindexes = sel ? sel.frozenCopy() : null;
1136
+
1137
+ // determine which indexes are now invalid
1138
+ if (last) last = last.indexSetForSource(content);
1139
+ if (sel) sel = sel.indexSetForSource(content);
1140
+
1141
+ if (sel && last) diff = sel.without(last).add(last.without(sel));
1142
+ else diff = sel || last;
1143
+
1144
+ if (diff && diff.get('length')>0) this.reloadSelectionIndexes(diff);
1145
+ },
1146
+
1147
+ /** @private
1148
+ Contains the current item views that need their selection to be repainted.
1149
+ This may be either NO, YES, or an IndexSet.
1150
+ */
1151
+ _invalidSelection: NO,
1152
+
1153
+ /**
1154
+ Called whenever the selection changes. The passed index set will contain
1155
+ any affected indexes including those indexes that were previously
1156
+ selected and now should be deselected.
1157
+
1158
+ Pass null to reload the selection state for all items.
1159
+
1160
+ @param {SC.IndexSet} indexes affected indexes
1161
+ @returns {SC.CollectionView} reciever
1162
+ */
1163
+ reloadSelectionIndexes: function(indexes) {
1164
+ var invalid = this._invalidSelection ;
1165
+ if (indexes && (invalid !== YES)) {
1166
+ if (invalid) invalid.add(indexes)
1167
+ else invalid = this._invalidSelection = indexes.copy();
1168
+
1169
+ } else this._invalidSelection = YES ; // force a total reload
1170
+
1171
+ if (this.get('isVisibleInWindow')) {
1172
+ this.invokeOnce(this.reloadSelectionIndexesIfNeeded);
1173
+ }
1174
+
1175
+ return this ;
1176
+ },
1177
+
1178
+ /**
1179
+ Reloads the selection state if needed on any dirty indexes. Normally this
1180
+ will run once at the end of the runloop, but you can force the item views
1181
+ to reload their selection immediately by calling this method.
1182
+
1183
+ You can also override this method if needed to change the way the
1184
+ selection is reloaded on item views. The default behavior will simply
1185
+ find any item views in the nowShowing range that are affected and
1186
+ modify them.
1187
+
1188
+ @returns {SC.CollectionView} receiver
1189
+ */
1190
+ reloadSelectionIndexesIfNeeded: function() {
1191
+ var invalid = this._invalidSelection;
1192
+ if (!invalid || !this.get('isVisibleInWindow')) return this ;
1193
+
1194
+ var nowShowing = this.get('nowShowing'),
1195
+ reload = this._invalidIndexes,
1196
+ content = this.get('content'),
1197
+ sel = this.get('selection');
1198
+
1199
+ this._invalidSelection = NO; // reset invalid
1200
+
1201
+ // fast path. if we are going to reload everything anyway, just forget
1202
+ // about it. Also if we don't have a nowShowing, nothing to do.
1203
+ if (reload === YES || !nowShowing) return this ;
1204
+
1205
+ // if invalid is YES instead of index set, just reload everything
1206
+ if (invalid === YES) invalid = nowShowing;
1207
+
1208
+ // if we will reload some items anyway, don't bother
1209
+ if (reload && reload.isIndexSet) invalid = invalid.without(reload);
1210
+
1211
+ // iterate through each item and set the isSelected state.
1212
+ invalid.forEach(function(idx) {
1213
+ if (!nowShowing.contains(idx)) return; // not showing
1214
+ var view = this.itemViewForContentIndex(idx, NO);
1215
+ if (view) view.set('isSelected', sel ? sel.contains(content, idx) : NO);
1216
+ },this);
1217
+
1218
+ return this ;
1219
+ },
1220
+
1221
+ /**
1222
+ Selection primitive. Selects the passed IndexSet of items, optionally
1223
+ extending the current selection. If extend is NO or not passed then this
1224
+ will replace the selection with the passed value. Otherwise the indexes
1225
+ will be added to the current selection.
1226
+
1227
+ @param {Number|SC.IndexSet} indexes index or indexes to select
1228
+ @param extend {Boolean} optionally extend the selection
1229
+ @returns {SC.CollectionView} receiver
1230
+ */
1231
+ select: function(indexes, extend) {
1232
+
1233
+ var content = this.get('content'),
1234
+ del = this.get('selectionDelegate'),
1235
+ cdel = this.get('contentDelegate'),
1236
+ groupIndexes = cdel.contentGroupIndexes(this, content),
1237
+ sel;
1238
+
1239
+ // normalize
1240
+ if (SC.typeOf(indexes) === SC.T_NUMBER) {
1241
+ indexes = SC.IndexSet.create(indexes, 1);
1242
+ }
1243
+
1244
+ // if we are passed an empty index set or null, clear the selection.
1245
+ if (indexes && indexes.get('length')>0) {
1246
+
1247
+ // first remove any group indexes - these can never be selected
1248
+ if (groupIndexes && groupIndexes.get('length')>0) {
1249
+ indexes = indexes.copy().remove(groupIndexes);
1250
+ }
789
1251
 
1252
+ // give the delegate a chance to alter the items
1253
+ indexes = del.collectionViewShouldSelectIndexes(this, indexes, extend);
1254
+ if (!indexes || indexes.get('length')===0) return this; // nothing to do
1255
+
1256
+ } else indexes = null;
1257
+
1258
+ // build the selection object, merging if needed
1259
+ if (extend && (sel = this.get('selection'))) sel = sel.copy();
1260
+ else sel = SC.SelectionSet.create();
1261
+ if (indexes) sel.add(content, indexes);
1262
+
1263
+ // give delegate one last chance
1264
+ sel = del.collectionViewSelectionForProposedSelection(this, sel);
1265
+ if (!sel) sel = SC.SelectionSet.create(); // empty
1266
+
1267
+ // if we're not extending the selection, clear the selection anchor
1268
+ this._selectionAnchor = null ;
1269
+ this.set('selection', sel.freeze()) ;
1270
+ return this;
1271
+ },
1272
+
1273
+ /**
1274
+ Primtive to remove the indexes from the selection.
1275
+
1276
+ @param {Number|SC.IndexSet} indexes index or indexes to select
1277
+ @returns {SC.CollectionView} receiver
1278
+ */
1279
+ deselect: function(indexes) {
1280
+
1281
+ var sel = this.get('selection'),
1282
+ content = this.get('content'),
1283
+ del = this.get('selectionDelegate');
1284
+
1285
+ if (!sel || sel.get('length')===0) return this; // nothing to do
1286
+
1287
+ // normalize
1288
+ if (SC.typeOf(indexes) === SC.T_NUMBER) {
1289
+ indexes = SC.IndexSet.create(indexes, 1);
1290
+ }
1291
+
1292
+ // give the delegate a chance to alter the items
1293
+ indexes = del.collectionViewShouldDeselectIndexes(this, indexes) ;
1294
+ if (!indexes || indexes.get('length')===0) return this; // nothing to do
1295
+
1296
+ // now merge change - note we expect sel && indexes to not be null
1297
+ sel = sel.copy().remove(content, indexes);
1298
+ sel = del.collectionViewSelectionForProposedSelection(this, sel);
1299
+ if (!sel) sel = SC.SelectionSet.create(); // empty
1300
+
1301
+ this.set('selection', sel.freeze()) ;
1302
+ return this ;
1303
+ },
1304
+
1305
+ /** @private
1306
+ Finds the next selectable item, up to content length, by asking the
1307
+ delegate. If a non-selectable item is found, the index is skipped. If
1308
+ no item is found, selection index is returned unmodified.
1309
+
1310
+ Return value will always be in the range of the bottom of the current
1311
+ selection index and the proposed index.
1312
+
1313
+ @param {Number} proposedIndex the desired index to select
1314
+ @param {Number} bottom optional bottom of selection use as fallback
1315
+ @returns {Number} next selectable index.
1316
+ */
1317
+ _findNextSelectableItemFromIndex: function(proposedIndex, bottom) {
1318
+
1319
+ var lim = this.get('length'),
1320
+ range = SC.IndexSet.create(),
1321
+ content = this.get('content'),
1322
+ del = this.get('selectionDelegate'),
1323
+ cdel = this.get('contentDelegate'),
1324
+ groupIndexes = cdel.contentGroupIndexes(this, content),
1325
+ ret, sel ;
1326
+
1327
+ // fast path
1328
+ if (!groupIndexes && (del.collectionViewShouldSelectIndexes === this.collectionViewShouldSelectIndexes)) {
1329
+ return proposedIndex;
1330
+ }
1331
+
1332
+ // loop forwards looking for an index that is allowed by delegate
1333
+ // we could alternatively just pass the whole range but this might be
1334
+ // slow for the delegate
1335
+ while (proposedIndex < lim) {
1336
+ if (!groupIndexes || !groupIndexes.contains(proposedIndex)) {
1337
+ range.add(proposedIndex);
1338
+ ret = del.collectionViewShouldSelectIndexes(this, range);
1339
+ if (ret && ret.get('length') >= 1) return proposedIndex ;
1340
+ range.remove(proposedIndex);
1341
+ }
1342
+ proposedIndex++;
1343
+ }
1344
+
1345
+ // if nothing was found, return top of selection
1346
+ if (bottom === undefined) {
1347
+ sel = this.get('selection');
1348
+ bottom = sel ? sel.get('max') : -1 ;
1349
+ }
1350
+ return bottom ;
1351
+ },
1352
+
1353
+ /** @private
1354
+ Finds the previous selectable item, up to the first item, by asking the
1355
+ delegate. If a non-selectable item is found, the index is skipped. If
1356
+ no item is found, selection index is returned unmodified.
1357
+
1358
+ @param {Integer} proposedIndex the desired index to select
1359
+ @returns {Integer} the previous selectable index. This will always be in the range of the top of the current selection index and the proposed index.
1360
+ @private
1361
+ */
1362
+ _findPreviousSelectableItemFromIndex: function(proposedIndex, top) {
1363
+ var range = SC.IndexSet.create(),
1364
+ content = this.get('content'),
1365
+ del = this.get('selectionDelegate'),
1366
+ cdel = this.get('contentDelegate'),
1367
+ groupIndexes = cdel.contentGroupIndexes(this, content),
1368
+ ret ;
1369
+
1370
+ if (SC.none(proposedIndex)) proposedIndex = -1;
1371
+
1372
+ // fast path
1373
+ if (!groupIndexes && (del.collectionViewShouldSelectIndexes === this.collectionViewShouldSelectIndexes)) {
1374
+ return proposedIndex;
1375
+ }
1376
+
1377
+ // loop backwards looking for an index that is allowed by delegate
1378
+ // we could alternatively just pass the whole range but this might be
1379
+ // slow for the delegate
1380
+ while (proposedIndex >= 0) {
1381
+ if (!groupIndexes || !groupIndexes.contains(proposedIndex)) {
1382
+ range.add(proposedIndex);
1383
+ ret = del.collectionViewShouldSelectIndexes(this, range);
1384
+ if (ret && ret.get('length') >= 1) return proposedIndex ;
1385
+ range.remove(proposedIndex);
1386
+ }
1387
+ proposedIndex--;
1388
+ }
1389
+
1390
+ // if nothing was found, return top of selection
1391
+ if (top === undefined) {
1392
+ var sel = this.get('selection');
1393
+ top = sel ? sel.get('min') : -1 ;
1394
+ }
1395
+ if (SC.none(top)) top = -1;
1396
+ return top ;
1397
+ },
1398
+
1399
+ /**
1400
+ Select one or more items before the current selection, optionally
1401
+ extending the current selection. Also scrolls the selected item into
1402
+ view.
1403
+
1404
+ Selection does not wrap around.
1405
+
1406
+ @param extend {Boolean} (Optional) If true, the selection will be extended
1407
+ instead of replaced. Defaults to false.
1408
+ @param numberOfItems {Integer} (Optional) The number of previous to be
1409
+ selected. Defaults to 1
1410
+ @returns {SC.CollectionView} receiver
1411
+ */
1412
+ selectPreviousItem: function(extend, numberOfItems) {
1413
+ if (SC.none(numberOfItems)) numberOfItems = 1 ;
1414
+ if (SC.none(extend)) extend = false ;
1415
+
1416
+ var sel = this.get('selection'),
1417
+ content = this.get('content');
1418
+ if (sel) sel = sel.indexSetForSource(content);
1419
+
1420
+ var selTop = sel ? sel.get('min') : -1,
1421
+ selBottom = sel ? sel.get('max')-1 : -1,
1422
+ anchor = this._selectionAnchor;
1423
+ if (SC.none(anchor)) anchor = selTop;
1424
+
1425
+ // if extending, then we need to do some fun stuff to build the array
1426
+ if (extend) {
1427
+
790
1428
  // If the selBottom is after the anchor, then reduce the selection
791
1429
  if (selBottom > anchor) {
792
1430
  selBottom = selBottom - numberOfItems ;
@@ -797,30 +1435,27 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
797
1435
  }
798
1436
 
799
1437
  // Ensure we are not out of bounds
800
- if (selTop < 0) selTop = 0 ;
1438
+ if (SC.none(selTop) || (selTop < 0)) selTop = 0 ;
801
1439
  if (selBottom < selTop) selBottom = selTop ;
802
1440
 
803
1441
  // if not extending, just select the item previous to the selTop
804
1442
  } else {
805
- selTop = this._findPreviousSelectableItemFromIndex(this._indexOfSelectionTop() - numberOfItems);
806
- if (selTop < 0) selTop = 0 ;
1443
+ selTop = this._findPreviousSelectableItemFromIndex(selTop - numberOfItems);
1444
+ if (SC.none(selTop) || (selTop < 0)) selTop = 0 ;
807
1445
  selBottom = selTop ;
808
1446
  anchor = null ;
809
1447
  }
810
1448
 
811
- // now build array of new items to select
812
- var items = [] ;
813
- while(selTop <= selBottom) {
814
- items[items.length] = content.objectAt(selTop++) ;
815
- }
1449
+ var scrollToIndex = selTop ;
816
1450
 
817
- // ensure that the item is visible and set the selection
818
- if (items.length > 0) {
819
- this.scrollToContent(items[0]);
820
- this.selectItems(items);
821
- }
1451
+ // now build new selection
1452
+ sel = SC.IndexSet.create(selTop, selBottom+1-selTop);
822
1453
 
1454
+ // ensure that the item is visible and set the selection
1455
+ this.scrollToContentIndex(scrollToIndex) ;
1456
+ this.select(sel) ;
823
1457
  this._selectionAnchor = anchor ;
1458
+ return this ;
824
1459
  },
825
1460
 
826
1461
  /**
@@ -833,215 +1468,144 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
833
1468
  instead of replaced. Defaults to false.
834
1469
  @param numberOfItems {Integer} (Optional) The number of items to be
835
1470
  selected. Defaults to 1.
836
- @returns {void}
1471
+ @returns {SC.CollectionView} receiver
837
1472
  */
838
1473
  selectNextItem: function(extend, numberOfItems) {
839
1474
  if (SC.none(numberOfItems)) numberOfItems = 1 ;
840
1475
  if (SC.none(extend)) extend = false ;
841
-
842
- var content = this.get('content');
843
- var contentLength = content.get('length') ;
844
-
1476
+
1477
+ var sel = this.get('selection'),
1478
+ content = this.get('content');
1479
+ if (sel) sel = sel.indexSetForSource(content);
1480
+
1481
+ var selTop = sel ? sel.get('min') : -1,
1482
+ selBottom = sel ? sel.get('max')-1 : -1,
1483
+ anchor = this._selectionAnchor,
1484
+ lim = this.get('length');
1485
+
1486
+ if (SC.none(anchor)) anchor = selTop;
1487
+
845
1488
  // if extending, then we need to do some fun stuff to build the array
846
- var selTop, selBottom, anchor ;
847
1489
  if (extend) {
848
- selTop = this._indexOfSelectionTop() ;
849
- selBottom = this._indexOfSelectionBottom() ;
850
- anchor = SC.none(this._selectionAnchor) ? selTop : this._selectionAnchor ;
851
- this._selectionAnchor = anchor ;
852
1490
 
853
1491
  // If the selTop is before the anchor, then reduce the selection
854
1492
  if (selTop < anchor) {
855
1493
  selTop = selTop + numberOfItems ;
856
1494
 
857
- // otherwise, select the next item after the top
1495
+ // otherwise, select the next item after the bottom
858
1496
  } else {
859
- selBottom = this._findNextSelectableItemFromIndex(selBottom + numberOfItems);
1497
+ selBottom = this._findNextSelectableItemFromIndex(selBottom + numberOfItems, selBottom);
860
1498
  }
861
1499
 
862
1500
  // Ensure we are not out of bounds
863
- if (selBottom >= contentLength) selBottom = contentLength-1;
1501
+ if (selBottom >= lim) selBottom = lim-1;
864
1502
  if (selTop > selBottom) selTop = selBottom ;
865
1503
 
866
1504
  // if not extending, just select the item next to the selBottom
867
1505
  } else {
868
- selBottom = this._findNextSelectableItemFromIndex(this._indexOfSelectionBottom() + numberOfItems);
1506
+ selBottom = this._findNextSelectableItemFromIndex(selBottom + numberOfItems, selBottom);
869
1507
 
870
- if (selBottom >= contentLength) selBottom = contentLength-1;
1508
+ if (selBottom >= lim) selBottom = lim-1;
871
1509
  selTop = selBottom ;
872
1510
  anchor = null ;
873
1511
  }
874
1512
 
875
- // now build array of new items to select
876
- var items = [] ;
877
- while(selTop <= selBottom) {
878
- items[items.length] = content.objectAt(selTop++) ;
879
- }
880
-
881
- // ensure that the item is visible and set the selection
882
- if (items.length > 0) {
883
- this.scrollToContent(items[0]);
884
- this.selectItems(items);
885
- }
1513
+ var scrollToIndex = selBottom ;
886
1514
 
1515
+ // now build new selection
1516
+ sel = SC.IndexSet.create(selTop, selBottom-selTop+1);
1517
+
1518
+ // ensure that the item is visible and set the selection
1519
+ this.scrollToContentIndex(scrollToIndex) ;
1520
+ this.select(sel) ;
887
1521
  this._selectionAnchor = anchor ;
1522
+ return this ;
888
1523
  },
889
-
890
- /**
891
- Scroll the rootElement (if needed) to ensure that the item is visible.
892
- @param {SC.Record} record The record to scroll to
893
- @returns {void}
894
- */
895
- scrollToContent: function(record) {
896
- var content, itemView, contentIndex, groupBy;
897
1524
 
898
- // find the itemView. if not present, add one.
899
- content = SC.makeArray(this.get('content'));
900
- if (content.indexOf(record) < 0) return ; // do nothing if not in content.
901
-
902
- itemView = this.itemViewForContent(record) ;
903
- if (!itemView) {
904
- content = SC.makeArray(this.get('content')) ;
905
- contentIndex = content.indexOf(record) ;
906
- groupBy = this.get('groupBy');
907
- itemView = this._insertItemViewFor(record, groupBy, contentIndex);
908
- }
909
- if (itemView) this.scrollToItemView(itemView);
910
- },
911
-
912
1525
  /**
913
- Scroll the rootElement (if needed) to ensure that the item is visible.
914
- @param {SC.ClassicView} view The item view to scroll to
915
- @returns {void}
916
- */
917
- scrollToItemView: function(view) {
918
- // find first scrollable view.
919
- var scrollable = this ;
920
- while(scrollable && (scrollable != SC.window) && (!scrollable.get('isScrollable'))) {
921
- scrollable = scrollable.get('parentNode') ;
922
- }
923
- if (!scrollable || (scrollable == SC.window)) return ; // no scrollable!
924
- scrollable.scrollToVisible(view) ;
925
- },
926
-
927
- /**
928
- Selects the passed array of items, optionally extending the
929
- current selection.
1526
+ Deletes the selected content if canDeleteContent is YES. This will invoke
1527
+ delegate methods to provide fine-grained control. Returns YES if the
1528
+ deletion was possible, even if none actually occurred.
930
1529
 
931
- @param items {Array} The item or items to select.
932
- @param extendSelection {Boolean} If true, extends the selection instead of
933
- replacing it.
1530
+ @returns {Boolean} YES if deletion is possible.
934
1531
  */
935
- selectItems: function(items, extendSelection) {
936
- var base = (extendSelection) ? this.get('selection') : [] ;
937
- var sel = [] ;
938
-
939
- items = SC.makeArray(items) ;
940
- for (var i = 0, len = items.length; i < len; i++) {
941
- if (this.invokeDelegateMethod(this.delegate, 'collectionViewShouldSelectItem', this, items[i])) {
942
- sel.push(items[i]);
943
- }
944
- }
1532
+ deleteSelection: function() {
945
1533
 
946
- var set = SC.Set.create(base), obj ;
947
- set.addEach(sel) ; // sel contains selectable items
948
- sel.length = 0 ; // we don't want duplicates in the selection, so clear it
949
- while (obj = set.pop()) sel.push(obj) ; // now fill it back up..
1534
+ // perform some basic checks...
1535
+ if (!this.get('canDeleteContent')) return NO;
1536
+
1537
+ var sel = this.get('selection'),
1538
+ content = this.get('content'),
1539
+ del = this.get('selectionDelegate'),
1540
+ indexes = sel&&content ? sel.indexSetForSource(content) : null;
1541
+
1542
+ if (!content || !indexes || indexes.get('length') === 0) return NO ;
950
1543
 
951
- // if we're not extending the selection, clear the selection anchor
952
- this._selectionAnchor = null ;
953
- this.set('selection', sel) ;
954
- },
955
-
956
- /**
957
- Removes the items from the selection.
958
- */
959
- deselectItems: function(items) {
960
- var base = SC.makeArray(this.get('selection')) ;
961
- var sel = [] ;
1544
+ // let the delegate decide what to actually delete. If this returns an
1545
+ // empty index set or null, just do nothing.
1546
+ indexes = del.collectionViewShouldDeleteIndexes(this, indexes);
1547
+ if (!indexes || indexes.get('length') === 0) return NO ;
962
1548
 
963
- items = SC.makeArray(items) ;
1549
+ // now have the delegate (or us) perform the deletion. The default
1550
+ // delegate implementation just uses standard SC.Array methods to do the
1551
+ // right thing.
1552
+ del.collectionViewDeleteContent(this, this.get('content'), indexes);
964
1553
 
965
- var set = SC.Set.create(base), obj ;
966
- set.removeEach(items) ;
967
- while (obj = set.pop()) sel.push(obj) ;
1554
+ // also, fix up the selection by removing the actual items we removed
1555
+ // set selection directly instead of calling select() since we are just
1556
+ // fixing up the selection.
1557
+ sel = this.get('selection').copy().remove(content, indexes);
1558
+ this.set('selection', sel.freeze());
968
1559
 
969
- this.set('selection', sel) ;
1560
+ return YES ;
970
1561
  },
971
1562
 
1563
+ // ..........................................................
1564
+ // SCROLLING
1565
+ //
1566
+
972
1567
  /**
973
- Deletes the selected content if canDeleteContent is YES.
974
-
975
- This will invoke delegate methods to provide fine-grained control.
1568
+ Scroll the rootElement (if needed) to ensure that the item is visible.
976
1569
 
977
- @returns {Boolean} YES if deletion is possible, even if none actually occurred.
1570
+ @param {Number} contentIndex The index of the item to scroll to
1571
+ @returns {SC.CollectionView} receiver
978
1572
  */
979
- deleteSelection: function() {
980
- // console.log('deleteSelection called on %@'.fmt(this));
981
- // perform some basic checks...
982
- if (!this.get('canDeleteContent')) return NO;
983
- var sel = SC.makeArray(this.get('selection'));
984
- if (!sel || sel.get('length') === 0) return NO ;
985
-
986
- // let the delegate decide what to actually delete. If this returns an
987
- // empty array or null, just do nothing.
988
- sel = this.invokeDelegateMethod(this.delegate, 'collectionViewShouldDeleteContent', this, sel) ;
989
- sel = SC.makeArray(sel) ; // ensure this is an array
990
- if (!sel || sel.get('length') === 0) return YES ;
991
-
992
- // now have the delegate (or us) perform the deletion. The collection
993
- // view implements a default version of this method.
994
- this.invokeDelegateMethod(this.delegate, 'collectionViewDeleteContent', this, sel) ;
995
- return YES ;
1573
+ scrollToContentIndex: function(contentIndex) {
1574
+ var itemView = this.itemViewForContentIndex(contentIndex) ;
1575
+ if (itemView) this.scrollToItemView(itemView) ;
1576
+ return this;
996
1577
  },
997
-
1578
+
998
1579
  /**
999
- Default implementation of the delegate method.
1000
-
1001
- This method will delete the passed items from the content array using
1002
- standard array methods. This is often suitable if you are using an
1003
- array controller or a real array for your content.
1004
-
1005
- @param view {SC.CollectionView} this
1006
- @param sel {Array} the items to delete
1007
- @returns {Boolean} YES if the deletion was a success.
1008
- */
1009
- collectionViewDeleteContent: function(view, sel) {
1010
-
1011
- // get the content. Bail if this cannot be used as an array.
1012
- var content = this.get('content') ;
1013
- if (!content) return NO; // nothing to do
1014
-
1015
- // determine the method to use
1016
- var hasDestroyObject = SC.$type(content.destroyObject) === SC.T_FUNCTION ;
1017
- var hasRemoveObject = SC.$type(content.removeObject) === SC.T_FUNCTION ;
1018
- if (!hasDestroyObject && !hasRemoveObject) return NO; // nothing to do
1019
-
1020
- // suspend property notifications and remove the objects...
1021
- if (content.beginPropertyChanges) content.beginPropertyChanges();
1022
- var idx = sel.get('length') ;
1023
- while(--idx >= 0) {
1024
- var item = sel.objectAt(idx) ;
1025
- if (hasDestroyObject) {
1026
- content.destroyObject(item);
1027
- } else {
1028
- content.removeObject(item);
1029
- }
1580
+ Scroll to the passed item view. If the item view is not visible on screen
1581
+ this method will not work.
1582
+
1583
+ @param {SC.View} view The item view to scroll to
1584
+ @returns {SC.CollectionView} receiver
1585
+ */
1586
+ scrollToItemView: function(view) {
1587
+ if (!view.get('parentView')) return this; // nothing to do
1588
+ if (!view.get('layer')) {
1589
+ if (this.get('layer')) view.updateLayerLocation();
1590
+ else return this; // nothing to do
1030
1591
  }
1031
- // begin notifying again...
1032
- if (content.endPropertyChanges) content.endPropertyChanges() ;
1033
1592
 
1034
- return YES ; // done!
1593
+ var scrollable = this;
1594
+ while (scrollable && !scrollable.isPane) {
1595
+ if (scrollable.get('isScrollable')) scrollable.scrollToVisible(view);
1596
+ scrollable = scrollable.get('parentView');
1597
+ }
1598
+ return this ;
1035
1599
  },
1036
-
1037
- // ......................................
1038
- // EVENT HANDLING
1039
- //
1600
+
1601
+ // ..........................................................
1602
+ // KEYBOARD EVENTS
1603
+ //
1040
1604
 
1041
1605
  /** @private */
1042
1606
  keyDown: function(evt) {
1043
- // console.log('keyDown called on %@'.fmt(this));
1044
- return this.interpretKeyEvents(evt) ;
1607
+ var ret = this.interpretKeyEvents(evt) ;
1608
+ return !ret ? NO : ret ;
1045
1609
  },
1046
1610
 
1047
1611
  /** @private */
@@ -1051,8 +1615,9 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1051
1615
  Handle select all keyboard event.
1052
1616
  */
1053
1617
  selectAll: function(evt) {
1054
- var content = (this.get('content') || []).slice() ;
1055
- this.selectItems(content, NO) ;
1618
+ var content = this.get('content'),
1619
+ sel = content ? SC.IndexSet.create(0, content.get('length')) : null;
1620
+ this.select(sel, NO) ;
1056
1621
  return YES ;
1057
1622
  },
1058
1623
 
@@ -1060,7 +1625,6 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1060
1625
  Handle delete keyboard event.
1061
1626
  */
1062
1627
  deleteBackward: function(evt) {
1063
- // console.log('deleteBackward called on %@ with evt %@'.fmt(this, evt));
1064
1628
  return this.deleteSelection() ;
1065
1629
  },
1066
1630
 
@@ -1068,7 +1632,6 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1068
1632
  Handle delete keyboard event.
1069
1633
  */
1070
1634
  deleteForward: function(evt) {
1071
- // console.log('deleteForward called on %@ with evt %@'.fmt(this, evt));
1072
1635
  return this.deleteSelection() ;
1073
1636
  },
1074
1637
 
@@ -1077,6 +1640,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1077
1640
  */
1078
1641
  moveDown: function(sender, evt) {
1079
1642
  this.selectNextItem(false, this.get('itemsPerRow') || 1) ;
1643
+ this._cv_performSelectAction(null, evt, this.ACTION_DELAY);
1080
1644
  return true ;
1081
1645
  },
1082
1646
 
@@ -1085,14 +1649,72 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1085
1649
  */
1086
1650
  moveUp: function(sender, evt) {
1087
1651
  this.selectPreviousItem(false, this.get('itemsPerRow') || 1) ;
1652
+ this._cv_performSelectAction(null, evt, this.ACTION_DELAY);
1088
1653
  return true ;
1089
1654
  },
1090
1655
 
1091
1656
  /** @private
1092
1657
  Selects the previous item if itemsPerRow > 1. Otherwise does nothing.
1658
+ If item is expandable, will collapse.
1093
1659
  */
1094
1660
  moveLeft: function(sender, evt) {
1095
- if ((this.get('itemsPerRow') || 1) > 1) this.selectPreviousItem(false, 1) ;
1661
+ if ((this.get('itemsPerRow') || 1) > 1) {
1662
+ this.selectPreviousItem(false, 1);
1663
+ this._cv_performSelectAction(null, evt, this.ACTION_DELAY);
1664
+
1665
+ } else {
1666
+ var sel = this.get('selection'),
1667
+ content = this.get('content'),
1668
+ indexes = sel ? sel.indexSetForSource(content) : null;
1669
+
1670
+ // Collapse the element if it is expanded. However, if there is exactly
1671
+ // one item selected and the item is already collapsed or is a leaf
1672
+ // node, then select the (expanded) parent element instead as a
1673
+ // convenience to the user.
1674
+ if ( indexes ) {
1675
+ var del = undefined, // We'll load it lazily
1676
+ selectParent = false,
1677
+ index = undefined;
1678
+
1679
+ if ( indexes.get('length') === 1 ) {
1680
+ index = indexes.get('firstObject');
1681
+ del = this.get('contentDelegate');
1682
+ var state = del.contentIndexDisclosureState(this, content, index);
1683
+ if (state !== SC.BRANCH_OPEN) selectParent = true;
1684
+ }
1685
+
1686
+ if ( selectParent ) {
1687
+ // TODO: PERFORMANCE: It would be great to have a function like
1688
+ // SC.CollectionView.selectParentItem() or something similar
1689
+ // for performance reasons. But since we don't currently
1690
+ // have such a function, let's just iterate through the
1691
+ // previous items until we find the first one with a outline
1692
+ // level of one less than the selected item.
1693
+ var desiredOutlineLevel = del.contentIndexOutlineLevel(this, content, index) - 1;
1694
+ if ( desiredOutlineLevel >= 0 ) {
1695
+ var parentIndex = -1;
1696
+ while ( parentIndex < 0 ) {
1697
+ var previousItemIndex = this._findPreviousSelectableItemFromIndex(index - 1);
1698
+ if (previousItemIndex < 0 ) return false; // Sanity-check.
1699
+ index = previousItemIndex;
1700
+ var outlineLevel = del.contentIndexOutlineLevel(this, content, index);
1701
+ if ( outlineLevel === desiredOutlineLevel ) {
1702
+ parentIndex = previousItemIndex;
1703
+ }
1704
+ }
1705
+
1706
+ // If we found the parent, select it now.
1707
+ if ( parentIndex !== -1 ) {
1708
+ this.select(index);
1709
+ }
1710
+ }
1711
+ }
1712
+ else {
1713
+ this.collapse(indexes);
1714
+ }
1715
+ }
1716
+ }
1717
+
1096
1718
  return true ;
1097
1719
  },
1098
1720
 
@@ -1100,19 +1722,30 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1100
1722
  Selects the next item if itemsPerRow > 1. Otherwise does nothing.
1101
1723
  */
1102
1724
  moveRight: function(sender, evt) {
1103
- if ((this.get('itemsPerRow') || 1) > 1) this.selectNextItem(false, 1) ;
1725
+ if ((this.get('itemsPerRow') || 1) > 1) {
1726
+ this.selectNextItem(false, 1) ;
1727
+ this._cv_performSelectAction(null, evt, this.ACTION_DELAY);
1728
+ } else {
1729
+ var sel = this.get('selection'),
1730
+ content = this.get('content'),
1731
+ indexes = sel ? sel.indexSetForSource(content) : null;
1732
+ if (indexes) this.expand(indexes);
1733
+ }
1734
+
1104
1735
  return true ;
1105
1736
  },
1106
1737
 
1107
1738
  /** @private */
1108
1739
  moveDownAndModifySelection: function(sender, evt) {
1109
1740
  this.selectNextItem(true, this.get('itemsPerRow') || 1) ;
1741
+ this._cv_performSelectAction(null, evt, this.ACTION_DELAY);
1110
1742
  return true ;
1111
1743
  },
1112
1744
 
1113
1745
  /** @private */
1114
1746
  moveUpAndModifySelection: function(sender, evt) {
1115
1747
  this.selectPreviousItem(true, this.get('itemsPerRow') || 1) ;
1748
+ this._cv_performSelectAction(null, evt, this.ACTION_DELAY);
1116
1749
  return true ;
1117
1750
  },
1118
1751
 
@@ -1120,7 +1753,10 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1120
1753
  Selects the previous item if itemsPerRow > 1. Otherwise does nothing.
1121
1754
  */
1122
1755
  moveLeftAndModifySelection: function(sender, evt) {
1123
- if ((this.get('itemsPerRow') || 1) > 1) this.selectPreviousItem(true, 1) ;
1756
+ if ((this.get('itemsPerRow') || 1) > 1) {
1757
+ this.selectPreviousItem(true, 1) ;
1758
+ this._cv_performSelectAction(null, evt, this.ACTION_DELAY);
1759
+ }
1124
1760
  return true ;
1125
1761
  },
1126
1762
 
@@ -1128,10 +1764,58 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1128
1764
  Selects the next item if itemsPerRow > 1. Otherwise does nothing.
1129
1765
  */
1130
1766
  moveRightAndModifySelection: function(sender, evt) {
1131
- if ((this.get('itemsPerRow') || 1) > 1) this.selectNextItem(true, 1) ;
1767
+ if ((this.get('itemsPerRow') || 1) > 1) {
1768
+ this.selectNextItem(true, 1) ;
1769
+ this._cv_performSelectAction(null, evt, this.ACTION_DELAY);
1770
+ }
1132
1771
  return true ;
1133
1772
  },
1134
1773
 
1774
+
1775
+
1776
+ /** @private
1777
+ if content value is editable and we have one item selected, then edit.
1778
+ otherwise, invoke action.
1779
+ */
1780
+ insertNewline: function(sender, evt) {
1781
+ var canEdit = this.get('isEditable') && this.get('canEditContent'),
1782
+ sel, content, set, idx, itemView;
1783
+
1784
+ // first make sure we have a single item selected; get idx
1785
+ if (canEdit) {
1786
+ sel = this.get('selection') ;
1787
+ content = this.get('content');
1788
+ if (sel && sel.get('length') === 1) {
1789
+ set = sel.indexSetForSource(content);
1790
+ idx = set ? set.get('min') : -1;
1791
+ canEdit = idx>=0;
1792
+ }
1793
+ }
1794
+
1795
+ // next find itemView and ensure it supports editing
1796
+ if (canEdit) {
1797
+ itemView = this.itemViewForContentIndex(idx);
1798
+ canEdit = itemView && SC.typeOf(itemView.beginEditing)===SC.T_FUNCTION;
1799
+ }
1800
+
1801
+ // ok, we can edit..
1802
+ if (canEdit) {
1803
+ this.scrollToContentIndex(idx);
1804
+ itemView = this.itemViewForContentIndex(idx); // just in case
1805
+ itemView.beginEditing();
1806
+
1807
+ // invoke action
1808
+ } else {
1809
+ this.invokeLater(this._cv_action, 0, itemView, null) ;
1810
+ }
1811
+
1812
+ return YES ; // always handle
1813
+ },
1814
+
1815
+ // ..........................................................
1816
+ // MOUSE EVENTS
1817
+ //
1818
+
1135
1819
  /** @private
1136
1820
  Handles mouse down events on the collection view or on any of its
1137
1821
  children.
@@ -1144,324 +1828,231 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1144
1828
  @returns {Boolean} Usually YES.
1145
1829
  */
1146
1830
  mouseDown: function(ev) {
1147
- // console.log('%@.mouseDown(%@)'.fmt(this, ev));
1148
- // console.log(ev.originalEvent);
1149
1831
 
1150
1832
  // When the user presses the mouse down, we don't do much just yet.
1151
1833
  // Instead, we just need to save a bunch of state about the mouse down
1152
1834
  // so we can choose the right thing to do later.
1153
1835
 
1154
- // save the original mouse down event for use in dragging.
1155
- this._mouseDownEvent = ev ;
1156
-
1157
1836
  // Toggle selection only triggers on mouse up. Do nothing.
1158
- if (this.useToggleSelection) return true;
1837
+ if (this.get('useToggleSelection')) return true;
1159
1838
 
1160
- // Make sure that saved mouseDown state is always reset in case we do
1161
- // not get a paired mouseUp. (Only happens if subclass does not call us
1162
- // like it should)
1163
- this._mouseDownAt = this._shouldSelect = this._shouldDeselect =
1164
- this._shouldReselect = this._refreshSelection = false;
1165
-
1166
- // debugger ;
1167
1839
  // find the actual view the mouse was pressed down on. This will call
1168
1840
  // hitTest() on item views so they can implement non-square detection
1169
1841
  // modes. -- once we have an item view, get its content object as well.
1170
- var mouseDownView = (this._mouseDownView = this.itemViewForEvent(ev));
1171
- var mouseDownContent =
1172
- (this._mouseDownContent = mouseDownView ? mouseDownView.get('content') : null);
1842
+ var itemView = this.itemViewForEvent(ev),
1843
+ content = this.get('content'),
1844
+ contentIndex = itemView ? itemView.get('contentIndex') : -1,
1845
+ info, anchor ;
1846
+
1847
+ info = this.mouseDownInfo = {
1848
+ event: ev,
1849
+ itemView: itemView,
1850
+ contentIndex: contentIndex,
1851
+ at: Date.now()
1852
+ };
1173
1853
 
1174
1854
  // become first responder if possible.
1175
1855
  this.becomeFirstResponder() ;
1176
1856
 
1177
- // console.log(mouseDownView);
1178
-
1179
1857
  // recieved a mouseDown on the collection element, but not on one of the
1180
1858
  // childItems... unless we do not allow empty selections, set it to empty.
1181
- if (!mouseDownView) {
1182
- if (this.get('allowDeselectAll')) this.selectItems([], false);
1183
- return true ;
1859
+ if (!itemView) {
1860
+ if (this.get('allowDeselectAll')) this.select(null, false);
1861
+ return YES ;
1184
1862
  }
1185
1863
 
1186
1864
  // collection some basic setup info
1187
- var selection = this.get('selection') || [] ;
1188
- var isSelected = (selection.indexOf(mouseDownContent) !== -1) ;
1189
- var modifierKeyPressed = ev.ctrlKey || ev.metaKey ;
1190
- if (mouseDownView.checkboxView && (SC.Event.element(ev) == ev.checkboxView.rootElement)) {
1191
- modifierKeyPressed = true ;
1192
- }
1193
- this._modifierKeyPressed = modifierKeyPressed ;
1865
+ var sel = this.get('selection'), isSelected, modifierKeyPressed;
1866
+ if (sel) sel = sel.indexSetForSource(content);
1194
1867
 
1195
- this._mouseDownAt = Date.now();
1868
+ isSelected = sel ? sel.contains(contentIndex) : NO;
1869
+ info.modifierKeyPressed = modifierKeyPressed = ev.ctrlKey || ev.metaKey ;
1196
1870
 
1197
1871
  // holding down a modifier key while clicking a selected item should
1198
1872
  // deselect that item...deselect and bail.
1199
1873
  if (modifierKeyPressed && isSelected) {
1200
- this._shouldDeselect = mouseDownContent;
1874
+ info.shouldDeselect = contentIndex >= 0;
1875
+
1201
1876
  // if the shiftKey was pressed, then we want to extend the selection
1202
1877
  // from the last selected item
1203
- } else if (ev.shiftKey && selection.get('length') > 0) {
1204
- selection = this._findSelectionExtendedByShift(selection, mouseDownContent) ;
1205
- this.selectItems(selection) ;
1878
+ } else if (ev.shiftKey && sel && sel.get('length') > 0) {
1879
+ sel = this._findSelectionExtendedByShift(sel, contentIndex);
1880
+ anchor = this._selectionAnchor ;
1881
+ this.select(sel) ;
1882
+ this._selectionAnchor = anchor; //save the anchor
1206
1883
 
1207
1884
  // If no modifier key was pressed, then clicking on the selected item
1208
1885
  // should clear the selection and reselect only the clicked on item.
1209
1886
  } else if (!modifierKeyPressed && isSelected) {
1210
- this._shouldReselect = mouseDownContent;
1887
+ info.shouldReselect = contentIndex >= 0;
1211
1888
 
1212
1889
  // Otherwise, if selecting on mouse down, simply select the clicked on
1213
1890
  // item, adding it to the current selection if a modifier key was pressed.
1214
1891
  } else {
1215
- if (this.get("selectOnMouseDown")){
1216
- this.selectItems(mouseDownContent, modifierKeyPressed);
1217
- } else this._shouldSelect = mouseDownContent ;
1892
+ if (this.get("selectOnMouseDown")) {
1893
+ this.select(contentIndex, modifierKeyPressed);
1894
+ } else {
1895
+ info.shouldSelect = contentIndex >= 0 ;
1896
+ }
1218
1897
  }
1219
1898
 
1220
1899
  // saved for extend by shift ops.
1221
- this._previousMouseDownContent = mouseDownContent;
1900
+ info.previousContentIndex = contentIndex;
1222
1901
 
1223
- return true;
1902
+ return YES;
1224
1903
  },
1225
1904
 
1226
1905
  /** @private */
1227
1906
  mouseUp: function(ev) {
1228
1907
 
1229
- var canAct = this.get('actOnSelect') ;
1230
- var view = this.itemViewForEvent(ev) ;
1231
- var content, selection;
1908
+ var view = this.itemViewForEvent(ev),
1909
+ info = this.mouseDownInfo,
1910
+ idx = info.contentIndex,
1911
+ contentIndex, sel, isSelected, canEdit, itemView, content;
1232
1912
 
1233
- if (this.useToggleSelection) {
1913
+ if (this.get('useToggleSelection')) {
1234
1914
  if (!view) return ; // do nothing when clicked outside of elements
1235
1915
 
1236
1916
  // determine if item is selected. If so, then go on.
1237
- selection = this.get('selection') || [] ;
1238
- content = (view) ? view.get('content') : null ;
1239
- var isSelected = selection.include(content) ;
1240
- if (isSelected) {
1241
- this.deselectItems([content]) ;
1242
- } else this.selectItems([content],true) ;
1917
+ sel = this.get('selection') ;
1918
+ contentIndex = (view) ? view.get('contentIndex') : -1 ;
1919
+ isSelected = sel && sel.include(contentIndex) ;
1920
+
1921
+ if (isSelected) this.deselect(contentIndex) ;
1922
+ else this.select(contentIndex, YES) ;
1243
1923
 
1244
1924
  } else {
1245
- content = (view) ? view.get('content') : null ;
1925
+ contentIndex = (view) ? view.get('contentIndex') : -1 ;
1246
1926
 
1247
1927
  // this will be set if the user simply clicked on an unselected item and
1248
1928
  // selectOnMouseDown was NO.
1249
- if (this._shouldSelect) this.selectItems(this._shouldSelect, this._modifierKeyPressed);
1929
+ if (info.shouldSelect) this.select(idx, info.modifierKeyPressed);
1250
1930
 
1251
1931
  // This is true if the user clicked on a selected item with a modifier
1252
1932
  // key pressed.
1253
- if (this._shouldDeselect) this.deselectItems(this._shouldDeselect);
1933
+ if (info.shouldDeselect) this.deselect(idx);
1254
1934
 
1255
1935
  // This is true if the user clicked on a selected item without a
1256
1936
  // modifier-key pressed. When this happens we try to begin editing
1257
1937
  // on the content. If that is not allowed, then simply clear the
1258
1938
  // selection and reselect the clicked on item.
1259
- if (this._shouldReselect) {
1939
+ if (info.shouldReselect) {
1260
1940
 
1941
+ //debugger ;
1261
1942
  // - contentValueIsEditable is true
1262
- var canEdit = this.get('contentValueIsEditable') ;
1943
+ canEdit = this.get('isEditable') && this.get('canEditContent') ;
1263
1944
 
1264
1945
  // - the user clicked on an item that was already selected
1946
+ // ^ this is the only way shouldReset is set to YES
1947
+
1265
1948
  // - is the only item selected
1266
1949
  if (canEdit) {
1267
- var sel = this.get('selection') ;
1268
- canEdit = sel && (sel.get('length') === 1) && (sel.objectAt(0) === this._shouldReselect) ;
1950
+ sel = this.get('selection') ;
1951
+ canEdit = sel && (sel.get('length') === 1);
1269
1952
  }
1270
1953
 
1271
1954
  // - the item view responds to contentHitTest() and returns YES.
1272
1955
  // - the item view responds to beginEditing and returns YES.
1273
1956
  if (canEdit) {
1274
- var itemView = this.itemViewForContent(this._shouldReselect) ;
1957
+ itemView = this.itemViewForContentIndex(idx) ;
1275
1958
  canEdit = itemView && (!itemView.contentHitTest || itemView.contentHitTest(ev)) ;
1276
1959
  canEdit = (canEdit && itemView.beginEditing) ? itemView.beginEditing() : NO ;
1277
1960
  }
1278
1961
 
1279
1962
  // if cannot edit, just reselect
1280
- if (!canEdit) this.selectItems(this._shouldReselect,false) ;
1963
+ if (!canEdit) this.select(idx, false) ;
1281
1964
  }
1282
1965
 
1283
1966
  this._cleanupMouseDown() ;
1284
1967
  }
1968
+
1969
+ // handle actions on editing
1970
+ this._cv_performSelectAction(view, ev, 0, ev.clickCount);
1285
1971
 
1286
- this._mouseDownEvent = null ;
1287
- if (canAct) this._action(ev, view) ;
1288
-
1289
- return false; // bubble event to allow didDoubleClick to be called...
1972
+ return NO; // bubble event to allow didDoubleClick to be called...
1290
1973
  },
1291
1974
 
1292
1975
  /** @private */
1293
1976
  _cleanupMouseDown: function() {
1294
- this._mouseDownAt = this._shouldDeselect = this._shouldReselect = this._refreshSelection = this._shouldSelect = false;
1295
- this._mouseDownEvent = this._mouseDownContent = this._mouseDownView = null ;
1977
+
1978
+ // delete items explicitly to avoid leaks on IE
1979
+ var info = this.mouseDownInfo, key;
1980
+ if (info) {
1981
+ for(key in info) {
1982
+ if (!info.hasOwnProperty(key)) continue;
1983
+ delete info[key];
1984
+ }
1985
+ }
1986
+ this.mouseDownInfo = null;
1296
1987
  },
1297
1988
 
1298
1989
  /** @private */
1299
1990
  mouseMoved: function(ev) {
1300
- var view = this.itemViewForEvent(ev) ;
1991
+ var view = this.itemViewForEvent(ev),
1992
+ last = this._lastHoveredItem ;
1993
+
1301
1994
  // handle hover events.
1302
- if(this._lastHoveredItem && ((view === null) || (view != this._lastHoveredItem)) && this._lastHoveredItem.mouseOut) {
1303
- this._lastHoveredItem.mouseOut(ev);
1995
+ if (view !== last) {
1996
+ if (last && last.mouseOut) last.mouseOut(ev);
1997
+ if (view && view.mouseOver) view.mouseOver(ev);
1304
1998
  }
1305
1999
  this._lastHoveredItem = view ;
1306
- if (view && view.mouseOver) view.mouseOver(ev) ;
2000
+
2001
+ if (view && view.mouseMoved) view.mouseMoved(ev);
2002
+ return YES;
1307
2003
  },
1308
2004
 
1309
2005
  /** @private */
1310
2006
  mouseOut: function(ev) {
1311
-
1312
2007
  var view = this._lastHoveredItem ;
1313
2008
  this._lastHoveredItem = null ;
1314
- if (view && view.didMouseOut) view.didMouseOut(ev) ;
1315
- },
1316
-
1317
- /** @private */
1318
- doubleClick: function(ev) {
1319
- var view = this.itemViewForEvent(ev) ;
1320
- if (view) {
1321
- this._action(view, ev) ;
1322
- return true ;
1323
- } else return false ;
2009
+ if (view && view.mouseOut) view.mouseOut(ev) ;
2010
+ return YES ;
1324
2011
  },
1325
2012
 
1326
2013
  /** @private */
1327
- _findSelectionExtendedByShift: function(selection, mouseDownContent) {
1328
- var content = this.get('content');
2014
+ _findSelectionExtendedByShift: function(sel, contentIndex) {
1329
2015
 
1330
- // bounds of the collection...
1331
- var contentLowerBounds = 0;
1332
- var contentUpperBounds = (content.get('length') - 1);
1333
-
1334
- var selectionBeginIndex = content.indexOf(selection[0]) ;
1335
- var selectionEndIndex = content.indexOf(selection[selection.length-1]) ;
1336
-
1337
- var previousMouseDownIndex = content.indexOf(this._previousMouseDownContent);
1338
- // _previousMouseDownContent couldn't be found... either it hasn't been set yet or the record has been deleted by the user
1339
- // fall back to the first selected item.
1340
- if (previousMouseDownIndex == -1) previousMouseDownIndex = selectionBeginIndex;
1341
-
1342
- var currentMouseDownIndex = content.indexOf(mouseDownContent);
1343
- // sanity check...
1344
- if (currentMouseDownIndex == -1) throw "Unable to extend selection to an item that's not in the content array!";
2016
+ // fast path. if we don't have a selection, just select index
2017
+ if (!sel || sel.get('length')===0) {
2018
+ return SC.IndexSet.create(contentIndex);
2019
+ }
1345
2020
 
2021
+ // if we do have a selection, then figure out how to extend it.
2022
+ var content = this.get('content'),
2023
+ lim = content.get('length')-1,
2024
+ min = sel.get('min'),
2025
+ max = sel.get('max')-1,
2026
+ info = this.mouseDownInfo,
2027
+ anchor = this._selectionAnchor ;
2028
+ if (SC.none(anchor)) anchor = -1;
2029
+
1346
2030
  // clicked before the current selection set... extend it's beginning...
1347
- if (currentMouseDownIndex < selectionBeginIndex) {
1348
- selectionBeginIndex = currentMouseDownIndex;
1349
- }
2031
+ if (contentIndex < min) {
2032
+ min = contentIndex;
2033
+ if (anchor<0) this._selectionAnchor = anchor = max; //anchor at end
1350
2034
 
1351
2035
  // clicked after the current selection set... extend it's ending...
1352
- if (currentMouseDownIndex > selectionEndIndex) {
1353
- selectionEndIndex = currentMouseDownIndex;
1354
- }
2036
+ } else if (contentIndex > max) {
2037
+ max = contentIndex;
2038
+ if (anchor<0) this._selectionAnchor = anchor = min; // anchor at start
1355
2039
 
1356
2040
  // clicked inside the selection set... need to determine where the last
1357
2041
  // selection was and use that as an anchor.
1358
- if ((currentMouseDownIndex > selectionBeginIndex) && (currentMouseDownIndex < selectionEndIndex)) {
1359
- if (currentMouseDownIndex === previousMouseDownIndex) {
1360
- selectionBeginIndex = currentMouseDownIndex;
1361
- selectionEndIndex = currentMouseDownIndex;
1362
- } else if (currentMouseDownIndex > previousMouseDownIndex) {
1363
- selectionBeginIndex = previousMouseDownIndex;
1364
- selectionEndIndex = currentMouseDownIndex;
1365
- } else if (currentMouseDownIndex < previousMouseDownIndex){
1366
- selectionBeginIndex = currentMouseDownIndex;
1367
- selectionEndIndex = previousMouseDownIndex;
1368
- }
1369
- }
1370
-
1371
- // slice doesn't include the last index passed... silly..
1372
- selectionEndIndex++;
1373
-
1374
- // shouldn't need to sanity check that the selection is in bounds due to
1375
- // the indexOf checks above...I'll have faith that indexOf hasn't lied to
1376
- // me...
1377
- return content.slice(selectionBeginIndex, selectionEndIndex);
1378
- },
1379
-
1380
- /** @private
1381
- Finds the next selectable item, up to content length, by asking the
1382
- delegate. If a non-selectable item is found, the index is skipped. If
1383
- no item is found, selection index is returned unmodified.
1384
-
1385
- @param {Integer} proposedIndex the desired index to select
1386
- @returns {Integer} the next selectable index. This will always be in the range of the bottom of the current selection index and the proposed index.
1387
- @private
1388
- */
1389
- _findNextSelectableItemFromIndex: function(proposedIndex) {
1390
- var content = this.get('content');
1391
- var contentLength = content.get('length');
1392
- var bottom = this._indexOfSelectionTop();
1393
-
1394
- while (proposedIndex < contentLength &&
1395
- this.invokeDelegateMethod(this.delegate, 'collectionViewShouldSelectItem', this, content.objectAt(proposedIndex)) === NO) {
1396
- proposedIndex++;
1397
- }
1398
- return (proposedIndex < contentLength) ? proposedIndex : bottom;
1399
- },
1400
-
1401
- /** @private
1402
- Finds the previous selectable item, up to the first item, by asking the
1403
- delegate. If a non-selectable item is found, the index is skipped. If
1404
- no item is found, selection index is returned unmodified.
1405
-
1406
- @param {Integer} proposedIndex the desired index to select
1407
- @returns {Integer} the previous selectable index. This will always be in the range of the top of the current selection index and the proposed index.
1408
- @private
1409
- */
1410
- _findPreviousSelectableItemFromIndex: function(proposedIndex) {
1411
- var content = this.get('content');
1412
- var contentLength = content.get('length');
1413
- var top = this._indexOfSelectionTop();
1414
-
1415
- while (proposedIndex >= 0 &&
1416
- this.invokeDelegateMethod(this.delegate, 'collectionViewShouldSelectItem', this, content.objectAt(proposedIndex)) === NO) {
1417
- proposedIndex--;
1418
- }
1419
- return (proposedIndex >= 0) ? proposedIndex : top ;
1420
- },
1421
-
1422
- /** @private
1423
- if content value is editable and we have one item selected, then edit.
1424
- otherwise, invoke action.
1425
- */
1426
- insertNewline: function() {
1427
- var sel, itemView;
1428
- if (this.get('contentValueIsEditable')) {
1429
- sel = this.get('selection') ;
1430
- if (sel && sel.get('length') === 1) {
1431
- itemView = this.itemViewForContent(sel.objectAt(0)) ;
1432
- if (itemView && itemView.beginEditing) {
1433
- this.scrollToItemView(itemView) ;
1434
- itemView.beginEditing() ;
1435
- }
1436
- }
2042
+ } else if (contentIndex >= min && contentIndex <= max) {
2043
+ if (anchor<0) this._selectionAnchor = anchor = min; //anchor at start
1437
2044
 
1438
- // invoke action!
1439
- } else {
1440
- sel = this.get('selection') ;
1441
- itemView = (sel && sel.get('length') === 1) ? this.itemViewForContent(sel.objectAt(0)) : null ;
1442
- this._action(itemView, null) ;
2045
+ if (contentIndex === anchor) min = max = contentIndex ;
2046
+ else if (contentIndex > anchor) {
2047
+ min = anchor;
2048
+ max = contentIndex ;
2049
+ } else if (contentIndex < anchor) {
2050
+ min = contentIndex;
2051
+ max = anchor ;
2052
+ }
1443
2053
  }
1444
-
1445
- return YES ; // always handle
1446
- },
1447
-
1448
- // ......................................
1449
- // FIRST RESPONDER
1450
- //
1451
-
1452
- /** @private
1453
- Called whenever the collection becomes first responder.
1454
- Adds the focused class to the element.
1455
- */
1456
- didBecomeFirstResponder: function() {
1457
- // console.log('didBecomeFirstResponder called on %@'.fmt(this));
1458
- this.$().addClass('focus') ;
1459
- },
1460
-
1461
- /** @private */
1462
- willLoseFirstResponder: function() {
1463
- // console.log('willLoseFirstResponder called on %@'.fmt(this));
1464
- this.$().removeClass('focus');
2054
+
2055
+ return SC.IndexSet.create(min, max - min + 1);
1465
2056
  },
1466
2057
 
1467
2058
  // ......................................
@@ -1482,22 +2073,21 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1482
2073
  }.property().cacheable(),
1483
2074
 
1484
2075
  /**
1485
- This property is set to the array of content objects that are the subject
1486
- of a drag whenever a drag is initiated on the collection view. You can
1487
- consult this property when implementing your collection view delegate
1488
- methods, but otherwise you should not use this property in your code.
1489
-
1490
- Note that drag content will always appear in the same order the content
1491
- appears in the source content array.
1492
-
1493
- @type Array
2076
+ This property is set to the IndexSet of content objects that are the
2077
+ subject of a drag whenever a drag is initiated on the collection view.
2078
+ You can consult this property when implementing your collection view
2079
+ delegate methods, but otherwise you should not use this property in your
2080
+ code.
2081
+
2082
+ @type SC.IndexSet
1494
2083
  */
1495
2084
  dragContent: null,
1496
2085
 
1497
2086
  /**
1498
2087
  This property is set to the proposed insertion index during a call to
1499
- collectionViewValidateDragOperation(). Your delegate implementations can change
1500
- the value of this property to enforce a drop some in some other location.
2088
+ collectionViewValidateDragOperation(). Your delegate implementations can
2089
+ change the value of this property to enforce a drop some in some other
2090
+ location.
1501
2091
 
1502
2092
  @type Number
1503
2093
  */
@@ -1505,11 +2095,12 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1505
2095
 
1506
2096
  /**
1507
2097
  This property is set to the proposed drop operation during a call to
1508
- collectionViewValidateDragOperation(). Your delegate implementations can change
1509
- the value of this property to enforce a different type of drop operation.
2098
+ collectionViewValidateDragOperation(). Your delegate implementations can
2099
+ change the value of this property to enforce a different type of drop
2100
+ operation.
1510
2101
 
1511
2102
  @type Number
1512
- @field
2103
+ @property
1513
2104
  */
1514
2105
  proposedDropOperation: null,
1515
2106
 
@@ -1523,56 +2114,58 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1523
2114
  - a mouse down event was saved by the mouseDown method.
1524
2115
  */
1525
2116
  mouseDragged: function(ev) {
2117
+
2118
+ var del = this.get('selectionDelegate'),
2119
+ content = this.get('content'),
2120
+ sel = this.get('selection'),
2121
+ info = this.mouseDownInfo,
2122
+ dragContent, dragDataTypes, dragView;
2123
+
1526
2124
  // if the mouse down event was cleared, there is nothing to do; return.
1527
- if (this._mouseDownEvent === null) return YES ;
2125
+ if (!info || info.contentIndex<0) return YES ;
1528
2126
 
1529
2127
  // Don't do anything unless the user has been dragging for 123msec
1530
- if ((Date.now() - this._mouseDownAt) < 123) return YES ;
2128
+ if ((Date.now() - info.at) < 123) return YES ;
1531
2129
 
1532
2130
  // OK, they must be serious, decide if a drag will be allowed.
1533
- if (this.invokeDelegateMethod(this.delegate, 'collectionViewShouldBeginDrag', this)) {
2131
+ if (del.collectionViewShouldBeginDrag(this)) {
1534
2132
 
1535
2133
  // First, get the selection to drag. Drag an array of selected
1536
2134
  // items appearing in this collection, in the order of the
1537
2135
  // collection.
1538
2136
  //
1539
- // Set this to the dragContent property.
1540
- var content = this.get('content') || [] ;
1541
- var dragContent;
1542
-
2137
+ // Compute the dragContent - the indexes we will be dragging.
1543
2138
  // if we don't select on mouse down, then the selection has not been
1544
2139
  // updated to whatever the user clicked. Instead use
1545
2140
  // mouse down content.
1546
2141
  if (!this.get("selectOnMouseDown")) {
1547
- dragContent = [this._mouseDownContent];
1548
- } else {
1549
- dragContent = this.get('selection').sort(function(a,b) {
1550
- a = content.indexOf(a) ;
1551
- b = content.indexOf(b) ;
1552
- return (a<b) ? -1 : ((a>b) ? 1 : 0) ;
1553
- });
1554
- }
2142
+ dragContent = SC.IndexSet.create(info.contentIndex);
2143
+ } else dragContent = sel ? sel.indexSetForSource(content) : null;
2144
+ if (!dragContent) return YES; // nothing to drag
1555
2145
 
2146
+ dragContent = { content: content, indexes: dragContent };
1556
2147
  this.set('dragContent', dragContent) ;
1557
2148
 
1558
2149
  // Get the set of data types supported by the delegate. If this returns
1559
2150
  // a null or empty array and reordering content is not also supported
1560
2151
  // then do not start the drag.
1561
- if (this.get('dragDataTypes').get('length') > 0) {
2152
+ dragDataTypes = this.get('dragDataTypes');
2153
+ if (dragDataTypes && dragDataTypes.get('length') > 0) {
2154
+
1562
2155
  // Build the drag view to use for the ghost drag. This
1563
2156
  // should essentially contain any visible drag items.
1564
- // var view = this.ghostViewFor(dragContent) ;
1565
- var view = this.dragViewFor(dragContent) ;
2157
+ dragView = del.collectionViewDragViewFor(this, dragContent.indexes);
2158
+ if (!dragView) dragView = this._cv_dragViewFor(dragContent.indexes);
1566
2159
 
1567
2160
  // Initiate the drag
1568
2161
  SC.Drag.start({
1569
- event: this._mouseDownEvent,
2162
+ event: info.event,
1570
2163
  source: this,
1571
- dragView: view,
2164
+ dragView: dragView,
1572
2165
  ghost: NO,
1573
2166
  slideBack: YES,
1574
2167
  dataSource: this
1575
- }) ;
2168
+ });
1576
2169
 
1577
2170
  // Also use this opportunity to clean up since mouseUp won't
1578
2171
  // get called.
@@ -1580,13 +2173,55 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1580
2173
  this._lastInsertionIndex = null ;
1581
2174
 
1582
2175
  // Drag was not allowed by the delegate, so bail.
1583
- } else {
1584
- this.set('dragContent', null) ;
1585
- }
2176
+ } else this.set('dragContent', null) ;
1586
2177
 
1587
2178
  return YES ;
1588
2179
  }
1589
2180
  },
2181
+
2182
+ /** @private
2183
+ Compute a default drag view by grabbing the raw layers and inserting them
2184
+ into a drag view.
2185
+ */
2186
+ _cv_dragViewFor: function(dragContent) {
2187
+ // find only the indexes that are in both dragContent and nowShowing.
2188
+ var indexes = this.get('nowShowing').without(dragContent);
2189
+ indexes = this.get('nowShowing').without(indexes);
2190
+
2191
+ var dragLayer = this.get('layer').cloneNode(false);
2192
+ var view = SC.View.create({ layer: dragLayer, parentView: this });
2193
+
2194
+ // cleanup weird stuff that might make the drag look out of place
2195
+ SC.$(dragLayer).css('backgroundColor', 'transparent')
2196
+ .css('border', 'none')
2197
+ .css('top', 0).css('left', 0);
2198
+
2199
+ indexes.forEach(function(i) {
2200
+ var itemView = this.itemViewForContentIndex(i),
2201
+ isSelected, layer;
2202
+
2203
+ // render item view without isSelected state.
2204
+ if (itemView) {
2205
+ isSelected = itemView.get('isSelected');
2206
+ itemView.set('isSelected', NO);
2207
+
2208
+ itemView.updateLayerIfNeeded();
2209
+ layer = itemView.get('layer');
2210
+ if (layer) layer = layer.cloneNode(true);
2211
+
2212
+ itemView.set('isSelected', isSelected);
2213
+ itemView.updateLayerIfNeeded();
2214
+ }
2215
+
2216
+ if (layer) dragLayer.appendChild(layer);
2217
+ layer = null;
2218
+
2219
+ }, this);
2220
+
2221
+ dragLayer = null;
2222
+ return view ;
2223
+ },
2224
+
1590
2225
 
1591
2226
  /**
1592
2227
  Implements the drag data source protocol for the collection view. This
@@ -1597,26 +2232,18 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1597
2232
  @type Array
1598
2233
  */
1599
2234
  dragDataTypes: function() {
1600
- // console.log('dragDataTypes called on %@'.fmt(this));
1601
-
1602
2235
  // consult delegate.
1603
- var ret = this.invokeDelegateMethod(this.delegate, 'collectionViewDragDataTypes', this) ;
1604
- var canReorderContent = this.get('canReorderContent') ;
1605
-
1606
- // bail if ret returned null or empty array and cannot reorder.
1607
- if ((!ret || ret.get('length')===0) && !canReorderContent) return [];
1608
-
1609
- // add reorder type if needed.
1610
- if (canReorderContent) {
1611
- ret = (ret) ? ret.slice() : [] ;
1612
-
1613
- var key = this.get('reorderDataType') ;
1614
- if (ret.indexOf(key) < 0) ret.push(key) ;
2236
+ var del = this.get('selectionDelegate'),
2237
+ ret = del.collectionViewDragDataTypes(this),
2238
+ key ;
2239
+
2240
+ if (this.get('canReorderContent')) {
2241
+ ret = ret ? ret.copy() : [];
2242
+ key = this.get('reorderDataType');
2243
+ if (ret.indexOf(key) < 0) ret.push(key);
1615
2244
  }
1616
- return ret ;
1617
-
1618
- //data: { "_mouseDownContent": dragContent }
1619
-
2245
+
2246
+ return ret ? ret : [];
1620
2247
  }.property(),
1621
2248
 
1622
2249
  /**
@@ -1629,39 +2256,44 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1629
2256
  // if this is a reorder, then return drag content.
1630
2257
  if (this.get('canReorderContent')) {
1631
2258
  if (dataType === this.get('reorderDataType')) {
1632
- // console.log('dragContent is %@'.fmt(this.get('dragContent')));
1633
2259
  return this.get('dragContent') ;
1634
2260
  }
1635
2261
  }
1636
2262
 
1637
- // otherwise, just pass along to the delegate.
1638
- return this.invokeDelegateMethod(this.delegate, 'collectionViewDragDataForType', this, drag, dataType) ;
2263
+ // otherwise, just pass along to the delegate
2264
+ var del = this.get('selectionDelegate');
2265
+ return del.collectionViewDragDataForType(this, drag, dataType);
1639
2266
  },
1640
2267
 
1641
2268
  /**
1642
2269
  Implements the SC.DropTarget interface. The default implementation will
1643
2270
  consult the collection view delegate, if you implement those methods.
2271
+
2272
+ This method is called once when the drag enters the view area. It's
2273
+ return value will be stored on the drag object as allowedDragOperations,
2274
+ possibly further constrained by the drag source.
2275
+
2276
+ @param {SC.Drag} drag the drag object
2277
+ @param {SC.Event} evt the event triggering this change, if available
2278
+ @returns {Number} logical OR'd mask of allowed drag operations.
1644
2279
  */
1645
2280
  computeDragOperations: function(drag, evt) {
1646
- // console.log('computeDragOperations called on %@'.fmt(this));
1647
-
2281
+
1648
2282
  // the proposed drag operation is DRAG_REORDER only if we can reorder
1649
2283
  // content and the drag contains reorder content.
1650
- var op = SC.DRAG_NONE ;
2284
+ var op = SC.DRAG_NONE,
2285
+ del = this.get('selectionDelegate');
2286
+
1651
2287
  if (this.get('canReorderContent')) {
1652
- var types = drag.get('dataTypes') ;
1653
- if (types.indexOf(this.get('reorderDataType')) >= 0) {
2288
+ if (drag.get('dataTypes').indexOf(this.get('reorderDataType')) >= 0) {
1654
2289
  op = SC.DRAG_REORDER ;
1655
2290
  }
1656
2291
  }
1657
2292
 
1658
2293
  // Now pass this onto the delegate.
1659
- // op = this.invokeDelegateMethod(this.delegate, 'collectionViewValidateDrop', this, drag, SC.DROP_ANY, -1, op) ;
1660
- op = this.invokeDelegateMethod(this.delegate, 'collectionViewComputeDragOperations', this, drag, op) ;
1661
-
2294
+ op = del.collectionViewComputeDragOperations(this, drag, op);
1662
2295
  if (op & SC.DRAG_REORDER) op = SC.DRAG_MOVE ;
1663
2296
 
1664
- // return
1665
2297
  return op ;
1666
2298
  },
1667
2299
 
@@ -1669,17 +2301,23 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1669
2301
  Determines the allowed drop operation insertion point, operation type,
1670
2302
  and the drag operation to be performed. Used by dragUpdated() and
1671
2303
  performDragOperation().
2304
+
2305
+ @param {SC.Drag} drag the drag object
2306
+ @param {SC.Event} evt source of this request, if available
2307
+ @param {Number} dragOp allowed drag operation mask
2308
+ Returns three params: [drop index, drop operation, allowed drag ops]
1672
2309
  */
1673
- _computeDropOperationState: function(drag, evt) {
2310
+ _computeDropOperationState: function(drag, evt, dragOp) {
1674
2311
 
1675
2312
  // get the insertion index for this location. This can be computed
1676
2313
  // by a subclass using whatever method. This method is not expected to
1677
2314
  // do any data valdidation, just to map the location to an insertion
1678
2315
  // index.
1679
- var loc = drag.get('location') ;
1680
- loc = this.convertFrameFromView(loc, null) ;
1681
- var dropOp = SC.DROP_BEFORE ;
1682
- var dragOp = SC.DRAG_NONE ;
2316
+ var loc = this.convertFrameFromView(drag.get('location'), null),
2317
+ dropOp = SC.DROP_BEFORE,
2318
+ del = this.get('selectionDelegate'),
2319
+ canReorder = this.get('canReorderContent'),
2320
+ objects, content, isPreviousInDrag, isNextInDrag, len;
1683
2321
 
1684
2322
  // STEP 1: Try with a DROP_ON option -- send straight to delegate if
1685
2323
  // supported by view.
@@ -1687,7 +2325,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1687
2325
  // get the computed insertion index and possibly drop operation.
1688
2326
  // prefer to drop ON.
1689
2327
  var idx = this.insertionIndexForLocation(loc, SC.DROP_ON) ;
1690
- if (SC.$type(idx) === SC.T_ARRAY) {
2328
+ if (SC.typeOf(idx) === SC.T_ARRAY) {
1691
2329
  dropOp = idx[1] ; // order matters here
1692
2330
  idx = idx[0] ;
1693
2331
  }
@@ -1696,64 +2334,63 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1696
2334
  // delegate method. If the delegate method does not support dropping on,
1697
2335
  // then it will return DRAG_NONE, in which case we will try again with
1698
2336
  // drop before.
1699
- if (dropOp == SC.DROP_ON) {
1700
- // console.log('dropOp === SC.DROP_ON');
2337
+ if (dropOp === SC.DROP_ON) {
1701
2338
 
1702
2339
  // Now save the insertion index and the dropOp. This may be changed by
1703
2340
  // the collection delegate.
1704
2341
  this.set('proposedInsertionIndex', idx) ;
1705
2342
  this.set('proposedDropOperation', dropOp) ;
1706
- dragOp = this.invokeDelegateMethod(this.delegate, 'collectionViewValidateDragOperation', this, drag, dragOp, idx, dropOp) ;
2343
+ dragOp = del.collectionViewValidateDragOperation(this, drag, dragOp, idx, dropOp) ;
1707
2344
  idx = this.get('proposedInsertionIndex') ;
1708
2345
  dropOp = this.get('proposedDropOperation') ;
1709
2346
  this._dropInsertionIndex = this._dropOperation = null ;
1710
2347
 
1711
2348
  // The delegate is OK with a drop on also, so just return.
1712
- if (dragOp !== SC.DRAG_NONE) {
1713
- // console.log('[idx, dropOp, dragOp] is [%@, %@, %@]'.fmt(idx, dropOp, dragOp));
1714
- return [idx, dropOp, dragOp] ;
2349
+ if (dragOp !== SC.DRAG_NONE) return [idx, dropOp, dragOp] ;
1715
2350
 
1716
2351
  // The delegate is NOT OK with a drop on, try to get the insertion
1717
2352
  // index again, but this time prefer SC.DROP_BEFORE, then let the
1718
2353
  // rest of the method run...
1719
- } else {
2354
+ else {
1720
2355
  dropOp = SC.DROP_BEFORE ;
1721
2356
  idx = this.insertionIndexForLocation(loc, SC.DROP_BEFORE) ;
1722
- if (SC.$type(idx) === SC.T_ARRAY) {
2357
+ if (SC.typeOf(idx) === SC.T_ARRAY) {
1723
2358
  dropOp = idx[1] ; // order matters here
1724
2359
  idx = idx[0] ;
1725
2360
  }
1726
2361
  }
1727
2362
  }
1728
2363
 
1729
- // console.log('this is a redorder drag, dropOp is %@'.fmt(dropOp)) ;
1730
-
1731
2364
  // if this is a reorder drag, set the proposed op to SC.DRAG_REORDER and
1732
2365
  // validate the insertion point. This only works if the insertion point
1733
- // is DROP_BEFORE. DROP_ON is not handled by reordering content.
1734
- if ((idx >= 0) && this.get('canReorderContent') && (dropOp === SC.DROP_BEFORE)) {
2366
+ // is DROP_BEFORE or DROP_AFTER. DROP_ON is not handled by reordering
2367
+ // content.
2368
+ if ((idx >= 0) && canReorder && (dropOp !== SC.DROP_ON)) {
1735
2369
 
1736
- var objects = drag.dataForType(this.get('reorderDataType')) ;
2370
+ objects = drag.dataForType(this.get('reorderDataType')) ;
1737
2371
  if (objects) {
1738
- // console.log('found objects');
1739
- var content = this.get('content') || [] ;
1740
- // console.log('objects is %@, content is %@'.fmt(objects, content));
2372
+ content = this.get('content') ;
1741
2373
 
1742
2374
  // if the insertion index is in between two items in the drag itself,
1743
2375
  // then this is not allowed. Either use the last insertion index or
1744
2376
  // find the first index that is not in between selections. Stop when
1745
2377
  // we get to the beginning.
1746
- var previousContent = (idx > 0) ? content.objectAt(idx-1) : null ;
1747
- var nextContent = (idx < content.get('length')) ? content.objectAt(idx) : null;
1748
-
1749
- var isPreviousInDrag = (previousContent) ? objects.indexOf(previousContent)>=0 : NO;
1750
- var isNextInDrag = (nextContent) ? objects.indexOf(nextContent)>=0 : NO;
2378
+ if (dropOp === SC.DROP_BEFORE) {
2379
+ isPreviousInDrag = objects.indexes.contains(idx-1);
2380
+ isNextInDrag = objects.indexes.contains(idx);
2381
+ } else {
2382
+ isPreviousInDrag = objects.indexes.contains(idx);
2383
+ isNextInDrag = objects.indexes.contains(idx-1);
2384
+ }
1751
2385
 
1752
2386
  if (isPreviousInDrag && isNextInDrag) {
1753
2387
  if (SC.none(this._lastInsertionIndex)) {
1754
- while((idx >= 0) && (objects.indexOf(content.objectAt(idx)) >= 0)) {
1755
- idx-- ;
1756
- }
2388
+ if (dropOp === SC.DROP_BEFORE) {
2389
+ while ((idx >= 0) && objects.indexes.contains(idx)) idx--;
2390
+ } else {
2391
+ len = content ? content.get('length') : 0;
2392
+ while ((idx < len) && objects.indexes.contains(idx)) idx++;
2393
+ }
1757
2394
  } else idx = this._lastInsertionIndex ;
1758
2395
  }
1759
2396
 
@@ -1763,14 +2400,11 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1763
2400
  }
1764
2401
  }
1765
2402
 
1766
- // console.log('the dragOp is %@'.fmt(dragOp)) ;
1767
-
1768
2403
  // Now save the insertion index and the dropOp. This may be changed by
1769
2404
  // the collection delegate.
1770
2405
  this.set('proposedInsertionIndex', idx) ;
1771
2406
  this.set('proposedDropOperation', dropOp) ;
1772
- // dragOp = this.invokeDelegateMethod(this.delegate, 'collectionViewValidateDrop', this, drag, dropOp, idx, dragOp) ;
1773
- dragOp = this.invokeDelegateMethod(this.delegate, 'collectionViewValidateDragOperation', this, drag, dragOp, idx, dropOp) ;
2407
+ dragOp = del.collectionViewValidateDragOperation(this, drag, dragOp, idx, dropOp) ;
1774
2408
  idx = this.get('proposedInsertionIndex') ;
1775
2409
  dropOp = this.get('proposedDropOperation') ;
1776
2410
  this._dropInsertionIndex = this._dropOperation = null ;
@@ -1786,15 +2420,16 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1786
2420
  content on its own.
1787
2421
  */
1788
2422
  dragUpdated: function(drag, evt) {
1789
- // console.log('dragUpdated called in %@'.fmt(this));
1790
- var state = this._computeDropOperationState(drag, evt) ;
1791
- // console.log('state is %@'.fmt(state));
1792
- var idx = state[0], dropOp = state[1], dragOp = state[2] ;
1793
2423
 
1794
- // if the insertion index or dropOp have changed, update the insertion point
2424
+ var op = drag.get('allowedDragOperations'),
2425
+ state = this._computeDropOperationState(drag, evt, op),
2426
+ idx = state[0], dropOp = state[1], dragOp = state[2];
2427
+
2428
+ // if the insertion index or dropOp have changed, update the insertion
2429
+ // point
1795
2430
  if (dragOp !== SC.DRAG_NONE) {
1796
2431
  if ((this._lastInsertionIndex !== idx) || (this._lastDropOperation !== dropOp)) {
1797
- var itemView = this.itemViewForContent(this.get('content').objectAt(idx));
2432
+ var itemView = this.itemViewForContentIndex(idx) ;
1798
2433
  this.showInsertionPoint(itemView, dropOp) ;
1799
2434
  }
1800
2435
 
@@ -1805,7 +2440,8 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1805
2440
  this._lastInsertionIndex = this._lastDropOperation = null ;
1806
2441
  }
1807
2442
 
1808
- // Normalize drag operation to the standard kinds accepted by the drag system.
2443
+ // Normalize drag operation to the standard kinds accepted by the drag
2444
+ // system.
1809
2445
  return (dragOp & SC.DRAG_REORDER) ? SC.DRAG_MOVE : dragOp;
1810
2446
  },
1811
2447
 
@@ -1818,15 +2454,6 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1818
2454
  this._lastInsertionIndex = this._lastDropOperation = null ;
1819
2455
  },
1820
2456
 
1821
- // /**
1822
- // Implements the SC.DropTarget protocol. Hides any visible insertion
1823
- // point and clears some cached values.
1824
- // */
1825
- // dragEnded: function() {
1826
- // this.hideInsertionPoint() ;
1827
- // this._lastInsertionIndex = this._lastDropOperation = null ;
1828
- // },
1829
-
1830
2457
  /**
1831
2458
  Implements the SC.DropTarget protocol.
1832
2459
  */
@@ -1838,61 +2465,52 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1838
2465
  reordering content.
1839
2466
  */
1840
2467
  performDragOperation: function(drag, op) {
1841
- // console.log('performDragOperation called on %@ with drag.dataTypes %@'.fmt(this, drag.get('dataTypes')));
1842
- // console.log('op is %@'.fmt(SC.Drag.inspectOperation(op)));
1843
2468
  // Get the correct insertion point, drop operation, etc.
1844
- var state = this._computeDropOperationState(drag, null, op) ;
1845
- var idx = state[0], dropOp = state[1], dragOp = state[2] ;
1846
- // console.log('dragOp is %@'.fmt(SC.Drag.inspectOperation(dragOp)));
1847
-
2469
+ var state = this._computeDropOperationState(drag, null, op),
2470
+ idx = state[0], dropOp = state[1], dragOp = state[2],
2471
+ del = this.get('selectionDelegate'),
2472
+ performed, objects, data, content, shift;
2473
+
1848
2474
  // The dragOp is the kinds of ops allowed. The drag operation must
1849
2475
  // be included in that set.
1850
2476
  if (dragOp & SC.DRAG_REORDER) {
1851
2477
  op = (op & SC.DRAG_MOVE) ? SC.DRAG_REORDER : SC.DRAG_NONE ;
1852
- } else {
1853
- op = op & dragOp ;
1854
- }
1855
- // console.log('after processing, op is %@'.fmt(SC.Drag.inspectOperation(op)));
2478
+ } else op = op & dragOp ;
1856
2479
 
1857
2480
  // If no allowed drag operation could be found, just return.
1858
2481
  if (op === SC.DRAG_NONE) return op;
1859
2482
 
1860
2483
  // Some operation is allowed through, give the delegate a chance to
1861
2484
  // handle it.
1862
- // var performed = this.invokeDelegateMethod(this.delegate, 'collectionViewAcceptDrop', this, drag, dropOp, idx, op) ;
1863
- var performed = this.invokeDelegateMethod(this.delegate, 'collectionViewPerformDragOperation', this, drag, op, idx, dropOp) ;
2485
+ performed = del.collectionViewPerformDragOperation(this, drag, op, idx, dropOp) ;
1864
2486
 
1865
- // console.log('performed is %@'.fmt(SC.Drag.inspectOperation(performed)));
1866
2487
  // If the delegate did not handle the drag (i.e. returned SC.DRAG_NONE),
1867
2488
  // and the op type is REORDER, then do the reorder here.
1868
- // console.log('performed & SC.DRAG_NONE is %@, op & SC.DRAG_REORDER is %@'.fmt((performed & SC.DRAG_NONE),(op & SC.DRAG_REORDER)));
1869
2489
  if ((performed === SC.DRAG_NONE) && (op & SC.DRAG_REORDER)) {
1870
- var objects = drag.dataForType(this.get('reorderDataType')) ;
1871
- // console.log('objects is %@'.fmt(objects));
1872
- if (!objects) return SC.DRAG_NONE ;
1873
2490
 
1874
- var content = this.get('content') ;
1875
- content.beginPropertyChanges(); // suspend notifications
2491
+ data = drag.dataForType(this.get('reorderDataType')) ;
2492
+ if (!data) return SC.DRAG_NONE ;
1876
2493
 
1877
- // find the old index and remove it.
1878
- var old ;
1879
- var objectsIdx = objects.get('length') ;
1880
- while(--objectsIdx >= 0) {
1881
- var obj = objects.objectAt(objectsIdx) ;
1882
- old = content.indexOf(obj) ;
1883
- if (old >= 0) content.removeAt(old) ;
1884
- if ((old >= 0) && (old < idx)) idx--; //adjust idx
1885
- }
2494
+ content = this.get('content') ;
2495
+ content.beginPropertyChanges(); // suspend notifications
2496
+
2497
+ // get each object, then remove it from the content. they will be
2498
+ // added again later.
2499
+ objects = [];
2500
+ shift = 0;
2501
+ data.indexes.forEach(function(i) {
2502
+ objects.push(content.objectAt(i-shift));
2503
+ content.removeAt(i-shift);
2504
+ shift++;
2505
+ if (i < idx) idx--;
2506
+ if ((dropOp === SC.DROP_AFTER) && (i === idx)) idx--;
2507
+ }, this);
1886
2508
 
1887
- // now insert objects at new location
1888
- content.replace(idx, 0, objects) ;
2509
+ // now insert objects into new insertion locaiton
2510
+ content.replace(idx, 0, objects);
2511
+ this.select(SC.IndexSet.create(idx, objects.length));
1889
2512
  content.endPropertyChanges(); // restart notifications
1890
-
1891
- var outOfDateIndices = Math.min(old, idx) ;
1892
- if (outOfDateIndices < 0 ) outOfDateIndices = 0 ;
1893
-
1894
- this.rowHeightsDidChangeInRange({ start: outOfDateIndices, length: content.get('length')-outOfDateIndices }) ;
1895
-
2513
+
1896
2514
  // make the op into its actual value
1897
2515
  op = SC.DRAG_MOVE ;
1898
2516
  }
@@ -1907,44 +2525,16 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1907
2525
  collectionViewShouldBeginDrag: function(view) {
1908
2526
  return this.get('canReorderContent') ;
1909
2527
  },
2528
+
1910
2529
 
1911
- // concludeDragOperation: function(op, drag) {
1912
- // this.hideInsertionPoint() ;
1913
- // this._lastInsertionIndex = null ;
1914
- // },
1915
-
1916
- /**
1917
- If some state changes that causes the row height for a range of rows
1918
- then you should call this method to notify the view that it needs to
1919
- recalculate the row heights for the collection.
1920
-
1921
- Anytime your content array changes, the rows are invalidated
1922
- automatically so you only need to use this for cases where your rows
1923
- heights may change without changing the content array itself.
1924
-
1925
- If all rows heights have changed, you can pass null to invalidate the
1926
- whole range.
1927
-
1928
- @param {Range} range or null.
1929
- @returns {SC.CollectionView} reciever
1930
- */
1931
- rowHeightsDidChangeInRange: function(range) {},
2530
+ // ..........................................................
2531
+ // INSERTION POINT
2532
+ //
1932
2533
 
1933
- /**
1934
- The insertion orientation. This is used to determine which
1935
- dimension we should pay attention to when determining insertion point for
1936
- a mouse click.
1937
-
1938
- {{{
1939
- SC.HORIZONTAL_ORIENTATION: look at the X dimension only
1940
- SC.VERTICAL_ORIENTATION: look at the Y dimension only
1941
- }}}
1942
- */
1943
- insertionOrientation: SC.HORIZONTAL_ORIENTATION,
1944
2534
 
1945
2535
  /**
1946
2536
  Get the preferred insertion point for the given location, including
1947
- an insertion preference of before or after the named index.
2537
+ an insertion preference of before, after or on the named index.
1948
2538
 
1949
2539
  You can implement this method in a subclass if you like to perform a
1950
2540
  more efficient check. The default implementation will loop through the
@@ -1953,12 +2543,12 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1953
2543
 
1954
2544
  This method should return an array with two values. The first value is
1955
2545
  the insertion point index and the second value is the drop operation,
1956
- which should be one of SC.DROP_BEFORE or SC.DROP_ON.
2546
+ which should be one of SC.DROP_BEFORE, SC.DROP_AFTER, or SC.DROP_ON.
1957
2547
 
1958
2548
  The preferred drop operation passed in should be used as a hint as to
1959
- the type of operation the drag and drop could would prefer to receive.
1960
- If the dropOperaiton is SC.DROP_ON, then you should return a DROP_ON
1961
- mode if possible. Otherwise, you should never return DROP_ON.
2549
+ the type of operation the view would prefer to receive. If the
2550
+ dropOperation is SC.DROP_ON, then you should return a DROP_ON mode if
2551
+ possible. Otherwise, you should never return DROP_ON.
1962
2552
 
1963
2553
  For compatibility, you can also return just the insertion index. If you
1964
2554
  do this, then the collction view will assume the drop operation is
@@ -1971,155 +2561,180 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1971
2561
  @param dropOperation {DropOp} the preferred drop operation.
1972
2562
  @returns {Array} [proposed drop index, drop operation]
1973
2563
  */
1974
- insertionIndexForLocation: function(loc, dropOperation) {
1975
- var content = this.get('content') ;
1976
- var f, itemView, curSide, lastSide = null ;
1977
- var orient = this.get('insertionOrientation') ;
1978
- var ret= null ;
1979
- for(var idx=0; ((ret === null) && (idx<content.length)); idx++) {
1980
- itemView = this.itemViewForContent(content.objectAt(idx));
1981
- f = this.convertFrameFromView(itemView.get('frame'), itemView) ;
1982
-
1983
- // if we are a horizontal orientation, look for the first item that
1984
- // will "switch sides" on the x path an the maxY is greater than Y.
1985
- // This assumes you will flow top to bottom, but it should work if you
1986
- // flow LTR or RTL.
1987
- if (orient == SC.HORIZONTAL_ORIENTATION) {
1988
- if (SC.maxY(f) > loc.y) {
1989
- curSide = (SC.maxX(f) < loc.x) ? -1 : 1 ;
1990
- } else curSide = null ;
1991
-
1992
- // if we are a vertical orientation, look for the first item that
1993
- // will "swithc sides" on the y path and the maxX is greater than X.
1994
- // This assumes you will flow LTR, but it should work if you flow
1995
- // bottom to top or top to bottom.
1996
- } else {
1997
- if (SC.minX(f) < loc.x) {
1998
- curSide = (SC.maxY(f) < loc.y) ? -1 : 1 ;
1999
- } else curSide = null ;
2000
- }
2001
-
2002
- // if we "switched" sides then return this item view.
2003
- if (curSide !== null) {
2004
-
2005
- // OK, we found an item view, while we have this data, decide if
2006
- // we should insert before or after the view
2007
- if ((lastSide !== null) && (curSide != lastSide)) {
2008
- ret = idx ;
2009
- if (orient == SC.HORIZONTAL_ORIENTATION) {
2010
- if (SC.midX(f) < loc.x) ret++ ;
2011
- } else {
2012
- if (SC.midY(f) < loc.y) ret++ ;
2013
- }
2014
- }
2015
- lastSide =curSide ;
2016
- }
2017
- }
2018
-
2019
- // Handle some edge cases
2020
- if ((ret === null) || (ret < 0)) ret = 0 ;
2021
- if (ret > content.length) ret = content.length ;
2564
+ insertionIndexForLocation: function(loc, dropOperation) {
2565
+ var ret = 0 ;
2566
+
2567
+ // var content = this.get('content'),
2568
+ // nowShowing = this.get('nowShowing'),
2569
+ // orient = this.get('insertionOrientation'),
2570
+ // lastSide = null,
2571
+ // ret = null,
2572
+ // itemView, curSide, f;
2573
+ //
2574
+ // for(var idx=0; ((ret === null) && (idx<content.length)); idx++) {
2575
+ // // itemView = this.itemViewForContent(content.objectAt(idx));
2576
+ // itemView = this.itemViewForContentIndex(idx);
2577
+ // f = this.convertFrameFromView(itemView.get('frame'), itemView) ;
2578
+ //
2579
+ // // if we are a horizontal orientation, look for the first item that
2580
+ // // will "switch sides" on the x path an the maxY is greater than Y.
2581
+ // // This assumes you will flow top to bottom, but it should work if you
2582
+ // // flow LTR or RTL.
2583
+ // if (orient == SC.HORIZONTAL_ORIENTATION) {
2584
+ // if (SC.maxY(f) > loc.y) {
2585
+ // curSide = (SC.maxX(f) < loc.x) ? -1 : 1 ;
2586
+ // } else curSide = null ;
2587
+ //
2588
+ // // if we are a vertical orientation, look for the first item that
2589
+ // // will "switch sides" on the y path and the maxX is greater than X.
2590
+ // // This assumes you will flow LTR, but it should work if you flow
2591
+ // // bottom to top or top to bottom.
2592
+ // } else {
2593
+ // if (SC.minX(f) < loc.x) {
2594
+ // curSide = (SC.maxY(f) < loc.y) ? -1 : 1 ;
2595
+ // } else curSide = null ;
2596
+ // }
2597
+ //
2598
+ // // if we "switched" sides then return this item view.
2599
+ // if (curSide !== null) {
2600
+ //
2601
+ // // OK, we found an item view, while we have this data, decide if
2602
+ // // we should insert before or after the view
2603
+ // if ((lastSide !== null) && (curSide != lastSide)) {
2604
+ // ret = idx ;
2605
+ // if (orient == SC.HORIZONTAL_ORIENTATION) {
2606
+ // if (SC.midX(f) < loc.x) ret++ ;
2607
+ // } else {
2608
+ // if (SC.midY(f) < loc.y) ret++ ;
2609
+ // }
2610
+ // }
2611
+ // lastSide =curSide ;
2612
+ // }
2613
+ // }
2614
+ //
2615
+ // // Handle some edge cases
2616
+ // if ((ret === null) || (ret < 0)) ret = 0 ;
2617
+ // if (ret > content.length) ret = content.length ;
2022
2618
 
2023
2619
  // Done. Phew. Return.
2024
2620
  return ret;
2025
2621
  },
2026
2622
 
2027
- /**
2028
- Override to show the insertion point during a drag.
2029
-
2030
- Called during a drag to show the insertion point. Passed value is the
2031
- item view that you should display the insertion point before. If the
2032
- passed value is null, then you should show the insertion point AFTER that
2033
- last item view returned by the itemViews property.
2034
-
2035
- Once this method is called, you are guaranteed to also recieve a call to
2036
- hideInsertionPoint() at some point in the future.
2037
-
2038
- The default implementation of this method does nothing.
2039
-
2040
- @param itemView {SC.ClassicView} view the insertion point should appear directly before. If null, show insertion point at end.
2041
- @param dropOperation {Number} the drop operation. will be SC.DROP_BEFORE or SC.DROP_ON
2042
-
2043
- @returns {void}
2623
+ // ..........................................................
2624
+ // INTERNAL SUPPORT
2625
+ //
2626
+
2627
+ /** @private - when we become visible, reload if needed. */
2628
+ _cv_isVisibleInWindowDidChange: function() {
2629
+ if (this.get('isVisibleInWindow')) {
2630
+ if (this._invalidIndexes) this.invokeOnce(this.reloadIfNeeded);
2631
+ if (this._invalidSelection) {
2632
+ this.invokeOnce(this.reloadSelectionIndexesIfNeeded);
2633
+ }
2634
+ }
2635
+ }.observes('isVisibleInWindow'),
2636
+
2637
+
2638
+ /**
2639
+ Default delegate method implementation, returns YES if isSelectable
2640
+ is also true.
2044
2641
  */
2045
- showInsertionPoint: function(itemView, dropOperation) {
2046
- return (dropOperation === SC.DROP_BEFORE) ? this.showInsertionPointBefore(itemView) : this.hideInsertionPoint() ;
2642
+ collectionViewShouldSelectItem: function(view, item) {
2643
+ return this.get('isSelectable') ;
2047
2644
  },
2048
2645
 
2049
- /**
2050
- @deprecated
2051
-
2052
- Show the insertion point during a drag before the named item view.
2053
-
2054
- This method has been deprecated in favor of the more generic
2055
- showInsertionPoint() which can be used to show drops occurring both on
2056
- and before an itemView. If you do not implement showInsertionPoint()
2057
- yourself, the default implementation will call this method whenever the
2058
- drop operation is SC.DROP_BEFORE.
2059
-
2060
- @param itemView {SC.ClassicView} the item view to show before.
2061
- @returns {void}
2062
- */
2063
- showInsertionPointBefore: function(itemView) {},
2646
+ _TMP_DIFF1: SC.IndexSet.create(),
2647
+ _TMP_DIFF2: SC.IndexSet.create(),
2064
2648
 
2065
- /**
2066
- Override to hide the insertion point when a drag ends.
2067
-
2068
- Called during a drag to hide the insertion point. This will be called
2069
- when the user exits the view, cancels the drag or completes the drag. It
2070
- will not be called when the insertion point changes during a drag.
2071
-
2072
- You should expect to receive one or more calls to
2073
- showInsertionPointBefore() during a drag followed by at least one call to
2074
- this method at the end. Your method should not raise an error if it is
2075
- called more than once.
2076
-
2077
- @returns {void}
2078
- */
2079
- hideInsertionPoint: function() {},
2649
+ /** @private
2080
2650
 
2081
- dragViewFor: function(dragContent) {
2082
- var view = SC.View.create() ;
2083
-
2084
- var ary = dragContent ;
2085
- for (var idx=0, len=ary.length; idx<len; idx++) {
2086
- var itemView = this.itemViewForContent(ary[idx]) ;
2087
- if (itemView) view.$().append(itemView.rootElement.cloneNode(true)) ;
2651
+ Whenever the nowShowing range changes, update the range observer on the
2652
+ content item and instruct the view to reload any indexes that are not in
2653
+ the previous nowShowing range.
2654
+
2655
+ */
2656
+ _cv_nowShowingDidChange: function() {
2657
+ var nowShowing = this.get('nowShowing'),
2658
+ last = this._sccv_lastNowShowing,
2659
+ diff, diff1, diff2;
2660
+
2661
+ // find the differences between the two
2662
+ // NOTE: reuse a TMP IndexSet object to avoid creating lots of objects
2663
+ // during scrolling
2664
+ if (last && nowShowing && (last !== nowShowing)) {
2665
+ diff1 = this._TMP_DIFF1.add(last).remove(nowShowing);
2666
+ diff2 = this._TMP_DIFF2.add(nowShowing).remove(last);
2667
+ diff = diff1.add(diff2);
2668
+ } else diff = last || nowShowing ;
2669
+
2670
+ // if nowShowing has actually changed, then update
2671
+ if (diff && diff.get('length') > 0) {
2672
+ this._sccv_lastNowShowing = nowShowing ? nowShowing.frozenCopy() : null;
2673
+ this.updateContentRangeObserver();
2674
+ this.reload(diff);
2088
2675
  }
2089
2676
 
2090
- var frame = this.convertFrameToView(this.get('clippingFrame'), null) ;
2091
- view.adjust({ top: frame.y, left: frame.x, width: frame.width, height: frame.height }) ;
2092
- return view ;
2677
+ // cleanup tmp objects
2678
+ if (diff1) diff1.clear();
2679
+ if (diff2) diff2.clear();
2680
+
2681
+ }.observes('nowShowing'),
2682
+
2683
+ init: function() {
2684
+ sc_super();
2685
+ if (this.get('canReorderContent')) this._cv_canReorderContentDidChange();
2686
+ this._sccv_lastNowShowing = this.get('nowShowing').clone();
2687
+ if (this.content) this._cv_contentDidChange();
2688
+ if (this.selection) this._cv_selectionDidChange();
2093
2689
  },
2094
2690
 
2095
- /**
2096
- Default delegate method implementation, returns YES if isSelectable
2097
- is also true.
2691
+ /** @private
2692
+ Become a drop target whenever reordering content is enabled.
2098
2693
  */
2099
- collectionViewShouldSelectItem: function(view, item) {
2100
- return this.get('isSelectable') ;
2101
- },
2694
+ _cv_canReorderContentDidChange: function() {
2695
+ if (this.get('canReorderContent')) {
2696
+ if (!this.get('isDropTarget')) this.set('isDropTarget', YES);
2697
+ SC.Drag.addDropTarget(this);
2698
+ }
2699
+ }.observes('canReorderContent'),
2102
2700
 
2103
- // ......................................
2104
- // INTERNAL
2105
- //
2701
+ /** @private
2702
+ Fires an action after a selection if enabled.
2703
+
2704
+ if actOnSelect is YES, then try to invoke the action, passing the
2705
+ current selection (saved as a separate array so that a change in sel
2706
+ in the meantime will not be lost)
2707
+ */
2708
+ _cv_performSelectAction: function(view, ev, delay, clickCount) {
2709
+ var sel;
2710
+ if (delay === undefined) delay = 0 ;
2711
+ if (clickCount === undefined) clickCount = 1;
2712
+ if ((clickCount>1) || this.get('actOnSelect')) {
2713
+ sel = this.get('selection');
2714
+ sel = sel ? sel.toArray() : [];
2715
+ if (this._cv_actionTimer) this._cv_actionTimer.invalidate();
2716
+ this._cv_actionTimer = this.invokeLater(this._cv_action, delay, view, ev, sel) ;
2717
+ }
2718
+ },
2106
2719
 
2107
2720
  /** @private
2108
2721
  Perform the action. Supports legacy behavior as well as newer style
2109
2722
  action dispatch.
2110
2723
  */
2111
- _action: function(view, evt) {
2112
- // console.log('_action invoked on %@ with view %@, evt %@'.fmt(this, view, evt));
2724
+ _cv_action: function(view, evt, context) {
2113
2725
  var action = this.get('action');
2114
2726
  var target = this.get('target') || null;
2115
- // console.log('action %@, target %@'.fmt(action, target));
2727
+
2728
+ this._cv_actionTimer = null;
2116
2729
  if (action) {
2117
2730
  // if the action is a function, just call it
2118
- if (SC.$type(action) == SC.T_FUNCTION) return this.action(view, evt) ;
2731
+ if (SC.typeOf(action) === SC.T_FUNCTION) return this.action(view, evt) ;
2119
2732
 
2120
2733
  // otherwise, use the new sendAction style
2121
2734
  var pane = this.get('pane') ;
2122
- if (pane) pane.rootResponder.sendAction(action, target, this, pane);
2735
+ if (pane) {
2736
+ pane.rootResponder.sendAction(action, target, this, pane, context);
2737
+ }
2123
2738
  // SC.app.sendAction(action, target, this) ;
2124
2739
 
2125
2740
  // if no action is specified, then trigger the support action,
@@ -2129,13 +2744,14 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
2129
2744
 
2130
2745
  // if the target view has its own internal action handler,
2131
2746
  // trigger that.
2132
- } else if (SC.$type(view._action) == SC.T_FUNCTION) {
2747
+ } else if (SC.typeOf(view._action) == SC.T_FUNCTION) {
2133
2748
  return view._action(evt) ;
2134
2749
 
2135
2750
  // otherwise call the action method to support older styles.
2136
- } else if (SC.$type(view.action) == SC.T_FUNCTION) {
2751
+ } else if (SC.typeOf(view.action) == SC.T_FUNCTION) {
2137
2752
  return view.action(evt) ;
2138
2753
  }
2139
2754
  }
2140
2755
 
2756
+
2141
2757
  });