Available Stores

The following dojo.data stores are pre-packaged with Dojo:

dojo.data.ItemFileReadStore read-only store for JSON data
dojo.data.ItemFileWriteStore read/write store for JSON data
dojox.data.CsvStore read-only store for comma-separated variable (CSV) formatted data
dojox.data.OpmlStore read-only store for Outline Processor Markup Language (OPML)
dojox.data.HtmlTableStore read-only store for data kept in HTML-formatted tables
dojox.data.XmlStore read/write store for basic XML data
dojox.data.FlickrStore read store for queries on flickr.com, and a good example data store for web services
dojox.data.FlickrRestStore read store for queries on flickr.com, and a good example data store for web services. More advanced version of FlickrStore
dojox.data.QueryReadStore like ItemFileReadStore, read-only store for JSON data, but queries servers on each request
dojox.data.AtomReadStore read store for Atom XML documents.

dojo.data.ItemFileReadStore

Summary:
Dojo core provides a basic implementation of a read-only datastore, ItemFileReadStore. This store reads the JSON structured contents from an http endpoint (service or URL), or from an in-memory JavaScript object, and stores all the items in-memory for simple and quick access. ItemFileReadStore is designed to allow for flexibility in how it represents item hierarchy, references, and custom data types. It also provides options for which attribute can act as the unique identifier (for dojo.data.api.Identity), and which attribute can be used as a general label for an item. This store has an expectation that data is provided to in in a specific though very flexible, format. All of the examples on this page demonstrate the general format expected.

The following dojo.data APIs are implemented by ItemFileReadStore

  • dojo.data.api.Read
  • dojo.data.api.Identity

Format Examples: The following examples of data conform to the format the store requires for item input.


JSON with References: The following geography example uses references (items referencing another item declared in the data):

{ 'identifier': 'name',
  'label': 'name',
  'items': [
     { 'name':'Africa', 'type':'continent',
         'children':[{'_reference':'Egypt'}, {'_reference':'Kenya'}, {'_reference':'Sudan'}] },
     { 'name':'Egypt', 'type':'country' },
     { 'name':'Kenya', 'type':'country',
         'children':[{'_reference':'Nairobi'}, {'_reference':'Mombasa'}] },
     { 'name':'Nairobi', 'type':'city' },
     { 'name':'Mombasa', 'type':'city' },
     { 'name':'Sudan', 'type':'country',
         'children':{'_reference':'Khartoum'} },
     { 'name':'Khartoum', type:'city' },
     { 'name':'Asia', 'type':'continent',
         'children':[{'_reference':'China'}, {'_reference':'India'}, {'_reference':'Russia'}, {'_reference':'Mongolia'}] },
     { 'name':'China', 'type':'country' },
     { 'name':'India', 'type':'country' },
     { 'name':'Russia', 'type':'country' },
     { 'name':'Mongolia', 'type':'country' },
     { 'name':'Australia', 'type':'continent', 'population':'21 million',
         'children':{'_reference':'Commonwealth of Australia'}},
     { 'name':'Commonwealth of Australia', 'type':'country', 'population':'21 million'},
     { 'name':'Europe', 'type':'continent',
         'children':[{'_reference':'Germany'}, {'_reference':'France'}, {'_reference':'Spain'}, {'_reference':'Italy'}] },
     { 'name':'Germany', 'type':'country' },
     { 'name':'France', 'type':'country' },
     { 'name':'Spain', 'type':'country' },
     { 'name':'Italy', 'type':'country' },
     { 'name':'North America', 'type':'continent',
         'children':[{'_reference':'Mexico'}, {'_reference':'Canada'}, {'_reference':'United States of America'}] },
     { 'name':'Mexico', 'type':'country', 'population':'108 million', 'area':'1,972,550 sq km',
         'children':[{'_reference':'Mexico City'}, {'_reference':'Guadalajara'}] },
     { 'name':'Mexico City', 'type':'city', 'population':'19 million', 'timezone':'-6 UTC'},
     { 'name':'Guadalajara', 'type':'city', 'population':'4 million', 'timezone':'-6 UTC' },
     { 'name':'Canada', 'type':'country',  'population':'33 million', 'area':'9,984,670 sq km',
         'children':[{'_reference':'Ottawa'}, {'_reference':'Toronto'}] },
     { 'name':'Ottawa', 'type':'city', 'population':'0.9 million', 'timezone':'-5 UTC'},
     { 'name':'Toronto', 'type':'city', 'population':'2.5 million', 'timezone':'-5 UTC' },
     { 'name':'United States of America', 'type':'country' },
     { 'name':'South America', 'type':'continent',
         'children':[{'_reference':'Brazil'}, {'_reference':'Argentina'}] },
     { 'name':'Brazil', 'type':'country', 'population':'186 million' },
     { 'name':'Argentina', 'type':'country', 'population':'40 million' }
]}

JSON with Hierarchy: The following geography example uses hierarchical items (items that contain definitions of other items):

{ 'identifier': 'name',
  'items': [
    { 'name':'Africa', 'type':'continent', children:[
        { 'name':'Egypt', 'type':'country' }, 
        { 'name':'Kenya', 'type':'country', children:[
            { 'name':'Nairobi', 'type':'city' },
            { 'name':'Mombasa', 'type':'city' } ]
        },
        { 'name':'Sudan', 'type':'country', 'children':
            { 'name':'Khartoum', 'type':'city' } 
        } ]
    },
    { 'name':'Asia', 'type':'continent', 'children':[
        { 'name':'China', 'type':'country' },
        { 'name':'India', 'type':'country' },
        { 'name':'Russia', 'type':'country' },
        { 'name':'Mongolia', 'type':'country' } ]
    },
    { 'name':'Australia', 'type':'continent', 'population':'21 million', 'children':
        { 'name':'Commonwealth of Australia', 'type':'country', 'population':'21 million'}
    },
    { 'name':'Europe', 'type':'continent', 'children':[
        { 'name':'Germany', 'type':'country' },
        { 'name':'France', 'type':'country' },
        { 'name':'Spain', 'type':'country' },
        { 'name':'Italy', 'type':'country' } ]
    },
    { 'name':'North America', 'type':'continent', 'children':[
        { 'name':'Mexico', 'type':'country',  'population':'108 million', 'area':'1,972,550 sq km', 'children':[
            { 'name':'Mexico City', 'type':'city', 'population':'19 million', 'timezone':'-6 UTC'},
            { 'name':'Guadalajara', 'type':'city', 'population':'4 million', 'timezone':'-6 UTC' } ]
        },
        { 'name':'Canada', 'type':'country', 'population':'33 million', 'area':'9,984,670 sq km', 'children':[
            { 'name':'Ottawa', 'type':'city', 'population':'0.9 million', 'timezone':'-5 UTC'},
            { 'name':'Toronto', 'type':'city', 'population':'2.5 million', 'timezone':'-5 UTC' }]
        },
        { 'name':'United States of America', 'type':'country' } ]
    },
    { 'name':'South America', 'type':'continent', children:[
        { 'name':'Brazil', 'type':'country', 'population':'186 million' },
        { 'name':'Argentina', 'type':'country', 'population':'40 million' } ]
    } ]
}

Custom Data Types: The following example uses custom data types (items with custom data types):

{ 'identifier': 'abbr', 
  'label': 'name',
  'items': [
    { 'abbr':'ec', 'name':'Ecuador',           'capital':'Quito' },
    { 'abbr':'eg', 'name':'Egypt',             'capital':'Cairo' },
    { 'abbr':'sv', 'name':'El Salvador',       'capital':'San Salvador' },
    { 'abbr':'gq', 'name':'Equatorial Guinea', 'capital':'Malabo' },
    { 'abbr':'er',
      'name':'Eritrea',
      'capital':'Asmara',
      'independence':{'_type':'Date', '_value':"1993-05-24T00:00:00Z"} // May 24, 1993 in ISO-8601 standard  
      },
    { 'abbr':'ee',
      'name':'Estonia',
      'capital':'Tallinn',
      'independence':{'_type':'Date', '_value':"1991-08-20T00:00:00Z"} // August 20, 1991 in ISO-8601 standard
      },
    { 'abbr':'et',
      'name':'Ethiopia',
      'capital':'Addis Ababa' }
]}

Note: For custom data types, ItemFileStore looks for attributes that have an object format value and contains the following two specific attributes:

_type
Attribute used to look up in the typeMap which constructor should be used to instantiate the custom data type.
_value
Parameter that is the data to be passed to the constructor of the type. In this case, the custom type is a JavaScript Date object, and its value is the ISO-8601 string format of the date.

NOTE: The top level wrapper of the input for each of these examples is a JavaScript object. It has the following basic attributes that define the list of items that constitute the data and meta information about them:

identifier
Optional Metadata. The attribute in each item to act as the unique identifier for that item. This parameter is optional.
label
Optional Metadata. The attribute in each item to act as the human-readable label for that item. This parameter is optional.
items
An array of JavaScript objects that act as the items of the store. The attributes that are part of the items can be any valid JavaScript attribute name. Note that there is a special way for items to reference other items in the store when dealing with hierarchical data.

Constructor parameters

The constructor for ItemFileReadStore takes the following possible parameters in its keyword arguments:

url
The URL from which to load the JSON data. This is optional. If it isn't specified, then you should specify the 'data' parameter to identify the in-memory javascript object that constitutes the basis for the store content.
data
The JavaScript object that represents the stores contents, as defined by the structure displayed in the examples. This is optional.
typeMap
A JavaScript associative map of data types to deserialize them. This is optional. See the Custom Data Type Mapping for more details.

Custom Data Type Mappings

Custom data types are a way to specify how the store should interpret a value of an attribute on an 'item' when it is parsing and loading the store from the ItemFile format. The ItemFileReadStore has one built in custom data type; the 'Date' object. It, by default, maps attribute values of {_type: "Date" _value: "some ISO string"} to a new Date instance instead of treating that JS object as a child item with attributes of '_type' and '_value'. The ItemFileReadStore also allows users to define their own custom types so that they can control how the information in the ItemFile format is interpreted. There are a couple of ways to map custom data types. There is a simple mapping method and general purpose mapping method that are described in the following sections.

Simple mapping: Direct Constructor

The direct constructor approach is the simplest way to map a type. This example assumes that the value stored in _value can be used as the parameter to the constructor for an object:

var typeMap = {
                 "Color": dojo.Color,
                 ...
              };

General Purpose Mapping

General purpose mapping is intended for cases where you cannot directly pass the value of _value into a constructor. A good example of this is how Date is mapped internally in ItemFileReadStore. This is used because ItemFileReadStore reads in the _value for a date as a serialized ISO string, which is not a format the general JavaScript Date object understands.

The following example shows the Date serialization load from an ISOString format:

var typeMap = {
                  "Date": {
                             type: Date,
                             deserialize: function(value){
                                 return dojo.date.stamp.fromISOString(value);
                             }
                          }
              };

Query Syntax

The fetch method query syntax for ItemFileReadStore is simple and straightforward. It allows a list of attributes to match against in an AND fashion. For example, a query object that looks like the following example will locate all items that have attributes of those names that match both of those values:

{ foo:"bar", bit:"bite"}

Note that ItemFileReadStore supports the use of wild cards (multi-character * and single character ?) in its attribute value matching.

Examples

To find all items with attribute foo that start with bar, the query would be:

{ foo:"bar*"}

To find all items with attribute foo the value of which ends with ar and ignoring only the first character, the query would be:

{ foo:"?ar"}

NOTE: Other stores should follow the same semantics in defining queries for consistency.

Usage Examples

For these examples, we'll assume a data source as defined by the example data format in this page unless otherwise specified.

Example 1: Query for all continents

var store = new dojo.data.ItemFileReadStore({url: "geography.json"});
var gotContinents = function(items, request){
    for (var i = 0; i < items.length; i++){
       var item = items[i];
       console.log("Located continent: " + store.getLabel(item));
    }
}
var request = store.fetch({query: {type:"continent"}, onComplete: gotContinents});

Example 2: Query for names that start with A, case insensitively

var store = new dojo.data.ItemFileReadStore({url: "geography.json"});
var gotNames= function(items, request){
    for (var i = 0; i < items.length; i++){
       var item = items[i];
       console.log("Located name that started with A: " + store.getLabel(item));
    }
}
var request = store.fetch({query: {name:"A*"}, queryOptions: {ignoreCase: true}, onComplete: gotNames});

Example 3: Programmatic creation of ItemFileReadStore without a URL. Then query for names that start with J, case insensitively.

IMPORTANT NOTE: In this example the object 'dataItems' is modified and used directly by the store to generate the internal store representation of all the data. This is for efficiency of use and code size. If you want to repeatedly reuse the dataItems object you will need to clone the object and pass the clone into the store. If you do not clone the object and try to reuse dataItems, you will likely encounter errors due to the modifications the ItemFileReadStore applies to the data set so that it can perform store operations efficiently.
var dataItems = {
   identifier: 'name',
   label: 'name',
   items: [
      {name: 'John Smith'},
      {name: 'Bob Smith'},
      {name: 'Nancy Smith}',
      {name: 'John Doe'}
   ] 
};
var store = new dojo.data.ItemFileReadStore({data: dataItems});
var gotNames= function(items, request){
    for (var i = 0; i < items.length; i++){
       var item = items[i];
       console.log("Located name that started with J: " + store.getLabel(item));
    }
}
var request = store.fetch({query: {name:"J*"}, queryOptions: {ignoreCase: true}, onComplete: gotNames});

Further Examples

For further examples refer to the Using Datastores section of the Dojo book or refer to the test cases for dojo.data provided in the tests sub-directory of your dojo distribution.

dojo.data.ItemFileWriteStore

Dojo core provides the ItemFileWriteStore store as an extension to ItemFileReadStore that adds on the dojo.data.api.Write and dojo.data.api.Notification API support to ItemFileReadStore. It was specifically created as a separate class so that if you only need read capability, you do not incur the download penalty of the write and notification API support if you won't use it. If your application needs to write to the ItemFileStore instead of just Read, then ItemFileWriteStore is the store you should instantiate. The input data format is identical to ItemFileReadStore.

The following dojo.data APIs are implemented by ItemFileWriteStore

  • dojo.data.api.Read
  • dojo.data.api.Write
  • dojo.data.api.Identity
  • dojo.data.api.Notification

Constructor Parameters

The constructor for ItemFileWriteStore takes the same parameters as ItemFileReadStore these are the following possible parameters in its keyword arguments:

url
The URL from which to load the JSON data. This is optional.
data
The JavaScript object that represents the stores contents, as defined by the previous structure. This is optional.
typeMap
A JavaScript associative map of data types to deserialize them. This is optional. See the Custom Data Type Mapping for more details.

Custom Data Type Mappings

The custom type mapping for the ItemFileWriteStore follows the same conventions as the ItemFileReadStore. The only caveat is, that for general purpose mappings, you must also provide a serialize function for mapping so the data can be rendered back out appropriately. For simple mapping, object.toString() is sufficient.

Simple Mapping: Direct Constructor

The direct constructor approach is the simplest way to map a type. This one assumes that the value stored in _value can be used as the parameter to the constructor for an object. When serializing this back to the ItemFileFormat, this assumes that object.toString() is sufficient for the _value value as shown in the following example:

var typeMap = {
                 "Color": dojo.Color,
                 ...
              };

General Purpose Mapping

The general purpose mapping is intended for cases in which you cannot directly pass the value of _value into a constructor. A good example of this is how Date is mapped internally in the ItemFileReadStore. This is used because ItemFileReadStore reads in the _value for a date as a serialized ISO string, which is not a format the general JavasScript Date object understands.

The following example shows date serialization and deserialization mapping:

var typeMap = {
                  "Date": {
                             type: Date,
                             deserialize: function(value){
                                 return dojo.date.stamp.fromISOString(value);
                             },
                             serialize: function(object){
                                 return dojo.date.stamp.toISOString(object);
                             }
                          }
              };

Query Syntax

The query syntax for ItemFileWriteStore is identical to the query syntax of ItemFileReadStore so see that section for more information.

Usage Examples

For these examples, we'll assume a datasource as defined by the following example data:

{ identifier: 'abbr',
  label: 'name',
  items: [
    { abbr:'ec', name:'Ecuador',           capital:'Quito' },
    { abbr:'eg', name:'Egypt',             capital:'Cairo' },
    { abbr:'sv', name:'El Salvador',       capital:'San Salvador' },
    { abbr:'gq', name:'Equatorial Guinea', capital:'Malabo' },
    { abbr:'er', name:'Eritrea',           capital:'Asmara' },
    { abbr:'ee', name:'Estonia',           capital:'Tallinn' },
    { abbr:'et', name:'Ethiopia',          capital:'Addis Ababa' }
]}

Example 1: Add in a new country

var store = new dojo.data.ItemFileWriteStore({url: "countries.json"});
var usa = store.newItem({abbr: 'us', name: 'United States of America', capital: 'Washington DC'});

function saveDone(){
    alert("Done saving.");
}
function saveFailed(){
    alert("Save failed.");
}
store.save({onComplete: saveDone, onError: saveFailed});

Example 2: Delete a country

var store = new dojo.data.ItemFileWriteStore({url: "countries.json"}); function saveDone(){ alert("Done saving."); } function saveFailed(){ alert("Save failed."); } var gotNames= function(items, request){ for (var i = 0; i < items.length; i++){ console.log("Deleted country: " + store.getLabel(item); store.deleteItem(items[i]); } store.save({onComplete: saveDone, onError: saveFailed}); } var request = store.fetch({query: {name:"Egypt"}, queryOptions: {ignoreCase: true}, onComplete: gotNames});

Custom Saving

The save method by itself only updates the in-memory copy. To write the store back to the server, you need to override the extension point "_saveCustom". In markup language, it'd look something like:

You could also extend ItemFileWriteStore using Dojo's inheritance facilities:

dojo.declare("CustomItemFileWriteStore", ItemFileWriteStore, {
        _saveCustom: function(saveCompleteCallback, saveFailedCallback){
                //  xhrPost/xhrPut your data back to your server (convert it to the server format first if need be)
                // 'this' keyword refers to the ItemFileWriteStore instance being extended
                saveCompleteCallback();
        }
});

Further examples

For further examples refer to the Using Datastores section of the Dojo book or refer to the test cases for dojo.data provided in the tests sub-directory of your dojo distribution.

dojox.data.CsvStore

CsvStore is a simple read-only store provided by Dojo and contained in the DojoX project. CsvStore is a read interface that works with CSV formated data files. The CSV file format is commonly known to folks who work regularly with spread sheet data. Like ItemFileReadStore, CsvStore reads the contents from an http endpoint or a JavaScript Data object that contains CSV formatted data. The following is an example of a CSV data source:

Title, Year, Producer
City of God, 2002, Katia Lund
Rain,, Christine Jeffs
2001: A Space Odyssey, , Stanley Kubrick
"This is a ""fake"" movie title", 1957, Sidney Lumet
Alien, 1979   , Ridley Scott
"The Sequel to ""Dances With Wolves.""", 1982, Ridley Scott
"Caine Mutiny, The", 1954, "Dymtryk ""the King"", Edward"

Note that in the above data, the first row is always assumed to be the column names. Those are what get assigned as the attribute names for the CSV items. Each row in the CSV data is treated as a single item.

The following dojo.data APIs are implemented by CsvStore

  • dojo.data.api.Read
  • dojo.data.api.Identity

Constructor Parameters

The constructor for CsvStore takes three possible parameters in its keyword arguments as defined in the following list:

url
The URL from which to load the CSV data. This is optional.
data
The JavaScript object which represents the stores contents as defined by the structure in the previous example. This is optional.
label
A string that identifies which column to treat as the human-readable label. It must match one of the column labels in the file for it to be effective.

Query Syntax:

The fetch method query syntax for CsvStore is simple and straightforward. It allows a list of attributes to match against in an AND fashion, just like ItemFileReadStore. For example, a query object that looks like the following example will locate all items that have attributes of those names that match both of those values:

{ foo:"bar", bit:"bite"}

Note that CsvStore supports the use of wild cards (multi-character * and single character ?) in its attribute value matching.

Examples

To find all items with attribute foo that start with bar, the query would be:

{ foo:"bar*"}

To find all items with attribute foo the value of which ends with ar and ignoring only the first character, the query would be:

{ foo:"?ar"}

NOTE: Other stores should follow the same semantics in how it defines its queries for consistency.

Usage Examples

Assume a data source as defined by the example data format in this page.

Example 1: Query for all movies by producer Ridley Scott

var store = new dojox.data.CsvStore({url: "movies.csv", label: "Title"});
var gotMovies = function(items, request){
    for (var i = 0; i < items.length; i++){
       var item = items[i];
       console.log("Located movie: " + store.getLabel(item);
    }
}
var request = store.fetch({query: {Producer:"Ridley Scott"}, onComplete: gotMovies});

Example 2: Query for titles that that start with A, case insensitively

var store = new dojo.data.ItemFileReadStore({url: "movies.csv", label: "Title"});
var gotTitles= function(items, request){
    for (var i = 0; i < items.length; i++){
       var item = items[i];
       console.log("Located name that started with A: " + store.getLabel(item);
    }
}
var request = store.fetch({query: {Title:"A*"}, queryOptions: {ignoreCase: true}, onComplete: gotTitles});

Further Examples

For further examples refer to the test cases provided in dojox.data.tests.

dojox.data.OpmlStore

OpmlStore is a simple read-only store provided by Dojo and contained in the DojoX project. OpmlStore is a read interface to work with Opml formatted XML files. Similar to ItemFileReadStore, OpmlStore reads the contents from an http endpoint or a browser DOM object that contains Opml formatted data.

The following dojo.data APIs are implemented by OpmlStore

  • dojo.data.api.Read
  • dojo.data.api.Identity

The following example shows an Opml data source:



    
        geography.opml
        2006-11-10
        2006-11-13
        Magellan, Ferdinand
    
    
        
            
            
                
                
            
            
                
            
        
        
            
            
            
            
        
        
            
        
        
            
            
            
            
        
        
            
                
                
            
            
                
                
            
            
        
        
            
            
        
    

Note: An item in OpmlStore is an entry. The atributes defined in the tag make up the attributes that are exposed for that item. Any child items (nested tags) are accessable using the special attribute children.

Constructor Parameters

The constructor for OpmlStore takes the following possible parameters in its keyword args:

url
The URL from which to load the Opmldata. This is optional.
data
The raw Opml DOM that represents the stores contents as defined by the previous structure. This is optional.
label
A string that identifies which column to treat as the human-readable label. It must match one of the attribute names in the ags for it to be effective.

Query Syntax

The fetch method query syntax for OpmlStore is simple and straightforward. It allows a list of attributes to match against in an AND fashion, just like ItemFileReadStore. For example, the following query object locates all items that have attributes of those names that match both of those values:

{ foo:"bar", bit:"bite"}

Note that OpmlStore supports the use of wild cards (multi-character * and single character ?) in its attribute value matching.

Examples

To find all items with attribute foobar, the query would be:

{ foo:"bar*"}

To find all items with attribute foo that value ends with ar, and ignoring only the first character, the query would be:

{ foo:"?ar"}

NOTE: Other stores should follow the same semantics in defining queries for consistency.

Usage Examples

For these examples, we'll assume a datasource as defined by the example data format in this page.

Example 1: Query for all continents starting with A

var store = new dojox.data.OpmlStore({url: "grography.xml", label: "Text"});
var gotContinents = function(items, request){
    for (var i = 0; i < items.length; i++){
       var item = items[i];
       console.log("Located continent: " + store.getLabel(item);
    }
}
var request = store.fetch({query: {text: "A*", type: "continent"}, onComplete: gotContinents});

Example 2: Query for all continents starting with A, case insensitively

var store = new dojox.data.OpmlStore({url: "grography.xml", label: "Text"});
var gotContinents = function(items, request){
    for (var i = 0; i < items.length; i++){
       var item = items[i];
       console.log("Located continent: " + store.getLabel(item);
    }
}
var request = store.fetch({query: {text: "a*", type: "continent"}, queryOptions: {ignoreCase: true}, onComplete: gotContinents});

Further Examples

For further examples refer to the test cases provided in dojox.data.tests.

dojox.data.AtomReadStore

Note: available from v1.1 and later
AtomReadStore parses and makes available data stored in Atom XML documents. Atom is a standard for transferring data via XML, similar to RSS but far more powerful.
The AtomReadStore loads a remote Atom XML document from the URL provided to it, and then makes its data available. It supports the following item attributes, mostly taken directly from the Atom specification.

To Be Completed

dojox.data.FlickrRestStore

FlickrRestStore is an implementation of the dojo.data API provides access to the Flickr photo sharing site's REST API. Many advanced features are available, including tag search, sorting on numerous attributes, full text search, support for simultaneous clients, result caching and more.

Dojo has several examples of browser in-memory stores, such as dojo.data.ItemFileReadStore, dojox.data.CsvStore, and dojox.data.OmplStore. While these stores are useful and great examples of how data stores can be used to wrapper accessing data, they are not the only way data is served and processed. In many cases, data stores can wrapper external services. It is those services that perform the querying and filtering of data, and then provide only that as a subset back to the data store for presentation as items.

FlickrRestStore is one such store. The purpose of FlickrRestStore is to wrapper the public photo feed of the Flickr service. Then by simply using the FlickrRestStore store, as you would any data store in Dojo, you now have access to querying the vast repository of public photos made available by others on the Web. Look at http://archive.dojotoolkit.org/nightly/dojotoolkit/dojox/data/demos/demo... to see it in action, or look here for some example usages.

The following dojo.data APIs are implemented by FlickrRestStore

  • dojo.data.api.Read

FlickrRestStore is built upon FlickrStore, a store which reads from Flickrs more simplistic public API. However, FlickrRestStore provides many more features:

  • Build on Flickr REST API. The Flickr REST API, a much more flexible API than the one used by FlickrStore. This opens up the possibility of having a very comprehensive data store that can have a very rich querying interface.
  • Results caching. If a request is repeated, or a subset of one or more previous requests is requested, it is returned immediately.
  • Supports multiple simultaneous clients. If an identical request is received before the first request has completed, a second remote request is not made. Instead, the second caller is notified when the first request completes. An example of this would be two widgets sharing the same FlickrRestStore, and individually paging through the photos.
  • Result sorting. The available sort attributes are date-taken, date-published and interestingness. See the examples below for their usage.
  • Full text search. The Flickr API supports full text searching, which you can do by passing a text parameter to the request query. See the examples below for its usage.
  • Filter by Photo Set. By specifying a setid parameter on the query, you can retrieve photos only from a single set. Due to limitations of the Flickr API, using this parameter negates the use of the full text search.
  • Tag search. Passing a tags parameter to the request query searches by the given tags. This parameter can be either a comma separated string, or an array of strings. See the examples below.

Flickr Terms and Conditions

Note: While this store wraps making calls to the Flickr service, as a user, you should still verify that you agree to the terms and conditions by which you are using the public Flickr photo service. Review their terms and conditions, and the API terms and conditions, here.

API Key

Another difference between FlickrRestStore and FlickrStore is that, due to the fact that FlickrRestStore works with the Flickr REST APIs, you will need to get a free API key from Flickr. You can do so at http://www.flickr.com/services/api/keys/apply/.

The Flickr service provides its data back in a wide variety of formats (for example, ATOM, RSS, and JSON) but FlickrRestStore only makes use of the JSON format. The following example shows a query that FlickrRestStore processes and the response:

Example

Query the first three photos from a user:


URL: http://www.flickr.com/services/rest/?format=json&method=flickr.people.ge...

Response:

jsonFlickrApi({
    "photos":{
        "page":1, 
        "pages":98, 
        "perpage":3,
        "total":"489", 
        "photo":[
            {
                 "id":"1352049918", 
                 "owner":"44153025@N00", 
                 "secret":"5636009306", 
                 "server":"1111", 
                 "farm":2, 
                 "title":"The Liffey Panorama", 
                 "ispublic":1, 
                 "isfriend":0, 
                 "isfamily":0
            }, 
            {
                "id":"1351120079", 
                "owner":"44153025@N00", 
                "secret":"880bf6a003", 
                "server":"1027", 
                "farm":2, 
                "title":"Many Hands make pretty flowers", 
                "ispublic":1, 
                "isfriend":0, 
                "isfamily":0
            }, 
            {
                "id":"1322051485", 
                "owner":"44153025@N00", 
                "secret":"b7c529335d", 
                "server":"1110", 
                "farm":2, 
                "title":"Wok'n'Roll baby!", 
                "ispublic":1, 
                "isfriend":0, 
                "isfamily":0
            }
       ]
    }, 
    "stat":"ok"}
)

FlickrRestStore's role is to process the query parameters passed to the dojo.data.api.Read API and generate the appropriate service URL. It then processes the response from the service and handles accessing the items returned from the query. It also provides simple attribute access to all the values.

Constructor Parameters

FlickrRestStore does not have any constructor parameters.

Item Attributes

All items returned from FlickrRestStore have the following attributes that can be accessed using the dojo.data.api.Read API to retrieve data about the item:

title
The title of the photo.
author
The person who published the photo to Flickr.
imageUrl
A URL to the full resolution photo image.
imageUrlSmall
A URL to the small (icon sized) resolution photo image.
imageUrlMedium
A URL to the mid resolution photo image.
imageUrlThumb
A URL to the thumbnail sized resolution photo image.
link
A URL linking to the Flickr page displaying the image.
dateTaken
The date the photo was taken.
datePublished
The date the photo was published.

Query Syntax

The fetch method query syntax for FlickrRestStore is simple and straightforward. It allows the following attributes to be set and queried against:

userid
A Flickr userid to use to narrow the search scope, e.g. '44153025@N00'. This is required.
apikey
A Flickr API key. Flickr requires clients of their REST APIs to register for an API key. This is free, and can be done at http://www.flickr.com/services/api/keys/apply/. Note: do not reuse the API key used in Dojo examples, register your own. This is required.
setid
The id of a photo set to use to narrow the result data. This is optional. If not specified,
photos from the users primary stream are returned.
page
Specifies the page of results to use. If not used, then the standard start parameter is used. This is optional.
lang
Specifies the language to return the results in. This is optional.
tags
Specifies the tags to search for. This can be either a comma separated list, or an array of strings. This is optional.
text
The text to use in a full text search. This matches any text in the title or description of a photo. This is optional.
sort
The order to sort the results in. This is a JSON object with two fields, as specified by the dojo.data API.

  • attribute: This specifies the name of the attribute to sort on. The supported attribute names are

    • date-posted
    • date-taken
    • interestingness

    If an attribute is not specified, the default is date-posted

  • descending: If set to true, the photos are sorted in descending order. If set to false, or not specified, the photos are sorted in ascending order.

. This is optional.

Note: Unlike many of the other example stores, the FlickrRestStore store cannot do wild-card matching of the attributes. This is because the Flickr public photo feed service cannot do it. In an ideal service implementation, the Flickr service would provide a mechanism by with to pass in wild cards as part of its query parameters.

Usage Examples

The following example shows how you would query for the first ten images belonging to a single user, then emit the title, author, and image URL to the console:

var flickrRestStore = new dojox.data.FlickrRestStore();

function onBegin(totalCount, requestObj){
    console.log("TOTAL IMAGE COUNT:" + totalCount);
}
function onItem(item, requestObj){
    console.log("TITLE: " + flickrStore.getValue(item, "title");
    console.log("AUTHOR: " + flickrStore.getValue(item, "author");
    console.log("LINK: " + flickrStore.getValue(item, "link");
    console.log("IMAGE URL: " + flickrStore.getValue(item, "imageUrl");
    console.log("IMAGE URL Small: " + flickrStore.getValue(item, "imageUrlSmall");
    console.log("IMAGE URL Medium: " + flickrStore.getValue(item, "imageUrlMedium");
    console.log("IMAGE URL Thumbnail: " + flickrStore.getValue(item, "imageUrlThumb");
    console.log("DATE TAKEN: " + flickrStore.getValue(item, "dateTaken");
    console.log("DATE PUBLISHED: " + flickrStore.getValue(item, "datePublished");
}
function onComplete(items, request){
    console.log("DONE!")
}
function onError(error, request){
    console.log("FAILED!")
}

var request = {
    query: {
      userid: "44153025@N00",
      apikey: "8c6803164dbc395fb7131c9d54843627"                                        
    },

    onBegin: onBegin,
    onItem: onItem,
    onComplete: onComplete,
    onError: onError
  };

//Get ten photos from user "44153025@N00".
request.start = 0;     //start at the beginning
request.count = 10;  //Retrieve ten images
flickrRestStore.fetch(request);

//Get ten photos from user "44153025@N00", 
//matching the tags volleyball or dublin.
request.start = 0;     //start at the beginning
request.count = 10;  //Retrieve ten images
request.query.tags = ["volleyball", "dublin"];

//This causes the search to match 
//"volleyball" OR "dublin".  To do an AND query, 
//use request.query.tagmode = "all"
request.query.tagmode = "any";  
flickrRestStore.fetch(request);

//Get fifty photos from user "44153025@N00", 
//sorting descending on interestingness.
request.start = 0;     //start at the beginning
request.count = 50;  //Retrieve fifty images
request.query.tags = null; //delete the tags from the previous request

//The sort parameter is an array, as the 
//dojo.data API specifies that a READ store should
//support multiple sort attributes.  
//However, the Flickr API only supports a single 
//sort parameter.
request.query.sort = [
   {
      //'attribute' could also be "date-taken" or "date-posted"
      attribute: "interestingness", 
      descending: true
   }
];
flickrRestStore.fetch(request);

//Get the second page of twenty photos from a given set
request.start = 20;
request.count = 20;
request.query.setid = "72157600959797470";
request.query.sort = null; //Clean up after the last request
flickrRestStore.fetch(request);

//Perform a full text search, retrieving 50 photos
request.start = 0;
request.count = 50;
//clean up after the previous request
request.query.setid = null;
//Finds all photos with "kinsale" in the title or description
request.query.text = "kinsale"; 
flickrRestStore.fetch(request);

Further Examples

For further examples refer to the test cases provided in dojox/data/tests/stores/FlickrRestStore.js.

dojox.data.HtmlTableStore

HtmlTableStore is a simple read-only store provided by Dojo and contained in the DojoX project. HtmlTableStore is a read interface to work with HTML tables with a generally set format. HTML tables are common ways for Web data to be displayed and they can be extremely useful as an alternate representation of data that is displayed in a charting or gauge widget. This store was created so that widgets, that can use dojo.data data stores, can read their input from HTML table data islands in the current page or in a remote page URL. This store implements both dojo.data.api.Read and dojo.data.api.Identity.

The following example shows an HTML table that this store can read:



    Books2.html


isbn title author
A9B57C Title of 1 Author of 1
A9B57F Title of 2 Author of 2
A9B577 Title of 3 Author of 3
A9B574 Title of 4 Author of 4
A9B5CC Title of 5 Author of 5

Note that the table rows in the tag are the items. The tag is used for defining the attribute name for each column in the table row for an item.

Constructor Parameters

The constructor for HtmlTableStore takes the following possible parameters in its keyword arguments:

url
The URL from which to load the HTML file containing the HTML table. This is optional.
tableId
The id of the HTML tag that contains the table to read from, in either a remote page (if the URL was passed) or in the current HTML DOM if the url parameter is null. This is required.

Query Syntax

The fetch method query syntax for HtmlTableStore is simple and straightforward. It allows for a list of attributes to match against in an AND fashion, just like ItemFileReadStore. For example, the following query object will locate all items that have attributes of those names that match both values:

{ foo:"bar", bit:"bite"}

Note that HtmlTableStore supports the use of wild cards (multi-character * and single character ?) in its attribute value matching.

Examples

To find all items with attribute foo that start with bar, the query would be:

{ foo:"bar*"}

To find all items with attribute foo that value ends with ar and ignoring only the first character, the query would be:

{ foo:"?ar"}

NOTE: Other stores should follow the same semantics in defining queries for consistency.

Usage Examples

For these examples, we'll assume a data source as defined by the example data format in this page.

Example 1: Query for all books that start with ISBN: A9B57

var store = new dojox.data.HtmlTableStore({tableId: "books2"});
var gotBooks = function(items, request){
    for (var i = 0; i < items.length; i++){
       var item = items[i];
       console.log("Located book: " + store.getValue(item, "title"));
    }
}
var request = store.fetch({query: {isbn:"A9B57*"}, onComplete: gotBooks});

Example 2: Query for all books that start with ISBN: A9B57 Case insensitively

var store = new dojox.data.HtmlTableStore({tableId: "books2"});
var gotBooks = function(items, request){
    for (var i = 0; i < items.length; i++){
       var item = items[i];
       console.log("Located book: " + store.getValue(item, "title");
    }
}
var request = store.fetch({query: {isbn:"a9b57*"}, queryOptions: {ignoreCase: true}, onComplete: gotBooks});

Further Examples

For further examples, refer to the test cases provided in

dojox.data.tests
.

dojox.data.XmlStore

XmlStore is a store provided by Dojo that is contained in the DojoX project. XmlStore is a read and write interface to basic XML data. XML is a common data interchange format and a store that can work with fairly generic XML documents is useful. The store is designed so that you can over-ride certain functions to get specific behaviors to occur when performing reads and saves.

The following dojo.data APIs are implemented by XmlStore

  • dojo.data.api.Read
  • dojo.data.api.Write

The following is an example of an XML document that this store can read:



    
        A9B57C
        Title of 1
        Author of 1
    
    
        A9B57F
        Title of 2
        Author of 2
    
    
        A9B577
        Title of 3
        Author of 3
    
    
        A9B574
        Title of 4
        Author of 4
    
    
        A9B5CC
        Title of 5
        Author of 5
    

Constructor Parameters

The constructor for XmlStore takes the following possible parameters in its keyword arguments:

url
The URL from which to load the XML file containing the data. This URL is also the end point used for posting data base in a save. This is optional.
sendQuery
Boolean option whether or not to send the query to a server for processing. The default is false.
false
It is assumed the server sends back the entire store dataset and the filerting and sorting must occur on the client side.
true
It is assumed the server is handling the filtering and is only sending back XML nodes that match the query. No filtering occurs clientside.
rootItem
A tag name for root items. This is optional. If it is not provided, then the XmlStore assumes the tags under the root element of the document are the root items.
keyAttribute
An attribute name for a key or an indentity. This is optional.
attributeMap
An anonymous object that contains properties for attribute mapping, for example {"tag_name.item_attribute_name": "@xml_attribute_name", ...} This is optional. This is done so that attributes which are actual XML tag attributes (and not sub-tags of an XML tag), can be referenced.
label
The attribute of an item to use for the return of getLabel(). This is optional.

Functions intended to be over-ridden to alter save behavior

The following functions can be over-ridden to alter save behavior, as described:

_getPostUrl(item)
Function that can be over-ridden to alter the way the store POSTs new items to the service. Note that this follows the REST convention in which an HTTP POST is a creation of a new resource.
_getPutUrl(item)
Function that can be over-ridden to alter the way the store PUTs updated items to the service. Note that this follows the REST convention in which an HTTP PUT is an update of an existing resource.
_getDeleteUrl(item)
Function that can be over-ridden to alter the way the store sends a DELETE item to the service. Note that this follows the REST convention in which an HTTP DELETE is used to remove a resource.

Query Syntax

The fetch method query syntax for XmlStore is simple and straightforward. It allows for a list of attributes to match against in an AND fashion, just like ItemFileReadStore. For example, the following query object will locate all items that have attributes of those names that match both of those values:

{ foo:"bar", bit:"bite"}

Note that XmlStore supports the use of wild cards (multi-character * and single character ?) in its attribute value matching.

Examples

To find all items with attribute foo that start with bar, the query would be:

{ foo:"bar*"}

To find all items with attribute foo the value of which ends with ar and ignoring only the first character, the query would be:

{ foo:"?ar"}

NOTE: Other stores should follow the same query definition semantics for consistency.

Usage Examples

For these examples, we'll assume a data source as defined by the example data format in this page.

Example 1: Query for all books that start with ISBN: A9B57

var store = new dojox.data.XmlStore({url: "books.xml", rootItem: "book"});
var gotBooks = function(items, request){
    for (var i = 0; i < items.length; i++){
       var item = items[i];
       console.log("Located book: " + store.getValue(item, "title");
    }
}
var request = store.fetch({query: {isbn:"A9B57*"}, onComplete: gotBooks});

Example 2: Query for all books that start with ISBN: A9B57 Case insensitively

var store = new dojox.data.XmlStore({url: "books.xml", rootItem: "book"});
var gotBooks = function(items, request){
    for (var i = 0; i < items.length; i++){
       var item = items[i];
       console.log("Located book: " + store.getValue(item, "title");
    }
}
var request = store.fetch({query: {isbn:"a9b57*"}, queryOptions: {ignoreCase: true}, onComplete: gotBooks});

Further Examples

For further examples see the test cases provided in dojox.data.tests.

dojox.data.FlickrStore

Dojo has several examples of browser in-memory stores, such as dojo.data.ItemFileReadStore, dojox.data.CsvStore, and dojox.data.OmplStore. While these stores are useful and great examples of how data stores can be used to wrapper accessing data, they are not the only way data is served and processed. In many cases, data stores can wrapper external services. It is those services that perform the querying and filtering of data, and then provide only that as a subset back to the data store for presentation as items.

FlickrStore is one such store. The purpose of FlickrStore is to wrapper the public photo feed of the Flickr service. Then by simply using the FlickrStore store, as you would any data store in Dojo, you now have access to querying the vast repository of public photos made available by others on the Web.

Note: While this store wraps making calls to the Flickr service, as a user, you should still verify that you agree to the terms and conditions by which you are using the public flickr photo service. Review their terms and conditions, and the API terms and conditions.

The Flickr service provides its data back in a wide variety of formats (for example, ATOM, RSS, and JSON) but FlickrStore only makes use of the JSON format. The following example shows a query that FlickrStore processes and the response:

Query (all pictures with tags animals, foxes, and cute):

http://api.flickr.com/services/feeds/photos_public.gne?tags=animals,bats,cute&format=json&tagmode=all

Response:

jsonFlickrFeed({
		"title": "Photos from everyone tagged animals, foxes and cute",
		"link": "http://www.flickr.com/photos/",
		"description": "",
		"modified": "2007-05-24T09:35:27Z",
		"generator": "http://www.flickr.com/",
		"items": [
	   {
			"title": "Ceramic Figures",
			"link": "http://www.flickr.com/photos/36362445@N00/511998141/",
			"media": {"m":"http://farm1.static.flickr.com/228/511998141_7b8398c3eb_m.jpg"},
			"date_taken": "2006-04-04T10:21:43-08:00",
			"description": "<p><a href="http://www.flickr.com/people/36362445@N00/">travellingcharl</a> posted a photo:</p> <p><a href="http://www.flickr.com/photos/36362445@N00/511998141/" title="Ceramic Figures"><img src="http://farm1.static.flickr.com/228/511998141_7b8398c3eb_m.jpg" width="240" height="180" alt="Ceramic Figures" /></a></p> <p>Ceramic figurines inside the Shinto shrine.</p>",
			"published": "2007-05-24T09:35:27Z",
			"author": "nobody@flickr.com (travellingcharl)",
			"tags": "cute animals japan ceramic geocaching coins toyko foxes naritatbstation3"
	   },
	   {
			"title": "Red Fox pup",
			"link": "http://www.flickr.com/photos/norwick/301289990/",
			"media": {"m":"http://farm1.static.flickr.com/104/301289990_da7413890b_m.jpg"},
			"date_taken": "2005-06-09T16:17:49-08:00",
			"description": "<p><a href="http://www.flickr.com/people/norwick/">bluebird's</a> posted a photo:</p> <p><a href="http://www.flickr.com/photos/norwick/301289990/" title="Red Fox pup"><img src="http://farm1.static.flickr.com/104/301289990_da7413890b_m.jpg" width="240" height="180" alt="Red Fox pup" /></a></p> <p>Are you sure it's save to come out!</p>",
			"published": "2006-11-19T22:14:47Z",
			"author": "nobody@flickr.com (bluebird's)",
			"tags": "wild summer canada cute nature beauty field animals landscape tiere scenery jung wilde sommer wildlife natur young feld felder adorable peaceful canadian alberta summertime prairie aussicht prairies landschaft foxes alert tier countrylife predators kanada redfox perky okotoks junger rotfuchs kanadische foxpups roterfuchs"
	   }
        ]
})

FlickrStore's role is to process the query parameters passed to the dojo.data.api.Read API and generate the appropriate service URL. It then processes the response from the service and handles accessing the items returned from the query. It also provides simple attribute access to all the values.

Constructor Parameters

FlickrStore does not have any constructor parameters.

Item Attributes

All items returned from FlickrStore have the following attributes that can be accessed using the dojo.data.api.Read API to retrieve data about the item:

title
The title of the photo.
author
The person who published the photo to Flickr.
description
A description of the photo. This will generally contain HTML formatted text.
dateTaken
A JavaScript date object representing the date the photo was taken.
datePublished
A JavaScript date object representing the date the photo was published to Flickr.
tags
The tags that are assigned to this photo. flickrStore.getValue(item, "tags") returns the first tag, flickrStore.getValues(item, "tags") returns all tags.
imageUrl
A URL to the full resolution photo image.
imageUrlSmall
A URL to the small (icon sized) resolution photo image.
imageUrlMedium
A URL to the mid resolution photo image.
link
A URL linking to the Flickr page displaying the image.

Query Syntax

The fetch method query syntax for FlickrStore is simple and straightforward. It allows the following attributes to be set and queried against:

id
A Flickr userid to use to narrow the search scope. This is optional.
ids
A comma separated list of IDs used to narrow search scope. This is optional.
tags
A comma separated list of tags to search for matches on. This is optional.
tagmode
Indicates whether all tags must match from the list or any can match from the list. Valid values are all or any and the default is any.
lang
Specifies the language to return the results in. This is optional.

Note: Unlike all the other example stores, the FlickrStore store cannot do wild-card matching of the attributes. This is because the Flickr public photo feed service cannot do it. In an ideal service implementation, the Flickr service would provide a mechanism by with to pass in wild cards as part of its query parameters. Also, the Flickr public feed API limits the number of returned photos to a maximum of twenty.

Usage Examples

The following example shows how you would query for all images with the 3dny tag, then emit the title, author, and image URL to the console:

var flickrStore = new FlickrStore();

                function onItem(item, requestObj){
                    console.log("TITLE: " + flickrStore(item, "title");
                    console.log("AUTHOR: " + flickrStore(item, "author");
                    console.log("IMAGE URL: " + flickrStore(item, "imageUrl");
                }
                function onComplete(items, request){
                    console.log("DONE!")
                }
                function onError(error, request){
                    console.log("FAILED!")
                }

                //Get the photos tagged 3dny...
                flickrStore.fetch({
                                    query: {tags:"3dny"},
                                    onItem: onItem,
                                    onComplete: onComplete,
                                    onError: onError
                                });

Further Examples

For further examples refer to the test cases provided in dojox/data/tests/stores/FlickrStore.js<.code>. In addition, there are demos using the FlickrStore store available in dojox/data/demos/.

dojox.data.QueryReadStore (1.0)

New in 1.0, QueryReadStore is very similar to ItemReadStore. They both use JSON as their exchange format. The difference is in the way they query data. ItemReadStore makes one fetch from the server, and handles all sorting and filtering in the client. That's fine for hundreds of records, even thousands. But for hundreds of thousands of records or slow Internet connections, that's less feasible.

QueryReadStore makes a request to the server for each sorting or query. This makes it ideal for large datasets with small windows of data, as in dijit.FilteringSelect.

Query Translation

A dojo.data request follows a specific JSON format. As an example, suppose we have a FilteringSelect which looks up states. When the user presses "A", the dojo.data request is:

{
   query: {name: "A*"},
   queryOptions: {ignoreCase: true},
   sort: [{attribute:"name", descending:false}],
   start: 0,
   count: 10
}

Now we want to hand this off to the server. Odds are, your server doesn't recognize incoming JSON, and asking it to do so is too restrictive. Instead, most server queries follow a REST pattern like this:

states.php?q=A*

Fortunately, it's easy to translate between the two. You simply subclass QueryReadStore like this:

dojo.provide("custom.ComboBoxReadStore");
dojo.declare("custom.ComboBoxReadStore", dojox.data.QueryReadStore, {
    fetch:function(request) {
	request.serverQuery = {q:request.query.name};
        // Call superclasses' fetch
	return this.inherited("fetch", arguments);
    }
});

We can place this file into a folder "custom" at the same level as the dojo, dijit and dojox directories of the distribution. (See Creating Your Own Modules for a discussion and alternatives.

You can download QueryReadStore.php below (it's also in /dojox/data/tests/stores/QueryReadStore.php) to run this example on a PHP server. The server portion hands over a portion of the states that fits the query. The full client program:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
            "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>QueryReadStore Demo</title>
    <style type="text/css">
        @import "http://o.aolcdn.com/dojo/1.0.0/dijit/themes/tundra/tundra.css";
        @import "http://o.aolcdn.com/dojo/1.0.0/dojo/dojo.css"
    </style>
    <script type="text/javascript" src="http://o.aolcdn.com/dojo/1.0.0/dojo/dojo.xd.js"
            djConfig="parseOnLoad: true">
</script>
    <script type="text/javascript">
       dojo.require("dojo.parser");
       dojo.require("dojox.data.QueryReadStore");
       dojo.require("dijit.form.FilteringSelect");
       dojo.require("custom.ComboBoxReadStore");
    </script>
</head>
<body class="tundra">
    <div dojoType="ComboBoxReadStore" jsId="store"
         url="QueryReadStore.php"
         requestMethod="get">

    </div>
    State: <input id="fs" dojoType="dijit.form.FilteringSelect" store="store" pageSize="5" />
</body></html>