sproutcore 1.9.1 → 1.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/VERSION.yml +1 -1
  2. data/lib/frameworks/sproutcore/CHANGELOG.md +212 -11
  3. data/lib/frameworks/sproutcore/frameworks/ajax/system/response.js +17 -17
  4. data/lib/frameworks/sproutcore/frameworks/ajax/tests/system/request.js +103 -86
  5. data/lib/frameworks/sproutcore/frameworks/bootstrap/system/browser.js +1 -1
  6. data/lib/frameworks/sproutcore/frameworks/bootstrap/tests/system/browser.js +33 -21
  7. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/platform.js +3 -1
  8. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/root_responder.js +7 -2
  9. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view.js +5 -2
  10. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/animation.js +7 -0
  11. data/lib/frameworks/sproutcore/frameworks/datastore/data_sources/data_source.js +1 -1
  12. data/lib/frameworks/sproutcore/frameworks/datastore/system/many_array.js +36 -30
  13. data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/many_array/core_methods.js +84 -42
  14. data/lib/frameworks/sproutcore/frameworks/desktop/{resources/debug → debug/resources}/a_sample_image.jpg +0 -0
  15. data/lib/frameworks/sproutcore/frameworks/desktop/{resources/debug → debug/resources}/iframe.html +1 -1
  16. data/lib/frameworks/sproutcore/frameworks/desktop/protocols/responder.js +68 -68
  17. data/lib/frameworks/sproutcore/frameworks/desktop/resources/list_item.css +1 -0
  18. data/lib/frameworks/sproutcore/frameworks/desktop/resources/menu_item_view.css +1 -0
  19. data/lib/frameworks/sproutcore/frameworks/desktop/resources/slider.css +6 -5
  20. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/segmented/methods.js +1 -1
  21. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/segmented/ui.js +1 -1
  22. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/web/ui.js +2 -0
  23. data/lib/frameworks/sproutcore/frameworks/desktop/views/segmented.js +45 -36
  24. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/{gestureable.js → gesturable.js} +0 -0
  25. data/lib/frameworks/sproutcore/frameworks/foundation/render_delegates/label.js +2 -4
  26. data/lib/frameworks/sproutcore/frameworks/foundation/resources/benchmark.css +1 -0
  27. data/lib/frameworks/sproutcore/frameworks/foundation/tests/views/label/ui.js +13 -0
  28. data/lib/frameworks/sproutcore/frameworks/foundation/views/inline_text_field.js +4 -33
  29. data/lib/frameworks/sproutcore/frameworks/foundation/views/label.js +31 -17
  30. data/lib/frameworks/sproutcore/frameworks/foundation/views/text_field.js +59 -1
  31. data/lib/frameworks/sproutcore/frameworks/runtime/core.js +2 -2
  32. data/lib/frameworks/sproutcore/themes/ace/resources/master-detail/master-detail.css +4 -0
  33. data/lib/gen/app/templates/apps/@target_name@/Buildfile +3 -1
  34. data/lib/gen/app/templates/apps/@target_name@/core.js +3 -3
  35. data/lib/gen/app/templates/apps/@target_name@/main.js +7 -5
  36. data/lib/gen/app/templates/apps/@target_name@/resources/main_page.css +20 -0
  37. data/lib/gen/app/templates/apps/@target_name@/resources/main_page.js +7 -7
  38. data/lib/gen/controller/templates/controllers/@filename@_controller.js +2 -2
  39. data/lib/gen/data-source/templates/data_sources/@filename@_data_source.js +21 -21
  40. data/lib/gen/design/templates/resources/@filename@.css +12 -0
  41. data/lib/gen/framework/templates/frameworks/@target_name@/core.js +3 -3
  42. data/lib/gen/framework/templates/frameworks/@target_name@/english.lproj/strings.js +1 -1
  43. data/lib/gen/language/templates/@filename@/strings.js +1 -1
  44. data/lib/gen/model/templates/models/@filename@_model.js +1 -1
  45. data/lib/gen/page/templates/pages/@target_name@/core.js +3 -3
  46. data/lib/gen/project/templates/@filename@/Buildfile +29 -5
  47. data/lib/gen/{responder → state}/Buildfile +3 -2
  48. data/lib/gen/state/README +1 -0
  49. data/lib/gen/state/USAGE +15 -0
  50. data/lib/gen/state/templates/states/@filename@_state.js +24 -0
  51. data/lib/gen/state/templates/tests/states/@filename@_test.js +12 -0
  52. data/lib/gen/statechart_app/templates/apps/@target_name@/Buildfile +3 -1
  53. data/lib/gen/statechart_app/templates/apps/@target_name@/core.js +3 -3
  54. data/lib/gen/statechart_app/templates/apps/@target_name@/main.js +3 -10
  55. data/lib/gen/statechart_app/templates/apps/@target_name@/resources/main_page.css +20 -0
  56. data/lib/gen/statechart_app/templates/apps/@target_name@/resources/main_page.js +7 -7
  57. data/lib/gen/statechart_app/templates/apps/@target_name@/statechart.js +3 -3
  58. data/lib/sproutcore/models/generator.rb +1 -1
  59. data/spec/lib/models/generator/snake_case_spec.rb +26 -0
  60. data/sproutcore.gemspec +16 -16
  61. data/vendor/chance/lib/chance/instance/spriting.rb +32 -30
  62. metadata +30 -26
  63. data/lib/frameworks/sproutcore/frameworks/desktop/resources/debug/apple-logo1.jpeg +0 -0
  64. data/lib/gen/responder/README +0 -1
  65. data/lib/gen/responder/USAGE +0 -15
  66. data/lib/gen/responder/templates/states/@filename@_state.js +0 -36
@@ -193,7 +193,7 @@ SC.detectBrowser = function(userAgent, language) {
193
193
  // Normalize the os name.
194
194
  if (isIOSDevice) { osAndVersion[1] = SC.OS.ios; }
195
195
  else if (osAndVersion[1] === 'mac os x' || osAndVersion[1] === 'mac os') { osAndVersion[1] = SC.OS.mac; }
196
- else if (osAndVersion[1] === 'windows nt') { osAndVersion[1] = SC.OS.windows; }
196
+ else if (osAndVersion[1] === 'windows nt') { osAndVersion[1] = SC.OS.win; }
197
197
 
198
198
  // Normalize the os version.
199
199
  osAndVersion[2] = osAndVersion[2] ? osAndVersion[2].replace(/_/g, '.') : '0';
@@ -41,7 +41,7 @@ var userAgents = {
41
41
  device: SC.DEVICE.desktop,
42
42
  name: SC.BROWSER.chrome,
43
43
  version: '16.0.912.36',
44
- os: SC.OS.windows,
44
+ os: SC.OS.win,
45
45
  osVersion: '6.1',
46
46
  engine: SC.ENGINE.webkit,
47
47
  engineVersion: '535.7'
@@ -62,7 +62,7 @@ var userAgents = {
62
62
  device: SC.DEVICE.desktop,
63
63
  name: SC.BROWSER.opera,
64
64
  version: '8.65',
65
- os: SC.OS.windows,
65
+ os: SC.OS.win,
66
66
  osVersion: '5.1',
67
67
  engine: SC.ENGINE.opera,
68
68
  engineVersion: '8.65'
@@ -71,7 +71,7 @@ var userAgents = {
71
71
  device: SC.DEVICE.desktop,
72
72
  name: SC.BROWSER.opera,
73
73
  version: '10.62',
74
- os: SC.OS.windows,
74
+ os: SC.OS.win,
75
75
  osVersion: '6.1',
76
76
  engine: SC.ENGINE.presto,
77
77
  engineVersion: '2.6.30'
@@ -80,7 +80,7 @@ var userAgents = {
80
80
  device: SC.DEVICE.desktop,
81
81
  name: SC.BROWSER.opera,
82
82
  version: '11.51',
83
- os: SC.OS.windows,
83
+ os: SC.OS.win,
84
84
  osVersion: '5.1',
85
85
  engine: SC.ENGINE.presto,
86
86
  engineVersion: '2.9.168'
@@ -89,7 +89,7 @@ var userAgents = {
89
89
  device: SC.DEVICE.desktop,
90
90
  name: SC.BROWSER.opera,
91
91
  version: '12.00',
92
- os: SC.OS.windows,
92
+ os: SC.OS.win,
93
93
  osVersion: '6.1',
94
94
  engine: SC.ENGINE.presto,
95
95
  engineVersion: '2.9.181'
@@ -119,7 +119,7 @@ var userAgents = {
119
119
  device: SC.DEVICE.desktop,
120
120
  name: SC.BROWSER.ie,
121
121
  version: '7.0',
122
- os: SC.OS.windows,
122
+ os: SC.OS.win,
123
123
  osVersion: '6.0',
124
124
  engine: SC.ENGINE.trident,
125
125
  engineVersion: '7.0'
@@ -128,7 +128,7 @@ var userAgents = {
128
128
  device: SC.DEVICE.desktop,
129
129
  name: SC.BROWSER.ie,
130
130
  version: '8.0',
131
- os: SC.OS.windows,
131
+ os: SC.OS.win,
132
132
  osVersion: '6.0',
133
133
  engine: SC.ENGINE.trident,
134
134
  engineVersion: '4.0'
@@ -137,7 +137,7 @@ var userAgents = {
137
137
  device: SC.DEVICE.desktop,
138
138
  name: SC.BROWSER.ie,
139
139
  version: '9.0',
140
- os: SC.OS.windows,
140
+ os: SC.OS.win,
141
141
  osVersion: '9.0',
142
142
  engine: SC.ENGINE.trident,
143
143
  engineVersion: '9.0'
@@ -146,7 +146,7 @@ var userAgents = {
146
146
  device: SC.DEVICE.desktop,
147
147
  name: SC.BROWSER.ie,
148
148
  version: '10.0',
149
- os: SC.OS.windows,
149
+ os: SC.OS.win,
150
150
  osVersion: '6.1',
151
151
  engine: SC.ENGINE.trident,
152
152
  engineVersion: '6.0'
@@ -155,7 +155,7 @@ var userAgents = {
155
155
  device: SC.DEVICE.desktop,
156
156
  name: SC.BROWSER.ie,
157
157
  version: '10.6',
158
- os: SC.OS.windows,
158
+ os: SC.OS.win,
159
159
  osVersion: '6.1',
160
160
  engine: SC.ENGINE.trident,
161
161
  engineVersion: '5.0'
@@ -164,11 +164,20 @@ var userAgents = {
164
164
  device: SC.DEVICE.desktop,
165
165
  name: SC.BROWSER.ie,
166
166
  version: '10.0',
167
- os: SC.OS.windows,
167
+ os: SC.OS.win,
168
168
  osVersion: '6.2',
169
169
  engine: SC.ENGINE.trident,
170
170
  engineVersion: '6.0'
171
171
  },
172
+ "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)": {
173
+ device: SC.DEVICE.desktop,
174
+ name: SC.BROWSER.ie,
175
+ version: '9.0',
176
+ os: SC.OS.win,
177
+ osVersion: '6.1',
178
+ engine: SC.ENGINE.trident,
179
+ engineVersion: '5.0'
180
+ },
172
181
 
173
182
  // MOZILLA
174
183
 
@@ -176,7 +185,7 @@ var userAgents = {
176
185
  device: SC.DEVICE.desktop,
177
186
  name: SC.ENGINE.gecko,
178
187
  version: '0',
179
- os: SC.OS.windows,
188
+ os: SC.OS.win,
180
189
  osVersion: '6.1',
181
190
  engine: SC.ENGINE.gecko,
182
191
  engineVersion: '2.0b4'
@@ -215,7 +224,7 @@ var userAgents = {
215
224
  device: SC.DEVICE.desktop,
216
225
  name: SC.BROWSER.firefox,
217
226
  version: '3.5.13',
218
- os: SC.OS.windows,
227
+ os: SC.OS.win,
219
228
  osVersion: '6.1',
220
229
  engine: SC.ENGINE.gecko,
221
230
  engineVersion: '1.9.1.13'
@@ -224,7 +233,7 @@ var userAgents = {
224
233
  device: SC.DEVICE.desktop,
225
234
  name: SC.BROWSER.firefox,
226
235
  version: '3.6.10',
227
- os: SC.OS.windows,
236
+ os: SC.OS.win,
228
237
  osVersion: '6.1',
229
238
  engine: SC.ENGINE.gecko,
230
239
  engineVersion: '1.9.2.10'
@@ -233,7 +242,7 @@ var userAgents = {
233
242
  device: SC.DEVICE.desktop,
234
243
  name: SC.BROWSER.firefox,
235
244
  version: '4.0b7pre',
236
- os: SC.OS.windows,
245
+ os: SC.OS.win,
237
246
  osVersion: '6.1',
238
247
  engine: SC.ENGINE.gecko,
239
248
  engineVersion: '2.0b7pre'
@@ -242,7 +251,7 @@ var userAgents = {
242
251
  device: SC.DEVICE.desktop,
243
252
  name: SC.BROWSER.firefox,
244
253
  version: '6.0',
245
- os: SC.OS.windows,
254
+ os: SC.OS.win,
246
255
  osVersion: '6.1',
247
256
  engine: SC.ENGINE.gecko,
248
257
  engineVersion: '6.0'
@@ -299,7 +308,7 @@ var userAgents = {
299
308
  device: SC.DEVICE.desktop,
300
309
  name: SC.BROWSER.safari,
301
310
  version: '4.1',
302
- os: SC.OS.windows,
311
+ os: SC.OS.win,
303
312
  osVersion: '5.0',
304
313
  engine: SC.ENGINE.webkit,
305
314
  engineVersion: '533.16'
@@ -308,7 +317,7 @@ var userAgents = {
308
317
  device: SC.DEVICE.desktop,
309
318
  name: SC.BROWSER.safari,
310
319
  version: '5.0.1',
311
- os: SC.OS.windows,
320
+ os: SC.OS.win,
312
321
  osVersion: '5.2',
313
322
  engine: SC.ENGINE.webkit,
314
323
  engineVersion: '533.17.8'
@@ -317,7 +326,7 @@ var userAgents = {
317
326
  device: SC.DEVICE.desktop,
318
327
  name: SC.BROWSER.safari,
319
328
  version: '5.0.2',
320
- os: SC.OS.windows,
329
+ os: SC.OS.win,
321
330
  osVersion: '6.1',
322
331
  engine: SC.ENGINE.webkit,
323
332
  engineVersion: '533.18.1'
@@ -326,7 +335,7 @@ var userAgents = {
326
335
  device: SC.DEVICE.desktop,
327
336
  name: SC.BROWSER.safari,
328
337
  version: '5.0.3',
329
- os: SC.OS.windows,
338
+ os: SC.OS.win,
330
339
  osVersion: '6.0',
331
340
  engine: SC.ENGINE.webkit,
332
341
  engineVersion: '533.19.4'
@@ -335,7 +344,7 @@ var userAgents = {
335
344
  device: SC.DEVICE.desktop,
336
345
  name: SC.BROWSER.safari,
337
346
  version: '5.0.4',
338
- os: SC.OS.windows,
347
+ os: SC.OS.win,
339
348
  osVersion: '6.1',
340
349
  engine: SC.ENGINE.webkit,
341
350
  engineVersion: '533.20.25'
@@ -485,6 +494,9 @@ for (var userAgent in userAgents) {
485
494
  browser = SC.detectBrowser(userAgent);
486
495
  expected = userAgents[userAgent];
487
496
  for (var key in expected) {
497
+ if ( !browser.hasOwnProperty(key) || browser[key] == null) {
498
+ ok(false,"Property %@ not set, expected %@".fmt(key, expected[key]));
499
+ }
488
500
  if (browser[key] && typeof browser[key] !== "function") {
489
501
  equals(browser[key], expected[key], "'" + key + "' should be '" + expected[key] + "'");
490
502
  }
@@ -44,6 +44,8 @@ SC.platform = SC.Object.create({
44
44
  - Android is assumed to support touch, but incorrectly reports that it does not.
45
45
  - See: https://github.com/Modernizr/Modernizr/issues/84 for a discussion on detecting
46
46
  touch capability.
47
+ - See: https://github.com/highslide-software/highcharts.com/issues/1331 for a discussion
48
+ about why we need to check if ontouchstart is null in addition to check if it's defined
47
49
  */
48
50
  /**
49
51
  YES if the current device supports touch events, NO otherwise.
@@ -53,7 +55,7 @@ SC.platform = SC.Object.create({
53
55
 
54
56
  @property {Boolean}
55
57
  */
56
- touch: 'ontouchstart' in window || SC.browser.name === SC.BROWSER.android,
58
+ touch: !SC.none(window.ontouchstart) || SC.browser.name === SC.BROWSER.android,
57
59
 
58
60
  /**
59
61
  YES if the current browser supports bounce on scroll.
@@ -2017,8 +2017,13 @@ SC.RootResponder = SC.Object.extend(
2017
2017
  drag: function() { return false; },
2018
2018
 
2019
2019
  contextmenu: function(evt) {
2020
- var view = this.targetViewForEvent(evt) ;
2021
- return this.sendEvent('contextMenu', evt, view);
2020
+ var view = this.targetViewForEvent(evt);
2021
+
2022
+ // Determine if any views took responsibility for the event.
2023
+ view = this.sendEvent('contextMenu', evt, view);
2024
+ ret = view ? evt.hasCustomEventHandling : YES;
2025
+
2026
+ return ret;
2022
2027
  },
2023
2028
 
2024
2029
  // ..........................................................
@@ -1466,10 +1466,13 @@ SC.CoreView.reopen(
1466
1466
  Used to block the contextMenu per view.
1467
1467
 
1468
1468
  @param evt {SC.Event} the contextmenu event
1469
- @returns YES if the contextmenu can show up
1469
+ @returns YES if the contextmenu will be allowed to show up
1470
1470
  */
1471
1471
  contextMenu: function(evt) {
1472
- if (this.get('isContextMenuEnabled')) { return YES; }
1472
+ if (this.get('isContextMenuEnabled')) {
1473
+ evt.allowDefault();
1474
+ return YES;
1475
+ }
1473
1476
  }
1474
1477
 
1475
1478
  });
@@ -66,6 +66,13 @@ SC.View.reopen(
66
66
  }
67
67
 
68
68
  if (callback) { options.callback = callback; }
69
+ else if (options.callback) {
70
+ //@if(debug)
71
+ // Provide a little developer support if they are doing something that should be considered wrong.
72
+ SC.warn("Developer Warning: The callback method should be given as an argument not as part of the options object. Future versions of SproutCore will no longer support having the callback in the options hash. See discussion at: https://github.com/sproutcore/sproutcore/issues/861.");
73
+ //@endif
74
+ callback = options.callback;
75
+ }
69
76
 
70
77
  // In the case of zero duration, just adjust and call the callback.
71
78
  if (options.duration === 0) {
@@ -194,7 +194,7 @@ SC.MIXED_STATE = '__MIXED__';
194
194
 
195
195
  * `dataSourceDidFetchQuery(query)` — the data source must call this when
196
196
  it has completed fetching any related data for the query. This returns the
197
- query results (record array) status into a `READY` state.
197
+ query results (i.e. the record array) status into a `READY` state.
198
198
  * `dataSourceDidErrorQuery(query, error)` — the data source should call
199
199
  this if it encounters an error in executing the query. This puts the query
200
200
  results into an `ERROR` state.
@@ -8,13 +8,13 @@
8
8
  /**
9
9
  @class
10
10
 
11
- A `ManyArray` is used to map an array of record ids back to their
11
+ A `ManyArray` is used to map an array of record ids back to their
12
12
  record objects which will be materialized from the owner store on demand.
13
-
14
- Whenever you create a `toMany()` relationship, the value returned from the
13
+
14
+ Whenever you create a `toMany()` relationship, the value returned from the
15
15
  property will be an instance of `ManyArray`. You can generally customize the
16
16
  behavior of ManyArray by passing settings to the `toMany()` helper.
17
-
17
+
18
18
  @extends SC.Enumerable
19
19
  @extends SC.Array
20
20
  @since SproutCore 1.0
@@ -53,7 +53,7 @@ SC.ManyArray = SC.Object.extend(SC.Enumerable, SC.Array,
53
53
 
54
54
  /**
55
55
  The `ManyAttribute` that created this array.
56
-
56
+
57
57
  @default null
58
58
  @type SC.ManyAttribute
59
59
  */
@@ -71,7 +71,7 @@ SC.ManyArray = SC.Object.extend(SC.Enumerable, SC.Array,
71
71
  }.property('record').cacheable(),
72
72
 
73
73
  /**
74
- The `storeKey` for the parent record of this many array. Editing this
74
+ The `storeKey` for the parent record of this many array. Editing this
75
75
  array will place the parent record into a `READY_DIRTY` state.
76
76
 
77
77
  @type Number
@@ -83,7 +83,7 @@ SC.ManyArray = SC.Object.extend(SC.Enumerable, SC.Array,
83
83
 
84
84
 
85
85
  /**
86
- Returns the `storeId`s in read-only mode. Avoids modifying the record
86
+ Returns the `storeId`s in read-only mode. Avoids modifying the record
87
87
  unnecessarily.
88
88
 
89
89
  @type SC.Array
@@ -95,9 +95,9 @@ SC.ManyArray = SC.Object.extend(SC.Enumerable, SC.Array,
95
95
 
96
96
 
97
97
  /**
98
- Returns an editable array of `storeId`s. Marks the owner records as
99
- modified.
100
-
98
+ Returns an editable array of `storeId`s. Marks the owner records as
99
+ modified.
100
+
101
101
  @type {SC.Array}
102
102
  @property
103
103
  */
@@ -176,7 +176,7 @@ SC.ManyArray = SC.Object.extend(SC.Enumerable, SC.Array,
176
176
 
177
177
  /** @private
178
178
  Returned length is a pass-through to the `storeIds` array.
179
-
179
+
180
180
  @type Number
181
181
  @property
182
182
  */
@@ -231,7 +231,7 @@ SC.ManyArray = SC.Object.extend(SC.Enumerable, SC.Array,
231
231
  len = recs ? (recs.get ? recs.get('length') : recs.length) : 0,
232
232
  record = this.get('record'),
233
233
  pname = this.get('propertyName'),
234
- i, keys, ids, toRemove, inverse, attr, inverseRecord;
234
+ i, ids, toRemove, inverse, attr, inverseRecord;
235
235
 
236
236
  // map to store keys
237
237
  ids = [] ;
@@ -326,11 +326,11 @@ SC.ManyArray = SC.Object.extend(SC.Enumerable, SC.Array,
326
326
  @param {SC.Record} inverseRecord the record this array is a part of
327
327
  @returns {SC.ManyArray} receiver
328
328
  */
329
- addInverseRecord: function(inverseRecord) {
330
-
329
+ addInverseRecord: function (inverseRecord) {
330
+ // Fast path!
331
331
  if (!inverseRecord) return this;
332
- var id = inverseRecord.get('id'),
333
- storeIds = this.get('editableStoreIds'),
332
+
333
+ var storeIds = this.get('editableStoreIds'),
334
334
  orderBy = this.get('orderBy'),
335
335
  len = storeIds.get('length'),
336
336
  idx, record;
@@ -348,26 +348,32 @@ SC.ManyArray = SC.Object.extend(SC.Enumerable, SC.Array,
348
348
  return this;
349
349
  },
350
350
 
351
- /** @private
352
- binary search to find insertion location
353
- */
354
- _findInsertionLocation: function(rec, min, max, orderBy) {
355
- var idx = min+Math.floor((max-min)/2),
351
+ /** @private binary search to find insertion location */
352
+ _findInsertionLocation: function (rec, min, max, orderBy) {
353
+ var idx = min + Math.floor((max - min) / 2),
356
354
  cur = this.objectAt(idx),
357
355
  order = this._compare(rec, cur, orderBy);
356
+
358
357
  if (order < 0) {
359
- if (idx===0) return idx;
360
- else return this._findInsertionLocation(rec, 0, idx, orderBy);
358
+ // The location is before the first index.
359
+ if (idx === 0) return idx;
360
+
361
+ // The location is in the lower subset.
362
+ else return this._findInsertionLocation(rec, 0, idx - 1, orderBy);
361
363
  } else if (order > 0) {
362
- if (idx >= max) return idx;
363
- else return this._findInsertionLocation(rec, idx, max, orderBy);
364
- } else return idx;
364
+ // The location is after the current index.
365
+ if (idx >= max) return idx + 1;
366
+
367
+ // The location is in the upper subset.
368
+ else return this._findInsertionLocation(rec, idx + 1, max, orderBy);
369
+ } else {
370
+ // The location is the current index.
371
+ return idx;
372
+ }
365
373
  },
366
374
 
367
- /** @private
368
- function to compare to objects
369
- */
370
- _compare: function(a, b, orderBy) {
375
+ /** @private function to compare two objects*/
376
+ _compare: function (a, b, orderBy) {
371
377
  var t = SC.typeOf(orderBy),
372
378
  ret, idx, len;
373
379
 
@@ -9,17 +9,17 @@
9
9
  var store, storeKey, storeId, rec, storeIds, recs, arrayRec;
10
10
  module("SC.ManyArray core methods", {
11
11
  setup: function() {
12
-
12
+
13
13
  // setup dummy app and store
14
14
  MyApp = SC.Object.create({
15
15
  store: SC.Store.create()
16
16
  });
17
-
17
+
18
18
  // setup a dummy model
19
19
  MyApp.Foo = SC.Record.extend({});
20
-
20
+
21
21
  SC.RunLoop.begin();
22
-
22
+
23
23
  // load some data
24
24
  storeIds = [1,2,3,4];
25
25
  MyApp.store.loadRecords(MyApp.Foo, [
@@ -29,25 +29,25 @@ module("SC.ManyArray core methods", {
29
29
  { guid: 4, firstName: "Johnny", lastName: "Cash", age: 17 },
30
30
  { guid: 50, firstName: "Holder", fooMany: storeIds }
31
31
  ]);
32
-
32
+
33
33
  storeKey = MyApp.store.storeKeyFor(MyApp.Foo, 1);
34
-
34
+
35
35
  // get record
36
36
  rec = MyApp.store.materializeRecord(storeKey);
37
37
  storeId = rec.get('id');
38
-
38
+
39
39
  // get many array.
40
40
  arrayRec = MyApp.store.materializeRecord(MyApp.store.storeKeyFor(MyApp.Foo, 50));
41
-
42
- recs = SC.ManyArray.create({
41
+
42
+ recs = SC.ManyArray.create({
43
43
  record: arrayRec,
44
- propertyName: "fooMany",
44
+ propertyName: "fooMany",
45
45
  recordType: MyApp.Foo,
46
46
  isEditable: YES
47
47
  });
48
- arrayRec.relationships = [recs];
48
+ arrayRec.relationships = [recs];
49
49
  },
50
-
50
+
51
51
  teardown: function() {
52
52
  SC.RunLoop.end();
53
53
  }
@@ -55,25 +55,25 @@ module("SC.ManyArray core methods", {
55
55
 
56
56
  // ..........................................................
57
57
  // LENGTH
58
- //
58
+ //
59
59
 
60
60
  test("should pass through length", function() {
61
- equals(recs.get('length'), storeIds.length, 'rec should pass through length');
61
+ equals(recs.get('length'), storeIds.length, 'rec should pass through length');
62
62
  });
63
63
 
64
64
  test("changing storeIds length should change length of rec array also", function() {
65
65
 
66
66
  var oldlen = recs.get('length');
67
-
67
+
68
68
  storeIds.pushObject(SC.Store.generateStoreKey()); // change length
69
-
69
+
70
70
  ok(storeIds.length > oldlen, 'precond - storeKeys.length should have changed');
71
- equals(recs.get('length'), storeIds.length, 'rec should pass through length');
71
+ equals(recs.get('length'), storeIds.length, 'rec should pass through length');
72
72
  });
73
73
 
74
74
  // ..........................................................
75
75
  // objectAt
76
- //
76
+ //
77
77
 
78
78
  test("should materialize record for object", function() {
79
79
  equals(storeIds[0], storeId, 'precond - storeIds[0] should be storeId');
@@ -86,12 +86,12 @@ test("reading past end of array length should return undefined", function() {
86
86
 
87
87
  test("modifying the underlying storeId should change the returned materialized record", function() {
88
88
  // read record once to make it materialized
89
- equals(recs.objectAt(0), rec, 'recs.objectAt(0) should materialize record');
90
-
89
+ equals(recs.objectAt(0), rec, 'recs.objectAt(0) should materialize record');
90
+
91
91
  // create a new record.
92
92
  var rec2 = MyApp.store.createRecord(MyApp.Foo, { guid: 5, firstName: "Fred" });
93
93
  var storeId2 = rec2.get('id');
94
-
94
+
95
95
  // add to beginning of storeKey array
96
96
  storeIds.unshiftObject(storeId2);
97
97
  equals(recs.get('length'), 5, 'should now have length of 5');
@@ -102,39 +102,39 @@ test("modifying the underlying storeId should change the returned materialized r
102
102
  test("reading a record not loaded in store should trigger retrieveRecord", function() {
103
103
  var callCount = 0;
104
104
 
105
- // patch up store to record a call and to make it look like data is not
105
+ // patch up store to record a call and to make it look like data is not
106
106
  // loaded.
107
-
107
+
108
108
  MyApp.store.removeDataHash(storeKey, SC.Record.EMPTY);
109
109
  MyApp.store.retrieveRecord = function() { callCount++; };
110
-
110
+
111
111
  var rec = recs.objectAt(0);
112
112
  equals(MyApp.store.readStatus(rec), SC.Record.EMPTY, 'precond - storeKey must not be loaded');
113
-
113
+
114
114
  equals(callCount, 1, 'store.retrieveRecord() should have been called');
115
115
  });
116
116
 
117
117
  // ..........................................................
118
118
  // replace()
119
- //
119
+ //
120
120
 
121
121
  test("adding a record to the ManyArray should pass through storeIds", function() {
122
122
 
123
123
  // read record once to make it materialized
124
- equals(recs.objectAt(0), rec, 'recs.objectAt(0) should materialize record');
125
-
124
+ equals(recs.objectAt(0), rec, 'recs.objectAt(0) should materialize record');
125
+
126
126
  // create a new record.
127
127
  var rec2 = MyApp.store.createRecord(MyApp.Foo, { guid: 5, firstName: "rec2" });
128
128
  var storeId2 = rec2.get('id');
129
-
129
+
130
130
  // add record to beginning of record array
131
131
  recs.unshiftObject(rec2);
132
-
132
+
133
133
  // verify record array
134
134
  equals(recs.get('length'), 5, 'should now have length of 2');
135
135
  equals(recs.objectAt(0), rec2, 'recs.objectAt(0) should return new record');
136
136
  equals(recs.objectAt(1), rec, 'recs.objectAt(1) should return old record');
137
-
137
+
138
138
  // verify storeKeys
139
139
  storeIds = arrayRec.readAttribute('fooMany'); // array might have changed
140
140
  equals(storeIds.objectAt(0), storeId2, 'storeKeys[0] should return new storeKey');
@@ -143,7 +143,7 @@ test("adding a record to the ManyArray should pass through storeIds", function()
143
143
 
144
144
  // ..........................................................
145
145
  // Property Observing
146
- //
146
+ //
147
147
 
148
148
  test("changing the underlying storeIds should notify observers of records", function() {
149
149
 
@@ -152,8 +152,8 @@ test("changing the underlying storeIds should notify observers of records", func
152
152
  cnt: 0,
153
153
  observer: function() { this.cnt++; }
154
154
  });
155
- recs.addObserver('[]', obj, obj.observer);
156
-
155
+ recs.addObserver('[]', obj, obj.observer);
156
+
157
157
  // now modify storeKeys
158
158
  storeIds.pushObject(5);
159
159
  equals(obj.cnt, 1, 'observer should have fired after changing storeKeys');
@@ -165,34 +165,34 @@ test("swapping storeIds array should change ManyArray and observers", function()
165
165
  var rec2 = MyApp.store.createRecord(MyApp.Foo, { guid: 5, firstName: "rec2" });
166
166
  var storeId2 = rec2.get('id');
167
167
  var storeIds2 = [storeId2];
168
-
168
+
169
169
  // setup observer
170
170
  var obj = SC.Object.create({
171
171
  cnt: 0,
172
172
  observer: function() { this.cnt++; }
173
173
  });
174
- recs.addObserver('[]', obj, obj.observer);
175
-
174
+ recs.addObserver('[]', obj, obj.observer);
175
+
176
176
  // read record once to make it materialized
177
- equals(recs.objectAt(0), rec, 'recs.objectAt(0) should materialize record');
178
-
177
+ equals(recs.objectAt(0), rec, 'recs.objectAt(0) should materialize record');
178
+
179
179
  // now swap storeKeys
180
180
  obj.cnt = 0 ;
181
181
  arrayRec.writeAttribute('fooMany', storeIds2);
182
182
 
183
183
  SC.RunLoop.end();
184
184
  SC.RunLoop.begin();
185
-
185
+
186
186
  // verify observer fired and record changed
187
187
  equals(obj.cnt, 1, 'observer should have fired after swap');
188
188
  equals(recs.objectAt(0), rec2, 'recs.objectAt(0) should return new rec');
189
-
189
+
190
190
  // modify storeKey2, make sure observer fires and content changes
191
191
  obj.cnt = 0;
192
192
  storeIds2.unshiftObject(storeId);
193
193
  equals(obj.cnt, 1, 'observer should have fired after edit');
194
194
  equals(recs.get('length'), 2, 'should reflect new length');
195
- equals(recs.objectAt(0), rec, 'recs.objectAt(0) should return pushed rec');
195
+ equals(recs.objectAt(0), rec, 'recs.objectAt(0) should return pushed rec');
196
196
 
197
197
  });
198
198
 
@@ -202,3 +202,45 @@ test("reduced properties", function() {
202
202
  equals(recs.get('@min(age)'), 7, 'min reducer should return the correct value');
203
203
  equals(recs.get('@average(age)'), (32+30+7+17)/4.0, 'average reducer should return the correct value');
204
204
  });
205
+
206
+ test("Test that _findInsertionLocation returns the correct location.", function () {
207
+ var location,
208
+ newRec,
209
+ sortByFirstName = function (a, b) {
210
+ if (a.get('firstName') == b.get('firstName')) return 0;
211
+ else if (a.get('firstName') < b.get('firstName')) return -1;
212
+ else return 1;
213
+ };
214
+
215
+ // Order the many array manually by firstName.
216
+ arrayRec.set('fooMany', [3,2,1,4]);
217
+ recs._storeIdsContentDidChange(0, 4, 4);
218
+
219
+ // Check the insertion location of a record that should appear first.
220
+ newRec = SC.Object.create({ guid: 5, firstName: "Adam", lastName: "Doe", age: 15 });
221
+ location = recs._findInsertionLocation(newRec, 0, recs.get('length') - 1, sortByFirstName);
222
+
223
+ equals(location, 0, "The insertion location should be");
224
+
225
+ // Check the insertion location of a record that should appear in the middle.
226
+ newRec = SC.Object.create({ guid: 5, firstName: "Farmer", lastName: "Doe", age: 95 });
227
+ location = recs._findInsertionLocation(newRec, 0, recs.get('length') - 1, sortByFirstName);
228
+
229
+ equals(location, 1, "The insertion location should be");
230
+
231
+ newRec = SC.Object.create({ guid: 5, firstName: "Jen", lastName: "Doe", age: 95 });
232
+ location = recs._findInsertionLocation(newRec, 0, recs.get('length') - 1, sortByFirstName);
233
+
234
+ equals(location, 2, "The insertion location should be");
235
+
236
+ newRec = SC.Object.create({ guid: 5, firstName: "Johnny", lastName: "Doe", age: 95 });
237
+ location = recs._findInsertionLocation(newRec, 0, recs.get('length') - 1, sortByFirstName);
238
+
239
+ equals(location, 3, "The insertion location should be");
240
+
241
+ // Check the insertion location of a record that should appear last.
242
+ newRec = SC.Object.create({ guid: 5, firstName: "Zues", lastName: "Doe", age: 95 });
243
+ location = recs._findInsertionLocation(newRec, 0, recs.get('length') - 1, sortByFirstName);
244
+
245
+ equals(location, 4, "The insertion location should be");
246
+ });