sproutcore 0.9.14 → 0.9.15

Sign up to get free protection for your applications and to get access to all the features.
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