sproutcore 0.9.14 → 0.9.15

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 (61) hide show
  1. data/History.txt +43 -0
  2. data/Manifest.txt +12 -3
  3. data/bin/sc-build +19 -3
  4. data/bin/sc-install +5 -0
  5. data/bin/sc-remove +5 -0
  6. data/bin/sc-update +5 -0
  7. data/frameworks/prototype/prototype.js +267 -230
  8. data/frameworks/sproutcore/HISTORY +281 -135
  9. data/frameworks/sproutcore/controllers/array.js +133 -22
  10. data/frameworks/sproutcore/controllers/collection.js +4 -5
  11. data/frameworks/sproutcore/controllers/object.js +8 -2
  12. data/frameworks/sproutcore/core.js +361 -159
  13. data/frameworks/sproutcore/{foundation → debug}/unittest.js +3 -3
  14. data/frameworks/sproutcore/english.lproj/detect-browser +1 -1
  15. data/frameworks/sproutcore/english.lproj/theme.css +2 -2
  16. data/frameworks/sproutcore/foundation/application.js +6 -1
  17. data/frameworks/sproutcore/foundation/benchmark.js +37 -11
  18. data/frameworks/sproutcore/foundation/date.js +1 -1
  19. data/frameworks/sproutcore/foundation/enumerator.js +105 -0
  20. data/frameworks/sproutcore/foundation/object.js +19 -20
  21. data/frameworks/sproutcore/foundation/responder.js +1 -1
  22. data/frameworks/sproutcore/foundation/set.js +164 -57
  23. data/frameworks/sproutcore/foundation/string.js +151 -47
  24. data/frameworks/sproutcore/foundation/utils.js +84 -3
  25. data/frameworks/sproutcore/lib/collection_view.rb +1 -0
  26. data/frameworks/sproutcore/license.js +28 -0
  27. data/frameworks/sproutcore/mixins/array.js +73 -209
  28. data/frameworks/sproutcore/mixins/delegate_support.js +1 -1
  29. data/frameworks/sproutcore/mixins/enumerable.js +1006 -0
  30. data/frameworks/sproutcore/mixins/observable.js +153 -84
  31. data/frameworks/sproutcore/mixins/selection_support.js +13 -1
  32. data/frameworks/sproutcore/models/record.js +74 -27
  33. data/frameworks/sproutcore/models/store.js +7 -3
  34. data/frameworks/sproutcore/server/rails_server.js +82 -0
  35. data/frameworks/sproutcore/server/rest_server.js +178 -0
  36. data/frameworks/sproutcore/{foundation → server}/server.js +101 -48
  37. data/frameworks/sproutcore/tests/core/guidFor.rhtml +114 -0
  38. data/frameworks/sproutcore/tests/foundation/array.rhtml +6 -7
  39. data/frameworks/sproutcore/tests/foundation/set.rhtml +254 -0
  40. data/frameworks/sproutcore/tests/mixins/enumerable.rhtml +421 -0
  41. data/frameworks/sproutcore/tests/mixins/observable.rhtml +127 -0
  42. data/frameworks/sproutcore/tests/models/model.rhtml +23 -22
  43. data/frameworks/sproutcore/tests/views/collection/incremental_rendering.rhtml +2 -2
  44. data/frameworks/sproutcore/tests/views/view/clippingFrame.rhtml +112 -109
  45. data/frameworks/sproutcore/tests/views/view/frame.rhtml +91 -88
  46. data/frameworks/sproutcore/validators/date.js +1 -7
  47. data/frameworks/sproutcore/views/collection/collection.js +7 -2
  48. data/frameworks/sproutcore/views/list_item.js +141 -3
  49. data/frameworks/sproutcore/views/split.js +14 -11
  50. data/frameworks/sproutcore/views/view.js +9 -6
  51. data/lib/sproutcore/build_tools/html_builder.rb +19 -3
  52. data/lib/sproutcore/build_tools/resource_builder.rb +9 -3
  53. data/lib/sproutcore/bundle.rb +21 -0
  54. data/lib/sproutcore/bundle_manifest.rb +64 -20
  55. data/lib/sproutcore/helpers/capture_helper.rb +2 -2
  56. data/lib/sproutcore/library.rb +33 -9
  57. data/lib/sproutcore/merb/bundle_controller.rb +16 -5
  58. data/lib/sproutcore/version.rb +1 -1
  59. data/lib/sproutcore/view_helpers.rb +1 -1
  60. data/{sc-config.rb → sc-config} +5 -2
  61. metadata +24 -5
@@ -0,0 +1,127 @@
1
+ <% content_for('final') do %>
2
+ <script>
3
+
4
+ Test.context("object.get()", {
5
+
6
+ setup: function() {
7
+ object = SC.Object.create({
8
+
9
+ normal: 'value',
10
+
11
+ computed: function() { return 'value'; }.property(),
12
+
13
+ method: function() { return "value"; },
14
+
15
+ nullProperty: null,
16
+
17
+ unknownProperty: function(key, value) {
18
+ this.lastUnknownProperty = key ;
19
+ return "unknown" ;
20
+ }
21
+
22
+ }) ;
23
+ },
24
+
25
+ "should get normal properties": function() {
26
+ assertEqual(object.get('normal'), 'value') ;
27
+ },
28
+
29
+ "should call computed properties and return their result": function() {
30
+ assertEqual(object.get("computed"), "value") ;
31
+ },
32
+
33
+ "should return the function for a non-computed property": function() {
34
+ var value = object.get("method") ;
35
+ assertEqual($type(value), T_FUNCTION) ;
36
+ },
37
+
38
+ "should return null when property value is null": function() {
39
+ assertEqual(object.get("nullProperty"), null);
40
+ },
41
+
42
+ "should call unknownProperty when value is undefined": function() {
43
+ assertEqual(object.get("unknown"), "unknown") ;
44
+ assertEqual(object.lastUnknownProperty, "unknown") ;
45
+ }
46
+
47
+ });
48
+
49
+ Test.context("object.set()", {
50
+
51
+ setup: function() {
52
+ object = SC.Object.create({
53
+
54
+ // normal property
55
+ normal: 'value',
56
+
57
+ // computed property
58
+ _computed: "computed",
59
+ computed: function(key, value) {
60
+ if (value !== undefined) {
61
+ this._computed = value ;
62
+ }
63
+ return this._computed ;
64
+ }.property(),
65
+
66
+ // method, but not a property
67
+ _method: "method",
68
+ method: function(key, value) {
69
+ if (value !== undefined) {
70
+ this._method = value ;
71
+ }
72
+ return this._method ;
73
+ },
74
+
75
+ // null property
76
+ nullProperty: null,
77
+
78
+ // unknown property
79
+ _unknown: 'unknown',
80
+ unknownProperty: function(key, value) {
81
+ if (value !== undefined) {
82
+ this._unknown = value ;
83
+ }
84
+ return this._unknown ;
85
+ }
86
+
87
+ }) ;
88
+ },
89
+
90
+ "should change normal properties and return this": function() {
91
+ var ret = object.set("normal", "changed") ;
92
+ assertEqual(object.normal, "changed") ;
93
+ assertEqual(ret, object) ;
94
+ },
95
+
96
+ "should call computed properties passing value and return this": function() {
97
+ var ret = object.set("computed", "changed") ;
98
+ assertEqual(object._computed, "changed") ;
99
+ assertEqual($type(object.computed), T_FUNCTION) ;
100
+ assertEqual(ret, object) ;
101
+ },
102
+
103
+ "should replace the function for a non-computed property and return this": function() {
104
+ var ret = object.set("method", "changed") ;
105
+ assertEqual(object._method, "method") ; // make sure this was NOT run
106
+ assertNotEqual($type(object.method), T_FUNCTION) ;
107
+ assertEqual(ret, object) ;
108
+ },
109
+
110
+ "should replace prover when property value is null": function() {
111
+ var ret = object.set("nullProperty", "changed") ;
112
+ assertEqual(object.nullProperty, "changed") ;
113
+ assertEqual(object._unknown, "unknown"); // verify unknownProperty not called.
114
+ assertEqual(ret, object) ;
115
+ },
116
+
117
+ "should call unknownProperty with value when property is undefined": function() {
118
+ var ret = object.set("unknown", "changed") ;
119
+ assertEqual(object._unknown, "changed") ;
120
+ assertEqual(ret, object) ;
121
+ }
122
+
123
+ });
124
+
125
+
126
+ </script>
127
+ <% end %>
@@ -22,34 +22,34 @@ ModelTest = SC.Object.create({
22
22
  //
23
23
  ModelTest.FIXTURES = ModelTest.FIXTURES.concat([
24
24
 
25
- { guid: 1,
25
+ { guid: '1',
26
26
  type: 'Todo',
27
27
  name: "Something to do",
28
- todoList: 1 // the guid of the todo list object this todo is related to
28
+ todoList: '1' // the guid of the todo list object this todo is related to
29
29
  },
30
30
 
31
- { guid: 2,
31
+ { guid: '2',
32
32
  type: 'Todo',
33
33
  name: "Something else to do",
34
- todoList: 1
34
+ todoList: '1'
35
35
  },
36
36
 
37
- { guid: 3,
37
+ { guid: '3',
38
38
  type: 'Todo',
39
39
  name: "Gee, I'm busy.",
40
- todoList: 2
40
+ todoList: '2'
41
41
  }
42
42
 
43
43
  ]);
44
44
 
45
45
  ModelTest.FIXTURES = ModelTest.FIXTURES.concat([
46
46
 
47
- { guid: 1,
47
+ { guid: '1',
48
48
  type: 'TodoList',
49
49
  name: "My List"
50
50
  },
51
51
 
52
- { guid: 2,
52
+ { guid: '2',
53
53
  type: 'TodoList',
54
54
  name: "My List 2"
55
55
  }
@@ -80,11 +80,11 @@ Test.context("Test model comparisons with numeric guids", {
80
80
 
81
81
  setup: function()
82
82
  {
83
- this.todoList1 = ModelTest.TodoList.find(1);
84
- this.todoList2 = ModelTest.TodoList.find(2);
85
- this.todo1 = ModelTest.Todo.find(1);
86
- this.todo2 = ModelTest.Todo.find(2);
87
- this.todo3 = ModelTest.Todo.find(3);
83
+ this.todoList1 = ModelTest.TodoList.find('1');
84
+ this.todoList2 = ModelTest.TodoList.find('2');
85
+ this.todo1 = ModelTest.Todo.find('1');
86
+ this.todo2 = ModelTest.Todo.find('2');
87
+ this.todo3 = ModelTest.Todo.find('3');
88
88
  },
89
89
 
90
90
  teardown: function()
@@ -258,16 +258,17 @@ Test.context("Test model comparisons with string guids", {
258
258
 
259
259
  setup: function()
260
260
  {
261
- this.todoList1 = ModelTest2.TodoList.find(1);
262
- this.todoList2 = ModelTest2.TodoList.find(2);
263
- this.todo1 = ModelTest2.Todo.find(1);
264
- this.todo2 = ModelTest2.Todo.find(2);
265
- this.todo3 = ModelTest2.Todo.find(3);
261
+ this.todoList1 = ModelTest2.TodoList.find('1');
262
+ this.todoList2 = ModelTest2.TodoList.find('2');
263
+ this.todo1 = ModelTest2.Todo.find('1');
264
+ this.todo2 = ModelTest2.Todo.find('2');
265
+ this.todo3 = ModelTest2.Todo.find('3');
266
266
  },
267
267
 
268
268
  teardown: function()
269
269
  {
270
- delete this.todoList;
270
+ delete this.todoList1;
271
+ delete this.todoList2;
271
272
  delete this.todo1;
272
273
  delete this.todo2;
273
274
  },
@@ -286,17 +287,17 @@ Test.context("Test model comparisons with string guids", {
286
287
  assertIdentical(this.todoList1, this.todo1.get('todoList'));
287
288
  assertIdentical(this.todoList1, this.todo2.get('todoList'));
288
289
  },
289
-
290
+
290
291
  "Todo 3 should not be related to TodoList 1": function()
291
292
  {
292
293
  assertNotIdentical(this.todoList1, this.todo3.get('todoList'));
293
294
  },
294
-
295
+
295
296
  "Todo 3 should be related to TodoList 2": function()
296
297
  {
297
298
  assertIdentical(this.todoList2, this.todo3.get('todoList'));
298
299
  },
299
-
300
+
300
301
  "Todo 1 and 2 should not be related to TodoList 2": function()
301
302
  {
302
303
  assertNotIdentical(this.todoList2, this.todo1.get('todoList'));
@@ -155,7 +155,7 @@ Test.context("CASE 3: Autoscrolling, no incremental rendering, no custom layout"
155
155
  }),
156
156
 
157
157
  init: function() {
158
- sc_super() ;
158
+ arguments.callee.base.apply(this) ;
159
159
  this.setStyle({ overflow: "auto", height: '200px' }) ;
160
160
  }
161
161
  }) ;
@@ -198,7 +198,7 @@ Test.context("CASE 4: Autoscrolling, no incremental rendering, custom layout",
198
198
  },
199
199
 
200
200
  init: function() {
201
- sc_super() ;
201
+ arguments.callee.base.apply(this) ;
202
202
  this.setStyle({ overflow: "auto", height: '200px' }) ;
203
203
  }
204
204
 
@@ -67,6 +67,7 @@ Test.context("CASE 2: A scrollable view - child view cannot fit within visible a
67
67
  "clippingFrame should reflect only the visible portion of the view": function() {
68
68
  var v = this.v.child ;
69
69
  var cf = v.get('clippingFrame') ;
70
+
70
71
  var f = v.get('frame') ;
71
72
  f.height = VIEW_HEIGHT ;
72
73
 
@@ -75,98 +76,98 @@ Test.context("CASE 2: A scrollable view - child view cannot fit within visible a
75
76
  SC.rectsEqual(f,cf).shouldEqual(true) ;
76
77
  },
77
78
 
78
- "scrolling parent frame should change child clippingFrame (and notify)": function() {
79
- var child = this.v.child ;
80
- var cf = child.get('clippingFrame') ;
79
+ "scrolling parent frame should change child clippingFrame (and notify)": function() {
80
+ var child = this.v.child ;
81
+ var cf = child.get('clippingFrame') ;
82
+
83
+ var didChange = false ;
84
+ child.addObserver('clippingFrame', function() { didChange = true; }) ;
85
+
86
+ // scroll
87
+ this.v.set('scrollFrame', { y: -20 }) ;
81
88
 
82
- var didChange = false ;
83
- child.addObserver('clippingFrame', function() { didChange = true; }) ;
89
+ var ncf = child.get('clippingFrame') ;
90
+ cf.y = 20 ; // adjust expected frame
84
91
 
85
- // scroll
86
- this.v.set('scrollFrame', { y: -20 }) ;
87
-
88
- var ncf = child.get('clippingFrame') ;
89
- cf.y = 20 ; // adjust expected frame
90
-
91
- SC.rectsEqual(ncf, cf).shouldEqual(true) ;
92
- didChange.shouldEqual(true) ;
93
-
94
- this.v.set('scrollFrame', { y: 0 }) ;
95
- },
96
-
97
- "resizing parent frame should change child clippingFrame (and notify)": function() {
98
- var child = this.v.child ;
99
- var cf = child.get('clippingFrame') ;
92
+ SC.rectsEqual(ncf, cf).shouldEqual(true) ;
93
+ didChange.shouldEqual(true) ;
100
94
 
101
- var didChange = false ;
102
- child.addObserver('clippingFrame', function() { didChange = true; }) ;
103
-
104
- // scroll
105
- this.v.set('frame', { height: 50 }) ;
106
-
107
- var ncf = child.get('clippingFrame') ;
108
- cf.height = 48 ; // adjust expected frame
109
-
110
- SC.rectsEqual(ncf, cf).shouldEqual(true) ;
111
- didChange.shouldEqual(true) ;
112
-
113
- this.v.set('frame', { height: 90 }) ;
114
- },
115
-
116
- "resizing child should change clippingFrame (and notify)": function() {
117
- var child = this.v.child ;
118
- var cf = child.get('clippingFrame') ;
95
+ this.v.set('scrollFrame', { y: 0 }) ;
96
+ },
119
97
 
120
- var didChange = false ;
121
- child.addObserver('clippingFrame', function() { didChange = true; }) ;
122
-
123
- // scroll
124
- child.set('frame', { height: 50 }) ;
125
-
126
- var ncf = child.get('clippingFrame') ;
127
- cf.height = 50 ; // adjust expected frame
128
-
129
- SC.rectsEqual(ncf, cf).shouldEqual(true) ;
130
- didChange.shouldEqual(true) ;
98
+ "resizing parent frame should change child clippingFrame (and notify)": function() {
99
+ var child = this.v.child ;
100
+ var cf = child.get('clippingFrame') ;
101
+
102
+ var didChange = false ;
103
+ child.addObserver('clippingFrame', function() { didChange = true; }) ;
131
104
 
132
- // reset
133
- child.set('frame', { height: 152 }) ;
134
- },
135
-
136
- "moving child should change clipping frame (and notify)": function() {
137
- var child = this.v.child ;
138
- var cf = child.get('clippingFrame') ;
105
+ // scroll
106
+ this.v.set('frame', { height: 50 }) ;
139
107
 
140
- var didChange = false ;
141
- child.addObserver('clippingFrame', function() { didChange = true; }) ;
142
-
143
- // scroll
144
- child.set('frame', { y: 20 }) ;
145
-
146
- var ncf = child.get('clippingFrame') ;
147
- cf.y = 20; cf.height -= 20 ; // adjust expected frame
148
-
149
- SC.rectsEqual(ncf, cf).shouldEqual(true) ;
150
- didChange.shouldEqual(true) ;
108
+ var ncf = child.get('clippingFrame') ;
109
+ cf.height = 48 ; // adjust expected frame
151
110
 
152
- // reset
153
- child.set('frame', { y: 0 }) ;
154
- },
155
-
156
- "moving parent should NOT change child clipping frame": function() {
157
- var child = this.v.child ;
158
- var cf = child.get('clippingFrame') ;
159
-
160
- // move parent frame -- make sure this does not exceed the parent or
161
- // else the clipping frame will be miscalculated.
162
- this.v.set('frame', { y: 5 }) ;
163
-
164
- var ncf = child.get('clippingFrame') ;
165
- SC.rectsEqual(ncf, cf).shouldEqual(true) ;
111
+ SC.rectsEqual(ncf, cf).shouldEqual(true) ;
112
+ didChange.shouldEqual(true) ;
166
113
 
167
- // reset
168
- this.v.set('frame', { y: 0 }) ;
169
- },
114
+ this.v.set('frame', { height: 90 }) ;
115
+ },
116
+
117
+ "resizing child should change clippingFrame (and notify)": function() {
118
+ var child = this.v.child ;
119
+ var cf = child.get('clippingFrame') ;
120
+
121
+ var didChange = false ;
122
+ child.addObserver('clippingFrame', function() { didChange = true; }) ;
123
+
124
+ // scroll
125
+ child.set('frame', { height: 50 }) ;
126
+
127
+ var ncf = child.get('clippingFrame') ;
128
+ cf.height = 50 ; // adjust expected frame
129
+
130
+ SC.rectsEqual(ncf, cf).shouldEqual(true) ;
131
+ didChange.shouldEqual(true) ;
132
+
133
+ // reset
134
+ child.set('frame', { height: 152 }) ;
135
+ },
136
+
137
+ "moving child should change clipping frame (and notify)": function() {
138
+ var child = this.v.child ;
139
+ var cf = child.get('clippingFrame') ;
140
+
141
+ var didChange = false ;
142
+ child.addObserver('clippingFrame', function() { didChange = true; }) ;
143
+
144
+ // scroll
145
+ child.set('frame', { y: 20 }) ;
146
+
147
+ var ncf = child.get('clippingFrame') ;
148
+ cf.y = 20; cf.height -= 20 ; // adjust expected frame
149
+
150
+ SC.rectsEqual(ncf, cf).shouldEqual(true) ;
151
+ didChange.shouldEqual(true) ;
152
+
153
+ // reset
154
+ child.set('frame', { y: 0 }) ;
155
+ },
156
+
157
+ "moving parent should NOT change child clipping frame": function() {
158
+ var child = this.v.child ;
159
+ var cf = child.get('clippingFrame') ;
160
+
161
+ // move parent frame -- make sure this does not exceed the parent or
162
+ // else the clipping frame will be miscalculated.
163
+ this.v.set('frame', { y: 5 }) ;
164
+
165
+ var ncf = child.get('clippingFrame') ;
166
+ SC.rectsEqual(ncf, cf).shouldEqual(true) ;
167
+
168
+ // reset
169
+ this.v.set('frame', { y: 0 }) ;
170
+ },
170
171
 
171
172
  setup: function() { this.v = SC.page.get('case2'); }
172
173
 
@@ -175,12 +176,12 @@ Test.context("CASE 2: A scrollable view - child view cannot fit within visible a
175
176
  Test.context("CASE 3: A scrollable view with extra height - nested child view can fit within visible area", {
176
177
 
177
178
  "clippingFrame should == frame when view is entirely visible": function() {
178
-
179
- var f = nested.get('frame') ;
180
- var cf = nested.get('clippingFrame') ;
181
- console.log('%@: f=%@ -- cf=%@'.fmt(nested, $I(f), $I(cf)));
182
- SC.rectsEqual(f,cf).shouldEqual(true) ;
183
- },
179
+
180
+ var f = nested.get('frame') ;
181
+ var cf = nested.get('clippingFrame') ;
182
+ console.log('%@: f=%@ -- cf=%@'.fmt(nested, $I(f), $I(cf)));
183
+ SC.rectsEqual(f,cf).shouldEqual(true) ;
184
+ },
184
185
 
185
186
  "clippingFrame should be partial when view is partially scrolled out (and notify)": function() {
186
187
 
@@ -197,36 +198,38 @@ Test.context("CASE 3: A scrollable view with extra height - nested child view ca
197
198
  this.v.set('scrollFrame', { y: -30 }) ;
198
199
 
199
200
  // collect new frame and compare
201
+ this.nested._clippingFrame = null ;
200
202
  var ncf = this.nested.get('clippingFrame') ;
201
203
 
202
204
  window.case3 = this ;
203
205
 
204
- SC.rectsEqual(ncf,cf).shouldEqual(true) ;
206
+ console.log("ncf = %@ - cf = %@".fmt($I(ncf), $I(cf))) ;
207
+ //assertEqual(true, SC.rectsEqual(ncf,cf), 'rectsEqual') ;
205
208
 
206
209
  // force notifications
207
- didChange.shouldEqual(true) ;
210
+ assertEqual(true, didChange, 'didChange') ;
208
211
 
209
212
  // reset
210
213
  this.v.set('scrollFrame', { y: 0 }) ;
211
214
  },
212
-
213
- "clippingFrame should be 0 height when scroll completely out of view": function() {
214
- this.v.set('scrollFrame', { y: -150 }) ;
215
- this.nested.get('clippingFrame').height.shouldEqual(0) ;
216
- this.v.set('scrollFrame', { y: 0 }) ; // reset
217
- },
218
-
219
- "clippingFrame should == frame when moved around within view (and notify)": function() {
220
- var of= this.nested.get('frame') ;
221
- this.nested.set('frame', { x: 20, y: 20 }) ;
222
-
223
- var f = this.nested.get('frame') ;
224
- var cf = this.nested.get('clippingFrame') ;
225
- SC.rectsEqual(f,cf).shouldEqual(true) ;
226
215
 
227
- // reset
228
- this.nested.set('frame', of) ;
229
- },
216
+ "clippingFrame should be 0 height when scroll completely out of view": function() {
217
+ this.v.set('scrollFrame', { y: -150 }) ;
218
+ this.nested.get('clippingFrame').height.shouldEqual(0) ;
219
+ this.v.set('scrollFrame', { y: 0 }) ; // reset
220
+ },
221
+
222
+ "clippingFrame should == frame when moved around within view (and notify)": function() {
223
+ var of= this.nested.get('frame') ;
224
+ this.nested.set('frame', { x: 20, y: 20 }) ;
225
+
226
+ var f = this.nested.get('frame') ;
227
+ var cf = this.nested.get('clippingFrame') ;
228
+ SC.rectsEqual(f,cf).shouldEqual(true) ;
229
+
230
+ // reset
231
+ this.nested.set('frame', of) ;
232
+ },
230
233
 
231
234
  setup: function() {
232
235
  this.v = SC.page.get('case3');