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.
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
+ }) ;