sproutcore 1.10.3.1 → 1.11.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (380) hide show
  1. checksums.yaml +8 -8
  2. data/CHANGELOG +4 -8
  3. data/VERSION.yml +2 -2
  4. data/lib/frameworks/sproutcore/Buildfile +5 -4
  5. data/lib/frameworks/sproutcore/CHANGELOG.md +274 -40
  6. data/lib/frameworks/sproutcore/CONTRIBUTORS.md +133 -0
  7. data/lib/frameworks/sproutcore/README.md +31 -144
  8. data/lib/frameworks/sproutcore/apps/showcase/controllers/source_tree_controller.js +9 -4
  9. data/lib/frameworks/sproutcore/apps/showcase/resources/stylesheet.css +5 -0
  10. data/lib/frameworks/sproutcore/apps/showcase/system/views_item_content.js +1 -1
  11. data/lib/frameworks/sproutcore/apps/showcase/views/split_views.js +15 -2
  12. data/lib/frameworks/sproutcore/apps/showcase/views/stacked_views.js +1 -1
  13. data/lib/frameworks/sproutcore/apps/tests/english.lproj/main_page.js +11 -1
  14. data/lib/frameworks/sproutcore/frameworks/ajax/mixins/websocket_delegate.js +90 -0
  15. data/lib/frameworks/sproutcore/frameworks/ajax/system/request.js +81 -5
  16. data/lib/frameworks/sproutcore/frameworks/ajax/system/response.js +23 -4
  17. data/lib/frameworks/sproutcore/frameworks/ajax/system/websocket.js +475 -0
  18. data/lib/frameworks/sproutcore/frameworks/ajax/tests/system/request.js +149 -26
  19. data/lib/frameworks/sproutcore/frameworks/ajax/tests/system/websocket.js +197 -0
  20. data/lib/frameworks/sproutcore/frameworks/ajax/tests/system/xhr_response_test.js +65 -0
  21. data/lib/frameworks/sproutcore/frameworks/bootstrap/system/loader.js +4 -0
  22. data/lib/frameworks/sproutcore/frameworks/core_foundation/child_view_layouts/horizontal_stack_layout.js +232 -52
  23. data/lib/frameworks/sproutcore/frameworks/core_foundation/child_view_layouts/vertical_stack_layout.js +235 -49
  24. data/lib/frameworks/sproutcore/frameworks/core_foundation/controllers/array.js +23 -13
  25. data/lib/frameworks/sproutcore/frameworks/core_foundation/controllers/object.js +3 -1
  26. data/lib/frameworks/sproutcore/frameworks/core_foundation/core.js +81 -1
  27. data/lib/frameworks/sproutcore/frameworks/core_foundation/english.lproj/ordinal.js +17 -0
  28. data/lib/frameworks/sproutcore/frameworks/core_foundation/ext/string.js +7 -0
  29. data/lib/frameworks/sproutcore/frameworks/{desktop/tests/views/disclosure/methods.js → core_foundation/french.lproj/ordinal.js} +7 -4
  30. data/lib/frameworks/sproutcore/frameworks/core_foundation/panes/layout.js +2 -6
  31. data/lib/frameworks/sproutcore/frameworks/core_foundation/panes/main.js +1 -1
  32. data/lib/frameworks/sproutcore/frameworks/core_foundation/panes/pane.js +104 -69
  33. data/lib/frameworks/sproutcore/frameworks/core_foundation/panes/pane_statechart.js +6 -1
  34. data/lib/frameworks/sproutcore/frameworks/core_foundation/protocols/child_view_layout_protocol.js +59 -0
  35. data/lib/frameworks/sproutcore/frameworks/core_foundation/protocols/view_transition_protocol.js +18 -1
  36. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/application.js +192 -0
  37. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/bezier_curves.js +52 -0
  38. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/color.js +384 -64
  39. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/core_query.js +6 -14
  40. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/device.js +21 -35
  41. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/event.js +72 -36
  42. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/locale.js +90 -34
  43. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/platform.js +55 -7
  44. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/render_context.js +20 -15
  45. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/req_anim_frame.js +9 -10
  46. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/root_responder.js +763 -542
  47. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/selection_set.js +4 -3
  48. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/sparse_array.js +1 -7
  49. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/string.js +14 -0
  50. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/touch.js +538 -0
  51. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/utils/rect.js +56 -1
  52. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/controllers/array/array_case.js +99 -4
  53. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/controllers/object/single_case.js +25 -19
  54. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/core_tests.js +75 -0
  55. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/ext/number_test.js +81 -0
  56. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/mixins/action_support.js +4 -4
  57. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/mixins/responder_context.js +4 -4
  58. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/mixins/string.js +19 -1
  59. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/system/color.js +36 -20
  60. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/system/root_responder/design_modes_test.js +83 -0
  61. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/system/root_responder/makeMainPane.js +7 -3
  62. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/system/root_responder/mouse_events.js +338 -0
  63. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/system/root_responder/root_responder.js +14 -89
  64. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/system/root_responder/touch.js +106 -0
  65. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/system/sparse_array.js +2 -2
  66. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/system/touch.js +136 -0
  67. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/system/utils/rect.js +42 -1
  68. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/pane/append_remove.js +11 -0
  69. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/pane/child_view.js +5 -5
  70. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/pane/design_mode_test.js +457 -0
  71. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/pane/sendEvent.js +36 -10
  72. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/background_color.js +44 -0
  73. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/border_frame_test.js +51 -24
  74. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/childViewLayout_test.js +176 -1
  75. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/clippingFrame.js +46 -16
  76. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/convertFrames.js +69 -15
  77. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/didAppendToDocument.js +2 -2
  78. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layout.js +7 -1
  79. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layoutDidChange.js +30 -10
  80. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layoutStyle.js +376 -71
  81. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/static_layout.js +0 -10
  82. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/viewDidResize.js +117 -34
  83. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/view_states_test.js +52 -2
  84. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view.js +656 -42
  85. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/animation.js +159 -38
  86. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/cursor.js +0 -7
  87. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/design_mode.js +206 -0
  88. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/enabled.js +0 -28
  89. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/keyboard.js +21 -6
  90. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/layout.js +372 -450
  91. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/layout_style.js +28 -13
  92. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/manipulation.js +22 -51
  93. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/statechart.js +59 -30
  94. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/theming.js +0 -29
  95. data/lib/frameworks/sproutcore/frameworks/datastore/mixins/relationship_support.js +22 -10
  96. data/lib/frameworks/sproutcore/frameworks/datastore/models/children_attribute.js +42 -36
  97. data/lib/frameworks/sproutcore/frameworks/datastore/models/many_attribute.js +54 -3
  98. data/lib/frameworks/sproutcore/frameworks/datastore/models/record.js +178 -59
  99. data/lib/frameworks/sproutcore/frameworks/datastore/models/record_attribute.js +2 -2
  100. data/lib/frameworks/sproutcore/frameworks/datastore/system/child_array.js +206 -132
  101. data/lib/frameworks/sproutcore/frameworks/datastore/system/many_array.js +214 -118
  102. data/lib/frameworks/sproutcore/frameworks/datastore/system/nested_store.js +96 -13
  103. data/lib/frameworks/sproutcore/frameworks/datastore/system/query.js +14 -4
  104. data/lib/frameworks/sproutcore/frameworks/datastore/system/record_array.js +82 -42
  105. data/lib/frameworks/sproutcore/frameworks/datastore/system/store.js +272 -177
  106. data/lib/frameworks/sproutcore/frameworks/datastore/tests/integration/store_interaction_test.js +54 -0
  107. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/datetime_recordattribute.js +24 -16
  108. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/many_attribute.js +6 -3
  109. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/nested_records/data_store.js +267 -35
  110. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/nested_records/nested_record.js +57 -46
  111. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/nested_records/nested_record_array.js +150 -53
  112. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/nested_records/nested_record_array_complex.js +57 -17
  113. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/nested_records/nested_record_complex.js +13 -9
  114. data/lib/frameworks/sproutcore/frameworks/{experimental/frameworks/polymorphism → datastore}/tests/models/polymorphism/many.js +2 -2
  115. data/lib/frameworks/sproutcore/frameworks/{experimental/frameworks/polymorphism → datastore}/tests/models/polymorphism/simple.js +0 -0
  116. data/lib/frameworks/sproutcore/frameworks/{experimental/frameworks/polymorphism → datastore}/tests/models/polymorphism/single.js +12 -2
  117. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/record/writeAttribute.js +20 -15
  118. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/record_attribute.js +9 -2
  119. data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/many_array/core_methods.js +80 -14
  120. data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/autonomous_dataSourceCallbacks.js +280 -0
  121. data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/autonomous_pushChanges.js +232 -0
  122. data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/chain.js +31 -5
  123. data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/query/parse.js +16 -2
  124. data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/store/core_methods.js +60 -40
  125. data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/store/materializeRecord.js +78 -0
  126. data/lib/frameworks/sproutcore/frameworks/datetime/frameworks/core/system/datetime.js +13 -1
  127. data/lib/frameworks/sproutcore/frameworks/datetime/frameworks/core/tests/system/datetime.js +20 -0
  128. data/lib/frameworks/sproutcore/frameworks/datetime/frameworks/localized/{resources → english.lproj}/strings.js +0 -0
  129. data/lib/frameworks/sproutcore/frameworks/datetime/frameworks/localized/french.lproj/strings.js +45 -0
  130. data/lib/frameworks/sproutcore/frameworks/designer/designers/object_designer.js +7 -3
  131. data/lib/frameworks/sproutcore/frameworks/desktop/mixins/collection_row_delegate.js +125 -44
  132. data/lib/frameworks/sproutcore/frameworks/desktop/panes/alert.js +139 -48
  133. data/lib/frameworks/sproutcore/frameworks/desktop/panes/draggable.js +202 -0
  134. data/lib/frameworks/sproutcore/frameworks/desktop/panes/menu.js +59 -56
  135. data/lib/frameworks/sproutcore/frameworks/desktop/panes/palette.js +13 -49
  136. data/lib/frameworks/sproutcore/frameworks/desktop/panes/picker.js +466 -305
  137. data/lib/frameworks/sproutcore/frameworks/desktop/protocols/drag_source.js +49 -12
  138. data/lib/frameworks/sproutcore/frameworks/desktop/render_delegates/slider.js +79 -21
  139. data/lib/frameworks/sproutcore/frameworks/desktop/render_delegates/split.js +12 -2
  140. data/lib/frameworks/sproutcore/frameworks/desktop/resources/menu_item_view.css +8 -0
  141. data/lib/frameworks/sproutcore/frameworks/desktop/resources/overlay-scroller.css +187 -0
  142. data/lib/frameworks/sproutcore/frameworks/desktop/system/drag.js +94 -30
  143. data/lib/frameworks/sproutcore/frameworks/desktop/tests/panes/alert/ui.js +163 -3
  144. data/lib/frameworks/sproutcore/frameworks/desktop/tests/panes/menu/methods.js +97 -78
  145. data/lib/frameworks/sproutcore/frameworks/desktop/tests/panes/menu/ui.js +61 -1
  146. data/lib/frameworks/sproutcore/frameworks/desktop/tests/panes/panel/methods.js +7 -3
  147. data/lib/frameworks/sproutcore/frameworks/desktop/tests/panes/panel/ui.js +47 -22
  148. data/lib/frameworks/sproutcore/frameworks/desktop/tests/panes/picker/methods.js +66 -9
  149. data/lib/frameworks/sproutcore/frameworks/desktop/tests/panes/picker/ui.js +21 -11
  150. data/lib/frameworks/sproutcore/frameworks/desktop/tests/panes/sheet/ui.js +12 -18
  151. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/button/methods.js +17 -14
  152. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/button/ui.js +2 -1
  153. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/checkbox/methods.js +9 -6
  154. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/collection/collection_fast_path.js +54 -21
  155. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/collection/content.js +52 -20
  156. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/collection/itemViewForContentIndex.js +94 -4
  157. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/collection/keyboard.js +177 -0
  158. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/collection/layerIdFor.js +13 -1
  159. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/collection/length.js +9 -9
  160. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/collection/mouse.js +18 -0
  161. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/date_field/methods.js +104 -0
  162. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/disclosure/ui.js +48 -49
  163. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/grid/drag_and_drop.js +22 -18
  164. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/grid/methods.js +17 -5
  165. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/link_view_test.js +136 -0
  166. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/list/contentIndexesInRect.js +77 -0
  167. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/list/drag_and_drop.js +53 -16
  168. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/list/layoutForContentIndex.js +41 -0
  169. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/list/rowDelegate.js +25 -25
  170. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/list/rowOffsetForContentIndex.js +102 -27
  171. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/list/{rowHeightForContentIndex.js → rowSizeForContentIndex.js} +7 -6
  172. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/list/ui_outline.js +2 -0
  173. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/list/ui_row_heights.js +70 -75
  174. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/list/ui_simple.js +29 -30
  175. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/list_item.js +57 -0
  176. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/menu_scroll_view/menu_scroll_view_test.js +206 -0
  177. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/progress/ui.js +15 -0
  178. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/radio/methods.js +15 -7
  179. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/scroll/integration.js +16 -11
  180. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/scroll/methods.js +164 -12
  181. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/scroll/scale.js +387 -0
  182. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/scroll/touch.js +549 -0
  183. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/scroll/ui.js +214 -45
  184. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/scroller.js +5 -5
  185. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/segmented/methods.js +73 -22
  186. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/segmented/ui.js +88 -3
  187. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/select/methods.js +8 -0
  188. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/slider/methods.js +16 -1
  189. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/slider/ui.js +54 -0
  190. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/split/dividers.js +21 -2
  191. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/static_content.js +31 -25
  192. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/tab/methods.js +109 -29
  193. data/lib/frameworks/sproutcore/frameworks/desktop/views/button.js +10 -1
  194. data/lib/frameworks/sproutcore/frameworks/desktop/views/checkbox.js +3 -0
  195. data/lib/frameworks/sproutcore/frameworks/desktop/views/collection.js +779 -603
  196. data/lib/frameworks/sproutcore/frameworks/desktop/views/date_field.js +106 -7
  197. data/lib/frameworks/sproutcore/frameworks/desktop/views/link_view.js +406 -0
  198. data/lib/frameworks/sproutcore/frameworks/desktop/views/list.js +437 -245
  199. data/lib/frameworks/sproutcore/frameworks/desktop/views/list_item.js +13 -0
  200. data/lib/frameworks/sproutcore/frameworks/desktop/views/menu_item.js +124 -62
  201. data/lib/frameworks/sproutcore/frameworks/desktop/views/menu_scroll.js +176 -597
  202. data/lib/frameworks/sproutcore/frameworks/desktop/views/menu_scroller_view.js +206 -0
  203. data/lib/frameworks/sproutcore/frameworks/desktop/views/popup_button.js +3 -0
  204. data/lib/frameworks/sproutcore/frameworks/desktop/views/progress.js +5 -4
  205. data/lib/frameworks/sproutcore/frameworks/desktop/views/radio.js +3 -0
  206. data/lib/frameworks/sproutcore/frameworks/desktop/views/scene.js +56 -158
  207. data/lib/frameworks/sproutcore/frameworks/desktop/views/scroll_view.js +2560 -0
  208. data/lib/frameworks/sproutcore/frameworks/desktop/views/scroller.js +458 -242
  209. data/lib/frameworks/sproutcore/frameworks/desktop/views/segmented.js +117 -54
  210. data/lib/frameworks/sproutcore/frameworks/desktop/views/select.js +18 -12
  211. data/lib/frameworks/sproutcore/frameworks/desktop/views/slider.js +162 -34
  212. data/lib/frameworks/sproutcore/frameworks/desktop/views/split.js +30 -15
  213. data/lib/frameworks/sproutcore/frameworks/desktop/views/split_divider.js +33 -7
  214. data/lib/frameworks/sproutcore/frameworks/desktop/views/static_content.js +22 -2
  215. data/lib/frameworks/sproutcore/frameworks/desktop/views/tab.js +47 -22
  216. data/lib/frameworks/sproutcore/frameworks/experimental/Buildfile +0 -6
  217. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/forms/views/form.js +2 -1
  218. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/forms/views/form_row.js +21 -21
  219. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/select_view/ext/menu.js +14 -3
  220. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/select_view/mixins/select_view_menu.js +24 -10
  221. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/select_view/tests/ext/menu_resizing.js +1 -1
  222. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/select_view/tests/mixins/select_view_menu/bindings.js +7 -4
  223. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/select_view/tests/mixins/select_view_menu/check_selected.js +7 -9
  224. data/lib/frameworks/sproutcore/frameworks/{desktop/tests/panes/select_button/methods.js → experimental/frameworks/select_view/tests/views/select/method.js} +54 -76
  225. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/select_view/tests/views/select/selected_item.js +35 -0
  226. data/lib/frameworks/sproutcore/frameworks/{desktop/tests/panes/select_button → experimental/frameworks/select_view/tests/views/select}/ui.js +107 -36
  227. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/select_view/views/select.js +225 -66
  228. data/lib/frameworks/sproutcore/frameworks/foundation/controllers/tree.js +39 -38
  229. data/lib/frameworks/sproutcore/frameworks/foundation/core.js +5 -18
  230. data/lib/frameworks/sproutcore/frameworks/foundation/debug/control_test_pane.js +12 -0
  231. data/lib/frameworks/sproutcore/frameworks/foundation/english.lproj/inflections.js +84 -0
  232. data/lib/frameworks/sproutcore/frameworks/foundation/french.lproj/inflections.js +41 -0
  233. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/auto_mixin.js +1 -0
  234. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/auto_resize.js +7 -0
  235. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/content_display.js +3 -4
  236. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/flowed_layout.js +6 -2
  237. data/lib/frameworks/sproutcore/frameworks/foundation/private/tree_item_observer.js +408 -239
  238. data/lib/frameworks/sproutcore/frameworks/foundation/render_delegates/canvas_image.js +1 -1
  239. data/lib/frameworks/sproutcore/frameworks/foundation/resources/text_field.css +2 -1
  240. data/lib/frameworks/sproutcore/frameworks/foundation/spanish.lproj/inflections.js +38 -0
  241. data/lib/frameworks/sproutcore/frameworks/foundation/system/benchmark.js +104 -76
  242. data/lib/frameworks/sproutcore/frameworks/foundation/system/string.js +20 -94
  243. data/lib/frameworks/sproutcore/frameworks/foundation/system/text_selection.js +33 -22
  244. data/lib/frameworks/sproutcore/frameworks/foundation/system/undo_manager.js +475 -0
  245. data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/auto_resize_test.js +163 -1
  246. data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/flowed_layout/tests.js +41 -0
  247. data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/staticLayout.js +2 -5
  248. data/lib/frameworks/sproutcore/frameworks/foundation/tests/private/tree_item_observer/methods.js +268 -0
  249. data/lib/frameworks/sproutcore/frameworks/foundation/tests/system/undo_manager.js +231 -0
  250. data/lib/frameworks/sproutcore/frameworks/foundation/tests/views/container/ui.js +16 -0
  251. data/lib/frameworks/sproutcore/frameworks/foundation/tests/views/image/ui.js +27 -0
  252. data/lib/frameworks/sproutcore/frameworks/foundation/tests/views/text_field/methods.js +24 -0
  253. data/lib/frameworks/sproutcore/frameworks/foundation/tests/views/text_field/ui.js +135 -6
  254. data/lib/frameworks/sproutcore/frameworks/foundation/transitions/fade_transition.js +6 -0
  255. data/lib/frameworks/sproutcore/frameworks/foundation/transitions/pop_transition.js +7 -0
  256. data/lib/frameworks/sproutcore/frameworks/foundation/transitions/scale_transition.js +6 -0
  257. data/lib/frameworks/sproutcore/frameworks/foundation/transitions/slide_transition.js +4 -0
  258. data/lib/frameworks/sproutcore/frameworks/foundation/transitions/swap_dissolve_transition.js +3 -1
  259. data/lib/frameworks/sproutcore/frameworks/foundation/validators/credit_card.js +21 -21
  260. data/lib/frameworks/sproutcore/frameworks/foundation/views/container.js +65 -15
  261. data/lib/frameworks/sproutcore/frameworks/foundation/views/image.js +4 -1
  262. data/lib/frameworks/sproutcore/frameworks/foundation/views/label.js +1 -1
  263. data/lib/frameworks/sproutcore/frameworks/foundation/views/text_field.js +193 -213
  264. data/lib/frameworks/sproutcore/frameworks/jquery/{jquery-1.8.3-patched.js → jquery-1.11.1.js} +7507 -6684
  265. data/lib/frameworks/sproutcore/frameworks/routing/system/routes.js +28 -11
  266. data/lib/frameworks/sproutcore/frameworks/routing/tests/system/routes.js +26 -0
  267. data/lib/frameworks/sproutcore/frameworks/runtime/core.js +54 -25
  268. data/lib/frameworks/sproutcore/frameworks/runtime/ext/array.js +0 -6
  269. data/lib/frameworks/sproutcore/frameworks/runtime/ext/number.js +36 -0
  270. data/lib/frameworks/sproutcore/frameworks/runtime/ext/window.js +25 -0
  271. data/lib/frameworks/sproutcore/frameworks/runtime/mixins/array.js +3 -3
  272. data/lib/frameworks/sproutcore/frameworks/runtime/mixins/enumerable.js +1 -1
  273. data/lib/frameworks/sproutcore/frameworks/runtime/mixins/observable.js +156 -66
  274. data/lib/frameworks/sproutcore/frameworks/runtime/private/observer_set.js +2 -2
  275. data/lib/frameworks/sproutcore/frameworks/runtime/system/binding.js +150 -65
  276. data/lib/frameworks/sproutcore/frameworks/runtime/system/index_set.js +57 -11
  277. data/lib/frameworks/sproutcore/frameworks/runtime/system/object.js +68 -49
  278. data/lib/frameworks/sproutcore/frameworks/runtime/system/run_loop.js +14 -6
  279. data/lib/frameworks/sproutcore/frameworks/runtime/system/string.js +23 -23
  280. data/lib/frameworks/sproutcore/frameworks/runtime/tests/ext/number_test.js +44 -0
  281. data/lib/frameworks/sproutcore/frameworks/runtime/tests/mixins/array.js +0 -10
  282. data/lib/frameworks/sproutcore/frameworks/runtime/tests/mixins/enumerable/enumerable.js +340 -285
  283. data/lib/frameworks/sproutcore/frameworks/runtime/tests/system/binding.js +104 -3
  284. data/lib/frameworks/sproutcore/frameworks/runtime/tests/system/observer_set.js +14 -1
  285. data/lib/frameworks/sproutcore/frameworks/runtime/tests/system/string.js +15 -2
  286. data/lib/frameworks/sproutcore/frameworks/statechart/system/state.js +21 -18
  287. data/lib/frameworks/sproutcore/frameworks/statechart/system/statechart.js +52 -19
  288. data/lib/frameworks/sproutcore/frameworks/statechart/tests/event_handling/responder/pane.js +27 -24
  289. data/lib/frameworks/sproutcore/frameworks/template_view/controls/button.js +30 -0
  290. data/lib/frameworks/sproutcore/frameworks/template_view/ext/handlebars/bind.js +1 -1
  291. data/lib/frameworks/sproutcore/frameworks/template_view/ext/handlebars/collection.js +2 -0
  292. data/lib/frameworks/sproutcore/frameworks/template_view/ext/handlebars/view.js +1 -0
  293. data/lib/frameworks/sproutcore/frameworks/template_view/tests/mixins/template_helpers/checkbox_support.js +2 -2
  294. data/lib/frameworks/sproutcore/frameworks/template_view/tests/views/template/handlebars.js +4 -2
  295. data/lib/frameworks/sproutcore/frameworks/template_view/views/bindable_span.js +1 -1
  296. data/lib/frameworks/sproutcore/frameworks/template_view/views/template_collection.js +16 -14
  297. data/lib/frameworks/sproutcore/frameworks/testing/core.js +5 -3
  298. data/lib/frameworks/sproutcore/frameworks/testing/system/plan.js +13 -0
  299. data/lib/frameworks/sproutcore/lib/index.rhtml +2 -2
  300. data/lib/frameworks/sproutcore/phantomjs/test_runner.js +28 -7
  301. data/lib/frameworks/sproutcore/scripts/run_sc_server_master.sh +1 -1
  302. data/lib/frameworks/sproutcore/themes/ace/resources/_variables.css +2 -0
  303. data/lib/frameworks/sproutcore/themes/ace/resources/disclosure/ace/disclosure.css +1 -0
  304. data/lib/frameworks/sproutcore/themes/ace/resources/picker/popover/popover.css +3 -4
  305. data/lib/frameworks/sproutcore/themes/ace/resources/scroller/horizontal/horizontal.css +15 -15
  306. data/lib/frameworks/sproutcore/themes/ace/resources/scroller/horizontal/horizontal_overlay.css +74 -0
  307. data/lib/frameworks/sproutcore/themes/ace/resources/scroller/vertical/vertical.css +11 -13
  308. data/lib/frameworks/sproutcore/themes/ace/resources/scroller/vertical/vertical_overlay.css +74 -0
  309. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/jumbo/knob-active.png +0 -0
  310. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/jumbo/knob-active@2x.png +0 -0
  311. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/jumbo/knob.png +0 -0
  312. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/jumbo/knob@2x.png +0 -0
  313. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/{22px → jumbo}/slider.css +9 -4
  314. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/jumbo/track.png +0 -0
  315. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/jumbo/track@2x.png +0 -0
  316. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/regular/knob-active.png +0 -0
  317. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/regular/knob-active@2x.png +0 -0
  318. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/regular/knob.png +0 -0
  319. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/regular/knob@2x.png +0 -0
  320. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/regular/slider.css +32 -0
  321. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/regular/track.png +0 -0
  322. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/regular/track@2x.png +0 -0
  323. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/slider.css +13 -0
  324. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/small/knob-active.png +0 -0
  325. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/small/knob-active@2x.png +0 -0
  326. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/small/knob.png +0 -0
  327. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/small/knob@2x.png +0 -0
  328. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/small/slider.css +32 -0
  329. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/small/track.png +0 -0
  330. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/small/track@2x.png +0 -0
  331. data/lib/frameworks/sproutcore/themes/ace/resources/split/split.css +2 -3
  332. data/lib/sproutcore/builders/chance_file.rb +3 -3
  333. data/lib/sproutcore/helpers/minifier.rb +1 -0
  334. data/vendor/chance/lib/chance/instance.rb +34 -34
  335. data/vendor/chance/lib/chance/instance/spriting.rb +21 -16
  336. metadata +81 -58
  337. data/lib/frameworks/sproutcore/frameworks/core_foundation/panes/visibility.js +0 -17
  338. data/lib/frameworks/sproutcore/frameworks/desktop/mixins/collection_fast_path.js +0 -710
  339. data/lib/frameworks/sproutcore/frameworks/desktop/mixins/scrollable.js +0 -267
  340. data/lib/frameworks/sproutcore/frameworks/desktop/resources/touch-scroller.css +0 -196
  341. data/lib/frameworks/sproutcore/frameworks/desktop/system/undo_manager.js +0 -224
  342. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/select_field/methods.js +0 -163
  343. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/select_field/ui.js +0 -177
  344. data/lib/frameworks/sproutcore/frameworks/desktop/views/scroll.js +0 -2053
  345. data/lib/frameworks/sproutcore/frameworks/desktop/views/select_button.js +0 -1024
  346. data/lib/frameworks/sproutcore/frameworks/desktop/views/select_field.js +0 -404
  347. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/render_delegates/menu_scroller.js +0 -28
  348. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/tests/menu/scroll.js +0 -235
  349. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/views/menu/scroll.js +0 -363
  350. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/views/menu/scroller.js +0 -250
  351. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/polymorphism/README.md +0 -47
  352. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/polymorphism/models/record.js +0 -134
  353. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/desktop_scroller.js +0 -92
  354. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/native_scroll.js +0 -25
  355. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/scroll.js +0 -33
  356. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/touch_scroller.js +0 -76
  357. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/tests/scroll/integration.js +0 -25
  358. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/tests/scroll/methods.js +0 -143
  359. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/tests/scroll/ui.js +0 -256
  360. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/core_scroll.js +0 -1164
  361. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/core_scroller.js +0 -332
  362. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/desktop/scroll.js +0 -236
  363. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/desktop/scroller.js +0 -347
  364. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/scroll.js +0 -15
  365. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/scroller.js +0 -10
  366. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/touch/scroll.js +0 -804
  367. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/touch/scroller.js +0 -133
  368. data/lib/frameworks/sproutcore/frameworks/foundation/tasks/preload_bundle.js +0 -41
  369. data/lib/frameworks/sproutcore/themes/ace/resources/scroller/horizontal/horizontal_touch.css +0 -91
  370. data/lib/frameworks/sproutcore/themes/ace/resources/scroller/vertical/vertical_touch.css +0 -92
  371. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/14px/knob.png +0 -0
  372. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/14px/knob_active.png +0 -0
  373. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/14px/slider.css +0 -27
  374. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/16px/knob.png +0 -0
  375. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/16px/knob_active.png +0 -0
  376. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/16px/slider.css +0 -27
  377. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/22px/knob.png +0 -0
  378. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/22px/knob_active.png +0 -0
  379. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/22px/track.png +0 -0
  380. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/track.png +0 -0
@@ -410,7 +410,7 @@ SC.ButtonView = SC.View.extend(SC.Control,
410
410
  // Invoke the actual action method after a small delay to give the user a
411
411
  // chance to see the highlight. This is especially important if the button
412
412
  // closes a pane, for example.
413
- this.invokeLater('triggerAction', 200, evt);
413
+ this.invokeLater('triggerAction', SC.ButtonView.TRIGGER_DELAY, evt);
414
414
  return YES;
415
415
  },
416
416
 
@@ -636,6 +636,9 @@ SC.ButtonView = SC.View.extend(SC.Control,
636
636
  On mouse down, set active only if enabled.
637
637
  */
638
638
  mouseDown: function(evt) {
639
+ // Fast path, reject secondary clicks.
640
+ if (evt.which !== 1) return false;
641
+
639
642
  var buttonBehavior = this.get('buttonBehavior');
640
643
 
641
644
  if (!this.get('isEnabledInPane')) return YES ; // handled event, but do nothing
@@ -880,7 +883,13 @@ SC.ButtonView = SC.View.extend(SC.Control,
880
883
 
881
884
  }) ;
882
885
 
886
+ /**
887
+ How long to wait before triggering the action.
883
888
 
889
+ @constant
890
+ @type {Number}
891
+ */
892
+ SC.ButtonView.TRIGGER_DELAY = 200;
884
893
 
885
894
  /**
886
895
  The delay after which "click" behavior should transition to "click and hold"
@@ -76,6 +76,9 @@ SC.CheckboxView = SC.ButtonView.extend(
76
76
 
77
77
  /** @private */
78
78
  mouseDown: function(evt) {
79
+ // Fast path, reject secondary clicks.
80
+ if (evt.which && evt.which !== 1) return false;
81
+
79
82
  if(!this.get('isEnabledInPane')) return YES;
80
83
  this.set('isActive', YES);
81
84
  this._isMouseDown = YES;
@@ -5,8 +5,7 @@
5
5
  // License: Licensed under MIT license (see license.js)
6
6
  // ==========================================================================
7
7
 
8
- sc_require('mixins/collection_view_delegate');
9
- sc_require('views/list_item');
8
+ sc_require('mixins/collection_view_delegate') ;
10
9
 
11
10
  /**
12
11
  Special drag operation passed to delegate if the collection view proposes
@@ -60,6 +59,40 @@ SC.DRAG_REORDER = 0x0010;
60
59
  SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionContent,
61
60
  /** @scope SC.CollectionView.prototype */ {
62
61
 
62
+ /** @private */
63
+ _content: null,
64
+
65
+ /** @private */
66
+ _cv_actionTimer: null,
67
+
68
+ /** @private */
69
+ _cv_contentRangeObserver: null,
70
+
71
+ /** @private */
72
+ _cv_selection: null,
73
+
74
+ /** @private */
75
+ _pools: null,
76
+
77
+ /** @private Timer used to track time immediately after a mouse up event. */
78
+ _sc_clearMouseJustDownTimer: null,
79
+
80
+ /** @private Flag used to track when the mouse is pressed. */
81
+ _sc_isMouseDown: false,
82
+
83
+ /** @private Flag used to track when mouse was just down so that mousewheel events firing as the finger is lifted don't shoot the slider over. */
84
+ _sc_isMouseJustDown: false,
85
+
86
+ /** @private */
87
+ _sc_itemViews: null,
88
+
89
+
90
+ /** @private */
91
+ _TMP_DIFF1: SC.IndexSet.create(),
92
+
93
+ /** @private */
94
+ _TMP_DIFF2: SC.IndexSet.create(),
95
+
63
96
  /**
64
97
  @type Array
65
98
  @default ['sc-collection-view']
@@ -156,9 +189,9 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
156
189
  @observes length
157
190
  @observes clippingFrame
158
191
  */
159
- nowShowing: function () {
192
+ nowShowing: function() {
160
193
  // If there is an in-scroll clipping frame, use it.
161
- var clippingFrame = this._inScrollClippingFrame || this.get('clippingFrame');
194
+ var clippingFrame = this.get('clippingFrame');
162
195
 
163
196
  return this.computeNowShowing(clippingFrame);
164
197
  }.property('length', 'clippingFrame').cacheable(),
@@ -465,6 +498,33 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
465
498
  */
466
499
  target: null,
467
500
 
501
+ /**
502
+ Invoked when the user single clicks on the right icon of an item.
503
+
504
+ Set this to the name of the action you want to send down the
505
+ responder chain when the user single clicks on the right icon of an item
506
+ You can optionally specify a specific target as
507
+ well using the rightIconTarget property.
508
+
509
+ @type String
510
+ @default null
511
+ */
512
+ rightIconAction: null,
513
+
514
+ /**
515
+ Optional target to send the action to when the user clicks on the right icon
516
+ of an item.
517
+
518
+ If you set the rightIconAction property to the name of an action, you can
519
+ optionally specify the target object you want the action to be sent to.
520
+ This can be either an actual object or a property path that will resolve
521
+ to an object at the time that the action is invoked.
522
+
523
+ @type String|Object
524
+ @default null
525
+ */
526
+ rightIconTarget: null,
527
+
468
528
  /**
469
529
  Property on content items to use for display.
470
530
 
@@ -503,25 +563,14 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
503
563
  */
504
564
  isActive: NO,
505
565
 
506
-
507
- /**
508
- This property is used to store the calculated height to have
509
- a consistent scrolling behavior due to the issues generated by using top
510
- instead of `scrollTop`. We could look at the min-height set in the view, but
511
- to avoid performance hits we simply store it and the scrollView will use it if
512
- different than 0.
566
+ /** @deprecated Version 1.11.0. SC.ScrollView observes the frame (height/width) of the collection.
513
567
 
514
568
  @type Number
515
569
  @default 0
516
570
  */
517
571
  calculatedHeight: 0,
518
572
 
519
- /**
520
- This property is used to store the calculated width to have
521
- a consistent scrolling behavior due to the issues generated by using left
522
- instead of `scrollLeft`. We could look at the min-width set in the view, but
523
- to avoid performance hits we simply store it and the scrollView will use it if
524
- different than 0.
573
+ /** @deprecated Version 1.11.0. SC.ScrollView observes the frame (height/width) of the collection.
525
574
 
526
575
  @type Number
527
576
  @default 0
@@ -553,7 +602,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
553
602
 
554
603
  @returns {Hash} layout properties
555
604
  */
556
- computeLayout: function () {
605
+ computeLayout: function() {
557
606
  return null;
558
607
  },
559
608
 
@@ -566,7 +615,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
566
615
  itemView
567
616
  @returns {Hash} a view layout
568
617
  */
569
- layoutForContentIndex: function (contentIndex) {
618
+ layoutForContentIndex: function(contentIndex) {
570
619
  return null;
571
620
  },
572
621
 
@@ -580,7 +629,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
580
629
  @type SC.IndexSet
581
630
  @observes length
582
631
  */
583
- allContentIndexes: function () {
632
+ allContentIndexes: function() {
584
633
  return SC.IndexSet.create(0, this.get('length')).freeze();
585
634
  }.property('length').cacheable(),
586
635
 
@@ -595,7 +644,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
595
644
  @param {Rect} rect the visible rect
596
645
  @returns {SC.IndexSet} now showing indexes
597
646
  */
598
- contentIndexesInRect: function (rect) {
647
+ contentIndexesInRect: function(rect) {
599
648
  return null; // select all
600
649
  },
601
650
 
@@ -617,7 +666,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
617
666
  else {
618
667
  var len = this.get('length'),
619
668
  max = r.get('max');
620
- if (max > len) r = r.copy().remove(len, max - len).freeze();
669
+ if (max > len) r = r.copy().remove(len, max-len).freeze();
621
670
  }
622
671
 
623
672
  return r;
@@ -641,7 +690,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
641
690
 
642
691
  @returns {void}
643
692
  */
644
- showInsertionPoint: function (itemView, dropOperation) {},
693
+ showInsertionPoint: function(itemView, dropOperation) {},
645
694
 
646
695
  /**
647
696
  Override to hide the insertion point when a drag ends.
@@ -657,7 +706,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
657
706
 
658
707
  @returns {void}
659
708
  */
660
- hideInsertionPoint: function () {},
709
+ hideInsertionPoint: function() {},
661
710
 
662
711
 
663
712
  // ..........................................................
@@ -692,7 +741,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
692
741
  @field
693
742
  @type Object
694
743
  */
695
- selectionDelegate: function () {
744
+ selectionDelegate: function() {
696
745
  var del = this.get('delegate'), content = this.get('content');
697
746
  return this.delegateFor('isCollectionViewDelegate', del, content);
698
747
  }.property('delegate', 'content').cacheable(),
@@ -706,7 +755,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
706
755
  @field
707
756
  @type Object
708
757
  */
709
- contentDelegate: function () {
758
+ contentDelegate: function() {
710
759
  var del = this.get('delegate'), content = this.get('content');
711
760
  return this.delegateFor('isCollectionContent', del, content);
712
761
  }.property('delegate', 'content').cacheable(),
@@ -731,7 +780,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
731
780
  @param {SC.IndexSet} indexes affected indexes or null for all items
732
781
  @returns {void}
733
782
  */
734
- contentRangeDidChange: function (content, object, key, indexes) {
783
+ contentRangeDidChange: function(content, object, key, indexes) {
735
784
  if (!object && (key === '[]')) {
736
785
  this.notifyPropertyChange('_contentGroupIndexes');
737
786
  this.reload(indexes); // note: if indexes == null, reloads all
@@ -754,7 +803,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
754
803
  @param {SC.IndexSet} indexes the indexes in the content array affected
755
804
  @returns {void}
756
805
  */
757
- contentPropertyDidChange: function (target, key, indexes) {},
806
+ contentPropertyDidChange: function(target, key, indexes) {},
758
807
 
759
808
  /**
760
809
  Called whenever the view needs to updates its `contentRangeObserver` to
@@ -775,12 +824,12 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
775
824
 
776
825
  @returns {void}
777
826
  */
778
- updateContentRangeObserver: function () {
827
+ updateContentRangeObserver: function() {
779
828
  var nowShowing = this.get('nowShowing'),
780
829
  observer = this._cv_contentRangeObserver,
781
830
  content = this.get('content');
782
831
 
783
- if (!content) return; // nothing to do
832
+ if (!content) return ; // nothing to do
784
833
 
785
834
  if (observer) {
786
835
  content.updateRangeObserver(observer, nowShowing);
@@ -789,7 +838,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
789
838
  observer = content.addRangeObserver(nowShowing, this, func, null);
790
839
 
791
840
  // Cache the range observer so we can clean it up later.
792
- this._cv_contentRangeObserver = observer;
841
+ this._cv_contentRangeObserver = observer ;
793
842
  }
794
843
 
795
844
  },
@@ -805,13 +854,13 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
805
854
 
806
855
  @returns {void}
807
856
  */
808
- removeContentRangeObserver: function () {
857
+ removeContentRangeObserver: function() {
809
858
  var content = this.get('content'),
810
- observer = this._cv_contentRangeObserver;
859
+ observer = this._cv_contentRangeObserver ;
811
860
 
812
861
  if (observer) {
813
862
  if (content) content.removeRangeObserver(observer);
814
- this._cv_contentRangeObserver = null;
863
+ this._cv_contentRangeObserver = null ;
815
864
  }
816
865
  },
817
866
 
@@ -822,7 +871,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
822
871
 
823
872
  @returns {void}
824
873
  */
825
- contentLengthDidChange: function () {
874
+ contentLengthDidChange: function() {
826
875
  var content = this.get('content');
827
876
  this.set('length', content ? content.get('length') : 0);
828
877
  this.invokeOnce(this.adjustLayout);
@@ -843,9 +892,9 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
843
892
  - reload effected item views
844
893
  - update layout for receiver
845
894
  */
846
- _cv_contentDidChange: function () {
895
+ _cv_contentDidChange: function() {
847
896
  var content = this.get('content'),
848
- lfunc = this.contentLengthDidChange;
897
+ lfunc = this.contentLengthDidChange ;
849
898
 
850
899
  if (content === this._content) return; // nothing to do
851
900
 
@@ -860,7 +909,8 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
860
909
  for (var key in this._pools) {
861
910
  this._pools[key].invoke('destroy');
862
911
  }
863
- delete this._pools;
912
+
913
+ this._pools = null;
864
914
  }
865
915
 
866
916
  // cache
@@ -910,20 +960,27 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
910
960
  @param {SC.IndexSet} indexes
911
961
  @returns {SC.CollectionView} receiver
912
962
  */
913
- reload: function (indexes) {
914
- var invalid = this._invalidIndexes;
963
+ reload: function(indexes) {
964
+ var invalid = this._invalidIndexes,
965
+ length;
966
+
915
967
  if (indexes && invalid !== YES) {
916
968
  if (invalid) invalid.add(indexes);
917
969
  else invalid = this._invalidIndexes = indexes.clone();
918
970
 
971
+ // If the last item in the list changes, we need to reload the previous last
972
+ // item also so that the isLast attribute updates appropriately.
973
+ length = this.get('length');
974
+ if (length > 1 && invalid.max === length) {
975
+ invalid.add(length - 2);
919
976
  }
920
- else {
921
- this._invalidIndexes = YES; // force a total reload
977
+ } else {
978
+ this._invalidIndexes = YES ; // force a total reload
922
979
  }
923
980
 
924
981
  if (this.get('isVisibleInWindow')) this.invokeOnce(this.reloadIfNeeded);
925
982
 
926
- return this;
983
+ return this ;
927
984
  },
928
985
 
929
986
  /**
@@ -933,19 +990,19 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
933
990
 
934
991
  @returns {SC.CollectionView} receiver
935
992
  */
936
- reloadIfNeeded: function () {
993
+ reloadIfNeeded: function() {
937
994
  var invalid = this._invalidIndexes;
938
- if (!invalid || !this.get('isVisibleInWindow')) return this; // delay
939
- this._invalidIndexes = NO;
995
+ if (!invalid || !this.get('isVisibleInWindow')) return this ; // delay
996
+ this._invalidIndexes = NO ;
940
997
 
941
998
  var len, existing,
942
- nowShowing = this.get('nowShowing'),
999
+ nowShowing = this.get('nowShowing'),
943
1000
  itemViews = this._sc_itemViews || [],
944
1001
  idx;
945
1002
 
946
1003
  // if the set is defined but it contains the entire nowShowing range, just
947
1004
  // replace
948
- if (invalid.isIndexSet && invalid.contains(nowShowing)) invalid = YES;
1005
+ if (invalid.isIndexSet && invalid.contains(nowShowing)) invalid = YES ;
949
1006
 
950
1007
  // if an index set, just update indexes
951
1008
  if (invalid.isIndexSet) {
@@ -954,7 +1011,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
954
1011
  // should be redrawn (exists and still showing), should be created (
955
1012
  // doesn't exist and now showing) or should be destroyed (exists and no
956
1013
  // longer showing).
957
- invalid.forEach(function (idx) {
1014
+ invalid.forEach(function(idx) {
958
1015
  // Get the existing item view, if there is one.
959
1016
  existing = itemViews[idx];
960
1017
  if (existing) {
@@ -965,8 +1022,8 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
965
1022
  // Create it (may fetch from pool).
966
1023
  if (nowShowing.contains(idx)) {
967
1024
  this.itemViewForContentIndex(idx, YES);
968
- }
969
- }, this);
1025
+ }
1026
+ },this);
970
1027
 
971
1028
  // if set is NOT defined, replace entire content with nowShowing
972
1029
  } else {
@@ -977,27 +1034,33 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
977
1034
  existing = itemViews ? itemViews[idx] : null;
978
1035
  if (existing) {
979
1036
  this._removeItemView(existing, idx);
1037
+ }
980
1038
  }
981
- }
982
1039
 
983
1040
  // Only after the children are removed should we create the new views.
984
1041
  // We do this in order to maximize the chance of re-use should the view
985
1042
  // be marked as such.
986
- nowShowing.forEach(function (idx) {
1043
+ nowShowing.forEach(function(idx) {
987
1044
  this.itemViewForContentIndex(idx, YES);
988
1045
  }, this);
989
1046
  }
990
1047
 
991
- return this;
1048
+ return this ;
992
1049
  },
993
1050
 
994
- /** @private */
1051
+ /** @private Use a shared object so that we are not creating objects for every item view configuration. */
995
1052
  _TMP_ATTRS: {},
996
1053
 
997
- /** @private */
1054
+ /** @private
1055
+ The item view classes, cached here for performance. Note that if these ever change, they may
1056
+ also need to be updated in the isGroupView code block in _reconfigureItemView below.
1057
+ */
998
1058
  _COLLECTION_CLASS_NAMES: ['sc-collection-item', 'sc-item'],
999
1059
 
1000
- /** @private */
1060
+ /** @private
1061
+ The group view classes, cached here for performance. Note that if these ever change, they may
1062
+ also need to be updated in the isGroupView code block in _reconfigureItemView below.
1063
+ */
1001
1064
  _GROUP_COLLECTION_CLASS_NAMES: ['sc-collection-item', 'sc-group-item'],
1002
1065
 
1003
1066
  /**
@@ -1023,10 +1086,23 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1023
1086
  @param {Boolean} rebuild internal use only
1024
1087
  @returns {SC.View} instantiated view
1025
1088
  */
1026
- itemViewForContentIndex: function (idx, rebuild) {
1089
+ itemViewForContentIndex: function(idx, rebuild) {
1027
1090
  var ret,
1028
1091
  views;
1029
1092
 
1093
+ // Gatekeep! Since this method is often called directly by loops that may
1094
+ // suffer from bounds issues, we should validate the idx and return nothing
1095
+ // rather than returning an invalid item view.
1096
+ if (SC.none(idx) || idx < 0 || idx >= this.get('length')) {
1097
+ //@if(debug)
1098
+ // Developer support
1099
+ SC.warn("Developer Warning: %@ - itemViewForContentIndex(%@): The index, %@, is not within the range of the content.".fmt(this, idx, idx));
1100
+ //@endif
1101
+
1102
+ return null; // FAST PATH!!
1103
+ }
1104
+
1105
+
1030
1106
  // Initialize internal views cache.
1031
1107
  views = this._sc_itemViews;
1032
1108
  if (!views) { views = this._sc_itemViews = []; }
@@ -1037,9 +1113,9 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1037
1113
  if (rebuild) {
1038
1114
  ret.destroy();
1039
1115
  ret = null;
1040
- } else {
1116
+ } else {
1041
1117
  return ret;
1042
- }
1118
+ }
1043
1119
  }
1044
1120
 
1045
1121
  var attrs,
@@ -1072,26 +1148,30 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1072
1148
  if (!ret.get('_isRendered')) {
1073
1149
  ret.invokeOnce(ret._doRender);
1074
1150
  }
1151
+ }
1075
1152
  }
1076
- }
1077
1153
 
1078
1154
  // If we weren't able to re-use a view, then create a new one.
1079
1155
  if (!ret) {
1080
1156
  ret = this.createItemView(exampleView, idx, attrs);
1081
1157
  containerView.insertBefore(ret, null); // Equivalent to 'append()', but avoids one more function call
1082
- }
1158
+ }
1083
1159
 
1084
1160
  views[idx] = ret;
1085
- return ret;
1161
+ return ret ;
1086
1162
  },
1087
1163
 
1088
1164
  /**
1089
- Helper method for getting the item view of a specific content object
1165
+ Convenience method for getting the item view of a content object.
1090
1166
 
1091
1167
  @param {Object} object
1092
1168
  */
1093
- itemViewForContentObject: function (object) {
1094
- return this.itemViewForContentIndex(this.get('content').indexOf(object));
1169
+ itemViewForContentObject: function(object) {
1170
+ var content = this.get('content');
1171
+ if (!content) return null;
1172
+ var contentIndex = content.indexOf(object);
1173
+ if (contentIndex === -1) return null;
1174
+ return this.itemViewForContentIndex(contentIndex);
1095
1175
  },
1096
1176
 
1097
1177
  /** @private */
@@ -1113,7 +1193,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1113
1193
  @param {Hash} attrs expected attributes
1114
1194
  @returns {SC.View} item view instance
1115
1195
  */
1116
- createItemView: function (exampleClass, idx, attrs) {
1196
+ createItemView: function(exampleClass, idx, attrs) {
1117
1197
  return exampleClass.create(attrs);
1118
1198
  },
1119
1199
 
@@ -1124,9 +1204,9 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1124
1204
  @param {Number} idx the content index
1125
1205
  @returns {String} layer id, must be suitable for use in HTML id attribute
1126
1206
  */
1127
- layerIdFor: function (idx) {
1207
+ layerIdFor: function(idx) {
1128
1208
  var ret = this._TMP_LAYERID;
1129
- ret[0] = SC.guidFor(this);
1209
+ ret[0] = this.get('layerId');
1130
1210
  ret[1] = idx;
1131
1211
  return ret.join('-');
1132
1212
  },
@@ -1137,16 +1217,15 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1137
1217
 
1138
1218
  @param {String} id the layer id
1139
1219
  */
1140
- contentIndexForLayerId: function (id) {
1141
- if (!id || !(id = id.toString())) return null; // nothing to do
1220
+ contentIndexForLayerId: function(id) {
1221
+ if (!id || !(id = id.toString())) return null ; // nothing to do
1142
1222
 
1143
- var base = this._baseLayerId;
1144
- if (!base) base = this._baseLayerId = SC.guidFor(this) + "-";
1223
+ var base = this.get('layerId') + '-';
1145
1224
 
1146
1225
  // no match
1147
- if ((id.length <= base.length) || (id.indexOf(base) !== 0)) return null;
1148
- var ret = Number(id.slice(id.lastIndexOf('-') + 1));
1149
- return isNaN(ret) ? null : ret;
1226
+ if ((id.length <= base.length) || (id.indexOf(base) !== 0)) return null ;
1227
+ var ret = Number(id.slice(id.lastIndexOf('-')+1));
1228
+ return isNaN(ret) ? null : ret ;
1150
1229
  },
1151
1230
 
1152
1231
 
@@ -1163,9 +1242,9 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1163
1242
  @param {SC.Event} evt An event
1164
1243
  @returns {SC.View} the item view or null
1165
1244
  */
1166
- itemViewForEvent: function (evt) {
1167
- var responder = this.getPath('pane.rootResponder');
1168
- if (!responder) return null; // fast path
1245
+ itemViewForEvent: function(evt) {
1246
+ var responder = this.getPath('pane.rootResponder') ;
1247
+ if (!responder) return null ; // fast path
1169
1248
 
1170
1249
  var element = evt.target,
1171
1250
  layer = this.get('layer'),
@@ -1175,15 +1254,15 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1175
1254
  // walk up the element hierarchy until we find this or an element with an
1176
1255
  // id matching the base guid (i.e. a collection item)
1177
1256
  while (element && element !== document && element !== layer) {
1178
- id = element ? SC.$(element).attr('id') : null;
1257
+ id = element ? SC.$(element).attr('id') : null ;
1179
1258
  if (id && (contentIndex = this.contentIndexForLayerId(id)) !== null) {
1180
- break;
1259
+ break;
1181
1260
  }
1182
- element = element.parentNode;
1261
+ element = element.parentNode ;
1183
1262
  }
1184
1263
 
1185
1264
  // no matching element found?
1186
- if (contentIndex === null || (element === layer)) {
1265
+ if (contentIndex===null || (element === layer)) {
1187
1266
  element = layer = null; // avoid memory leaks
1188
1267
  return null;
1189
1268
  }
@@ -1208,14 +1287,14 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1208
1287
  @param {SC.IndexSet} indexes the indexes to expand
1209
1288
  @returns {SC.CollectionView} receiver
1210
1289
  */
1211
- expand: function (indexes) {
1290
+ expand: function(indexes) {
1212
1291
  if (!indexes) return this; // nothing to do
1213
1292
  var del = this.get('contentDelegate'),
1214
1293
  content = this.get('content');
1215
1294
 
1216
- indexes.forEach(function (i) {
1295
+ indexes.forEach(function(i) {
1217
1296
  var state = del.contentIndexDisclosureState(this, content, i);
1218
- if (state === SC.BRANCH_CLOSED) del.contentIndexExpand(this, content, i);
1297
+ if (state === SC.BRANCH_CLOSED) del.contentIndexExpand(this,content,i);
1219
1298
  }, this);
1220
1299
  return this;
1221
1300
  },
@@ -1227,14 +1306,14 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1227
1306
  @param {SC.IndexSet} indexes the indexes to expand
1228
1307
  @returns {SC.CollectionView} receiver
1229
1308
  */
1230
- collapse: function (indexes) {
1309
+ collapse: function(indexes) {
1231
1310
  if (!indexes) return this; // nothing to do
1232
1311
  var del = this.get('contentDelegate'),
1233
1312
  content = this.get('content');
1234
1313
 
1235
- indexes.forEach(function (i) {
1314
+ indexes.forEach(function(i) {
1236
1315
  var state = del.contentIndexDisclosureState(this, content, i);
1237
- if (state === SC.BRANCH_OPEN) del.contentIndexCollapse(this, content, i);
1316
+ if (state === SC.BRANCH_OPEN) del.contentIndexCollapse(this,content,i);
1238
1317
  }, this);
1239
1318
  return this;
1240
1319
  },
@@ -1247,7 +1326,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1247
1326
  Called whenever the selection object is changed to a new value. Begins
1248
1327
  observing the selection for changes.
1249
1328
  */
1250
- _cv_selectionDidChange: function () {
1329
+ _cv_selectionDidChange: function() {
1251
1330
  var sel = this.get('selection'),
1252
1331
  last = this._cv_selection,
1253
1332
  func = this._cv_selectionContentDidChange;
@@ -1256,7 +1335,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1256
1335
  if (last) last.removeObserver('[]', this, func);
1257
1336
  if (sel) sel.addObserver('[]', this, func);
1258
1337
 
1259
- this._cv_selection = sel;
1338
+ this._cv_selection = sel ;
1260
1339
  this._cv_selectionContentDidChange();
1261
1340
  }.observes('selection'),
1262
1341
 
@@ -1264,11 +1343,11 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1264
1343
  Called whenever the selection object or its content changes. This will
1265
1344
  repaint any items that changed their selection state.
1266
1345
  */
1267
- _cv_selectionContentDidChange: function () {
1346
+ _cv_selectionContentDidChange: function() {
1268
1347
  var sel = this.get('selection'),
1269
1348
  last = this._cv_selindexes, // clone of last known indexes
1270
1349
  content = this.get('content'),
1271
- diff;
1350
+ diff ;
1272
1351
 
1273
1352
  // save new last
1274
1353
  this._cv_selindexes = sel ? sel.frozenCopy() : null;
@@ -1280,7 +1359,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1280
1359
  if (sel && last) diff = sel.without(last).add(last.without(sel));
1281
1360
  else diff = sel || last;
1282
1361
 
1283
- if (diff && diff.get('length') > 0) this.reloadSelectionIndexes(diff);
1362
+ if (diff && diff.get('length')>0) this.reloadSelectionIndexes(diff);
1284
1363
  },
1285
1364
 
1286
1365
  /** @private
@@ -1299,19 +1378,19 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1299
1378
  @param {SC.IndexSet} indexes affected indexes
1300
1379
  @returns {SC.CollectionView} receiver
1301
1380
  */
1302
- reloadSelectionIndexes: function (indexes) {
1303
- var invalid = this._invalidSelection;
1381
+ reloadSelectionIndexes: function(indexes) {
1382
+ var invalid = this._invalidSelection ;
1304
1383
  if (indexes && (invalid !== YES)) {
1305
- if (invalid) { invalid.add(indexes); }
1384
+ if (invalid) { invalid.add(indexes) ; }
1306
1385
  else { invalid = this._invalidSelection = indexes.copy(); }
1307
1386
 
1308
- } else this._invalidSelection = YES; // force a total reload
1387
+ } else this._invalidSelection = YES ; // force a total reload
1309
1388
 
1310
1389
  if (this.get('isVisibleInWindow')) {
1311
1390
  this.invokeOnce(this.reloadSelectionIndexesIfNeeded);
1312
1391
  }
1313
1392
 
1314
- return this;
1393
+ return this ;
1315
1394
  },
1316
1395
 
1317
1396
  /**
@@ -1326,9 +1405,9 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1326
1405
 
1327
1406
  @returns {SC.CollectionView} receiver
1328
1407
  */
1329
- reloadSelectionIndexesIfNeeded: function () {
1408
+ reloadSelectionIndexesIfNeeded: function() {
1330
1409
  var invalid = this._invalidSelection;
1331
- if (!invalid || !this.get('isVisibleInWindow')) return this;
1410
+ if (!invalid || !this.get('isVisibleInWindow')) return this ;
1332
1411
 
1333
1412
  var nowShowing = this.get('nowShowing'),
1334
1413
  reload = this._invalidIndexes,
@@ -1339,7 +1418,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1339
1418
 
1340
1419
  // fast path. if we are going to reload everything anyway, just forget
1341
1420
  // about it. Also if we don't have a nowShowing, nothing to do.
1342
- if (reload === YES || !nowShowing) return this;
1421
+ if (reload === YES || !nowShowing) return this ;
1343
1422
 
1344
1423
  // if invalid is YES instead of index set, just reload everything
1345
1424
  if (invalid === YES) invalid = nowShowing;
@@ -1348,13 +1427,13 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1348
1427
  if (reload && reload.isIndexSet) invalid = invalid.without(reload);
1349
1428
 
1350
1429
  // iterate through each item and set the isSelected state.
1351
- invalid.forEach(function (idx) {
1430
+ invalid.forEach(function(idx) {
1352
1431
  if (!nowShowing.contains(idx)) return; // not showing
1353
1432
  var view = this.itemViewForContentIndex(idx, NO);
1354
1433
  if (view) view.set('isSelected', sel ? sel.contains(content, idx) : NO);
1355
- }, this);
1434
+ },this);
1356
1435
 
1357
- return this;
1436
+ return this ;
1358
1437
  },
1359
1438
 
1360
1439
  /**
@@ -1367,7 +1446,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1367
1446
  @param extend {Boolean} optionally extend the selection
1368
1447
  @returns {SC.CollectionView} receiver
1369
1448
  */
1370
- select: function (indexes, extend) {
1449
+ select: function(indexes, extend) {
1371
1450
  var content = this.get('content'),
1372
1451
  del = this.get('selectionDelegate'),
1373
1452
  groupIndexes = this.get('_contentGroupIndexes'),
@@ -1381,16 +1460,16 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1381
1460
  }
1382
1461
 
1383
1462
  // if we are passed an empty index set or null, clear the selection.
1384
- if (indexes && indexes.get('length') > 0) {
1463
+ if (indexes && indexes.get('length')>0) {
1385
1464
 
1386
1465
  // first remove any group indexes - these can never be selected
1387
- if (groupIndexes && groupIndexes.get('length') > 0) {
1466
+ if (groupIndexes && groupIndexes.get('length')>0) {
1388
1467
  indexes = indexes.copy().remove(groupIndexes);
1389
1468
  }
1390
1469
 
1391
1470
  // give the delegate a chance to alter the items
1392
1471
  indexes = del.collectionViewShouldSelectIndexes(this, indexes, extend);
1393
- if (!indexes || indexes.get('length') === 0) return this; // nothing to do
1472
+ if (!indexes || indexes.get('length')===0) return this; // nothing to do
1394
1473
 
1395
1474
  } else indexes = null;
1396
1475
 
@@ -1398,10 +1477,10 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1398
1477
  if (extend && (sel = this.get('selection'))) sel = sel.copy();
1399
1478
  else sel = SC.SelectionSet.create();
1400
1479
 
1401
- if (indexes && indexes.get('length') > 0) {
1480
+ if (indexes && indexes.get('length')>0) {
1402
1481
 
1403
1482
  // when selecting only one item, always select by content
1404
- if (indexes.get('length') === 1) {
1483
+ if (indexes.get('length') === 1 && !this.get('allowDuplicateItems')) {
1405
1484
  sel.addObject(content.objectAt(indexes.get('firstObject')));
1406
1485
 
1407
1486
  // otherwise select an index range
@@ -1414,8 +1493,8 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1414
1493
  if (!sel) sel = SC.SelectionSet.create(); // empty
1415
1494
 
1416
1495
  // if we're not extending the selection, clear the selection anchor
1417
- this._selectionAnchor = null;
1418
- this.set('selection', sel.freeze());
1496
+ this._selectionAnchor = null ;
1497
+ this.set('selection', sel.freeze()) ;
1419
1498
  return this;
1420
1499
  },
1421
1500
 
@@ -1425,13 +1504,13 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1425
1504
  @param {Number|SC.IndexSet} indexes index or indexes to deselect
1426
1505
  @returns {SC.CollectionView} receiver
1427
1506
  */
1428
- deselect: function (indexes) {
1507
+ deselect: function(indexes) {
1429
1508
  var sel = this.get('selection'),
1430
1509
  content = this.get('content'),
1431
1510
  del = this.get('selectionDelegate');
1432
1511
 
1433
1512
  if (!this.get('isSelectable') || !this.get('isEnabledInPane')) return this;
1434
- if (!sel || sel.get('length') === 0) return this; // nothing to do
1513
+ if (!sel || sel.get('length')===0) return this; // nothing to do
1435
1514
 
1436
1515
  // normalize
1437
1516
  if (SC.typeOf(indexes) === SC.T_NUMBER) {
@@ -1439,16 +1518,16 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1439
1518
  }
1440
1519
 
1441
1520
  // give the delegate a chance to alter the items
1442
- indexes = del.collectionViewShouldDeselectIndexes(this, indexes);
1443
- if (!indexes || indexes.get('length') === 0) return this; // nothing to do
1521
+ indexes = del.collectionViewShouldDeselectIndexes(this, indexes) ;
1522
+ if (!indexes || indexes.get('length')===0) return this; // nothing to do
1444
1523
 
1445
1524
  // now merge change - note we expect sel && indexes to not be null
1446
1525
  sel = sel.copy().remove(content, indexes);
1447
1526
  sel = del.collectionViewSelectionForProposedSelection(this, sel);
1448
1527
  if (!sel) sel = SC.SelectionSet.create(); // empty
1449
1528
 
1450
- this.set('selection', sel.freeze());
1451
- return this;
1529
+ this.set('selection', sel.freeze()) ;
1530
+ return this ;
1452
1531
  },
1453
1532
 
1454
1533
  /** @private
@@ -1463,12 +1542,12 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1463
1542
  @param {Number} bottom optional bottom of selection use as fallback
1464
1543
  @returns {Number} next selectable index.
1465
1544
  */
1466
- _findNextSelectableItemFromIndex: function (proposedIndex, bottom) {
1545
+ _findNextSelectableItemFromIndex: function(proposedIndex, bottom) {
1467
1546
  var lim = this.get('length'),
1468
1547
  range = SC.IndexSet.create(),
1469
1548
  del = this.get('selectionDelegate'),
1470
1549
  groupIndexes = this.get('_contentGroupIndexes'),
1471
- ret, sel;
1550
+ ret, sel ;
1472
1551
 
1473
1552
  // fast path
1474
1553
  if (!groupIndexes && (del.collectionViewShouldSelectIndexes === this.collectionViewShouldSelectIndexes)) {
@@ -1482,7 +1561,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1482
1561
  if (!groupIndexes || !groupIndexes.contains(proposedIndex)) {
1483
1562
  range.add(proposedIndex);
1484
1563
  ret = del.collectionViewShouldSelectIndexes(this, range);
1485
- if (ret && ret.get('length') >= 1) return proposedIndex;
1564
+ if (ret && ret.get('length') >= 1) return proposedIndex ;
1486
1565
  range.remove(proposedIndex);
1487
1566
  }
1488
1567
  proposedIndex++;
@@ -1491,9 +1570,9 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1491
1570
  // if nothing was found, return top of selection
1492
1571
  if (bottom === undefined) {
1493
1572
  sel = this.get('selection');
1494
- bottom = sel ? sel.get('max') : -1;
1573
+ bottom = sel ? sel.get('max') : -1 ;
1495
1574
  }
1496
- return bottom;
1575
+ return bottom ;
1497
1576
  },
1498
1577
 
1499
1578
  /** @private
@@ -1504,11 +1583,11 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1504
1583
  @param {Integer} proposedIndex the desired index to select
1505
1584
  @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.
1506
1585
  */
1507
- _findPreviousSelectableItemFromIndex: function (proposedIndex, top) {
1586
+ _findPreviousSelectableItemFromIndex: function(proposedIndex, top) {
1508
1587
  var range = SC.IndexSet.create(),
1509
1588
  del = this.get('selectionDelegate'),
1510
1589
  groupIndexes = this.get('_contentGroupIndexes'),
1511
- ret;
1590
+ ret ;
1512
1591
 
1513
1592
  if (SC.none(proposedIndex)) proposedIndex = -1;
1514
1593
 
@@ -1524,7 +1603,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1524
1603
  if (!groupIndexes || !groupIndexes.contains(proposedIndex)) {
1525
1604
  range.add(proposedIndex);
1526
1605
  ret = del.collectionViewShouldSelectIndexes(this, range);
1527
- if (ret && ret.get('length') >= 1) return proposedIndex;
1606
+ if (ret && ret.get('length') >= 1) return proposedIndex ;
1528
1607
  range.remove(proposedIndex);
1529
1608
  }
1530
1609
  proposedIndex--;
@@ -1533,10 +1612,10 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1533
1612
  // if nothing was found, return top of selection
1534
1613
  if (top === undefined) {
1535
1614
  var sel = this.get('selection');
1536
- top = sel ? sel.get('min') : -1;
1615
+ top = sel ? sel.get('min') : -1 ;
1537
1616
  }
1538
1617
  if (SC.none(top)) top = -1;
1539
- return top;
1618
+ return top ;
1540
1619
  },
1541
1620
 
1542
1621
  /**
@@ -1552,7 +1631,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1552
1631
  selected. Defaults to 1
1553
1632
  @returns {SC.CollectionView} receiver
1554
1633
  */
1555
- selectPreviousItem: function (extend, numberOfItems) {
1634
+ selectPreviousItem: function(extend, numberOfItems) {
1556
1635
  if (SC.none(numberOfItems)) numberOfItems = 1;
1557
1636
  if (SC.none(extend)) extend = false;
1558
1637
 
@@ -1561,7 +1640,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1561
1640
  if (sel) sel = sel.indexSetForSource(content);
1562
1641
 
1563
1642
  var selTop = sel ? sel.get('min') : -1,
1564
- selBottom = sel ? sel.get('max') - 1 : -1,
1643
+ selBottom = sel ? sel.get('max')-1 : -1,
1565
1644
  anchor = this._selectionAnchor;
1566
1645
  if (SC.none(anchor)) anchor = selTop;
1567
1646
 
@@ -1570,7 +1649,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1570
1649
 
1571
1650
  // If the selBottom is after the anchor, then reduce the selection
1572
1651
  if (selBottom > anchor) {
1573
- selBottom = selBottom - numberOfItems;
1652
+ selBottom = selBottom - numberOfItems ;
1574
1653
 
1575
1654
  // otherwise, select the previous item from the top
1576
1655
  } else {
@@ -1578,29 +1657,29 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1578
1657
  }
1579
1658
 
1580
1659
  // Ensure we are not out of bounds
1581
- if (SC.none(selTop) || (selTop < 0)) selTop = 0;
1660
+ if (SC.none(selTop) || (selTop < 0)) selTop = 0 ;
1582
1661
  if (!content.objectAt(selTop)) selTop = sel ? sel.get('min') : -1;
1583
- if (selBottom < selTop) selBottom = selTop;
1662
+ if (selBottom < selTop) selBottom = selTop ;
1584
1663
 
1585
1664
  // if not extending, just select the item previous to the selTop
1586
1665
  } else {
1587
1666
  selTop = this._findPreviousSelectableItemFromIndex(selTop - numberOfItems);
1588
- if (SC.none(selTop) || (selTop < 0)) selTop = 0;
1667
+ if (SC.none(selTop) || (selTop < 0)) selTop = 0 ;
1589
1668
  if (!content.objectAt(selTop)) selTop = sel ? sel.get('min') : -1;
1590
- selBottom = selTop;
1591
- anchor = null;
1669
+ selBottom = selTop ;
1670
+ anchor = null ;
1592
1671
  }
1593
1672
 
1594
- var scrollToIndex = selTop;
1673
+ var scrollToIndex = selTop ;
1595
1674
 
1596
1675
  // now build new selection
1597
- sel = SC.IndexSet.create(selTop, selBottom + 1 - selTop);
1676
+ sel = SC.IndexSet.create(selTop, selBottom+1-selTop);
1598
1677
 
1599
1678
  // ensure that the item is visible and set the selection
1600
- this.scrollToContentIndex(scrollToIndex);
1601
- this.select(sel);
1602
- this._selectionAnchor = anchor;
1603
- return this;
1679
+ this.scrollToContentIndex(scrollToIndex) ;
1680
+ this.select(sel) ;
1681
+ this._selectionAnchor = anchor ;
1682
+ return this ;
1604
1683
  },
1605
1684
 
1606
1685
  /**
@@ -1615,16 +1694,16 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1615
1694
  selected. Defaults to 1.
1616
1695
  @returns {SC.CollectionView} receiver
1617
1696
  */
1618
- selectNextItem: function (extend, numberOfItems) {
1619
- if (SC.none(numberOfItems)) numberOfItems = 1;
1620
- if (SC.none(extend)) extend = false;
1697
+ selectNextItem: function(extend, numberOfItems) {
1698
+ if (SC.none(numberOfItems)) numberOfItems = 1 ;
1699
+ if (SC.none(extend)) extend = false ;
1621
1700
 
1622
1701
  var sel = this.get('selection'),
1623
1702
  content = this.get('content');
1624
1703
  if (sel) sel = sel.indexSetForSource(content);
1625
1704
 
1626
1705
  var selTop = sel ? sel.get('min') : -1,
1627
- selBottom = sel ? sel.get('max') - 1 : -1,
1706
+ selBottom = sel ? sel.get('max')-1 : -1,
1628
1707
  anchor = this._selectionAnchor,
1629
1708
  lim = this.get('length');
1630
1709
 
@@ -1635,7 +1714,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1635
1714
 
1636
1715
  // If the selTop is before the anchor, then reduce the selection
1637
1716
  if (selTop < anchor) {
1638
- selTop = selTop + numberOfItems;
1717
+ selTop = selTop + numberOfItems ;
1639
1718
 
1640
1719
  // otherwise, select the next item after the bottom
1641
1720
  } else {
@@ -1643,34 +1722,34 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1643
1722
  }
1644
1723
 
1645
1724
  // Ensure we are not out of bounds
1646
- if (selBottom >= lim) selBottom = lim - 1;
1725
+ if (selBottom >= lim) selBottom = lim-1;
1647
1726
 
1648
1727
  // we also need to check that the item exists
1649
1728
  if (!content.objectAt(selBottom)) selBottom = sel ? sel.get('max') - 1 : -1;
1650
1729
 
1651
1730
  // and if top has eclipsed bottom, handle that too.
1652
- if (selTop > selBottom) selTop = selBottom;
1731
+ if (selTop > selBottom) selTop = selBottom ;
1653
1732
 
1654
1733
  // if not extending, just select the item next to the selBottom
1655
1734
  } else {
1656
1735
  selBottom = this._findNextSelectableItemFromIndex(selBottom + numberOfItems, selBottom);
1657
1736
 
1658
- if (selBottom >= lim) selBottom = lim - 1;
1737
+ if (selBottom >= lim) selBottom = lim-1;
1659
1738
  if (!content.objectAt(selBottom)) selBottom = sel ? sel.get('max') - 1 : -1;
1660
- selTop = selBottom;
1661
- anchor = null;
1739
+ selTop = selBottom ;
1740
+ anchor = null ;
1662
1741
  }
1663
1742
 
1664
- var scrollToIndex = selBottom;
1743
+ var scrollToIndex = selBottom ;
1665
1744
 
1666
1745
  // now build new selection
1667
- sel = SC.IndexSet.create(selTop, selBottom - selTop + 1);
1746
+ sel = SC.IndexSet.create(selTop, selBottom-selTop+1);
1668
1747
 
1669
1748
  // ensure that the item is visible and set the selection
1670
- this.scrollToContentIndex(scrollToIndex);
1671
- this.select(sel);
1672
- this._selectionAnchor = anchor;
1673
- return this;
1749
+ this.scrollToContentIndex(scrollToIndex) ;
1750
+ this.select(sel) ;
1751
+ this._selectionAnchor = anchor ;
1752
+ return this ;
1674
1753
  },
1675
1754
 
1676
1755
  /**
@@ -1680,28 +1759,28 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1680
1759
 
1681
1760
  @returns {Boolean} YES if deletion is possible.
1682
1761
  */
1683
- deleteSelection: function () {
1762
+ deleteSelection: function() {
1684
1763
  // perform some basic checks...
1685
1764
  if (!this.get('isEditable') || !this.get('canDeleteContent')) return NO;
1686
1765
 
1687
1766
  var sel = this.get('selection'),
1688
1767
  content = this.get('content'),
1689
1768
  del = this.get('selectionDelegate'),
1690
- indexes = sel && content ? sel.indexSetForSource(content) : null;
1769
+ indexes = sel&&content ? sel.indexSetForSource(content) : null;
1691
1770
 
1692
- if (!content || !indexes || indexes.get('length') === 0) return NO;
1771
+ if (!content || !indexes || indexes.get('length') === 0) return NO ;
1693
1772
 
1694
1773
  // let the delegate decide what to actually delete. If this returns an
1695
1774
  // empty index set or null, just do nothing.
1696
1775
  indexes = del.collectionViewShouldDeleteIndexes(this, indexes);
1697
- if (!indexes || indexes.get('length') === 0) return NO;
1776
+ if (!indexes || indexes.get('length') === 0) return NO ;
1698
1777
 
1699
1778
  // now have the delegate (or us) perform the deletion. The default
1700
1779
  // delegate implementation just uses standard SC.Array methods to do the
1701
1780
  // right thing.
1702
1781
  del.collectionViewDeleteContent(this, this.get('content'), indexes);
1703
1782
 
1704
- return YES;
1783
+ return YES ;
1705
1784
  },
1706
1785
 
1707
1786
  // ..........................................................
@@ -1714,9 +1793,9 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1714
1793
  @param {Number} contentIndex The index of the item to scroll to
1715
1794
  @returns {SC.CollectionView} receiver
1716
1795
  */
1717
- scrollToContentIndex: function (contentIndex) {
1718
- var itemView = this.itemViewForContentIndex(contentIndex);
1719
- if (itemView) this.scrollToItemView(itemView);
1796
+ scrollToContentIndex: function(contentIndex) {
1797
+ var itemView = this.itemViewForContentIndex(contentIndex) ;
1798
+ if (itemView) this.scrollToItemView(itemView) ;
1720
1799
  return this;
1721
1800
  },
1722
1801
 
@@ -1727,9 +1806,9 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1727
1806
  @param {SC.View} view The item view to scroll to
1728
1807
  @returns {SC.CollectionView} receiver
1729
1808
  */
1730
- scrollToItemView: function (view) {
1809
+ scrollToItemView: function(view) {
1731
1810
  if (view) view.scrollToVisible();
1732
- return this;
1811
+ return this ;
1733
1812
  },
1734
1813
 
1735
1814
  // ..........................................................
@@ -1737,84 +1816,92 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1737
1816
  //
1738
1817
 
1739
1818
  /** @private */
1740
- keyDown: function (evt) {
1741
- var ret = this.interpretKeyEvents(evt);
1742
- return !ret ? NO : ret;
1819
+ keyDown: function(evt) {
1820
+ var ret = this.interpretKeyEvents(evt) ;
1821
+ return !ret ? NO : ret ;
1743
1822
  },
1744
1823
 
1745
1824
  /** @private */
1746
- keyUp: function () { return true; },
1825
+ keyUp: function() { return true; },
1747
1826
 
1748
1827
  /** @private
1749
1828
  Handle space key event. Do action
1750
1829
  */
1751
- insertText: function (chr, evt) {
1830
+ insertText: function(chr, evt) {
1752
1831
  if (chr === ' ') {
1753
1832
  var sel = this.get('selection');
1754
- if (sel && sel.get('length') > 0) {
1833
+ if (sel && sel.get('length')>0) {
1755
1834
  this.invokeLater(this._cv_action, 0, null, evt);
1756
1835
  }
1757
- return YES;
1758
- } else return NO;
1836
+ return YES ;
1837
+ } else return NO ;
1759
1838
  },
1760
1839
 
1761
1840
  /** @private
1762
1841
  Handle select all keyboard event.
1763
1842
  */
1764
- selectAll: function (evt) {
1843
+ selectAll: function(evt) {
1765
1844
  var content = this.get('content'),
1766
- sel = content ? SC.IndexSet.create(0, content.get('length')) : null;
1767
- this.select(sel, NO);
1768
- return YES;
1845
+ del = this.delegateFor('allowsMultipleSelection', this.get('delegate'), content);
1846
+
1847
+ if (del && del.get('allowsMultipleSelection')) {
1848
+ var sel = content ? SC.IndexSet.create(0, content.get('length')) : null;
1849
+ this.select(sel, NO) ;
1850
+ }
1851
+ return YES ;
1769
1852
  },
1770
1853
 
1771
1854
  /** @private
1772
1855
  Remove selection of any selected items.
1773
1856
  */
1774
- deselectAll: function () {
1857
+ deselectAll: function() {
1775
1858
  var content = this.get('content'),
1776
- sel = content ? SC.IndexSet.create(0, content.get('length')) : null;
1777
- this.deselect(sel, NO);
1778
- return YES;
1859
+ del = this.delegateFor('allowsEmptySelection', this.get('delegate'), content);
1860
+
1861
+ if (del && del.get('allowsEmptySelection')) {
1862
+ var sel = content ? SC.IndexSet.create(0, content.get('length')) : null;
1863
+ this.deselect(sel, NO) ;
1864
+ }
1865
+ return YES ;
1779
1866
  },
1780
1867
 
1781
1868
  /** @private
1782
1869
  Handle delete keyboard event.
1783
1870
  */
1784
- deleteBackward: function (evt) {
1785
- return this.deleteSelection();
1871
+ deleteBackward: function(evt) {
1872
+ return this.deleteSelection() ;
1786
1873
  },
1787
1874
 
1788
1875
  /** @private
1789
1876
  Handle delete keyboard event.
1790
1877
  */
1791
- deleteForward: function (evt) {
1792
- return this.deleteSelection();
1878
+ deleteForward: function(evt) {
1879
+ return this.deleteSelection() ;
1793
1880
  },
1794
1881
 
1795
1882
  /** @private
1796
1883
  Selects the same item on the next row or moves down one if itemsPerRow = 1
1797
1884
  */
1798
- moveDown: function (sender, evt) {
1799
- this.selectNextItem(false, this.get('itemsPerRow') || 1);
1885
+ moveDown: function(sender, evt) {
1886
+ this.selectNextItem(false, this.get('itemsPerRow') || 1) ;
1800
1887
  this._cv_performSelectAction(null, evt, this.ACTION_DELAY);
1801
- return true;
1888
+ return true ;
1802
1889
  },
1803
1890
 
1804
1891
  /** @private
1805
1892
  Selects the same item on the next row or moves up one if itemsPerRow = 1
1806
1893
  */
1807
- moveUp: function (sender, evt) {
1808
- this.selectPreviousItem(false, this.get('itemsPerRow') || 1);
1894
+ moveUp: function(sender, evt) {
1895
+ this.selectPreviousItem(false, this.get('itemsPerRow') || 1) ;
1809
1896
  this._cv_performSelectAction(null, evt, this.ACTION_DELAY);
1810
- return true;
1897
+ return true ;
1811
1898
  },
1812
1899
 
1813
1900
  /** @private
1814
1901
  Selects the previous item if itemsPerRow > 1. Otherwise does nothing.
1815
1902
  If item is expandable, will collapse.
1816
1903
  */
1817
- moveLeft: function (evt) {
1904
+ moveLeft: function(evt) {
1818
1905
  // If the control key is down, this may be a browser shortcut and
1819
1906
  // we should not handle the arrow key.
1820
1907
  if (evt.ctrlKey || evt.metaKey) return NO;
@@ -1832,19 +1919,19 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1832
1919
  // one item selected and the item is already collapsed or is a leaf
1833
1920
  // node, then select the (expanded) parent element instead as a
1834
1921
  // convenience to the user.
1835
- if (indexes) {
1922
+ if ( indexes ) {
1836
1923
  var del, // We'll load it lazily
1837
1924
  selectParent = false,
1838
1925
  index;
1839
1926
 
1840
- if (indexes.get('length') === 1) {
1927
+ if ( indexes.get('length') === 1 ) {
1841
1928
  index = indexes.get('firstObject');
1842
1929
  del = this.get('contentDelegate');
1843
1930
  var state = del.contentIndexDisclosureState(this, content, index);
1844
1931
  if (state !== SC.BRANCH_OPEN) selectParent = true;
1845
1932
  }
1846
1933
 
1847
- if (selectParent) {
1934
+ if ( selectParent ) {
1848
1935
  // TODO: PERFORMANCE: It would be great to have a function like
1849
1936
  // SC.CollectionView.selectParentItem() or something similar
1850
1937
  // for performance reasons. But since we don't currently
@@ -1852,20 +1939,20 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1852
1939
  // previous items until we find the first one with a outline
1853
1940
  // level of one less than the selected item.
1854
1941
  var desiredOutlineLevel = del.contentIndexOutlineLevel(this, content, index) - 1;
1855
- if (desiredOutlineLevel >= 0) {
1942
+ if ( desiredOutlineLevel >= 0 ) {
1856
1943
  var parentIndex = -1;
1857
- while (parentIndex < 0) {
1944
+ while ( parentIndex < 0 ) {
1858
1945
  var previousItemIndex = this._findPreviousSelectableItemFromIndex(index - 1);
1859
- if (previousItemIndex < 0) return false; // Sanity-check.
1946
+ if (previousItemIndex < 0 ) return false; // Sanity-check.
1860
1947
  index = previousItemIndex;
1861
1948
  var outlineLevel = del.contentIndexOutlineLevel(this, content, index);
1862
- if (outlineLevel === desiredOutlineLevel) {
1949
+ if ( outlineLevel === desiredOutlineLevel ) {
1863
1950
  parentIndex = previousItemIndex;
1864
1951
  }
1865
1952
  }
1866
1953
 
1867
1954
  // If we found the parent, select it now.
1868
- if (parentIndex !== -1) {
1955
+ if ( parentIndex !== -1 ) {
1869
1956
  this.select(index);
1870
1957
  }
1871
1958
  }
@@ -1876,19 +1963,19 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1876
1963
  }
1877
1964
  }
1878
1965
 
1879
- return true;
1966
+ return true ;
1880
1967
  },
1881
1968
 
1882
1969
  /** @private
1883
1970
  Selects the next item if itemsPerRow > 1. Otherwise does nothing.
1884
1971
  */
1885
- moveRight: function (evt) {
1972
+ moveRight: function(evt) {
1886
1973
  // If the control key is down, this may be a browser shortcut and
1887
1974
  // we should not handle the arrow key.
1888
1975
  if (evt.ctrlKey || evt.metaKey) return NO;
1889
1976
 
1890
1977
  if ((this.get('itemsPerRow') || 1) > 1) {
1891
- this.selectNextItem(false, 1);
1978
+ this.selectNextItem(false, 1) ;
1892
1979
  this._cv_performSelectAction(null, evt, this.ACTION_DELAY);
1893
1980
  } else {
1894
1981
  var sel = this.get('selection'),
@@ -1897,96 +1984,104 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
1897
1984
  if (indexes) this.expand(indexes);
1898
1985
  }
1899
1986
 
1900
- return true;
1987
+ return true ;
1901
1988
  },
1902
1989
 
1903
1990
  /** @private */
1904
- moveDownAndModifySelection: function (sender, evt) {
1905
- this.selectNextItem(true, this.get('itemsPerRow') || 1);
1991
+ moveDownAndModifySelection: function(sender, evt) {
1992
+ var content = this.get('content'),
1993
+ del = this.delegateFor('allowsMultipleSelection', this.get('delegate'), content);
1994
+
1995
+ if (del && del.get('allowsMultipleSelection')) {
1996
+ this.selectNextItem(true, this.get('itemsPerRow') || 1) ;
1906
1997
  this._cv_performSelectAction(null, evt, this.ACTION_DELAY);
1907
- return true;
1998
+ }
1999
+ return true ;
1908
2000
  },
1909
2001
 
1910
2002
  /** @private */
1911
- moveUpAndModifySelection: function (sender, evt) {
1912
- this.selectPreviousItem(true, this.get('itemsPerRow') || 1);
2003
+ moveUpAndModifySelection: function(sender, evt) {
2004
+ var content = this.get('content'),
2005
+ del = this.delegateFor('allowsMultipleSelection', this.get('delegate'), content);
2006
+
2007
+ if (del && del.get('allowsMultipleSelection')) {
2008
+ this.selectPreviousItem(true, this.get('itemsPerRow') || 1) ;
1913
2009
  this._cv_performSelectAction(null, evt, this.ACTION_DELAY);
1914
- return true;
2010
+ }
2011
+ return true ;
1915
2012
  },
1916
2013
 
1917
2014
  /** @private
1918
2015
  Selects the previous item if itemsPerRow > 1. Otherwise does nothing.
1919
2016
  */
1920
- moveLeftAndModifySelection: function (sender, evt) {
2017
+ moveLeftAndModifySelection: function(sender, evt) {
1921
2018
  if ((this.get('itemsPerRow') || 1) > 1) {
1922
- this.selectPreviousItem(true, 1);
2019
+ this.selectPreviousItem(true, 1) ;
1923
2020
  this._cv_performSelectAction(null, evt, this.ACTION_DELAY);
1924
2021
  }
1925
- return true;
2022
+ return true ;
1926
2023
  },
1927
2024
 
1928
2025
  /** @private
1929
2026
  Selects the next item if itemsPerRow > 1. Otherwise does nothing.
1930
2027
  */
1931
- moveRightAndModifySelection: function (sender, evt) {
2028
+ moveRightAndModifySelection: function(sender, evt) {
1932
2029
  if ((this.get('itemsPerRow') || 1) > 1) {
1933
- this.selectNextItem(true, 1);
2030
+ this.selectNextItem(true, 1) ;
1934
2031
  this._cv_performSelectAction(null, evt, this.ACTION_DELAY);
1935
2032
  }
1936
- return true;
2033
+ return true ;
1937
2034
  },
1938
2035
 
1939
2036
  /** @private
1940
2037
  if content value is editable and we have one item selected, then edit.
1941
2038
  otherwise, invoke action.
1942
2039
  */
1943
- insertNewline: function (sender, evt) {
1944
- var canEdit = this.get('isEditable') && this.get('canEditContent'),
2040
+ insertNewline: function(sender, evt) {
2041
+ var wantsEdit = this.get('isEditable') && this.get('canEditContent'),
2042
+ canEdit = false,
1945
2043
  sel, content, set, idx, itemView;
1946
2044
 
1947
- // first make sure we have a single item selected; get idx
1948
- if (canEdit) {
1949
- sel = this.get('selection');
2045
+ // Make sure we have a single item selected and the item view supports beginEditing
2046
+ if (wantsEdit) {
2047
+ sel = this.get('selection') ;
1950
2048
  content = this.get('content');
2049
+
1951
2050
  if (sel && sel.get('length') === 1) {
1952
2051
  set = sel.indexSetForSource(content);
1953
2052
  idx = set ? set.get('min') : -1;
1954
- canEdit = idx >= 0;
1955
- }
1956
- }
1957
2053
 
1958
2054
  // next find itemView and ensure it supports editing
1959
- if (canEdit) {
1960
2055
  itemView = this.itemViewForContentIndex(idx);
1961
- canEdit = itemView && SC.typeOf(itemView.beginEditing) === SC.T_FUNCTION;
2056
+ canEdit = itemView && SC.typeOf(itemView.beginEditing)===SC.T_FUNCTION;
2057
+ }
1962
2058
  }
1963
2059
 
1964
2060
  // ok, we can edit..
1965
2061
  if (canEdit) {
1966
2062
  this.scrollToContentIndex(idx);
1967
- itemView = this.itemViewForContentIndex(idx); // just in case
1968
2063
  itemView.beginEditing();
1969
2064
 
1970
2065
  // invoke action
1971
2066
  } else {
1972
- this.invokeLater(this._cv_action, 0, itemView, null);
2067
+ this.invokeLater(this._cv_action, 0, itemView, null) ;
1973
2068
  }
1974
2069
 
1975
- return YES; // always handle
2070
+ return YES ; // always handle
1976
2071
  },
1977
2072
 
1978
- insertTab: function (evt) {
2073
+ insertTab: function(evt) {
1979
2074
  var view = this.get('nextValidKeyView');
1980
2075
  if (view) view.becomeFirstResponder();
1981
2076
  else evt.allowDefault();
1982
- return YES; // handled
2077
+ return YES ; // handled
1983
2078
  },
1984
2079
 
1985
- insertBacktab: function (evt) {
2080
+ insertBacktab: function(evt) {
1986
2081
  var view = this.get('previousValidKeyView');
1987
2082
  if (view) view.becomeFirstResponder();
1988
2083
  else evt.allowDefault();
1989
- return YES; // handled
2084
+ return YES ; // handled
1990
2085
  },
1991
2086
 
1992
2087
  // ..........................................................
@@ -2004,204 +2099,241 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2004
2099
  @param ev {Event} the mouse down event
2005
2100
  @returns {Boolean} Usually YES.
2006
2101
  */
2007
- mouseDown: function (ev) {
2008
- var content = this.get('content');
2102
+ mouseDown: function(ev) {
2103
+ var isEnabledInPane = this.get('isEnabledInPane'),
2104
+ isSelectable = this.get('isSelectable'),
2105
+ handled = false;
2009
2106
 
2010
- // Fast path!
2011
- if (!content) return this.get('isSelectable');
2107
+ // If enabled and selectable, handle the event.
2108
+ if (isEnabledInPane && isSelectable) {
2109
+ var content = this.get('content');
2012
2110
 
2013
- var itemView = this.itemViewForEvent(ev),
2014
- contentIndex = itemView ? itemView.get('contentIndex') : -1,
2015
- info, anchor, sel, isSelected, modifierKeyPressed, didSelect = NO,
2016
- allowsMultipleSel = content.get('allowsMultipleSelection');
2111
+ handled = true;
2017
2112
 
2018
- if (!this.get('isEnabledInPane')) return contentIndex > -1;
2113
+ if (content) {
2114
+ var itemView = this.itemViewForEvent(ev),
2115
+ allowsMultipleSel = content.get('allowsMultipleSelection'),
2116
+ didSelect = false,
2117
+ sel, isSelected,
2118
+ contentIndex;
2019
2119
 
2020
- if (!this.get('isSelectable')) return NO;
2120
+ // Ensure that the view is first responder if possible.
2121
+ this.becomeFirstResponder();
2021
2122
 
2022
- // become first responder if possible.
2023
- this.becomeFirstResponder();
2123
+ // Determine the content index of the item view.
2124
+ contentIndex = itemView ? itemView.get('contentIndex') : -1;
2024
2125
 
2025
- // Toggle the selection if selectOnMouseDown is true
2026
- if (this.get('useToggleSelection')) {
2027
- if (this.get('selectOnMouseDown')) {
2028
- if (!itemView) return; // do nothing when clicked outside of elements
2126
+ // Toggle the selection if useToggleSelection is true.
2127
+ if (this.get('useToggleSelection')) {
2029
2128
 
2030
- // determine if item is selected. If so, then go on.
2031
- sel = this.get('selection');
2032
- isSelected = sel && sel.containsObject(itemView.get('content'));
2129
+ if (this.get('selectOnMouseDown') && itemView) {
2130
+ // Determine if item is selected. If so, then go on.
2131
+ sel = this.get('selection');
2132
+
2133
+ isSelected = sel && sel.contains(content, contentIndex, 1);
2134
+ if (isSelected) {
2135
+ this.deselect(contentIndex);
2136
+ } else if (!allowsMultipleSel) {
2137
+ this.select(contentIndex, false);
2138
+ didSelect = true;
2139
+ } else {
2140
+ this.select(contentIndex, true);
2141
+ didSelect = true;
2142
+ }
2143
+ }
2033
2144
 
2034
- if (isSelected) {
2035
- this.deselect(contentIndex);
2036
- } else if (!allowsMultipleSel) {
2037
- this.select(contentIndex, NO);
2038
- didSelect = YES;
2145
+ // Normal selection behavior.
2039
2146
  } else {
2040
- this.select(contentIndex, YES);
2041
- didSelect = YES;
2147
+ var info, anchor, modifierKeyPressed;
2148
+
2149
+ // Received a mouseDown on the view, but not on one of the item views.
2150
+ if (!itemView) {
2151
+ // Deselect all.
2152
+ if (this.get('allowDeselectAll')) this.select(null, false);
2153
+
2154
+ } else {
2155
+ // Collect some basic setup info.
2156
+ sel = this.get('selection');
2157
+ if (sel) sel = sel.indexSetForSource(content);
2158
+
2159
+ info = this.mouseDownInfo = {
2160
+ event: ev,
2161
+ itemView: itemView,
2162
+ contentIndex: contentIndex,
2163
+ at: Date.now()
2164
+ };
2165
+
2166
+ isSelected = sel ? sel.contains(contentIndex) : NO;
2167
+ info.modifierKeyPressed = modifierKeyPressed = ev.ctrlKey || ev.metaKey;
2168
+
2169
+
2170
+ // holding down a modifier key while clicking a selected item should
2171
+ // deselect that item...deselect and bail.
2172
+ if (modifierKeyPressed && isSelected) {
2173
+ info.shouldDeselect = contentIndex >= 0;
2174
+
2175
+ // if the shiftKey was pressed, then we want to extend the selection
2176
+ // from the last selected item
2177
+ } else if (ev.shiftKey && sel && sel.get('length') > 0 && allowsMultipleSel) {
2178
+ sel = this._findSelectionExtendedByShift(sel, contentIndex);
2179
+ anchor = this._selectionAnchor;
2180
+ this.select(sel) ;
2181
+ didSelect = true;
2182
+ this._selectionAnchor = anchor; //save the anchor
2183
+
2184
+ // If no modifier key was pressed, then clicking on the selected item
2185
+ // should clear the selection and reselect only the clicked on item.
2186
+ } else if (!modifierKeyPressed && isSelected) {
2187
+ info.shouldReselect = contentIndex >= 0;
2188
+
2189
+ // Otherwise, if selecting on mouse down, simply select the clicked on
2190
+ // item, adding it to the current selection if a modifier key was pressed.
2191
+ } else {
2192
+
2193
+ if ((ev.shiftKey || modifierKeyPressed) && !allowsMultipleSel) {
2194
+ this.select(null, false);
2195
+ didSelect = true;
2196
+ }
2197
+
2198
+ if (this.get("selectOnMouseDown")) {
2199
+ this.select(contentIndex, modifierKeyPressed);
2200
+ didSelect = true;
2201
+ } else {
2202
+ info.shouldSelect = contentIndex >= 0;
2203
+ }
2204
+ }
2205
+
2206
+ // saved for extend by shift ops.
2207
+ info.previousContentIndex = contentIndex;
2208
+ }
2042
2209
  }
2043
2210
 
2211
+ // Trigger select action if select occurred.
2044
2212
  if (didSelect && this.get('actOnSelect')) {
2045
- // handle actions on editing
2046
2213
  this._cv_performSelectAction(itemView, ev);
2047
2214
  }
2048
2215
  }
2049
-
2050
- return YES;
2051
- }
2052
-
2053
- // received a mouseDown on the collection element, but not on one of the
2054
- // childItems... unless we do not allow empty selections, set it to empty.
2055
- if (!itemView) {
2056
- if (this.get('allowDeselectAll')) this.select(null, false);
2057
- return YES;
2058
2216
  }
2059
2217
 
2060
- // collection some basic setup info
2061
- sel = this.get('selection');
2062
- if (sel) sel = sel.indexSetForSource(content);
2063
-
2064
- info = this.mouseDownInfo = {
2065
- event: ev,
2066
- itemView: itemView,
2067
- contentIndex: contentIndex,
2068
- at: Date.now()
2069
- };
2070
-
2071
- isSelected = sel ? sel.contains(contentIndex) : NO;
2072
- info.modifierKeyPressed = modifierKeyPressed = ev.ctrlKey || ev.metaKey;
2073
-
2074
-
2075
- // holding down a modifier key while clicking a selected item should
2076
- // deselect that item...deselect and bail.
2077
- if (modifierKeyPressed && isSelected) {
2078
- info.shouldDeselect = contentIndex >= 0;
2079
-
2080
- // if the shiftKey was pressed, then we want to extend the selection
2081
- // from the last selected item
2082
- } else if (ev.shiftKey && sel && sel.get('length') > 0 && allowsMultipleSel) {
2083
- sel = this._findSelectionExtendedByShift(sel, contentIndex);
2084
- anchor = this._selectionAnchor;
2085
- this.select(sel);
2086
- this._selectionAnchor = anchor; //save the anchor
2087
-
2088
- // If no modifier key was pressed, then clicking on the selected item
2089
- // should clear the selection and reselect only the clicked on item.
2090
- } else if (!modifierKeyPressed && isSelected) {
2091
- info.shouldReselect = contentIndex >= 0;
2092
-
2093
- // Otherwise, if selecting on mouse down, simply select the clicked on
2094
- // item, adding it to the current selection if a modifier key was pressed.
2095
- } else {
2096
-
2097
- if ((ev.shiftKey || modifierKeyPressed) && !allowsMultipleSel) {
2098
- this.select(null, false);
2099
- }
2100
-
2101
- if (this.get("selectOnMouseDown")) {
2102
- this.select(contentIndex, modifierKeyPressed);
2103
- } else {
2104
- info.shouldSelect = contentIndex >= 0;
2105
- }
2218
+ if (handled) {
2219
+ // Track that mouse is down.
2220
+ this._sc_isMouseDown = true;
2106
2221
  }
2107
2222
 
2108
- // saved for extend by shift ops.
2109
- info.previousContentIndex = contentIndex;
2110
-
2111
- return YES;
2223
+ return handled;
2112
2224
  },
2113
2225
 
2114
2226
  /** @private */
2115
- mouseUp: function (ev) {
2116
- var view = this.itemViewForEvent(ev),
2117
- info = this.mouseDownInfo,
2118
- content = this.get('content');
2119
-
2120
- // Fast path!
2121
- if (!content) {
2122
- this._cleanupMouseDown();
2123
- return true;
2124
- }
2125
-
2126
- var contentIndex = view ? view.get('contentIndex') : -1,
2127
- sel, isSelected, canEdit, itemView, idx,
2128
- allowsMultipleSel = content.get('allowsMultipleSelection');
2129
-
2130
- if (!this.get('isEnabledInPane')) return contentIndex > -1;
2131
- if (!this.get('isSelectable')) return NO;
2227
+ mouseUp: function(ev) {
2228
+ var isEnabledInPane = this.get('isEnabledInPane'),
2229
+ isSelectable = this.get('isSelectable');
2230
+
2231
+ // If enabled and selectable, handle the event.
2232
+ if (isEnabledInPane && isSelectable) {
2233
+ var content = this.get('content');
2234
+
2235
+ if (content) {
2236
+ var itemView = this.itemViewForEvent(ev),
2237
+ info = this.mouseDownInfo,
2238
+ didSelect = false,
2239
+ sel, isSelected,
2240
+ contentIndex;
2241
+
2242
+ // Determine the content index of the item view.
2243
+ contentIndex = itemView ? itemView.get('contentIndex') : -1;
2132
2244
 
2133
- if (this.get('useToggleSelection')) {
2134
- // Return if clicked outside of elements or if toggle was handled by mouseDown
2135
- if (!view || this.get('selectOnMouseDown')) return NO;
2245
+ // Toggle the selection if useToggleSelection is true.
2246
+ if (this.get('useToggleSelection')) {
2247
+ // If the toggle wasn't done on mouse down, handle it now.
2248
+ if (!this.get('selectOnMouseDown') && itemView) {
2249
+ var allowsMultipleSel = content.get('allowsMultipleSelection');
2250
+
2251
+ // determine if item is selected. If so, then go on.
2252
+ sel = this.get('selection') ;
2253
+ isSelected = sel && sel.contains(content, contentIndex, 1);
2254
+
2255
+ if (isSelected) {
2256
+ this.deselect(contentIndex) ;
2257
+ } else if (!allowsMultipleSel) {
2258
+ this.select(contentIndex, false);
2259
+ didSelect = true;
2260
+ } else {
2261
+ this.select(contentIndex, true);
2262
+ didSelect = true;
2263
+ }
2264
+ }
2136
2265
 
2137
- // determine if item is selected. If so, then go on.
2138
- sel = this.get('selection');
2139
- isSelected = sel && sel.containsObject(view.get('content'));
2266
+ } else if (info) {
2267
+ var idx = info.contentIndex;
2140
2268
 
2141
- if (isSelected) {
2142
- this.deselect(contentIndex);
2143
- } else if (!allowsMultipleSel) {
2144
- this.select(contentIndex, NO);
2145
- } else {
2146
- this.select(contentIndex, YES);
2147
- }
2269
+ // this will be set if the user simply clicked on an unselected item and
2270
+ // selectOnMouseDown was NO.
2271
+ if (info.shouldSelect) {
2272
+ this.select(idx, info.modifierKeyPressed);
2273
+ didSelect = true;
2274
+ }
2148
2275
 
2149
- } else if (info) {
2150
- idx = info.contentIndex;
2151
- contentIndex = (view) ? view.get('contentIndex') : -1;
2276
+ // This is true if the user clicked on a selected item with a modifier
2277
+ // key pressed.
2278
+ if (info.shouldDeselect) this.deselect(idx);
2152
2279
 
2153
- // this will be set if the user simply clicked on an unselected item and
2154
- // selectOnMouseDown was NO.
2155
- if (info.shouldSelect) this.select(idx, info.modifierKeyPressed);
2280
+ // This is true if the user clicked on a selected item without a
2281
+ // modifier-key pressed. When this happens we try to begin editing
2282
+ // on the content. If that is not allowed, then simply clear the
2283
+ // selection and reselect the clicked on item.
2284
+ if (info.shouldReselect) {
2156
2285
 
2157
- // This is true if the user clicked on a selected item with a modifier
2158
- // key pressed.
2159
- if (info.shouldDeselect) this.deselect(idx);
2286
+ // - contentValueIsEditable is true
2287
+ var canEdit = this.get('isEditable') && this.get('canEditContent') ;
2160
2288
 
2161
- // This is true if the user clicked on a selected item without a
2162
- // modifier-key pressed. When this happens we try to begin editing
2163
- // on the content. If that is not allowed, then simply clear the
2164
- // selection and reselect the clicked on item.
2165
- if (info.shouldReselect) {
2289
+ // - the user clicked on an item that was already selected
2290
+ // ^ this is the only way shouldReset is set to YES
2166
2291
 
2167
- // - contentValueIsEditable is true
2168
- canEdit = this.get('isEditable') && this.get('canEditContent');
2292
+ // - is the only item selected
2293
+ if (canEdit) {
2294
+ sel = this.get('selection') ;
2295
+ canEdit = sel && (sel.get('length') === 1);
2296
+ }
2169
2297
 
2170
- // - the user clicked on an item that was already selected
2171
- // ^ this is the only way shouldReset is set to YES
2298
+ // - the item view responds to contentHitTest() and returns YES.
2299
+ // - the item view responds to beginEditing and returns YES.
2300
+ if (canEdit) {
2301
+ itemView = this.itemViewForContentIndex(idx) ;
2302
+ canEdit = itemView && (!itemView.contentHitTest || itemView.contentHitTest(ev)) ;
2303
+ canEdit = (canEdit && itemView.beginEditing) ? itemView.beginEditing() : NO ;
2304
+ }
2172
2305
 
2173
- // - is the only item selected
2174
- if (canEdit) {
2175
- sel = this.get('selection');
2176
- canEdit = sel && (sel.get('length') === 1);
2177
- }
2306
+ // if cannot edit, schedule a reselect (but give doubleClick a chance)
2307
+ if (!canEdit) {
2308
+ if (this._cv_reselectTimer) this._cv_reselectTimer.invalidate() ;
2309
+ this._cv_reselectTimer = this.invokeLater(this.select, 300, idx, false) ;
2310
+ }
2311
+ }
2178
2312
 
2179
- // - the item view responds to contentHitTest() and returns YES.
2180
- // - the item view responds to beginEditing and returns YES.
2181
- if (canEdit) {
2182
- itemView = this.itemViewForContentIndex(idx);
2183
- canEdit = itemView && (!itemView.contentHitTest || itemView.contentHitTest(ev));
2184
- canEdit = (canEdit && itemView.beginEditing) ? itemView.beginEditing() : NO;
2313
+ // Clean up.
2314
+ this._cleanupMouseDown();
2185
2315
  }
2186
2316
 
2187
- // if cannot edit, schedule a reselect (but give doubleClick a chance)
2188
- if (!canEdit) {
2189
- if (this._cv_reselectTimer) this._cv_reselectTimer.invalidate();
2190
- this._cv_reselectTimer = this.invokeLater(this.select, 300, idx, false);
2317
+ // Trigger select action if select occurred.
2318
+ if (didSelect && this.get('actOnSelect')) {
2319
+ this._cv_performSelectAction(itemView, ev);
2191
2320
  }
2192
2321
  }
2193
2322
 
2194
- this._cleanupMouseDown();
2323
+ // To avoid annoying jitter from Magic Mouse (which sends mousewheel events while trying
2324
+ // to lift your finger after a drag), capture mousewheel events for a small period of time.
2325
+ this._sc_isMouseJustDown = true;
2326
+ this._sc_clearMouseJustDownTimer = this.invokeLater(this._sc_clearMouseJustDown, 250);
2195
2327
  }
2196
2328
 
2197
- // handle actions on editing
2198
- this._cv_performSelectAction(view, ev, 0, ev.clickCount);
2329
+ // Track that mouse is up no matter what (e.g. mouse went down and then view was disabled before mouse up).
2330
+ this._sc_isMouseDown = false;
2199
2331
 
2200
- return NO; // bubble event to allow didDoubleClick to be called...
2332
+ return false; // Bubble event to allow didDoubleClick to be called...
2201
2333
  },
2202
2334
 
2203
2335
  /** @private */
2204
- _cleanupMouseDown: function () {
2336
+ _cleanupMouseDown: function() {
2205
2337
 
2206
2338
  // delete items explicitly to avoid leaks on IE
2207
2339
  var info = this.mouseDownInfo, key;
@@ -2215,27 +2347,35 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2215
2347
  },
2216
2348
 
2217
2349
  /** @private */
2218
- mouseMoved: function (ev) {
2350
+ mouseMoved: function(ev) {
2219
2351
  var view = this.itemViewForEvent(ev),
2220
- last = this._lastHoveredItem;
2352
+ last = this._lastHoveredItem ;
2221
2353
 
2222
2354
  // handle hover events.
2223
2355
  if (view !== last) {
2224
2356
  if (last && last.mouseExited) last.mouseExited(ev);
2225
2357
  if (view && view.mouseEntered) view.mouseEntered(ev);
2226
2358
  }
2227
- this._lastHoveredItem = view;
2359
+ this._lastHoveredItem = view ;
2228
2360
 
2229
2361
  if (view && view.mouseMoved) view.mouseMoved(ev);
2230
2362
  return YES;
2231
2363
  },
2232
2364
 
2233
2365
  /** @private */
2234
- mouseExited: function (ev) {
2235
- var view = this._lastHoveredItem;
2236
- this._lastHoveredItem = null;
2237
- if (view && view.mouseExited) view.mouseExited(ev);
2238
- return YES;
2366
+ mouseExited: function(ev) {
2367
+ var view = this._lastHoveredItem ;
2368
+ this._lastHoveredItem = null ;
2369
+ if (view && view.mouseExited) view.mouseExited(ev) ;
2370
+ return YES ;
2371
+ },
2372
+
2373
+ /** @private We capture mouseWheel events while the mouse is pressed, this is to prevent jitter from slight mouse wheels while pressing
2374
+ and lifting the finger (especially a problem with the Magic Mouse) */
2375
+ mouseWheel: function (evt) {
2376
+ // Capture mouse wheel events when mouse is pressed or immediately after a mouse up (to avoid
2377
+ // excessive Magic Mouse wheel events while the person lifts their finger).
2378
+ return this._sc_isMouseDown || this._sc_isMouseJustDown;
2239
2379
  },
2240
2380
 
2241
2381
  // ..........................................................
@@ -2243,14 +2383,14 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2243
2383
  //
2244
2384
 
2245
2385
  /** @private */
2246
- touchStart: function (touch, evt) {
2386
+ touchStart: function(touch, evt) {
2247
2387
  var itemView = this.itemViewForEvent(touch),
2248
2388
  contentIndex = itemView ? itemView.get('contentIndex') : -1;
2249
2389
 
2250
2390
  if (!this.get('isEnabledInPane')) return contentIndex > -1;
2251
2391
 
2252
2392
  // become first responder if possible.
2253
- this.becomeFirstResponder();
2393
+ this.becomeFirstResponder() ;
2254
2394
 
2255
2395
  this._touchSelectedView = itemView;
2256
2396
 
@@ -2266,8 +2406,8 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2266
2406
  },
2267
2407
 
2268
2408
  /** @private */
2269
- touchesDragged: function (evt, touches) {
2270
- touches.forEach(function (touch) {
2409
+ touchesDragged: function(evt, touches) {
2410
+ touches.forEach(function(touch){
2271
2411
  if (
2272
2412
  Math.abs(touch.pageX - touch.startX) > 5 ||
2273
2413
  Math.abs(touch.pageY - touch.startY) > 5
@@ -2280,7 +2420,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2280
2420
  },
2281
2421
 
2282
2422
  /** @private */
2283
- touchEnd: function (touch) {
2423
+ touchEnd: function(touch) {
2284
2424
  /*
2285
2425
  TODO [CC] We should be using itemViewForEvent here, but because
2286
2426
  ListItemView re-renders itself once isSelected is called
@@ -2289,7 +2429,8 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2289
2429
  fail when using touch events.
2290
2430
  */
2291
2431
  // var itemView = this.itemViewForEvent(touch),
2292
- var itemView = this._touchSelectedView,
2432
+ var content = this.get('content'),
2433
+ itemView = this._touchSelectedView,
2293
2434
  contentIndex = itemView ? itemView.get('contentIndex') : -1,
2294
2435
  isSelected = NO, sel, shouldSelect;
2295
2436
 
@@ -2298,7 +2439,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2298
2439
  if (contentIndex > -1) {
2299
2440
  if (this.get('useToggleSelection')) {
2300
2441
  sel = this.get('selection');
2301
- isSelected = sel && sel.containsObject(itemView.get('content'));
2442
+ isSelected = sel && sel.contains(content, contentIndex, 1);
2302
2443
  shouldSelect = !isSelected;
2303
2444
  }
2304
2445
  else
@@ -2318,7 +2459,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2318
2459
  },
2319
2460
 
2320
2461
  /** @private */
2321
- touchCancelled: function (evt) {
2462
+ touchCancelled: function(evt) {
2322
2463
  // Remove fake selection
2323
2464
  if (this._touchSelectedView) {
2324
2465
  this._touchSelectedView.set('isSelected', NO);
@@ -2327,41 +2468,41 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2327
2468
  },
2328
2469
 
2329
2470
  /** @private */
2330
- _findSelectionExtendedByShift: function (sel, contentIndex) {
2471
+ _findSelectionExtendedByShift: function(sel, contentIndex) {
2331
2472
 
2332
2473
  // fast path. if we don't have a selection, just select index
2333
- if (!sel || sel.get('length') === 0) {
2474
+ if (!sel || sel.get('length')===0) {
2334
2475
  return SC.IndexSet.create(contentIndex);
2335
2476
  }
2336
2477
 
2337
2478
  // if we do have a selection, then figure out how to extend it.
2338
2479
  var min = sel.get('min'),
2339
- max = sel.get('max') - 1,
2340
- anchor = this._selectionAnchor;
2480
+ max = sel.get('max')-1,
2481
+ anchor = this._selectionAnchor ;
2341
2482
  if (SC.none(anchor)) anchor = -1;
2342
2483
 
2343
2484
  // clicked before the current selection set... extend it's beginning...
2344
2485
  if (contentIndex < min) {
2345
2486
  min = contentIndex;
2346
- if (anchor < 0) this._selectionAnchor = anchor = max; //anchor at end
2487
+ if (anchor<0) this._selectionAnchor = anchor = max; //anchor at end
2347
2488
 
2348
2489
  // clicked after the current selection set... extend it's ending...
2349
2490
  } else if (contentIndex > max) {
2350
2491
  max = contentIndex;
2351
- if (anchor < 0) this._selectionAnchor = anchor = min; // anchor at start
2492
+ if (anchor<0) this._selectionAnchor = anchor = min; // anchor at start
2352
2493
 
2353
2494
  // clicked inside the selection set... need to determine where the last
2354
2495
  // selection was and use that as an anchor.
2355
2496
  } else if (contentIndex >= min && contentIndex <= max) {
2356
- if (anchor < 0) this._selectionAnchor = anchor = min; //anchor at start
2497
+ if (anchor<0) this._selectionAnchor = anchor = min; //anchor at start
2357
2498
 
2358
- if (contentIndex === anchor) min = max = contentIndex;
2499
+ if (contentIndex === anchor) min = max = contentIndex ;
2359
2500
  else if (contentIndex > anchor) {
2360
2501
  min = anchor;
2361
- max = contentIndex;
2502
+ max = contentIndex ;
2362
2503
  } else if (contentIndex < anchor) {
2363
2504
  min = contentIndex;
2364
- max = anchor;
2505
+ max = anchor ;
2365
2506
  }
2366
2507
  }
2367
2508
 
@@ -2381,8 +2522,8 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2381
2522
  @field
2382
2523
  @type String
2383
2524
  */
2384
- reorderDataType: function () {
2385
- return 'SC.CollectionView.Reorder.' + SC.guidFor(this);
2525
+ reorderDataType: function() {
2526
+ return 'SC.CollectionView.Reorder.'+SC.guidFor(this) ;
2386
2527
  }.property().cacheable(),
2387
2528
 
2388
2529
  /**
@@ -2437,10 +2578,10 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2437
2578
  dragContent, dragDataTypes, dragView;
2438
2579
 
2439
2580
  // if the mouse down event was cleared, there is nothing to do; return.
2440
- if (!info || info.contentIndex < 0) return YES;
2581
+ if (!info || info.contentIndex<0) return YES ;
2441
2582
 
2442
2583
  // Don't do anything unless the user has been dragging for 123msec
2443
- if ((Date.now() - info.at) < 123) return YES;
2584
+ if ((Date.now() - info.at) < 123) return YES ;
2444
2585
 
2445
2586
  // OK, they must be serious, decide if a drag will be allowed.
2446
2587
  if (this.get('isEditable') && del.collectionViewShouldBeginDrag(this)) {
@@ -2458,9 +2599,9 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2458
2599
  } else dragContent = sel ? sel.indexSetForSource(content) : null;
2459
2600
 
2460
2601
  // remove any group indexes. groups cannot be dragged.
2461
- if (dragContent && groupIndexes && groupIndexes.get('length') > 0) {
2602
+ if (dragContent && groupIndexes && groupIndexes.get('length')>0) {
2462
2603
  dragContent = dragContent.copy().remove(groupIndexes);
2463
- if (dragContent.get('length') === 0) dragContent = null;
2604
+ if (dragContent.get('length')===0) dragContent = null;
2464
2605
  else dragContent.freeze();
2465
2606
  }
2466
2607
 
@@ -2468,7 +2609,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2468
2609
  else dragContent = dragContent.frozenCopy(); // so it doesn't change
2469
2610
 
2470
2611
  dragContent = { content: content, indexes: dragContent };
2471
- this.set('dragContent', dragContent);
2612
+ this.set('dragContent', dragContent) ;
2472
2613
 
2473
2614
  // Get the set of data types supported by the delegate. If this returns
2474
2615
  // a null or empty array and reordering content is not also supported
@@ -2494,13 +2635,13 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2494
2635
 
2495
2636
  // Also use this opportunity to clean up since mouseUp won't
2496
2637
  // get called.
2497
- this._cleanupMouseDown();
2498
- this._lastInsertionIndex = null;
2638
+ this._cleanupMouseDown() ;
2639
+ this._lastInsertionIndex = null ;
2499
2640
 
2500
2641
  // Drag was not allowed by the delegate, so bail.
2501
- } else this.set('dragContent', null);
2642
+ } else this.set('dragContent', null) ;
2502
2643
 
2503
- return YES;
2644
+ return YES ;
2504
2645
  }
2505
2646
  },
2506
2647
 
@@ -2508,12 +2649,12 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2508
2649
  Compute a default drag view by grabbing the raw layers and inserting them
2509
2650
  into a drag view.
2510
2651
  */
2511
- _cv_dragViewFor: function (dragContent) {
2652
+ _cv_dragViewFor: function(dragContent) {
2512
2653
  // find only the indexes that are in both dragContent and nowShowing.
2513
2654
  var indexes = this.get('nowShowing').without(dragContent),
2514
2655
  dragLayer = this.get('layer').cloneNode(false),
2515
2656
  view = SC.View.create({ layer: dragLayer, parentView: this }),
2516
- height = 0, layout;
2657
+ height=0, layout;
2517
2658
 
2518
2659
  indexes = this.get('nowShowing').without(indexes);
2519
2660
 
@@ -2522,19 +2663,37 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2522
2663
  .css('border', 'none')
2523
2664
  .css('top', 0).css('left', 0);
2524
2665
 
2525
- indexes.forEach(function (i) {
2666
+ indexes.forEach(function(i) {
2526
2667
  var itemView = this.itemViewForContentIndex(i),
2527
- isSelected, layer;
2668
+ isSelected, itemViewLayer, layer;
2528
2669
 
2529
- // render item view without isSelected state.
2530
2670
  if (itemView) {
2671
+ // render item view without isSelected state.
2531
2672
  isSelected = itemView.get('isSelected');
2532
2673
  itemView.set('isSelected', NO);
2533
-
2534
2674
  itemView.updateLayerIfNeeded();
2535
- layer = itemView.get('layer');
2536
- if (layer) layer = layer.cloneNode(true);
2675
+ itemViewLayer = itemView.get('layer');
2676
+
2677
+ if (itemViewLayer) {
2678
+ layer = itemViewLayer.cloneNode(true);
2679
+
2680
+ // photocopy any canvas elements
2681
+ var itemViewCanvasses = itemView.$().find('canvas');
2682
+ if (itemViewCanvasses) {
2683
+ var layerCanvasses = $(layer).find('canvas'),
2684
+ len = itemViewCanvasses.length,
2685
+ itemViewCanvas, layerCanvas;
2686
+ for (i = 0; i < len; i++) {
2687
+ itemViewCanvas = itemViewCanvasses[i];
2688
+ layerCanvas = layerCanvasses[i];
2689
+ layerCanvas.height = itemViewCanvas.height;
2690
+ layerCanvas.width = itemViewCanvas.width;
2691
+ layerCanvas.getContext('2d').drawImage(itemViewCanvas, 0, 0);
2692
+ }
2693
+ }
2694
+ }
2537
2695
 
2696
+ // reset item view
2538
2697
  itemView.set('isSelected', isSelected);
2539
2698
  itemView.updateLayerIfNeeded();
2540
2699
  }
@@ -2542,18 +2701,19 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2542
2701
  if (layer) {
2543
2702
  dragLayer.appendChild(layer);
2544
2703
  layout = itemView.get('layout');
2545
- if (layout.height + layout.top > height) {
2546
- height = layout.height + layout.top;
2704
+ if(layout.height+layout.top>height){
2705
+ height = layout.height+layout.top;
2547
2706
  }
2548
2707
  }
2549
- layer = null;
2550
2708
 
2709
+ layer = null;
2551
2710
  }, this);
2552
- // we don't want to show the scrollbars, resize the dragview'
2553
- view.set('layout', { height: height });
2711
+
2712
+ // we don't want to show the scrollbars, resize the dragview
2713
+ view.set('layout', {height:height});
2554
2714
 
2555
2715
  dragLayer = null;
2556
- return view;
2716
+ return view ;
2557
2717
  },
2558
2718
 
2559
2719
 
@@ -2565,11 +2725,11 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2565
2725
  @field
2566
2726
  @type Array
2567
2727
  */
2568
- dragDataTypes: function () {
2728
+ dragDataTypes: function() {
2569
2729
  // consult delegate.
2570
2730
  var del = this.get('selectionDelegate'),
2571
2731
  ret = del.collectionViewDragDataTypes(this),
2572
- key;
2732
+ key ;
2573
2733
 
2574
2734
  if (this.get('canReorderContent')) {
2575
2735
  ret = ret ? ret.copy() : [];
@@ -2585,12 +2745,12 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2585
2745
  this method will consult the collection view delegate if one has been
2586
2746
  provided. It also respects the canReorderContent method.
2587
2747
  */
2588
- dragDataForType: function (drag, dataType) {
2748
+ dragDataForType: function(drag, dataType) {
2589
2749
 
2590
2750
  // if this is a reorder, then return drag content.
2591
2751
  if (this.get('canReorderContent')) {
2592
2752
  if (dataType === this.get('reorderDataType')) {
2593
- return this.get('dragContent');
2753
+ return this.get('dragContent') ;
2594
2754
  }
2595
2755
  }
2596
2756
 
@@ -2611,7 +2771,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2611
2771
  @param {SC.Event} evt the event triggering this change, if available
2612
2772
  @returns {Number} logical OR'd mask of allowed drag operations.
2613
2773
  */
2614
- computeDragOperations: function (drag, evt) {
2774
+ computeDragOperations: function(drag, evt) {
2615
2775
  // the proposed drag operation is DRAG_REORDER only if we can reorder
2616
2776
  // content and the drag contains reorder content.
2617
2777
  var op = SC.DRAG_NONE,
@@ -2619,15 +2779,15 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2619
2779
 
2620
2780
  if (this.get('canReorderContent')) {
2621
2781
  if (drag.get('dataTypes').indexOf(this.get('reorderDataType')) >= 0) {
2622
- op = SC.DRAG_REORDER;
2782
+ op = SC.DRAG_REORDER ;
2623
2783
  }
2624
2784
  }
2625
2785
 
2626
2786
  // Now pass this onto the delegate.
2627
2787
  op = del.collectionViewComputeDragOperations(this, drag, op);
2628
- if (op & SC.DRAG_REORDER) op = SC.DRAG_MOVE;
2788
+ if (op & SC.DRAG_REORDER) op = SC.DRAG_MOVE ;
2629
2789
 
2630
- return op;
2790
+ return op ;
2631
2791
  },
2632
2792
 
2633
2793
  /** @private
@@ -2640,7 +2800,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2640
2800
  @param {Number} dragOp allowed drag operation mask
2641
2801
  Returns three params: [drop index, drop operation, allowed drag ops]
2642
2802
  */
2643
- _computeDropOperationState: function (drag, evt, dragOp) {
2803
+ _computeDropOperationState: function(drag, evt, dragOp) {
2644
2804
  // get the insertion index for this location. This can be computed
2645
2805
  // by a subclass using whatever method. This method is not expected to
2646
2806
  // do any data validation, just to map the location to an insertion
@@ -2656,10 +2816,10 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2656
2816
 
2657
2817
  // get the computed insertion index and possibly drop operation.
2658
2818
  // prefer to drop ON.
2659
- var idx = this.insertionIndexForLocation(loc, SC.DROP_ON);
2819
+ var idx = this.insertionIndexForLocation(loc, SC.DROP_ON) ;
2660
2820
  if (SC.typeOf(idx) === SC.T_ARRAY) {
2661
- dropOp = idx[1]; // order matters here
2662
- idx = idx[0];
2821
+ dropOp = idx[1] ; // order matters here
2822
+ idx = idx[0] ;
2663
2823
  }
2664
2824
 
2665
2825
  // if the return drop operation is DROP_ON, then just check it with the
@@ -2670,25 +2830,25 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2670
2830
 
2671
2831
  // Now save the insertion index and the dropOp. This may be changed by
2672
2832
  // the collection delegate.
2673
- this.set('proposedInsertionIndex', idx);
2674
- this.set('proposedDropOperation', dropOp);
2675
- tmp = del.collectionViewValidateDragOperation(this, drag, dragOp, idx, dropOp);
2676
- idx = this.get('proposedInsertionIndex');
2677
- dropOp = this.get('proposedDropOperation');
2678
- this._dropInsertionIndex = this._dropOperation = null;
2833
+ this.set('proposedInsertionIndex', idx) ;
2834
+ this.set('proposedDropOperation', dropOp) ;
2835
+ tmp = del.collectionViewValidateDragOperation(this, drag, dragOp, idx, dropOp) ;
2836
+ idx = this.get('proposedInsertionIndex') ;
2837
+ dropOp = this.get('proposedDropOperation') ;
2838
+ this._dropInsertionIndex = this._dropOperation = null ;
2679
2839
 
2680
2840
  // The delegate is OK with a drop on also, so just return.
2681
- if (tmp !== SC.DRAG_NONE) return [idx, dropOp, tmp];
2841
+ if (tmp !== SC.DRAG_NONE) return [idx, dropOp, tmp] ;
2682
2842
 
2683
2843
  // The delegate is NOT OK with a drop on, try to get the insertion
2684
2844
  // index again, but this time prefer SC.DROP_BEFORE, then let the
2685
2845
  // rest of the method run...
2686
2846
  else {
2687
- dropOp = SC.DROP_BEFORE;
2688
- idx = this.insertionIndexForLocation(loc, SC.DROP_BEFORE);
2847
+ dropOp = SC.DROP_BEFORE ;
2848
+ idx = this.insertionIndexForLocation(loc, SC.DROP_BEFORE) ;
2689
2849
  if (SC.typeOf(idx) === SC.T_ARRAY) {
2690
- dropOp = idx[1]; // order matters here
2691
- idx = idx[0];
2850
+ dropOp = idx[1] ; // order matters here
2851
+ idx = idx[0] ;
2692
2852
  }
2693
2853
  }
2694
2854
  }
@@ -2699,20 +2859,20 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2699
2859
  // content.
2700
2860
  if ((idx >= 0) && canReorder && (dropOp !== SC.DROP_ON)) {
2701
2861
 
2702
- objects = drag.dataForType(this.get('reorderDataType'));
2862
+ objects = drag.dataForType(this.get('reorderDataType')) ;
2703
2863
  if (objects) {
2704
- content = this.get('content');
2864
+ content = this.get('content') ;
2705
2865
 
2706
2866
  // if the insertion index is in between two items in the drag itself,
2707
2867
  // then this is not allowed. Either use the last insertion index or
2708
2868
  // find the first index that is not in between selections. Stop when
2709
2869
  // we get to the beginning.
2710
2870
  if (dropOp === SC.DROP_BEFORE) {
2711
- isPreviousInDrag = objects.indexes.contains(idx - 1);
2871
+ isPreviousInDrag = objects.indexes.contains(idx-1);
2712
2872
  isNextInDrag = objects.indexes.contains(idx);
2713
2873
  } else {
2714
2874
  isPreviousInDrag = objects.indexes.contains(idx);
2715
- isNextInDrag = objects.indexes.contains(idx - 1);
2875
+ isNextInDrag = objects.indexes.contains(idx-1);
2716
2876
  }
2717
2877
 
2718
2878
  if (isPreviousInDrag && isNextInDrag) {
@@ -2723,26 +2883,26 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2723
2883
  len = content ? content.get('length') : 0;
2724
2884
  while ((idx < len) && objects.indexes.contains(idx)) idx++;
2725
2885
  }
2726
- } else idx = this._lastInsertionIndex;
2886
+ } else idx = this._lastInsertionIndex ;
2727
2887
  }
2728
2888
 
2729
2889
  // If we found a valid insertion point to reorder at, then set the op
2730
2890
  // to custom DRAG_REORDER.
2731
- if (idx >= 0) dragOp = SC.DRAG_REORDER;
2891
+ if (idx >= 0) dragOp = SC.DRAG_REORDER ;
2732
2892
  }
2733
2893
  }
2734
2894
 
2735
2895
  // Now save the insertion index and the dropOp. This may be changed by
2736
2896
  // the collection delegate.
2737
- this.set('proposedInsertionIndex', idx);
2738
- this.set('proposedDropOperation', dropOp);
2739
- dragOp = del.collectionViewValidateDragOperation(this, drag, dragOp, idx, dropOp);
2740
- idx = this.get('proposedInsertionIndex');
2741
- dropOp = this.get('proposedDropOperation');
2742
- this._dropInsertionIndex = this._dropOperation = null;
2897
+ this.set('proposedInsertionIndex', idx) ;
2898
+ this.set('proposedDropOperation', dropOp) ;
2899
+ dragOp = del.collectionViewValidateDragOperation(this, drag, dragOp, idx, dropOp) ;
2900
+ idx = this.get('proposedInsertionIndex') ;
2901
+ dropOp = this.get('proposedDropOperation') ;
2902
+ this._dropInsertionIndex = this._dropOperation = null ;
2743
2903
 
2744
2904
  // return generated state
2745
- return [idx, dropOp, dragOp];
2905
+ return [idx, dropOp, dragOp] ;
2746
2906
  },
2747
2907
 
2748
2908
  /**
@@ -2754,7 +2914,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2754
2914
  @param {SC.Drag} drag The drag that was updated
2755
2915
  @param {SC.Event} evt The event for the drag
2756
2916
  */
2757
- dragUpdated: function (drag, evt) {
2917
+ dragUpdated: function(drag, evt) {
2758
2918
  var op = drag.get('allowedDragOperations'),
2759
2919
  state = this._computeDropOperationState(drag, evt, op),
2760
2920
  idx = state[0], dropOp = state[1], dragOp = state[2];
@@ -2763,15 +2923,15 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2763
2923
  // point
2764
2924
  if (dragOp !== SC.DRAG_NONE) {
2765
2925
  if ((this._lastInsertionIndex !== idx) || (this._lastDropOperation !== dropOp)) {
2766
- var itemView = this.itemViewForContentIndex(idx);
2767
- this.showInsertionPoint(itemView, dropOp);
2926
+ var itemView = this.itemViewForContentIndex(idx) ;
2927
+ this.showInsertionPoint(itemView, dropOp) ;
2768
2928
  }
2769
2929
 
2770
- this._lastInsertionIndex = idx;
2771
- this._lastDropOperation = dropOp;
2930
+ this._lastInsertionIndex = idx ;
2931
+ this._lastDropOperation = dropOp ;
2772
2932
  } else {
2773
- this.hideInsertionPoint();
2774
- this._lastInsertionIndex = this._lastDropOperation = null;
2933
+ this.hideInsertionPoint() ;
2934
+ this._lastInsertionIndex = this._lastDropOperation = null ;
2775
2935
  }
2776
2936
 
2777
2937
  // Normalize drag operation to the standard kinds accepted by the drag
@@ -2784,8 +2944,8 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2784
2944
  point and clears some cached values.
2785
2945
  */
2786
2946
  dragEnded: function () {
2787
- this.hideInsertionPoint();
2788
- this._lastInsertionIndex = this._lastDropOperation = null;
2947
+ this.hideInsertionPoint() ;
2948
+ this._lastInsertionIndex = this._lastDropOperation = null ;
2789
2949
  },
2790
2950
 
2791
2951
  /**
@@ -2793,7 +2953,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2793
2953
 
2794
2954
  @returns {Boolean} YES
2795
2955
  */
2796
- acceptDragOperation: function (drag, op) {
2956
+ acceptDragOperation: function(drag, op) {
2797
2957
  return YES;
2798
2958
  },
2799
2959
 
@@ -2806,7 +2966,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2806
2966
  @param {Number} op The drag operation to perform
2807
2967
  @return {Number} The operation performed
2808
2968
  */
2809
- performDragOperation: function (drag, op) {
2969
+ performDragOperation: function(drag, op) {
2810
2970
  // Get the correct insertion point, drop operation, etc.
2811
2971
  var state = this._computeDropOperationState(drag, null, op),
2812
2972
  idx = state[0], dropOp = state[1], dragOp = state[2],
@@ -2816,32 +2976,32 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2816
2976
  // The dragOp is the kinds of ops allowed. The drag operation must
2817
2977
  // be included in that set.
2818
2978
  if (dragOp & SC.DRAG_REORDER) {
2819
- op = (op & SC.DRAG_MOVE) ? SC.DRAG_REORDER : SC.DRAG_NONE;
2820
- } else op = op & dragOp;
2979
+ op = (op & SC.DRAG_MOVE) ? SC.DRAG_REORDER : SC.DRAG_NONE ;
2980
+ } else op = op & dragOp ;
2821
2981
 
2822
2982
  // If no allowed drag operation could be found, just return.
2823
2983
  if (op === SC.DRAG_NONE) return op;
2824
2984
 
2825
2985
  // Some operation is allowed through, give the delegate a chance to
2826
2986
  // handle it.
2827
- performed = del.collectionViewPerformDragOperation(this, drag, op, idx, dropOp);
2987
+ performed = del.collectionViewPerformDragOperation(this, drag, op, idx, dropOp) ;
2828
2988
 
2829
2989
  // If the delegate did not handle the drag (i.e. returned SC.DRAG_NONE),
2830
2990
  // and the op type is REORDER, then do the reorder here.
2831
2991
  if ((performed === SC.DRAG_NONE) && (op & SC.DRAG_REORDER)) {
2832
2992
 
2833
- data = drag.dataForType(this.get('reorderDataType'));
2834
- if (!data) return SC.DRAG_NONE;
2993
+ data = drag.dataForType(this.get('reorderDataType')) ;
2994
+ if (!data) return SC.DRAG_NONE ;
2835
2995
 
2836
- content = this.get('content');
2996
+ content = this.get('content') ;
2837
2997
 
2838
2998
  // check for special case - inserting BEFORE ourself...
2839
2999
  // in this case just pretend the move happened since it's a no-op
2840
3000
  // anyway
2841
3001
  indexes = data.indexes;
2842
- if (indexes.get('length') === 1) {
3002
+ if (indexes.get('length')===1) {
2843
3003
  if (((dropOp === SC.DROP_BEFORE) || (dropOp === SC.DROP_AFTER)) &&
2844
- (indexes.get('min') === idx)) return SC.DRAG_MOVE;
3004
+ (indexes.get('min')===idx)) return SC.DRAG_MOVE;
2845
3005
  }
2846
3006
 
2847
3007
  content.beginPropertyChanges(); // suspend notifications
@@ -2850,9 +3010,9 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2850
3010
  // added again later.
2851
3011
  objects = [];
2852
3012
  shift = 0;
2853
- data.indexes.forEach(function (i) {
3013
+ data.indexes.forEach(function(i) {
2854
3014
  objects.push(content.objectAt(i - shift));
2855
- content.removeAt(i - shift);
3015
+ content.removeAt(i-shift);
2856
3016
  shift++;
2857
3017
  if (i < idx) idx--;
2858
3018
  }, this);
@@ -2864,7 +3024,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2864
3024
  content.endPropertyChanges(); // restart notifications
2865
3025
 
2866
3026
  // make the op into its actual value
2867
- op = SC.DRAG_MOVE;
3027
+ op = SC.DRAG_MOVE ;
2868
3028
  }
2869
3029
 
2870
3030
  return op;
@@ -2876,7 +3036,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2876
3036
 
2877
3037
  @param {SC.View} view
2878
3038
  */
2879
- collectionViewShouldBeginDrag: function (view) {
3039
+ collectionViewShouldBeginDrag: function(view) {
2880
3040
  return this.get('canReorderContent');
2881
3041
  },
2882
3042
 
@@ -2915,63 +3075,19 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2915
3075
  @param {DropOp} dropOperation the preferred drop operation.
2916
3076
  @returns {Array} format: [index, op]
2917
3077
  */
2918
- insertionIndexForLocation: function (loc, dropOperation) {
3078
+ insertionIndexForLocation: function(loc, dropOperation) {
2919
3079
  return -1;
2920
3080
  },
2921
3081
 
2922
3082
  // ..........................................................
2923
- // SCROLLING
3083
+ // INTERNAL SUPPORT
2924
3084
  //
2925
3085
 
2926
- /** @private SC.ScrollView */
2927
- touchScrollDidStart: function () {
2928
- var clippingFrame = this.get('clippingFrame');
2929
-
2930
- // Create the in-scroll clipping frame that will be used while touch scrolling.
2931
- this._inScrollClippingFrame = {
2932
- x: clippingFrame.x,
2933
- y: clippingFrame.y,
2934
- width: clippingFrame.width,
2935
- height: clippingFrame.height
2936
- };
2937
- },
2938
-
2939
- /* @private Internal property used to track the rate of touch scroll change events. */
2940
- _lastTouchScrollTime: null,
2941
-
2942
- /** @private SC.ScrollView */
2943
- touchScrollDidChange: function (left, top) {
2944
- // Fast path! Don't try to update too soon.
2945
- if (Date.now() - this._lastTouchScrollTime < 30) { return; }
2946
-
2947
- var inScrollClippingFrame = this._inScrollClippingFrame;
2948
-
2949
- // Update the in-scroll clipping frame with the new values.
2950
- inScrollClippingFrame.x = left;
2951
- inScrollClippingFrame.y = top;
2952
-
2953
- // Indicate that nowShowing should be re-computed (this will use the
2954
- // in-scroll clipping frame when it does).
2955
- // TODO: perform a raw update that doesn't require the run loop.
2956
- SC.run(function () {
2957
- this.notifyPropertyChange('nowShowing');
2958
- this.invokeOnce('_cv_nowShowingDidChange');
2959
- }, this);
2960
-
2961
- // Track the last time we updated.
2962
- this._lastTouchScrollTime = Date.now();
2963
- },
2964
-
2965
- /** @private SC.ScrollView */
2966
- touchScrollDidEnd: function () {
2967
- // Clean up so that the regular clippingFrame is used again.
2968
- this._inScrollClippingFrame = null;
3086
+ /** @private Clears the mouse just down flag. */
3087
+ _sc_clearMouseJustDown: function () {
3088
+ this._sc_isMouseJustDown = false;
2969
3089
  },
2970
3090
 
2971
- // ..........................................................
2972
- // INTERNAL SUPPORT
2973
- //
2974
-
2975
3091
  /** @private - when we are about to become visible, reload if needed. */
2976
3092
  willShowInDocument: function () {
2977
3093
  if (this._invalidIndexes) this.invokeOnce(this.reloadIfNeeded);
@@ -2992,16 +3108,10 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
2992
3108
  Default delegate method implementation, returns YES if isSelectable
2993
3109
  is also true.
2994
3110
  */
2995
- collectionViewShouldSelectItem: function (view, item) {
2996
- return this.get('isSelectable');
3111
+ collectionViewShouldSelectItem: function(view, item) {
3112
+ return this.get('isSelectable') ;
2997
3113
  },
2998
3114
 
2999
- /** @private */
3000
- _TMP_DIFF1: SC.IndexSet.create(),
3001
-
3002
- /** @private */
3003
- _TMP_DIFF2: SC.IndexSet.create(),
3004
-
3005
3115
  /** @private
3006
3116
 
3007
3117
  Whenever the nowShowing range changes, update the range observer on the
@@ -3009,7 +3119,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
3009
3119
  the previous nowShowing range.
3010
3120
 
3011
3121
  */
3012
- _cv_nowShowingDidChange: function () {
3122
+ _cv_nowShowingDidChange: function() {
3013
3123
  var nowShowing = this.get('nowShowing'),
3014
3124
  last = this._sccv_lastNowShowing,
3015
3125
  diff, diff1, diff2;
@@ -3022,7 +3132,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
3022
3132
  diff1 = this._TMP_DIFF1.add(last).remove(nowShowing);
3023
3133
  diff2 = this._TMP_DIFF2.add(nowShowing).remove(last);
3024
3134
  diff = diff1.add(diff2);
3025
- } else diff = last || nowShowing;
3135
+ } else diff = last || nowShowing ;
3026
3136
  }
3027
3137
 
3028
3138
  // if nowShowing has actually changed, then update
@@ -3039,35 +3149,65 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
3039
3149
  }.observes('nowShowing'),
3040
3150
 
3041
3151
  /** @private */
3042
- init: function () {
3043
- sc_super();
3152
+ init: function() {
3153
+ sc_super();
3044
3154
 
3155
+ //@if (debug)
3045
3156
  if (this.useFastPath) {
3046
- //@if(debug)
3047
3157
  // Deprecation warning for those that were using SC.CollectionFastPath.
3048
3158
  SC.warn("Developer Warning: SC.CollectionView `useFastPath` has been deprecated. The performance improvements have been integrated directly into SC.CollectionView as the default behavior. Please disable the useFastPath property and refer to the SC.CollectionView documentation for more information.");
3049
- //@endif
3050
- this.mixin(SC.CollectionFastPath);
3051
3159
  }
3160
+ //@endif
3052
3161
 
3053
- //@if(debug)
3162
+ //@if (debug)
3054
3163
  if (this.willReload || this.didReload) {
3055
3164
  // Deprecation warning for willReload and didReload. These don't seem to serve any purpose.
3056
3165
  SC.warn("Developer Warning: SC.CollectionView no longer calls willReload and didReload on its subclasses because it includes item view and layer pooling in itself by default.");
3057
3166
  }
3058
3167
  //@endif
3059
3168
 
3060
- if (this.get('canReorderContent')) this._cv_canReorderContentDidChange();
3061
- this._sccv_lastNowShowing = this.get('nowShowing').clone();
3169
+ if (this.get('canReorderContent')) this._cv_canReorderContentDidChange();
3170
+ this._sccv_lastNowShowing = this.get('nowShowing').clone();
3062
3171
 
3063
- if (this.content) this._cv_contentDidChange();
3064
- if (this.selection) this._cv_selectionDidChange();
3172
+ if (this.content) this._cv_contentDidChange();
3173
+ if (this.selection) this._cv_selectionDidChange();
3174
+
3175
+ // Set our initial layout. It's important that our computed layout exist on instantiation so that containing views
3176
+ // understand in which way the collection will grow (e.g. if we compute height, then the container won't adjust height).
3177
+ this.adjustLayout();
3178
+ },
3179
+
3180
+ /** @private SC.View.prototype.destroy. */
3181
+ destroy: function () {
3182
+ sc_super();
3183
+
3184
+ // All manipulations made to objects we use must be reversed!
3185
+ var content = this._content;
3186
+ if (content) {
3187
+ content.removeObserver('length', this, this.contentLengthDidChange);
3188
+
3189
+ this._content = null;
3190
+ }
3191
+
3192
+ var sel = this._cv_selection;
3193
+ if (sel) {
3194
+ sel.removeObserver('[]', this, this._cv_selectionContentDidChange);
3195
+
3196
+ this._cv_selection = null;
3197
+ }
3198
+
3199
+ var contentRangeObserver = this._cv_contentRangeObserver;
3200
+ if (contentRangeObserver) {
3201
+ if (content) content.removeRangeObserver(contentRangeObserver);
3202
+
3203
+ this._cv_contentRangeObserver = null;
3204
+ }
3065
3205
  },
3066
3206
 
3067
3207
  /** @private
3068
3208
  Become a drop target whenever reordering content is enabled.
3069
3209
  */
3070
- _cv_canReorderContentDidChange: function () {
3210
+ _cv_canReorderContentDidChange: function() {
3071
3211
  if (this.get('canReorderContent')) {
3072
3212
  if (!this.get('isDropTarget')) this.set('isDropTarget', YES);
3073
3213
  SC.Drag.addDropTarget(this);
@@ -3081,16 +3221,16 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
3081
3221
  current selection (saved as a separate array so that a change in sel
3082
3222
  in the meantime will not be lost)
3083
3223
  */
3084
- _cv_performSelectAction: function (view, ev, delay, clickCount) {
3224
+ _cv_performSelectAction: function(view, ev, delay, clickCount) {
3085
3225
  var sel;
3086
- if (delay === undefined) delay = 0;
3226
+ if (delay === undefined) delay = 0 ;
3087
3227
  if (clickCount === undefined) clickCount = 1;
3088
- if ((clickCount > 1) || this.get('actOnSelect')) {
3089
- if (this._cv_reselectTimer) this._cv_reselectTimer.invalidate();
3228
+ if ((clickCount>1) || this.get('actOnSelect')) {
3229
+ if (this._cv_reselectTimer) this._cv_reselectTimer.invalidate() ;
3090
3230
  sel = this.get('selection');
3091
3231
  sel = sel ? sel.toArray() : [];
3092
3232
  if (this._cv_actionTimer) this._cv_actionTimer.invalidate();
3093
- this._cv_actionTimer = this.invokeLater(this._cv_action, delay, view, ev, sel);
3233
+ this._cv_actionTimer = this.invokeLater(this._cv_action, delay, view, ev, sel) ;
3094
3234
  }
3095
3235
  },
3096
3236
 
@@ -3098,17 +3238,17 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
3098
3238
  Perform the action. Supports legacy behavior as well as newer style
3099
3239
  action dispatch.
3100
3240
  */
3101
- _cv_action: function (view, evt, context) {
3241
+ _cv_action: function(view, evt, context) {
3102
3242
  var action = this.get('action');
3103
3243
  var target = this.get('target') || null;
3104
3244
 
3105
3245
  this._cv_actionTimer = null;
3106
3246
  if (action) {
3107
3247
  // if the action is a function, just call it
3108
- if (SC.typeOf(action) === SC.T_FUNCTION) return this.action(view, evt);
3248
+ if (SC.typeOf(action) === SC.T_FUNCTION) return this.action(view, evt) ;
3109
3249
 
3110
3250
  // otherwise, use the new sendAction style
3111
- var pane = this.get('pane');
3251
+ var pane = this.get('pane') ;
3112
3252
  if (pane) {
3113
3253
  pane.rootResponder.sendAction(action, target, this, pane, context);
3114
3254
  }
@@ -3116,21 +3256,22 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
3116
3256
  // if no action is specified, then trigger the support action,
3117
3257
  // if supported.
3118
3258
  } else if (!view) {
3119
- return; // nothing to do
3259
+ return ; // nothing to do
3120
3260
 
3121
3261
  // if the target view has its own internal action handler,
3122
3262
  // trigger that.
3123
- } else if (SC.typeOf(view._action) == SC.T_FUNCTION) {
3124
- return view._action(evt);
3263
+ } else if (SC.typeOf(view._action) === SC.T_FUNCTION) {
3264
+ return view._action(evt) ;
3125
3265
 
3126
3266
  // otherwise call the action method to support older styles.
3127
- } else if (SC.typeOf(view.action) == SC.T_FUNCTION) {
3128
- return view.action(evt);
3267
+ } else if (SC.typeOf(view.action) === SC.T_FUNCTION) {
3268
+ return view.action(evt) ;
3129
3269
  }
3130
3270
  },
3131
3271
 
3272
+ /** @private */
3132
3273
  _attrsForContentIndex: function (idx) {
3133
- var attrs = this._TMP_ATTRS,
3274
+ var attrs = this._TMP_ATTRS, // NOTE: This is a shared object so every property of it must be set for each use.
3134
3275
  del = this.get('contentDelegate'),
3135
3276
  items = this.get('content'),
3136
3277
  isGroupView = this._contentIndexIsGroup(idx),
@@ -3155,12 +3296,18 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
3155
3296
  attrs.owner = attrs.displayDelegate = this;
3156
3297
  attrs.page = this.page;
3157
3298
  attrs.outlineLevel = outlineLevel;
3299
+ attrs.isLast = idx === items.get('length') - 1;
3158
3300
 
3159
3301
  if (isGroupView) attrs.classNames = this._GROUP_COLLECTION_CLASS_NAMES;
3160
3302
  else attrs.classNames = this._COLLECTION_CLASS_NAMES;
3161
3303
 
3162
- attrs.layout = this.layoutForContentIndex(idx);
3163
- if (!attrs.layout) { attrs.layout = SC.View.prototype.layout; }
3304
+ // Layout may be calculated by the collection view beforehand. If so,
3305
+ // assign it to the attributes. If the collection view doesn't calculate
3306
+ // layout or defers calculating layout, then we shouldn't force a layout
3307
+ // on the child view.
3308
+ var layout = this.layoutForContentIndex(idx);
3309
+ if (layout) { attrs.layout = layout; }
3310
+ else { delete attrs.layout; }
3164
3311
 
3165
3312
  return attrs;
3166
3313
  },
@@ -3190,7 +3337,7 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
3190
3337
  return del.contentIndexIsGroup(this, items, idx);
3191
3338
  } else {
3192
3339
  return false;
3193
- }
3340
+ }
3194
3341
  },
3195
3342
 
3196
3343
  /** @private
@@ -3251,15 +3398,13 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
3251
3398
  var layout = this.layoutForContentIndex(contentIndex);
3252
3399
 
3253
3400
  if (layout && layout.height) {
3401
+ // Handle both top aligned and bottom aligned layouts.
3254
3402
  if (layout.top) { layout.top = -layout.height; }
3255
3403
  else { layout.bottom = -layout.height; }
3256
3404
  } else if (layout && layout.width) {
3405
+ // Handle both left aligned and right aligned layouts.
3257
3406
  if (layout.left) { layout.left = -layout.width; }
3258
3407
  else { layout.right = -layout.width; }
3259
- } else {
3260
- // There is not really a valid layout for a collection. Just shape it and
3261
- // place it out of view.
3262
- layout = { left: -100, width: 100, top: -100, height: 100 };
3263
3408
  }
3264
3409
 
3265
3410
  return layout;
@@ -3273,6 +3418,8 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
3273
3418
  */
3274
3419
  _reconfigureItemView: function (itemView, attrs) {
3275
3420
  itemView.beginPropertyChanges();
3421
+
3422
+ // Update the view with the new properties.
3276
3423
  itemView.set('content', attrs.content);
3277
3424
  itemView.set('contentIndex', attrs.contentIndex);
3278
3425
  itemView.set('isEnabled', attrs.isEnabled);
@@ -3281,9 +3428,35 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
3281
3428
  itemView.set('isDeletable', attrs.isDeletable);
3282
3429
  itemView.set('isSelected', attrs.isSelected);
3283
3430
  itemView.set('layerId', attrs.layerId);
3284
- itemView.set('layout', attrs.layout);
3285
3431
  itemView.set('outlineLevel', attrs.outlineLevel);
3286
3432
  itemView.set('disclosureState', attrs.disclosureState);
3433
+ itemView.set('isLast', attrs.isLast);
3434
+
3435
+ // Don't assign null/undefined layouts.
3436
+ if (attrs.layout) { itemView.set('layout', attrs.layout); }
3437
+
3438
+ // If the view's isGroupView property is changing, the associated CSS classes need to
3439
+ // be updated.
3440
+ var isCurrentlyGroupView = itemView.get('isGroupView'),
3441
+ shouldBeGroupView = attrs.isGroupView;
3442
+ if (isCurrentlyGroupView !== shouldBeGroupView) {
3443
+ itemView.set('isGroupView', shouldBeGroupView);
3444
+ var classNames = itemView.get('classNames'),
3445
+ elem = itemView.$();
3446
+ // Going from group view to item view...
3447
+ if (isCurrentlyGroupView && !shouldBeGroupView) {
3448
+ classNames.pushObject('sc-item');
3449
+ classNames.removeObject('sc-group-item');
3450
+ elem.setClass({ 'sc-item': YES, 'sc-group-item': NO });
3451
+ }
3452
+ // Going from item view to group view...
3453
+ else {
3454
+ classNames.removeObject('sc-item');
3455
+ classNames.pushObject('sc-group-item');
3456
+ elem.setClass({ 'sc-item': NO, 'sc-group-item': YES });
3457
+ }
3458
+ }
3459
+ // Wrap up.
3287
3460
  itemView.endPropertyChanges();
3288
3461
  },
3289
3462
 
@@ -3293,7 +3466,8 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
3293
3466
  _removeItemView: function (itemView, idx) {
3294
3467
  var exampleView,
3295
3468
  items = this.get('content'),
3296
- layout,
3469
+ canPoolView, canPoolLayer,
3470
+ poolLayout,
3297
3471
  pool,
3298
3472
  prototype,
3299
3473
  wasPooled = false;
@@ -3304,11 +3478,13 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
3304
3478
  if (items && itemView.get('content') === items.objectAt(idx)) {
3305
3479
 
3306
3480
  exampleView = this._exampleViewForContentIndex(idx);
3307
- if (SC.none(exampleView.prototype.isReusable) || exampleView.prototype.isReusable) {
3481
+ prototype = exampleView.prototype;
3482
+ canPoolView = SC.none(prototype.isReusable) || prototype.isReusable;
3483
+ if (canPoolView) {
3308
3484
  // If the exampleView is reusable, send the view to its pool.
3309
3485
  pool = this._poolForExampleView(exampleView);
3310
3486
 
3311
- //@if(debug)
3487
+ //@if (debug)
3312
3488
  // Add a bit of developer support if they are migrating over from SC.CollectionFastPath
3313
3489
  if (itemView.hibernateInPool) {
3314
3490
  SC.error("Developer Error: Item views that want to do clean up before being pooled should implement sleepInPool not hibernateInPool. This will be temporarily fixed up for development mode only, but must be changed before production.");
@@ -3322,13 +3498,14 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
3322
3498
  pool.push(itemView);
3323
3499
 
3324
3500
  // If the exampleView's layer isn't reusable, destroy it.
3325
- prototype = exampleView.prototype;
3326
- if (!SC.none(prototype.isLayerReusable) && !prototype.isLayerReusable) {
3327
- itemView.destroyLayer();
3328
- } else {
3501
+ poolLayout = this._poolLayoutForContentIndex(idx);
3502
+ canPoolLayer = poolLayout && (SC.none(prototype.isLayerReusable) || prototype.isLayerReusable);
3503
+ if (canPoolLayer) {
3329
3504
  // If the layer is sticking around, be sure to move it out of view.
3330
- layout = this._poolLayoutForContentIndex(idx);
3331
- itemView.set('layout', layout);
3505
+ itemView.set('layout', poolLayout);
3506
+ } else {
3507
+ // We can't pool layers that are prohibited or that cannot be moved out of view (i.e. no poolLayout)
3508
+ itemView.destroyLayer();
3332
3509
  }
3333
3510
 
3334
3511
  // Ensure that the id of views in the pool don't clash with ids that
@@ -3347,5 +3524,4 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate, SC.CollectionConte
3347
3524
  delete this._sc_itemViews[idx];
3348
3525
  }
3349
3526
 
3350
-
3351
3527
  });