]>
git.wh0rd.org - tt-rss.git/blob - lib/dojo/store/Observable.js.uncompressed.js
1 define ( "dojo/store/Observable" , [ "../_base/kernel" , "../_base/lang" , "../_base/Deferred" , "../_base/array"
2 ], function ( kernel
, lang
, Deferred
, array
) {
4 // dojo/store/Observable
8 var ds
= lang
. getObject ( "dojo.store" , true );
10 return ds
. Observable = function ( store
){
12 // The Observable store wrapper takes a store and sets an observe method on query()
13 // results that can be used to monitor results for changes.
16 // Observable wraps an existing store so that notifications can be made when a query
20 // Create a Memory store that returns an observable query, and then log some
21 // information about that query.
23 // | var store = dojo.store.Observable(new dojo.store.Memory({
25 // | {id: 1, name: "one", prime: false},
26 // | {id: 2, name: "two", even: true, prime: true},
27 // | {id: 3, name: "three", prime: true},
28 // | {id: 4, name: "four", even: true, prime: false},
29 // | {id: 5, name: "five", prime: true}
32 // | var changes = [], results = store.query({ prime: true });
33 // | var observer = results.observe(function(object, previousIndex, newIndex){
34 // | changes.push({previousIndex:previousIndex, newIndex:newIndex, object:object});
37 // See the Observable tests for more information.
39 var undef
, queryUpdaters
= [], revision
= 0 ;
40 // a Comet driven store could directly call notify to notify observers when data has
41 // changed on the backend
42 store
. notify = function ( object
, existingId
){
44 var updaters
= queryUpdaters
. slice ();
45 for ( var i
= 0 , l
= updaters
. length
; i
< l
; i
++){
46 updaters
[ i
]( object
, existingId
);
49 var originalQuery
= store
. query
;
50 store
. query = function ( query
, options
){
51 options
= options
|| {};
52 var results
= originalQuery
. apply ( this , arguments
);
53 if ( results
&& results
. forEach
){
54 var nonPagedOptions
= lang
. mixin ({}, options
);
55 delete nonPagedOptions
. start
;
56 delete nonPagedOptions
. count
;
58 var queryExecutor
= store
. queryEngine
&& store
. queryEngine ( query
, nonPagedOptions
);
59 var queryRevision
= revision
;
60 var listeners
= [], queryUpdater
;
61 results
. observe = function ( listener
, includeObjectUpdates
){
62 if ( listeners
. push ( listener
) == 1 ){
63 // first listener was added, create the query checker and updater
64 queryUpdaters
. push ( queryUpdater = function ( changed
, existingId
){
65 Deferred
. when ( results
, function ( resultsArray
){
66 var atEnd
= resultsArray
. length
!= options
. count
;
68 if (++ queryRevision
!= revision
){
69 throw new Error ( "Query is out of date, you must observe() the query prior to any data modifications" );
71 var removedObject
, removedFrom
= - 1 , insertedInto
= - 1 ;
72 if ( existingId
!== undef
){
74 for ( i
= 0 , l
= resultsArray
. length
; i
< l
; i
++){
75 var object
= resultsArray
[ i
];
76 if ( store
. getIdentity ( object
) == existingId
){
77 removedObject
= object
;
79 if ( queryExecutor
|| ! changed
){ // if it was changed and we don't have a queryExecutor, we shouldn't remove it because updated objects would be eliminated
80 resultsArray
. splice ( i
, 1 );
89 // if a matches function exists, use that (probably more efficient)
90 ( queryExecutor
. matches
? queryExecutor
. matches ( changed
) : queryExecutor ([ changed
]). length
)){
92 var firstInsertedInto
= removedFrom
> - 1 ?
93 removedFrom
: // put back in the original slot so it doesn't move unless it needs to (relying on a stable sort below)
95 resultsArray
. splice ( firstInsertedInto
, 0 , changed
); // add the new item
96 insertedInto
= array
. indexOf ( queryExecutor ( resultsArray
), changed
); // sort it
97 // we now need to push the chagne back into the original results array
98 resultsArray
. splice ( firstInsertedInto
, 1 ); // remove the inserted item from the previous index
100 if (( options
. start
&& insertedInto
== 0 ) ||
101 (! atEnd
&& insertedInto
== resultsArray
. length
)){
102 // if it is at the end of the page, assume it goes into the prev or next page
105 resultsArray
. splice ( insertedInto
, 0 , changed
); // and insert into the results array with the correct index
108 } else if ( changed
&& ! options
. start
){
109 // we don't have a queryEngine, so we can't provide any information
110 // about where it was inserted, but we can at least indicate a new object
111 insertedInto
= removedFrom
>= 0 ? removedFrom
: ( store
. defaultIndex
|| 0 );
113 if (( removedFrom
> - 1 || insertedInto
> - 1 ) &&
114 ( includeObjectUpdates
|| ! queryExecutor
|| ( removedFrom
!= insertedInto
))){
115 var copyListeners
= listeners
. slice ();
116 for ( i
= 0 ; listener
= copyListeners
[ i
]; i
++){
117 listener ( changed
|| removedObject
, removedFrom
, insertedInto
);
125 // remove this listener
126 var index
= array
. indexOf ( listeners
, listener
);
127 if ( index
> - 1 ){ // check to make sure we haven't already called cancel
128 listeners
. splice ( index
, 1 );
129 if (! listeners
. length
){
130 // no more listeners, remove the query updater too
131 queryUpdaters
. splice ( array
. indexOf ( queryUpdaters
, queryUpdater
), 1 );
141 function whenFinished ( method
, action
){
142 var original
= store
[ method
];
144 store
[ method
] = function ( value
){
146 // if one method calls another (like add() calling put()) we don't want two events
147 return original
. apply ( this , arguments
);
151 var results
= original
. apply ( this , arguments
);
152 Deferred
. when ( results
, function ( results
){
153 action (( typeof results
== "object" && results
) || value
);
162 // monitor for updates by listening to these methods
163 whenFinished ( "put" , function ( object
){
164 store
. notify ( object
, store
. getIdentity ( object
));
166 whenFinished ( "add" , function ( object
){
167 store
. notify ( object
);
169 whenFinished ( "remove" , function ( id
){
170 store
. notify ( undefined , id
);