sproutcore 1.9.0 → 1.9.1

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