]>
git.wh0rd.org - tt-rss.git/blob - lib/dojo/data/ObjectStore.js.uncompressed.js
ff0ac99c70dcabee834e4efa3b8aab2e20b10c70
1 define ( "dojo/data/ObjectStore" , [ "../_base/lang" , "../Evented" , "../_base/declare" , "../_base/Deferred" , "../_base/array" ,
2 "../_base/connect" , "../regexp"
3 ], function ( lang
, Evented
, declare
, Deferred
, array
, connect
, regexp
){
6 // dojo/data/ObjectStore
8 function convertRegex ( character
){
9 return character
== '*' ? '.*' : character
== '?' ? '.' : character
;
11 return declare ( "dojo.data.ObjectStore" , [ Evented
],{
13 // A Dojo Data implementation that wraps Dojo object stores for backwards
17 constructor : function ( options
){
19 // The configuration information to pass into the data store.
21 // - options.objectStore:
23 // The object store to use as the source provider for this data store
25 this . _dirtyObjects
= [];
26 if ( options
. labelAttribute
){
27 // accept the old labelAttribute to make it easier to switch from old data stores
28 options
. labelProperty
= options
. labelAttribute
;
30 lang
. mixin ( this , options
);
32 labelProperty
: "label" ,
34 getValue : function ( /*Object*/ item
, /*String*/ property
, /*value?*/ defaultValue
){
36 // Gets the value of an item's 'property'
38 // The item to get the value from
40 // property to look up value for
44 return typeof item
. get === "function" ? item
. get ( property
) :
46 item
[ property
] : defaultValue
;
48 getValues : function ( item
, property
){
50 // Gets the value of an item's 'property' and returns
51 // it. If this value is an array it is just returned,
52 // if not, the value is added to an array and that is returned.
55 // property to look up value for
57 var val
= this . getValue ( item
, property
);
58 return val
instanceof Array
? val
: val
=== undefined ? [] : [ val
];
61 getAttributes : function ( item
){
63 // Gets the available attributes of an item's 'property' and returns
69 if ( item
. hasOwnProperty ( i
) && !( i
. charAt ( 0 ) == '_' && i
. charAt ( 1 ) == '_' )){
76 hasAttribute : function ( item
, attribute
){
78 // Checks to see if item has attribute
82 // The attribute to check
83 return attribute
in item
;
86 containsValue : function ( item
, attribute
, value
){
88 // Checks to see if 'item' has 'value' at 'attribute'
92 // The attribute to check
94 // The value to look for
95 return array
. indexOf ( this . getValues ( item
, attribute
), value
) > - 1 ;
99 isItem : function ( item
){
101 // Checks to see if the argument is an item
105 // we have no way of determining if it belongs, we just have object returned from
107 return ( typeof item
== 'object' ) && item
&& !( item
instanceof Date
);
110 isItemLoaded : function ( item
){
112 // Checks to see if the item is loaded.
116 return item
&& typeof item
. load
!== "function" ;
119 loadItem : function ( args
){
121 // Loads an item and calls the callback handler. Note, that this will call the callback
122 // handler even if the item is loaded. Consequently, you can use loadItem to ensure
123 // that an item is loaded is situations when the item may or may not be loaded yet.
124 // If you access a value directly through property access, you can use this to load
125 // a lazy value as well (doesn't need to be an item).
127 // See dojo/data/api/Read.fetch()
129 // | store.loadItem({
130 // | item: item, // this item may or may not be loaded
131 // | onItem: function(item){
132 // | // do something with the item
137 if ( typeof args
. item
. load
=== "function" ){
138 Deferred
. when ( args
. item
. load (), function ( result
){
139 item
= result
; // in synchronous mode this can allow loadItem to return the value
140 var func
= result
instanceof Error
? args
. onError
: args
. onItem
;
142 func
. call ( args
. scope
, result
);
145 } else if ( args
. onItem
){
146 // even if it is already loaded, we will use call the callback, this makes it easier to
147 // use when it is not known if the item is loaded (you can always safely call loadItem).
148 args
. onItem
. call ( args
. scope
, args
. item
);
152 close : function ( request
){
154 // See dojo/data/api/Read.close()
155 return request
&& request
. abort
&& request
. abort ();
157 fetch : function ( args
){
159 // See dojo/data/api/Read.fetch()
161 args
= lang
. delegate ( args
, args
&& args
. queryOptions
);
163 var scope
= args
. scope
|| self
;
164 var query
= args
. query
;
165 if ( typeof query
== "object" ){ // can be null, but that is ignore by for-in
166 query
= lang
. delegate ( query
); // don't modify the original
168 // find any strings and convert them to regular expressions for wildcard support
169 var required
= query
[ i
];
170 if ( typeof required
== "string" ){
171 query
[ i
] = RegExp ( "^" + regexp
. escapeString ( required
, "*? \\ " ). replace ( /\\.|\*|\?/g , convertRegex
) + "$" , args
. ignoreCase
? "mi" : "m" );
172 query
[ i
]. toString
= ( function ( original
){
181 var results
= this . objectStore
. query ( query
, args
);
182 Deferred
. when ( results
. total
, function ( totalCount
){
183 Deferred
. when ( results
, function ( results
){
185 args
. onBegin
. call ( scope
, totalCount
|| results
. length
, args
);
188 for ( var i
= 0 ; i
< results
. length
; i
++){
189 args
. onItem
. call ( scope
, results
[ i
], args
);
193 args
. onComplete
. call ( scope
, args
. onItem
? null : results
, args
);
198 function errorHandler ( error
){
200 args
. onError
. call ( scope
, error
, args
);
203 args
. abort = function (){
211 // if we were previously observing, cancel the last time to avoid multiple notifications. Just the best we can do for the impedance mismatch between APIs
212 this . observing
. cancel ();
214 this . observing
= results
. observe ( function ( object
, removedFrom
, insertedInto
){
215 if ( array
. indexOf ( self
. _dirtyObjects
, object
) == - 1 ){
216 if ( removedFrom
== - 1 ){
219 else if ( insertedInto
== - 1 ){
220 self
. onDelete ( object
);
223 for ( var i
in object
){
224 if ( i
!= self
. objectStore
. idProperty
){
225 self
. onSet ( object
, i
, null , object
[ i
]);
232 this . onFetch ( results
);
236 getFeatures : function (){
238 // return the store feature set
241 "dojo.data.api.Read" : !! this . objectStore
. get ,
242 "dojo.data.api.Identity" : true ,
243 "dojo.data.api.Write" : !! this . objectStore
. put
,
244 "dojo.data.api.Notification" : true
248 getLabel : function ( /* dojo/data/api/Item */ item){
250 // See dojo/data/api/Read.getLabel()
251 if(this.isItem(item)){
252 return this.getValue(item,this.labelProperty); //String
254 return undefined; //undefined
257 getLabelAttributes: function(/* dojo/data/api/Item */ item){
259 // See dojo/data/api/Read.getLabelAttributes()
260 return [this.labelProperty]; //array
263 //Identity API Support
266 getIdentity: function(item){
268 // returns the identity of the given item
269 // See dojo/data/api/Read.getIdentity()
270 return this.objectStore.getIdentity ? this.objectStore.getIdentity(item) : item[this.objectStore.idProperty || "id"];
273 getIdentityAttributes: function(item){
275 // returns the attributes which are used to make up the
276 // identity of an item. Basically returns this.objectStore.idProperty
277 // See dojo/data/api/Read.getIdentityAttributes()
279 return [this.objectStore.idProperty];
282 fetchItemByIdentity: function(args){
284 // fetch an item by its identity, by looking in our index of what we have loaded
286 Deferred.when(this.objectStore.get(args.identity),
289 args.onItem.call(args.scope, result);
292 args.onError.call(args.scope, error);
298 newItem: function(data, parentInfo){
300 // adds a new item to the store at the specified point.
301 // Takes two parameters, data, and options.
303 // The data to be added in as an item.
305 // See dojo/data/api/Write.newItem()
308 // get the previous value or any empty array
309 var values = this.getValue(parentInfo.parent,parentInfo.attribute,[]);
311 values = values.concat([data]);
312 data.__parent = values;
313 this.setValue(parentInfo.parent, parentInfo.attribute, values);
315 this._dirtyObjects.push({object:data, save: true});
319 deleteItem: function(item){
321 // deletes item and any references to that item from the store.
325 // If the desire is to delete only one reference, unsetAttribute or
326 // setValue is the way to go.
327 this.changing(item, true);
331 setValue: function(item, attribute, value){
333 // sets 'attribute' on 'item' to 'value'
334 // See dojo/data/api/Write.setValue()
336 var old = item[attribute];
338 item[attribute]=value;
339 this.onSet(item,attribute,old,value);
341 setValues: function(item, attribute, values){
343 // sets 'attribute' on 'item' to 'value' value
345 // See dojo/data/api/Write.setValues()
347 if(!lang.isArray(values)){
348 throw new Error("setValues expects to be passed an Array object as its value");
350 this.setValue(item,attribute,values);
353 unsetAttribute: function(item, attribute){
355 // unsets 'attribute' on 'item'
356 // See dojo/data/api/Write.unsetAttribute()
359 var old = item[attribute];
360 delete item[attribute];
361 this.onSet(item,attribute,old,undefined);
364 changing: function(object,_deleting){
366 // adds an object to the list of dirty objects. This object
367 // contains a reference to the object itself as well as a
368 // cloned and trimmed version of old object for use with
371 // Indicates that the given object is changing and should be marked as
372 // dirty for the next save
373 // _deleting: [private] Boolean
375 object.__isDirty = true;
376 //if an object is already in the list of dirty objects, don't add it again
377 //or it will overwrite the premodification data set.
378 for(var i=0; i<this._dirtyObjects.length; i++){
379 var dirty = this._dirtyObjects[i];
380 if(object==dirty.object){
382 // we are deleting, no object is an indicator of deletiong
383 dirty.object = false;
384 if(!this._saveNotNeeded){
391 var old = object instanceof Array ? [] : {};
393 if(object.hasOwnProperty(i)){
397 this._dirtyObjects.push({object: !_deleting && object, old: old, save: !this._saveNotNeeded});
400 save: function(kwArgs){
402 // Saves the dirty data using object store provider. See dojo/data/api/Write for API.
405 // This will cause the save to commit the dirty data for all
406 // ObjectStores as a single transaction.
408 // - kwArgs.revertOnError:
409 // This will cause the changes to be reverted if there is an
410 // error on the save. By default a revert is executed unless
411 // a value of false is provide for this parameter.
414 // Called when an error occurs in the commit
416 // - kwArgs.onComplete:
417 // Called when an the save/commit is completed
419 kwArgs = kwArgs || {};
420 var result, actions = [];
421 var savingObjects = [];
423 var dirtyObjects = this._dirtyObjects;
424 var left = dirtyObjects.length;// this is how many changes are remaining to be received from the server
426 connect.connect(kwArgs,"onError",function(){
427 if(kwArgs.revertOnError !== false){
428 var postCommitDirtyObjects = dirtyObjects;
429 dirtyObjects = savingObjects;
430 self.revert(); // revert if there was an error
431 self._dirtyObjects = postCommitDirtyObjects;
434 self._dirtyObjects = dirtyObjects.concat(savingObjects);
437 if(this.objectStore.transaction){
438 var transaction = this.objectStore.transaction();
440 for(var i = 0; i < dirtyObjects.length; i++){
441 var dirty = dirtyObjects[i];
442 var object = dirty.object;
444 delete object.__isDirty;
446 result = this.objectStore.put(object, {overwrite: !!old});
448 else if(typeof old != "undefined"){
449 result = this.objectStore.remove(this.getIdentity(old));
451 savingObjects.push(dirty);
452 dirtyObjects.splice(i--,1);
453 Deferred.when(result, function(value){
455 if(kwArgs.onComplete){
456 kwArgs.onComplete.call(kwArgs.scope, actions);
461 // on an error we want to revert, first we want to separate any changes that were made since the commit
462 left = -1; // first make sure that success isn't called
463 kwArgs.onError.call(kwArgs.scope, value);
468 transaction.commit();
471 kwArgs.onError.call(kwArgs.scope, value);
477 // returns any modified data to its original state prior to a save();
479 var dirtyObjects = this._dirtyObjects;
480 for(var i = dirtyObjects.length; i > 0;){
482 var dirty = dirtyObjects[i];
483 var object = dirty.object;
488 if(old.hasOwnProperty(j) && object[j] !== old[j]){
489 this.onSet(object, j, object[j], old[j]);
494 if(!old.hasOwnProperty(j)){
495 this.onSet(object, j, object[j]);
500 // was an addition, remove it
501 this.onDelete(object);
503 // was a deletion, we will add it back
506 delete (object || old).__isDirty;
507 dirtyObjects.splice(i, 1);
511 isDirty: function(item){
513 // returns true if the item is marked as dirty or true if there are any dirty items
517 return !!this._dirtyObjects.length;
519 return item.__isDirty;
522 // Notification Support
526 // See dojo/data/api/Notification.onSet()
530 // See dojo/data/api/Notification.onNew()
532 onDelete: function(){
534 // See dojo/data/api/Notification.onDelete()
536 // an extra to get result sets
537 onFetch: function(results){
539 // Called when a fetch occurs