sproutcore 1.11.0.rc2 → 1.11.0.rc3
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.
- checksums.yaml +8 -8
- data/CHANGELOG +10 -0
- data/VERSION.yml +1 -1
- data/lib/frameworks/sproutcore/CHANGELOG.md +114 -1
- data/lib/frameworks/sproutcore/apps/showcase/views/views_item_view.js +1 -7
- data/lib/frameworks/sproutcore/apps/showcase/views/views_list_view.js +9 -9
- data/lib/frameworks/sproutcore/frameworks/ajax/system/request.js +167 -5
- data/lib/frameworks/sproutcore/frameworks/ajax/system/response.js +24 -8
- data/lib/frameworks/sproutcore/frameworks/core_foundation/child_view_layouts/stack_layout.js +737 -0
- data/lib/frameworks/sproutcore/frameworks/core_foundation/panes/layout.js +0 -6
- data/lib/frameworks/sproutcore/frameworks/core_foundation/panes/pane.js +11 -7
- data/lib/frameworks/sproutcore/frameworks/core_foundation/panes/pane_statechart.js +7 -11
- data/lib/frameworks/sproutcore/frameworks/core_foundation/protocols/child_view_layout_protocol.js +8 -3
- data/lib/frameworks/sproutcore/frameworks/core_foundation/protocols/observable_protocol.js +9 -6
- data/lib/frameworks/sproutcore/frameworks/{desktop/protocols/responder.js → core_foundation/protocols/responder_protocol.js} +83 -17
- data/lib/frameworks/sproutcore/frameworks/core_foundation/protocols/{sparse_array_delegate.js → sparse_array_delegate_protocol.js} +11 -7
- data/lib/frameworks/sproutcore/frameworks/core_foundation/protocols/view_transition_protocol.js +11 -6
- data/lib/frameworks/sproutcore/frameworks/core_foundation/system/color.js +2 -2
- data/lib/frameworks/sproutcore/frameworks/core_foundation/system/page.js +0 -22
- data/lib/frameworks/sproutcore/frameworks/core_foundation/system/root_responder.js +61 -56
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/main_pane.js +2 -2
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/pane/append_remove.js +3 -3
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/animation.js +63 -39
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/border_frame_test.js +28 -28
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/createLayer.js +10 -4
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layout.js +102 -1
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layoutDidChange.js +4 -4
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layoutStyle.js +103 -103
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/replaceAllChildren_test.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/view.js +77 -1
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/view_states_test.js +18 -17
- data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view.js +42 -49
- data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/animation.js +5 -6
- data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/enabled.js +16 -5
- data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/layout.js +241 -102
- data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/layout_style.js +1 -4
- data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/manipulation.js +0 -11
- data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/statechart.js +993 -610
- data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/theming.js +3 -2
- data/lib/frameworks/sproutcore/frameworks/datastore/data_sources/data_source.js +6 -11
- data/lib/frameworks/sproutcore/frameworks/datastore/system/nested_store.js +94 -27
- data/lib/frameworks/sproutcore/frameworks/datastore/system/query.js +133 -53
- data/lib/frameworks/sproutcore/frameworks/datastore/system/store.js +30 -35
- data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/record/writeAttribute.js +3 -2
- data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/nested_store/writeDataHash.js +73 -29
- data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/store/conflictedStoreKeys_test.js +156 -0
- data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/store/dataSourceCallbacks.js +61 -37
- data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/store/find.js +2 -2
- data/lib/frameworks/sproutcore/frameworks/datetime/frameworks/core/system/datetime.js +68 -39
- data/lib/frameworks/sproutcore/frameworks/designer/tests/coders/page.js +1 -2
- data/lib/frameworks/sproutcore/frameworks/desktop/panes/panel.js +8 -6
- data/lib/frameworks/sproutcore/frameworks/desktop/panes/picker.js +80 -14
- data/lib/frameworks/sproutcore/frameworks/desktop/panes/sheet.js +2 -2
- data/lib/frameworks/sproutcore/frameworks/desktop/protocols/{drag_data_source.js → drag_data_source_protocol.js} +16 -10
- data/lib/frameworks/sproutcore/frameworks/desktop/protocols/{drag_source.js → drag_source_protocol.js} +28 -26
- data/lib/frameworks/sproutcore/frameworks/desktop/protocols/{drop_target.js → drop_target_protocol.js} +73 -75
- data/lib/frameworks/sproutcore/frameworks/desktop/system/drag.js +4 -4
- data/lib/frameworks/sproutcore/frameworks/desktop/tests/panes/panel/ui.js +39 -23
- data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/collection/mouse.js +120 -97
- data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/list/rowSizeForContentIndex.js +26 -25
- data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/scroll/ui.js +3 -3
- data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/segmented/ui.js +5 -0
- data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/split/{dividers.js → dividers_test.js} +38 -38
- data/lib/frameworks/sproutcore/frameworks/desktop/views/collection.js +29 -14
- data/lib/frameworks/sproutcore/frameworks/desktop/views/menu_scroll.js +2 -1
- data/lib/frameworks/sproutcore/frameworks/desktop/views/scroll_view.js +13 -18
- data/lib/frameworks/sproutcore/frameworks/desktop/views/segmented.js +41 -35
- data/lib/frameworks/sproutcore/frameworks/desktop/views/slider.js +14 -14
- data/lib/frameworks/sproutcore/frameworks/desktop/views/split.js +41 -26
- data/lib/frameworks/sproutcore/frameworks/desktop/views/static_content.js +2 -12
- data/lib/frameworks/sproutcore/frameworks/foundation/gestures/tap.js +2 -2
- data/lib/frameworks/sproutcore/frameworks/foundation/mixins/auto_resize.js +14 -10
- data/lib/frameworks/sproutcore/frameworks/foundation/mixins/gesturable.js +104 -63
- data/lib/frameworks/sproutcore/frameworks/foundation/protocols/swap_transition_protocol.js +9 -4
- data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/auto_mixin_tests.js +1 -2
- data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/auto_resize_test.js +33 -33
- data/lib/frameworks/sproutcore/frameworks/foundation/tests/transitions/view_transitions_test.js +5 -5
- data/lib/frameworks/sproutcore/frameworks/foundation/tests/views/container/methods.js +0 -4
- data/lib/frameworks/sproutcore/frameworks/foundation/tests/views/container/transition_test.js +0 -4
- data/lib/frameworks/sproutcore/frameworks/foundation/tests/views/container/ui.js +0 -2
- data/lib/frameworks/sproutcore/frameworks/foundation/views/text_field.js +12 -8
- data/lib/frameworks/sproutcore/frameworks/media/resources/silence.mp3 +0 -0
- data/lib/frameworks/sproutcore/frameworks/media/tests/audio.js +69 -0
- data/lib/frameworks/sproutcore/frameworks/media/views/audio.js +1 -0
- data/lib/frameworks/sproutcore/frameworks/runtime/core.js +2 -2
- data/lib/frameworks/sproutcore/frameworks/runtime/mixins/observable.js +11 -4
- data/lib/frameworks/sproutcore/frameworks/runtime/protocols/mixin_protocol.js +150 -0
- data/lib/frameworks/sproutcore/frameworks/runtime/system/binding.js +447 -137
- data/lib/frameworks/sproutcore/frameworks/runtime/system/object.js +9 -15
- data/lib/frameworks/sproutcore/frameworks/runtime/system/set.js +19 -17
- data/lib/frameworks/sproutcore/frameworks/runtime/tests/system/binding.js +188 -16
- data/lib/frameworks/sproutcore/frameworks/statechart/system/state.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/template_view/panes/template.js +0 -3
- data/lib/frameworks/sproutcore/frameworks/template_view/tests/panes/template.js +0 -17
- data/lib/frameworks/sproutcore/frameworks/template_view/tests/views/template/collection.js +43 -26
- data/lib/frameworks/sproutcore/frameworks/template_view/views/bindable_span.js +9 -2
- data/lib/frameworks/sproutcore/themes/ace/resources/collection/normal/list.css +0 -1
- data/lib/frameworks/sproutcore/themes/ace/resources/scroll/scroll.css +3 -0
- data/sproutcore.gemspec +3 -3
- metadata +19 -17
- data/lib/frameworks/sproutcore/frameworks/core_foundation/child_view_layouts/horizontal_stack_layout.js +0 -465
- data/lib/frameworks/sproutcore/frameworks/core_foundation/child_view_layouts/vertical_stack_layout.js +0 -472
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/build.js +0 -87
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/build_children.js +0 -89
@@ -76,8 +76,9 @@ SC.View.reopen(
|
|
76
76
|
Also, because
|
77
77
|
*/
|
78
78
|
_sc_view_themeDidChange: function() {
|
79
|
-
|
80
|
-
this._lastTheme
|
79
|
+
var curTheme = this.get('theme');
|
80
|
+
if (this._lastTheme === curTheme) { return; }
|
81
|
+
this._lastTheme = curTheme;
|
81
82
|
|
82
83
|
// invalidate child view base themes, if present
|
83
84
|
var childViews = this.childViews, len = childViews.length, idx;
|
@@ -194,18 +194,15 @@ 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 (i.e. the record array) status into a `READY` state.
|
197
|
+
query results (i.e. the record array) status into a `READY` state. If the query is a 'remote'
|
198
|
+
type, the ordered array of store keys representing the results from the server must be passed
|
199
|
+
as a second argument.
|
198
200
|
* `dataSourceDidErrorQuery(query, error)` — the data source should call
|
199
201
|
this if it encounters an error in executing the query. This puts the query
|
200
202
|
results into an `ERROR` state.
|
201
203
|
* `dataSourceDidCancelQuery(query)` — the data source should call this
|
202
204
|
if loading the results is cancelled.
|
203
205
|
|
204
|
-
In addition to these callbacks, the method `loadQueryResults(query, storeKey)`
|
205
|
-
is used by data sources when handling remote queries. This method is similar to
|
206
|
-
`dataSourceDidFetchQuery()`, except that you also provide an array of storeKeys
|
207
|
-
(or a promise to provide store keys) that comprises the result set.
|
208
|
-
|
209
206
|
@extend SC.Object
|
210
207
|
@since SproutCore 1.0
|
211
208
|
*/
|
@@ -248,13 +245,11 @@ SC.DataSource = SC.Object.extend( /** @scope SC.DataSource.prototype */ {
|
|
248
245
|
server instead of from memory. Usually you will only need to use this
|
249
246
|
type of query when loading large amounts of data from the server.
|
250
247
|
|
251
|
-
Like
|
248
|
+
Like local queries, to fetch a remote query you will need to load any data
|
252
249
|
you need to fetch from the server and add the records to the store. Once
|
253
250
|
you are finished loading this data, however, you must also call
|
254
|
-
SC.Store#
|
255
|
-
represent the latest results from the server.
|
256
|
-
call datasSourceDidFetchQuery() so you don't need to call this method
|
257
|
-
yourself.
|
251
|
+
SC.Store#dataSourceDidFetchQuery() with the array of storeKeys that
|
252
|
+
represent the latest results from the server.
|
258
253
|
|
259
254
|
If you want to support incremental loading from the server for remote
|
260
255
|
queries, you can do so by passing a SC.SparseArray instance instead of
|
@@ -133,14 +133,52 @@ SC.NestedStore = SC.Store.extend(
|
|
133
133
|
commitChanges: function(force) {
|
134
134
|
if (this.get('hasChanges')) {
|
135
135
|
var pstore = this.get('parentStore');
|
136
|
-
pstore.commitChangesFromNestedStore(this, this.chainedChanges, force);
|
136
|
+
pstore.commitChangesFromNestedStore(this, this.get('chainedChanges'), force);
|
137
137
|
}
|
138
138
|
|
139
139
|
// clear out custom changes - even if there is nothing to commit.
|
140
140
|
this.reset();
|
141
|
-
return this
|
141
|
+
return this;
|
142
142
|
},
|
143
143
|
|
144
|
+
/**
|
145
|
+
An array of store keys for all conflicting records with the parent store. If there are no
|
146
|
+
conflicting records, this property will be null.
|
147
|
+
|
148
|
+
@readonly
|
149
|
+
@field
|
150
|
+
@type Array
|
151
|
+
@default null
|
152
|
+
*/
|
153
|
+
conflictedStoreKeys: function () {
|
154
|
+
var ret = null;
|
155
|
+
|
156
|
+
if (this.get('hasChanges')) {
|
157
|
+
var pstore = this.get('parentStore'),
|
158
|
+
locks = this.locks,
|
159
|
+
revisions = pstore.revisions;
|
160
|
+
|
161
|
+
if (locks && revisions) {
|
162
|
+
var changes = this.get('chainedChanges');
|
163
|
+
|
164
|
+
for (var i = 0, len = changes.length; i < len; i++) {
|
165
|
+
var storeKey = changes[i],
|
166
|
+
lock = locks[storeKey] || 1,
|
167
|
+
revision = revisions[storeKey] || 1;
|
168
|
+
|
169
|
+
// If the same revision for the item does not match the current revision, then someone has
|
170
|
+
// changed the data hash in this store and we have a conflict.
|
171
|
+
if (lock < revision) {
|
172
|
+
if (!ret) ret = [];
|
173
|
+
ret.push(storeKey);
|
174
|
+
}
|
175
|
+
}
|
176
|
+
}
|
177
|
+
}
|
178
|
+
|
179
|
+
return ret;
|
180
|
+
}.property('chainedChanges').cacheable(),
|
181
|
+
|
144
182
|
/**
|
145
183
|
Propagate this store's successful changes to its parent (if exists). At the end, it clears the
|
146
184
|
local, private status of the committed records therefore the method can be called several times
|
@@ -150,37 +188,41 @@ SC.NestedStore = SC.Store.extend(
|
|
150
188
|
@returns {SC.Store} receiver
|
151
189
|
*/
|
152
190
|
commitSuccessfulChanges: function(force) {
|
153
|
-
|
154
|
-
|
155
|
-
|
191
|
+
var chainedChanges = this.get('chainedChanges');
|
192
|
+
|
193
|
+
if (this.get('hasChanges') && chainedChanges) {
|
194
|
+
var dataHashes = this.dataHashes,
|
156
195
|
revisions = this.revisions,
|
157
196
|
statuses = this.statuses,
|
158
197
|
editables = this.editables,
|
159
198
|
locks = this.locks;
|
199
|
+
|
160
200
|
var successfulChanges = chainedChanges.filter( function(storeKey) {
|
161
201
|
var state = this.readStatus(storeKey);
|
162
202
|
|
163
|
-
return state===SC.Record.READY_CLEAN || state===SC.Record.DESTROYED_CLEAN;
|
203
|
+
return state === SC.Record.READY_CLEAN || state === SC.Record.DESTROYED_CLEAN;
|
164
204
|
}, this );
|
205
|
+
|
165
206
|
var pstore = this.get('parentStore');
|
166
207
|
|
167
208
|
pstore.commitChangesFromNestedStore(this, successfulChanges, force);
|
168
209
|
|
169
210
|
// remove the local status so these records that have been successfully committed on the server
|
170
211
|
// are no longer retrieved from this nested store but from the parent
|
171
|
-
successfulChanges.
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
if (revisions && revisions.hasOwnProperty(storeKey))
|
176
|
-
|
177
|
-
if (
|
178
|
-
if (
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
}, this );
|
212
|
+
for (var i = 0, len = successfulChanges.get('length'); i < len; i++) {
|
213
|
+
var storeKey = successfulChanges.objectAt(i);
|
214
|
+
|
215
|
+
if (dataHashes && dataHashes.hasOwnProperty(storeKey)) { delete dataHashes[storeKey]; }
|
216
|
+
if (revisions && revisions.hasOwnProperty(storeKey)) { delete revisions[storeKey]; }
|
217
|
+
if (editables) { delete editables[storeKey]; }
|
218
|
+
if (locks) { delete locks[storeKey]; }
|
219
|
+
if (statuses && statuses.hasOwnProperty(storeKey)) { delete statuses[storeKey]; }
|
220
|
+
|
221
|
+
chainedChanges.remove(storeKey);
|
222
|
+
}
|
183
223
|
|
224
|
+
// Indicate that chainedChanges has changed.
|
225
|
+
if (successfulChanges.length > 0) { this.notifyPropertyChange('chainedChanges'); }
|
184
226
|
}
|
185
227
|
|
186
228
|
return this;
|
@@ -234,7 +276,6 @@ SC.NestedStore = SC.Store.extend(
|
|
234
276
|
Resets a store's data hash contents to match its parent.
|
235
277
|
*/
|
236
278
|
reset: function() {
|
237
|
-
var nRecords, nr, sk;
|
238
279
|
// requires a pstore to reset
|
239
280
|
var parentStore = this.get('parentStore');
|
240
281
|
if (!parentStore) throw SC.Store.NO_PARENT_STORE_ERROR;
|
@@ -249,12 +290,11 @@ SC.NestedStore = SC.Store.extend(
|
|
249
290
|
this.parentRecords = parentStore.parentRecords ? SC.beget(parentStore.parentRecords) : {};
|
250
291
|
|
251
292
|
// also, reset private temporary objects
|
293
|
+
this.set('hasChanges', false);
|
252
294
|
this.chainedChanges = this.locks = this.editables = null;
|
253
295
|
this.changelog = null ;
|
254
296
|
|
255
297
|
// TODO: Notify record instances
|
256
|
-
|
257
|
-
this.set('hasChanges', NO);
|
258
298
|
},
|
259
299
|
|
260
300
|
/** @private
|
@@ -435,6 +475,9 @@ SC.NestedStore = SC.Store.extend(
|
|
435
475
|
if (!editables) editables = this.editables = [];
|
436
476
|
editables[storeKey] = 1 ; // use number for dense array support
|
437
477
|
|
478
|
+
// propagate the data to the child records
|
479
|
+
this._updateChildRecordHashes(storeKey, hash, status);
|
480
|
+
|
438
481
|
return this ;
|
439
482
|
},
|
440
483
|
|
@@ -465,18 +508,36 @@ SC.NestedStore = SC.Store.extend(
|
|
465
508
|
storeKey = storeKeys;
|
466
509
|
}
|
467
510
|
|
468
|
-
var changes = this.chainedChanges;
|
511
|
+
var changes = this.get('chainedChanges');
|
469
512
|
if (!changes) changes = this.chainedChanges = SC.Set.create();
|
470
513
|
|
471
|
-
|
514
|
+
var that = this,
|
515
|
+
didAddChainedChanges = false;
|
516
|
+
|
517
|
+
for (idx = 0; idx < len; idx++) {
|
472
518
|
if (isArray) storeKey = storeKeys[idx];
|
519
|
+
|
473
520
|
this._lock(storeKey);
|
474
521
|
this.revisions[storeKey] = rev;
|
475
|
-
|
522
|
+
|
523
|
+
if (!changes.contains(storeKey)) {
|
524
|
+
changes.add(storeKey);
|
525
|
+
didAddChainedChanges = true;
|
526
|
+
}
|
527
|
+
|
476
528
|
this._notifyRecordPropertyChange(storeKey, statusOnly, key);
|
529
|
+
|
530
|
+
// notify also the child records
|
531
|
+
this._propagateToChildren(storeKey, function(storeKey){
|
532
|
+
that.dataHashDidChange(storeKey, null, statusOnly, key);
|
533
|
+
});
|
477
534
|
}
|
478
535
|
|
479
536
|
this.setIfChanged('hasChanges', YES);
|
537
|
+
if (didAddChainedChanges) {
|
538
|
+
this.notifyPropertyChange('chainedChanges');
|
539
|
+
}
|
540
|
+
|
480
541
|
return this ;
|
481
542
|
},
|
482
543
|
|
@@ -492,7 +553,11 @@ SC.NestedStore = SC.Store.extend(
|
|
492
553
|
// save a lock for each store key if it does not have one already
|
493
554
|
// also add each storeKey to my own changes set.
|
494
555
|
var pstore = this.get('parentStore'), psRevisions = pstore.revisions, i;
|
495
|
-
var myLocks = this.locks,
|
556
|
+
var myLocks = this.locks,
|
557
|
+
myChanges = this.get('chainedChanges'),
|
558
|
+
len,
|
559
|
+
storeKey;
|
560
|
+
|
496
561
|
if (!myLocks) myLocks = this.locks = [];
|
497
562
|
if (!myChanges) myChanges = this.chainedChanges = SC.Set.create();
|
498
563
|
|
@@ -504,7 +569,9 @@ SC.NestedStore = SC.Store.extend(
|
|
504
569
|
}
|
505
570
|
|
506
571
|
// Finally, mark store as dirty if we have changes
|
507
|
-
this.setIfChanged('hasChanges', myChanges.get('length')>0);
|
572
|
+
this.setIfChanged('hasChanges', myChanges.get('length') > 0);
|
573
|
+
this.notifyPropertyChange('chainedChanges');
|
574
|
+
|
508
575
|
this.flush();
|
509
576
|
|
510
577
|
return this ;
|
@@ -533,7 +600,7 @@ SC.NestedStore = SC.Store.extend(
|
|
533
600
|
because that can disconnect us from upper and/or lower stores.
|
534
601
|
*/
|
535
602
|
retrieveRecords: function(recordTypes, ids, storeKeys, isRefresh, callbacks) {
|
536
|
-
var pstore = this.get('parentStore'), idx, storeKey,
|
603
|
+
var pstore = this.get('parentStore'), idx, storeKey,
|
537
604
|
len = (!storeKeys) ? ids.length : storeKeys.length,
|
538
605
|
K = SC.Record, status;
|
539
606
|
|
@@ -11,51 +11,114 @@ sc_require('models/record');
|
|
11
11
|
/**
|
12
12
|
@class
|
13
13
|
|
14
|
-
This class permits you to perform queries on your data store or a remote
|
15
|
-
|
14
|
+
This class permits you to perform queries on your data store or a remote data store. Here is a
|
15
|
+
simple example of a *local* (see below) query,
|
16
16
|
|
17
17
|
query = SC.Query.create({
|
18
|
-
conditions: "firstName = '
|
18
|
+
conditions: "firstName = 'Johnny' AND lastName = 'Cash'"
|
19
19
|
});
|
20
20
|
|
21
|
-
|
22
|
-
the query
|
21
|
+
This query, when used with the store, will return all records which have record attributes of
|
22
|
+
`firstName` equal to 'Johnny' and `lastName` equal to 'Cash'. To use the query with the store
|
23
|
+
simple pass it to `find` like so,
|
23
24
|
|
24
25
|
records = MyApp.store.find(query);
|
25
26
|
|
26
|
-
`records` will be
|
27
|
-
|
28
|
-
|
27
|
+
In this example, `records` will be an `SC.RecordArray` array containing all matching records. The
|
28
|
+
amazing feature of record arrays backed by *local* queries is that they update automatically
|
29
|
+
whenever the records in the store change. This means that once we've run the query once, any time
|
30
|
+
data is loaded or unloaded from the store, the results of the query (i.e. `records`) will
|
31
|
+
update automatically. This allows for truly powerful and dynamic uses, such as having a list of
|
32
|
+
filtered results that continually updates instantly as data pages in or out in the background.
|
33
|
+
|
34
|
+
To limit the query to a record type of `MyApp.MyModel`, you can specify the type as a property of
|
35
|
+
the query like this,
|
29
36
|
|
30
37
|
query = SC.Query.create({
|
31
|
-
conditions: "firstName = '
|
38
|
+
conditions: "firstName = 'Johnny' AND lastName = 'Cash'",
|
32
39
|
recordType: MyApp.MyModel
|
33
40
|
});
|
34
41
|
|
35
|
-
Calling `find()` like above will now return only records of type MyApp.MyModel.
|
36
|
-
|
37
|
-
|
38
|
-
is given.
|
42
|
+
Calling `find()` like above will now return only records of type MyApp.MyModel. It is recommended
|
43
|
+
to limit your query to a record type, since the query will have to look for matching records in
|
44
|
+
the whole store if no record type is given.
|
39
45
|
|
40
|
-
You can give an order, which the resulting records
|
46
|
+
You can also give an order for *local* queries, which the resulting records will use,
|
41
47
|
|
42
48
|
query = SC.Query.create({
|
43
|
-
conditions: "firstName = '
|
49
|
+
conditions: "firstName = 'Johnny' AND lastName = 'Cash'",
|
44
50
|
recordType: MyApp.MyModel,
|
45
51
|
orderBy: "lastName, year DESC"
|
46
52
|
});
|
47
53
|
|
48
|
-
The default order direction is ascending. You can change it to descending
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
54
|
+
The default order direction is ascending. You can change it to descending by writing `'DESC'`
|
55
|
+
behind the property name as was done in the example above. If no order is given, or records are
|
56
|
+
equal in respect to a given order, records will be ordered by their storeKeys which increment
|
57
|
+
depending on load order.
|
58
|
+
|
59
|
+
Note, you can check if a certain record matches the query by calling `query.contains(record)`.
|
60
|
+
|
61
|
+
## Local vs. Remote Queries
|
62
|
+
|
63
|
+
The default type for queries is 'local', but there is another type we can use called 'remote'.
|
64
|
+
The distinction between local and remote queries is a common tripping point for new SproutCore
|
65
|
+
developers, but hopefully the following description helps keep it clear. The terms local and
|
66
|
+
remote refer to the *location of the data where the query is performed and by whom*. A local query
|
67
|
+
will be run by the client against the store of data within the client, while a remote query will
|
68
|
+
be run on by the server against some remote store of data. This seems simple enough, but it can
|
69
|
+
lead to a few misconceptions.
|
70
|
+
|
71
|
+
The first misconception is that local queries don't ever result in a call to a server. This is
|
72
|
+
not the case; when a local query is first used with the store it will generally result in a call
|
73
|
+
to the server, but whether or not it does depends on your store's data source. Keep this in mind,
|
74
|
+
local queries *only run against the data loaded in the client's store*. If the client store is
|
75
|
+
empty, the results of the query will be empty even though there may be thousands of matching
|
76
|
+
records on a server somewhere. That's why when a query is first used, the store will look for a
|
77
|
+
data source that implements the `fetch(store, query)` method. Your data source can then decide
|
78
|
+
whether additional data should be loaded into the store first in order to better fulfill the
|
79
|
+
query.
|
80
|
+
|
81
|
+
This is entirely up to your client/server implementation, but a common use case is to have a
|
82
|
+
general query trigger the load of a large set of data and then any more specific queries will only
|
83
|
+
run against what was already loaded. For more details, @see SC.DataSource.prototype.fetch. So to
|
84
|
+
recap, local queries are passed to the data source fetch method the first time they are used so
|
85
|
+
that the data source has a chance to load additional data that the query may need.
|
86
|
+
|
87
|
+
Once we get past the first misconception; local queries are actually pretty easy to understand and
|
88
|
+
to work with. We run the queries, get the resulting record array and watch as the results almost
|
89
|
+
magically update as the data in the client changes. Local queries are the default type, and
|
90
|
+
typically we will use local queries almost exclusively in SproutCore apps. So why do we have a
|
91
|
+
remote type?
|
92
|
+
|
93
|
+
In a previous paragraph, we considered how a local query would be empty if the local store was
|
94
|
+
empty even though there may be thousands of matching records on a server. Well what if there were
|
95
|
+
millions of records on the server? When dealing with extremely large datasets, it's not feasible
|
96
|
+
to load all of the records into the client so that the client can run a query against them. This
|
97
|
+
is the role of the 'remote' type. Remote queries are not actually "run" by the client at all,
|
98
|
+
which is the next misconception; you cannot run a remote query.
|
99
|
+
|
100
|
+
This misconception is another way of saying that you can't set conditions or order on a remote
|
101
|
+
query. The 'remote' SC.Query type is simply a reflection of some database query that was run
|
102
|
+
against a data store somewhere outside of the client. For example, say we want to still find all
|
103
|
+
the records on the server with `firstName` equal to 'Johnny' and `lastName` equal to 'Cash', but
|
104
|
+
now there are millions of records. This type of query is best left to a MySQL or other database on
|
105
|
+
a server and thus the server will have exposed an API endpoint that will return the results of
|
106
|
+
such a search when passed some search terms.
|
107
|
+
|
108
|
+
Again, this is entirely up to your client/server configuration, but the way it is handled by the
|
109
|
+
data source is nearly identical to how local queries will be handled. In both situations, the
|
110
|
+
data source is passed the query the first time it is run and when the data source is done it calls
|
111
|
+
the same method on the store, `dataSourceDidFetchQuery`. In both situations too, any data that the
|
112
|
+
data source receives should be loaded into the client store using `loadRecords`. However, because
|
113
|
+
there may be lots of other data in the client store, the 'remote' query must also be told which
|
114
|
+
records pertain to it and in what order, which is done by passing the store keys of the new data
|
115
|
+
also to `dataSourceDidFetchQuery`.
|
116
|
+
|
117
|
+
So to recap, use 'local' queries to filter the data currently in the client store and use 'remote'
|
118
|
+
queries to represent results filtered by a remote server. Both may be used by a data source to
|
119
|
+
load data from a server.
|
120
|
+
|
121
|
+
## SproutCore Query Language
|
59
122
|
|
60
123
|
Features of the query language:
|
61
124
|
|
@@ -86,7 +149,7 @@ sc_require('models/record');
|
|
86
149
|
|
87
150
|
You cannot use both types of parameters in a single query!
|
88
151
|
|
89
|
-
Operators:
|
152
|
+
### Operators:
|
90
153
|
|
91
154
|
- `=`
|
92
155
|
- `!=`
|
@@ -107,7 +170,7 @@ sc_require('models/record');
|
|
107
170
|
of a Model class on its right side, only records of this
|
108
171
|
type will match)
|
109
172
|
|
110
|
-
Boolean Operators:
|
173
|
+
### Boolean Operators:
|
111
174
|
|
112
175
|
- `AND`
|
113
176
|
- `OR`
|
@@ -118,8 +181,7 @@ sc_require('models/record');
|
|
118
181
|
- `(` and `)`
|
119
182
|
|
120
183
|
|
121
|
-
Adding Your Own Query Handlers
|
122
|
-
---
|
184
|
+
## Adding Your Own Query Handlers
|
123
185
|
|
124
186
|
You can extend the query language with your own operators by calling:
|
125
187
|
|
@@ -135,7 +197,7 @@ sc_require('models/record');
|
|
135
197
|
@extends SC.Freezable
|
136
198
|
@since SproutCore 1.0
|
137
199
|
*/
|
138
|
-
|
200
|
+
// TODO: Rename local vs. remote to avoid confusion.
|
139
201
|
SC.Query = SC.Object.extend(SC.Copyable, SC.Freezable,
|
140
202
|
/** @scope SC.Query.prototype */ {
|
141
203
|
|
@@ -184,7 +246,7 @@ SC.Query = SC.Object.extend(SC.Copyable, SC.Freezable,
|
|
184
246
|
|
185
247
|
@type String | Function
|
186
248
|
*/
|
187
|
-
orderBy:
|
249
|
+
orderBy: null,
|
188
250
|
|
189
251
|
/**
|
190
252
|
The base record type or types for the query. This must be specified to
|
@@ -681,7 +743,7 @@ SC.Query = SC.Object.extend(SC.Copyable, SC.Freezable,
|
|
681
743
|
evaluate: function (r,w) {
|
682
744
|
var left = this.leftSide.evaluate(r,w);
|
683
745
|
var right = this.rightSide.evaluate(r,w);
|
684
|
-
return SC.compare(left, right)
|
746
|
+
return SC.compare(left, right) === -1; //left < right;
|
685
747
|
}
|
686
748
|
},
|
687
749
|
|
@@ -695,7 +757,7 @@ SC.Query = SC.Object.extend(SC.Copyable, SC.Freezable,
|
|
695
757
|
evaluate: function (r,w) {
|
696
758
|
var left = this.leftSide.evaluate(r,w);
|
697
759
|
var right = this.rightSide.evaluate(r,w);
|
698
|
-
return SC.compare(left, right)
|
760
|
+
return SC.compare(left, right) !== 1; //left <= right;
|
699
761
|
}
|
700
762
|
},
|
701
763
|
|
@@ -709,7 +771,7 @@ SC.Query = SC.Object.extend(SC.Copyable, SC.Freezable,
|
|
709
771
|
evaluate: function (r,w) {
|
710
772
|
var left = this.leftSide.evaluate(r,w);
|
711
773
|
var right = this.rightSide.evaluate(r,w);
|
712
|
-
return SC.compare(left, right)
|
774
|
+
return SC.compare(left, right) === 1; //left > right;
|
713
775
|
}
|
714
776
|
},
|
715
777
|
|
@@ -723,7 +785,7 @@ SC.Query = SC.Object.extend(SC.Copyable, SC.Freezable,
|
|
723
785
|
evaluate: function (r,w) {
|
724
786
|
var left = this.leftSide.evaluate(r,w);
|
725
787
|
var right = this.rightSide.evaluate(r,w);
|
726
|
-
return SC.compare(left, right)
|
788
|
+
return SC.compare(left, right) !== -1; //left >= right;
|
727
789
|
}
|
728
790
|
},
|
729
791
|
|
@@ -773,7 +835,7 @@ SC.Query = SC.Object.extend(SC.Copyable, SC.Freezable,
|
|
773
835
|
if (allType !== SC.T_ARRAY) all = all.toArray();
|
774
836
|
var found = false;
|
775
837
|
var i = 0;
|
776
|
-
while ( found===false && i<all.length ) {
|
838
|
+
while ( found === false && i < all.length ) {
|
777
839
|
if ( value == all[i] ) found = true;
|
778
840
|
i++;
|
779
841
|
}
|
@@ -901,14 +963,12 @@ SC.Query = SC.Object.extend(SC.Copyable, SC.Freezable,
|
|
901
963
|
c = null,
|
902
964
|
t = null,
|
903
965
|
token = null,
|
904
|
-
tokenType = null,
|
905
966
|
currentToken = null,
|
906
967
|
currentTokenType = null,
|
907
968
|
currentTokenValue = null,
|
908
969
|
currentDelimiter = null,
|
909
970
|
endOfString = false,
|
910
971
|
endOfToken = false,
|
911
|
-
belongsToToken = false,
|
912
972
|
skipThisCharacter = false,
|
913
973
|
rememberCount = {};
|
914
974
|
|
@@ -928,8 +988,8 @@ SC.Query = SC.Object.extend(SC.Copyable, SC.Freezable,
|
|
928
988
|
// reserved words
|
929
989
|
if ( !t.delimited ) {
|
930
990
|
for ( var anotherToken in grammar ) {
|
931
|
-
if (
|
932
|
-
|
991
|
+
if (grammar[anotherToken].reservedWord &&
|
992
|
+
anotherToken == tokenValue ) {
|
933
993
|
tokenType = anotherToken;
|
934
994
|
}
|
935
995
|
}
|
@@ -1085,8 +1145,8 @@ SC.Query = SC.Object.extend(SC.Copyable, SC.Freezable,
|
|
1085
1145
|
var p = position;
|
1086
1146
|
var tl = tokenLogic(p);
|
1087
1147
|
if ( !tl ) return false;
|
1088
|
-
if (side
|
1089
|
-
if (side
|
1148
|
+
if (side === 'left') return tl.leftType;
|
1149
|
+
if (side === 'right') return tl.rightType;
|
1090
1150
|
}
|
1091
1151
|
|
1092
1152
|
function evalType (position) {
|
@@ -1110,8 +1170,8 @@ SC.Query = SC.Object.extend(SC.Copyable, SC.Freezable,
|
|
1110
1170
|
function tokenIsMissingChilds (position) {
|
1111
1171
|
var p = position;
|
1112
1172
|
if ( p < 0 ) return true;
|
1113
|
-
return (expectedType('left',p) && !l[p].leftSide)
|
1114
|
-
|
1173
|
+
return (expectedType('left',p) && !l[p].leftSide) ||
|
1174
|
+
(expectedType('right',p) && !l[p].rightSide);
|
1115
1175
|
}
|
1116
1176
|
|
1117
1177
|
function typesAreMatching (parent, child) {
|
@@ -1161,15 +1221,15 @@ SC.Query = SC.Object.extend(SC.Copyable, SC.Freezable,
|
|
1161
1221
|
|
1162
1222
|
// step through the tokenList
|
1163
1223
|
|
1164
|
-
for (i=0; i < l.length; i++) {
|
1224
|
+
for (i = 0; i < l.length; i++) {
|
1165
1225
|
shouldCheckAgain = false;
|
1166
1226
|
|
1167
|
-
if ( l[i].tokenType
|
1227
|
+
if ( l[i].tokenType === 'UNKNOWN' ) {
|
1168
1228
|
error.push('found unknown token: '+l[i].tokenValue);
|
1169
1229
|
}
|
1170
1230
|
|
1171
|
-
if ( l[i].tokenType
|
1172
|
-
if ( l[i].tokenType
|
1231
|
+
if ( l[i].tokenType === 'OPEN_PAREN' ) openParenthesisStack.push(i);
|
1232
|
+
if ( l[i].tokenType === 'CLOSE_PAREN' ) removeParenthesesPair(i);
|
1173
1233
|
|
1174
1234
|
if ( preceedingTokenCanBeMadeChild(i) ) makeChild(i);
|
1175
1235
|
|
@@ -1183,7 +1243,7 @@ SC.Query = SC.Object.extend(SC.Copyable, SC.Freezable,
|
|
1183
1243
|
}
|
1184
1244
|
|
1185
1245
|
// error if tokenList l is not a single token now
|
1186
|
-
if (l.length
|
1246
|
+
if (l.length === 1) l = l[0];
|
1187
1247
|
else error.push('string did not resolve to a single tree');
|
1188
1248
|
|
1189
1249
|
// If we have errors, return an error object.
|
@@ -1193,7 +1253,7 @@ SC.Query = SC.Object.extend(SC.Copyable, SC.Freezable,
|
|
1193
1253
|
tree: l,
|
1194
1254
|
// Conform to SC.Error.
|
1195
1255
|
isError: YES,
|
1196
|
-
errorVal: function() { return this.error }
|
1256
|
+
errorVal: function() { return this.error; }
|
1197
1257
|
};
|
1198
1258
|
}
|
1199
1259
|
// Otherwise the token list is now a tree and can be returned.
|
@@ -1224,6 +1284,26 @@ SC.Query = SC.Object.extend(SC.Copyable, SC.Freezable,
|
|
1224
1284
|
return orderOp;
|
1225
1285
|
}
|
1226
1286
|
else {
|
1287
|
+
// @if(debug)
|
1288
|
+
// debug mode syntax checks.
|
1289
|
+
// first check is position of ASC or DESC
|
1290
|
+
var ASCpos = orderOp.indexOf("ASC");
|
1291
|
+
var DESCpos = orderOp.indexOf("DESC");
|
1292
|
+
if (ASCpos > -1 || DESCpos > -1) { // if they exist
|
1293
|
+
if (ASCpos > -1 && (ASCpos + 3) !== orderOp.length) {
|
1294
|
+
SC.warn("Developer Warning: You have an orderBy syntax error in a Query, %@: ASC should be in the last position.".fmt(orderOp));
|
1295
|
+
}
|
1296
|
+
if (DESCpos > -1 && (DESCpos + 4) !== orderOp.length) {
|
1297
|
+
SC.warn("Developer Warning: You have an orderBy syntax error in a Query, %@: DESC should be in the last position.".fmt(orderOp));
|
1298
|
+
}
|
1299
|
+
}
|
1300
|
+
|
1301
|
+
// check for improper separation chars
|
1302
|
+
if (orderOp.indexOf(":") > -1 || orderOp.indexOf(":") > -1) {
|
1303
|
+
SC.warn("Developer Warning: You have an orderBy syntax error in a Query, %@: Colons or semicolons should not be used as a separation character.".fmt(orderOp));
|
1304
|
+
}
|
1305
|
+
// @endif
|
1306
|
+
|
1227
1307
|
var o = orderOp.split(',');
|
1228
1308
|
for (var i=0; i < o.length; i++) {
|
1229
1309
|
var p = o[i];
|
@@ -1231,7 +1311,7 @@ SC.Query = SC.Object.extend(SC.Copyable, SC.Freezable,
|
|
1231
1311
|
p = p.replace(/\s+/,',');
|
1232
1312
|
p = p.split(',');
|
1233
1313
|
o[i] = {propertyName: p[0]};
|
1234
|
-
if (p[1] && p[1]
|
1314
|
+
if (p[1] && p[1] === 'DESC') o[i].descending = true;
|
1235
1315
|
}
|
1236
1316
|
|
1237
1317
|
return o;
|
@@ -1305,7 +1385,7 @@ SC.Query.mixin( /** @scope SC.Query */ {
|
|
1305
1385
|
orderStoreKeys: function(storeKeys, query, store) {
|
1306
1386
|
// apply the sort if there is one
|
1307
1387
|
if (storeKeys) {
|
1308
|
-
|
1388
|
+
storeKeys.sort(function(a, b) {
|
1309
1389
|
return SC.Query.compareStoreKeys(query, store, a, b);
|
1310
1390
|
});
|
1311
1391
|
}
|