sproutcore 0.9.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/Manifest.txt +42 -29
  2. data/README.txt +1 -1
  3. data/Rakefile +5 -0
  4. data/app_generators/sproutcore/sproutcore_generator.rb +4 -4
  5. data/app_generators/sproutcore/templates/sc-config.rb +72 -0
  6. data/bin/sc-build +2 -0
  7. data/clients/sc_docs/controllers/docs.js +1 -0
  8. data/clients/sc_docs/english.lproj/body.rhtml +5 -0
  9. data/clients/sc_docs/views/doc_frame.js +1 -1
  10. data/clients/sc_test_runner/controllers/runner.js +2 -2
  11. data/clients/sc_test_runner/english.lproj/body.rhtml +5 -0
  12. data/clients/sc_test_runner/main.js +12 -12
  13. data/clients/sc_test_runner/models/test.js +3 -0
  14. data/clients/sc_test_runner/views/test_label.js +1 -1
  15. data/config/hoe.rb +2 -0
  16. data/frameworks/sproutcore/controllers/array.js +132 -125
  17. data/frameworks/sproutcore/drag/drag.js +5 -2
  18. data/frameworks/sproutcore/english.lproj/buttons.css +64 -64
  19. data/frameworks/sproutcore/english.lproj/collections.css +82 -0
  20. data/frameworks/sproutcore/english.lproj/images/buttons-sprite.png +0 -0
  21. data/frameworks/sproutcore/english.lproj/images/sproutcore-logo.png +0 -0
  22. data/frameworks/sproutcore/english.lproj/images/sticky-note.png +0 -0
  23. data/frameworks/sproutcore/english.lproj/menu.css +1 -1
  24. data/frameworks/sproutcore/english.lproj/theme.css +13 -4
  25. data/frameworks/sproutcore/foundation/array.js +24 -2
  26. data/frameworks/sproutcore/foundation/benchmark.js +12 -5
  27. data/frameworks/sproutcore/foundation/observable.js +62 -1
  28. data/frameworks/sproutcore/foundation/utils.js +16 -0
  29. data/frameworks/sproutcore/tests/views/label_item.rhtml +21 -0
  30. data/frameworks/sproutcore/tests/views/list.rhtml +21 -0
  31. data/frameworks/sproutcore/tests/views/scroll.rhtml +21 -0
  32. data/frameworks/sproutcore/views/collection.js +401 -73
  33. data/frameworks/sproutcore/views/collection/collection_item.js +36 -0
  34. data/frameworks/sproutcore/views/collection/grid.js +149 -0
  35. data/frameworks/sproutcore/views/collection/image_cell.js +154 -0
  36. data/frameworks/sproutcore/views/collection/list.js +115 -0
  37. data/frameworks/sproutcore/views/collection/text_cell.js +128 -0
  38. data/frameworks/sproutcore/views/image.js +1 -1
  39. data/frameworks/sproutcore/views/label.js +6 -2
  40. data/frameworks/sproutcore/views/scroll.js +34 -0
  41. data/frameworks/sproutcore/views/view.js +12 -4
  42. data/generators/client/client_generator.rb +3 -11
  43. data/generators/client/templates/english.lproj/body.css +75 -0
  44. data/generators/client/templates/english.lproj/body.rhtml +17 -2
  45. data/generators/model/templates/fixture.js +32 -0
  46. data/lib/sproutcore/build_tools/html_builder.rb +29 -11
  47. data/lib/sproutcore/build_tools/resource_builder.rb +1 -1
  48. data/lib/sproutcore/bundle.rb +19 -7
  49. data/lib/sproutcore/library.rb +39 -21
  50. data/lib/sproutcore/merb/bundle_controller.rb +3 -8
  51. data/lib/sproutcore/version.rb +1 -1
  52. data/lib/sproutcore/view_helpers.rb +7 -5
  53. data/lib/sproutcore/view_helpers/core_views.rb +11 -3
  54. data/sc-config.rb +7 -0
  55. data/tasks/deployment.rake +15 -2
  56. metadata +44 -31
  57. data/app_generators/sproutcore/templates/environment.yml +0 -4
  58. 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
+ }) ;