sproutcore 0.9.0 → 0.9.1
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.
- data/Manifest.txt +42 -29
- data/README.txt +1 -1
- data/Rakefile +5 -0
- data/app_generators/sproutcore/sproutcore_generator.rb +4 -4
- data/app_generators/sproutcore/templates/sc-config.rb +72 -0
- data/bin/sc-build +2 -0
- data/clients/sc_docs/controllers/docs.js +1 -0
- data/clients/sc_docs/english.lproj/body.rhtml +5 -0
- data/clients/sc_docs/views/doc_frame.js +1 -1
- data/clients/sc_test_runner/controllers/runner.js +2 -2
- data/clients/sc_test_runner/english.lproj/body.rhtml +5 -0
- data/clients/sc_test_runner/main.js +12 -12
- data/clients/sc_test_runner/models/test.js +3 -0
- data/clients/sc_test_runner/views/test_label.js +1 -1
- data/config/hoe.rb +2 -0
- data/frameworks/sproutcore/controllers/array.js +132 -125
- data/frameworks/sproutcore/drag/drag.js +5 -2
- data/frameworks/sproutcore/english.lproj/buttons.css +64 -64
- data/frameworks/sproutcore/english.lproj/collections.css +82 -0
- data/frameworks/sproutcore/english.lproj/images/buttons-sprite.png +0 -0
- data/frameworks/sproutcore/english.lproj/images/sproutcore-logo.png +0 -0
- data/frameworks/sproutcore/english.lproj/images/sticky-note.png +0 -0
- data/frameworks/sproutcore/english.lproj/menu.css +1 -1
- data/frameworks/sproutcore/english.lproj/theme.css +13 -4
- data/frameworks/sproutcore/foundation/array.js +24 -2
- data/frameworks/sproutcore/foundation/benchmark.js +12 -5
- data/frameworks/sproutcore/foundation/observable.js +62 -1
- data/frameworks/sproutcore/foundation/utils.js +16 -0
- data/frameworks/sproutcore/tests/views/label_item.rhtml +21 -0
- data/frameworks/sproutcore/tests/views/list.rhtml +21 -0
- data/frameworks/sproutcore/tests/views/scroll.rhtml +21 -0
- data/frameworks/sproutcore/views/collection.js +401 -73
- data/frameworks/sproutcore/views/collection/collection_item.js +36 -0
- data/frameworks/sproutcore/views/collection/grid.js +149 -0
- data/frameworks/sproutcore/views/collection/image_cell.js +154 -0
- data/frameworks/sproutcore/views/collection/list.js +115 -0
- data/frameworks/sproutcore/views/collection/text_cell.js +128 -0
- data/frameworks/sproutcore/views/image.js +1 -1
- data/frameworks/sproutcore/views/label.js +6 -2
- data/frameworks/sproutcore/views/scroll.js +34 -0
- data/frameworks/sproutcore/views/view.js +12 -4
- data/generators/client/client_generator.rb +3 -11
- data/generators/client/templates/english.lproj/body.css +75 -0
- data/generators/client/templates/english.lproj/body.rhtml +17 -2
- data/generators/model/templates/fixture.js +32 -0
- data/lib/sproutcore/build_tools/html_builder.rb +29 -11
- data/lib/sproutcore/build_tools/resource_builder.rb +1 -1
- data/lib/sproutcore/bundle.rb +19 -7
- data/lib/sproutcore/library.rb +39 -21
- data/lib/sproutcore/merb/bundle_controller.rb +3 -8
- data/lib/sproutcore/version.rb +1 -1
- data/lib/sproutcore/view_helpers.rb +7 -5
- data/lib/sproutcore/view_helpers/core_views.rb +11 -3
- data/sc-config.rb +7 -0
- data/tasks/deployment.rake +15 -2
- metadata +44 -31
- data/app_generators/sproutcore/templates/environment.yml +0 -4
- data/environment.yml +0 -9
@@ -0,0 +1,82 @@
|
|
1
|
+
/* @override http://localhost:4020/static/sproutcore/en/_cache/collections-1205614097.css */
|
2
|
+
|
3
|
+
/* @group Common */
|
4
|
+
|
5
|
+
body {
|
6
|
+
overflow: hidden;
|
7
|
+
}
|
8
|
+
|
9
|
+
.collection-item {
|
10
|
+
cursor: pointer ;
|
11
|
+
}
|
12
|
+
|
13
|
+
/* @end */
|
14
|
+
|
15
|
+
/* @group List View */
|
16
|
+
|
17
|
+
.sc-theme .sc-collection-view {
|
18
|
+
background-color: white ;
|
19
|
+
}
|
20
|
+
|
21
|
+
.sc-theme .list-insertion-point {
|
22
|
+
border: 1px #4e4977 solid;
|
23
|
+
position: absolute ;
|
24
|
+
}
|
25
|
+
|
26
|
+
.sc-theme .list-insertion-point .anchor {
|
27
|
+
position: absolute ;
|
28
|
+
width: 7px;
|
29
|
+
height: 7px;
|
30
|
+
left: -6px;
|
31
|
+
top: -4px;
|
32
|
+
background: static_url('images/buttons-sprite.png') no-repeat 0px -931px;
|
33
|
+
}
|
34
|
+
|
35
|
+
/* @end */
|
36
|
+
|
37
|
+
/* @group Grid View */
|
38
|
+
|
39
|
+
.sc-theme .grid-insertion-point {
|
40
|
+
border: 1px #4e4977 solid;
|
41
|
+
position: absolute ;
|
42
|
+
}
|
43
|
+
|
44
|
+
.sc-theme .grid-insertion-point .anchor {
|
45
|
+
position: absolute ;
|
46
|
+
width: 7px;
|
47
|
+
height: 7px;
|
48
|
+
left: -4px;
|
49
|
+
top: -6px;
|
50
|
+
background: static_url('images/buttons-sprite.png') no-repeat 0px -931px;
|
51
|
+
}
|
52
|
+
|
53
|
+
/* @end */
|
54
|
+
|
55
|
+
/* @group Text Cell */
|
56
|
+
|
57
|
+
.sc-theme .collection-item {
|
58
|
+
text-decoration: none ;
|
59
|
+
color: #000;
|
60
|
+
border-top: 1px white solid;
|
61
|
+
background-color: white ;
|
62
|
+
}
|
63
|
+
|
64
|
+
.sc-theme .collection-item.text-cell {
|
65
|
+
display: block ;
|
66
|
+
padding: 0 6px;
|
67
|
+
line-height: 22px;
|
68
|
+
}
|
69
|
+
.sc-theme .collection-item.sel {
|
70
|
+
background-color: #ddd;
|
71
|
+
border-top: 1px solid #eee;
|
72
|
+
}
|
73
|
+
|
74
|
+
.sc-theme .sc-collection-view.focus .collection-item.sel {
|
75
|
+
background-color: #40007e;
|
76
|
+
color: white ;
|
77
|
+
border-top: 1px solid #84788f;
|
78
|
+
}
|
79
|
+
|
80
|
+
/* @end */
|
81
|
+
|
82
|
+
|
Binary file
|
Binary file
|
Binary file
|
@@ -1,3 +1,12 @@
|
|
1
|
+
/* @override http://localhost:4020/static/sproutcore/en/_cache/theme-1205438214.css */
|
2
|
+
|
3
|
+
/* @group Root Container */
|
4
|
+
|
5
|
+
.sc-scroll-view {
|
6
|
+
overflow: auto ;
|
7
|
+
}
|
8
|
+
|
9
|
+
/* @end */
|
1
10
|
|
2
11
|
/* @group Root Form */
|
3
12
|
|
@@ -24,16 +33,16 @@ input.show-hint {
|
|
24
33
|
/* @group sc-theme */
|
25
34
|
|
26
35
|
body.sc-theme {
|
27
|
-
font:
|
36
|
+
font: 13px Helvetica, Verdana, sans-serif;
|
37
|
+
line-height: 18px;
|
28
38
|
background-color: #f0f0f0 ;
|
29
|
-
padding:
|
39
|
+
padding: 0px;
|
30
40
|
}
|
31
41
|
|
32
42
|
.sc-theme h1 {
|
33
|
-
margin: 0
|
43
|
+
margin: 0;
|
34
44
|
padding: 0 ;
|
35
45
|
margin-bottom: 10px;
|
36
|
-
border-bottom: 1px #888 dotted ;
|
37
46
|
}
|
38
47
|
|
39
48
|
.sc-theme label {
|
@@ -68,6 +68,7 @@ SC.Array = {
|
|
68
68
|
*/
|
69
69
|
objectAt: function(idx)
|
70
70
|
{
|
71
|
+
if (idx < 0) return undefined ;
|
71
72
|
if (idx >= this.get('length')) return undefined;
|
72
73
|
return this.get(idx);
|
73
74
|
},
|
@@ -86,7 +87,8 @@ SC.Array = {
|
|
86
87
|
notified.
|
87
88
|
*/
|
88
89
|
arrayContentDidChange: function() {
|
89
|
-
this.
|
90
|
+
var kvo = (this._kvo) ? this._kvo().changes : '(null)';
|
91
|
+
this.notifyPropertyChange('[]') ;
|
90
92
|
},
|
91
93
|
|
92
94
|
/**
|
@@ -204,8 +206,27 @@ SC.Array = {
|
|
204
206
|
// enumerable methods since Arrays are already enumerable.
|
205
207
|
Object.extend(Array.prototype, SC.Array) ;
|
206
208
|
|
207
|
-
// Now make SC.Array enumerable
|
209
|
+
// Now make SC.Array enumerable and add other array method we did not want to
|
210
|
+
// override in Array itself.
|
208
211
|
Object.extend(SC.Array, Enumerable) ;
|
212
|
+
Object.extend(SC.Array, {
|
213
|
+
/**
|
214
|
+
Returns a new array that is a slice of the receiver. This implementation
|
215
|
+
uses the observable array methods to retrieve the objects for the new slice.
|
216
|
+
|
217
|
+
@param beginIndex {Integer} (Optional) index to begin slicing from. Default: 0
|
218
|
+
@param endIndex {Integer} (Optional) index to end the slice at. Default: 0
|
219
|
+
*/
|
220
|
+
slice: function(beginIndex, endIndex) {
|
221
|
+
var ret = [];
|
222
|
+
var length = this.get('length') ;
|
223
|
+
if (beginIndex == null) beginIndex = 0 ;
|
224
|
+
if ((endIndex == null) || (endIndex > length)) endIndex = length ;
|
225
|
+
while(beginIndex < endIndex) ret[ret.length] = this.objectAt(beginIndex++) ;
|
226
|
+
return ret ;
|
227
|
+
}
|
228
|
+
|
229
|
+
}) ;
|
209
230
|
|
210
231
|
// ........................................................
|
211
232
|
// A few basic enhancements to the Array class.
|
@@ -298,6 +319,7 @@ Object.extend(Array.prototype, {
|
|
298
319
|
if (value !== undefined) return null ;
|
299
320
|
return this.invoke('get', key) ;
|
300
321
|
}
|
322
|
+
|
301
323
|
}) ;
|
302
324
|
|
303
325
|
Array.prototype.collect = Array.prototype.map ;
|
@@ -58,23 +58,30 @@ SC.Benchmark = {
|
|
58
58
|
Call this method at the start of whatever you want to collect.
|
59
59
|
if topLevelOnly is passed, then recursive calls to the start will be
|
60
60
|
ignored and only the top level call will be benchmarked.
|
61
|
+
|
62
|
+
@param key {String} A unique key that identifies this benchmark. All calls to start/end with the same key will be groups together.
|
63
|
+
@param topLevelOnly {Boolean} If true then recursive calls to this method with the same key will be ignored.
|
64
|
+
@param time {Integer} Only pass if you want to explicitly set the start time. Otherwise the start time is now.
|
61
65
|
*/
|
62
|
-
start: function(key, topLevelOnly) {
|
66
|
+
start: function(key, topLevelOnly, time) {
|
63
67
|
if (!this.enabled) return ;
|
64
68
|
var stat = this._statFor(key) ;
|
65
69
|
|
66
70
|
if (topLevelOnly && stat._starts.length > 0) {
|
67
71
|
stat._starts.push('ignore') ;
|
68
72
|
} else {
|
69
|
-
stat._starts.push(Date.now()) ;
|
73
|
+
stat._starts.push(time || Date.now()) ;
|
70
74
|
}
|
71
75
|
},
|
72
76
|
|
73
77
|
/**
|
74
78
|
Call this method at the end of whatever you want to collect. This will
|
75
79
|
save the collected benchmark.
|
80
|
+
|
81
|
+
@param key {String} The benchmark key you used when you called start()
|
82
|
+
@param time {Integer} Only pass if you want to explicitly set the end time. Otherwise start time is now.
|
76
83
|
*/
|
77
|
-
end: function(key) {
|
84
|
+
end: function(key, time) {
|
78
85
|
if (!this.enabled) return ;
|
79
86
|
var stat = this._statFor(key) ;
|
80
87
|
var start = stat._starts.pop() ;
|
@@ -86,7 +93,7 @@ SC.Benchmark = {
|
|
86
93
|
// top level only.
|
87
94
|
if (start == 'ignore') return ;
|
88
95
|
|
89
|
-
stat.amt += Date.now() - start ;
|
96
|
+
stat.amt += (time || Date.now()) - start ;
|
90
97
|
stat.runs++ ;
|
91
98
|
|
92
99
|
if (this.verbose) this.log(key) ;
|
@@ -198,7 +205,7 @@ SC.Benchmark = {
|
|
198
205
|
return ret ;
|
199
206
|
},
|
200
207
|
|
201
|
-
reset: function() { this.stats = {} ;
|
208
|
+
reset: function() { this.stats = {} ; debugger;},
|
202
209
|
|
203
210
|
// This is private, but it is used in some places, so we are keeping this for
|
204
211
|
// compatibility.
|
@@ -174,6 +174,25 @@ SC.Observable = {
|
|
174
174
|
if (tuple[0] == null) return null ;
|
175
175
|
return tuple[0].set(tuple[1], value) ;
|
176
176
|
},
|
177
|
+
|
178
|
+
|
179
|
+
/**
|
180
|
+
Convenience method to get an array of properties.
|
181
|
+
|
182
|
+
Pass in multiple property keys or an array of property keys. This
|
183
|
+
method uses getPath() so you can also pass key paths.
|
184
|
+
|
185
|
+
@returns {Array} Values of property keys.
|
186
|
+
*/
|
187
|
+
getEach: function() {
|
188
|
+
var keys = $A(arguments).flatten() ;
|
189
|
+
var ret = [];
|
190
|
+
for(var idx=0; idx<keys.length;idx++) {
|
191
|
+
ret[ret.length] = this.getPath(keys[idx]);
|
192
|
+
}
|
193
|
+
return ret ;
|
194
|
+
},
|
195
|
+
|
177
196
|
|
178
197
|
/**
|
179
198
|
Increments the value of a property.
|
@@ -228,18 +247,60 @@ SC.Observable = {
|
|
228
247
|
},
|
229
248
|
|
230
249
|
/**
|
231
|
-
|
250
|
+
Notify the observer system that a property is about to change.
|
251
|
+
|
252
|
+
Sometimes you need to change a value directly or indirectly without actually
|
253
|
+
calling get() or set() on it. In this case, you can use this method and
|
254
|
+
propertyDidChange() instead. Calling these two methods together will notify all
|
255
|
+
observers that the property has potentially changed value.
|
256
|
+
|
257
|
+
Note that you must always call propertyWillChange and propertyDidChange as a pair.
|
258
|
+
If you do not, it may get the property change groups out of order and cause
|
259
|
+
notifications to be delivered more often than you would like.
|
260
|
+
|
261
|
+
@param key {String} The property key that is about to change.
|
232
262
|
*/
|
233
263
|
propertyWillChange: function(key) {
|
234
264
|
this._kvo().changes++ ;
|
235
265
|
},
|
236
266
|
|
267
|
+
/**
|
268
|
+
Notify the observer system that a property has just changed.
|
269
|
+
|
270
|
+
Sometimes you need to change a value directly or indirectly without actually
|
271
|
+
calling get() or set() on it. In this case, you can use this method and
|
272
|
+
propertyWillChange() instead. Calling these two methods together will notify all
|
273
|
+
observers that the property has potentially changed value.
|
274
|
+
|
275
|
+
Note that you must always call propertyWillChange and propertyDidChange as a pair.
|
276
|
+
If you do not, it may get the property change groups out of order and cause
|
277
|
+
notifications to be delivered more often than you would like.
|
278
|
+
|
279
|
+
@param key {String} The property key that has just changed.
|
280
|
+
@param value {Object} The new value of the key. May be null.
|
281
|
+
*/
|
237
282
|
propertyDidChange: function(key,value) {
|
238
283
|
this._kvo().changed[key] = value ;
|
239
284
|
var kvo = this._kvo() ; kvo.changes--; kvo.revision++ ;
|
240
285
|
if (kvo.changes <= 0) this._notifyPropertyObservers() ;
|
241
286
|
},
|
242
287
|
|
288
|
+
/**
|
289
|
+
Convenience method to call propertyWillChange/propertyDidChange.
|
290
|
+
|
291
|
+
Sometimes you need to notify observers that a property has changed value without
|
292
|
+
actually changing this value. In those cases, you can use this method as a
|
293
|
+
convenience instead of calling propertyWillChange() and propertyDidChange().
|
294
|
+
|
295
|
+
@param key {String} The property key that has just changed.
|
296
|
+
@param value {Object} The new value of the key. May be null.
|
297
|
+
@returns {void}
|
298
|
+
*/
|
299
|
+
notifyPropertyChange: function(key, value) {
|
300
|
+
this.propertyWillChange(key) ;
|
301
|
+
this.propertyDidChange(key, value) ;
|
302
|
+
},
|
303
|
+
|
243
304
|
/**
|
244
305
|
This may be a simpler way to notify of changes if you are making a major
|
245
306
|
update or don't know exactly which properties have changed. This ignores
|
@@ -56,6 +56,22 @@ Object.extend(SC,
|
|
56
56
|
(point.y >= SC.minY(f)) &&
|
57
57
|
(point.x <= SC.maxX(f)) &&
|
58
58
|
(point.y <= SC.maxY(f)) ;
|
59
|
+
},
|
60
|
+
|
61
|
+
/** Return true if the two frames match.
|
62
|
+
|
63
|
+
@param r1 {Rect} the first rect
|
64
|
+
@param r2 {Rect} the second rect
|
65
|
+
@param delta {Float} an optional delta that allows for rects that do not match exactly. Defaults to 0.1
|
66
|
+
@returns {Boolean} true if rects match
|
67
|
+
*/
|
68
|
+
rectsEqual: function(r1, r2, delta) {
|
69
|
+
if (delta == null) delta = 0.1;
|
70
|
+
if (Math.abs(r1.y - r2.y) > delta) return false ;
|
71
|
+
if (Math.abs(r1.x - r2.x) > delta) return false ;
|
72
|
+
if (Math.abs(r1.width - r2.width) > delta) return false ;
|
73
|
+
if (Math.abs(r1.height - r2.height) > delta) return false ;
|
74
|
+
return true ;
|
59
75
|
}
|
60
76
|
|
61
77
|
}) ;
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<% # ========================================================================
|
2
|
+
# Sproutcore.LabelItemView Unit Test
|
3
|
+
# ========================================================================
|
4
|
+
%>
|
5
|
+
<% content_for('final') do %>
|
6
|
+
|
7
|
+
<script>
|
8
|
+
|
9
|
+
Test.context("Sproutcore.LabelItemView",{
|
10
|
+
|
11
|
+
"TODO: Add your own tests here": function() {
|
12
|
+
true.shouldEqual(true) ;
|
13
|
+
}
|
14
|
+
|
15
|
+
}) ;
|
16
|
+
|
17
|
+
appMain = main; main = null ; // Cancel main() so app does not start
|
18
|
+
|
19
|
+
</script>
|
20
|
+
|
21
|
+
<% end %>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<% # ========================================================================
|
2
|
+
# Sproutcore.ListView Unit Test
|
3
|
+
# ========================================================================
|
4
|
+
%>
|
5
|
+
<% content_for('final') do %>
|
6
|
+
|
7
|
+
<script>
|
8
|
+
|
9
|
+
Test.context("Sproutcore.ListView",{
|
10
|
+
|
11
|
+
"TODO: Add your own tests here": function() {
|
12
|
+
true.shouldEqual(true) ;
|
13
|
+
}
|
14
|
+
|
15
|
+
}) ;
|
16
|
+
|
17
|
+
appMain = main; main = null ; // Cancel main() so app does not start
|
18
|
+
|
19
|
+
</script>
|
20
|
+
|
21
|
+
<% end %>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<% # ========================================================================
|
2
|
+
# Sproutcore.ScrollView Unit Test
|
3
|
+
# ========================================================================
|
4
|
+
%>
|
5
|
+
<% content_for('final') do %>
|
6
|
+
|
7
|
+
<script>
|
8
|
+
|
9
|
+
Test.context("Sproutcore.ScrollView",{
|
10
|
+
|
11
|
+
"TODO: Add your own tests here": function() {
|
12
|
+
true.shouldEqual(true) ;
|
13
|
+
}
|
14
|
+
|
15
|
+
}) ;
|
16
|
+
|
17
|
+
appMain = main; main = null ; // Cancel main() so app does not start
|
18
|
+
|
19
|
+
</script>
|
20
|
+
|
21
|
+
<% end %>
|
@@ -351,7 +351,8 @@ SC.CollectionView = SC.View.extend(
|
|
351
351
|
|
352
352
|
/**
|
353
353
|
Property returns all of the item views, regardless of group view.
|
354
|
-
|
354
|
+
|
355
|
+
@property
|
355
356
|
@returns {Array} the item views.
|
356
357
|
*/
|
357
358
|
itemViews: function() {
|
@@ -363,6 +364,31 @@ SC.CollectionView = SC.View.extend(
|
|
363
364
|
return ret;
|
364
365
|
}.property(),
|
365
366
|
|
367
|
+
/**
|
368
|
+
The property on content objects item views should display.
|
369
|
+
|
370
|
+
Most built-in item views will respect this property. You can also use it when writing
|
371
|
+
you own item views.
|
372
|
+
*/
|
373
|
+
displayProperty: null,
|
374
|
+
|
375
|
+
/**
|
376
|
+
Enables keyboard-based navigate if set to true.
|
377
|
+
*/
|
378
|
+
acceptsFirstResponder: false,
|
379
|
+
|
380
|
+
/**
|
381
|
+
If your layout uses a grid or horizontal-based layout, then make sure this
|
382
|
+
property is always up to date with the current number of items per row.
|
383
|
+
|
384
|
+
The CollectionView will use this property to support keyboard navigation
|
385
|
+
using the arrow keys.
|
386
|
+
|
387
|
+
If your collection view is simply a vertical list of items then you do not need
|
388
|
+
to edit this property.
|
389
|
+
*/
|
390
|
+
itemsPerRow: 1,
|
391
|
+
|
366
392
|
/**
|
367
393
|
Returns true if the passed view belongs to the collection.
|
368
394
|
|
@@ -379,6 +405,22 @@ SC.CollectionView = SC.View.extend(
|
|
379
405
|
return !!this._itemViews[SC.getGUID(view)];
|
380
406
|
},
|
381
407
|
|
408
|
+
// ......................................
|
409
|
+
// FIRST RESPONDER
|
410
|
+
//
|
411
|
+
|
412
|
+
/**
|
413
|
+
Called whenever the collection becomes first responder.
|
414
|
+
Adds the focused class to the element.
|
415
|
+
*/
|
416
|
+
didBecomeFirstResponder: function() {
|
417
|
+
this.addClassName('focus') ;
|
418
|
+
},
|
419
|
+
|
420
|
+
willLoseFirstResponder: function() {
|
421
|
+
this.removeClassName('focus');
|
422
|
+
},
|
423
|
+
|
382
424
|
// ......................................
|
383
425
|
// DRAG AND DROP SUPPORT
|
384
426
|
//
|
@@ -425,7 +467,7 @@ SC.CollectionView = SC.View.extend(
|
|
425
467
|
// This assumes you will flow LTR, but it should work if you flow
|
426
468
|
// bottom to top or top to bottom.
|
427
469
|
} else {
|
428
|
-
if (SC.
|
470
|
+
if (SC.minX(f) < loc.x) {
|
429
471
|
curSide = (SC.maxY(f) < loc.y) ? -1 : 1 ;
|
430
472
|
} else curSide = null ;
|
431
473
|
}
|
@@ -488,26 +530,86 @@ SC.CollectionView = SC.View.extend(
|
|
488
530
|
@returns {void}
|
489
531
|
*/
|
490
532
|
hideInsertionPoint: function() {},
|
533
|
+
|
534
|
+
/**
|
535
|
+
Override this method to provide your own ghost image for a drag.
|
536
|
+
|
537
|
+
Note that the only purpose of this view is to render a visible drag element. It is
|
538
|
+
not critical that you make this element bindable, etc.
|
539
|
+
|
540
|
+
@param dragContent {Array} Array of content objects that will be used in the drag.
|
541
|
+
*/
|
542
|
+
ghostViewFor: function(dragContent) {
|
543
|
+
var view = SC.View.create() ;
|
544
|
+
view.set('frame', this.get('frame')) ;
|
545
|
+
view.set('isPositioned', true) ;
|
546
|
+
var idx = dragContent.length ;
|
547
|
+
var maxX = 0; var maxY = 0 ;
|
548
|
+
|
549
|
+
while(--idx >= 0) {
|
550
|
+
var itemView = this.itemViewForContent(dragContent[idx]) ;
|
551
|
+
if (!itemView) continue ;
|
552
|
+
var f = itemView.get('frame') ;
|
553
|
+
var dom = itemView.rootElement ;
|
554
|
+
if (!dom) continue ;
|
555
|
+
|
556
|
+
// save the maxX & maxY. This will be used to trim the size
|
557
|
+
// of the ghost view later.
|
558
|
+
if (SC.maxX(f) > maxX) maxX = SC.maxX(f) ;
|
559
|
+
if (SC.maxY(f) > maxY) maxY = SC.maxY(f) ;
|
560
|
+
|
561
|
+
// Clone the contents of this node. We should probably apply the
|
562
|
+
// computed style to the cloned nodes in order to make sure they match even if the
|
563
|
+
// CSS styles do not match. Make sure the items are properly
|
564
|
+
// positioned.
|
565
|
+
dom = dom.cloneNode(true) ;
|
566
|
+
Element.setStyle(dom, { position: "absolute", left: "%@px".fmt(f.x), top: "%@px".fmt(f.y), width: "%@px".fmt(f.width), height: "%@px".fmt(f.height) }) ;
|
567
|
+
view.rootElement.appendChild(dom) ;
|
568
|
+
}
|
569
|
+
|
570
|
+
// Trim the size of the view to match the maxX & maxY as well as overflow
|
571
|
+
view.setStyle({ overflow: 'hidden', width: "%@px.".fmt(maxX+1), height: "%@px".fmt(maxY+1) }) ;
|
572
|
+
|
573
|
+
return view ;
|
574
|
+
},
|
491
575
|
|
492
|
-
// handle mouse drags. If the canReorderContent is enabled, allow the
|
493
|
-
// user to start a reorder.
|
494
576
|
mouseDragged: function(ev) {
|
495
577
|
// Don't do anything unless the user has been dragging for 123msec
|
496
578
|
if ((Date.now() - this._mouseDownAt) < 123) return true ;
|
497
579
|
|
498
580
|
// OK, they must be serious, start a drag if possible.
|
499
|
-
// Also use this opportunity to clean up since mouseUp won't
|
500
|
-
// get called.
|
501
581
|
if (this.get('canReorderContent')) {
|
582
|
+
|
583
|
+
// we need to recalculate the frame at this point.
|
584
|
+
this.flushFrameCache();
|
585
|
+
|
586
|
+
// First, get the selection to drag. Drag an array of selected
|
587
|
+
// items appearing in this collection, in the order of the
|
588
|
+
// collection.
|
589
|
+
var content = this.get('content') || [] ;
|
590
|
+
var dragContent = this.get('selection').sort(function(a,b) {
|
591
|
+
a = content.indexOf(a) ; b = content.indexOf(b) ;
|
592
|
+
return (a<b) ? -1 : ((a>b) ? 1 : 0) ;
|
593
|
+
});
|
594
|
+
|
595
|
+
// Build the drag view to use for the ghost drag. This
|
596
|
+
// should essentially contain any visible drag items.
|
597
|
+
var view = this.ghostViewFor(dragContent) ;
|
598
|
+
|
599
|
+
// Initiate the drag
|
502
600
|
SC.Drag.start({
|
503
601
|
event: this._mouseDownEvent,
|
504
602
|
source: this,
|
505
|
-
dragView:
|
603
|
+
dragView: view,
|
506
604
|
ghost: NO,
|
507
605
|
slideBack: YES,
|
508
|
-
data: { "_mouseDownContent":
|
606
|
+
data: { "_mouseDownContent": dragContent }
|
509
607
|
}) ;
|
608
|
+
|
609
|
+
// Also use this opportunity to clean up since mouseUp won't
|
610
|
+
// get called.
|
510
611
|
this._cleanupMouseDown() ;
|
612
|
+
this._lastInsertionIndex = null ;
|
511
613
|
}
|
512
614
|
},
|
513
615
|
|
@@ -525,9 +627,33 @@ SC.CollectionView = SC.View.extend(
|
|
525
627
|
if (this.get('canReorderContent')) {
|
526
628
|
var loc = drag.get('location') ;
|
527
629
|
loc = this.convertFrameFromView(loc, null) ;
|
630
|
+
|
631
|
+
// get the insertion index for this location. This can be computed
|
632
|
+
// by a subclass using whatever method. This method is not expected to
|
633
|
+
// do any data valdidation, just to map the location to an insertion index.
|
528
634
|
var ret = this.insertionIndexForLocation(loc) ;
|
635
|
+
|
636
|
+
// now that we have an index, find the nearest index that we can actually
|
637
|
+
// insert at, or do not allow.
|
638
|
+
var objects = (drag.source == this) ? (drag.dataForType('_mouseDownContent') || []) : [];
|
639
|
+
var content = this.get('content') || [] ;
|
640
|
+
|
641
|
+
// if the insertion index is in between two items in the drag itself, then this is
|
642
|
+
// not allowed. Either use the last insertion index or find the first index that is not
|
643
|
+
// in between selections.
|
644
|
+
var isPreviousInDrag = (ret > 0) ? objects.indexOf(content.objectAt(ret-1)) : -1 ;
|
645
|
+
var isNextInDrag = (ret < content.get('length')-1) ? objects.indexOf(content.objectAt(ret)) : -1 ;
|
646
|
+
if (isPreviousInDrag>=0 && isNextInDrag>=0) {
|
647
|
+
if (this._lastInsertionIndex == null) {
|
648
|
+
while((ret > 0) && (objects.indexOf(content.objectAt(ret)) >= 0)) ret-- ;
|
649
|
+
} else ret = this._lastInsertionIndex ;
|
650
|
+
}
|
651
|
+
|
652
|
+
// Now that we have verified that, check to see if a drop is allowed in the
|
653
|
+
// insertion index with the delegate.
|
654
|
+
// TODO
|
655
|
+
|
529
656
|
if (this._lastInsertionIndex != ret) {
|
530
|
-
console.log("--itemView: %@".fmt(ret)) ;
|
531
657
|
var itemView = this.itemViewForContent(this.get('content').objectAt(ret));
|
532
658
|
this.showInsertionPointBefore(itemView) ;
|
533
659
|
}
|
@@ -553,29 +679,39 @@ SC.CollectionView = SC.View.extend(
|
|
553
679
|
|
554
680
|
performDragOperation: function(op, drag) {
|
555
681
|
|
682
|
+
SC.Benchmark.start('%@ performDragOperation'.fmt(this._guid)) ;
|
683
|
+
|
556
684
|
var loc = drag.get('location') ;
|
557
685
|
loc = this.convertFrameFromView(loc, null) ;
|
558
686
|
|
559
687
|
// if op is MOVE or COPY, add item to view.
|
560
|
-
var
|
561
|
-
if (
|
688
|
+
var objects = drag.dataForType('_mouseDownContent') ;
|
689
|
+
if (objects && (op == SC.DRAG_MOVE)) {
|
562
690
|
|
563
691
|
// find the index to for the new insertion
|
564
692
|
var idx = this.insertionIndexForLocation(loc) ;
|
565
|
-
|
693
|
+
|
566
694
|
var content = this.get('content') ;
|
567
695
|
content.beginPropertyChanges(); // suspend notifications
|
568
|
-
|
696
|
+
|
697
|
+
// debugger ;
|
569
698
|
// find the old index and remove it.
|
570
|
-
var
|
571
|
-
|
572
|
-
|
699
|
+
var objectsIdx = objects.get('length') ;
|
700
|
+
while(--objectsIdx >= 0) {
|
701
|
+
var obj = objects.objectAt(objectsIdx) ;
|
702
|
+
var old = content.indexOf(obj) ;
|
703
|
+
if (old >= 0) content.removeAt(old) ;
|
704
|
+
if ((old >= 0) && (old < idx)) idx--; //adjust idx
|
705
|
+
}
|
573
706
|
|
574
|
-
// now insert
|
575
|
-
content.
|
707
|
+
// now insert objects at new location
|
708
|
+
content.replace(idx, 0, objects) ;
|
576
709
|
content.endPropertyChanges(); // restart notifications
|
577
710
|
}
|
578
711
|
|
712
|
+
SC.Benchmark.end('%@ performDragOperation'.fmt(this._guid)) ;
|
713
|
+
console.log(SC.Benchmark.report()) ;
|
714
|
+
|
579
715
|
return SC.DRAG_MOVE;
|
580
716
|
},
|
581
717
|
|
@@ -609,6 +745,7 @@ SC.CollectionView = SC.View.extend(
|
|
609
745
|
{
|
610
746
|
var el = this.containerElement || this.rootElement;
|
611
747
|
|
748
|
+
SC.Benchmark.start('%@: updateChildren'.fmt(this._guid)) ;
|
612
749
|
// initial setup
|
613
750
|
if (this._firstUpdate)
|
614
751
|
{
|
@@ -713,6 +850,7 @@ SC.CollectionView = SC.View.extend(
|
|
713
850
|
this.updateSelectionStates() ;
|
714
851
|
this.flushFrameCache() ;
|
715
852
|
this.set('isDirty',false);
|
853
|
+
SC.Benchmark.end('%@: updateChildren'.fmt(this._guid)) ;
|
716
854
|
},
|
717
855
|
|
718
856
|
/**
|
@@ -746,6 +884,10 @@ SC.CollectionView = SC.View.extend(
|
|
746
884
|
|
747
885
|
var firstChild = null ;
|
748
886
|
|
887
|
+
// save the first child to be modified. This will be
|
888
|
+
// passed to the layout method.
|
889
|
+
var firstModifiedChild = null;
|
890
|
+
|
749
891
|
while (child || (inGroup && (loc < contentCount) && !expired)) {
|
750
892
|
|
751
893
|
// get the content object.
|
@@ -796,6 +938,7 @@ SC.CollectionView = SC.View.extend(
|
|
796
938
|
parent.insertBefore(newChild,child);
|
797
939
|
this._itemViews[SC.getGUID(newChild)] = newChild;
|
798
940
|
itemViewsDidChange = true;
|
941
|
+
if (!firstModifiedChild) firstModifiedChild = newChild ;
|
799
942
|
child = newChild;
|
800
943
|
}
|
801
944
|
|
@@ -813,26 +956,22 @@ SC.CollectionView = SC.View.extend(
|
|
813
956
|
|
814
957
|
|
815
958
|
// maybe save the current render loc and reschedule.
|
816
|
-
if (expired && (loc < contentCount))
|
817
|
-
{
|
959
|
+
if (expired && (loc < contentCount)) {
|
818
960
|
this._lastRenderLoc = loc ;
|
819
961
|
this._lastRenderChild = child ;
|
820
962
|
setTimeout(this.updateChildren.bind(this),1) ; // do more later.
|
821
|
-
}
|
822
|
-
else
|
823
|
-
{
|
963
|
+
} else {
|
824
964
|
this._resetExpiredRender();
|
825
965
|
}
|
826
966
|
|
827
967
|
// now let the collection view layout the views that changed (if
|
828
968
|
// it is implemented.)
|
829
|
-
if (this.layoutChildViewsFor)
|
830
|
-
{
|
969
|
+
if (firstModifiedChild && this.layoutChildViewsFor) {
|
831
970
|
var el = this.containerElement || this.rootElement;
|
832
971
|
if (this._cachedParent) {
|
833
972
|
this._cachedParent.insertBefore(el,this._cachedSibling);
|
834
973
|
}
|
835
|
-
this.layoutChildViewsFor(parent,
|
974
|
+
this.layoutChildViewsFor(parent, firstModifiedChild);
|
836
975
|
if (this._cachedParent) {
|
837
976
|
this._cachedParent.removeChild(el);
|
838
977
|
}
|
@@ -981,24 +1120,11 @@ SC.CollectionView = SC.View.extend(
|
|
981
1120
|
// This will set the collection height.
|
982
1121
|
updateComputedViewHeight: function(groupView) {
|
983
1122
|
var height = this.computedViewHeight(groupView) ;
|
984
|
-
if (height
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
} else {
|
990
|
-
if (!groupView._heightView) {
|
991
|
-
groupView._heightView = document.createElement('div') ;
|
992
|
-
groupView.rootElement.appendChild(groupView._heightView) ;
|
993
|
-
Element.setStyle(groupView._heightView,{
|
994
|
-
position: 'absolute', left: '0px', display: 'block',
|
995
|
-
width: '1px', height: '1px'
|
996
|
-
}) ;
|
997
|
-
}
|
998
|
-
|
999
|
-
if (height != groupView._lastComputedHeight) {
|
1000
|
-
Element.setStyle(groupView._heightView,{ top: height + 'px' }) ;
|
1001
|
-
groupView._lastComputedHeight = height ;
|
1123
|
+
if (height >= 0) {
|
1124
|
+
var f = this.get('frame') ;
|
1125
|
+
if (Math.abs(f.height - height) > 0.1) {
|
1126
|
+
f.height = height ;
|
1127
|
+
this.set('frame', { height: height }) ;
|
1002
1128
|
}
|
1003
1129
|
}
|
1004
1130
|
},
|
@@ -1007,36 +1133,158 @@ SC.CollectionView = SC.View.extend(
|
|
1007
1133
|
// SELECTION
|
1008
1134
|
//
|
1009
1135
|
|
1010
|
-
|
1136
|
+
_indexOfSelectionTop: function() {
|
1137
|
+
var content = this.get('content');
|
1138
|
+
var sel = this.get('selection');
|
1139
|
+
if (!content || !sel) return - 1;
|
1140
|
+
|
1141
|
+
// find the first item in the selection
|
1142
|
+
var contentLength = content.get('length') ;
|
1143
|
+
var indexOfSelected = contentLength ; var idx = sel.length ;
|
1144
|
+
while(--idx >= 0) {
|
1145
|
+
var curIndex = content.indexOf(sel[idx]) ;
|
1146
|
+
if ((curIndex >= 0) && (curIndex < indexOfSelected)) indexOfSelected = curIndex ;
|
1147
|
+
}
|
1148
|
+
|
1149
|
+
return (indexOfSelected >= contentLength) ? -1 : indexOfSelected ;
|
1150
|
+
},
|
1151
|
+
|
1152
|
+
_indexOfSelectionBottom: function() {
|
1153
|
+
var content = this.get('content');
|
1154
|
+
var sel = this.get('selection');
|
1155
|
+
if (!content || !sel) return - 1;
|
1156
|
+
|
1157
|
+
var indexOfSelected = -1 ; var idx = sel.length ;
|
1158
|
+
while(--idx >= 0) {
|
1159
|
+
var curIndex = content.indexOf(sel[idx]) ;
|
1160
|
+
if (curIndex > indexOfSelected) indexOfSelected = curIndex ;
|
1161
|
+
}
|
1162
|
+
|
1163
|
+
return (indexOfSelected < 0) ? -1 : indexOfSelected ;
|
1164
|
+
},
|
1165
|
+
|
1166
|
+
/**
|
1167
|
+
Select one or more items before the current selection, optionally
|
1168
|
+
extending the current selection. Also scrolls the selected item into view.
|
1169
|
+
|
1170
|
+
Selection does not wrap around.
|
1171
|
+
|
1172
|
+
@param extend {Boolean} (Optional) If true, the selection will be extended instead of replaced. Defaults to false.
|
1173
|
+
@param numberOfItems {Integer} (Optional) The number of previous to be selected. Defaults to 1
|
1174
|
+
@returns {void}
|
1175
|
+
*/
|
1176
|
+
selectPreviousItem: function(extend, numberOfItems)
|
1011
1177
|
{
|
1012
|
-
|
1178
|
+
if (numberOfItems == null) numberOfItems = 1 ;
|
1179
|
+
if (extend == null) extend = false ;
|
1180
|
+
|
1013
1181
|
var content = this.get('content');
|
1014
|
-
var
|
1015
|
-
|
1016
|
-
|
1017
|
-
var
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1182
|
+
var contentLength = content.get('length') ;
|
1183
|
+
|
1184
|
+
// if extending, then we need to do some fun stuff to build the array
|
1185
|
+
var selTop, selBottom, anchor ;
|
1186
|
+
if (extend) {
|
1187
|
+
selTop = this._indexOfSelectionTop() ;
|
1188
|
+
selBottom = this._indexOfSelectionBottom() ;
|
1189
|
+
anchor = (this._selectionAnchor == null) ? selTop : this._selectionAnchor ;
|
1190
|
+
this._selectionAnchor = anchor ;
|
1191
|
+
|
1192
|
+
// If the selBottom is after the anchor, then reduce the selection
|
1193
|
+
if (selBottom > anchor) {
|
1194
|
+
selBottom-- ;
|
1195
|
+
|
1196
|
+
// otherwise, select the previous item from the top
|
1197
|
+
} else {
|
1198
|
+
selTop-- ;
|
1199
|
+
}
|
1200
|
+
|
1201
|
+
// Ensure we are not out of bounds
|
1202
|
+
if (selTop < 0) selTop = 0 ;
|
1203
|
+
if (selBottom < selTop) selBottom = selTop ;
|
1204
|
+
|
1205
|
+
// if not extending, just select the item previous to the selTop
|
1206
|
+
} else {
|
1207
|
+
selTop = this._indexOfSelectionTop() - 1;
|
1208
|
+
if (selTop < 0) selTop = 0 ;
|
1209
|
+
selBottom = selTop ;
|
1210
|
+
anchor = null ;
|
1211
|
+
}
|
1212
|
+
|
1213
|
+
// now build array of new items to select
|
1214
|
+
var items = [] ;
|
1215
|
+
while(selTop <= selBottom) {
|
1216
|
+
items[items.length] = content.objectAt(selTop++) ;
|
1217
|
+
}
|
1218
|
+
|
1219
|
+
// ensure that the item is visible and set the selection
|
1220
|
+
if (items.length > 0) {
|
1221
|
+
this.scrollToItemRecord(items.first());
|
1222
|
+
this.selectItems(items);
|
1223
|
+
}
|
1224
|
+
|
1225
|
+
this._selectionAnchor = anchor ;
|
1024
1226
|
},
|
1025
1227
|
|
1026
|
-
|
1228
|
+
/**
|
1229
|
+
Select one or more items folling the current selection, optionally
|
1230
|
+
extending the current selection. Also scrolls to selected item.
|
1231
|
+
|
1232
|
+
Selection does not wrap around.
|
1233
|
+
|
1234
|
+
@param extend {Boolean} (Optional) If true, the selection will be extended instead of replaced. Defaults to false.
|
1235
|
+
@param numberOfItems {Integer} (Optional) The number of items to be selected. Defaults to 1.
|
1236
|
+
@returns {void}
|
1237
|
+
*/
|
1238
|
+
selectNextItem: function(extend, numberOfItems)
|
1027
1239
|
{
|
1028
|
-
|
1240
|
+
if (numberOfItems == null) numberOfItems = 1 ;
|
1241
|
+
if (extend == null) extend = false ;
|
1242
|
+
|
1029
1243
|
var content = this.get('content');
|
1030
|
-
var
|
1031
|
-
|
1032
|
-
|
1033
|
-
var
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1244
|
+
var contentLength = content.get('length') ;
|
1245
|
+
|
1246
|
+
// if extending, then we need to do some fun stuff to build the array
|
1247
|
+
var selTop, selBottom, anchor ;
|
1248
|
+
if (extend) {
|
1249
|
+
selTop = this._indexOfSelectionTop() ;
|
1250
|
+
selBottom = this._indexOfSelectionBottom() ;
|
1251
|
+
anchor = (this._selectionAnchor == null) ? selTop : this._selectionAnchor ;
|
1252
|
+
this._selectionAnchor = anchor ;
|
1253
|
+
|
1254
|
+
// If the selTop is before the anchor, then reduce the selection
|
1255
|
+
if (selTop < anchor) {
|
1256
|
+
selTop++ ;
|
1257
|
+
|
1258
|
+
// otherwise, select the next item after the top
|
1259
|
+
} else {
|
1260
|
+
selBottom++ ;
|
1261
|
+
}
|
1262
|
+
|
1263
|
+
// Ensure we are not out of bounds
|
1264
|
+
if (selBottom >= contentLength) selBottom = contentLength-1;
|
1265
|
+
if (selTop > selBottom) selTop = selBottom ;
|
1266
|
+
|
1267
|
+
// if not extending, just select the item next to the selBottom
|
1268
|
+
} else {
|
1269
|
+
selBottom = this._indexOfSelectionBottom() + 1;
|
1270
|
+
if (selBottom >= contentLength) selBottom = contentLength-1;
|
1271
|
+
selTop = selBottom ;
|
1272
|
+
anchor = null ;
|
1273
|
+
}
|
1274
|
+
|
1275
|
+
// now build array of new items to select
|
1276
|
+
var items = [] ;
|
1277
|
+
while(selTop <= selBottom) {
|
1278
|
+
items[items.length] = content.objectAt(selTop++) ;
|
1279
|
+
}
|
1280
|
+
|
1281
|
+
// ensure that the item is visible and set the selection
|
1282
|
+
if (items.length > 0) {
|
1283
|
+
this.scrollToItemRecord(items.first());
|
1284
|
+
this.selectItems(items);
|
1285
|
+
}
|
1286
|
+
|
1287
|
+
this._selectionAnchor = anchor ;
|
1040
1288
|
},
|
1041
1289
|
|
1042
1290
|
/**
|
@@ -1075,12 +1323,25 @@ SC.CollectionView = SC.View.extend(
|
|
1075
1323
|
}
|
1076
1324
|
},
|
1077
1325
|
|
1326
|
+
/**
|
1327
|
+
Selects the passed array of items, optionally extending the
|
1328
|
+
current selection.
|
1329
|
+
|
1330
|
+
@param items {Array} The item or items to select.
|
1331
|
+
@param extendSelection {Boolean} If true, extends the selection instead of replacing it.
|
1332
|
+
*/
|
1078
1333
|
selectItems: function(items, extendSelection) {
|
1079
1334
|
var base = (extendSelection) ? this.get('selection') : [] ;
|
1080
1335
|
var sel = [items].concat(base).flatten().uniq() ;
|
1336
|
+
|
1337
|
+
// if you are not extending the selection, then clear the selection anchor.
|
1338
|
+
this._selectionAnchor = null ;
|
1081
1339
|
this.set('selection',sel) ;
|
1082
1340
|
},
|
1083
|
-
|
1341
|
+
|
1342
|
+
/**
|
1343
|
+
Removes the items from the selection.
|
1344
|
+
*/
|
1084
1345
|
deselectItems: function(items) {
|
1085
1346
|
items = [items].flatten() ;
|
1086
1347
|
var base = this.get('selection') || [] ;
|
@@ -1093,6 +1354,73 @@ SC.CollectionView = SC.View.extend(
|
|
1093
1354
|
// EVENT HANDLING
|
1094
1355
|
//
|
1095
1356
|
|
1357
|
+
keyDown: function(evt) {
|
1358
|
+
return this.interpretKeyEvents(evt) ;
|
1359
|
+
},
|
1360
|
+
|
1361
|
+
keyUp: function() { return true; },
|
1362
|
+
|
1363
|
+
/** @private
|
1364
|
+
Selects the same item on the next row. Or moves down one if
|
1365
|
+
itemsPerRow = 1
|
1366
|
+
*/
|
1367
|
+
moveDown: function(sender, evt) {
|
1368
|
+
this.selectNextItem(false, this.get('itemsPerRow') || 1) ;
|
1369
|
+
return true ;
|
1370
|
+
},
|
1371
|
+
|
1372
|
+
/** @private
|
1373
|
+
Selects the same item on the next row. Or moves up one if
|
1374
|
+
itemsPerRow = 1
|
1375
|
+
*/
|
1376
|
+
moveUp: function(sender, evt) {
|
1377
|
+
this.selectPreviousItem(false, this.get('itemsPerRow') || 1) ;
|
1378
|
+
return true ;
|
1379
|
+
},
|
1380
|
+
|
1381
|
+
/** @private
|
1382
|
+
Selects the previous item if itemsPerRow > 1. Otherwise does nothing.
|
1383
|
+
*/
|
1384
|
+
moveLeft: function(sender, evt) {
|
1385
|
+
if ((this.get('itemsPerRow') || 1) > 1) this.selectNextItem(false, 1) ;
|
1386
|
+
return true ;
|
1387
|
+
},
|
1388
|
+
|
1389
|
+
/** @private
|
1390
|
+
Selects the next item if itemsPerRow > 1. Otherwise does nothing.
|
1391
|
+
*/
|
1392
|
+
moveRight: function(sender, evt) {
|
1393
|
+
if ((this.get('itemsPerRow') || 1) > 1) this.selectPreviousItem(false, 1) ;
|
1394
|
+
return true ;
|
1395
|
+
},
|
1396
|
+
|
1397
|
+
moveDownAndModifySelection: function(sender, evt) {
|
1398
|
+
this.selectNextItem(true, this.get('itemsPerRow') || 1) ;
|
1399
|
+
return true ;
|
1400
|
+
},
|
1401
|
+
|
1402
|
+
moveUpAndModifySelection: function(sender, evt) {
|
1403
|
+
this.selectPreviousItem(true, this.get('itemsPerRow') || 1) ;
|
1404
|
+
return true ;
|
1405
|
+
},
|
1406
|
+
|
1407
|
+
/** @private
|
1408
|
+
Selects the previous item if itemsPerRow > 1. Otherwise does nothing.
|
1409
|
+
*/
|
1410
|
+
moveLeftAndModifySelection: function(sender, evt) {
|
1411
|
+
if ((this.get('itemsPerRow') || 1) > 1) this.selectNextItem(true, 1) ;
|
1412
|
+
return true ;
|
1413
|
+
},
|
1414
|
+
|
1415
|
+
/** @private
|
1416
|
+
Selects the next item if itemsPerRow > 1. Otherwise does nothing.
|
1417
|
+
*/
|
1418
|
+
moveRightAndModifySelection: function(sender, evt) {
|
1419
|
+
if ((this.get('itemsPerRow') || 1) > 1) this.selectPreviousItem(true, 1) ;
|
1420
|
+
return true ;
|
1421
|
+
},
|
1422
|
+
|
1423
|
+
|
1096
1424
|
/**
|
1097
1425
|
Find the item view underneath the passed mouse location.
|
1098
1426
|
|
@@ -1177,6 +1505,9 @@ SC.CollectionView = SC.View.extend(
|
|
1177
1505
|
var mouseDownView = this._mouseDownView = this.itemViewForEvent(ev);
|
1178
1506
|
var mouseDownContent = this._mouseDownContent = (mouseDownView) ? mouseDownView.get('content') : null;
|
1179
1507
|
|
1508
|
+
// become first responder if possible.
|
1509
|
+
this.becomeFirstResponder() ;
|
1510
|
+
|
1180
1511
|
// recieved a mouseDown on the collection element, but not on one of the childItems... bail
|
1181
1512
|
if (!mouseDownView) {
|
1182
1513
|
if (this.get('allowDeselectAll')) this.selectItems([], false);
|
@@ -1235,8 +1566,6 @@ SC.CollectionView = SC.View.extend(
|
|
1235
1566
|
|
1236
1567
|
_mouseUp: function(ev) {
|
1237
1568
|
|
1238
|
-
console.info('_mouseUp!');
|
1239
|
-
|
1240
1569
|
var canAct = this.get('actOnSelect') ;
|
1241
1570
|
var view = this.itemViewForEvent(ev) ;
|
1242
1571
|
|
@@ -1482,8 +1811,7 @@ SC.CollectionView = SC.View.extend(
|
|
1482
1811
|
// called on content change *and* content.[] change...
|
1483
1812
|
_contentPropertyObserver: function(target,key,value)
|
1484
1813
|
{
|
1485
|
-
if (!this._updating)
|
1486
|
-
{
|
1814
|
+
if (!this._updating) {
|
1487
1815
|
this._updating = true;
|
1488
1816
|
this.set('isDirty',true);
|
1489
1817
|
this._resetExpiredRender();
|