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,36 @@
|
|
1
|
+
// ========================================================================
|
2
|
+
// SproutCore
|
3
|
+
// copyright 2006-2007 Sprout Systems, Inc.
|
4
|
+
// ========================================================================
|
5
|
+
|
6
|
+
/**
|
7
|
+
Apply this mixin to any view class to automatically inherit most of the
|
8
|
+
basic properties you need to support to act as an item view in a collection.
|
9
|
+
|
10
|
+
In addition to this module, make sure that your view class knows how to
|
11
|
+
render the object set on the 'content' property.
|
12
|
+
|
13
|
+
This module provides both the properties and reasonable default observers.
|
14
|
+
You can override them in your own class as well.
|
15
|
+
|
16
|
+
@namespace
|
17
|
+
*/
|
18
|
+
SC.CollectionItem = {
|
19
|
+
|
20
|
+
/** Set to true when the item is selected. */
|
21
|
+
isSelected: false,
|
22
|
+
|
23
|
+
/** By default, adds the 'sel' CSS class if selected. */
|
24
|
+
isSelectedObserver: function() {
|
25
|
+
this.setClassName('sel', this.get('isSelected')) ;
|
26
|
+
}.observes('isSelected'),
|
27
|
+
|
28
|
+
/** Set to true when the item is enabled. */
|
29
|
+
isEnabled: true,
|
30
|
+
|
31
|
+
/** By default, adds the disabled CSS class if disabled. */
|
32
|
+
isEnabledObserver: function() {
|
33
|
+
this.setClassName('disabled', !this.get('isEnabled'));
|
34
|
+
}.observes('isEnabled')
|
35
|
+
|
36
|
+
};
|
@@ -0,0 +1,149 @@
|
|
1
|
+
// ==========================================================================
|
2
|
+
// SC.GridView
|
3
|
+
// ==========================================================================
|
4
|
+
|
5
|
+
require('views/collection') ;
|
6
|
+
require('views/collection/text_cell');
|
7
|
+
|
8
|
+
/** @class
|
9
|
+
|
10
|
+
A grid view renders a collection of items in a grid of rows and columns.
|
11
|
+
|
12
|
+
@extends SC.CollectionView
|
13
|
+
@author Charles Jolley
|
14
|
+
@version 1.0
|
15
|
+
*/
|
16
|
+
SC.GridView = SC.CollectionView.extend(
|
17
|
+
/** @scope SC.GridView.prototype */ {
|
18
|
+
|
19
|
+
emptyElement: '<div class="grid-view"></div>',
|
20
|
+
|
21
|
+
/**
|
22
|
+
The common row height for grid items.
|
23
|
+
|
24
|
+
The value should be an integer expressed in pixels.
|
25
|
+
*/
|
26
|
+
rowHeight: 48,
|
27
|
+
|
28
|
+
/**
|
29
|
+
The minimum column width for grid items. Items will actually
|
30
|
+
be laid out as needed to completely fill the space, but the minimum
|
31
|
+
width of each item will be this value.
|
32
|
+
*/
|
33
|
+
columnWidth: 64,
|
34
|
+
|
35
|
+
/**
|
36
|
+
The default example item view will render text-based items.
|
37
|
+
|
38
|
+
You can override this as you wish.
|
39
|
+
*/
|
40
|
+
exampleView: SC.TextCellView,
|
41
|
+
|
42
|
+
insertionOrientation: SC.HORIZONTAL_ORIENTATION,
|
43
|
+
|
44
|
+
/** @private */
|
45
|
+
layoutChildViewsFor: function(parentView, startingView) {
|
46
|
+
var rowHeight = this.get('rowHeight') ;
|
47
|
+
var columnWidth = this.get('columnWidth') ;
|
48
|
+
if ((rowHeight == null) || (columnWidth == null)) return false ;
|
49
|
+
|
50
|
+
// set items per row.
|
51
|
+
parentView = parentView || this ;
|
52
|
+
var f = parentView.get('frame') ;
|
53
|
+
f.x= f.y = 0 ;
|
54
|
+
var itemsPerRow = Math.floor(f.width / (columnWidth || 1)) ;
|
55
|
+
if (this.get('itemsPerRow') != itemsPerRow) this.set('itemsPerRow', itemsPerRow);
|
56
|
+
|
57
|
+
// fix width to evenly match items per row
|
58
|
+
columnWidth = Math.floor(f.width/itemsPerRow) ;
|
59
|
+
|
60
|
+
// get the startingView and the starting X,Y
|
61
|
+
if (!startingView) startingView = parentView.firstChild ;
|
62
|
+
var x,y ;
|
63
|
+
if (startingView && startingView.previousSibling) {
|
64
|
+
var prevFrame = startingView.previousSibling.get('frame') ;
|
65
|
+
x = SC.maxX(prevFrame); y = SC.minY(prevFrame) ;
|
66
|
+
} else { x = f.x; y = f.y; }
|
67
|
+
|
68
|
+
// Now setup the default frame
|
69
|
+
var maxX = SC.maxX(f);
|
70
|
+
var minX = f.x;
|
71
|
+
f = { x: 0, y: 0, height: rowHeight, width: columnWidth } ;
|
72
|
+
var view = startingView ;
|
73
|
+
while(view) {
|
74
|
+
// loop back to beginning of next line if needed.
|
75
|
+
if (x >= maxX) {
|
76
|
+
x = minX ;
|
77
|
+
y += rowHeight ;
|
78
|
+
}
|
79
|
+
|
80
|
+
// save frame
|
81
|
+
view.set('isPositioned', true) ;
|
82
|
+
f.y = y ; f.x = x;
|
83
|
+
if (!SC.rectsEqual(view.get('frame'), f)) view.set('frame', f) ;
|
84
|
+
x += columnWidth;
|
85
|
+
view = view.nextSibling ;
|
86
|
+
}
|
87
|
+
return true;
|
88
|
+
},
|
89
|
+
|
90
|
+
// computedViewHeight: function(groupView) {
|
91
|
+
// var content = this.get('content') ;
|
92
|
+
// var rowHeight = this.get('rowHeight') ;
|
93
|
+
// var parentNode = this.get('parentNode') ;
|
94
|
+
// var minHeight = (parentNode) ? 20 : parentNode.get('frame').height ;
|
95
|
+
// var height = 0 ;
|
96
|
+
//
|
97
|
+
// if (content && rowHeight) {
|
98
|
+
// var rows = content.get('length') ;
|
99
|
+
// height = rows * rowHeight ;
|
100
|
+
// }
|
101
|
+
// if (height < minHeight) height = minHeight ;
|
102
|
+
// return height ;
|
103
|
+
// },
|
104
|
+
|
105
|
+
insertionPointClass: SC.View.extend({
|
106
|
+
emptyElement: '<div class="grid-insertion-point"><span class="anchor"></span></div>'
|
107
|
+
}),
|
108
|
+
|
109
|
+
showInsertionPointBefore: function(itemView) {
|
110
|
+
if (!itemView) return ;
|
111
|
+
|
112
|
+
if (!this._insertionPointView) {
|
113
|
+
this._insertionPointView = this.insertionPointClass.create() ;
|
114
|
+
} ;
|
115
|
+
|
116
|
+
var insertionPoint = this._insertionPointView ;
|
117
|
+
var itemViewFrame = itemView.get('frame') ;
|
118
|
+
f = { height: itemViewFrame.height - 6,
|
119
|
+
x: itemViewFrame.x,
|
120
|
+
y: itemViewFrame.y + 6,
|
121
|
+
width: 0
|
122
|
+
};
|
123
|
+
if (!SC.rectsEqual(insertionPoint.get('frame'), f)) {
|
124
|
+
insertionPoint.set('frame', f) ;
|
125
|
+
}
|
126
|
+
|
127
|
+
if (insertionPoint.parentNode != itemView.parentNode) {
|
128
|
+
itemView.parentNode.appendChild(insertionPoint) ;
|
129
|
+
}
|
130
|
+
},
|
131
|
+
|
132
|
+
hideInsertionPoint: function() {
|
133
|
+
var insertionPoint = this._insertionPointView ;
|
134
|
+
if (insertionPoint) insertionPoint.removeFromParent() ;
|
135
|
+
},
|
136
|
+
|
137
|
+
// // We can do this much faster programatically using the rowHeight
|
138
|
+
insertionIndexForLocation: function(loc) {
|
139
|
+
var f = this.get('frame') ;
|
140
|
+
loc = this.convertFrameFromView(loc, null) ;
|
141
|
+
|
142
|
+
var itemsPerRow = this.get('itemsPerRow') || 1 ;
|
143
|
+
var columnWidth = Math.floor(f.width / itemsPerRow) ;
|
144
|
+
var row = Math.floor((loc.y - f.y) / this.get('rowHeight') + 0.5) ;
|
145
|
+
var col = Math.floor((loc.x - f.x) / columnWidth + 0.5) ;
|
146
|
+
return (row*itemsPerRow) + col ;
|
147
|
+
}
|
148
|
+
|
149
|
+
}) ;
|
@@ -0,0 +1,154 @@
|
|
1
|
+
// ==========================================================================
|
2
|
+
// SC.TextCellView
|
3
|
+
// ==========================================================================
|
4
|
+
|
5
|
+
require('views/collection/collection_item') ;
|
6
|
+
|
7
|
+
/** @class
|
8
|
+
|
9
|
+
An image cell displays a single image inside of a collection view. Unlike
|
10
|
+
a single imageView, the image cell will automatically position the image
|
11
|
+
inside of the view to reflect the actual size of the image on load.
|
12
|
+
|
13
|
+
@extends SC.View
|
14
|
+
@author AuthorName
|
15
|
+
@version 0.1
|
16
|
+
*/
|
17
|
+
SC.ImageCellView = SC.View.extend(SC.CollectionItem,
|
18
|
+
/** @scope SC.ImageCellView.prototype */ {
|
19
|
+
|
20
|
+
emptyElement: '<div class="image-cell collection-item"><img src="%@" style="position:relative;" /></div>'.fmt(static_url('blank')),
|
21
|
+
|
22
|
+
/**
|
23
|
+
The content object this image item view will display.
|
24
|
+
*/
|
25
|
+
content: null,
|
26
|
+
|
27
|
+
/**
|
28
|
+
The owner view of this cell. The ImageCell relies on this
|
29
|
+
view to provide many of its behavioral defaults and for
|
30
|
+
event handling.
|
31
|
+
*/
|
32
|
+
owner: null,
|
33
|
+
|
34
|
+
/**
|
35
|
+
Set this to a validator or to a function and the value
|
36
|
+
will be passed through it before being set.
|
37
|
+
|
38
|
+
This is a default default that can be overidden by the
|
39
|
+
settings in the owner view.
|
40
|
+
*/
|
41
|
+
formatter: null,
|
42
|
+
|
43
|
+
displayProperty: null,
|
44
|
+
|
45
|
+
/**
|
46
|
+
This is the required margin you want to appear around the image. Expressed in px
|
47
|
+
*/
|
48
|
+
imageMargin: 2,
|
49
|
+
|
50
|
+
/**
|
51
|
+
The imageView that will manage the image itself. No bindings are
|
52
|
+
configured for the image; the cell will simply inform it when
|
53
|
+
important changes occur.
|
54
|
+
*/
|
55
|
+
imageView: SC.ImageView.extend({
|
56
|
+
|
57
|
+
// Resizes the imageView to fix within the boundaries of its
|
58
|
+
// parent. Automatically triggered when load status changes and
|
59
|
+
// also by owner whenever it is resized.
|
60
|
+
sizeToFit: function() {
|
61
|
+
|
62
|
+
// find the best fit.
|
63
|
+
var f= this.owner.get('frame') ;
|
64
|
+
var margin = this.owner.get('imageMargin') ;
|
65
|
+
f.width -= margin*2 ;
|
66
|
+
f.height -= margin*2 ;
|
67
|
+
|
68
|
+
var w = this.get('imageWidth') ;
|
69
|
+
var h = this.get('imageHeight') ;
|
70
|
+
var wideScaleFactor = (f.width / w) ;
|
71
|
+
var tallScaleFactor = (f.height / h) ;
|
72
|
+
var scaleFactor = (tallScaleFactor < wideScaleFactor) ? tallScaleFactor : wideScaleFactor;
|
73
|
+
w = w * scaleFactor ; h = h*scaleFactor;
|
74
|
+
|
75
|
+
var f= this.owner.get('frame') ; // reset w/o margin
|
76
|
+
var newFrame = { width: w, height: h, x: Math.floor((f.width - w) /2), y: Math.floor((f.height - h) /2) };
|
77
|
+
if (!SC.rectsEqual(newFrame, this.get('frame'))) {
|
78
|
+
this.set('frame', newFrame);
|
79
|
+
}
|
80
|
+
|
81
|
+
}.observes('status')
|
82
|
+
|
83
|
+
}).outletFor('img?'),
|
84
|
+
|
85
|
+
outlets: ['imageView'],
|
86
|
+
|
87
|
+
resizeChildrenWithOldSize: function() {
|
88
|
+
if (this.get('content')) {
|
89
|
+
this.outlet('imageView').sizeToFit() ;
|
90
|
+
}
|
91
|
+
},
|
92
|
+
|
93
|
+
// invoked whenever the content object changes.
|
94
|
+
_contentObserver: function() {
|
95
|
+
var content = this.get('content') ;
|
96
|
+
if (this._content == content) return ;
|
97
|
+
var f = this._boundValueDidChange() ;
|
98
|
+
|
99
|
+
// stop observing the old display property, if there is one.
|
100
|
+
if (this._content && this._displayProperty) {
|
101
|
+
this._content.removeObserver(this._displayProperty, f) ;
|
102
|
+
}
|
103
|
+
|
104
|
+
// start observing the new display property, if there is one
|
105
|
+
this._displayProperty = this._getDefault('displayProperty') ;
|
106
|
+
this._content = content ;
|
107
|
+
if (this._content && this._displayProperty) {
|
108
|
+
this._content.addObserver(this._displayProperty, f) ;
|
109
|
+
}
|
110
|
+
|
111
|
+
// notify value did change
|
112
|
+
this._valueDidChange() ;
|
113
|
+
}.observes('content'),
|
114
|
+
|
115
|
+
/**
|
116
|
+
@private
|
117
|
+
|
118
|
+
Invoked whenever the monitored value on the content object
|
119
|
+
changes.
|
120
|
+
|
121
|
+
The value processed is either the displayProperty, if set, or
|
122
|
+
it is the content object itself.
|
123
|
+
*/
|
124
|
+
_valueDidChange: function() {
|
125
|
+
var content = this.get('content') ;
|
126
|
+
var value = (content && this._displayProperty) ? content.get(this._displayProperty) : content;
|
127
|
+
var owner = this.get('owner') ;
|
128
|
+
|
129
|
+
// prepare the value...
|
130
|
+
|
131
|
+
// 1. apply the formatter
|
132
|
+
var formatter = this._getDefault('formatter') ;
|
133
|
+
if (formatter) {
|
134
|
+
var formattedValue = ($type(formatter) == T_FUNCTION) ? formatter(value, this) : formatter.fieldValueForObject(value, this) ;
|
135
|
+
if (formattedValue != null) value = formattedValue ;
|
136
|
+
}
|
137
|
+
|
138
|
+
// 2. If the returned value is not a string, convert it.
|
139
|
+
if (($type(value) != T_NULL) && value.toString) value = value.toString() ;
|
140
|
+
|
141
|
+
// 3. Apply URL to image view.
|
142
|
+
this.outlet('imageView').set('content', value) ;
|
143
|
+
},
|
144
|
+
|
145
|
+
_boundValueDidChange: function() {
|
146
|
+
return this._boundValueDidChange = this._boundValueDidChange || this._valueDidChange.bind(this);
|
147
|
+
},
|
148
|
+
|
149
|
+
// Retrieves the default value from the owner or locally.
|
150
|
+
_getDefault: function(keyName) {
|
151
|
+
var ret = (this.owner) ? this.owner.get(keyName) : null ;
|
152
|
+
return (ret != null) ? ret : this.get(keyName) ;
|
153
|
+
}
|
154
|
+
}) ;
|
@@ -0,0 +1,115 @@
|
|
1
|
+
// ==========================================================================
|
2
|
+
// SC.ListView
|
3
|
+
// ==========================================================================
|
4
|
+
|
5
|
+
require('views/collection') ;
|
6
|
+
require('views/collection/text_cell');
|
7
|
+
|
8
|
+
/** @class
|
9
|
+
|
10
|
+
A list view renders vertical lists of items. It is a specialized form of
|
11
|
+
collection view that is simpler than the table view, but more refined than
|
12
|
+
a generic collection.
|
13
|
+
|
14
|
+
You can use a list view just like a collection view, except that often you
|
15
|
+
also should provide a default rowHeight. Setting this value will allow
|
16
|
+
the ListView to optimize its rendering.
|
17
|
+
|
18
|
+
@extends SC.CollectionView
|
19
|
+
@author Charles Jolley
|
20
|
+
@version 1.0
|
21
|
+
*/
|
22
|
+
SC.ListView = SC.CollectionView.extend(
|
23
|
+
/** @scope SC.ListView.prototype */ {
|
24
|
+
|
25
|
+
emptyElement: '<div class="list-view"></div>',
|
26
|
+
|
27
|
+
/**
|
28
|
+
The common row height for list view items.
|
29
|
+
|
30
|
+
If you set this property, then the ListView will be able to use this
|
31
|
+
property to perform absolute layout of its children and to minimize t
|
32
|
+
number of actual views it has to create.
|
33
|
+
|
34
|
+
The value should be an integer expressed in pixels.
|
35
|
+
*/
|
36
|
+
rowHeight: 20,
|
37
|
+
|
38
|
+
/**
|
39
|
+
The default example item view will render text-based items.
|
40
|
+
|
41
|
+
You can override this as you wish.
|
42
|
+
*/
|
43
|
+
exampleView: SC.TextCellView,
|
44
|
+
|
45
|
+
insertionOrientation: SC.VERTICAL_ORIENTATION,
|
46
|
+
|
47
|
+
/** @private */
|
48
|
+
layoutChildViewsFor: function(parentView, startingView) {
|
49
|
+
var rowHeight = this.get('rowHeight') ;
|
50
|
+
if (rowHeight == null) return false ;
|
51
|
+
|
52
|
+
if (!startingView) startingView = parentView.firstChild ;
|
53
|
+
var y = (startingView && startingView.previousSibling) ? SC.maxY(startingView.previousSibling.get('frame')) : 0;
|
54
|
+
var f = (parentView || this).get('frame') ;
|
55
|
+
f = { x: 0, height: rowHeight } ;
|
56
|
+
var view = startingView || parentView.firstChild;
|
57
|
+
while(view) {
|
58
|
+
view.set('isPositioned', true) ;
|
59
|
+
f.y = y ;
|
60
|
+
if (!SC.rectsEqual(view.get('frame'), f)) view.set('frame', f) ;
|
61
|
+
y += rowHeight;
|
62
|
+
view = view.nextSibling ;
|
63
|
+
}
|
64
|
+
return true;
|
65
|
+
},
|
66
|
+
|
67
|
+
// computedViewHeight: function(groupView) {
|
68
|
+
// var content = this.get('content') ;
|
69
|
+
// var rowHeight = this.get('rowHeight') ;
|
70
|
+
// var parentNode = this.get('parentNode') ;
|
71
|
+
// var minHeight = (parentNode) ? 20 : parentNode.get('frame').height ;
|
72
|
+
// var height = 0 ;
|
73
|
+
//
|
74
|
+
// if (content && rowHeight) {
|
75
|
+
// var rows = content.get('length') ;
|
76
|
+
// height = rows * rowHeight ;
|
77
|
+
// }
|
78
|
+
// if (height < minHeight) height = minHeight ;
|
79
|
+
// return height ;
|
80
|
+
// },
|
81
|
+
|
82
|
+
insertionPointClass: SC.View.extend({
|
83
|
+
emptyElement: '<div class="list-insertion-point"><span class="anchor"></span></div>'
|
84
|
+
}),
|
85
|
+
|
86
|
+
showInsertionPointBefore: function(itemView) {
|
87
|
+
if (!itemView) return ;
|
88
|
+
|
89
|
+
if (!this._insertionPointView) {
|
90
|
+
this._insertionPointView = this.insertionPointClass.create() ;
|
91
|
+
} ;
|
92
|
+
|
93
|
+
var insertionPoint = this._insertionPointView ;
|
94
|
+
f = { height: 0, x: 8, y: itemView.get('frame').y, width: itemView.owner.get('frame').width };
|
95
|
+
insertionPoint.set('frame', f) ;
|
96
|
+
|
97
|
+
if (insertionPoint.parentNode != itemView.parentNode) {
|
98
|
+
itemView.parentNode.appendChild(insertionPoint) ;
|
99
|
+
}
|
100
|
+
},
|
101
|
+
|
102
|
+
hideInsertionPoint: function() {
|
103
|
+
var insertionPoint = this._insertionPointView ;
|
104
|
+
if (insertionPoint) insertionPoint.removeFromParent() ;
|
105
|
+
},
|
106
|
+
|
107
|
+
// We can do this much faster programatically using the rowHeight
|
108
|
+
insertionIndexForLocation: function(loc) {
|
109
|
+
var f = this.get('frame') ;
|
110
|
+
loc = this.convertFrameFromView(loc, null) ;
|
111
|
+
var ret = Math.floor((loc.y - f.y) / this.get('rowHeight') + 0.5) ;
|
112
|
+
return ret ;
|
113
|
+
}
|
114
|
+
|
115
|
+
}) ;
|