sproutcore 1.9.0 → 1.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 (24) hide show
  1. data/VERSION.yml +1 -1
  2. data/lib/frameworks/sproutcore/CHANGELOG.md +123 -1
  3. data/lib/frameworks/sproutcore/frameworks/ajax/system/request.js +13 -9
  4. data/lib/frameworks/sproutcore/frameworks/ajax/tests/system/request.js +68 -0
  5. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/locale.js +59 -60
  6. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/selection_set.js +6 -4
  7. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/system/locale.js +48 -29
  8. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/system/selection_set/remove.js +53 -17
  9. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/destroy.js +34 -0
  10. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/destroyLayer.js +47 -11
  11. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/viewDidResize.js +31 -36
  12. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view.js +10 -10
  13. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/layout.js +55 -23
  14. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/manipulation.js +1 -1
  15. data/lib/frameworks/sproutcore/frameworks/datastore/models/record.js +5 -1
  16. data/lib/frameworks/sproutcore/frameworks/datastore/system/nested_store.js +105 -105
  17. data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/commitChanges.js +131 -30
  18. data/lib/frameworks/sproutcore/frameworks/desktop/panes/menu.js +1 -0
  19. data/lib/frameworks/sproutcore/frameworks/foundation/system/utils/string_measurement.js +3 -2
  20. data/lib/frameworks/sproutcore/frameworks/runtime/core.js +2 -2
  21. data/lib/frameworks/sproutcore/frameworks/runtime/system/string.js +9 -5
  22. data/lib/sproutcore/helpers/minifier.rb +0 -2
  23. data/lib/sproutcore/rack/filesystem.rb +5 -5
  24. metadata +3 -2
@@ -457,7 +457,7 @@ SC.SelectionSet = SC.Object.extend(SC.Enumerable, SC.Freezable, SC.Copyable,
457
457
  @param {Object} source the source to limit
458
458
  @returns {SC.SelectionSet} receiver
459
459
  */
460
- constrain: function(source) {
460
+ constrain: function (source) {
461
461
  var set, len, max, objects;
462
462
 
463
463
  this.beginPropertyChanges();
@@ -477,9 +477,11 @@ SC.SelectionSet = SC.Object.extend(SC.Enumerable, SC.Freezable, SC.Copyable,
477
477
 
478
478
  // remove objects not in source
479
479
  if (objects = this._objects) {
480
- objects.forEach(function(cur) {
481
- if (source.indexOf(cur)<0) this.removeObject(cur);
482
- },this);
480
+ var i, cur;
481
+ for (i = objects.length - 1; i >= 0; i--) {
482
+ cur = objects[i];
483
+ if (source.indexOf(cur) < 0) this.removeObject(cur);
484
+ }
483
485
  }
484
486
 
485
487
  this.endPropertyChanges();
@@ -6,15 +6,15 @@
6
6
  // ==========================================================================
7
7
 
8
8
  var LocaleObject;
9
- module("object.SC.Locale()", {
9
+ module("object.SC.Locale()", {
10
10
  setup: function() {
11
-
11
+
12
12
  LocaleObject = SC.Locale.create({
13
13
  init: function(){
14
14
  sc_super();
15
15
  //hash of new languages
16
16
  var newLocales = { deflang: 'dl', empty: '' };
17
-
17
+
18
18
  //Added the new languages to the existing list of locales
19
19
  SC.Locale.addStrings(newLocales);
20
20
  }
@@ -22,12 +22,12 @@ module("object.SC.Locale()", {
22
22
 
23
23
  }
24
24
  });
25
-
25
+
26
26
  test("Locale.init() : Should return a flag if the language has been set during the locale initialization", function() {
27
27
  // As the locale is added during initialization the value of hasString is true
28
28
  equals(LocaleObject.hasStrings, true) ;
29
-
30
- //check the string values.
29
+
30
+ //check the string values.
31
31
  equals(LocaleObject.strings.deflang, 'dl') ;
32
32
  });
33
33
 
@@ -44,51 +44,70 @@ test("Locale.locWithDefault() : localized version of the string even if localize
44
44
  equals(LocaleObject.locWithDefault("empty", "Empty"), "");
45
45
  });
46
46
 
47
- test("Locale.addStrings() : Should be able to add the passed hash of strings to the locale's strings table", function() {
48
-
47
+ test("Locale.addStrings() : Should be able to add the passed hash of strings to the locale's strings table", function() {
48
+
49
49
  //Check for the new languages. This should be false as these are not added to the list of locales
50
50
  equals(false, SC.Locale.options().strings.chinese === 'zh' && SC.Locale.options().strings.dutch === 'nl') ;
51
-
51
+
52
52
  //hash of new languages
53
53
  var newLocales = { chinese: 'zh', czech: 'cs', dutch: 'nl'};
54
-
54
+
55
55
  //Added the new languages to the existing list of locales
56
56
  SC.Locale.addStrings(newLocales);
57
-
57
+
58
58
  //Result should be true as the new locales added to the list of default locales
59
59
  equals(true, SC.Locale.options().strings.chinese === 'zh' && SC.Locale.options().strings.dutch === 'nl') ;
60
60
  });
61
61
 
62
+ /**
63
+ There was a bug in SC.Locale where the `strings` object was cloned for each
64
+ subclass but then the original `strings` object was used to mix in new strings
65
+ and applied back. This meant that each subclass ended up sharing the
66
+ `strings` object and only one set of localizations (the last one) would exist.
67
+ */
68
+ test("Locale.extend.addStrings() : Subclasses should not share the strings object.", function() {
69
+ var strings;
70
+
71
+ strings = { 'hello': 'Hello' };
72
+ SC.Locale.locales.en.addStrings(strings);
73
+
74
+ strings = { 'hello': 'Bonjour' };
75
+ SC.Locale.locales.fr.addStrings(strings);
76
+
77
+ //Result should be true as the new locales added to the list of default locales
78
+ ok(SC.Locale.locales.en.prototype.strings !== SC.Locale.locales.fr.prototype.strings, "The strings object should not be shared between subclasses.");
79
+ });
80
+
62
81
  test("Locale.options() : Should provide the registered locales that have not been instantiated", function() {
63
-
82
+
64
83
  //hash of new languages
65
84
  var newLocales = { jamaican: 'ji', korean: 'ko'};
66
85
 
67
86
  //Added the new languages to the existing list of locales
68
87
  SC.Locale.addStrings(newLocales);
69
-
88
+
70
89
  //Options should return the list of registered locales, so checking if the returned object has strings.
71
90
  equals(SC.Locale.options().hasStrings, true) ;
72
-
91
+
73
92
  //Checking the strings with default locales.
74
93
  equals(true, SC.Locale.options().strings.jamaican === 'ji' && SC.Locale.options().strings.korean === 'ko') ;
75
94
  });
76
-
95
+
77
96
  test("Locale.normalizeLanguage() : Should provide the two character language code for the passed locale", function() {
78
97
  //If nothing is passed this will return the default code as 'en'
79
98
  equals(SC.Locale.normalizeLanguage(), 'en') ;
80
-
99
+
81
100
  //If the language is passed as 'English' this will return the code as 'en'
82
101
  equals(SC.Locale.normalizeLanguage('English'), 'en') ;
83
-
102
+
84
103
  //For any other code passed which is not in the default code it should return as it was passed
85
- equals(SC.Locale.normalizeLanguage('ab'), 'ab') ;
104
+ equals(SC.Locale.normalizeLanguage('ab'), 'ab') ;
86
105
  });
87
106
 
88
107
  test("Locale.toString() : Should return the current language set with the guid value", function() {
89
108
  // Creating the new locale by extending an existing Locale object
90
109
  SC.Locale.locales['mx'] = SC.Locale.extend({ _deprecatedLanguageCodes: ['mexican'] }) ;
91
-
110
+
92
111
  //Result should return the chinese object
93
112
  equals(SC.Locale.locales.mx.currentLocale.isObject, true) ;
94
113
 
@@ -96,23 +115,23 @@ test("Locale.toString() : Should return the current language set with the guid v
96
115
 
97
116
  test("Locale.createCurrentLocale() : Should create the Locale Object for the language selected", function() {
98
117
  //This will match the browser language with the SC language and create the object accordingly
99
- // This test will pass only for the default languages i.e en, fr, de, ja, es, it.
118
+ // This test will pass only for the default languages i.e en, fr, de, ja, es, it.
100
119
  equals(true, SC.Locale.createCurrentLocale().language === SC.browser.language) ;
101
-
120
+
102
121
  //Resetting the default browser language
103
122
  SC.browser.language='kn';
104
-
123
+
105
124
  //This is false as currentLocale will be created as 'en'
106
125
  equals(false, SC.Locale.createCurrentLocale().language===SC.browser.language) ;
107
- });
126
+ });
108
127
 
109
128
  test("Locale.localeClassFor() : Should find the locale class for the names language code or creates on based on its most likely parent", function() {
110
129
  // Local Class for any language other than default languages will be 'en'. Therefore this condition is false
111
130
  equals(false, SC.Locale.localeClassFor('nl').create().language === "nl") ;
112
-
131
+
113
132
  // This adds the new language with the parent language to the default list
114
133
  SC.Locale.locales['nl'] = SC.Locale.extend({ _deprecatedLanguageCodes: ['Dutch'] }) ;
115
-
134
+
116
135
  //This condition is true as the local class now exists for 'nl'
117
136
  equals(true, SC.Locale.localeClassFor('nl').create().language==="nl") ;
118
137
  });
@@ -122,19 +141,19 @@ test("Locale.define() : Should be able to define a particular type of locale", f
122
141
  longNames: 'Charles John Romonoski Gregory William'.split(' '),
123
142
  shortNames: ['C','A','Y','N']
124
143
  });
125
-
144
+
126
145
  //Result should return the new locale object
127
146
  equals(SC.Locale.locales.xy.isClass, true) ;
128
147
  });
129
148
 
130
149
  test("Locale.extend() : Should make sure important properties of Locale object are copied to a new class", function() {
131
150
  SC.Locale.locales['mn'] = SC.Locale.extend({ _deprecatedLanguageCodes: ['newlang'] }) ;
132
-
151
+
133
152
  //hash of new languages
134
153
  var testLocales = { test: 'te', newtest: 'nt'};
135
154
  //Added the new languages to the existing list of locales through the new locale object
136
155
  SC.Locale.locales.mn.addStrings(testLocales);
137
-
156
+
138
157
  //Result should be true as the new locales added to the list of default locales
139
158
  equals(SC.Locale.locales.mn.options().strings.test,'te') ;
140
- });
159
+ });
@@ -10,7 +10,7 @@ module("SC.SelectionSet#remove", {
10
10
  set = SC.SelectionSet.create();
11
11
  array = '0 1 2 3 4 5 6 7 8 9'.w();
12
12
  array2 = 'a b c d e f g h i k l m'.w();
13
-
13
+
14
14
  expected = SC.IndexSet.create(4,3);
15
15
  expected2 = SC.IndexSet.create(1);
16
16
  expected.source = array;
@@ -18,7 +18,7 @@ module("SC.SelectionSet#remove", {
18
18
  }
19
19
  });
20
20
 
21
- /*
21
+ /*
22
22
  validates that the selection set has the expected content. pass index sets
23
23
  with sources set appropriately. The order of the array is not important.
24
24
  */
@@ -26,12 +26,12 @@ function validate(set, expected, defaultSource) {
26
26
  var sources = set.get('sources'),
27
27
  len = expected.length,
28
28
  idx, cur, actual ;
29
-
30
- equals(sources.length, expected.length, 'should have same number of sources (actual sources: %@)'.fmt(sources));
31
-
29
+
30
+ equals(sources.length, expected.length, 'should have same number of sources (actual sources: %@)'.fmt(sources));
31
+
32
32
  for(idx=0;idx<len;idx++) {
33
33
  cur = expected[idx];
34
- if (!cur.source) cur.source =defaultSource;
34
+ if (!cur.source) cur.source =defaultSource;
35
35
  actual = set.indexSetForSource(cur.source, NO);
36
36
  ok(actual, 'should have indexSet for source: %@'.fmt(cur.source));
37
37
  equals(actual.source, cur.source, 'indexSet.source should match source');
@@ -40,7 +40,7 @@ function validate(set, expected, defaultSource) {
40
40
  }
41
41
  // ..........................................................
42
42
  // BASIC REMOVES
43
- //
43
+ //
44
44
 
45
45
  test("Removed indexes for single source", function() {
46
46
  set.add(array, 4, 3);
@@ -51,7 +51,7 @@ test("Removed indexes for single source", function() {
51
51
  });
52
52
 
53
53
  test("Removed multiple sources", function() {
54
-
54
+
55
55
  set.add(array, 4, 3).add(array2, 1);
56
56
  validate(set, [expected, expected2]); // precondition
57
57
 
@@ -71,13 +71,13 @@ test("Remove IndexSet with source", function() {
71
71
  });
72
72
 
73
73
  test("Adding another SelectionSet", function() {
74
-
74
+
75
75
  set.add(array, 4, 3).add(array2, 1);
76
76
  validate(set, [expected, expected2]); // precondition
77
77
 
78
78
  var x = SC.SelectionSet.create().add(array, 4,1).add(array2, 1);
79
79
  set.remove(x);
80
-
80
+
81
81
  expected.remove(4,1);
82
82
  validate(set, [SC.IndexSet.create(5,2)], array);
83
83
  });
@@ -85,27 +85,63 @@ test("Adding another SelectionSet", function() {
85
85
 
86
86
  // ..........................................................
87
87
  // SPECIAL CASES
88
- //
88
+ //
89
89
 
90
90
  test("removing index set should also remove individually added objects", function() {
91
91
  var objToRemove = array[3]; // item from one array...
92
92
  var objToNotRemove = array2[3]; // item from array we won't remove..
93
-
93
+
94
94
  // add both objects.
95
95
  set.addObject(objToRemove).addObject(objToNotRemove);
96
96
  set.add(array, 4, 3);
97
-
97
+
98
98
  ok(set.contains(objToRemove), 'set should contain objToRemove');
99
99
  ok(set.contains(objToNotRemove), 'set should contain objToNotRemove');
100
100
  equals(set.get('length'), 5, 'set.length should == two objects + index.length');
101
-
101
+
102
102
  // now remove from array set
103
- set.remove(array, 2, 4);
104
-
103
+ set.remove(array, 2, 4);
104
+
105
105
  SC.stopIt = NO ;
106
-
106
+
107
107
  ok(!set.contains(objToRemove), 'set should NOT contain objToRemove');
108
108
  ok(set.contains(objToNotRemove), 'set should contain objToNotRemove');
109
109
  equals(set.get('length'), 2, 'set.length should == 1 object + index.length');
110
110
  });
111
111
 
112
+
113
+ module("SC.SelectionSet#constrain", {
114
+ setup: function() {
115
+ set = SC.SelectionSet.create();
116
+ array = '0 1 2 3 4 5 6 7 8 9'.w();
117
+ array2 = 'a b c d e f g h i k l m'.w();
118
+
119
+ expected = SC.IndexSet.create(4,3);
120
+ expected2 = SC.IndexSet.create(1);
121
+ expected.source = array;
122
+ expected2.source = array2;
123
+ }
124
+ });
125
+
126
+ /**
127
+ After cleaning up a memory leak in SC.Set, it was discovered that the constrain
128
+ method of SC.SelectionSet doesn't work properly. It was naively using forEach
129
+ to iterate through the objects while mutating the array so that the last
130
+ object would never be constrained.
131
+
132
+ This test shows that you can constrain more than one object using the method.
133
+ */
134
+ test("Tests constrain helper method.", function () {
135
+ var objToRemove1 = 'a',
136
+ objToRemove2 = 'b';
137
+
138
+ set.add(array, 4, 3);
139
+ set.addObject(objToRemove1);
140
+ set.addObject(objToRemove2);
141
+ ok(set.contains(objToRemove1), 'Set should contain objToRemove1');
142
+ ok(set.contains(objToRemove2), 'Set should contain objToRemove2');
143
+ set.constrain(array);
144
+ ok(!set.contains(objToRemove1), 'Set should not contain objToRemove1');
145
+ ok(!set.contains(objToRemove2), 'Set should not contain objToRemove2');
146
+ });
147
+
@@ -0,0 +1,34 @@
1
+ // ==========================================================================
2
+ // Project: SproutCore - JavaScript Application Framework
3
+ // Copyright: ©2006-2011 Apple Inc. and contributors.
4
+ // License: Licensed under MIT license (see license.js)
5
+ // ==========================================================================
6
+
7
+ /* global module test equals context ok same */
8
+
9
+ module("SC.View#destroy");
10
+
11
+ test('isDestroyed works.', function() {
12
+ var v = SC.View.create();
13
+ ok(!v.get('isDestroyed'), 'undestroyed view\'s isDestroyed property is false.');
14
+ v.destroy();
15
+ ok(v.get('isDestroyed'), 'destroyed view\'s isDestroyed property is true.');
16
+ });
17
+
18
+ test('childViews specified as classes are also destroyed.', function() {
19
+ var v = SC.View.create({ childViews: [ SC.View ] }),
20
+ v2 = v.childViews[0];
21
+ v.destroy();
22
+ ok(v2.get('isDestroyed'), 'destroying a parent also destroys a child, mwaha.');
23
+ ok(!v2.get('parentView'), 'destroying a parent removes the parentView reference from the child.');
24
+ ok(v2.get('owner') === null, 'destroying a parent removes the owner reference from the child.');
25
+ });
26
+
27
+ test('childViews specified as instances are also destroyed.', function() {
28
+ var v2 = SC.View.create(),
29
+ v = SC.View.create({ childViews: [v2] });
30
+ v.destroy();
31
+ ok(v2.get('isDestroyed'), 'destroying a parent also destroys a child, mwaha.');
32
+ ok(!v2.get('parentView'), 'destroying a parent removes the parentView reference from the child.');
33
+ ok(v2.get('owner') === null, 'destroying a parent removes the owner reference from the child.');
34
+ });
@@ -3,25 +3,25 @@
3
3
  // Copyright: ©2006-2011 Apple Inc. and contributors.
4
4
  // License: Licensed under MIT license (see license.js)
5
5
  // ==========================================================================
6
+ /*globals module, test, equals, context, ok, same, Q$ */
6
7
 
7
- /*global module test equals context ok same Q$ */
8
8
 
9
9
  module("SC.View#destroyLayer");
10
10
 
11
11
  test("it if has no layer, does nothing", function() {
12
12
  var callCount = 0;
13
- var view = SC.View.create({
13
+ var view = SC.View.create({
14
14
  willDestroyLayer: function() { callCount++; }
15
15
  });
16
16
  ok(!view.get('layer'), 'precond - does NOT have layer');
17
-
17
+
18
18
  view.destroyLayer();
19
19
  equals(callCount, 0, 'did not invoke callback');
20
20
  });
21
21
 
22
22
  test("if it has a layer, calls willDestroyLayer on receiver and child views then deletes the layer", function() {
23
23
  var callCount = 0;
24
-
24
+
25
25
  var view = SC.View.create({
26
26
  willDestroyLayer: function() { callCount++; },
27
27
  childViews: [SC.View.extend({
@@ -33,9 +33,9 @@ test("if it has a layer, calls willDestroyLayer on receiver and child views then
33
33
  });
34
34
  view.createLayer();
35
35
  ok(view.get('layer'), 'precond - view has layer');
36
-
36
+
37
37
  view.destroyLayer();
38
- equals(callCount, 2, 'invoked destroy layer');
38
+ equals(callCount, 2, 'invoked destroy layer');
39
39
  ok(!view.get('layer'), 'view no longer has layer');
40
40
  });
41
41
 
@@ -46,11 +46,11 @@ test("if it has a layer, calls willDestroyLayerMixin on receiver and child views
46
46
  var mixinA = {
47
47
  willDestroyLayerMixin: function() { callCount++; }
48
48
  };
49
-
49
+
50
50
  var mixinB = {
51
51
  willDestroyLayerMixin: function() { callCount++; }
52
52
  };
53
-
53
+
54
54
  var view = SC.View.create(mixinA, mixinB, {
55
55
  childViews: [SC.View.extend(mixinA, mixinB, {
56
56
  childViews: [SC.View.extend(mixinA)]
@@ -58,7 +58,7 @@ test("if it has a layer, calls willDestroyLayerMixin on receiver and child views
58
58
  });
59
59
  view.createLayer();
60
60
  view.destroyLayer();
61
- equals(callCount, 5, 'invoked willDestroyLayerMixin on all mixins');
61
+ equals(callCount, 5, 'invoked willDestroyLayerMixin on all mixins');
62
62
  });
63
63
 
64
64
  test("returns receiver", function() {
@@ -69,10 +69,10 @@ test("returns receiver", function() {
69
69
  test("removes layer from parentNode if in DOM", function() {
70
70
  var view = SC.View.create();
71
71
  var layer = view.createLayer().get('layer');
72
-
72
+
73
73
  ok(layer, 'precond - has layer');
74
74
  document.body.appendChild(layer); // add to document body
75
-
75
+
76
76
  view.destroyLayer();
77
77
 
78
78
  if(layer.parentNode) equals(layer.parentNode.nodeType, 11, 'layer no longer in parent node');
@@ -80,4 +80,40 @@ test("removes layer from parentNode if in DOM", function() {
80
80
  layer = null; // cleanup
81
81
  });
82
82
 
83
+ /**
84
+ There is a bug that if childView layers are rendered when the parentView's
85
+ layer is created, the `layer` property on the childView will not be
86
+ cached. What occurs is that if the childView is removed from the parent
87
+ view without ever having its `layer` requested, then when it comes time
88
+ to destroy the layer of the childView, it will get('layer'), which had a
89
+ bug that only returned a layer if the view has a parent view. However,
90
+ since the child was removed from the parent first and then destroyed, it
91
+ no longer has a parent view and would return undefined for its `layer`.
83
92
 
93
+ This left elements in the DOM.
94
+ */
95
+ test("Tests that if the childView's layer was never cached and the childView is removed, it should still destroy the childView's layer", function() {
96
+ var childView,
97
+ layerId,
98
+ pane,
99
+ view;
100
+
101
+ childView = SC.View.create({});
102
+
103
+ layerId = childView.get('layerId');
104
+
105
+ view = SC.View.create({
106
+ childViews: [childView]
107
+ });
108
+
109
+ pane = SC.Pane.create({
110
+ childViews: [view]
111
+ }).append();
112
+
113
+ ok(document.getElementById(layerId), 'child layer should be in the DOM');
114
+ ok(!childView._view_layer, 'child view should not have cached its layer');
115
+ view.removeChild(childView);
116
+ ok(document.getElementById(layerId), 'child layer should be in the DOM');
117
+ childView.destroy();
118
+ ok(!document.getElementById(layerId), 'child layer should not be in the DOM');
119
+ });