1
0
mirror of https://git.tt-rss.org/git/tt-rss.git synced 2025-12-16 04:25:57 +00:00

upgrade Dojo to 1.6.1

This commit is contained in:
Andrew Dolgov
2011-11-08 20:40:44 +04:00
parent 870a70e109
commit 81bea17aef
680 changed files with 51915 additions and 74107 deletions

152
lib/dojo/store/Cache.js Normal file
View File

@@ -0,0 +1,152 @@
/*
Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
Available via Academic Free License >= 2.1 OR the modified BSD license.
see: http://dojotoolkit.org/license for details
*/
if(!dojo._hasResource["dojo.store.Cache"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.store.Cache"] = true;
dojo.provide("dojo.store.Cache");
dojo.getObject("store", true, dojo);
/*=====
dojo.declare("dojo.store.__CacheArgs", null, {
constructor: function(){
// summary:
// These are additional options for how caching is handled.
// isLoaded: Function?
// This is a function that will be called for each item in a query response to determine
// if it is cacheable. If isLoaded returns true, the item will be cached, otherwise it
// will not be cached. If isLoaded is not provided, all items will be cached.
this.isLoaded = isLoaded;
}
});
=====*/
dojo.store.Cache = function(masterStore, cachingStore, /*dojo.store.__CacheArgs*/ options){
// summary:
// The Cache store wrapper takes a master store and a caching store,
// caches data from the master into the caching store for faster
// lookup. Normally one would use a memory store for the caching
// store and a server store like JsonRest for the master store.
// masterStore:
// This is the authoritative store, all uncached requests or non-safe requests will
// be made against this store.
// cachingStore:
// This is the caching store that will be used to store responses for quick access.
// Typically this should be a local store.
// options:
// These are additional options for how caching is handled.
options = options || {};
return dojo.delegate(masterStore, {
query: function(query, directives){
var results = masterStore.query(query, directives);
results.forEach(function(object){
if(!options.isLoaded || options.isLoaded(object)){
cachingStore.put(object);
}
});
return results;
},
// look for a queryEngine in either store
queryEngine: masterStore.queryEngine || cachingStore.queryEngine,
get: function(id, directives){
return dojo.when(cachingStore.get(id), function(result){
return result || dojo.when(masterStore.get(id, directives), function(result){
if(result){
cachingStore.put(result, {id: id});
}
return result;
});
});
},
add: function(object, directives){
return dojo.when(masterStore.add(object, directives), function(result){
// now put result in cache
return cachingStore.add(typeof result == "object" ? result : object, directives);
});
},
put: function(object, directives){
// first remove from the cache, so it is empty until we get a response from the master store
cachingStore.remove((directives && directives.id) || this.getIdentity(object));
return dojo.when(masterStore.put(object, directives), function(result){
// now put result in cache
return cachingStore.put(typeof result == "object" ? result : object, directives);
});
},
remove: function(id, directives){
return dojo.when(masterStore.remove(id, directives), function(result){
return cachingStore.remove(id, directives);
});
},
evict: function(id){
return cachingStore.remove(id);
}
});
};
/*=====
dojo.declare("dojo.store.Cache", null, {
// example:
// | var master = new dojo.store.Memory(data);
// | var cacher = new dojo.store.Memory();
// | var store = new dojo.store.Cache(master, cacher);
//
query: function(query, directives){
// summary:
// Query the underlying master store and cache any results.
// query: Object|String
// The object or string containing query information. Dependent on the query engine used.
// directives: dojo.store.util.SimpleQueryEngine.__queryOptions?
// An optional keyword arguments object with additional parameters describing the query.
// returns: dojo.store.util.QueryResults
// A QueryResults object that can be used to iterate over.
},
get: function(id, directives){
// summary:
// Get the object with the specific id.
// id: Number
// The identifier for the object in question.
// directives: dojo.store.__GetOptions?
// Any additional parameters needed to describe how the get should be performed.
// returns: dojo.store.util.QueryResults
// A QueryResults object.
},
add: function(object, directives){
// summary:
// Add the given object to the store.
// object: Object
// The object to add to the store.
// directives: dojo.store.__AddOptions?
// Any additional parameters needed to describe how the add should be performed.
// returns: Number
// The new id for the object.
},
put: function(object, directives){
// summary:
// Put the object into the store (similar to an HTTP PUT).
// object: Object
// The object to put to the store.
// directives: dojo.store.__PutOptions?
// Any additional parameters needed to describe how the put should be performed.
// returns: Number
// The new id for the object.
},
remove: function(id, directives){
// summary:
// Remove the object with the specific id.
// id: Number
// The identifier for the object in question.
// directives: dojo.store.__RemoveOptions?
// Any additional parameters needed to describe how the remove should be performed.
},
evict: function(id){
// summary:
// Remove the object with the given id from the underlying caching store.
// id: Number
// The identifier for the object in question.
}
});
=====*/
}

142
lib/dojo/store/DataStore.js Normal file
View File

@@ -0,0 +1,142 @@
/*
Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
Available via Academic Free License >= 2.1 OR the modified BSD license.
see: http://dojotoolkit.org/license for details
*/
if(!dojo._hasResource["dojo.store.DataStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.store.DataStore"] = true;
dojo.provide("dojo.store.DataStore");
dojo.require("dojo.store.util.QueryResults");
dojo.declare("dojo.store.DataStore", null, {
target: "",
constructor: function(options){
// summary:
// This is an adapter for using Dojo Data stores with an object store consumer.
// You can provide a Dojo data store and use this adapter to interact with it through
// the Dojo object store API
// options: Object?
// This provides any configuration information that will be mixed into the store,
// including a reference to the Dojo data store under the property "store".
dojo.mixin(this, options);
},
_objectConverter: function(callback){
var store = this.store;
return function(item){
var object = {};
var attributes = store.getAttributes(item);
for(var i = 0; i < attributes.length; i++){
object[attributes[i]] = store.getValue(item, attributes[i]);
}
return callback(object);
};
},
get: function(id, options){
// summary:
// Retrieves an object by it's identity. This will trigger a fetchItemByIdentity
// id: Object?
// The identity to use to lookup the object
var returnedObject, returnedError;
var deferred = new dojo.Deferred();
this.store.fetchItemByIdentity({
identity: id,
onItem: this._objectConverter(function(object){
deferred.resolve(returnedObject = object);
}),
onError: function(error){
deferred.reject(returnedError = error);
}
});
if(returnedObject){
// if it was returned synchronously
return returnedObject;
}
if(returnedError){
throw returnedError;
}
return deferred.promise;
},
put: function(object, options){
// summary:
// Stores an object by its identity.
// object: Object
// The object to store.
// options: Object?
// Additional metadata for storing the data. Includes a reference to an id
// that the object may be stored with (i.e. { id: "foo" }).
var id = options && typeof options.id != "undefined" || this.getIdentity(object);
var store = this.store;
if(typeof id == "undefined"){
store.newItem(object);
}else{
store.fetchItemByIdentity({
identity: id,
onItem: function(item){
if(item){
for(var i in object){
if(store.getValue(item, i) != object[i]){
store.setValue(item, i, object[i]);
}
}
}else{
store.newItem(object);
}
}
});
}
},
remove: function(id){
// summary:
// Deletes an object by its identity.
// id: Object
// The identity to use to delete the object
var store = this.store;
this.store.fetchItemByIdentity({
identity: id,
onItem: function(item){
store.deleteItem(item);
}
});
},
query: function(query, options){
// summary:
// Queries the store for objects.
// query: Object
// The query to use for retrieving objects from the store
// options: Object?
// Optional options object as used by the underlying dojo.data Store.
// returns: dojo.store.util.QueryResults
// A query results object that can be used to iterate over results.
var returnedObject, returnedError;
var deferred = new dojo.Deferred();
deferred.total = new dojo.Deferred();
var converter = this._objectConverter(function(object){return object;});
this.store.fetch(dojo.mixin({
query: query,
onBegin: function(count){
deferred.total.resolve(count);
},
onComplete: function(results){
deferred.resolve(dojo.map(results, converter));
},
onError: function(error){
deferred.reject(error);
}
}, options));
return dojo.store.util.QueryResults(deferred);
},
getIdentity: function(object){
// summary:
// Fetch the identity for the given object.
// object: Object
// The data object to get the identity from.
// returns: Number
// The id of the given object.
return object[this.idProperty || this.store.getIdentityAttributes()[0]];
}
});
}

146
lib/dojo/store/JsonRest.js Normal file
View File

@@ -0,0 +1,146 @@
/*
Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
Available via Academic Free License >= 2.1 OR the modified BSD license.
see: http://dojotoolkit.org/license for details
*/
if(!dojo._hasResource["dojo.store.JsonRest"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.store.JsonRest"] = true;
dojo.provide("dojo.store.JsonRest");
dojo.require("dojo.store.util.QueryResults");
dojo.declare("dojo.store.JsonRest", null, {
constructor: function(/*dojo.store.JsonRest*/ options){
// summary:
// This is a basic store for RESTful communicating with a server through JSON
// formatted data.
// options:
// This provides any configuration information that will be mixed into the store
dojo.mixin(this, options);
},
// target: String
// The target base URL to use for all requests to the server. This string will be
// prepended to the id to generate the URL (relative or absolute) for requests
// sent to the server
target: "",
// idProperty: String
// Indicates the property to use as the identity property. The values of this
// property should be unique.
idProperty: "id",
get: function(id, options){
// summary:
// Retrieves an object by its identity. This will trigger a GET request to the server using
// the url `this.target + id`.
// id: Number
// The identity to use to lookup the object
// returns: Object
// The object in the store that matches the given id.
var headers = options || {};
headers.Accept = "application/javascript, application/json";
return dojo.xhrGet({
url:this.target + id,
handleAs: "json",
headers: headers
});
},
getIdentity: function(object){
// summary:
// Returns an object's identity
// object: Object
// The object to get the identity from
// returns: Number
return object[this.idProperty];
},
put: function(object, options){
// summary:
// Stores an object. This will trigger a PUT request to the server
// if the object has an id, otherwise it will trigger a POST request.
// object: Object
// The object to store.
// options: dojo.store.api.Store.PutDirectives?
// Additional metadata for storing the data. Includes an "id"
// property if a specific id is to be used.
// returns: Number
options = options || {};
var id = ("id" in options) ? options.id : this.getIdentity(object);
var hasId = typeof id != "undefined";
return dojo.xhr(hasId && !options.incremental ? "PUT" : "POST", {
url: hasId ? this.target + id : this.target,
postData: dojo.toJson(object),
handleAs: "json",
headers:{
"Content-Type": "application/json",
"If-Match": options.overwrite === true ? "*" : null,
"If-None-Match": options.overwrite === false ? "*" : null
}
});
},
add: function(object, options){
// summary:
// Adds an object. This will trigger a PUT request to the server
// if the object has an id, otherwise it will trigger a POST request.
// object: Object
// The object to store.
// options: dojo.store.api.Store.PutDirectives?
// Additional metadata for storing the data. Includes an "id"
// property if a specific id is to be used.
options = options || {};
options.overwrite = false;
return this.put(object, options);
},
remove: function(id){
// summary:
// Deletes an object by its identity. This will trigger a DELETE request to the server.
// id: Number
// The identity to use to delete the object
return dojo.xhrDelete({
url:this.target + id
});
},
query: function(query, options){
// summary:
// Queries the store for objects. This will trigger a GET request to the server, with the
// query added as a query string.
// query: Object
// The query to use for retrieving objects from the store.
// options: dojo.store.api.Store.QueryOptions?
// The optional arguments to apply to the resultset.
// returns: dojo.store.api.Store.QueryResults
// The results of the query, extended with iterative methods.
var headers = {Accept: "application/javascript, application/json"};
options = options || {};
if(options.start >= 0 || options.count >= 0){
headers.Range = "items=" + (options.start || '0') + '-' +
(("count" in options && options.count != Infinity) ?
(options.count + (options.start || 0) - 1) : '');
}
if(dojo.isObject(query)){
query = dojo.objectToQuery(query);
query = query ? "?" + query: "";
}
if(options && options.sort){
query += (query ? "&" : "?") + "sort(";
for(var i = 0; i<options.sort.length; i++){
var sort = options.sort[i];
query += (i > 0 ? "," : "") + (sort.descending ? '-' : '+') + encodeURIComponent(sort.attribute);
}
query += ")";
}
var results = dojo.xhrGet({
url: this.target + (query || ""),
handleAs: "json",
headers: headers
});
results.total = results.then(function(){
var range = results.ioArgs.xhr.getResponseHeader("Content-Range");
return range && (range=range.match(/\/(.*)/)) && +range[1];
});
return dojo.store.util.QueryResults(results);
}
});
}

164
lib/dojo/store/Memory.js Normal file
View File

@@ -0,0 +1,164 @@
/*
Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
Available via Academic Free License >= 2.1 OR the modified BSD license.
see: http://dojotoolkit.org/license for details
*/
if(!dojo._hasResource["dojo.store.Memory"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.store.Memory"] = true;
dojo.provide("dojo.store.Memory");
dojo.require("dojo.store.util.QueryResults");
dojo.require("dojo.store.util.SimpleQueryEngine");
dojo.declare("dojo.store.Memory", null, {
// summary:
// This is a basic in-memory object store. It implements dojo.store.api.Store.
constructor: function(/*dojo.store.Memory*/ options){
// summary:
// Creates a memory object store.
// options:
// This provides any configuration information that will be mixed into the store.
// This should generally include the data property to provide the starting set of data.
this.index = {};
dojo.mixin(this, options);
this.setData(this.data || []);
},
// data: Array
// The array of all the objects in the memory store
data:null,
// idProperty: String
// Indicates the property to use as the identity property. The values of this
// property should be unique.
idProperty: "id",
// index: Object
// An index of data by id
index:null,
// queryEngine: Function
// Defines the query engine to use for querying the data store
queryEngine: dojo.store.util.SimpleQueryEngine,
get: function(id){
// summary:
// Retrieves an object by its identity
// id: Number
// The identity to use to lookup the object
// returns: Object
// The object in the store that matches the given id.
return this.index[id];
},
getIdentity: function(object){
// summary:
// Returns an object's identity
// object: Object
// The object to get the identity from
// returns: Number
return object[this.idProperty];
},
put: function(object, options){
// summary:
// Stores an object
// object: Object
// The object to store.
// options: dojo.store.api.Store.PutDirectives??
// Additional metadata for storing the data. Includes an "id"
// property if a specific id is to be used.
// returns: Number
var id = options && options.id || object[this.idProperty] || Math.random();
this.index[id] = object;
var data = this.data,
idProperty = this.idProperty;
for(var i = 0, l = data.length; i < l; i++){
if(data[i][idProperty] == id){
data[i] = object;
return id;
}
}
this.data.push(object);
return id;
},
add: function(object, options){
// summary:
// Creates an object, throws an error if the object already exists
// object: Object
// The object to store.
// options: dojo.store.api.Store.PutDirectives??
// Additional metadata for storing the data. Includes an "id"
// property if a specific id is to be used.
// returns: Number
if(this.index[options && options.id || object[this.idProperty]]){
throw new Error("Object already exists");
}
return this.put(object, options);
},
remove: function(id){
// summary:
// Deletes an object by its identity
// id: Number
// The identity to use to delete the object
delete this.index[id];
var data = this.data,
idProperty = this.idProperty;
for(var i = 0, l = data.length; i < l; i++){
if(data[i][idProperty] == id){
data.splice(i, 1);
return;
}
}
},
query: function(query, options){
// summary:
// Queries the store for objects.
// query: Object
// The query to use for retrieving objects from the store.
// options: dojo.store.api.Store.QueryOptions?
// The optional arguments to apply to the resultset.
// returns: dojo.store.api.Store.QueryResults
// The results of the query, extended with iterative methods.
//
// example:
// Given the following store:
//
// | var store = new dojo.store.Memory({
// | data: [
// | {id: 1, name: "one", prime: false },
// | {id: 2, name: "two", even: true, prime: true},
// | {id: 3, name: "three", prime: true},
// | {id: 4, name: "four", even: true, prime: false},
// | {id: 5, name: "five", prime: true}
// | ]
// | });
//
// ...find all items where "prime" is true:
//
// | var results = store.query({ prime: true });
//
// ...or find all items where "even" is true:
//
// | var results = store.query({ even: true });
return dojo.store.util.QueryResults(this.queryEngine(query, options)(this.data));
},
setData: function(data){
// summary:
// Sets the given data as the source for this store, and indexes it
// data: Object[]
// An array of objects to use as the source of data.
if(data.items){
// just for convenience with the data format IFRS expects
this.idProperty = data.identifier;
data = this.data = data.items;
}else{
this.data = data;
}
for(var i = 0, l = data.length; i < l; i++){
var object = data[i];
this.index[object[this.idProperty]] = object;
}
}
});
}

View File

@@ -0,0 +1,173 @@
/*
Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
Available via Academic Free License >= 2.1 OR the modified BSD license.
see: http://dojotoolkit.org/license for details
*/
if(!dojo._hasResource["dojo.store.Observable"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.store.Observable"] = true;
dojo.provide("dojo.store.Observable");
dojo.getObject("store", true, dojo);
dojo.store.Observable = function(store){
// summary:
// The Observable store wrapper takes a store and sets an observe method on query()
// results that can be used to monitor results for changes.
//
// description:
// Observable wraps an existing store so that notifications can be made when a query
// is performed.
//
// example:
// Create a Memory store that returns an observable query, and then log some
// information about that query.
//
// | var store = dojo.store.Observable(new dojo.store.Memory({
// | data: [
// | {id: 1, name: "one", prime: false},
// | {id: 2, name: "two", even: true, prime: true},
// | {id: 3, name: "three", prime: true},
// | {id: 4, name: "four", even: true, prime: false},
// | {id: 5, name: "five", prime: true}
// | ]
// | }));
// | var changes = [], results = store.query({ prime: true });
// | var observer = results.observe(function(object, previousIndex, newIndex){
// | changes.push({previousIndex:previousIndex, newIndex:newIndex, object:object});
// | });
//
// See the Observable tests for more information.
var queryUpdaters = [], revision = 0;
// a Comet driven store could directly call notify to notify observers when data has
// changed on the backend
store.notify = function(object, existingId){
revision++;
var updaters = queryUpdaters.slice();
for(var i = 0, l = updaters.length; i < l; i++){
updaters[i](object, existingId);
}
};
var originalQuery = store.query;
store.query = function(query, options){
options = options || {};
var results = originalQuery.apply(this, arguments);
if(results && results.forEach){
var nonPagedOptions = dojo.mixin({}, options);
delete nonPagedOptions.start;
delete nonPagedOptions.count;
var queryExecutor = store.queryEngine && store.queryEngine(query, nonPagedOptions);
var queryRevision = revision;
var listeners = [], queryUpdater;
results.observe = function(listener, includeObjectUpdates){
if(listeners.push(listener) == 1){
// first listener was added, create the query checker and updater
queryUpdaters.push(queryUpdater = function(changed, existingId){
dojo.when(results, function(resultsArray){
var atEnd = resultsArray.length != options.count;
var i;
if(++queryRevision != revision){
throw new Error("Query is out of date, you must observe() the query prior to any data modifications");
}
var removedObject, removedFrom = -1, insertedInto = -1;
if(existingId){
// remove the old one
for(i = 0, l = resultsArray.length; i < l; i++){
var object = resultsArray[i];
if(store.getIdentity(object) == existingId){
removedObject = object;
removedFrom = i;
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
resultsArray.splice(i, 1);
}
break;
}
}
}
if(queryExecutor){
// add the new one
if(changed &&
// if a matches function exists, use that (probably more efficient)
(queryExecutor.matches ? queryExecutor.matches(changed) : queryExecutor([changed]).length)){
if(removedFrom > -1){
// put back in the original slot so it doesn't move unless it needs to (relying on a stable sort below)
resultsArray.splice(removedFrom, 0, changed);
}else{
resultsArray.push(changed);
}
insertedInto = dojo.indexOf(queryExecutor(resultsArray), changed);
if((options.start && insertedInto == 0) ||
(!atEnd && insertedInto == resultsArray.length -1)){
// if it is at the end of the page, assume it goes into the prev or next page
insertedInto = -1;
}
}
}else if(changed){
// we don't have a queryEngine, so we can't provide any information
// about where it was inserted, but we can at least indicate a new object
insertedInto = removedFrom >= 0 ? removedFrom : (store.defaultIndex || 0);
}
if((removedFrom > -1 || insertedInto > -1) &&
(includeObjectUpdates || !queryExecutor || (removedFrom != insertedInto))){
var copyListeners = listeners.slice();
for(i = 0;listener = copyListeners[i]; i++){
listener(changed || removedObject, removedFrom, insertedInto);
}
}
});
});
}
return {
cancel: function(){
// remove this listener
listeners.splice(dojo.indexOf(listeners, listener), 1);
if(!listeners.length){
// no more listeners, remove the query updater too
queryUpdaters.splice(dojo.indexOf(queryUpdaters, queryUpdater), 1);
}
}
};
};
}
return results;
};
var inMethod;
function whenFinished(method, action){
var original = store[method];
if(original){
store[method] = function(value){
if(inMethod){
// if one method calls another (like add() calling put()) we don't want two events
return original.apply(this, arguments);
}
inMethod = true;
try{
return dojo.when(original.apply(this, arguments), function(results){
action((typeof results == "object" && results) || value);
return results;
});
}finally{
inMethod = false;
}
};
}
}
// monitor for updates by listening to these methods
whenFinished("put", function(object){
store.notify(object, store.getIdentity(object));
});
whenFinished("add", function(object){
store.notify(object);
});
whenFinished("remove", function(id){
store.notify(undefined, id);
});
return store;
};
}

10
lib/dojo/store/README Normal file
View File

@@ -0,0 +1,10 @@
This folder contains the stores and utilities implementing the proposed new Dojo Object Store API,
a successor and unifier to Dojo Data, Dojo Storage, and potentially Dojo Model. These
stores are brand new, and designed to provide simple lightweight implementations
providing core functionality for typical applications. These modules are under active
development, and exist here at this time to provide maximum visibility to the
efforts to design and develop this new API and set of base stores. The goal is
to have these stores ready for Dojo 1.6. In the meantime, these stores are likely to
have API changes, may be missing some functionality, tests, and/or documentation.
If these modules are not deemed suitably stable by the 1.6 release, this directory (or
individual modules) will be removed and be given a later release target.

304
lib/dojo/store/api/Store.js Normal file
View File

@@ -0,0 +1,304 @@
/*
Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
Available via Academic Free License >= 2.1 OR the modified BSD license.
see: http://dojotoolkit.org/license for details
*/
define([], function() {
// module:
// dojo/store/api/Store
// summary:
// The module defines the Dojo object store interface.
dojo.declare("dojo.store.api.Store", null, {
// summary:
// This is an abstract API that data provider implementations conform to.
// This file defines methods signatures and intentionally leaves all the
// methods unimplemented. For more information on the dojo.store APIs,
// please visit: http://dojotoolkit.org/reference-guide/dojo/store.html
// Every method and property is optional, and is only needed if the functionality
// it provides is required.
// Every method may return a promise for the specified return value if the
// execution of the operation is asynchronous (except
// for query() which already defines an async return value).
// idProperty: String
// If the store has a single primary key, this tndicates the property to use as the
// identity property. The values of this property should be unique.
idProperty: "id",
// queryEngine: Function
// If the store can be queried locally (on the client side in JS), this defines
// the query engine to use for querying the data store.
// This takes a query and query options and returns a function that can execute
// the provided query on a JavaScript array. The queryEngine may be replace to
// provide more sophisticated querying capabilities. For example:
// | var query = store.queryEngine({foo:"bar"}, {count:10});
// | query(someArray) -> filtered array
// The returned query function may have a "matches" property that can be
// used to determine if an object matches the query. For example:
// | query.matches({id:"some-object", foo:"bar"}) -> true
// | query.matches({id:"some-object", foo:"something else"}) -> false
queryEngine: null,
get: function(id){
// summary:
// Retrieves an object by its identity
// id: Number
// The identity to use to lookup the object
// returns: Object
// The object in the store that matches the given id.
},
getIdentity: function(object){
// summary:
// Returns an object's identity
// object: Object
// The object to get the identity from
// returns: String|Number
},
put: function(object, directives){
// summary:
// Stores an object
// object: Object
// The object to store.
// directives: dojo.store.api.Store.PutDirectives?
// Additional directives for storing objects.
// returns: Number|String
},
add: function(object, directives){
// summary:
// Creates an object, throws an error if the object already exists
// object: Object
// The object to store.
// directives: dojo.store.api.Store.PutDirectives?
// Additional directives for creating objects.
// returns: Number|String
},
remove: function(id){
// summary:
// Deletes an object by its identity
// id: Number
// The identity to use to delete the object
delete this.index[id];
var data = this.data,
idProperty = this.idProperty;
for(var i = 0, l = data.length; i < l; i++){
if(data[i][idProperty] == id){
data.splice(i, 1);
return;
}
}
},
query: function(query, options){
// summary:
// Queries the store for objects. This does not alter the store, but returns a
// set of data from the store.
// query: String|Object|Function
// The query to use for retrieving objects from the store.
// options: dojo.store.api.Store.QueryOptions
// The optional arguments to apply to the resultset.
// returns: dojo.store.api.Store.QueryResults
// The results of the query, extended with iterative methods.
//
// example:
// Given the following store:
//
// ...find all items where "prime" is true:
//
// | store.query({ prime: true }).forEach(function(object){
// | // handle each object
// | });
},
transaction: function(){
// summary:
// Starts a new transaction.
// Note that a store user might not call transaction() prior to using put,
// delete, etc. in which case these operations effectively could be thought of
// as "auto-commit" style actions.
// returns: dojo.store.api.Store.Transaction
// This represents the new current transaction.
},
getChildren: function(parent, options){
// summary:
// Retrieves the children of an object.
// parent: Object
// The object to find the children of.
// options: dojo.store.api.Store.QueryOptions?
// Additional options to apply to the retrieval of the children.
// returns: dojo.store.api.Store.QueryResults
// A result set of the children of the parent object.
},
getMetadata: function(object){
// summary:
// Returns any metadata about the object. This may include attribution,
// cache directives, history, or version information.
// object: Object
// The object to return metadata for.
// returns: Object
// An object containing metadata.
}
});
dojo.store.api.Store.PutDirectives = function(id, before, parent, overwrite){
// summary:
// Directives passed to put() and add() handlers for guiding the update and
// creation of stored objects.
// id: String|Number?
// Indicates the identity of the object if a new object is created
// before: Object?
// If the collection of objects in the store has a natural ordering,
// this indicates that the created or updated object should be placed before the
// object specified by the value of this property. A value of null indicates that the
// object should be last.
// parent: Object?,
// If the store is hierarchical (with single parenting) this property indicates the
// new parent of the created or updated object.
// overwrite: Boolean?
// If this is provided as a boolean it indicates that the object should or should not
// overwrite an existing object. A value of true indicates that a new object
// should not be created, the operation should update an existing object. A
// value of false indicates that an existing object should not be updated, a new
// object should be created (which is the same as an add() operation). When
// this property is not provided, either an update or creation is acceptable.
this.id = id;
this.before = before;
this.parent = parent;
this.overwrite = overwrite;
};
dojo.store.api.Store.SortInformation = function(attribute, descending){
// summary:
// An object describing what attribute to sort on, and the direction of the sort.
// attribute: String
// The name of the attribute to sort on.
// descending: Boolean
// The direction of the sort. Default is false.
this.attribute = attribute;
this.descending = descending;
};
dojo.store.api.Store.QueryOptions = function(sort, start, count){
// summary:
// Optional object with additional parameters for query results.
// sort: dojo.store.api.Store.SortInformation[]?
// A list of attributes to sort on, as well as direction
// For example:
// | [{attribute:"price, descending: true}].
// If the sort parameter is omitted, then the natural order of the store may be
// applied if there is a natural order.
// start: Number?
// The first result to begin iteration on
// count: Number?
// The number of how many results should be returned.
this.sort = sort;
this.start = start;
this.count = count;
};
dojo.declare("dojo.store.api.Store.QueryResults", null, {
// summary:
// This is an object returned from query() calls that provides access to the results
// of a query. Queries may be executed asynchronously.
forEach: function(callback, thisObject){
// summary:
// Iterates over the query results, based on
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/forEach.
// Note that this may executed asynchronously. The callback may be called
// after this function returns.
// callback:
// Function that is called for each object in the query results
// thisObject:
// The object to use as |this| in the callback.
},
filter: function(callback, thisObject){
// summary:
// Filters the query results, based on
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter.
// Note that this may executed asynchronously. The callback may be called
// after this function returns.
// callback:
// Function that is called for each object in the query results
// thisObject:
// The object to use as |this| in the callback.
// returns: dojo.store.api.Store.QueryResults
},
map: function(callback, thisObject){
// summary:
// Maps the query results, based on
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map.
// Note that this may executed asynchronously. The callback may be called
// after this function returns.
// callback:
// Function that is called for each object in the query results
// thisObject:
// The object to use as |this| in the callback.
// returns: dojo.store.api.Store.QueryResults
},
then: function(callback, errorHandler){
// summary:
// This registers a callback for when the query is complete, if the query is asynchronous.
// This is an optional method, and may not be present for synchronous queries.
// callback:
// This is called when the query is completed successfully, and is passed a single argument
// that is an array representing the query results.
// errorHandler:
// This is called if the query failed, and is passed a single argument that is the error
// for the failure.
},
observe: function(listener, includeAllUpdates){
// summary:
// This registers a callback for notification of when data is modified in the query results.
// This is an optional method, and is usually provided by dojo.store.Observable.
// listener: Function
// The listener function is called when objects in the query results are modified
// to affect the query result. The listener function is called with the following
// arguments:
// | listener(object, removedFrom, insertedInto);
// * The object parameter indicates the object that was create, modified, or deleted.
// * The removedFrom parameter indicates the index in the result array where
// the object used to be. If the value is -1, then the object is an addition to
// this result set (due to a new object being created, or changed such that it
// is a part of the result set).
// * The insertedInto parameter indicates the index in the result array where
// the object should be now. If the value is -1, then the object is a removal
// from this result set (due to an object being deleted, or changed such that it
// is not a part of the result set).
// includeAllUpdates:
// This indicates whether or not to include object updates that do not affect
// the inclusion or order of the object in the query results. By default this is false,
// which means that if any object is updated in such a way that it remains
// in the result set and it's position in result sets is not affected, then the listener
// will not be fired.
},
// total: Number|Promise?
// This property should be included in if the query options included the "count"
// property limiting the result set. This property indicates the total number of objects
// matching the query (as if "start" and "count" weren't present). This may be
// a promise if the query is asynchronous.
total: 0
});
dojo.declare("dojo.store.api.Store.Transaction", null, {
// summary:
// This is an object returned from transaction() calls that represents the current
// transaction.
commit: function(){
// summary:
// Commits the transaction. This may throw an error if it fails. Of if the operation
// is asynchronous, it may return a promise that represents the eventual success
// or failure of the commit.
},
abort: function(callback, thisObject){
// summary:
// Aborts the transaction. This may throw an error if it fails. Of if the operation
// is asynchronous, it may return a promise that represents the eventual success
// or failure of the abort.
}
});
});

View File

@@ -0,0 +1,67 @@
/*
Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
Available via Academic Free License >= 2.1 OR the modified BSD license.
see: http://dojotoolkit.org/license for details
*/
if(!dojo._hasResource["dojo.store.util.QueryResults"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.store.util.QueryResults"] = true;
dojo.provide("dojo.store.util.QueryResults");
dojo.getObject("store.util", true, dojo);
dojo.store.util.QueryResults = function(results){
// summary:
// A function that wraps the results of a store query with additional
// methods.
//
// description:
// QueryResults is a basic wrapper that allows for array-like iteration
// over any kind of returned data from a query. While the simplest store
// will return a plain array of data, other stores may return deferreds or
// promises; this wrapper makes sure that *all* results can be treated
// the same.
//
// Additional methods include `forEach`, `filter` and `map`.
//
// returns: Object
// An array-like object that can be used for iterating over.
//
// example:
// Query a store and iterate over the results.
//
// | store.query({ prime: true }).forEach(function(item){
// | // do something
// | });
if(!results){
return results;
}
// if it is a promise it may be frozen
if(results.then){
results = dojo.delegate(results);
}
function addIterativeMethod(method){
if(!results[method]){
results[method] = function(){
var args = arguments;
return dojo.when(results, function(results){
Array.prototype.unshift.call(args, results);
return dojo.store.util.QueryResults(dojo[method].apply(dojo, args));
});
};
}
}
addIterativeMethod("forEach");
addIterativeMethod("filter");
addIterativeMethod("map");
if(!results.total){
results.total = dojo.when(results, function(results){
return results.length;
});
}
return results;
};
}

View File

@@ -0,0 +1,116 @@
/*
Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
Available via Academic Free License >= 2.1 OR the modified BSD license.
see: http://dojotoolkit.org/license for details
*/
if(!dojo._hasResource["dojo.store.util.SimpleQueryEngine"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.store.util.SimpleQueryEngine"] = true;
dojo.provide("dojo.store.util.SimpleQueryEngine");
dojo.getObject("store.util", true, dojo);
dojo.store.util.SimpleQueryEngine = function(query, options){
// summary:
// Simple query engine that matches using filter functions, named filter
// functions or objects by name-value on a query object hash
//
// description:
// The SimpleQueryEngine provides a way of getting a QueryResults through
// the use of a simple object hash as a filter. The hash will be used to
// match properties on data objects with the corresponding value given. In
// other words, only exact matches will be returned.
//
// This function can be used as a template for more complex query engines;
// for example, an engine can be created that accepts an object hash that
// contains filtering functions, or a string that gets evaluated, etc.
//
// When creating a new dojo.store, simply set the store's queryEngine
// field as a reference to this function.
//
// query: Object
// An object hash with fields that may match fields of items in the store.
// Values in the hash will be compared by normal == operator, but regular expressions
// or any object that provides a test() method are also supported and can be
// used to match strings by more complex expressions
// (and then the regex's or object's test() method will be used to match values).
//
// options: dojo.store.util.SimpleQueryEngine.__queryOptions?
// An object that contains optional information such as sort, start, and count.
//
// returns: Function
// A function that caches the passed query under the field "matches". See any
// of the "query" methods on dojo.stores.
//
// example:
// Define a store with a reference to this engine, and set up a query method.
//
// | var myStore = function(options){
// | // ...more properties here
// | this.queryEngine = dojo.store.util.SimpleQueryEngine;
// | // define our query method
// | this.query = function(query, options){
// | return dojo.store.util.QueryResults(this.queryEngine(query, options)(this.data));
// | };
// | };
// create our matching query function
switch(typeof query){
default:
throw new Error("Can not query with a " + typeof query);
case "object": case "undefined":
var queryObject = query;
query = function(object){
for(var key in queryObject){
var required = queryObject[key];
if(required && required.test){
if(!required.test(object[key])){
return false;
}
}else if(required != object[key]){
return false;
}
}
return true;
};
break;
case "string":
// named query
if(!this[query]){
throw new Error("No filter function " + query + " was found in store");
}
query = this[query];
// fall through
case "function":
// fall through
}
function execute(array){
// execute the whole query, first we filter
var results = dojo.filter(array, query);
// next we sort
if(options && options.sort){
results.sort(function(a, b){
for(var sort, i=0; sort = options.sort[i]; i++){
var aValue = a[sort.attribute];
var bValue = b[sort.attribute];
if (aValue != bValue) {
return !!sort.descending == aValue > bValue ? -1 : 1;
}
}
return 0;
});
}
// now we paginate
if(options && (options.start || options.count)){
var total = results.length;
results = results.slice(options.start || 0, (options.start || 0) + (options.count || Infinity));
results.total = total;
}
return results;
}
execute.matches = query;
return execute;
};
}