sproutcore 0.9.2 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. data/History.txt +33 -0
  2. data/Manifest.txt +3 -14
  3. data/clients/sc_docs/controllers/docs.js +1 -0
  4. data/clients/sc_docs/core.js +1 -1
  5. data/clients/sc_test_runner/controllers/runner.js +5 -0
  6. data/clients/sc_test_runner/core.js +1 -1
  7. data/frameworks/sproutcore/english.lproj/core.css +41 -0
  8. data/frameworks/sproutcore/english.lproj/theme.css +20 -0
  9. data/frameworks/sproutcore/foundation/animator.js +11 -2
  10. data/frameworks/sproutcore/foundation/date.js +2 -2
  11. data/frameworks/sproutcore/foundation/object.js +2 -2
  12. data/frameworks/sproutcore/foundation/server.js +4 -3
  13. data/frameworks/sproutcore/foundation/set.js +1 -1
  14. data/frameworks/sproutcore/foundation/unittest.js +12 -9
  15. data/frameworks/sproutcore/lib/collection_view.rb +1 -0
  16. data/frameworks/sproutcore/lib/core_views.rb +4 -0
  17. data/frameworks/sproutcore/mixins/editable.js +144 -0
  18. data/frameworks/sproutcore/mixins/inline_editor_delegate.js +72 -0
  19. data/frameworks/sproutcore/mixins/observable.js +45 -16
  20. data/frameworks/sproutcore/mixins/scrollable.js +0 -1
  21. data/frameworks/sproutcore/tests/controllers/controller.rhtml +12 -12
  22. data/frameworks/sproutcore/tests/controllers/object.rhtml +2 -2
  23. data/frameworks/sproutcore/views/collection/collection.js +122 -68
  24. data/frameworks/sproutcore/views/collection/source_list.js +5 -0
  25. data/frameworks/sproutcore/views/field/text_field.js +7 -1
  26. data/frameworks/sproutcore/views/inline_text_field.js +397 -0
  27. data/frameworks/sproutcore/views/label.js +78 -68
  28. data/frameworks/sproutcore/views/list_item.js +184 -31
  29. data/frameworks/sproutcore/views/view.js +41 -9
  30. data/generators/client/templates/core.js +1 -1
  31. data/generators/client/templates/english.lproj/body.css +74 -0
  32. data/generators/framework/templates/core.js +1 -1
  33. data/lib/sproutcore/version.rb +1 -1
  34. metadata +5 -16
  35. data/clients/view_builder/builders/builder.js +0 -339
  36. data/clients/view_builder/builders/button.js +0 -81
  37. data/clients/view_builder/controllers/document.js +0 -21
  38. data/clients/view_builder/core.js +0 -19
  39. data/clients/view_builder/english.lproj/body.css +0 -77
  40. data/clients/view_builder/english.lproj/body.rhtml +0 -41
  41. data/clients/view_builder/english.lproj/controls.css +0 -0
  42. data/clients/view_builder/english.lproj/strings.js +0 -14
  43. data/clients/view_builder/main.js +0 -38
  44. data/clients/view_builder/tests/controllers/document.rhtml +0 -20
  45. data/clients/view_builder/tests/views/builder.rhtml +0 -20
  46. data/clients/view_builder/views/builder.js +0 -23
  47. data/frameworks/sproutcore/english.lproj/inline_text_editor.css +0 -21
  48. data/frameworks/sproutcore/views/inline_text_editor.js +0 -96
@@ -0,0 +1,72 @@
1
+ // ========================================================================
2
+ // SproutCore
3
+ // copyright 2006-2008 Sprout Systems, Inc.
4
+ // ========================================================================
5
+
6
+ /**
7
+ @namespace
8
+
9
+ The inline editor delegate receives notifications from the inline text
10
+ editor before, during, and after the user completes inline editing.
11
+
12
+ The inline editor delegate is used by views that work with the inline
13
+ editor. You may need to implement this protocol if you want to
14
+ use the inline editor in your own custom views.
15
+
16
+ @since SproutCore 1.0
17
+ */
18
+ SC.InlineEditorDelegate = {
19
+
20
+ /**
21
+ Called just before the inline edit displays itself but after it has been
22
+ configured for display.
23
+
24
+ You can use this method to make last minute changes to the display of
25
+ the inline editor or to collect its value.
26
+
27
+ @param inlineEditor {SC.InlineTextFieldView} The inline editor.
28
+ @returns {void}
29
+ */
30
+ inlineEditorWillBeginEditing: function(inlineEditor) {},
31
+
32
+ /**
33
+ Called just after an inline editor displays itself.
34
+
35
+ You can use this method to perform any hiding or other view changes
36
+ you need to perform on your own view to make room for the new editor.
37
+
38
+ Note tht editors are placed over the top of views in the page, not
39
+ inside of them from a DOM perspective.
40
+
41
+ @param inlineEditor {SC.InlineTextFieldView} The inline editor.
42
+ @returns {void}
43
+ */
44
+ inlineEditorDidBeginEditing: function(inlineEditor) {},
45
+
46
+ /**
47
+ Called just before an inline editor tries to end editing and hide
48
+ itself.
49
+
50
+ You can use this method to control whether the inline editor will
51
+ actually be allowed to end editing. For example, you might disallow
52
+ the editor to end editing if the new value fails validation.
53
+
54
+ @param inlineEditor {SC.InlineTextFieldView} the inline editor
55
+ @param finalValue {Object} the final value
56
+ @returns {Boolean} YES to allow the editor to end editing.
57
+ */
58
+ inlineEditorShouldEndEditing: function(inlineEditor, finalValue) {
59
+ return YES ;
60
+ },
61
+
62
+ /**
63
+ Called just after the inline editor has ended editing. You can use this
64
+ method to save the final value of the inline editor and to perform any
65
+ other cleanup you need to do.
66
+
67
+ @param inlineEditor {SC.InlineTextFieldView} the inline editor
68
+ @param finalValue {Object} the final value
69
+ @returns {void}
70
+ */
71
+ inlineEditorDidEndEditing: function(inlineEditor, finalValue) {}
72
+ };
@@ -6,9 +6,11 @@
6
6
  /**
7
7
  @namespace
8
8
 
9
- Key-Value Observing is one of the fundamental ways that models, controllers
10
- and views communicate with each other in a SproutCore application. Any
11
- object that has this module applied to it can be used in KVO-operations.
9
+ Key-Value-Observing (KVO) simply allows one object to observe changes to a
10
+ property on another object. It is one of the fundamental ways that models,
11
+ controllers and views communicate with each other in a SproutCore
12
+ application. Any object that has this module applied to it can be used in
13
+ KVO-operations.
12
14
 
13
15
  This module is applied automatically to all objects that inherit from
14
16
  SC.Object, which includes most objects bundled with the SproutCore
@@ -16,21 +18,22 @@
16
18
  but you will use the features provided by this module frequently, so it is
17
19
  important to understand how to use it.
18
20
 
19
- h2. About Key Value Observing
21
+ h2. Enabling Key Value Observing
20
22
 
21
- Key-Value-Observing (KVO) simply allows one object to observe changes to a
22
- property on another object. This can replace much of the "glue code" that
23
- you often have to write to make parts of your application work together.
23
+ With KVO, you can write functions that will be called automatically whenever
24
+ a property on a particular object changes. You can use this feature to
25
+ reduce the amount of "glue code" that you often write to tie the various
26
+ parts of your application together.
24
27
 
25
- Using KVO is easy. All you have to do is use the KVO methods to get and set
26
- properties. Instead of writing:
28
+ To use KVO, just use the KVO-aware methods get() and set() to access
29
+ properties instead of accessing properties directly. Instead of writing:
27
30
 
28
31
  {{{
29
32
  var aName = contact.firstName ;
30
33
  contact.firstName = 'Charles' ;
31
34
  }}}
32
-
33
- you use:
35
+
36
+ use:
34
37
 
35
38
  {{{
36
39
  var aName = contact.get('firstName') ;
@@ -40,8 +43,36 @@
40
43
  get() and set() work just like the normal "dot operators" provided by
41
44
  JavaScript but they provide you with much more power, including not only
42
45
  observing but computed properties as well.
46
+
47
+ h2. Observing Property Changes
48
+
49
+ You typically observe property changes simply by adding the observes()
50
+ call to the end of your method declarations in classes that you write. For
51
+ example:
52
+
53
+ {{{
54
+ SC.Object.create({
55
+ valueObserver: function() {
56
+ // Executes whenever the "Value" property changes
57
+ }.observes('value')
58
+ }) ;
59
+ }}}
60
+
61
+ Although this is the most common way to add an observer, this capability is
62
+ actually built into the SC.Object class on top of two methods defined in
63
+ this mixin called addObserver() and removeObserver(). You can use these two
64
+ methods to add and remove observers yourself if you need to do so at run
65
+ time.
66
+
67
+ To add an observer for a property, just call:
68
+
69
+ {{{
70
+ object.addObserver('propertyKey', targetObject, targetAction) ;
71
+ }}}
72
+
73
+ This will call the 'targetAction' method on the targetObject to be called
74
+ whenever the value of the propertyKey changes.
43
75
 
44
- INCOMPLETE
45
76
 
46
77
  */
47
78
  SC.Observable = {
@@ -674,9 +705,7 @@ SC.NotificationQueue = {
674
705
  this._flushing = false ;
675
706
 
676
707
  if (this.queue.length > 0) {
677
- this.invokeLater(this._reflush, 1) ;
708
+ SC.NotificationQueue.flush.invokeLater(SC.NotificationQueue, 1) ;
678
709
  }
679
- },
680
-
681
- _reflush: function() { SC.NotificationQueue.flush(); }
710
+ }
682
711
  } ;
@@ -112,7 +112,6 @@ SC.Scrollable = {
112
112
  Scrolls the receiver to the specified x,y coordinate
113
113
  */
114
114
  scrollTo: function(x,y) {
115
- console.log('scrollTo(%@,%@)'.fmt(x,y)) ;
116
115
  this.set('scrollFrame', { x: 0-x, y: 0-y }) ;
117
116
  },
118
117
 
@@ -135,16 +135,16 @@ Test.context("Chained SC.Controllers", {
135
135
  this.root.discardChanges() ;
136
136
  this.child.get('hasChanges').shouldEqual(NO) ;
137
137
  },
138
-
138
+
139
139
  "root.hasChanges should be FALSE after child.commit if root DOES NOT have other changes": function() {
140
140
  // edit child only
141
141
  this.child.editorDidChange() ;
142
142
  this.root.get('hasChanges').shouldEqual(YES) ;
143
-
143
+
144
144
  this.child.commitChanges() ;
145
145
  this.root.get('hasChanges').shouldEqual(NO) ;
146
146
  },
147
-
147
+
148
148
  "root.hasChanges should be TRUE after child.commit if root DOES have other changes": function() {
149
149
  // edit child and root
150
150
  this.child.editorDidChange() ;
@@ -154,7 +154,7 @@ Test.context("Chained SC.Controllers", {
154
154
  this.child.commitChanges() ;
155
155
  this.root.get('hasChanges').shouldEqual(YES) ;
156
156
  },
157
-
157
+
158
158
  "root.hasChanges should be FALSE after child.discard if root DOES NOT have other changes": function() {
159
159
  // edit child only
160
160
  this.child.editorDidChange() ;
@@ -173,8 +173,8 @@ Test.context("Chained SC.Controllers", {
173
173
  this.child.discardChanges() ;
174
174
  this.root.get('hasChanges').shouldEqual(YES) ;
175
175
  },
176
-
177
-
176
+
177
+
178
178
  "child.hasChanges should be TRUE if another child cannot commit": function() {
179
179
  // edit child and child2
180
180
  this.child.editorDidChange() ;
@@ -191,7 +191,7 @@ Test.context("Chained SC.Controllers", {
191
191
  this.child.get('hasChanges').shouldEqual(YES) ;
192
192
  this.root.get('hasChanges').shouldEqual(YES) ;
193
193
  },
194
-
194
+
195
195
  "child.hasChanges should be TRUE if another child cannot discard": function() {
196
196
  // edit child and child2
197
197
  this.child.editorDidChange() ;
@@ -208,12 +208,12 @@ Test.context("Chained SC.Controllers", {
208
208
  this.child.get('hasChanges').shouldEqual(YES) ;
209
209
  this.root.get('hasChanges').shouldEqual(YES) ;
210
210
  },
211
-
211
+
212
212
  "root.hasChanges should be TRUE if any child fails to commit": function() {
213
213
  // NOTE: Commits are not atomic. IN this case, child2 fails while child1 succeeds.
214
214
  // the hasChanges state of child2 and root must both be TRUE in this case, but
215
215
  // child1's state is undefined. It may have commited or it may not.
216
-
216
+
217
217
  this.child.editorDidChange() ;
218
218
  this.child2.editorDidChange() ;
219
219
  this.child2.isCommitSuccessful = NO ;
@@ -222,12 +222,12 @@ Test.context("Chained SC.Controllers", {
222
222
  this.root.get('hasChanges').shouldEqual(YES) ;
223
223
  this.child2.get('hasChanges').shouldEqual(YES) ;
224
224
  },
225
-
225
+
226
226
  "root.hasChanges should be TRUE if any child fails to discard": function() {
227
227
  // NOTE: Commits are not atomic. IN this case, child2 fails while child1 succeeds.
228
228
  // the hasChanges state of child2 and root must both be TRUE in this case, but
229
229
  // child1's state is undefined. It may have commited or it may not.
230
-
230
+
231
231
  this.child.editorDidChange() ;
232
232
  this.child2.editorDidChange() ;
233
233
  this.child2.isDiscardSuccessful = NO ;
@@ -236,7 +236,7 @@ Test.context("Chained SC.Controllers", {
236
236
  this.root.get('hasChanges').shouldEqual(YES) ;
237
237
  this.child2.get('hasChanges').shouldEqual(YES) ;
238
238
  },
239
-
239
+
240
240
  "child.commitChangesImmediately should be inherited from the root": function()
241
241
  {
242
242
  var Klass = SC.Controller.extend({ commitChangesImmediately: NO });
@@ -86,7 +86,7 @@ Test.context("SC.ObjectController", {
86
86
  this.c.get('value').shouldEqualEnum([0,1]) ;
87
87
  this.c.get('flag').shouldEqualEnum([YES, NO]) ;
88
88
 
89
- // get('array').shouldEqual: [ [0,0,0], [1,1,1] ];
89
+ // get('array').shouldEqual() [ [0,0,0], [1,1,1] ];
90
90
  var ar = this.c.get('array') ;
91
91
  ar.length.shouldEqual(2) ;
92
92
  ar[0].shouldEqualEnum([0,0,0]) ;
@@ -244,7 +244,7 @@ Test.context("SC.ObjectController", {
244
244
  this.c.get('value').shouldEqualEnum([0,1]) ;
245
245
  this.c.get('flag').shouldEqualEnum([YES, NO]) ;
246
246
 
247
- // get('array').shouldEqual: [ [0,0,0], [1,1,1] ];
247
+ // get('array').shouldEqual(): [ [0,0,0], [1,1,1] ];
248
248
  var ar = this.c.get('array') ;
249
249
  ar.length.shouldEqual(2) ;
250
250
  ar[0].shouldEqualEnum([0,0,0]) ;
@@ -186,11 +186,11 @@ SC.CollectionView = SC.View.extend(
186
186
  Trigger the action method on a single click.
187
187
 
188
188
  Normally, clicking on an item view in a collection will select the content
189
- object and double clicking will trigger the action method on the collection
190
- view.
189
+ object and double clicking will trigger the action method on the
190
+ collection view.
191
191
 
192
- If you set this property to true, then clicking on a view will both select it
193
- (if isSelected is true) and trigger the action method.
192
+ If you set this property to true, then clicking on a view will both select
193
+ it (if isSelected is true) and trigger the action method.
194
194
 
195
195
  Use this if you are using the collection view as a menu of items.
196
196
 
@@ -206,9 +206,9 @@ SC.CollectionView = SC.View.extend(
206
206
  passed property key. The exampleGroupView will be used to display the
207
207
  items in groups.
208
208
 
209
- If this property is set, you MUST ensure the items in the content array are
210
- already sorted by the group key. Otherwise item view groups might appear more
211
- than once.
209
+ If this property is set, you MUST ensure the items in the content array
210
+ are already sorted by the group key. Otherwise item view groups might
211
+ appear more than once.
212
212
 
213
213
  @type {String}
214
214
  */
@@ -217,25 +217,25 @@ SC.CollectionView = SC.View.extend(
217
217
  /**
218
218
  The view class to use when creating new item views.
219
219
 
220
- The collection view will automatically create an instance of the view class
221
- you set here for each item in its content array. You should provide your own
222
- subclass for this property to display the type of content you want.
220
+ The collection view will automatically create an instance of the view
221
+ class you set here for each item in its content array. You should provide
222
+ your own subclass for this property to display the type of content you
223
+ want.
223
224
 
224
225
  For best results, the view you set here should understand the following
225
226
  properties:
226
227
 
227
- {{{
228
- content: The content object from the content array your view should display
229
- isEnabled: True if the view should appear enabled
230
- isSelected: True if the view should appear selected
231
- }}}
228
+ - *content* The content object from the content array your view should display
229
+ - *isEnabled* True if the view should appear enabled
230
+ - *isSelected* True if the view should appear selected
232
231
 
233
- In general you do not want your child views to actually respond to mouse and
234
- keyboard events themselves. It is better to let the collection view do that.
232
+ In general you do not want your child views to actually respond to mouse
233
+ and keyboard events themselves. It is better to let the collection view
234
+ do that.
235
235
 
236
- If you do implement your own event handlers such as mouseDown or mouseUp, you
237
- should be sure to actually call the same method on the collection view to
238
- give it the chance to perform its own selection housekeeping.
236
+ If you do implement your own event handlers such as mouseDown or mouseUp,
237
+ you should be sure to actually call the same method on the collection view
238
+ to give it the chance to perform its own selection housekeeping.
239
239
 
240
240
  @type {SC.View}
241
241
  */
@@ -245,21 +245,21 @@ SC.CollectionView = SC.View.extend(
245
245
  The view class to use when displaying item views in groups.
246
246
 
247
247
  If the groupBy property is not null, then the collection view will create
248
- an instance of this view class with the item views that belong to the group
249
- as child nodes for each distinct group value it encounters.
248
+ an instance of this view class with the item views that belong to the
249
+ group as child nodes for each distinct group value it encounters.
250
250
 
251
251
  Your groupView should have two outlets:
252
252
 
253
253
  {{{
254
- labelView: The view to display the group label. The group value will be set
255
- as the content property of this view.
254
+ labelView: The view to display the group label. The group value will be
255
+ set as the content property of this view.
256
256
 
257
- itemView: This is the view the item views will be added to as children to
258
- this view.
257
+ itemView: This is the view the item views will be added to as children
258
+ to this view.
259
259
  }}}
260
260
 
261
- If groupBy is null, then this property will not be used. The default class
262
- provided here simply displays the group value in an H1 tag.
261
+ If groupBy is null, then this property will not be used. The default
262
+ class provided here simply displays the group value in an H1 tag.
263
263
 
264
264
  @type {SC.View}
265
265
  */
@@ -271,20 +271,21 @@ SC.CollectionView = SC.View.extend(
271
271
  }),
272
272
 
273
273
  /**
274
- Invoked when the user double clicks on an item (or single clicks of actOnSelect is true)
274
+ Invoked when the user double clicks on an item (or single clicks of
275
+ actOnSelect is true)
275
276
 
276
277
  Set this to the name of the action you want to send down the
277
- responder chain when the user double clicks on an item (or single clicks if
278
- actOnSelect is true). You can optionally specify a specific target as well
279
- using the target property.
278
+ responder chain when the user double clicks on an item (or single clicks
279
+ if actOnSelect is true). You can optionally specify a specific target as
280
+ well using the target property.
280
281
 
281
282
  If you do not specify an action, then the collection view will also try to
282
283
  invoke the action named on the target item view.
283
284
 
284
- Older versions of SproutCore expected the action property to contain an actual
285
- function that would be run. This format is still supported but is deprecated
286
- for future use. You should generally use the responder chain to handle your
287
- action for you.
285
+ Older versions of SproutCore expected the action property to contain an
286
+ actual function that would be run. This format is still supported but is
287
+ deprecated for future use. You should generally use the responder chain
288
+ to handle your action for you.
288
289
 
289
290
  @type {String}
290
291
  */
@@ -293,10 +294,10 @@ SC.CollectionView = SC.View.extend(
293
294
  /**
294
295
  Optional target to send the action to when the user double clicks.
295
296
 
296
- If you set the action property to the name of an action, you can optionally
297
- specify the target object you want the action to be sent to. This can be
298
- either an actual object or a property path that will resolve to an object at
299
- the time that the action is invoked.
297
+ If you set the action property to the name of an action, you can
298
+ optionally specify the target object you want the action to be sent to.
299
+ This can be either an actual object or a property path that will resolve
300
+ to an object at the time that the action is invoked.
300
301
 
301
302
  This property is ignored if you use the deprecated approach of making the
302
303
  action property a function.
@@ -309,7 +310,8 @@ SC.CollectionView = SC.View.extend(
309
310
  Set to true whenever the content changes and remains true until
310
311
  the content has been rerendered.
311
312
 
312
- You can also set this to true yourself to be notified when it is completed.
313
+ You can also set this to true yourself to be notified when it is
314
+ completed.
313
315
  */
314
316
  isDirty: false,
315
317
 
@@ -355,8 +357,8 @@ SC.CollectionView = SC.View.extend(
355
357
  The CollectionView will use this property to support keyboard navigation
356
358
  using the arrow keys.
357
359
 
358
- If your collection view is simply a vertical list of items then you do not need
359
- to edit this property.
360
+ If your collection view is simply a vertical list of items then you do not
361
+ need to edit this property.
360
362
  */
361
363
  itemsPerRow: 1,
362
364
 
@@ -770,13 +772,15 @@ SC.CollectionView = SC.View.extend(
770
772
  },
771
773
 
772
774
  /**
773
- Update the selection state for the item views to reflect the selection array.
775
+ Update the selection state for the item views to reflect the selection
776
+ array.
774
777
 
775
- This will update the isSelected property of all item views so that only those
776
- representing content objects found in the selection array are selected.
778
+ This will update the isSelected property of all item views so that only
779
+ those representing content objects found in the selection array are
780
+ selected.
777
781
 
778
- This method is called automatically whenever your content or selection properties
779
- changed. You should not need to call or override it often.
782
+ This method is called automatically whenever your content or selection
783
+ properties changed. You should not need to call or override it often.
780
784
  */
781
785
  updateSelectionStates: function() {
782
786
  if (!this._itemViews) return ;
@@ -1275,12 +1279,15 @@ SC.CollectionView = SC.View.extend(
1275
1279
 
1276
1280
  /**
1277
1281
  Select one or more items before the current selection, optionally
1278
- extending the current selection. Also scrolls the selected item into view.
1282
+ extending the current selection. Also scrolls the selected item into
1283
+ view.
1279
1284
 
1280
1285
  Selection does not wrap around.
1281
1286
 
1282
- @param extend {Boolean} (Optional) If true, the selection will be extended instead of replaced. Defaults to false.
1283
- @param numberOfItems {Integer} (Optional) The number of previous to be selected. Defaults to 1
1287
+ @param extend {Boolean} (Optional) If true, the selection will be extended
1288
+ instead of replaced. Defaults to false.
1289
+ @param numberOfItems {Integer} (Optional) The number of previous to be
1290
+ selected. Defaults to 1
1284
1291
  @returns {void}
1285
1292
  */
1286
1293
  selectPreviousItem: function(extend, numberOfItems)
@@ -1341,8 +1348,10 @@ SC.CollectionView = SC.View.extend(
1341
1348
 
1342
1349
  Selection does not wrap around.
1343
1350
 
1344
- @param extend {Boolean} (Optional) If true, the selection will be extended instead of replaced. Defaults to false.
1345
- @param numberOfItems {Integer} (Optional) The number of items to be selected. Defaults to 1.
1351
+ @param extend {Boolean} (Optional) If true, the selection will be extended
1352
+ instead of replaced. Defaults to false.
1353
+ @param numberOfItems {Integer} (Optional) The number of items to be
1354
+ selected. Defaults to 1.
1346
1355
  @returns {void}
1347
1356
  */
1348
1357
  selectNextItem: function(extend, numberOfItems)
@@ -1398,9 +1407,9 @@ SC.CollectionView = SC.View.extend(
1398
1407
  },
1399
1408
 
1400
1409
  /**
1401
- * Scroll the rootElement (if needed) to ensure that the item is visible.
1402
- * @param {SC.Record} record The record to scroll to
1403
- * @returns {void}
1410
+ Scroll the rootElement (if needed) to ensure that the item is visible.
1411
+ @param {SC.Record} record The record to scroll to
1412
+ @returns {void}
1404
1413
  */
1405
1414
  scrollToContent: function(record) {
1406
1415
  // find the itemView. if not present, add one.
@@ -1414,9 +1423,9 @@ SC.CollectionView = SC.View.extend(
1414
1423
  if (itemView) this.scrollToItemView(itemView);
1415
1424
  },
1416
1425
  /**
1417
- * Scroll the rootElement (if needed) to ensure that the item is visible.
1418
- * @param {SC.View} view The item view to scroll to
1419
- * @returns {void}
1426
+ Scroll the rootElement (if needed) to ensure that the item is visible.
1427
+ @param {SC.View} view The item view to scroll to
1428
+ @returns {void}
1420
1429
  */
1421
1430
  scrollToItemView: function( view )
1422
1431
  {
@@ -1434,7 +1443,8 @@ SC.CollectionView = SC.View.extend(
1434
1443
  current selection.
1435
1444
 
1436
1445
  @param items {Array} The item or items to select.
1437
- @param extendSelection {Boolean} If true, extends the selection instead of replacing it.
1446
+ @param extendSelection {Boolean} If true, extends the selection instead of
1447
+ replacing it.
1438
1448
  */
1439
1449
  selectItems: function(items, extendSelection) {
1440
1450
  var base = (extendSelection) ? this.get('selection') : [] ;
@@ -1537,7 +1547,7 @@ SC.CollectionView = SC.View.extend(
1537
1547
  // Make sure that saved mouseDown state is always reset in case we do
1538
1548
  // not get a paired mouseUp. (Only happens if subclass does not call us
1539
1549
  // like it should)
1540
- this._mouseDownAt = this._shouldDeselect =
1550
+ this._mouseDownAt = this._shouldDeselect =
1541
1551
  this._shouldReselect = this._refreshSelection = false;
1542
1552
 
1543
1553
  var mouseDownView = this._mouseDownView = this.itemViewForEvent(ev);
@@ -1574,8 +1584,8 @@ SC.CollectionView = SC.View.extend(
1574
1584
  selection = this._findSelectionExtendedByShift(selection, mouseDownContent) ;
1575
1585
  this.selectItems(selection) ;
1576
1586
 
1577
- // If no modifier key was pressed, then clicking on the selected item should clear
1578
- // the selection and reselect only the clicked on item.
1587
+ // If no modifier key was pressed, then clicking on the selected item
1588
+ // should clear the selection and reselect only the clicked on item.
1579
1589
  } else if (!modifierKeyPressed && isSelected) {
1580
1590
  this._shouldReselect = mouseDownContent;
1581
1591
 
@@ -1608,12 +1618,33 @@ SC.CollectionView = SC.View.extend(
1608
1618
 
1609
1619
  } else {
1610
1620
  if (this._shouldDeselect) this.deselectItems(this._shouldDeselect);
1611
- if (this._shouldReselect) this.selectItems(this._shouldReselect,false) ;
1612
1621
 
1613
- // this is invoked if the user clicked on a checkbox. If this is not
1614
- // done then the checkbox might not update properly.
1615
- if (this._refreshSelection) {
1622
+ // begin editing of an item view IF all of the following is true:
1623
+ // otherwise, just reselect.
1624
+ if (this._shouldReselect) {
1625
+
1626
+ // - contentValueIsEditable is true
1627
+ var canEdit = this.get('contentValueIsEditable') ;
1628
+
1629
+ // - the user clicked on an item that was already selected
1630
+ // - is the only item selected
1631
+ if (canEdit) {
1632
+ var sel = this.get('selection') ;
1633
+ canEdit = sel && (sel.get('length') === 1) && (sel.objectAt(0) === this._shouldReselect) ;
1634
+ }
1635
+
1636
+ // - the item view responds to contentHitTest() and returns YES.
1637
+ // - the item view responds to beginEditing and returns YES.
1638
+ if (canEdit) {
1639
+ var itemView = this.itemViewForContent(this._shouldReselect) ;
1640
+ canEdit = itemView && (!itemView.contentHitTest || itemView.contentHitTest(ev)) ;
1641
+ canEdit = (canEdit && itemView.beginEditing) ? itemView.beginEditing() : NO ;
1642
+ }
1643
+
1644
+ // if cannot edit, just reselect
1645
+ if (!canEdit) this.selectItems(this._shouldReselect,false) ;
1616
1646
  }
1647
+
1617
1648
  this._cleanupMouseDown() ;
1618
1649
  }
1619
1650
 
@@ -1713,6 +1744,29 @@ SC.CollectionView = SC.View.extend(
1713
1744
  },
1714
1745
 
1715
1746
 
1747
+ // if content value is editable and we have one item selected, then edit.
1748
+ // otherwise, invoke action.
1749
+ insertNewline: function() {
1750
+ if (this.get('contentValueIsEditable')) {
1751
+ var sel = this.get('selection') ;
1752
+ if (sel && sel.get('length') === 1) {
1753
+ var itemView = this.itemViewForContent(sel.objectAt(0)) ;
1754
+ if (itemView && itemView.beginEditing) {
1755
+ this.scrollToItemView(itemView) ;
1756
+ itemView.beginEditing() ;
1757
+ }
1758
+ }
1759
+
1760
+ // invoke action!
1761
+ } else {
1762
+ var sel = this.get('selection') ;
1763
+ var itemView = (sel && sel.get('length') === 1) ? this.itemViewForContent(sel.objectAt(0)) : null ;
1764
+ this._action(itemView, null) ;
1765
+ }
1766
+
1767
+ return YES ; // always handle
1768
+ },
1769
+
1716
1770
  // ......................................
1717
1771
  // FIRST RESPONDER
1718
1772
  //
@@ -1898,7 +1952,7 @@ SC.CollectionView = SC.View.extend(
1898
1952
  if ((Date.now() - this._mouseDownAt) < 123) return true ;
1899
1953
 
1900
1954
  // OK, they must be serious, start a drag if possible.
1901
- if (this.get('canReorderContent')) {
1955
+ if (this.get('canReorderContent') && this._mouseDownEvent != null) {
1902
1956
 
1903
1957
  // First, get the selection to drag. Drag an array of selected
1904
1958
  // items appearing in this collection, in the order of the