sproutcore 0.9.0 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- 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();
|