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
@@ -10,101 +10,102 @@
10
10
 
11
11
  <script>
12
12
 
13
- // Test.context("CASE 1: Auto-layout view with no padding & no border", {
14
- //
15
- // "frame should reflect current offset settings": function() {
16
- // var el = this.v.rootElement ;
17
- // var f = { x: el.offsetTop, y: el.offsetLeft, width: el.offsetWidth, height: el.offsetHeight };
18
- // console.log(this.v.get('frame').x) ;
19
- // console.log('this.frame', this.v.get('frame')) ;
20
- // SC.rectsEqual(f, this.v.get('frame')).shouldEqual(true) ;
21
- // },
22
- //
23
- // "frame should change when CSS changed": function() {
24
- // var origFrame = this.v.get('frame') ;
25
- //
26
- // this.v.addClassName('half') ;
27
- // this.v.viewFrameDidChange() ;
28
- //
29
- // SC.rectsEqual(this.v.get('frame'), origFrame).shouldEqual(false) ;
30
- //
31
- // // remove the class. make sure we have restored the frame
32
- // this.v.removeClassName('half') ; //reset
33
- // this.v.viewFrameDidChange() ;
34
- // SC.rectsEqual(this.v.get('frame'), origFrame).shouldEqual(true) ;
35
- // },
36
- //
37
- // "setting frame should change style, even if it does not impact actual value": function() {
38
- // var f = this.v.get('frame') ;
39
- // var ret = this.v.set('frame', { x: 10, y: 10 }) ;
40
- //
41
- // // it should change the style though
42
- // this.v.getStyle('left').shouldEqual('10px');
43
- // this.v.getStyle('top').shouldEqual('10px');
44
- //
45
- // // change the top & left. Since the layout is static here, this should not actually
46
- // // change anything.
47
- // SC.rectsEqual(ret, f).shouldEqual(true) ;
48
- // },
49
- //
50
- // setup: function() { this.v = SC.page.get('case1'); }
51
- //
52
- // });
53
- // //
54
- // // CASE 2: Manual-layout of view
55
- // Test.context("CASE 2: Manual-layout of view", {
56
- //
57
- // "frame should reflect current offset settings at first": function() {
58
- // var el = this.v.rootElement ;
59
- // var f = { x: el.offsetTop, y: el.offsetLeft, width: el.offsetWidth, height: el.offsetHeight };
60
- // SC.rectsEqual(f, this.v.get('frame')).shouldEqual(true) ;
61
- // },
13
+ Test.context("CASE 1: Auto-layout view with no padding & no border", {
14
+
15
+ "frame should reflect current offset settings": function() {
16
+ var el = this.v.rootElement ;
17
+ var f = { x: el.offsetTop, y: el.offsetLeft, width: el.offsetWidth, height: el.offsetHeight };
18
+ console.log(this.v.get('frame').x) ;
19
+ console.log('this.frame', this.v.get('frame')) ;
20
+ SC.rectsEqual(f, this.v.get('frame')).shouldEqual(true) ;
21
+ },
22
+
23
+ "frame should change when CSS changed": function() {
24
+ var origFrame = this.v.get('frame') ;
25
+
26
+ this.v.addClassName('half') ;
27
+ this.v.viewFrameDidChange() ;
28
+
29
+ SC.rectsEqual(this.v.get('frame'), origFrame).shouldEqual(false) ;
30
+
31
+ // remove the class. make sure we have restored the frame
32
+ this.v.removeClassName('half') ; //reset
33
+ this.v.viewFrameDidChange() ;
34
+ SC.rectsEqual(this.v.get('frame'), origFrame).shouldEqual(true) ;
35
+ },
36
+
37
+ "setting frame should change style, even if it does not impact actual value": function() {
38
+ var f = this.v.get('frame') ;
39
+ var ret = this.v.set('frame', { x: 10, y: 10 }) ;
40
+
41
+ // it should change the style though
42
+ this.v.getStyle('left').shouldEqual('10px');
43
+ this.v.getStyle('top').shouldEqual('10px');
44
+
45
+ // change the top & left. Since the layout is static here, this should not actually
46
+ // change anything.
47
+ SC.rectsEqual(ret, f).shouldEqual(true) ;
48
+ },
49
+
50
+ setup: function() { this.v = SC.page.get('case1'); }
51
+
52
+ });
62
53
  //
63
- // "should get absolute positioning": function() {
64
- // this.v.getStyle('position').shouldEqual('absolute') ;
65
- // },
66
- //
67
- // "frame should reflect set values exactly": function() {
68
- // var f = { x: 10, y: 10, width: 20, height: 20 } ;
69
- // var ret = this.v.set('frame', f);
70
- // SC.rectsEqual(f,ret).shouldEqual(true) ;
71
- // SC.rectsEqual(this.v.get('frame'), f).shouldEqual(true) ;
72
- // },
73
- //
74
- // "actual offset should reflect set values exactly": function() {
75
- // var f = { x: 10, y: 10, width: 20, height: 20 } ;
76
- // var ret = this.v.set('frame', f);
77
- // var el = this.v.rootElement;
78
- // el.offsetLeft.shouldEqual(10) ;
79
- // el.offsetTop.shouldEqual(10) ;
80
- // el.offsetWidth.shouldEqual(20) ;
81
- // el.offsetHeight.shouldEqual(20) ;
82
- // },
83
- //
84
- // "applying CSS class should not change frame": function() {
85
- // var f = this.v.get('frame') ;
86
- // this.v.addClassName('half') ;
87
- // SC.rectsEqual(this.v.get('frame'),f).shouldEqual(true) ;
88
- // },
89
- //
90
- // "getting frame should cache the value": function() {
91
- // f = this.v.get('frame') ;
92
- // this.assertNotNull(this.v._frame) ;
93
- // },
94
- //
95
- // setup: function() { this.v = SC.page.get('case2'); }
96
- //
97
- // });
54
+ // CASE 2: Manual-layout of view
55
+ Test.context("CASE 2: Manual-layout of view", {
56
+
57
+ "frame should reflect current offset settings at first": function() {
58
+ var el = this.v.rootElement ;
59
+ var f = { x: el.offsetTop, y: el.offsetLeft, width: el.offsetWidth, height: el.offsetHeight };
60
+ SC.rectsEqual(f, this.v.get('frame')).shouldEqual(true) ;
61
+ },
62
+
63
+ "should get absolute positioning": function() {
64
+ this.v.getStyle('position').shouldEqual('absolute') ;
65
+ },
66
+
67
+ "frame should reflect set values exactly": function() {
68
+ var f = { x: 10, y: 10, width: 20, height: 20 } ;
69
+ var ret = this.v.set('frame', f);
70
+ SC.rectsEqual(f,ret).shouldEqual(true) ;
71
+ SC.rectsEqual(this.v.get('frame'), f).shouldEqual(true) ;
72
+ },
73
+
74
+ "actual offset should reflect set values exactly": function() {
75
+ var f = { x: 10, y: 10, width: 20, height: 20 } ;
76
+ var ret = this.v.set('frame', f);
77
+ var el = this.v.rootElement;
78
+ el.offsetLeft.shouldEqual(10) ;
79
+ el.offsetTop.shouldEqual(10) ;
80
+ el.offsetWidth.shouldEqual(20) ;
81
+ el.offsetHeight.shouldEqual(20) ;
82
+ },
83
+
84
+ "applying CSS class should not change frame": function() {
85
+ var f = this.v.get('frame') ;
86
+ this.v.addClassName('half') ;
87
+ SC.rectsEqual(this.v.get('frame'),f).shouldEqual(true) ;
88
+ },
89
+
90
+ "getting frame should cache the value": function() {
91
+ f = this.v.get('frame') ;
92
+ this.assertNotNull(this.v._frame) ;
93
+ },
94
+
95
+ setup: function() { this.v = SC.page.get('case2'); }
96
+
97
+ });
98
98
 
99
99
  // CASE 3: Manual-layout View with padding & border
100
100
  Test.context("CASE 3: Manual-layout of view with padding & border", {
101
101
 
102
102
  "frame size should include padding and border": function() {
103
- var f = this.v.get('frame') ;
104
- var el = this.v.rootElement ;
103
+ var f = v.get('frame') ;
104
+ var el = v.rootElement ;
105
105
 
106
- Math.round(v.get('styleWidth')).shouldEqual(f.width - 24) ;
107
- Math.round(v.get('styleHeight')).shouldEqual(f.height - 24) ;
106
+ //debugger ;
107
+ assertEqual(Math.round(v.get('styleWidth')), f.width-24, 'width') ;
108
+ assertEqual(Math.round(v.get('styleHeight')), f.height-24, 'height') ;
108
109
  },
109
110
 
110
111
  "changing frame size should subtract padding and border": function() {
@@ -155,11 +156,13 @@ Test.context("CASE 4: Absolute positioned Child view inside of view with overflo
155
156
  Test.context("CASE 5: Non-positioned Child view inside of view with overflow = hidden", {
156
157
 
157
158
  "frame origin should be 0,0 & size should match owner's innerFrame": function() {
159
+ SC.DEBUGIT = YES ;
158
160
  var vf = this.v.get('innerFrame') ;
159
161
  var f = this.v.child.get('frame') ;
160
162
  f.x.shouldEqual(0) ;
161
163
  f.y.shouldEqual(0) ;
162
164
  f.width.shouldEqual(vf.width) ;
165
+ SC.DEBUGIT = NO ;
163
166
  },
164
167
 
165
168
  "nested frame origin should be 1,1 to account for border offset of parent": function() {
@@ -21,12 +21,6 @@ SC.Validator.Date = SC.Validator.extend(
21
21
  */
22
22
  format: 'NNN d, yyyy h:mm:ss a',
23
23
 
24
- /**
25
- If true, dates will be converted to a natural language format if
26
- possible such as "Tomorrow" or "Today".
27
- */
28
- naturalLanguage: true,
29
-
30
24
  /**
31
25
  if we have a number, then convert to a date object.
32
26
  */
@@ -36,7 +30,7 @@ SC.Validator.Date = SC.Validator.extend(
36
30
  date = new Date(object) ;
37
31
  } else if (object instanceof Date) { date = object; }
38
32
 
39
- if (date) object = date.format(this.get('format'),this.get('naturalLanguage')) ;
33
+ if (date) object = date.format(this.get('format')) ;
40
34
 
41
35
  return object ;
42
36
  },
@@ -1575,14 +1575,19 @@ SC.CollectionView = SC.View.extend(SC.CollectionViewDelegate,
1575
1575
 
1576
1576
  // get the content. Bail if this cannot be used as an array.
1577
1577
  var content = this.get('content') ;
1578
- if (!content || !content.removeObject) return NO ;
1578
+ if (!content) return NO; // nothing to do
1579
+
1580
+ // determine the method to use
1581
+ var hasDestroyObject = $type(content.destroyObject) === T_FUNCTION ;
1582
+ var hasRemoveObject = $type(content.removeObject) === T_FUNCTION ;
1583
+ if (!hasDestroyObject && !hasRemoveObject) return NO; // nothing to do
1579
1584
 
1580
1585
  // suspend property notifications and remove the objects...
1581
1586
  if (content.beginPropertyChanges) content.beginPropertyChanges();
1582
1587
  var idx = sel.get('length') ;
1583
1588
  while(--idx >= 0) {
1584
1589
  var item = sel.objectAt(idx) ;
1585
- content.removeObject(item) ;
1590
+ (hasDestroyObject) ? content.destroyObject(item) : content.removeObject(item);
1586
1591
  }
1587
1592
  // begin notifying again...
1588
1593
  if (content.endPropertyChanges) content.endPropertyChanges() ;
@@ -52,6 +52,15 @@ SC.ListItemView = SC.View.extend(SC.Control, SC.InlineEditorDelegate,
52
52
  */
53
53
  hasContentBranch: NO,
54
54
 
55
+ /**
56
+ (displayDelegate) The name of the property used for the checkbox value.
57
+
58
+ The checkbox will only be visible if this key is not null.
59
+
60
+ @type {String}
61
+ */
62
+ contentCheckboxKey: null,
63
+
55
64
  /**
56
65
  (displayDelegate) Property key to use for the icon url
57
66
 
@@ -105,6 +114,13 @@ SC.ListItemView = SC.View.extend(SC.Control, SC.InlineEditorDelegate,
105
114
  var content = this.get('content') ;
106
115
  var del = this.displayDelegate ;
107
116
 
117
+ // handle checkbox
118
+ var checkboxKey = this.getDelegateProperty(del, 'contentCheckboxKey') ;
119
+ if (checkboxKey) {
120
+ var checkboxValue = (content && content.get) ? content.get(checkboxKey) : false ;
121
+ html.push(this.renderCheckboxHtml(checkboxValue)) ;
122
+ }
123
+
108
124
  // handle icon
109
125
  if (this.getDelegateProperty(del, 'hasContentIcon')) {
110
126
  var iconKey = this.getDelegateProperty(del,'contentIconKey') ;
@@ -147,12 +163,50 @@ SC.ListItemView = SC.View.extend(SC.Control, SC.InlineEditorDelegate,
147
163
  }
148
164
  },
149
165
 
166
+ /**
167
+ Generates the HTML string used to represent the checkbox for your list
168
+ item. Override this to return your own custom HTML. The default version
169
+ will use the HTML provided by SC.CheckboxView.
170
+
171
+ @returns {String}
172
+ @param state {String} the checkbox state. YES, NO, or SC.MIXED_STATE
173
+ */
174
+ renderCheckboxHtml: function(state) {
175
+ var ret ;
176
+
177
+ // Note: this basically takes the HTML from the checkbox view and then
178
+ // inserts class names as necessary. This is cached to avoid using too
179
+ // much memory.
180
+ if (state === SC.MIXED_STATE) {
181
+ ret = SC.ListItemView._mixedCheckboxHtml ;
182
+ if (!ret) {
183
+ ret = SC.CheckboxView.prototype.emptyElement ;
184
+ ret = ret.replace('class="', 'class="mixed ') ;
185
+ SC.ListItemView._mixedCheckboxHtml = ret ;
186
+ }
187
+ } else if (state) {
188
+ ret = SC.ListItemView._selectedCheckboxHtml ;
189
+ if (!ret) {
190
+ ret = SC.CheckboxView.prototype.emptyElement ;
191
+ ret = ret.replace('class="', 'class="sel ') ;
192
+ SC.ListItemView._selectedCheckboxHtml = ret ;
193
+ }
194
+ } else {
195
+ ret = SC.ListItemView._normalCheckboxHtml ;
196
+ if (!ret) {
197
+ ret = SC.CheckboxView.prototype.emptyElement ;
198
+ SC.ListItemView._normalCheckboxHtml = ret ;
199
+ }
200
+ }
201
+ return ret ;
202
+ },
203
+
150
204
  /**
151
205
  renderIconHtml generates the html string used to represent the icon for
152
206
  your list item. override this to return your own custom HTML
153
207
 
154
208
  @returns {String}
155
- @arguments {String} the icon property based on your view's contentIconKey
209
+ @param icon {String} the icon property based on your view's contentIconKey
156
210
  */
157
211
  renderIconHtml: function(icon){
158
212
  var html = [];
@@ -177,7 +231,7 @@ SC.ListItemView = SC.View.extend(SC.Control, SC.InlineEditorDelegate,
177
231
  for your list item. override this to return your own custom HTML
178
232
 
179
233
  @returns {String}
180
- @arguments {String} the label property based on your view's
234
+ @param label {String} the label property based on your view's
181
235
  contentValueKey
182
236
  */
183
237
  renderLabelHtml: function(label){
@@ -203,7 +257,7 @@ SC.ListItemView = SC.View.extend(SC.Control, SC.InlineEditorDelegate,
203
257
  own custom HTML
204
258
 
205
259
  @returns {String}
206
- @arguments {Integer} the label property based on your view's
260
+ @param count {Integer} the label property based on your view's
207
261
  contentValueKey
208
262
  */
209
263
  renderCountHtml: function(count) {
@@ -245,6 +299,90 @@ SC.ListItemView = SC.View.extend(SC.Control, SC.InlineEditorDelegate,
245
299
  return html.join('');
246
300
  },
247
301
 
302
+ _isInsideElementWithClassName: function(className, evt) {
303
+ var el = Event.element(evt) ;
304
+ var rootElement = this.rootElement;
305
+ var ret = NO ;
306
+ while(!ret && el && (el !== rootElement)) {
307
+ if (Element.hasClassName(el, className)) ret = YES ;
308
+ el = el.parentNode ;
309
+ }
310
+
311
+ rootElement = el = null ; //avoid memory leaks
312
+ return ret ;
313
+ },
314
+
315
+ /** @private
316
+ mouseDown is handled only for clicks on the checkbox view or or action
317
+ button.
318
+ */
319
+ mouseDown: function(evt) {
320
+ var del = this.displayDelegate ;
321
+ var checkboxKey = this.getDelegateProperty(del, 'contentCheckboxKey') ;
322
+ if (checkboxKey) {
323
+ if (this._isInsideElementWithClassName('sc-checkbox-view', evt)) {
324
+ this._addCheckboxActiveState() ;
325
+ this._isMouseDownOnCheckbox = YES ;
326
+ this._isMouseInsideCheckbox = YES ;
327
+ return true ;
328
+ }
329
+ }
330
+
331
+ return false ; // otherwise let normal handlers do it...
332
+ },
333
+
334
+ mouseUp: function(evt) {
335
+ var ret= NO ;
336
+ if (this._isMouseDownOnCheckbox) {
337
+
338
+ // update only if mouse inside on mouse up...
339
+ if (this._isMouseInsideCheckbox) {
340
+ var del = this.displayDelegate ;
341
+ var checkboxKey = this.getDelegateProperty(del, 'contentCheckboxKey') ;
342
+ var content = this.get('content') ;
343
+ if (content && content.get) {
344
+ var value = content.get(checkboxKey) ;
345
+ value = (value === SC.MIXED_STATE) ? YES : !value ;
346
+ content.set(checkboxKey, value) ;
347
+ }
348
+ }
349
+
350
+ this._removeCheckboxActiveState() ;
351
+ ret = YES ;
352
+ }
353
+
354
+ this._isMouseInsideCheckbox = this._isMouseDownOnCheckbox = NO ;
355
+ return ret ;
356
+ },
357
+
358
+ mouseOut: function(evt) {
359
+ if (this._isMouseDownOnCheckbox) {
360
+ this._removeCheckboxActiveState() ;
361
+ this._isMouseInsideCheckbox = NO ;
362
+ }
363
+ return NO ;
364
+ },
365
+
366
+ mouseOver: function(evt) {
367
+ if (this._isMouseDownOnCheckbox) {
368
+ this._addCheckboxActiveState() ;
369
+ this._isMouseInsideCheckbox = YES ;
370
+ }
371
+ return NO ;
372
+ },
373
+
374
+ _addCheckboxActiveState: function() {
375
+ var el = this.$sel('.sc-checkbox-view') ;
376
+ if (this.get('isEnabled')) Element.addClassName(el, 'active') ;
377
+ el = null ;
378
+ },
379
+
380
+ _removeCheckboxActiveState: function() {
381
+ var el = this.$sel('.sc-checkbox-view') ;
382
+ Element.removeClassName(el, 'active') ;
383
+ el = null ;
384
+ },
385
+
248
386
  /**
249
387
  Returns true if a click is on the label text itself to enable editing.
250
388
 
@@ -108,17 +108,14 @@ SC.SplitView = SC.View.extend(SC.DelegateSupport,
108
108
 
109
109
  // thickness cannot be greater than the total of all the other views (
110
110
  // except for the flexibleView) added together.
111
- var total = this.get('innerFrame') ;
112
- available = (direction == SC.HORIZONTAL) ? total.width : total.height ;
113
- var views = this.get('childNodes') ;
114
- var idx = view.length ;
115
- var flexibleView = this.get('flexibleView') ;
116
- while(--idx >= 0) {
117
- var currentView = views[idx] ;
118
- if ((currentView != view) && (currentView != flexibleView)) {
119
- available -= this.thicknessForView(currentView) ;
120
- }
121
- }
111
+
112
+ // available = total minus thickness of all views except "view" and
113
+ // "flexible view" that is the same as thickness 'view' plus thickness
114
+ // 'flexible view'
115
+ var flexibleView = this.get('flexibleView');
116
+ var available = this.thicknessForView(view) +
117
+ this.thicknessForView(flexibleView);
118
+
122
119
  thickness = Math.min(thickness, available) ;
123
120
 
124
121
  // cannot be less than 0
@@ -160,6 +157,7 @@ SC.SplitView = SC.View.extend(SC.DelegateSupport,
160
157
  */
161
158
  computeFlexibleView: function() {
162
159
  var flexibleView = this.get('flexibleView') ;
160
+ var originalFlexibleView = flexibleView ;
163
161
  if (!flexibleView) {
164
162
  var views = this.get('childNodes') ;
165
163
  flexibleView = views[Math.ceil(views.length/2)] ;
@@ -170,6 +168,11 @@ SC.SplitView = SC.View.extend(SC.DelegateSupport,
170
168
  flexibleView = flexibleView.get('nextSibling') ;
171
169
  }
172
170
 
171
+ // save new flexible view if we had to fix it up.
172
+ if (originalFlexibleView !== flexibleView) {
173
+ this.set('flexibleView', flexibleView);
174
+ }
175
+
173
176
  return flexibleView;
174
177
  },
175
178
 
@@ -711,7 +711,7 @@ SC.View = SC.Responder.extend(SC.PathModule, SC.DelegateSupport,
711
711
  this.viewFrameDidChange() ;
712
712
  }
713
713
  ret = this.getStyle(key) ;
714
- ret = (ret === 'auto') ? null : parseInt(ret, 0) ;
714
+ ret = (ret === 'auto') ? null : Math.round(parseFloat(ret)) ;
715
715
 
716
716
  // all other properties just pass through (and do not change frame)
717
717
  } else {
@@ -960,7 +960,8 @@ SC.View = SC.Responder.extend(SC.PathModule, SC.DelegateSupport,
960
960
  // impact the offset
961
961
  if (SC.Platform.Firefox) {
962
962
  var parent = el.offsetParent ;
963
- if (parent && (Element.getStyle(parent, 'overflow') != 'visible')) {
963
+ var overflow = (parent) ? Element.getStyle(parent, 'overflow') : 'visible' ;
964
+ if (overflow && overflow !== 'visible') {
964
965
  var left = parseInt(Element.getStyle(parent, 'borderLeftWidth'),0) || 0 ;
965
966
  var top = parseInt(Element.getStyle(parent, 'borderTopWidth'),0) || 0 ;
966
967
  f.x += left; f.y += top ;
@@ -1087,7 +1088,8 @@ SC.View = SC.Responder.extend(SC.PathModule, SC.DelegateSupport,
1087
1088
  // impact the offset
1088
1089
  if (SC.Platform.Firefox) {
1089
1090
  var parent = el.offsetParent ;
1090
- if (parent && (Element.getStyle(parent, 'overflow') != 'visible')) {
1091
+ var overflow = (parent) ? Element.getStyle(parent, 'overflow') : 'visible' ;
1092
+ if (overflow && overflow !== 'visible') {
1091
1093
  var left = parseInt(Element.getStyle(parent, 'borderLeftWidth'),0) || 0 ;
1092
1094
  var top = parseInt(Element.getStyle(parent, 'borderTopWidth'),0) || 0 ;
1093
1095
  f.x += left; f.y += top ;
@@ -1321,7 +1323,7 @@ SC.View = SC.Responder.extend(SC.PathModule, SC.DelegateSupport,
1321
1323
  prect.x -= scrollFrame.x ;
1322
1324
  prect.y -= scrollFrame.y ;
1323
1325
  }
1324
-
1326
+
1325
1327
  // blend with current frame
1326
1328
  f = SC.intersectRects(f, prect) ;
1327
1329
  } else {
@@ -2026,7 +2028,7 @@ SC.View.mixin({
2026
2028
  if (el && el._configured) return SC.View.findViewForElement(el);
2027
2029
 
2028
2030
  // Now that we have found an element, instantiate the view.
2029
- var args = $A(arguments) ; args[0] = { rootElement: el } ;
2031
+ var args = SC.$A(arguments) ; args[0] = { rootElement: el } ;
2030
2032
  if (r) vStart = new Date().getTime();
2031
2033
  var ret = new this(args,this) ; // create instance.
2032
2034
  if (r) SC.idt.v_t += (new Date().getTime()) - vStart;
@@ -2039,7 +2041,7 @@ SC.View.mixin({
2039
2041
 
2040
2042
  // create in the view work is like viewFor but with 'null' for el
2041
2043
  create: function(configs) {
2042
- var args = $A(arguments) ;
2044
+ var args = SC.$A(arguments) ;
2043
2045
  args.unshift(null) ;
2044
2046
  return this.viewFor.apply(this,args) ;
2045
2047
  },
@@ -2106,6 +2108,7 @@ SC.View.mixin({
2106
2108
  break;
2107
2109
  case SC.window.rootElement:
2108
2110
  parentView = SC.window ;
2111
+ break;
2109
2112
  default:
2110
2113
  node = node.parentNode ;
2111
2114
  }
@@ -8,7 +8,7 @@ module SproutCore
8
8
 
9
9
  # Whenever you build an HTML file for a SproutCore client, an instance of
10
10
  # this class is created to actually process and build the HTML using
11
- # Erubus. If you want to add more methods to use in your HTML files, just
11
+ # Erubus or Haml. If you want to add more methods to use in your HTML files, just
12
12
  # include them in HtmlContext.
13
13
  #
14
14
  class HtmlContext
@@ -67,7 +67,15 @@ module SproutCore
67
67
  # Finally, render the layout. This should produce the final output to
68
68
  # return
69
69
  input = File.read(@layout_path)
70
- return eval(Erubis::Eruby.new.convert(input))
70
+
71
+ # render using either erb or haml
72
+ case File.extname(@layout_path)
73
+ when /\.rhtml$/, /\.html.erb$/
74
+ return eval(Erubis::Eruby.new.convert(input))
75
+ when /\.haml$/, /\.html.haml$/
76
+ require 'haml'
77
+ return Haml::Engine.new(input).to_html(self)
78
+ end
71
79
  end
72
80
 
73
81
  # render a single entry
@@ -80,7 +88,15 @@ module SproutCore
80
88
 
81
89
  # render. Result goes into @content_for_resources
82
90
  input = File.read(@entry.source_path)
83
- @content_for_resources += eval(Erubis::Eruby.new.convert(input))
91
+
92
+ # render using either erb or haml
93
+ case File.extname(@entry.source_path)
94
+ when /\.rhtml$/, /\.html.erb$/
95
+ @content_for_resources += eval(Erubis::Eruby.new.convert(input))
96
+ when /\.haml$/, /\.html.haml$/
97
+ require 'haml'
98
+ @content_for_resources += Haml::Engine.new(input).to_html(self)
99
+ end
84
100
 
85
101
  @filename =nil
86
102
  @entry = nil
@@ -59,9 +59,9 @@ module SproutCore
59
59
  #
60
60
  # The default will rewrite calls to static_url().
61
61
  def rewrite_inline_code(line, filename)
62
- line.gsub(/static_url\([\'\"](.+)[\'\"]\)/) do | rsrc |
62
+ line.gsub(/static_url\([\'\"](.+?)[\'\"]\)/) do | rsrc |
63
63
  entry = bundle.find_resource_entry($1, :language => language)
64
- static_url(entry.nil? ? '' : entry.url)
64
+ static_url(entry.nil? ? '' : entry.cacheable_url)
65
65
  end
66
66
  end
67
67
 
@@ -205,7 +205,13 @@ module SproutCore
205
205
  end
206
206
  end
207
207
 
208
- def self.build_fixture(entry, bundle); build_javascript(entry, bundle); end
208
+ def self.build_fixture(entry, bundle)
209
+ build_javascript(entry, bundle)
210
+ end
211
+
212
+ def self.build_debug(entry, bundle)
213
+ build_javascript(entry, bundle)
214
+ end
209
215
 
210
216
  end
211
217
 
@@ -66,6 +66,14 @@ module SproutCore
66
66
  # bundle_name:relative_path/to/client. Default:
67
67
  # sproutcore:lib/index.html
68
68
  #
69
+ # autobuild?: True if the bundle should be included in default builds.
70
+ # If set to false, then you must ASK for the bundle specifically to be
71
+ # built.
72
+ #
73
+ # use_digest_token: If true, the unique tokens adds to files will be
74
+ # MD5 digests instead of timestamps. This will ensure uniqueness when
75
+ # building on multiple machines.
76
+ #
69
77
  class Bundle
70
78
 
71
79
  LONG_LANGUAGE_MAP = { :english => :en, :french => :fr, :german => :de, :japanese => :ja, :spanish => :es, :italian => :it }
@@ -85,6 +93,7 @@ module SproutCore
85
93
  attr_reader :source_root, :build_root, :url_root, :index_root
86
94
  attr_reader :build_mode, :layout
87
95
  attr_reader :make_resources_relative
96
+ attr_reader :use_digest_tokens
88
97
 
89
98
  def library_root
90
99
  @library_root ||= library.nil? ? nil : library.root_path
@@ -116,6 +125,12 @@ module SproutCore
116
125
  library.minify_build_modes.include?(build_mode)
117
126
  end
118
127
 
128
+ # ==== Returns
129
+ # true if this bundle should be auto-built.
130
+ def autobuild?
131
+ @autobuild.nil? ? true : @autobuild
132
+ end
133
+
119
134
  # ==== Returns
120
135
  # The computed path to the layout rhtml.
121
136
  def layout_path
@@ -215,6 +230,12 @@ module SproutCore
215
230
  # layout: Path to the layout resource. This should be of the form
216
231
  @layout = opts[:layout] || 'sproutcore:lib/index.rhtml'
217
232
 
233
+ # autobuild?: Determines if you should autobuild...
234
+ @autobuild = opts[:autobuild]
235
+ @autobuild = true if @autobuild.nil?
236
+
237
+ @use_digest_tokens = opts[:use_digest_tokens] || (@build_mode == :production)
238
+
218
239
  reload!
219
240
  end
220
241